android: Refactor async diff adapters to use AbstractDiffAdapter
This commit is contained in:
		@@ -5,48 +5,28 @@ package org.yuzu.yuzu_emu.adapters
 | 
			
		||||
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import androidx.recyclerview.widget.AsyncDifferConfig
 | 
			
		||||
import androidx.recyclerview.widget.DiffUtil
 | 
			
		||||
import androidx.recyclerview.widget.ListAdapter
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.ListItemAddonBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.model.Addon
 | 
			
		||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 | 
			
		||||
 | 
			
		||||
class AddonAdapter : ListAdapter<Addon, AddonAdapter.AddonViewHolder>(
 | 
			
		||||
    AsyncDifferConfig.Builder(DiffCallback()).build()
 | 
			
		||||
) {
 | 
			
		||||
class AddonAdapter : AbstractDiffAdapter<Addon, AddonAdapter.AddonViewHolder>() {
 | 
			
		||||
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AddonViewHolder {
 | 
			
		||||
        ListItemAddonBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
			
		||||
            .also { return AddonViewHolder(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun getItemCount(): Int = currentList.size
 | 
			
		||||
 | 
			
		||||
    override fun onBindViewHolder(holder: AddonViewHolder, position: Int) =
 | 
			
		||||
        holder.bind(currentList[position])
 | 
			
		||||
 | 
			
		||||
    inner class AddonViewHolder(val binding: ListItemAddonBinding) :
 | 
			
		||||
        RecyclerView.ViewHolder(binding.root) {
 | 
			
		||||
        fun bind(addon: Addon) {
 | 
			
		||||
        AbstractViewHolder<Addon>(binding) {
 | 
			
		||||
        override fun bind(model: Addon) {
 | 
			
		||||
            binding.root.setOnClickListener {
 | 
			
		||||
                binding.addonSwitch.isChecked = !binding.addonSwitch.isChecked
 | 
			
		||||
            }
 | 
			
		||||
            binding.title.text = addon.title
 | 
			
		||||
            binding.version.text = addon.version
 | 
			
		||||
            binding.title.text = model.title
 | 
			
		||||
            binding.version.text = model.version
 | 
			
		||||
            binding.addonSwitch.setOnCheckedChangeListener { _, checked ->
 | 
			
		||||
                addon.enabled = checked
 | 
			
		||||
                model.enabled = checked
 | 
			
		||||
            }
 | 
			
		||||
            binding.addonSwitch.isChecked = addon.enabled
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class DiffCallback : DiffUtil.ItemCallback<Addon>() {
 | 
			
		||||
        override fun areItemsTheSame(oldItem: Addon, newItem: Addon): Boolean {
 | 
			
		||||
            return oldItem == newItem
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun areContentsTheSame(oldItem: Addon, newItem: Addon): Boolean {
 | 
			
		||||
            return oldItem == newItem
 | 
			
		||||
            binding.addonSwitch.isChecked = model.enabled
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,19 +8,14 @@ import android.text.TextUtils
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import androidx.fragment.app.FragmentActivity
 | 
			
		||||
import androidx.recyclerview.widget.AsyncDifferConfig
 | 
			
		||||
import androidx.recyclerview.widget.DiffUtil
 | 
			
		||||
import androidx.recyclerview.widget.ListAdapter
 | 
			
		||||
import androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
import org.yuzu.yuzu_emu.databinding.CardFolderBinding
 | 
			
		||||
import org.yuzu.yuzu_emu.fragments.GameFolderPropertiesDialogFragment
 | 
			
		||||
import org.yuzu.yuzu_emu.model.GameDir
 | 
			
		||||
import org.yuzu.yuzu_emu.model.GamesViewModel
 | 
			
		||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 | 
			
		||||
 | 
			
		||||
class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesViewModel) :
 | 
			
		||||
    ListAdapter<GameDir, FolderAdapter.FolderViewHolder>(
 | 
			
		||||
        AsyncDifferConfig.Builder(DiffCallback()).build()
 | 
			
		||||
    ) {
 | 
			
		||||
    AbstractDiffAdapter<GameDir, FolderAdapter.FolderViewHolder>() {
 | 
			
		||||
    override fun onCreateViewHolder(
 | 
			
		||||
        parent: ViewGroup,
 | 
			
		||||
        viewType: Int
 | 
			
		||||
@@ -29,18 +24,11 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
 | 
			
		||||
            .also { return FolderViewHolder(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onBindViewHolder(holder: FolderAdapter.FolderViewHolder, position: Int) =
 | 
			
		||||
        holder.bind(currentList[position])
 | 
			
		||||
 | 
			
		||||
    inner class FolderViewHolder(val binding: CardFolderBinding) :
 | 
			
		||||
        RecyclerView.ViewHolder(binding.root) {
 | 
			
		||||
        private lateinit var gameDir: GameDir
 | 
			
		||||
 | 
			
		||||
        fun bind(gameDir: GameDir) {
 | 
			
		||||
            this.gameDir = gameDir
 | 
			
		||||
 | 
			
		||||
        AbstractViewHolder<GameDir>(binding) {
 | 
			
		||||
        override fun bind(model: GameDir) {
 | 
			
		||||
            binding.apply {
 | 
			
		||||
                path.text = Uri.parse(gameDir.uriString).path
 | 
			
		||||
                path.text = Uri.parse(model.uriString).path
 | 
			
		||||
                path.postDelayed(
 | 
			
		||||
                    {
 | 
			
		||||
                        path.isSelected = true
 | 
			
		||||
@@ -50,7 +38,7 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
 | 
			
		||||
                )
 | 
			
		||||
 | 
			
		||||
                buttonEdit.setOnClickListener {
 | 
			
		||||
                    GameFolderPropertiesDialogFragment.newInstance(this@FolderViewHolder.gameDir)
 | 
			
		||||
                    GameFolderPropertiesDialogFragment.newInstance(model)
 | 
			
		||||
                        .show(
 | 
			
		||||
                            activity.supportFragmentManager,
 | 
			
		||||
                            GameFolderPropertiesDialogFragment.TAG
 | 
			
		||||
@@ -58,19 +46,9 @@ class FolderAdapter(val activity: FragmentActivity, val gamesViewModel: GamesVie
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                buttonDelete.setOnClickListener {
 | 
			
		||||
                    gamesViewModel.removeFolder(this@FolderViewHolder.gameDir)
 | 
			
		||||
                    gamesViewModel.removeFolder(model)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class DiffCallback : DiffUtil.ItemCallback<GameDir>() {
 | 
			
		||||
        override fun areItemsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
 | 
			
		||||
            return oldItem == newItem
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun areContentsTheSame(oldItem: GameDir, newItem: GameDir): Boolean {
 | 
			
		||||
            return oldItem == newItem
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,6 @@ import android.graphics.drawable.LayerDrawable
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.text.TextUtils
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import android.widget.ImageView
 | 
			
		||||
import android.widget.Toast
 | 
			
		||||
@@ -25,10 +24,6 @@ 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 kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
import kotlinx.coroutines.withContext
 | 
			
		||||
@@ -36,122 +31,26 @@ import org.yuzu.yuzu_emu.HomeNavigationDirections
 | 
			
		||||
import org.yuzu.yuzu_emu.R
 | 
			
		||||
import org.yuzu.yuzu_emu.YuzuApplication
 | 
			
		||||
import org.yuzu.yuzu_emu.activities.EmulationActivity
 | 
			
		||||
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
 | 
			
		||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
 | 
			
		||||
 | 
			
		||||
class GameAdapter(private val activity: AppCompatActivity) :
 | 
			
		||||
    ListAdapter<Game, GameViewHolder>(AsyncDifferConfig.Builder(DiffCallback()).build()),
 | 
			
		||||
    View.OnClickListener,
 | 
			
		||||
    View.OnLongClickListener {
 | 
			
		||||
    AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
 | 
			
		||||
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
 | 
			
		||||
        // Create a new view.
 | 
			
		||||
        val binding = CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
			
		||||
        binding.cardGame.setOnClickListener(this)
 | 
			
		||||
        binding.cardGame.setOnLongClickListener(this)
 | 
			
		||||
 | 
			
		||||
        // Use that view to create a ViewHolder.
 | 
			
		||||
        return GameViewHolder(binding)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onBindViewHolder(holder: GameViewHolder, position: Int) =
 | 
			
		||||
        holder.bind(currentList[position])
 | 
			
		||||
 | 
			
		||||
    override fun getItemCount(): Int = currentList.size
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Launches the game that was clicked on.
 | 
			
		||||
     *
 | 
			
		||||
     * @param view The card representing the game the user wants to play.
 | 
			
		||||
     */
 | 
			
		||||
    override fun onClick(view: View) {
 | 
			
		||||
        val holder = view.tag as GameViewHolder
 | 
			
		||||
 | 
			
		||||
        val gameExists = DocumentFile.fromSingleUri(
 | 
			
		||||
            YuzuApplication.appContext,
 | 
			
		||||
            Uri.parse(holder.game.path)
 | 
			
		||||
        )?.exists() == true
 | 
			
		||||
        if (!gameExists) {
 | 
			
		||||
            Toast.makeText(
 | 
			
		||||
                YuzuApplication.appContext,
 | 
			
		||||
                R.string.loader_error_file_not_found,
 | 
			
		||||
                Toast.LENGTH_LONG
 | 
			
		||||
            ).show()
 | 
			
		||||
 | 
			
		||||
            ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
 | 
			
		||||
            return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
 | 
			
		||||
        preferences.edit()
 | 
			
		||||
            .putLong(
 | 
			
		||||
                holder.game.keyLastPlayedTime,
 | 
			
		||||
                System.currentTimeMillis()
 | 
			
		||||
            )
 | 
			
		||||
            .apply()
 | 
			
		||||
 | 
			
		||||
        val openIntent = Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
 | 
			
		||||
            action = Intent.ACTION_VIEW
 | 
			
		||||
            data = Uri.parse(holder.game.path)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        activity.lifecycleScope.launch {
 | 
			
		||||
            withContext(Dispatchers.IO) {
 | 
			
		||||
                val layerDrawable = ResourcesCompat.getDrawable(
 | 
			
		||||
                    YuzuApplication.appContext.resources,
 | 
			
		||||
                    R.drawable.shortcut,
 | 
			
		||||
                    null
 | 
			
		||||
                ) as LayerDrawable
 | 
			
		||||
                layerDrawable.setDrawableByLayerId(
 | 
			
		||||
                    R.id.shortcut_foreground,
 | 
			
		||||
                    GameIconUtils.getGameIcon(activity, holder.game)
 | 
			
		||||
                        .toDrawable(YuzuApplication.appContext.resources)
 | 
			
		||||
                )
 | 
			
		||||
                val inset = YuzuApplication.appContext.resources
 | 
			
		||||
                    .getDimensionPixelSize(R.dimen.icon_inset)
 | 
			
		||||
                layerDrawable.setLayerInset(1, inset, inset, inset, inset)
 | 
			
		||||
                val shortcut =
 | 
			
		||||
                    ShortcutInfoCompat.Builder(YuzuApplication.appContext, holder.game.path)
 | 
			
		||||
                        .setShortLabel(holder.game.title)
 | 
			
		||||
                        .setIcon(
 | 
			
		||||
                            IconCompat.createWithAdaptiveBitmap(
 | 
			
		||||
                                layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
 | 
			
		||||
                            )
 | 
			
		||||
                        )
 | 
			
		||||
                        .setIntent(openIntent)
 | 
			
		||||
                        .build()
 | 
			
		||||
                ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val action = HomeNavigationDirections.actionGlobalEmulationActivity(holder.game, true)
 | 
			
		||||
        view.findNavController().navigate(action)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onLongClick(view: View): Boolean {
 | 
			
		||||
        val holder = view.tag as GameViewHolder
 | 
			
		||||
        val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(holder.game)
 | 
			
		||||
        view.findNavController().navigate(action)
 | 
			
		||||
        return true
 | 
			
		||||
        CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
 | 
			
		||||
            .also { return GameViewHolder(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inner class GameViewHolder(val binding: CardGameBinding) :
 | 
			
		||||
        RecyclerView.ViewHolder(binding.root) {
 | 
			
		||||
        lateinit var game: Game
 | 
			
		||||
 | 
			
		||||
        init {
 | 
			
		||||
            binding.cardGame.tag = this
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun bind(game: Game) {
 | 
			
		||||
            this.game = game
 | 
			
		||||
 | 
			
		||||
        AbstractViewHolder<Game>(binding) {
 | 
			
		||||
        override fun bind(model: Game) {
 | 
			
		||||
            binding.imageGameScreen.scaleType = ImageView.ScaleType.CENTER_CROP
 | 
			
		||||
            GameIconUtils.loadGameIcon(game, binding.imageGameScreen)
 | 
			
		||||
            GameIconUtils.loadGameIcon(model, binding.imageGameScreen)
 | 
			
		||||
 | 
			
		||||
            binding.textGameTitle.text = game.title.replace("[\\t\\n\\r]+".toRegex(), " ")
 | 
			
		||||
            binding.textGameTitle.text = model.title.replace("[\\t\\n\\r]+".toRegex(), " ")
 | 
			
		||||
 | 
			
		||||
            binding.textGameTitle.postDelayed(
 | 
			
		||||
                {
 | 
			
		||||
@@ -160,16 +59,79 @@ class GameAdapter(private val activity: AppCompatActivity) :
 | 
			
		||||
                },
 | 
			
		||||
                3000
 | 
			
		||||
            )
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class DiffCallback : DiffUtil.ItemCallback<Game>() {
 | 
			
		||||
        override fun areItemsTheSame(oldItem: Game, newItem: Game): Boolean {
 | 
			
		||||
            return oldItem == newItem
 | 
			
		||||
            binding.cardGame.setOnClickListener { onClick(model) }
 | 
			
		||||
            binding.cardGame.setOnLongClickListener { onLongClick(model) }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun areContentsTheSame(oldItem: Game, newItem: Game): Boolean {
 | 
			
		||||
            return oldItem == newItem
 | 
			
		||||
        fun onClick(game: Game) {
 | 
			
		||||
            val gameExists = DocumentFile.fromSingleUri(
 | 
			
		||||
                YuzuApplication.appContext,
 | 
			
		||||
                Uri.parse(game.path)
 | 
			
		||||
            )?.exists() == true
 | 
			
		||||
            if (!gameExists) {
 | 
			
		||||
                Toast.makeText(
 | 
			
		||||
                    YuzuApplication.appContext,
 | 
			
		||||
                    R.string.loader_error_file_not_found,
 | 
			
		||||
                    Toast.LENGTH_LONG
 | 
			
		||||
                ).show()
 | 
			
		||||
 | 
			
		||||
                ViewModelProvider(activity)[GamesViewModel::class.java].reloadGames(true)
 | 
			
		||||
                return
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val preferences =
 | 
			
		||||
                PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
 | 
			
		||||
            preferences.edit()
 | 
			
		||||
                .putLong(
 | 
			
		||||
                    game.keyLastPlayedTime,
 | 
			
		||||
                    System.currentTimeMillis()
 | 
			
		||||
                )
 | 
			
		||||
                .apply()
 | 
			
		||||
 | 
			
		||||
            val openIntent =
 | 
			
		||||
                Intent(YuzuApplication.appContext, EmulationActivity::class.java).apply {
 | 
			
		||||
                    action = Intent.ACTION_VIEW
 | 
			
		||||
                    data = Uri.parse(game.path)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            activity.lifecycleScope.launch {
 | 
			
		||||
                withContext(Dispatchers.IO) {
 | 
			
		||||
                    val layerDrawable = ResourcesCompat.getDrawable(
 | 
			
		||||
                        YuzuApplication.appContext.resources,
 | 
			
		||||
                        R.drawable.shortcut,
 | 
			
		||||
                        null
 | 
			
		||||
                    ) as LayerDrawable
 | 
			
		||||
                    layerDrawable.setDrawableByLayerId(
 | 
			
		||||
                        R.id.shortcut_foreground,
 | 
			
		||||
                        GameIconUtils.getGameIcon(activity, game)
 | 
			
		||||
                            .toDrawable(YuzuApplication.appContext.resources)
 | 
			
		||||
                    )
 | 
			
		||||
                    val inset = YuzuApplication.appContext.resources
 | 
			
		||||
                        .getDimensionPixelSize(R.dimen.icon_inset)
 | 
			
		||||
                    layerDrawable.setLayerInset(1, inset, inset, inset, inset)
 | 
			
		||||
                    val shortcut =
 | 
			
		||||
                        ShortcutInfoCompat.Builder(YuzuApplication.appContext, game.path)
 | 
			
		||||
                            .setShortLabel(game.title)
 | 
			
		||||
                            .setIcon(
 | 
			
		||||
                                IconCompat.createWithAdaptiveBitmap(
 | 
			
		||||
                                    layerDrawable.toBitmap(config = Bitmap.Config.ARGB_8888)
 | 
			
		||||
                                )
 | 
			
		||||
                            )
 | 
			
		||||
                            .setIntent(openIntent)
 | 
			
		||||
                            .build()
 | 
			
		||||
                    ShortcutManagerCompat.pushDynamicShortcut(YuzuApplication.appContext, shortcut)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            val action = HomeNavigationDirections.actionGlobalEmulationActivity(game, true)
 | 
			
		||||
            binding.root.findNavController().navigate(action)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun onLongClick(game: Game): Boolean {
 | 
			
		||||
            val action = HomeNavigationDirections.actionGlobalPerGamePropertiesFragment(game)
 | 
			
		||||
            binding.root.findNavController().navigate(action)
 | 
			
		||||
            return true
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user