Jelajahi Sumber

android: Create custom game icon loader

Charles Lombardo 2 tahun lalu
induk
melakukan
270f430f70

+ 2 - 22
src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt

@@ -3,8 +3,6 @@
 
 
 package org.yuzu.yuzu_emu.adapters
 package org.yuzu.yuzu_emu.adapters
 
 
-import android.graphics.Bitmap
-import android.graphics.BitmapFactory
 import android.net.Uri
 import android.net.Uri
 import android.text.TextUtils
 import android.text.TextUtils
 import android.view.LayoutInflater
 import android.view.LayoutInflater
@@ -15,23 +13,20 @@ import android.widget.Toast
 import androidx.appcompat.app.AppCompatActivity
 import androidx.appcompat.app.AppCompatActivity
 import androidx.documentfile.provider.DocumentFile
 import androidx.documentfile.provider.DocumentFile
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
 import androidx.navigation.findNavController
 import androidx.navigation.findNavController
 import androidx.preference.PreferenceManager
 import androidx.preference.PreferenceManager
 import androidx.recyclerview.widget.AsyncDifferConfig
 import androidx.recyclerview.widget.AsyncDifferConfig
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.ListAdapter
 import androidx.recyclerview.widget.ListAdapter
 import androidx.recyclerview.widget.RecyclerView
 import androidx.recyclerview.widget.RecyclerView
-import coil.load
-import kotlinx.coroutines.launch
 import org.yuzu.yuzu_emu.HomeNavigationDirections
 import org.yuzu.yuzu_emu.HomeNavigationDirections
-import org.yuzu.yuzu_emu.NativeLibrary
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.R
 import org.yuzu.yuzu_emu.YuzuApplication
 import org.yuzu.yuzu_emu.YuzuApplication
 import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
 import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder
 import org.yuzu.yuzu_emu.databinding.CardGameBinding
 import org.yuzu.yuzu_emu.databinding.CardGameBinding
 import org.yuzu.yuzu_emu.model.Game
 import org.yuzu.yuzu_emu.model.Game
 import org.yuzu.yuzu_emu.model.GamesViewModel
 import org.yuzu.yuzu_emu.model.GamesViewModel
+import org.yuzu.yuzu_emu.utils.GameIconUtils
 
 
 class GameAdapter(private val activity: AppCompatActivity) :
 class GameAdapter(private val activity: AppCompatActivity) :
     ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
     ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
@@ -98,12 +93,7 @@ class GameAdapter(private val activity: AppCompatActivity) :
             this.game = game
             this.game = game
 
 
             binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
             binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
-            activity.lifecycleScope.launch {
-                val bitmap = decodeGameIcon(game.path)
-                binding.imageGameScreen.load(bitmap) {
-                    error(R.drawable.default_icon)
-                }
-            }
+            GameIconUtils.loadGameIcon(game, binding.imageGameScreen)
 
 
             binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
             binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
 
 
@@ -126,14 +116,4 @@ class GameAdapter(private val activity: AppCompatActivity) :
             return oldItem == newItem
             return oldItem == newItem
         }
         }
     }
     }
-
-    private fun decodeGameIcon(uri: String): Bitmap? {
-        val data = NativeLibrary.getIcon(uri)
-        return BitmapFactory.decodeByteArray(
-            data,
-            0,
-            data.size,
-            BitmapFactory.Options()
-        )
-    }
 }
 }

+ 77 - 0
src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt

@@ -0,0 +1,77 @@
+// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.yuzu.yuzu_emu.utils
+
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.widget.ImageView
+import androidx.core.graphics.drawable.toDrawable
+import coil.ImageLoader
+import coil.decode.DataSource
+import coil.fetch.DrawableResult
+import coil.fetch.FetchResult
+import coil.fetch.Fetcher
+import coil.key.Keyer
+import coil.memory.MemoryCache
+import coil.request.ImageRequest
+import coil.request.Options
+import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
+import org.yuzu.yuzu_emu.model.Game
+
+class GameIconFetcher(
+    private val game: Game,
+    private val options: Options
+) : Fetcher {
+    override suspend fun fetch(): FetchResult {
+        return DrawableResult(
+            drawable = decodeGameIcon(game.path)!!.toDrawable(options.context.resources),
+            isSampled = false,
+            dataSource = DataSource.DISK
+        )
+    }
+
+    private fun decodeGameIcon(uri: String): Bitmap? {
+        val data = NativeLibrary.getIcon(uri)
+        return BitmapFactory.decodeByteArray(
+            data,
+            0,
+            data.size,
+            BitmapFactory.Options()
+        )
+    }
+
+    class Factory : Fetcher.Factory<Game> {
+        override fun create(data: Game, options: Options, imageLoader: ImageLoader): Fetcher =
+            GameIconFetcher(data, options)
+    }
+}
+
+class GameIconKeyer : Keyer<Game> {
+    override fun key(data: Game, options: Options): String = data.path
+}
+
+object GameIconUtils {
+    private val imageLoader = ImageLoader.Builder(YuzuApplication.appContext)
+        .components {
+            add(GameIconKeyer())
+            add(GameIconFetcher.Factory())
+        }
+        .memoryCache {
+            MemoryCache.Builder(YuzuApplication.appContext)
+                .maxSizePercent(0.25)
+                .build()
+        }
+        .build()
+
+    fun loadGameIcon(game: Game, imageView: ImageView) {
+        val request = ImageRequest.Builder(YuzuApplication.appContext)
+            .data(game)
+            .target(imageView)
+            .error(R.drawable.default_icon)
+            .build()
+        imageLoader.enqueue(request)
+    }
+}