BrowseIcons.kt 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. package eu.kanade.presentation.browse.components
  2. import android.content.pm.PackageManager
  3. import android.util.DisplayMetrics
  4. import androidx.compose.foundation.Image
  5. import androidx.compose.foundation.layout.Box
  6. import androidx.compose.foundation.layout.aspectRatio
  7. import androidx.compose.foundation.layout.height
  8. import androidx.compose.foundation.shape.RoundedCornerShape
  9. import androidx.compose.runtime.Composable
  10. import androidx.compose.runtime.State
  11. import androidx.compose.runtime.getValue
  12. import androidx.compose.runtime.produceState
  13. import androidx.compose.ui.Modifier
  14. import androidx.compose.ui.draw.clip
  15. import androidx.compose.ui.graphics.Color
  16. import androidx.compose.ui.graphics.ImageBitmap
  17. import androidx.compose.ui.graphics.asImageBitmap
  18. import androidx.compose.ui.graphics.painter.ColorPainter
  19. import androidx.compose.ui.platform.LocalContext
  20. import androidx.compose.ui.res.imageResource
  21. import androidx.compose.ui.res.painterResource
  22. import androidx.compose.ui.unit.dp
  23. import androidx.core.graphics.drawable.toBitmap
  24. import coil.compose.AsyncImage
  25. import eu.kanade.domain.source.model.Source
  26. import eu.kanade.presentation.util.bitmapPainterResource
  27. import eu.kanade.tachiyomi.R
  28. import eu.kanade.tachiyomi.extension.model.Extension
  29. import eu.kanade.tachiyomi.util.lang.withIOContext
  30. private val defaultModifier = Modifier
  31. .height(40.dp)
  32. .aspectRatio(1f)
  33. @Composable
  34. fun SourceIcon(
  35. source: Source,
  36. modifier: Modifier = Modifier,
  37. ) {
  38. val icon = source.icon
  39. if (icon != null) {
  40. Image(
  41. bitmap = icon,
  42. contentDescription = "",
  43. modifier = modifier.then(defaultModifier),
  44. )
  45. } else {
  46. Image(
  47. painter = painterResource(id = R.mipmap.ic_local_source),
  48. contentDescription = "",
  49. modifier = modifier.then(defaultModifier),
  50. )
  51. }
  52. }
  53. @Composable
  54. fun ExtensionIcon(
  55. extension: Extension,
  56. modifier: Modifier = Modifier,
  57. density: Int = DisplayMetrics.DENSITY_DEFAULT,
  58. ) {
  59. when (extension) {
  60. is Extension.Available -> {
  61. AsyncImage(
  62. model = extension.iconUrl,
  63. contentDescription = "",
  64. placeholder = ColorPainter(Color(0x1F888888)),
  65. error = bitmapPainterResource(id = R.drawable.cover_error),
  66. modifier = modifier
  67. .clip(RoundedCornerShape(4.dp))
  68. .then(defaultModifier),
  69. )
  70. }
  71. is Extension.Installed -> {
  72. val icon by extension.getIcon(density)
  73. when (icon) {
  74. Result.Error -> Image(
  75. bitmap = ImageBitmap.imageResource(id = R.mipmap.ic_local_source),
  76. contentDescription = "",
  77. modifier = modifier.then(defaultModifier),
  78. )
  79. Result.Loading -> Box(modifier = modifier.then(defaultModifier))
  80. is Result.Success -> Image(
  81. bitmap = (icon as Result.Success<ImageBitmap>).value,
  82. contentDescription = "",
  83. modifier = modifier.then(defaultModifier),
  84. )
  85. }
  86. }
  87. is Extension.Untrusted -> Image(
  88. bitmap = ImageBitmap.imageResource(id = R.mipmap.ic_untrusted_source),
  89. contentDescription = "",
  90. modifier = modifier.then(defaultModifier),
  91. )
  92. }
  93. }
  94. @Composable
  95. private fun Extension.getIcon(density: Int = DisplayMetrics.DENSITY_DEFAULT): State<Result<ImageBitmap>> {
  96. val context = LocalContext.current
  97. return produceState<Result<ImageBitmap>>(initialValue = Result.Loading, this) {
  98. withIOContext {
  99. value = try {
  100. val appInfo = context.packageManager.getApplicationInfo(pkgName, PackageManager.GET_META_DATA)
  101. val appResources = context.packageManager.getResourcesForApplication(appInfo)
  102. Result.Success(
  103. appResources.getDrawableForDensity(appInfo.icon, density, null)!!
  104. .toBitmap()
  105. .asImageBitmap(),
  106. )
  107. } catch (e: Exception) {
  108. Result.Error
  109. }
  110. }
  111. }
  112. }
  113. sealed class Result<out T> {
  114. object Loading : Result<Nothing>()
  115. object Error : Result<Nothing>()
  116. data class Success<out T>(val value: T) : Result<T>()
  117. }