通过 ChooserTargetService、FileProvider 和 grantUriPermission 向系统选择器提供图标
Providing icon to system chooser via ChooserTargetService, FileProvider and grantUriPermission
我在本地应用程序中存储了一些图像,这些图像与某些上下文(如联系人)相关联。我正在通过 ChooserTargetService
使用直接共享 (API 23+) 来显示可供选择的这些图像,我希望 ChooserTarget
个实例中 Icon
填充这些图像。
所以我想我可以为此使用 android.support.v4.content.FileProvider
(在 ChooserTargetService::onGetChooserTargets
内):
val file = File(File(filesDir, "images"), imageFileName)
val contentUri = FileProvider.getUriForFile(this, "com.company.fileprovider", file)
val icon = Icon.createWithContentUri(contentUri)
并在清单中:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mycompany.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths"/>
</provider>
但问题是我得到了一个例外
05-10 16:06:09.100 32444-32444/android:ui W/Icon: Unable to load image from URI: content://com.mycompany.fileprovider/images/icon_dice.png
java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.mycompany.fileprovider/images/icon_dice.png from pid=32444, uid=1000 requires the provider be exported, or grantUriPermission()
at android.os.Parcel.readException(Parcel.java:1684)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1147)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984)
at android.content.ContentResolver.openInputStream(ContentResolver.java:704)
at android.graphics.drawable.Icon.loadDrawableInner(Icon.java:335)
at android.graphics.drawable.Icon.loadDrawable(Icon.java:272)
at com.android.internal.app.ChooserActivity$ChooserTargetInfo.<init>(ChooserActivity.java:645)
at com.android.internal.app.ChooserActivity$ChooserListAdapter.addServiceResults(ChooserActivity.java:1003)
at com.android.internal.app.ChooserActivity.handleMessage(ChooserActivity.java:126)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
无法遵循 "exported" 建议,因为不幸的是 FileProvider
硬编码不允许它(来自 FileProvider.java
android 支持库源代码):
// Sanity check our security
if (info.exported) {
throw new SecurityException("Provider must not be exported");
}
if (!info.grantUriPermissions) {
throw new SecurityException("Provider must grant uri permissions");
}
所以我试着打电话给
grantUriPermission("<something goes here>", contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
但不清楚应该将什么作为包名第一个参数。从异常详细信息中,您可以推断出代码在 com.android.internal.app.ChooserActivity
中并由系统调用。
编辑:
无法使用 Icon.createWithFilePath
,因为您无法从不同的进程访问该文件:
W/Icon: Unable to load image from path: /data/user/0/com.mycompany.app/files/images/image.png
java.io.FileNotFoundException: /data/user/0/com.mycompany.app/files/images/image.png (Permission denied)
如果您尝试将文件设置为已弃用 Context.MODE_WORLD_READABLE
,您会得到:
java.lang.SecurityException: MODE_WORLD_READABLE no longer supported
在 Andorid 7 上。
为什么不在创建 ChooserTarget
之前使用文件中的数据创建图标图像。这就是 Google 的 Messenger 应用程序所做的。
File file = new File(new File(filesDir, "images"), imageFileName);
Bitmap b = BitmapFactory.decodeStream(new FileInputStream(file));
Icon icon = Icon.createWithBitmap(b);
return new ChooserTarget(title, icon, score, cn, extras);
如果您愿意,甚至可以对位图添加一些压缩。
但是,我必须警告您需要警惕您放置在活页夹中的这些对象的数量和大小...这些是 Parcelable 对象,从 7.0 开始,活页夹可以抛出TransacationTooLargeException
如果您将太多或太大的位图放入这些 ChooserTarget
或通过它发送的任何 Parcelable。
MODE_WORLD_READABLE 是一个安全漏洞。
所以,google 首先弃用它,然后完全删除它。
MODE_WORLD_READABLE 在 Android M 之前的版本中被弃用。在 Android N 中它不再受支持并抛出 SecurityException。
解决方案 :
请尝试不同的模式。我用了 Context.MODE_PRIVATE 并且有效。
我在本地应用程序中存储了一些图像,这些图像与某些上下文(如联系人)相关联。我正在通过 ChooserTargetService
使用直接共享 (API 23+) 来显示可供选择的这些图像,我希望 ChooserTarget
个实例中 Icon
填充这些图像。
所以我想我可以为此使用 android.support.v4.content.FileProvider
(在 ChooserTargetService::onGetChooserTargets
内):
val file = File(File(filesDir, "images"), imageFileName)
val contentUri = FileProvider.getUriForFile(this, "com.company.fileprovider", file)
val icon = Icon.createWithContentUri(contentUri)
并在清单中:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mycompany.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths"/>
</provider>
但问题是我得到了一个例外
05-10 16:06:09.100 32444-32444/android:ui W/Icon: Unable to load image from URI: content://com.mycompany.fileprovider/images/icon_dice.png
java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.mycompany.fileprovider/images/icon_dice.png from pid=32444, uid=1000 requires the provider be exported, or grantUriPermission()
at android.os.Parcel.readException(Parcel.java:1684)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1147)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984)
at android.content.ContentResolver.openInputStream(ContentResolver.java:704)
at android.graphics.drawable.Icon.loadDrawableInner(Icon.java:335)
at android.graphics.drawable.Icon.loadDrawable(Icon.java:272)
at com.android.internal.app.ChooserActivity$ChooserTargetInfo.<init>(ChooserActivity.java:645)
at com.android.internal.app.ChooserActivity$ChooserListAdapter.addServiceResults(ChooserActivity.java:1003)
at com.android.internal.app.ChooserActivity.handleMessage(ChooserActivity.java:126)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
无法遵循 "exported" 建议,因为不幸的是 FileProvider
硬编码不允许它(来自 FileProvider.java
android 支持库源代码):
// Sanity check our security
if (info.exported) {
throw new SecurityException("Provider must not be exported");
}
if (!info.grantUriPermissions) {
throw new SecurityException("Provider must grant uri permissions");
}
所以我试着打电话给
grantUriPermission("<something goes here>", contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
但不清楚应该将什么作为包名第一个参数。从异常详细信息中,您可以推断出代码在 com.android.internal.app.ChooserActivity
中并由系统调用。
编辑:
无法使用 Icon.createWithFilePath
,因为您无法从不同的进程访问该文件:
W/Icon: Unable to load image from path: /data/user/0/com.mycompany.app/files/images/image.png
java.io.FileNotFoundException: /data/user/0/com.mycompany.app/files/images/image.png (Permission denied)
如果您尝试将文件设置为已弃用 Context.MODE_WORLD_READABLE
,您会得到:
java.lang.SecurityException: MODE_WORLD_READABLE no longer supported
在 Andorid 7 上。
为什么不在创建 ChooserTarget
之前使用文件中的数据创建图标图像。这就是 Google 的 Messenger 应用程序所做的。
File file = new File(new File(filesDir, "images"), imageFileName);
Bitmap b = BitmapFactory.decodeStream(new FileInputStream(file));
Icon icon = Icon.createWithBitmap(b);
return new ChooserTarget(title, icon, score, cn, extras);
如果您愿意,甚至可以对位图添加一些压缩。
但是,我必须警告您需要警惕您放置在活页夹中的这些对象的数量和大小...这些是 Parcelable 对象,从 7.0 开始,活页夹可以抛出TransacationTooLargeException
如果您将太多或太大的位图放入这些 ChooserTarget
或通过它发送的任何 Parcelable。
MODE_WORLD_READABLE 是一个安全漏洞。
所以,google 首先弃用它,然后完全删除它。
MODE_WORLD_READABLE 在 Android M 之前的版本中被弃用。在 Android N 中它不再受支持并抛出 SecurityException。
解决方案 :
请尝试不同的模式。我用了 Context.MODE_PRIVATE 并且有效。