mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 13:27:45 -07:00
Android: Replace Glide with Coil image loading
This commit is contained in:
parent
e6583f8bec
commit
28faca63a6
@ -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'
|
||||
}
|
||||
|
@ -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<GameViewHolder>(),
|
||||
class GameAdapter(private val mActivity: FragmentActivity) : RecyclerView.Adapter<GameViewHolder>(),
|
||||
View.OnClickListener, OnLongClickListener {
|
||||
private var mGameFiles: List<GameFile>
|
||||
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<GameFile> = ArrayList()
|
||||
|
||||
/**
|
||||
* Called by the LayoutManager when it is necessary to create a new view.
|
||||
@ -65,7 +58,33 @@ class GameAdapter(activity: Activity) : RecyclerView.Adapter<GameViewHolder>(),
|
||||
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<GameViewHolder>(),
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Drawable?> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any,
|
||||
target: Target<Drawable?>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
enableInnerTitle(gameViewHolder)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable?,
|
||||
model: Any,
|
||||
target: Target<Drawable?>,
|
||||
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<Drawable?> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any,
|
||||
target: Target<Drawable?>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
enableInnerTitle(gameViewHolder)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable?,
|
||||
model: Any,
|
||||
target: Target<Drawable?>,
|
||||
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<Drawable?> {
|
||||
override fun onLoadFailed(
|
||||
e: GlideException?,
|
||||
model: Any,
|
||||
target: Target<Drawable?>,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
enableInnerTitle(gameViewHolder)
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onResourceReady(
|
||||
resource: Drawable?,
|
||||
model: Any,
|
||||
target: Target<Drawable?>,
|
||||
dataSource: DataSource,
|
||||
isFirstResource: Boolean
|
||||
): Boolean {
|
||||
disableInnerTitle(gameViewHolder)
|
||||
return false
|
||||
}
|
||||
})
|
||||
.into(object : CustomTarget<Drawable?>() {
|
||||
override fun onLoadCleared(placeholder: Drawable?) {}
|
||||
override fun onResourceReady(
|
||||
resource: Drawable,
|
||||
transition: Transition<in Drawable?>?
|
||||
) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user