diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/NativeLibrary.kt b/src/android/app/src/main/java/io/github/lime3ds/android/NativeLibrary.kt index 2c8cad96f..d4f7c7c0f 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/NativeLibrary.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/NativeLibrary.kt @@ -525,12 +525,28 @@ object NativeLibrary { external fun removeAmiibo() - const val SAVESTATE_SLOT_COUNT = 10 + const val SAVESTATE_SLOT_COUNT = 11 + const val QUICKSAVE_SLOT = 0 external fun getSavestateInfo(): Array? external fun saveState(slot: Int) + + fun loadStateIfAvailable(slot: Int): Boolean { + var available = false + getSavestateInfo()?.forEach { + if (it.slot == slot){ + available = true + return@forEach + } + } + if (available) { + loadState(slot) + return true + } + return false + } external fun loadState(slot: Int) /** diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/activities/EmulationActivity.kt b/src/android/app/src/main/java/io/github/lime3ds/android/activities/EmulationActivity.kt index aef0bbe95..011bb6d36 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/activities/EmulationActivity.kt @@ -76,7 +76,7 @@ class EmulationActivity : AppCompatActivity() { binding = ActivityEmulationBinding.inflate(layoutInflater) screenAdjustmentUtil = ScreenAdjustmentUtil(windowManager, settingsViewModel.settings) - hotkeyUtility = HotkeyUtility(screenAdjustmentUtil) + hotkeyUtility = HotkeyUtility(screenAdjustmentUtil, this) setContentView(binding.root) val navHostFragment = diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/features/hotkeys/Hotkey.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/hotkeys/Hotkey.kt index d20fe70c3..13f4bf31c 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/hotkeys/Hotkey.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/hotkeys/Hotkey.kt @@ -8,5 +8,7 @@ enum class Hotkey(val button: Int) { SWAP_SCREEN(10001), CYCLE_LAYOUT(10002), CLOSE_GAME(10003), - PAUSE_OR_RESUME(10004); + PAUSE_OR_RESUME(10004), + QUICKSAVE(10005), + QUICKLOAD(10006); } diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/features/hotkeys/HotkeyUtility.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/hotkeys/HotkeyUtility.kt index bc07064c3..cf864d154 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/hotkeys/HotkeyUtility.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/hotkeys/HotkeyUtility.kt @@ -4,11 +4,17 @@ package io.github.lime3ds.android.features.hotkeys +import android.content.Context +import android.widget.Toast +import io.github.lime3ds.android.NativeLibrary +import io.github.lime3ds.android.R import io.github.lime3ds.android.utils.EmulationLifecycleUtil import io.github.lime3ds.android.display.ScreenAdjustmentUtil -class HotkeyUtility(private val screenAdjustmentUtil: ScreenAdjustmentUtil) { - +class HotkeyUtility(private val screenAdjustmentUtil: ScreenAdjustmentUtil, private val context: Context) { +// TODO: +// hotkeyLoaded should be a global val tracking that tracks if a hotkey is laoded or not +// Move Quickload and Quicksave out of shortcuts related to ScreenAdjustmentUtil val hotkeyButtons = Hotkey.entries.map { it.button } fun handleHotkey(bindedButton: Int): Boolean { @@ -18,6 +24,23 @@ class HotkeyUtility(private val screenAdjustmentUtil: ScreenAdjustmentUtil) { Hotkey.CYCLE_LAYOUT.button -> screenAdjustmentUtil.cycleLayouts() Hotkey.CLOSE_GAME.button -> EmulationLifecycleUtil.closeGame() Hotkey.PAUSE_OR_RESUME.button -> EmulationLifecycleUtil.pauseOrResume() + Hotkey.QUICKSAVE.button -> { + NativeLibrary.saveState(NativeLibrary.QUICKSAVE_SLOT) + Toast.makeText(context, + context.getString(R.string.quicksave_saving), + Toast.LENGTH_SHORT).show() + } + Hotkey.QUICKLOAD.button -> { + val hotkeyLoaded = NativeLibrary.loadStateIfAvailable(NativeLibrary.QUICKSAVE_SLOT) + val stringRes = if(hotkeyLoaded) { + R.string.quickload_loading + } else { + R.string.quickload_not_found + } + Toast.makeText(context, + context.getString(stringRes), + Toast.LENGTH_SHORT).show() + } else -> {} } return true diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/Settings.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/Settings.kt index 84f455974..5079e5c83 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/Settings.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/Settings.kt @@ -137,6 +137,8 @@ class Settings { const val HOTKEY_CYCLE_LAYOUT = "hotkey_toggle_layout" const val HOTKEY_CLOSE_GAME = "hotkey_close_game" const val HOTKEY_PAUSE_OR_RESUME = "hotkey_pause_or_resume_game" + const val HOTKEY_QUICKSAVE = "hotkey_quickload" + const val HOTKEY_QUICKlOAD = "hotkey_quickpause" val buttonKeys = listOf( KEY_BUTTON_A, @@ -200,13 +202,17 @@ class Settings { HOTKEY_SCREEN_SWAP, HOTKEY_CYCLE_LAYOUT, HOTKEY_CLOSE_GAME, - HOTKEY_PAUSE_OR_RESUME + HOTKEY_PAUSE_OR_RESUME, + HOTKEY_QUICKSAVE, + HOTKEY_QUICKlOAD ) val hotkeyTitles = listOf( R.string.emulation_swap_screens, R.string.emulation_cycle_landscape_layouts, R.string.emulation_close_game, - R.string.emulation_toggle_pause + R.string.emulation_toggle_pause, + R.string.emulation_quicksave, + R.string.emulation_quickload, ) const val PREF_FIRST_APP_LAUNCH = "FirstApplicationLaunch" diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/view/InputBindingSetting.kt b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/view/InputBindingSetting.kt index 493e2e7d9..a06f6a95e 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/view/InputBindingSetting.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/features/settings/model/view/InputBindingSetting.kt @@ -133,6 +133,8 @@ class InputBindingSetting( Settings.HOTKEY_CYCLE_LAYOUT -> Hotkey.CYCLE_LAYOUT.button Settings.HOTKEY_CLOSE_GAME -> Hotkey.CLOSE_GAME.button Settings.HOTKEY_PAUSE_OR_RESUME -> Hotkey.PAUSE_OR_RESUME.button + Settings.HOTKEY_QUICKSAVE -> Hotkey.QUICKSAVE.button + Settings.HOTKEY_QUICKlOAD -> Hotkey.QUICKLOAD.button else -> -1 } diff --git a/src/android/app/src/main/java/io/github/lime3ds/android/fragments/EmulationFragment.kt b/src/android/app/src/main/java/io/github/lime3ds/android/fragments/EmulationFragment.kt index ae348edc3..9a8ec67e0 100644 --- a/src/android/app/src/main/java/io/github/lime3ds/android/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/io/github/lime3ds/android/fragments/EmulationFragment.kt @@ -504,12 +504,12 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram popupMenu.setOnMenuItemClickListener { when (it.itemId) { R.id.menu_emulation_save_state -> { - showSaveStateSubmenu() + showStateSubmenu(true) true } R.id.menu_emulation_load_state -> { - showLoadStateSubmenu() + showStateSubmenu(false) true } @@ -520,7 +520,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram popupMenu.show() } - private fun showSaveStateSubmenu() { + private fun showStateSubmenu(isSaving: Boolean) { + val savestates = NativeLibrary.getSavestateInfo() val popupMenu = PopupMenu( @@ -530,46 +531,40 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram popupMenu.menu.apply { for (i in 0 until NativeLibrary.SAVESTATE_SLOT_COUNT) { - val slot = i + 1 - val text = getString(R.string.emulation_empty_state_slot, slot) - add(text).setEnabled(true).setOnMenuItemClickListener { - displaySavestateWarning() - NativeLibrary.saveState(slot) + val slot = i + var enableClick = isSaving + val text = if (slot == NativeLibrary.QUICKSAVE_SLOT) { + enableClick = false + getString(R.string.emulation_quicksave_slot) + } else { + getString(R.string.emulation_empty_state_slot, slot) + } + + add(text).setEnabled(enableClick).setOnMenuItemClickListener { + if(isSaving) { + NativeLibrary.saveState(slot) + } else { + NativeLibrary.loadState(slot) + binding.drawerLayout.close() + Toast.makeText(context, + getString(R.string.quickload_loading), + Toast.LENGTH_SHORT).show() + } true } } } savestates?.forEach { - val text = getString(R.string.emulation_occupied_state_slot, it.slot, it.time) - popupMenu.menu.getItem(it.slot - 1).setTitle(text) - } - - popupMenu.show() - } - - private fun showLoadStateSubmenu() { - val savestates = NativeLibrary.getSavestateInfo() - - val popupMenu = PopupMenu( - requireContext(), - binding.inGameMenu.findViewById(R.id.menu_emulation_savestates) - ) - - popupMenu.menu.apply { - for (i in 0 until NativeLibrary.SAVESTATE_SLOT_COUNT) { - val slot = i + 1 - val text = getString(R.string.emulation_empty_state_slot, slot) - add(text).setEnabled(false).setOnMenuItemClickListener { - NativeLibrary.loadState(slot) - true - } + var enableClick = true + val text = if(it.slot == NativeLibrary.QUICKSAVE_SLOT) { + // do not allow saving in quicksave slot + enableClick = !isSaving + getString(R.string.emulation_occupied_quicksave_slot, it.time) + } else{ + getString(R.string.emulation_occupied_state_slot, it.slot, it.time) } - } - - savestates?.forEach { - val text = getString(R.string.emulation_occupied_state_slot, it.slot, it.time) - popupMenu.menu.getItem(it.slot - 1).setTitle(text).setEnabled(true) + popupMenu.menu.getItem(it.slot).setTitle(text).setEnabled(enableClick) } popupMenu.show() diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index bcac3b8c1..2f59bcc93 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -140,6 +140,16 @@ This control must be bound to a gamepad analog stick or D-pad axis! This control must be bound to a gamepad button! + + Quicksave + Quicksave + Quickload + Quicksave - %1$tF %1$tR + Saving… + Loading… + No Quicksave available. + + System Files Download system files to get Mii files, boot the HOME menu, and more