【问题标题】:Android Ringtone over Content Provider内容提供程序上的 Android 铃声
【发布时间】:2014-04-18 21:41:07
【问题描述】:

我想将我的资产文件夹中的铃声设置为默认铃声。现在我发现这是不可能的,因为无法从外部访问资产。所以我必须把文件从我的资产复制到 sd 或者我使用内容提供程序。最后一个我认为是更好的方法。

但我没有让它工作,我希望你能帮助我。这是我的内容提供者:

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;
    }
}

这里我要设置铃声:

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

但这不起作用。所以我这样做:

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
);

但这也不起作用。


------------- 使用流提供程序 -------------

使用 Steam Provider,如果我打开设置应用程序/声音,我会收到以下错误:

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)

如果我执行 android:exported="true" 和 android:grantUriPermissions="true" 我会收到以下错误消息:

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)

我能做什么?

【问题讨论】:

  • 您是否尝试过使用StreamProvider?它包装了支持库的FileProvider 并为资产提供content:// 样式的URI。
  • 看起来不错,但是不知道怎么用?我不明白演示。
  • 好的,我让它运行。但现在我得到权限错误。首先,我从 RingtoneManager 得知提供程序设置为 grantUriPermissions 为 ture 并导出 = true。但是当我这样做时,我从提供者那里得到了错误 提供者不能被导出。那我该怎么办?
  • 您找到解决方案了吗?我面临着几乎相同的问题stackoverflow.com/questions/27866201/…
  • 是和否。无法从资产文件夹中获取铃声。您必须先将其复制到外部存储中

标签: java android commonsware-cwac


【解决方案1】:

StreamProvider(或 Android 的 FileProvider)无法实现您想要的,因为两者都仅限于流请求由活动发起的情况,您可以使用 FLAG_GRANT_URI_READ_PERMISSION允许选择性地访问内容。欢迎您 fork StreamProvider 解除此限制。

但是,您会遇到媒体播放器倾向于需要可搜索流的问题,而通过StreamProvider 提供的资产无法实现这一点。

您的选择是:

  1. 将文件复制到外部存储。

  2. 将文件复制到内部存储,并使用 FileProviderStreamProvider 的分叉版本,以解除不得导出的限制,因为由简单文件支持的流在由 @ 提供服务时是可搜索的987654328@或StreamProvider

【讨论】:

  • 感谢您的回答!原始文件夹可以吗?
  • @Burner:不,从StreamProvider 及其消费者的角度来看,这与资产没有什么不同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-18
  • 2016-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多