【问题标题】:How to show an Image via URI for an android glance widget如何通过 URI 为 Android Glance 小部件显示图像
【发布时间】:2022-08-04 18:23:45
【问题描述】:

我有一个使用新的Glance api 创建的android 主屏幕小部件,其中包含一个惰性列。列中的每一行都显示带有ImageProvider(contentUri) 的图像。

图片已从带有Glide 的URL 中检索到,并使用FileOutputStream(filename) 保存到内部存储文件中。请参阅下面的MainActivity

当尝试使用getUriForFile() 检索和显示图像时,我的小部件只显示一条消息“正在加载...”并且从不显示图像。不会发生崩溃。它只是从不加载图像。

如何从内部存储中提取图像并将其显示在LazyColumnRow() 中?

请注意,我在此示例中仅显示一个位图作为概念证明,但打算最终显示 10 多个位图。我遵循以下帖子中的建议,该建议建议对多个图像使用 URI。

Crash in glance app widget image when trying to display bitmap

请注意,当我在 MainActivity() 中提取相同的 URI 时,它可以工作并在 ImageView 中显示图像。

GlanceAppWidget Content() 中定义的可组合

@Composable
fun LazyColumnRow(
    /*LazyColumnRow is called from a LazyColumn, with each filename passed in here*/
) {
    val context = LocalContext.current
    val filepath = File(context.getFilesDir(), \"my_images\")
    val filename = File(filepath, \"default_image.png\") /*each LazyColumn item will have different filename in PROD*/
    val contentUri: Uri = FileProvider.getUriForFile(context,\"${context.packageName}.fileprovider\", filename)
    
    Row(modifier = GlanceModifier) {
        Image(
            modifier = GlanceModifier.size(28.dp),
            provider = ImageProvider(contentUri), /*this is not working*/
            contentDescription = \"Image\"
        )
    }
}

MainActivity 下载和存储位图的类。它还可以 成功检索 URI 并在 imageView 中显示。

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    @OptIn(ExperimentalAnimationApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = inflate(layoutInflater)
        setContentView(binding.root)

        //Download image from url
        var bitmap: Bitmap? = null
        CoroutineScope(Dispatchers.IO).launch {
            bitmap = Glide.with(baseContext)
                .asBitmap()
                .load(\"https://picsum.photos/id/237/200\")
                .submit()
                .get()
        }

        //store image in internal storage file
        val filepath = File(baseContext.getFilesDir(), \"my_images\")
        if (!filepath.exists()) {
            filepath.mkdirs()
        }
        val filename = File(filepath, \"default_image.png\")
        try {
            FileOutputStream(filename).use { out ->
                bitmap?.compress(Bitmap.CompressFormat.PNG, 100, out)
            }
        } catch (e: IOException) {
            e.printStackTrace()
        }

        //retrieve image from internal storage file
        val contentUri: Uri = getUriForFile(
            baseContext,
            \"$packageName.fileprovider\",
            filename)

        //display in imageView. This code works.
        val imageView = findViewById<ImageView>(R.id.myImage)
        imageView.setImageURI(contentUri)
    }
}

在清单中为 FileProvider URI 声明的内容提供程序

<provider
    android:name=\"androidx.core.content.FileProvider\"
    android:authorities=\"${applicationId}.fileprovider\"
    android:exported=\"false\"
    android:grantUriPermissions=\"true\">
    <meta-data
        android:name=\"android.support.FILE_PROVIDER_PATHS\"
        android:resource=\"@xml/file_paths\" />
</provider>

xml/文件路径

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<paths xmlns:android=\"http://schemas.android.com/apk/res/android\">
    <files-path name=\"my_images\" path=\"/\" />
    <files-path name=\"my_docs\" path=\"docs/\" />
</paths>

  • bitmap has been retrieved from a URL (via \'Glide\') and saved to internal storage getFilesDir()我不相信。该 url 可能会指向 .jpg 或 .png 之类的图像文件。在 getFilesDir() 中,将再次存储一个图像文件。如果您使用中间位图,那么这看起来无关紧要。
  • @blackapps 你是对的。我已经更新了问题以使其更清楚。谢谢。
  • 使用 FileProvider 获取您在 getFilesDir() 中创建的文件的 uri 是没有意义的。
  • @blackapps 我假设 FileProvider 仅用于访问外部文件,例如那些用getExternalFilesDir(String) 创建的。这是小部件错误的原因吗?即因为远程视图无法访问使用getFilesDir() 创建的内部文件,而我应该使用getExternalFilesDir(String) 保存文件?
  • 不,如果您希望外部应用程序处理您的文件,您可以使用 FileProvider。如果应用程序自己处理文件,它可以使用原始路径。不需要uri。

标签: android uri glance-appwidget glance


【解决方案1】:

您需要明确授予 Home 活动的读取权限,例如:

/**
 * When sharing a content URI to an app widget, we need to grant read access to the Home activity.
 *
 * See https://stackoverflow.com/a/59108192/1474476
 */
fun Context.grantUriPermissionToHomeActivity(uri: Uri, flags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION) {
    grantUriPermission(packageManager.homeActivityPackages(), uri, flags)
}

fun PackageManager.homeActivityPackages(): List<String> {
    val intent = Intent(Intent.ACTION_MAIN)
    intent.addCategory(Intent.CATEGORY_HOME)
    return queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
        .map { it.activityInfo.packageName }
}

fun Context.grantUriPermission(packages: List<String>, uri: Uri, flags: Int) {
    for (name in packages) {
        grantUriPermission(name, uri, flags)
    }
}

【讨论】:

    猜你喜欢
    • 2018-03-13
    • 2016-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 2020-06-30
    • 1970-01-01
    • 2017-06-29
    相关资源
    最近更新 更多