문제

i want to set a ringtone from my asset Folder as a default ringtone. Now i found, that this is not possible, because asset isn't accessable from outside. So i must go the way to copy the file from my asset to sd or i use a content Provider. The last i think is the better way.

But I don't get it to work, I hope you can help me. Here is my Content Provider:

public class ringtoneContentProvider extends ContentProvider {
    private static final String TAG = "ringtoneCP";
    private static String[] mimeTypes = {"audio/ogg"};
    private Uri generatedUri;

    @Override
    public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
        Log.d("provider", "Provider openAssetFile wird aufgerufen");
        AssetManager am = getContext().getAssets();
        String file_name = uri.getLastPathSegment();
        if(file_name == null)
            throw new FileNotFoundException();
        AssetFileDescriptor afd = null;
        try {
            afd = am.openFd(file_name);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return afd;//super.openAssetFile(uri, mode);
    }

    @Override
    public String getType( Uri p1 )
    {
        Log.d("provider","provider getType");
        // TODO: Implement this method
        return "audio/ogg";
    }

    @Override
    public int delete( Uri p1, String p2, String[] p3 )
    {
        // TODO: Implement this method
        throw new RuntimeException("ContentProvider.delete not supported");
    }

    @Override
    public Cursor query( Uri p1, String[] p2, String p3, String[] p4, String p5 )
    {
        Log.d("provider","provider query 1");
        // TODO: Implement this method
        return null;
    }

    @Override
    public Cursor query( Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder, CancellationSignal cancellationSignal )
    {
        Log.d("provider","provider query 2");
        // TODO: Implement this method
        return super.query( uri, projection, selection, selectionArgs, sortOrder, cancellationSignal );
    }

    @Override
    public Uri insert( Uri p1, ContentValues p2 )
    {
        Log.d("provider","provider insert");
        // TODO: Implement this method
        return null;
    }

    @Override
    public boolean onCreate( )
    {
        Log.d("provider","provider onCreate");
        generatedUri = Uri.EMPTY;
        return true;
    }

    @Override
    public String[] getStreamTypes(Uri uri, String mimeTypeFilter) {
        Log.d("provider","provider getStreamTypes");
        return mimeTypes;
    }

    @Override
    public int update( Uri p1, ContentValues p2, String p3, String[] p4 )
    {
        Log.d("provider","provider update");
        // TODO: Implement this method
        return 0;
    }
}

an here I want to set the Ringtone:

RingtoneManager.setActualDefaultRingtoneUri(
                getApplicationContext(),
                RingtoneManager.TYPE_RINGTONE,
                Uri.parse("content://com.xyz.ringtoneContentProvider/ringtone.ogg")
        );

But this doesn't work. So I do this:

Uri myFile = Uri.parse("content://com.xyz.ringtoneContentProvider/ringtone.ogg");

//We now create a new content values object to store all the information
//about the ringtone.
ContentValues values = new ContentValues();
//values.put(MediaStore.MediaColumns.DATA, file.getAbsolutePath());
values.put(MediaStore.MediaColumns.DATA, myFile.getPath());
values.put(MediaStore.MediaColumns.TITLE, "MyRingtone"); // file.getName());
//values.put(MediaStore.MediaColumns.SIZE, file.length());
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/ogg");
values.put(MediaStore.Audio.AudioColumns.ARTIST, getApplicationContext().getString(R.string.app_name));
values.put(MediaStore.Audio.AudioColumns.IS_RINGTONE, true);
values.put(MediaStore.Audio.AudioColumns.IS_NOTIFICATION, false);
values.put(MediaStore.Audio.AudioColumns.IS_ALARM, false);
values.put(MediaStore.Audio.AudioColumns.IS_MUSIC, false);

//Work with the content resolver now
//First get the file we may have added previously and delete it,
//otherwise we will fill up the ringtone manager with a bunch of copies over time.
Uri uri = MediaStore.Audio.Media.getContentUriForPath(myFile.toString());
getApplicationContext().getContentResolver().delete(uri, MediaStore.MediaColumns.DATA + "=\"" + myFile + "\"", null);


//Ok now insert it
Uri newUri = getApplicationContext().getContentResolver().insert(myFile, values);

//Ok now set the ringtone from the content manager's uri, NOT the file's uri
RingtoneManager.setActualDefaultRingtoneUri(
        getApplicationContext(),
        RingtoneManager.TYPE_RINGTONE,
        newUri
);

But this didn't work either.


------------- With Stream Provider -------------

With the Steam Provider I get the following error if i open the Settings App / Sound:

The exact Error Message is: Writing exception to parcel
    java.lang.SecurityException: Permission Denial: reading com.commonsware.cwac.provider.StreamProvider uri content://com.xyz.myStreamProvider/ringtone.ogg from pid=1446, uid=1000 requires the provider be exported, or grantUriPermission()
            at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:467)
            at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:394)
            at android.content.ContentProvider$Transport.query(ContentProvider.java:194)
            at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:112)
            at android.os.Binder.execTransact(Binder.java:404)
            at dalvik.system.NativeStart.run(Native Method)

If I do android:exported="true" and android:grantUriPermissions="true" I get this Error Message:

FATAL EXCEPTION: main
    Process: com.xyz.app, PID: 1515
    java.lang.RuntimeException: Unable to get provider com.commonsware.cwac.provider.StreamProvider: java.lang.SecurityException: Provider must not be exported
            at android.app.ActivityThread.installProvider(ActivityThread.java:4793)
            at android.app.ActivityThread.installContentProviders(ActivityThread.java:4385)
            at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4325)
            at android.app.ActivityThread.access$1500(ActivityThread.java:135)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5017)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.SecurityException: Provider must not be exported
            at com.commonsware.cwac.provider.StreamProvider.attachInfo(StreamProvider.java:65)
            at android.app.ActivityThread.installProvider(ActivityThread.java:4790)
            at android.app.ActivityThread.installContentProviders(ActivityThread.java:4385)
            at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4325)
            at android.app.ActivityThread.access$1500(ActivityThread.java:135)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5017)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
            at dalvik.system.NativeStart.main(Native Method)

What could I do?

도움이 되었습니까?

해결책

What you want is not going to be possible with StreamProvider (or Android's FileProvider), as both are limited to the case where the request for the stream is initiated by an activity, where you can use FLAG_GRANT_URI_READ_PERMISSION to allow selective access to the content. You are welcome to fork StreamProvider to lift this restriction.

However, then you will run into the problem that media players will tend to want a seekable stream, which is not possible from an asset served via StreamProvider.

Your options are:

  1. Copy the file to external storage.

  2. Copy the file to internal storage, and use a forked version of FileProvider or StreamProvider that lifts the must-not-be-exported restriction, as streams backed by simple files are seekable when served by FileProvider or StreamProvider.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top