diff --git a/android/app/src/main/java/com/izzy2lost/x1box/GameLibraryActivity.kt b/android/app/src/main/java/com/izzy2lost/x1box/GameLibraryActivity.kt
index fc3423e806..731a6b259a 100644
--- a/android/app/src/main/java/com/izzy2lost/x1box/GameLibraryActivity.kt
+++ b/android/app/src/main/java/com/izzy2lost/x1box/GameLibraryActivity.kt
@@ -112,6 +112,16 @@ class GameLibraryActivity : AppCompatActivity() {
}
}
+ private var pendingCustomCoverGame: GameEntry? = null
+ private val pickCustomCover =
+ registerForActivityResult(ActivityResultContracts.GetContent()) { uri ->
+ val game = pendingCustomCoverGame ?: return@registerForActivityResult
+ pendingCustomCoverGame = null
+ if (uri != null) {
+ saveCustomCover(game, uri)
+ }
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_game_library)
@@ -534,6 +544,7 @@ class GameLibraryActivity : AppCompatActivity() {
pathText.text = getString(R.string.library_game_path, game.relativePath)
item.setOnClickListener { launchGame(game) }
+ item.setOnLongClickListener { showGameContextMenu(game); true }
gamesListContainer.addView(item)
}
}
@@ -574,6 +585,7 @@ class GameLibraryActivity : AppCompatActivity() {
nameText.text = game.title
sizeText.text = getString(R.string.library_game_size, formatSize(game.sizeBytes))
item.setOnClickListener { launchGame(game) }
+ item.setOnLongClickListener { showGameContextMenu(game); true }
bindCoverArt(coverImage, game)
}
@@ -601,6 +613,17 @@ class GameLibraryActivity : AppCompatActivity() {
private fun bindCoverArt(coverView: ImageView, game: GameEntry) {
coverView.tag = game.uri.toString()
+
+ val customFile = getCustomCoverFile(game)
+ if (customFile.exists()) {
+ coverView.load(customFile) {
+ crossfade(true)
+ placeholder(android.R.drawable.ic_menu_report_image)
+ error(android.R.drawable.ic_menu_report_image)
+ }
+ return
+ }
+
coverView.setImageResource(android.R.drawable.ic_menu_report_image)
if (!boxArtLookupEnabled) {
@@ -1241,6 +1264,65 @@ class GameLibraryActivity : AppCompatActivity() {
}
}
+ private fun customCoversDir(): File = File(filesDir, "custom_covers")
+
+ private fun getCustomCoverFile(game: GameEntry): File =
+ File(customCoversDir(), "${collapseCoverKey(game.title)}.png")
+
+ private fun saveCustomCover(game: GameEntry, uri: Uri) {
+ val dir = customCoversDir()
+ if (!dir.exists()) dir.mkdirs()
+ val target = getCustomCoverFile(game)
+ try {
+ val input = contentResolver.openInputStream(uri) ?: run {
+ Toast.makeText(this, getString(R.string.library_custom_cover_failed), Toast.LENGTH_SHORT).show()
+ return
+ }
+ input.use { stream ->
+ FileOutputStream(target).use { output ->
+ stream.copyTo(output)
+ }
+ }
+ boxArtCache.remove(normalizeCoverKey(game.title))
+ renderGames()
+ Toast.makeText(this, getString(R.string.library_custom_cover_set), Toast.LENGTH_SHORT).show()
+ } catch (_: IOException) {
+ Toast.makeText(this, getString(R.string.library_custom_cover_failed), Toast.LENGTH_SHORT).show()
+ }
+ }
+
+ private fun removeCustomCover(game: GameEntry) {
+ val file = getCustomCoverFile(game)
+ if (file.exists()) {
+ file.delete()
+ boxArtCache.remove(normalizeCoverKey(game.title))
+ renderGames()
+ Toast.makeText(this, getString(R.string.library_custom_cover_removed), Toast.LENGTH_SHORT).show()
+ }
+ }
+
+ private fun showGameContextMenu(game: GameEntry) {
+ val hasCustomCover = getCustomCoverFile(game).exists()
+ val options = buildList {
+ add(getString(R.string.library_custom_cover_set_option))
+ if (hasCustomCover) add(getString(R.string.library_custom_cover_remove_option))
+ }.toTypedArray()
+
+ MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
+ .setTitle(game.title)
+ .setItems(options) { _, which ->
+ when {
+ which == 0 -> {
+ pendingCustomCoverGame = game
+ pickCustomCover.launch("image/*")
+ }
+ which == 1 && hasCustomCover -> removeCustomCover(game)
+ }
+ }
+ .setNegativeButton(android.R.string.cancel, null)
+ .show()
+ }
+
private fun openExternalLink(url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)).apply {
addCategory(Intent.CATEGORY_BROWSABLE)
diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml
index 84d7718a30..4e9b519195 100644
--- a/android/app/src/main/res/values/strings.xml
+++ b/android/app/src/main/res/values/strings.xml
@@ -135,4 +135,10 @@
No screenshot yet
Slot %1$d - %2$s
Snapshot thumbnail
+
+ Set Custom Cover
+ Remove Custom Cover
+ Custom cover saved
+ Custom cover removed
+ Failed to save custom cover