diff --git a/Source/Android/app/build.gradle b/Source/Android/app/build.gradle index 3b1f8c3ca4..11349147e8 100644 --- a/Source/Android/app/build.gradle +++ b/Source/Android/app/build.gradle @@ -155,7 +155,7 @@ dependencies { implementation 'com.android.volley:volley:1.2.1' // For loading game covers from disk and GameTDB - implementation 'com.github.bumptech.glide:glide:4.13.2' + implementation 'io.coil-kt:coil:2.2.2' implementation 'com.nononsenseapps:filepicker:4.2.1' } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.kt index e3fa287967..919ded6fef 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameAdapter.kt @@ -3,7 +3,6 @@ package org.dolphinemu.dolphinemu.adapters import android.annotation.SuppressLint -import android.app.Activity import androidx.recyclerview.widget.RecyclerView import org.dolphinemu.dolphinemu.adapters.GameAdapter.GameViewHolder import android.view.View.OnLongClickListener @@ -11,29 +10,23 @@ import org.dolphinemu.dolphinemu.model.GameFile import android.view.ViewGroup import android.view.LayoutInflater import android.view.View -import org.dolphinemu.dolphinemu.utils.GlideUtils import org.dolphinemu.dolphinemu.services.GameFileCacheManager import org.dolphinemu.dolphinemu.R import android.view.animation.AnimationUtils import org.dolphinemu.dolphinemu.activities.EmulationActivity import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.dolphinemu.dolphinemu.databinding.CardGameBinding import org.dolphinemu.dolphinemu.dialogs.GamePropertiesDialog +import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting +import org.dolphinemu.dolphinemu.utils.CoilUtils import java.util.ArrayList -class GameAdapter(activity: Activity) : RecyclerView.Adapter(), +class GameAdapter(private val mActivity: FragmentActivity) : RecyclerView.Adapter(), View.OnClickListener, OnLongClickListener { - private var mGameFiles: List - private val mActivity: Activity - - /** - * Initializes the adapter's observer, which watches for changes to the dataset. The adapter will - * display no data until swapDataSet is called. - */ - init { - mGameFiles = ArrayList() - mActivity = activity - } + private var mGameFiles: List = ArrayList() /** * Called by the LayoutManager when it is necessary to create a new view. @@ -65,7 +58,33 @@ class GameAdapter(activity: Activity) : RecyclerView.Adapter(), val context = holder.itemView.context val gameFile = mGameFiles[position] - GlideUtils.loadGameCover(holder, holder.binding.imageGameScreen, gameFile, mActivity) + holder.apply { + if (BooleanSetting.MAIN_SHOW_GAME_TITLES.booleanGlobal) { + binding.textGameTitle.text = gameFile.title + binding.textGameTitle.visibility = View.VISIBLE + binding.textGameTitleInner.visibility = View.GONE + binding.textGameCaption.visibility = View.VISIBLE + } else { + binding.textGameTitleInner.text = gameFile.title + binding.textGameTitleInner.visibility = View.VISIBLE + binding.textGameTitle.visibility = View.GONE + binding.textGameCaption.visibility = View.GONE + } + } + + mActivity.lifecycleScope.launchWhenStarted { + withContext(Dispatchers.IO) { + val customCoverUri = CoilUtils.findCustomCover(gameFile) + withContext(Dispatchers.Main) { + CoilUtils.loadGameCover( + holder, + holder.binding.imageGameScreen, + gameFile, + customCoverUri + ) + } + } + } val animateIn = AnimationUtils.loadAnimation(context, R.anim.anim_card_game_in) animateIn.fillAfter = true @@ -86,15 +105,11 @@ class GameAdapter(activity: Activity) : RecyclerView.Adapter(), } } - class GameViewHolder(binding: CardGameBinding) : RecyclerView.ViewHolder(binding.root) { + class GameViewHolder(var binding: CardGameBinding) : RecyclerView.ViewHolder(binding.root) { var gameFile: GameFile? = null - @JvmField - var binding: CardGameBinding - init { binding.root.tag = this - this.binding = binding } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.kt index 66a249e50d..4f062538f9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/adapters/GameRowPresenter.kt @@ -7,20 +7,24 @@ import android.view.ViewGroup import androidx.leanback.widget.ImageCardView import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder import org.dolphinemu.dolphinemu.model.GameFile -import org.dolphinemu.dolphinemu.utils.GlideUtils import org.dolphinemu.dolphinemu.services.GameFileCacheManager import org.dolphinemu.dolphinemu.R import android.view.View import androidx.core.content.ContextCompat import android.widget.ImageView import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import org.dolphinemu.dolphinemu.dialogs.GamePropertiesDialog +import org.dolphinemu.dolphinemu.utils.CoilUtils /** * The Leanback library / docs call this a Presenter, but it works very * similarly to a RecyclerView.Adapter. */ -class GameRowPresenter : Presenter() { +class GameRowPresenter(private val mActivity: FragmentActivity) : Presenter() { + override fun onCreateViewHolder(parent: ViewGroup): ViewHolder { // Create a new view. val gameCard = ImageCardView(parent.context) @@ -63,7 +67,20 @@ class GameRowPresenter : Presenter() { holder.cardParent.contentText = gameFile.company } } - GlideUtils.loadGameCover(null, holder.imageScreenshot, gameFile, null) + + mActivity.lifecycleScope.launchWhenStarted { + withContext(Dispatchers.IO) { + val customCoverUri = CoilUtils.findCustomCover(gameFile) + withContext(Dispatchers.Main) { + CoilUtils.loadGameCover( + null, + holder.imageScreenshot, + gameFile, + customCoverUri + ) + } + } + } } override fun onUnbindViewHolder(viewHolder: ViewHolder) { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.kt index 5c7cf2ed6a..83d69ebbb4 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/GameDetailsDialog.kt @@ -3,17 +3,23 @@ package org.dolphinemu.dolphinemu.dialogs import android.app.Dialog +import android.graphics.Bitmap import android.os.Bundle import android.view.View +import android.widget.ImageView import org.dolphinemu.dolphinemu.services.GameFileCacheManager import org.dolphinemu.dolphinemu.R import com.google.android.material.dialog.MaterialAlertDialogBuilder import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.DialogFragment +import androidx.lifecycle.lifecycleScope +import coil.imageLoader +import coil.request.ImageRequest +import kotlinx.coroutines.launch import org.dolphinemu.dolphinemu.NativeLibrary import org.dolphinemu.dolphinemu.databinding.DialogGameDetailsBinding import org.dolphinemu.dolphinemu.databinding.DialogGameDetailsTvBinding -import org.dolphinemu.dolphinemu.utils.GlideUtils +import org.dolphinemu.dolphinemu.model.GameFile class GameDetailsDialog : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { @@ -73,7 +79,9 @@ class GameDetailsDialog : DialogFragment() { } } - GlideUtils.loadGameBanner(binding.banner, gameFile) + this.lifecycleScope.launch { + loadGameBanner(binding.banner, gameFile) + } builder.setView(binding.root) } else { @@ -123,13 +131,35 @@ class GameDetailsDialog : DialogFragment() { } } - GlideUtils.loadGameBanner(tvBinding.banner, gameFile) + this.lifecycleScope.launch { + loadGameBanner(tvBinding.banner, gameFile) + } builder.setView(tvBinding.root) } return builder.create() } + private suspend fun loadGameBanner(imageView: ImageView, gameFile: GameFile) { + val vector = gameFile.banner + val width = gameFile.bannerWidth + val height = gameFile.bannerHeight + + imageView.scaleType = ImageView.ScaleType.FIT_CENTER + val request = ImageRequest.Builder(imageView.context) + .target(imageView) + .error(R.drawable.no_banner) + + if (width > 0 && height > 0) { + val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + bitmap.setPixels(vector, 0, width, 0, 0, width, height) + request.data(bitmap) + } else { + request.data(R.drawable.no_banner) + } + imageView.context.imageLoader.execute(request.build()) + } + companion object { private const val ARG_GAME_PATH = "game_path" diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/GridOptionDialogFragment.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/GridOptionDialogFragment.kt index 94dda3f579..5f51038462 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/GridOptionDialogFragment.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/GridOptionDialogFragment.kt @@ -1,5 +1,6 @@ package org.dolphinemu.dolphinemu.fragments +import android.app.Activity import com.google.android.material.bottomsheet.BottomSheetDialogFragment import android.view.LayoutInflater import android.view.ViewGroup @@ -88,7 +89,7 @@ class GridOptionDialogFragment : BottomSheetDialogFragment() { NativeConfig.LAYER_BASE, mBindingMobile.switchDownloadCovers.isChecked ) - mView.reloadGrid() + (mView as Activity).recreate() } } @@ -118,7 +119,7 @@ class GridOptionDialogFragment : BottomSheetDialogFragment() { NativeConfig.LAYER_BASE, mBindingTv.switchDownloadCovers.isChecked ) - mView.reloadGrid() + (mView as Activity).recreate() } } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFile.java index b8de619e31..26f4c169ce 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFile.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/model/GameFile.java @@ -2,12 +2,15 @@ package org.dolphinemu.dolphinemu.model; -import android.content.Context; - import androidx.annotation.Keep; public class GameFile { + public static int REGION_NTSC_J = 0; + public static int REGION_NTSC_U = 1; + public static int REGION_PAL = 2; + public static int REGION_NTSC_K = 4; + @Keep private long mPointer; @@ -68,11 +71,6 @@ public class GameFile public native int getBannerHeight(); - public String getCoverPath(Context context) - { - return context.getExternalCacheDir().getPath() + "/GameCovers/" + getGameTdbId() + ".png"; - } - public String getCustomCoverPath() { return getPath().substring(0, getPath().lastIndexOf(".")) + ".cover.png"; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java index 0e1f55154b..2da1b8591d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/ui/main/TvMainActivity.java @@ -349,7 +349,7 @@ public final class TvMainActivity extends FragmentActivity } // Create an adapter for this row. - ArrayObjectAdapter row = new ArrayObjectAdapter(new GameRowPresenter()); + ArrayObjectAdapter row = new ArrayObjectAdapter(new GameRowPresenter(this)); row.addAll(0, gameFiles); // Keep a reference to the row in case we need to refresh it. diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoilUtils.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoilUtils.kt new file mode 100644 index 0000000000..233f620d23 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoilUtils.kt @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.utils + +import android.net.Uri +import android.view.View +import android.widget.ImageView +import coil.load +import coil.target.ImageViewTarget +import org.dolphinemu.dolphinemu.R +import org.dolphinemu.dolphinemu.adapters.GameAdapter.GameViewHolder +import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting +import org.dolphinemu.dolphinemu.model.GameFile +import java.io.File +import java.io.FileNotFoundException + +object CoilUtils { + fun loadGameCover( + gameViewHolder: GameViewHolder?, + imageView: ImageView, + gameFile: GameFile, + customCoverUri: Uri? + ) { + imageView.scaleType = ImageView.ScaleType.FIT_CENTER + val imageTarget = ImageViewTarget(imageView) + if (customCoverUri != null) { + imageView.load(customCoverUri) { + error(R.drawable.no_banner) + target( + onSuccess = { success -> + disableInnerTitle(gameViewHolder) + imageTarget.drawable = success + }, + onError = { error -> + enableInnerTitle(gameViewHolder) + imageTarget.drawable = error + } + ) + } + } else if (BooleanSetting.MAIN_USE_GAME_COVERS.booleanGlobal) { + imageView.load(CoverHelper.buildGameTDBUrl(gameFile, CoverHelper.getRegion(gameFile))) { + error(R.drawable.no_banner) + target( + onSuccess = { success -> + disableInnerTitle(gameViewHolder) + imageTarget.drawable = success + }, + onError = { error -> + enableInnerTitle(gameViewHolder) + imageTarget.drawable = error + } + ) + } + } else { + imageView.load(R.drawable.no_banner) + enableInnerTitle(gameViewHolder) + } + } + + private fun enableInnerTitle(gameViewHolder: GameViewHolder?) { + if (gameViewHolder != null && !BooleanSetting.MAIN_SHOW_GAME_TITLES.booleanGlobal) { + gameViewHolder.binding.textGameTitleInner.visibility = View.VISIBLE + } + } + + private fun disableInnerTitle(gameViewHolder: GameViewHolder?) { + if (gameViewHolder != null && !BooleanSetting.MAIN_SHOW_GAME_TITLES.booleanGlobal) { + gameViewHolder.binding.textGameTitleInner.visibility = View.GONE + } + } + + fun findCustomCover(gameFile: GameFile): Uri? { + val customCoverPath = gameFile.customCoverPath + var customCoverUri: Uri? = null + var customCoverExists = false + if (ContentHandler.isContentUri(customCoverPath)) { + try { + customCoverUri = ContentHandler.unmangle(customCoverPath) + customCoverExists = true + } catch (ignored: FileNotFoundException) { + } catch (ignored: SecurityException) { + // Let customCoverExists remain false + } + } else { + customCoverUri = Uri.parse(customCoverPath) + customCoverExists = File(customCoverPath).exists() + } + + return if (customCoverExists) { + customCoverUri + } else { + null + } + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoverHelper.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoverHelper.kt index f1deeb3730..9359d38eb3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoverHelper.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/CoverHelper.kt @@ -3,44 +3,34 @@ package org.dolphinemu.dolphinemu.utils import org.dolphinemu.dolphinemu.model.GameFile -import android.graphics.Bitmap -import java.io.FileOutputStream -import java.lang.Exception object CoverHelper { + @JvmStatic fun buildGameTDBUrl(game: GameFile, region: String?): String { val baseUrl = "https://art.gametdb.com/wii/cover/%s/%s.png" return String.format(baseUrl, region, game.gameTdbId) } + @JvmStatic fun getRegion(game: GameFile): String { val region: String = when (game.region) { - 0 -> "JA" - 1 -> "US" - 4 -> "KO" - 2 -> when (game.country) { - 3 -> "AU" - 4 -> "FR" - 5 -> "DE" - 6 -> "IT" - 8 -> "NL" - 9 -> "RU" - 10 -> "ES" - 0 -> "EN" + GameFile.REGION_NTSC_J -> "JA" + GameFile.REGION_NTSC_U -> "US" + GameFile.REGION_NTSC_K -> "KO" + GameFile.REGION_PAL -> when (game.country) { + 3 -> "AU" // Australia + 4 -> "FR" // France + 5 -> "DE" // Germany + 6 -> "IT" // Italy + 8 -> "NL" // Netherlands + 9 -> "RU" // Russia + 10 -> "ES" // Spain + 0 -> "EN" // Europe else -> "EN" } - 3 -> "EN" + 3 -> "EN" // Unknown else -> "EN" } return region } - - fun saveCover(cover: Bitmap, path: String?) { - try { - val out = FileOutputStream(path) - cover.compress(Bitmap.CompressFormat.PNG, 100, out) - out.close() - } catch (ignored: Exception) { - } - } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GlideUtils.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GlideUtils.kt deleted file mode 100644 index ba2b9e344f..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/GlideUtils.kt +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.utils - -import org.dolphinemu.dolphinemu.utils.CoverHelper.buildGameTDBUrl -import org.dolphinemu.dolphinemu.utils.CoverHelper.getRegion -import org.dolphinemu.dolphinemu.utils.CoverHelper.saveCover -import android.os.Looper -import org.dolphinemu.dolphinemu.model.GameFile -import com.bumptech.glide.Glide -import com.bumptech.glide.load.engine.DiskCacheStrategy -import org.dolphinemu.dolphinemu.R -import org.dolphinemu.dolphinemu.adapters.GameAdapter.GameViewHolder -import android.app.Activity -import android.graphics.Bitmap -import android.graphics.drawable.BitmapDrawable -import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting -import com.bumptech.glide.request.RequestListener -import android.graphics.drawable.Drawable -import android.net.Uri -import android.os.Handler -import android.view.View -import android.widget.ImageView -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.target.CustomTarget -import com.bumptech.glide.request.target.Target -import com.bumptech.glide.request.transition.Transition -import java.io.File -import java.io.FileNotFoundException -import java.util.concurrent.Executors - -object GlideUtils { - private val saveCoverExecutor = Executors.newSingleThreadExecutor() - private val unmangleExecutor = Executors.newSingleThreadExecutor() - private val unmangleHandler = Handler(Looper.getMainLooper()) - - fun loadGameBanner(imageView: ImageView, gameFile: GameFile) { - val context = imageView.context - val vector = gameFile.banner - val width = gameFile.bannerWidth - val height = gameFile.bannerHeight - if (width > 0 && height > 0) { - val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) - bitmap.setPixels(vector, 0, width, 0, 0, width, height) - Glide.with(context) - .load(bitmap) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerCrop() - .into(imageView) - } else { - Glide.with(context) - .load(R.drawable.no_banner) - .into(imageView) - } - } - - fun loadGameCover( - gameViewHolder: GameViewHolder?, - imageView: ImageView, - gameFile: GameFile, - activity: Activity? - ) { - gameViewHolder?.apply { - if (BooleanSetting.MAIN_SHOW_GAME_TITLES.booleanGlobal) { - binding.textGameTitle.text = gameFile.title - binding.textGameTitle.visibility = View.VISIBLE - binding.textGameTitleInner.visibility = View.GONE - binding.textGameCaption.visibility = View.VISIBLE - } else { - binding.textGameTitleInner.text = gameFile.title - binding.textGameTitle.visibility = View.GONE - binding.textGameCaption.visibility = View.GONE - } - } - - unmangleExecutor.execute { - val customCoverPath = gameFile.customCoverPath - var customCoverUri: Uri? = null - var customCoverExists = false - if (ContentHandler.isContentUri(customCoverPath)) { - try { - customCoverUri = ContentHandler.unmangle(customCoverPath) - customCoverExists = true - } catch (ignored: FileNotFoundException) { - // Let customCoverExists remain false - } catch (ignored: SecurityException) { - } - } else { - customCoverUri = Uri.parse(customCoverPath) - customCoverExists = File(customCoverPath).exists() - } - - val context = imageView.context - val finalCustomCoverExists = customCoverExists - val finalCustomCoverUri = customCoverUri - - val cover = File(gameFile.getCoverPath(context)) - val cachedCoverExists = cover.exists() - unmangleHandler.post { - // We can't get a reference to the current activity in the TV version. - // Luckily it won't attempt to start loads on destroyed activities. - if (activity != null) { - // We can't start an image load on a destroyed activity - if (activity.isDestroyed) { - return@post - } - } - - if (finalCustomCoverExists) { - Glide.with(imageView) - .load(finalCustomCoverUri) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerCrop() - .error(R.drawable.no_banner) - .listener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any, - target: Target, - isFirstResource: Boolean - ): Boolean { - enableInnerTitle(gameViewHolder) - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any, - target: Target, - dataSource: DataSource, - isFirstResource: Boolean - ): Boolean { - disableInnerTitle(gameViewHolder) - return false - } - }) - .into(imageView) - } else if (cachedCoverExists) { - Glide.with(imageView) - .load(cover) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerCrop() - .error(R.drawable.no_banner) - .listener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any, - target: Target, - isFirstResource: Boolean - ): Boolean { - enableInnerTitle(gameViewHolder) - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any, - target: Target, - dataSource: DataSource, - isFirstResource: Boolean - ): Boolean { - disableInnerTitle(gameViewHolder) - return false - } - }) - .into(imageView) - } else if (BooleanSetting.MAIN_USE_GAME_COVERS.booleanGlobal) { - Glide.with(context) - .load(buildGameTDBUrl(gameFile, getRegion(gameFile))) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .centerCrop() - .error(R.drawable.no_banner) - .listener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any, - target: Target, - isFirstResource: Boolean - ): Boolean { - enableInnerTitle(gameViewHolder) - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any, - target: Target, - dataSource: DataSource, - isFirstResource: Boolean - ): Boolean { - disableInnerTitle(gameViewHolder) - return false - } - }) - .into(object : CustomTarget() { - override fun onLoadCleared(placeholder: Drawable?) {} - override fun onResourceReady( - resource: Drawable, - transition: Transition? - ) { - val savedCover = (resource as BitmapDrawable).bitmap - saveCoverExecutor.execute { - saveCover( - savedCover, - gameFile.getCoverPath(context) - ) - } - imageView.setImageBitmap(savedCover) - } - }) - } else { - Glide.with(imageView.context) - .load(R.drawable.no_banner) - .into(imageView) - enableInnerTitle(gameViewHolder) - } - } - } - } - - private fun enableInnerTitle(gameViewHolder: GameViewHolder?) { - if (gameViewHolder != null && !BooleanSetting.MAIN_SHOW_GAME_TITLES.booleanGlobal) { - gameViewHolder.binding.textGameTitleInner.visibility = View.VISIBLE - } - } - - private fun disableInnerTitle(gameViewHolder: GameViewHolder?) { - if (gameViewHolder != null && !BooleanSetting.MAIN_SHOW_GAME_TITLES.booleanGlobal) { - gameViewHolder.binding.textGameTitleInner.visibility = View.GONE - } - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/TvUtil.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/TvUtil.java index 940542ef49..57c567715c 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/TvUtil.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/TvUtil.java @@ -186,9 +186,9 @@ public class TvUtil } } - if (contentUri == null && (cover = new File(game.getCoverPath(context))).exists()) + if (contentUri == null) { - contentUri = getUriForFile(context, getFileProvider(context), cover); + contentUri = Uri.parse(CoverHelper.buildGameTDBUrl(game, CoverHelper.getRegion(game))); } context.grantUriPermission(LEANBACK_PACKAGE, contentUri, FLAG_GRANT_READ_URI_PERMISSION);