Android: Use custom image loader for game covers
This fixes a bug where custom cover loading was initiated but would finish by the time another image view would be in the place of the previous one.
This commit is contained in:
parent
579ccb0710
commit
f13b29196d
|
@ -25,7 +25,7 @@ import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting
|
|||
import org.dolphinemu.dolphinemu.utils.CoilUtils
|
||||
import java.util.ArrayList
|
||||
|
||||
class GameAdapter(private val mActivity: FragmentActivity) : RecyclerView.Adapter<GameViewHolder>(),
|
||||
class GameAdapter : RecyclerView.Adapter<GameViewHolder>(),
|
||||
View.OnClickListener, OnLongClickListener {
|
||||
private var mGameFiles: List<GameFile> = ArrayList()
|
||||
|
||||
|
@ -72,20 +72,7 @@ class GameAdapter(private val mActivity: FragmentActivity) : RecyclerView.Adapte
|
|||
binding.textGameCaption.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
mActivity.lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val customCoverUri = CoilUtils.findCustomCover(gameFile)
|
||||
withContext(Dispatchers.Main) {
|
||||
CoilUtils.loadGameCover(
|
||||
holder,
|
||||
holder.binding.imageGameScreen,
|
||||
gameFile,
|
||||
customCoverUri
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
CoilUtils.loadGameCover(holder, holder.binding.imageGameScreen, gameFile)
|
||||
|
||||
val animateIn = AnimationUtils.loadAnimation(context, R.anim.anim_card_game_in)
|
||||
animateIn.fillAfter = true
|
||||
|
|
|
@ -24,7 +24,7 @@ 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(private val mActivity: FragmentActivity) : Presenter() {
|
||||
class GameRowPresenter : Presenter() {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup): ViewHolder {
|
||||
// Create a new view.
|
||||
|
@ -69,20 +69,7 @@ class GameRowPresenter(private val mActivity: FragmentActivity) : Presenter() {
|
|||
holder.cardParent.contentText = gameFile.getCompany()
|
||||
}
|
||||
}
|
||||
|
||||
mActivity.lifecycleScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
val customCoverUri = CoilUtils.findCustomCover(gameFile)
|
||||
withContext(Dispatchers.Main) {
|
||||
CoilUtils.loadGameCover(
|
||||
null,
|
||||
holder.imageScreenshot,
|
||||
gameFile,
|
||||
customCoverUri
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
CoilUtils.loadGameCover(null, holder.imageScreenshot, gameFile)
|
||||
}
|
||||
|
||||
override fun onUnbindViewHolder(viewHolder: ViewHolder) {
|
||||
|
|
|
@ -268,7 +268,7 @@ class TvMainActivity : FragmentActivity(), MainView, OnRefreshListener {
|
|||
}
|
||||
|
||||
// Create an adapter for this row.
|
||||
val row = ArrayObjectAdapter(GameRowPresenter(this))
|
||||
val row = ArrayObjectAdapter(GameRowPresenter())
|
||||
row.addAll(0, gameFiles)
|
||||
|
||||
// Keep a reference to the row in case we need to refresh it.
|
||||
|
|
|
@ -37,7 +37,7 @@ class PlatformGamesFragment : Fragment(), PlatformGamesView {
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
swipeRefresh = binding.swipeRefresh
|
||||
val gameAdapter = GameAdapter(requireActivity())
|
||||
val gameAdapter = GameAdapter()
|
||||
gameAdapter.stateRestorationPolicy =
|
||||
RecyclerView.Adapter.StateRestorationPolicy.PREVENT_WHEN_EMPTY
|
||||
|
||||
|
|
|
@ -2,11 +2,22 @@
|
|||
|
||||
package org.dolphinemu.dolphinemu.utils
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import coil.load
|
||||
import coil.target.ImageViewTarget
|
||||
import coil.ImageLoader
|
||||
import coil.decode.DataSource
|
||||
import coil.executeBlocking
|
||||
import coil.fetch.DrawableResult
|
||||
import coil.fetch.FetchResult
|
||||
import coil.fetch.Fetcher
|
||||
import coil.imageLoader
|
||||
import coil.key.Keyer
|
||||
import coil.memory.MemoryCache
|
||||
import coil.request.ImageRequest
|
||||
import coil.request.Options
|
||||
import org.dolphinemu.dolphinemu.DolphinApplication
|
||||
import org.dolphinemu.dolphinemu.R
|
||||
import org.dolphinemu.dolphinemu.adapters.GameAdapter.GameViewHolder
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting
|
||||
|
@ -14,47 +25,75 @@ import org.dolphinemu.dolphinemu.model.GameFile
|
|||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
class GameCoverFetcher(
|
||||
private val game: GameFile,
|
||||
private val options: Options
|
||||
) : Fetcher {
|
||||
override suspend fun fetch(): FetchResult {
|
||||
val customCoverUri = CoilUtils.findCustomCover(game)
|
||||
val builder = ImageRequest.Builder(DolphinApplication.getAppContext())
|
||||
var dataSource = DataSource.DISK
|
||||
val drawable: Drawable? = if (customCoverUri != null) {
|
||||
val request = builder.data(customCoverUri).error(R.drawable.no_banner).build()
|
||||
DolphinApplication.getAppContext().imageLoader.executeBlocking(request).drawable
|
||||
} else if (BooleanSetting.MAIN_USE_GAME_COVERS.boolean) {
|
||||
val request = builder.data(
|
||||
CoverHelper.buildGameTDBUrl(game, CoverHelper.getRegion(game))
|
||||
).error(R.drawable.no_banner).build()
|
||||
dataSource = DataSource.NETWORK
|
||||
DolphinApplication.getAppContext().imageLoader.executeBlocking(request).drawable
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return DrawableResult(
|
||||
// In the case where the drawable is null, intentionally throw an NPE. This tells Coil
|
||||
// to load the error drawable.
|
||||
drawable = drawable!!,
|
||||
isSampled = false,
|
||||
dataSource = dataSource
|
||||
)
|
||||
}
|
||||
|
||||
class Factory : Fetcher.Factory<GameFile> {
|
||||
override fun create(data: GameFile, options: Options, imageLoader: ImageLoader): Fetcher =
|
||||
GameCoverFetcher(data, options)
|
||||
}
|
||||
}
|
||||
|
||||
class GameCoverKeyer : Keyer<GameFile> {
|
||||
override fun key(data: GameFile, options: Options): String = data.getGameId() + data.getPath()
|
||||
}
|
||||
|
||||
object CoilUtils {
|
||||
private val imageLoader = ImageLoader.Builder(DolphinApplication.getAppContext())
|
||||
.components {
|
||||
add(GameCoverKeyer())
|
||||
add(GameCoverFetcher.Factory())
|
||||
}
|
||||
.memoryCache {
|
||||
MemoryCache.Builder(DolphinApplication.getAppContext())
|
||||
.maxSizePercent(0.25)
|
||||
.build()
|
||||
}
|
||||
.build()
|
||||
|
||||
fun loadGameCover(
|
||||
gameViewHolder: GameViewHolder?,
|
||||
imageView: ImageView,
|
||||
gameFile: GameFile,
|
||||
customCoverUri: Uri?
|
||||
gameFile: GameFile
|
||||
) {
|
||||
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.boolean) {
|
||||
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)
|
||||
}
|
||||
val imageRequest = ImageRequest.Builder(imageView.context)
|
||||
.data(gameFile)
|
||||
.error(R.drawable.no_banner)
|
||||
.target(imageView)
|
||||
.listener(
|
||||
onSuccess = { _, _ -> disableInnerTitle(gameViewHolder) },
|
||||
onError = { _, _ -> enableInnerTitle(gameViewHolder) }
|
||||
)
|
||||
.build()
|
||||
imageLoader.enqueue(imageRequest)
|
||||
}
|
||||
|
||||
private fun enableInnerTitle(gameViewHolder: GameViewHolder?) {
|
||||
|
|
Loading…
Reference in New Issue