mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
face lift
This commit is contained in:
@@ -30,7 +30,6 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import coil.load
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.button.MaterialButtonToggleGroup
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.materialswitch.MaterialSwitch
|
||||
import java.io.File
|
||||
@@ -107,19 +106,18 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
private val coverEntries = ArrayList<CoverEntry>()
|
||||
@Volatile private var coverIndexLoaded = false
|
||||
|
||||
private lateinit var folderText: TextView
|
||||
private lateinit var loadingSpinner: ProgressBar
|
||||
private lateinit var loadingText: TextView
|
||||
private lateinit var emptyText: TextView
|
||||
private lateinit var gamesListContainer: LinearLayout
|
||||
private lateinit var gamesGridContainer: LinearLayout
|
||||
private lateinit var btnChangeFolder: MaterialButton
|
||||
private lateinit var btnBootDashboard: MaterialButton
|
||||
private lateinit var btnSettings: MaterialButton
|
||||
private lateinit var btnSnapshots: MaterialButton
|
||||
private lateinit var btnConvertIso: MaterialButton
|
||||
private lateinit var btnAbout: ImageButton
|
||||
private lateinit var viewModeToggle: MaterialButtonToggleGroup
|
||||
private lateinit var btnViewList: MaterialButton
|
||||
private lateinit var btnViewGrid: MaterialButton
|
||||
private lateinit var switchBoxArtLookup: MaterialSwitch
|
||||
|
||||
private var gamesFolderUri: Uri? = null
|
||||
@@ -161,36 +159,29 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
folderText = findViewById(R.id.library_folder_text)
|
||||
loadingSpinner = findViewById(R.id.library_loading)
|
||||
loadingText = findViewById(R.id.library_loading_text)
|
||||
emptyText = findViewById(R.id.library_empty_text)
|
||||
gamesListContainer = findViewById(R.id.library_games_container)
|
||||
gamesGridContainer = findViewById(R.id.library_games_grid_container)
|
||||
btnChangeFolder = findViewById(R.id.btn_change_games_folder)
|
||||
btnBootDashboard = findViewById(R.id.btn_boot_dashboard)
|
||||
btnSettings = findViewById(R.id.btn_settings)
|
||||
btnSnapshots = findViewById(R.id.btn_snapshots)
|
||||
btnConvertIso = findViewById(R.id.btn_convert_iso)
|
||||
btnAbout = findViewById(R.id.btn_library_about)
|
||||
viewModeToggle = findViewById(R.id.library_view_mode_toggle)
|
||||
btnViewList = findViewById(R.id.btn_view_list)
|
||||
btnViewGrid = findViewById(R.id.btn_view_grid)
|
||||
switchBoxArtLookup = findViewById(R.id.switch_box_art_lookup)
|
||||
btnViewList.isCheckable = true
|
||||
btnViewGrid.isCheckable = true
|
||||
|
||||
gamesFolderUri = prefs.getString("gamesFolderUri", null)?.let(Uri::parse)
|
||||
useCoverGrid = prefs.getBoolean("library_cover_grid", false)
|
||||
boxArtLookupEnabled = prefs.getBoolean("library_box_art_lookup", true)
|
||||
|
||||
switchBoxArtLookup.isChecked = boxArtLookupEnabled
|
||||
viewModeToggle.check(if (useCoverGrid) R.id.btn_view_grid else R.id.btn_view_list)
|
||||
syncDisplayModeUi()
|
||||
|
||||
btnChangeFolder.setOnClickListener {
|
||||
if (isConvertingIso) {
|
||||
Toast.makeText(this, getString(R.string.library_convert_busy), Toast.LENGTH_SHORT).show()
|
||||
return@setOnClickListener
|
||||
}
|
||||
pickGamesFolder.launch(gamesFolderUri)
|
||||
}
|
||||
btnBootDashboard.setOnClickListener {
|
||||
if (isConvertingIso) {
|
||||
Toast.makeText(this, getString(R.string.library_convert_busy), Toast.LENGTH_SHORT).show()
|
||||
@@ -210,19 +201,8 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
btnAbout.setOnClickListener {
|
||||
showAboutDialog()
|
||||
}
|
||||
viewModeToggle.addOnButtonCheckedListener { _, checkedId, isChecked ->
|
||||
if (!isChecked) {
|
||||
return@addOnButtonCheckedListener
|
||||
}
|
||||
val nextGrid = checkedId == R.id.btn_view_grid
|
||||
if (nextGrid == useCoverGrid) {
|
||||
return@addOnButtonCheckedListener
|
||||
}
|
||||
useCoverGrid = nextGrid
|
||||
prefs.edit().putBoolean("library_cover_grid", useCoverGrid).apply()
|
||||
syncDisplayModeUi()
|
||||
renderGames()
|
||||
}
|
||||
btnViewList.setOnClickListener { setDisplayMode(false) }
|
||||
btnViewGrid.setOnClickListener { setDisplayMode(true) }
|
||||
switchBoxArtLookup.setOnCheckedChangeListener { _, checked ->
|
||||
boxArtLookupEnabled = checked
|
||||
prefs.edit().putBoolean("library_box_art_lookup", checked).apply()
|
||||
@@ -233,7 +213,6 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
updateConvertButtonState()
|
||||
|
||||
if (!isFolderReady(gamesFolderUri)) {
|
||||
folderText.text = getString(R.string.library_no_folder)
|
||||
Toast.makeText(this, getString(R.string.setup_pick_disc), Toast.LENGTH_SHORT).show()
|
||||
pickGamesFolder.launch(gamesFolderUri)
|
||||
return
|
||||
@@ -500,7 +479,7 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
showSnapshotPreviewDialog(preview)
|
||||
}
|
||||
} else {
|
||||
thumbnail.setImageResource(android.R.drawable.ic_menu_report_image)
|
||||
thumbnail.setImageResource(R.drawable.ic_xemu_image_placeholder)
|
||||
previewHint.text = getString(R.string.snapshot_preview_unavailable)
|
||||
previewHint.visibility = View.VISIBLE
|
||||
thumbnail.setOnClickListener(null)
|
||||
@@ -532,16 +511,15 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
setLoading(false)
|
||||
currentGames = emptyList()
|
||||
renderGames()
|
||||
folderText.text = getString(R.string.library_no_folder)
|
||||
return
|
||||
}
|
||||
val readyFolderUri = folderUri ?: return
|
||||
|
||||
folderText.text = getString(R.string.library_folder_value, formatTreeLabel(folderUri!!))
|
||||
setLoading(true, getString(R.string.library_loading_games))
|
||||
|
||||
val generation = ++scanGeneration
|
||||
Thread {
|
||||
val games = scanFolderForGames(folderUri)
|
||||
val games = scanFolderForGames(readyFolderUri)
|
||||
runOnUiThread {
|
||||
if (generation != scanGeneration) {
|
||||
return@runOnUiThread
|
||||
@@ -554,11 +532,23 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun syncDisplayModeUi() {
|
||||
btnViewList.isChecked = !useCoverGrid
|
||||
btnViewGrid.isChecked = useCoverGrid
|
||||
switchBoxArtLookup.visibility = if (useCoverGrid) View.VISIBLE else View.GONE
|
||||
gamesListContainer.visibility = if (useCoverGrid) View.GONE else View.VISIBLE
|
||||
gamesGridContainer.visibility = if (useCoverGrid) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun setDisplayMode(nextGrid: Boolean) {
|
||||
if (nextGrid == useCoverGrid) {
|
||||
return
|
||||
}
|
||||
useCoverGrid = nextGrid
|
||||
prefs.edit().putBoolean("library_cover_grid", useCoverGrid).apply()
|
||||
syncDisplayModeUi()
|
||||
renderGames()
|
||||
}
|
||||
|
||||
private fun setLoading(loading: Boolean, message: String? = null) {
|
||||
if (message != null) {
|
||||
loadingText.text = message
|
||||
@@ -598,13 +588,15 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
val nameText = item.findViewById<TextView>(R.id.game_name_text)
|
||||
val sizeText = item.findViewById<TextView>(R.id.game_size_text)
|
||||
val pathText = item.findViewById<TextView>(R.id.game_path_text)
|
||||
val coverImage = item.findViewById<ImageView>(R.id.game_list_cover_image)
|
||||
|
||||
nameText.text = game.title
|
||||
sizeText.text = buildGameSizeText(game)
|
||||
pathText.text = getString(R.string.library_game_path, game.relativePath)
|
||||
pathText.text = game.relativePath
|
||||
|
||||
item.setOnClickListener { launchGame(game) }
|
||||
item.setOnLongClickListener { showGameContextMenu(game); true }
|
||||
bindCoverArt(coverImage, game)
|
||||
gamesListContainer.addView(item)
|
||||
}
|
||||
}
|
||||
@@ -639,11 +631,9 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
row!!.addView(item, itemLp)
|
||||
|
||||
val nameText = item.findViewById<TextView>(R.id.game_cover_name_text)
|
||||
val sizeText = item.findViewById<TextView>(R.id.game_cover_size_text)
|
||||
val coverImage = item.findViewById<ImageView>(R.id.game_cover_image)
|
||||
|
||||
nameText.text = game.title
|
||||
sizeText.text = buildGameSizeText(game)
|
||||
item.setOnClickListener { launchGame(game) }
|
||||
item.setOnLongClickListener { showGameContextMenu(game); true }
|
||||
bindCoverArt(coverImage, game)
|
||||
@@ -673,19 +663,14 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
|
||||
private fun bindCoverArt(coverView: ImageView, game: GameEntry) {
|
||||
coverView.tag = game.uri.toString()
|
||||
showCoverPlaceholder(coverView)
|
||||
|
||||
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)
|
||||
}
|
||||
loadCoverArtIntoView(coverView, customFile)
|
||||
return
|
||||
}
|
||||
|
||||
coverView.setImageResource(android.R.drawable.ic_menu_report_image)
|
||||
|
||||
if (!boxArtLookupEnabled) {
|
||||
return
|
||||
}
|
||||
@@ -705,13 +690,37 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun applyBoxArtToView(coverView: ImageView, url: String) {
|
||||
coverView.load(url) {
|
||||
loadCoverArtIntoView(coverView, url)
|
||||
}
|
||||
|
||||
private fun loadCoverArtIntoView(coverView: ImageView, model: Any) {
|
||||
coverView.load(model) {
|
||||
crossfade(true)
|
||||
placeholder(android.R.drawable.ic_menu_report_image)
|
||||
error(android.R.drawable.ic_menu_report_image)
|
||||
placeholder(R.drawable.ic_xemu_image_placeholder)
|
||||
error(R.drawable.ic_xemu_image_placeholder)
|
||||
listener(
|
||||
onSuccess = { _, _ ->
|
||||
if (coverView.tag != null) {
|
||||
coverView.background = null
|
||||
}
|
||||
},
|
||||
onError = { _, _ ->
|
||||
showCoverPlaceholder(coverView)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCoverPlaceholder(coverView: ImageView) {
|
||||
val backgroundRes = if (coverView.id == R.id.game_list_cover_image) {
|
||||
R.drawable.game_list_cover_background
|
||||
} else {
|
||||
R.drawable.game_cover_image_background
|
||||
}
|
||||
coverView.setBackgroundResource(backgroundRes)
|
||||
coverView.setImageResource(R.drawable.ic_xemu_image_placeholder)
|
||||
}
|
||||
|
||||
private fun lookupBoxArtUrl(title: String): String? {
|
||||
ensureCoverIndexLoaded()
|
||||
val candidates = linkedSetOf<String>()
|
||||
@@ -936,13 +945,35 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
"${game.title}\n${game.relativePath}"
|
||||
}.toTypedArray()
|
||||
|
||||
MaterialAlertDialogBuilder(this)
|
||||
.setTitle(R.string.library_convert_pick_title)
|
||||
.setItems(labels) { _, which ->
|
||||
confirmIsoConversion(isoGames[which])
|
||||
val dp = resources.displayMetrics.density
|
||||
lateinit var convertDialog: androidx.appcompat.app.AlertDialog
|
||||
|
||||
val buttonList = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding((20 * dp).toInt(), (12 * dp).toInt(), (20 * dp).toInt(), 0)
|
||||
labels.forEachIndexed { i, label ->
|
||||
addView(MaterialButton(this@GameLibraryActivity, null,
|
||||
com.google.android.material.R.attr.materialButtonOutlinedStyle).apply {
|
||||
text = label
|
||||
isAllCaps = false
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).also { lp -> lp.bottomMargin = (8 * dp).toInt() }
|
||||
setOnClickListener {
|
||||
convertDialog.dismiss()
|
||||
confirmIsoConversion(isoGames[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
convertDialog = MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(R.string.library_convert_pick_title)
|
||||
.setView(buttonList)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
.create()
|
||||
convertDialog.show()
|
||||
}
|
||||
|
||||
private fun confirmIsoConversion(game: GameEntry) {
|
||||
@@ -1323,11 +1354,16 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
|
||||
private fun toGameTitle(fileName: String): String {
|
||||
val lower = fileName.lowercase(Locale.ROOT)
|
||||
return when {
|
||||
val rawTitle = when {
|
||||
lower.endsWith(".xiso.iso") -> fileName.dropLast(".xiso.iso".length)
|
||||
fileName.contains('.') -> fileName.substringBeforeLast('.')
|
||||
else -> fileName
|
||||
}
|
||||
val cleanedTitle = rawTitle
|
||||
.replace(Regex("(\\s*(\\([^\\)]*\\)|\\[[^\\]]*\\]))+$"), "")
|
||||
.replace(Regex("\\s+"), " ")
|
||||
.trim()
|
||||
return cleanedTitle.ifEmpty { rawTitle.trim() }
|
||||
}
|
||||
|
||||
private fun formatSize(bytes: Long): String {
|
||||
@@ -1372,14 +1408,6 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatTreeLabel(uri: Uri): String {
|
||||
val name = DocumentFile.fromTreeUri(this, uri)?.name
|
||||
if (!name.isNullOrBlank()) {
|
||||
return name
|
||||
}
|
||||
return uri.toString()
|
||||
}
|
||||
|
||||
private fun dp(value: Int): Int {
|
||||
return (value * resources.displayMetrics.density).toInt()
|
||||
}
|
||||
@@ -1551,20 +1579,41 @@ class GameLibraryActivity : AppCompatActivity() {
|
||||
}
|
||||
}.toTypedArray()
|
||||
|
||||
MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(game.title)
|
||||
.setItems(options) { _, which ->
|
||||
when (actions[which]) {
|
||||
GameContextAction.SET_CUSTOM_COVER -> {
|
||||
pendingCustomCoverGame = game
|
||||
pickCustomCover.launch("image/*")
|
||||
val dp = resources.displayMetrics.density
|
||||
lateinit var contextDialog: androidx.appcompat.app.AlertDialog
|
||||
|
||||
val buttonList = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding((20 * dp).toInt(), (12 * dp).toInt(), (20 * dp).toInt(), 0)
|
||||
options.forEachIndexed { i, label ->
|
||||
addView(MaterialButton(this@GameLibraryActivity, null,
|
||||
com.google.android.material.R.attr.materialButtonOutlinedStyle).apply {
|
||||
text = label
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).also { lp -> lp.bottomMargin = (8 * dp).toInt() }
|
||||
setOnClickListener {
|
||||
contextDialog.dismiss()
|
||||
when (actions[i]) {
|
||||
GameContextAction.SET_CUSTOM_COVER -> {
|
||||
pendingCustomCoverGame = game
|
||||
pickCustomCover.launch("image/*")
|
||||
}
|
||||
GameContextAction.REMOVE_CUSTOM_COVER -> removeCustomCover(game)
|
||||
GameContextAction.DELETE_GAME -> confirmDeleteGame(game)
|
||||
}
|
||||
}
|
||||
GameContextAction.REMOVE_CUSTOM_COVER -> removeCustomCover(game)
|
||||
GameContextAction.DELETE_GAME -> confirmDeleteGame(game)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
contextDialog = MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(game.title)
|
||||
.setView(buttonList)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
.create()
|
||||
contextDialog.show()
|
||||
}
|
||||
|
||||
private fun openExternalLink(url: String) {
|
||||
|
||||
@@ -21,10 +21,13 @@ import android.view.ViewConfiguration
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ListView
|
||||
import android.widget.RelativeLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import com.google.android.material.button.MaterialButton
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import org.libsdl.app.SDLActivity
|
||||
@@ -663,7 +666,7 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
showSnapshotPreviewDialog(preview)
|
||||
}
|
||||
} else {
|
||||
thumbnail.setImageResource(android.R.drawable.ic_menu_report_image)
|
||||
thumbnail.setImageResource(R.drawable.ic_xemu_image_placeholder)
|
||||
previewHint.text = getString(R.string.snapshot_preview_unavailable)
|
||||
previewHint.visibility = View.VISIBLE
|
||||
thumbnail.setOnClickListener(null)
|
||||
@@ -732,19 +735,51 @@ class MainActivity : SDLActivity(), InputManager.InputDeviceListener {
|
||||
getString(R.string.in_game_menu_quit_app),
|
||||
)
|
||||
|
||||
val dialog = MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(getString(R.string.in_game_menu_title))
|
||||
.setItems(options) { _, which ->
|
||||
when (which) {
|
||||
0 -> { /* Resume — dismiss dialog */ }
|
||||
1 -> toggleOnScreenController()
|
||||
2 -> showSaveStateDialog()
|
||||
3 -> showLoadStateDialog()
|
||||
4 -> showRebootSystemConfirmation()
|
||||
5 -> exitToGameLibrary()
|
||||
6 -> quitApp()
|
||||
}
|
||||
val dp = resources.displayMetrics.density
|
||||
lateinit var dialog: androidx.appcompat.app.AlertDialog
|
||||
|
||||
val buttonList = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding((20 * dp).toInt(), (12 * dp).toInt(), (20 * dp).toInt(), 0)
|
||||
options.forEachIndexed { i, label ->
|
||||
addView(MaterialButton(this@MainActivity, null,
|
||||
com.google.android.material.R.attr.materialButtonOutlinedStyle).apply {
|
||||
text = label
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).also { lp -> lp.bottomMargin = (8 * dp).toInt() }
|
||||
setOnClickListener {
|
||||
dialog.dismiss()
|
||||
when (i) {
|
||||
0 -> { /* Resume — dialog dismissed above */ }
|
||||
1 -> toggleOnScreenController()
|
||||
2 -> showSaveStateDialog()
|
||||
3 -> showLoadStateDialog()
|
||||
4 -> showRebootSystemConfirmation()
|
||||
5 -> exitToGameLibrary()
|
||||
6 -> quitApp()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
val scrollContainer = NestedScrollView(this).apply {
|
||||
isFillViewport = true
|
||||
overScrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS
|
||||
addView(
|
||||
buttonList,
|
||||
ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
dialog = MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(getString(R.string.in_game_menu_title))
|
||||
.setView(scrollContainer)
|
||||
.setOnDismissListener {
|
||||
inGameMenuDialog = null
|
||||
hideSystemUI()
|
||||
|
||||
@@ -8,6 +8,7 @@ import android.provider.OpenableColumns
|
||||
import android.text.format.Formatter
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.AutoCompleteTextView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
@@ -640,23 +641,45 @@ class SettingsActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
.toTypedArray()
|
||||
MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(R.string.settings_hdd_layout_pick_title)
|
||||
.setItems(labels) { _, which ->
|
||||
val layout = allLayouts[which]
|
||||
val availability = XboxHddFormatter.availabilityFor(inspection, layout)
|
||||
if (availability == XboxHddFormatter.LayoutAvailability.AVAILABLE) {
|
||||
showInitializeHddConfirmation(hddFile, layout, button)
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(R.string.settings_hdd_layout_unavailable_title)
|
||||
.setMessage(getString(hddLayoutUnavailableReasonRes(availability)))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
val dp = resources.displayMetrics.density
|
||||
lateinit var hddDialog: androidx.appcompat.app.AlertDialog
|
||||
|
||||
val buttonList = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding((20 * dp).toInt(), (12 * dp).toInt(), (20 * dp).toInt(), 0)
|
||||
labels.forEachIndexed { i, label ->
|
||||
addView(MaterialButton(this@SettingsActivity, null,
|
||||
com.google.android.material.R.attr.materialButtonOutlinedStyle).apply {
|
||||
text = label
|
||||
isAllCaps = false
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).also { lp -> lp.bottomMargin = (8 * dp).toInt() }
|
||||
setOnClickListener {
|
||||
hddDialog.dismiss()
|
||||
val layout = allLayouts[i]
|
||||
val availability = XboxHddFormatter.availabilityFor(inspection, layout)
|
||||
if (availability == XboxHddFormatter.LayoutAvailability.AVAILABLE) {
|
||||
showInitializeHddConfirmation(hddFile, layout, button)
|
||||
} else {
|
||||
MaterialAlertDialogBuilder(this@SettingsActivity, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(R.string.settings_hdd_layout_unavailable_title)
|
||||
.setMessage(getString(hddLayoutUnavailableReasonRes(availability)))
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
hddDialog = MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(R.string.settings_hdd_layout_pick_title)
|
||||
.setView(buttonList)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
.create()
|
||||
hddDialog.show()
|
||||
}
|
||||
|
||||
private fun showInitializeHddConfirmation(
|
||||
@@ -783,16 +806,37 @@ class SettingsActivity : AppCompatActivity() {
|
||||
getString(R.string.settings_dashboard_import_source_zip),
|
||||
getString(R.string.settings_dashboard_import_source_folder),
|
||||
)
|
||||
MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(R.string.settings_dashboard_import_source_title)
|
||||
.setItems(labels) { _, which ->
|
||||
when (which) {
|
||||
0 -> pickDashboardZip.launch(arrayOf("application/zip", "application/octet-stream"))
|
||||
else -> pickDashboardFolder.launch(null)
|
||||
}
|
||||
val dp = resources.displayMetrics.density
|
||||
lateinit var importDialog: androidx.appcompat.app.AlertDialog
|
||||
|
||||
val buttonList = LinearLayout(this).apply {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
setPadding((20 * dp).toInt(), (12 * dp).toInt(), (20 * dp).toInt(), 0)
|
||||
labels.forEachIndexed { i, label ->
|
||||
addView(MaterialButton(this@SettingsActivity, null,
|
||||
com.google.android.material.R.attr.materialButtonOutlinedStyle).apply {
|
||||
text = label
|
||||
layoutParams = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).also { lp -> lp.bottomMargin = (8 * dp).toInt() }
|
||||
setOnClickListener {
|
||||
importDialog.dismiss()
|
||||
when (i) {
|
||||
0 -> pickDashboardZip.launch(arrayOf("application/zip", "application/octet-stream"))
|
||||
else -> pickDashboardFolder.launch(null)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
importDialog = MaterialAlertDialogBuilder(this, R.style.ThemeOverlay_Xemu_RoundedDialog)
|
||||
.setTitle(R.string.settings_dashboard_import_source_title)
|
||||
.setView(buttonList)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
.create()
|
||||
importDialog.show()
|
||||
}
|
||||
|
||||
private fun prepareDashboardImportFromZip(uri: Uri) {
|
||||
|
||||
@@ -247,10 +247,10 @@ class SetupWizardActivity : AppCompatActivity() {
|
||||
}
|
||||
indicators.forEachIndexed { index, view ->
|
||||
view.setBackgroundResource(
|
||||
if (currentStep == index) {
|
||||
R.drawable.setup_wizard_indicator_active
|
||||
} else {
|
||||
R.drawable.setup_wizard_indicator_inactive
|
||||
when {
|
||||
currentStep == index -> R.drawable.setup_wizard_indicator_active
|
||||
index < currentStep -> R.drawable.setup_wizard_indicator_complete
|
||||
else -> R.drawable.setup_wizard_indicator_inactive
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
M3 Expressive spring press animation for FILLED buttons only.
|
||||
|
||||
"state_pressed=false" as the last item acts as the default/release state:
|
||||
since it has no other state constraints, it matches whenever nothing
|
||||
more specific matches — i.e. normal and released states.
|
||||
|
||||
Using android:interpolator/overshoot causes the scale to briefly pass
|
||||
1.0 on release (the "spring bounce-back" feel). When the view is already
|
||||
at scale 1.0 (startup), delta is 0 so overshoot produces no visible movement.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- Touch down: snap quickly to 94% -->
|
||||
<item android:state_pressed="true">
|
||||
<set android:ordering="together">
|
||||
<objectAnimator
|
||||
android:duration="80"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||
android:propertyName="scaleX"
|
||||
android:valueTo="0.94"
|
||||
android:valueType="floatType" />
|
||||
<objectAnimator
|
||||
android:duration="80"
|
||||
android:interpolator="@android:interpolator/fast_out_slow_in"
|
||||
android:propertyName="scaleY"
|
||||
android:valueTo="0.94"
|
||||
android:valueType="floatType" />
|
||||
</set>
|
||||
</item>
|
||||
|
||||
<!-- Release / default: spring back to full size with overshoot bounce -->
|
||||
<item>
|
||||
<set android:ordering="together">
|
||||
<objectAnimator
|
||||
android:duration="300"
|
||||
android:interpolator="@android:interpolator/overshoot"
|
||||
android:propertyName="scaleX"
|
||||
android:valueTo="1.0"
|
||||
android:valueType="floatType" />
|
||||
<objectAnimator
|
||||
android:duration="300"
|
||||
android:interpolator="@android:interpolator/overshoot"
|
||||
android:propertyName="scaleY"
|
||||
android:valueTo="1.0"
|
||||
android:valueType="floatType" />
|
||||
</set>
|
||||
</item>
|
||||
|
||||
</selector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.5" android:color="@color/xemu_text_muted" android:state_enabled="false" />
|
||||
<item android:color="@color/xemu_text" />
|
||||
</selector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.4" android:color="@color/xemu_outline_variant" android:state_enabled="false" />
|
||||
<item android:color="@color/xemu_green" android:state_checked="true" />
|
||||
<item android:color="@android:color/transparent" />
|
||||
</selector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.5" android:color="@color/xemu_text_muted" android:state_enabled="false" />
|
||||
<item android:color="@color/xemu_black" android:state_checked="true" />
|
||||
<item android:color="@color/xemu_text" />
|
||||
</selector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.5" android:color="@color/xemu_text_muted" android:state_enabled="false" />
|
||||
<item android:color="@color/xemu_black" android:state_checked="true" />
|
||||
<item android:color="@color/xemu_green_light" />
|
||||
</selector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:alpha="0.5" android:color="@color/xemu_text_muted" android:state_enabled="false" />
|
||||
<item android:color="@color/xemu_black" />
|
||||
</selector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/xemu_outline_variant" android:state_enabled="false" />
|
||||
<item android:color="@color/xemu_green" />
|
||||
</selector>
|
||||
@@ -0,0 +1,7 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:startColor="@color/xemu_surface_container_high"
|
||||
android:endColor="@color/xemu_surface_container" />
|
||||
<corners android:radius="14dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,7 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:startColor="@color/xemu_surface_container_high"
|
||||
android:endColor="@color/xemu_surface_container" />
|
||||
<corners android:radius="6dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<group android:translateY="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480-300q75 0 127.5-52.5T660-480q0-75-52.5-127.5T480-660q-75 0-127.5 52.5T300-480q0 75 52.5 127.5T480-300Zm0-140q-17 0-28.5-11.5T440-480q0-17 11.5-28.5T480-520q17 0 28.5 11.5T520-480q0 17-11.5 28.5T480-440Zm0 360q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<group android:translateY="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M160-160q-33 0-56.5-23.5T80-240v-480q0-33 23.5-56.5T160-800h207q16 0 30.5 6t25.5 17l57 57h360q17 0 28.5 11.5T880-680q0 17-11.5 28.5T840-640H447l-80-80H160v480l79-263q8-26 29.5-41.5T316-560h516q41 0 64.5 32.5T909-457l-72 240q-8 26-29.5 41.5T760-160H160Zm84-80h516l72-240H316l-72 240Zm-84-262v-218 218Zm84 262 72-240-72 240Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<group android:translateY="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M182-200q-51 0-79-35.5T82-322l42-300q9-60 53.5-99T282-760h396q60 0 104.5 39t53.5 99l42 300q7 51-21 86.5T778-200q-21 0-39-7.5T706-230l-90-90H344l-90 90q-15 15-33 22.5t-39 7.5Zm16-86 114-114h336l114 114q2 2 16 6 11 0 17.5-6.5T800-304l-44-308q-4-29-26-48.5T678-680H282q-30 0-52 19.5T204-612l-44 308q-2 11 4.5 17.5T182-280q2 0 16-6Zm482-154q17 0 28.5-11.5T720-480q0-17-11.5-28.5T680-520q-17 0-28.5 11.5T640-480q0 17 11.5 28.5T680-440Zm-80-120q17 0 28.5-11.5T640-600q0-17-11.5-28.5T600-640q-17 0-28.5 11.5T560-600q0 17 11.5 28.5T600-560Zm-120 80Zm-170-30v40q0 13 8.5 21.5T340-440q13 0 21.5-8.5T370-470v-40h40q13 0 21.5-8.5T440-540q0-13-8.5-21.5T410-570h-40v-40q0-13-8.5-21.5T340-640q-13 0-21.5 8.5T310-610v40h-40q-13 0-21.5 8.5T240-540q0 13 8.5 21.5T270-510h40Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<group android:translateY="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M200-520q-33 0-56.5-23.5T120-600v-160q0-33 23.5-56.5T200-840h160q33 0 56.5 23.5T440-760v160q0 33-23.5 56.5T360-520H200Zm0 400q-33 0-56.5-23.5T120-200v-160q0-33 23.5-56.5T200-440h160q33 0 56.5 23.5T440-360v160q0 33-23.5 56.5T360-120H200Zm400-400q-33 0-56.5-23.5T520-600v-160q0-33 23.5-56.5T600-840h160q33 0 56.5 23.5T840-760v160q0 33-23.5 56.5T760-520H600Zm0 400q-33 0-56.5-23.5T520-200v-160q0-33 23.5-56.5T600-440h160q33 0 56.5 23.5T840-360v160q0 33-23.5 56.5T760-120H600ZM200-600h160v-160H200v160Zm400 0h160v-160H600v160Zm0 400h160v-160H600v160Zm-400 0h160v-160H200v160Zm400-400Zm0 240Zm-240 0Zm0-240Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<group android:translateY="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M560-200q17 0 28.5-11.5T600-240q0-17-11.5-28.5T560-280q-17 0-28.5 11.5T520-240q0 17 11.5 28.5T560-200Zm120 0q17 0 28.5-11.5T720-240q0-17-11.5-28.5T680-280q-17 0-28.5 11.5T640-240q0 17 11.5 28.5T680-200ZM120-440v-360q0-33 23.5-56.5T200-880h560q33 0 56.5 23.5T840-800v360h-80v-360H200v360h-80Zm80 80v200h560v-200H200Zm0 280q-33 0-56.5-23.5T120-160v-280h720v280q0 33-23.5 56.5T760-80H200Zm0-360h560-560Zm0 80h560-560Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<group android:translateY="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480-120q-126 0-223-76.5T131-392q-4-15 6-27.5t27-14.5q16-2 29 6t18 24q24 90 99 147t170 57q117 0 198.5-81.5T760-480q0-117-81.5-198.5T480-760q-69 0-129 32t-101 88h70q17 0 28.5 11.5T360-600q0 17-11.5 28.5T320-560H160q-17 0-28.5-11.5T120-600v-160q0-17 11.5-28.5T160-800q17 0 28.5 11.5T200-760v54q51-64 124.5-99T480-840q75 0 140.5 28.5t114 77q48.5 48.5 77 114T840-480q0 75-28.5 140.5t-77 114q-48.5 48.5-114 77T480-120Zm40-376 100 100q11 11 11 28t-11 28q-11 11-28 11t-28-11L452-452q-6-6-9-13.5t-3-15.5v-159q0-17 11.5-28.5T480-680q17 0 28.5 11.5T520-640v144Z" />
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<group android:translateY="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm0 0v-560 560Zm80-80h400q12 0 18-11t-2-21L586-459q-6-8-16-8t-16 8L450-320l-74-99q-6-8-16-8t-16 8l-80 107q-8 10-2 21t18 11Z" />
|
||||
</group>
|
||||
</vector>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user