From 270f430f70d9325cf4dc3520ad359f14b9da6880 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sat, 12 Aug 2023 19:09:30 -0400 Subject: [PATCH] android: Create custom game icon loader --- .../org/yuzu/yuzu_emu/adapters/GameAdapter.kt | 24 +----- .../org/yuzu/yuzu_emu/utils/GameIconUtils.kt | 77 +++++++++++++++++++ 2 files changed, 79 insertions(+), 22 deletions(-) create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt index e91277d35..13359ef36 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/adapters/GameAdapter.kt @@ -3,8 +3,6 @@ package org.yuzu.yuzu_emu.adapters -import android.graphics.Bitmap -import android.graphics.BitmapFactory import android.net.Uri import android.text.TextUtils import android.view.LayoutInflater @@ -15,23 +13,20 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.preference.PreferenceManager import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView -import coil.load -import kotlinx.coroutines.launch import org.yuzu.yuzu_emu.HomeNavigationDirections -import org.yuzu.yuzu_emu.NativeLibrary import org.yuzu.yuzu_emu.R import org.yuzu.yuzu_emu.YuzuApplication import org.yuzu.yuzu_emu.adapters.GameAdapter.GameViewHolder import org.yuzu.yuzu_emu.databinding.CardGameBinding import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.model.GamesViewModel +import org.yuzu.yuzu_emu.utils.GameIconUtils class GameAdapter(private val activity: AppCompatActivity) : ListAdapter(AsyncDifferConfig.Builder(DiffCallback()).build()), @@ -98,12 +93,7 @@ class GameAdapter(private val activity: AppCompatActivity) : this.game = game 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(), " ") @@ -126,14 +116,4 @@ class GameAdapter(private val activity: AppCompatActivity) : return oldItem == newItem } } - - private fun decodeGameIcon(uri: String): Bitmap? { - val data = NativeLibrary.getIcon(uri) - return BitmapFactory.decodeByteArray( - data, - 0, - data.size, - BitmapFactory.Options() - ) - } } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/GameIconUtils.kt new file mode 100644 index 000000000..c0fe596d7 --- /dev/null +++ b/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 { + override fun create(data: Game, options: Options, imageLoader: ImageLoader): Fetcher = + GameIconFetcher(data, options) + } +} + +class GameIconKeyer : Keyer { + 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) + } +}