mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-04-08 10:21:29 -06:00
android: Add the ability to edit touch controls overlay without opening a game
This commit is contained in:
parent
3066887ff4
commit
67baf49f51
@ -6,6 +6,7 @@ package org.citra.citra_emu.activities
|
||||
|
||||
import android.Manifest.permission
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.PackageManager
|
||||
@ -116,24 +117,29 @@ class EmulationActivity : AppCompatActivity() {
|
||||
|
||||
EmulationLifecycleUtil.addShutdownHook(onShutdown)
|
||||
|
||||
isEmulationRunning = true
|
||||
instance = this
|
||||
if (!intent.getBooleanExtra(NO_GAME_EDIT_MODE, false)) {
|
||||
isEmulationRunning = true
|
||||
instance = this
|
||||
}
|
||||
|
||||
applyOrientationSettings() // Check for orientation settings at startup
|
||||
|
||||
val game = try {
|
||||
intent.extras?.let { extras ->
|
||||
BundleCompat.getParcelable(extras, "game", Game::class.java)
|
||||
} ?: run {
|
||||
Log.error("[EmulationActivity] Missing game data in intent extras")
|
||||
if (!intent.getBooleanExtra(NO_GAME_EDIT_MODE, false)) {
|
||||
val game = try {
|
||||
intent.extras?.let { extras ->
|
||||
BundleCompat.getParcelable(extras, "game", Game::class.java)
|
||||
} ?: run {
|
||||
Log.error("[EmulationActivity] Missing game data in intent extras")
|
||||
return
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.error("[EmulationActivity] Failed to retrieve game data: ${e.message}")
|
||||
return
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.error("[EmulationActivity] Failed to retrieve game data: ${e.message}")
|
||||
return
|
||||
}
|
||||
|
||||
NativeLibrary.playTimeManagerStart(game.titleId)
|
||||
|
||||
NativeLibrary.playTimeManagerStart(game.titleId)
|
||||
}
|
||||
}
|
||||
|
||||
// On some devices, the system bars will not disappear on first boot or after some
|
||||
@ -174,8 +180,10 @@ class EmulationActivity : AppCompatActivity() {
|
||||
override fun onDestroy() {
|
||||
EmulationLifecycleUtil.removeHook(onShutdown)
|
||||
NativeLibrary.playTimeManagerStop()
|
||||
isEmulationRunning = false
|
||||
instance = null
|
||||
if (!intent.getBooleanExtra(NO_GAME_EDIT_MODE, false)) {
|
||||
isEmulationRunning = false
|
||||
instance = null
|
||||
}
|
||||
secondaryDisplay.releasePresentation()
|
||||
secondaryDisplay.releaseVD()
|
||||
|
||||
@ -544,6 +552,13 @@ class EmulationActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private var instance: EmulationActivity? = null
|
||||
const val NO_GAME_EDIT_MODE = "noGameEditMode"
|
||||
|
||||
fun launchForOverlayEdit(context: Context): Intent {
|
||||
return Intent(context, EmulationActivity::class.java).apply {
|
||||
putExtra(NO_GAME_EDIT_MODE, true)
|
||||
}
|
||||
}
|
||||
|
||||
fun isRunning(): Boolean {
|
||||
return instance?.isEmulationRunning ?: false
|
||||
|
||||
@ -104,6 +104,7 @@ class Settings {
|
||||
const val SECTION_CAMERA = "Camera"
|
||||
const val SECTION_CONTROLS = "Controls"
|
||||
const val SECTION_RENDERER = "Renderer"
|
||||
const val SECTION_INPUT_OVERLAY = "Input Overlay"
|
||||
const val SECTION_LAYOUT = "Layout"
|
||||
const val SECTION_UTILITY = "Utility"
|
||||
const val SECTION_AUDIO = "Audio"
|
||||
@ -242,6 +243,7 @@ class Settings {
|
||||
SECTION_CAMERA,
|
||||
SECTION_CONTROLS,
|
||||
SECTION_RENDERER,
|
||||
SECTION_INPUT_OVERLAY,
|
||||
SECTION_LAYOUT,
|
||||
SECTION_STORAGE,
|
||||
SECTION_UTILITY,
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
package org.citra.citra_emu.features.settings.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.Resources
|
||||
import android.hardware.camera2.CameraAccessException
|
||||
@ -17,6 +18,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.serialization.builtins.IntArraySerializer
|
||||
import org.citra.citra_emu.CitraApplication
|
||||
import org.citra.citra_emu.R
|
||||
import org.citra.citra_emu.activities.EmulationActivity
|
||||
import org.citra.citra_emu.display.ScreenLayout
|
||||
import org.citra.citra_emu.display.StereoMode
|
||||
import org.citra.citra_emu.display.StereoWhichDisplay
|
||||
@ -99,6 +101,7 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
Settings.SECTION_CAMERA -> addCameraSettings(sl)
|
||||
Settings.SECTION_CONTROLS -> addControlsSettings(sl)
|
||||
Settings.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||
Settings.SECTION_INPUT_OVERLAY -> addInputOverlaySettings(sl)
|
||||
Settings.SECTION_LAYOUT -> addLayoutSettings(sl)
|
||||
Settings.SECTION_AUDIO -> addAudioSettings(sl)
|
||||
Settings.SECTION_DEBUG -> addDebugSettings(sl)
|
||||
@ -179,6 +182,14 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
Settings.SECTION_RENDERER
|
||||
)
|
||||
)
|
||||
add(
|
||||
SubmenuSetting(
|
||||
R.string.preferences_input_overlay,
|
||||
0,
|
||||
R.drawable.dpad,
|
||||
Settings.SECTION_INPUT_OVERLAY
|
||||
)
|
||||
)
|
||||
add(
|
||||
SubmenuSetting(
|
||||
R.string.preferences_layout,
|
||||
@ -1127,6 +1138,58 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addInputOverlaySettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_input_overlay))
|
||||
sl.apply {
|
||||
val inputOverlayOpacitySetting = object : AbstractBooleanSetting {
|
||||
private val preferences =
|
||||
PreferenceManager.getDefaultSharedPreferences(CitraApplication.appContext)
|
||||
|
||||
override var boolean: Boolean
|
||||
get() = preferences.getBoolean("EmulationMenuSettings_ShowOverlay", true)
|
||||
set(value) {
|
||||
preferences.edit()
|
||||
.putBoolean("EmulationMenuSettings_ShowOverlay", value)
|
||||
.apply()
|
||||
}
|
||||
|
||||
override val key: String? = null
|
||||
override val section: String? = null
|
||||
override val isRuntimeEditable: Boolean = true
|
||||
override val valueAsString: String
|
||||
get() = preferences.getBoolean("EmulationMenuSettings_ShowOverlay", true)
|
||||
.toString()
|
||||
override val defaultValue = true
|
||||
}
|
||||
|
||||
add(
|
||||
SwitchSetting(
|
||||
inputOverlayOpacitySetting,
|
||||
R.string.enable_input_overlay,
|
||||
0
|
||||
)
|
||||
)
|
||||
add(
|
||||
HeaderSetting(
|
||||
R.string.realtime_edit,
|
||||
)
|
||||
)
|
||||
add(
|
||||
RunnableSetting(
|
||||
R.string.edit_overlay_layout,
|
||||
R.string.edit_overlay_layout_description,
|
||||
false,
|
||||
R.drawable.dpad,
|
||||
runnable = {
|
||||
val intent = EmulationActivity.launchForOverlayEdit(CitraApplication.appContext)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
CitraApplication.appContext.startActivity(intent)
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addLayoutSettings(sl: ArrayList<SettingsItem>) {
|
||||
settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_layout))
|
||||
sl.apply {
|
||||
|
||||
@ -129,6 +129,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (args.noGameEditMode) {
|
||||
return
|
||||
}
|
||||
|
||||
val intent = requireActivity().intent
|
||||
var intentUri: Uri? = intent.data
|
||||
val oldIntentInfo = Pair(
|
||||
@ -212,6 +216,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
return
|
||||
}
|
||||
|
||||
if (args.noGameEditMode) {
|
||||
setupNoGameEditMode()
|
||||
return
|
||||
}
|
||||
|
||||
binding.surfaceEmulation.holder.addCallback(this)
|
||||
binding.doneControlConfig.setOnClickListener {
|
||||
binding.doneControlConfig.visibility = View.GONE
|
||||
@ -503,6 +512,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
if (args.noGameEditMode) {
|
||||
return
|
||||
}
|
||||
|
||||
Choreographer.getInstance().postFrameCallback(this)
|
||||
if (NativeLibrary.isRunning()) {
|
||||
emulationState.unpause()
|
||||
@ -530,7 +544,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
if (NativeLibrary.isRunning()) {
|
||||
if (!args.noGameEditMode && NativeLibrary.isRunning()) {
|
||||
emulationState.pause()
|
||||
}
|
||||
Choreographer.getInstance().removeFrameCallback(this)
|
||||
@ -574,6 +588,37 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupNoGameEditMode() {
|
||||
binding.surfaceInputOverlay.post {
|
||||
binding.surfaceInputOverlay.refreshControls(true)
|
||||
}
|
||||
|
||||
binding.doneControlConfig.setOnClickListener {
|
||||
finishNoGameEditMode()
|
||||
}
|
||||
|
||||
binding.doneControlConfig.visibility = View.VISIBLE
|
||||
binding.surfaceInputOverlay.setIsInEditMode(true)
|
||||
binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
binding.surfaceInputOverlay.visibility = View.VISIBLE
|
||||
binding.loadingIndicator.visibility = View.GONE
|
||||
|
||||
// in no game edit mode, back = done
|
||||
requireActivity().onBackPressedDispatcher.addCallback(
|
||||
viewLifecycleOwner,
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
finishNoGameEditMode()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun finishNoGameEditMode() {
|
||||
binding.surfaceInputOverlay.setIsInEditMode(false)
|
||||
emulationActivity.finish()
|
||||
}
|
||||
|
||||
private fun showSavestateMenu() {
|
||||
val popupMenu = PopupMenu(
|
||||
requireContext(),
|
||||
@ -1236,7 +1281,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun resetInputOverlay() {
|
||||
fun resetInputOverlay() {
|
||||
resetAllScales()
|
||||
preferences.edit()
|
||||
.putInt("controlOpacity", 50)
|
||||
|
||||
@ -570,7 +570,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshControls() {
|
||||
fun refreshControls(noGameEdit: Boolean = false) {
|
||||
// Remove all the overlay buttons from the HashSet.
|
||||
overlayButtons.clear()
|
||||
overlayDpads.clear()
|
||||
@ -583,7 +583,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
|
||||
}
|
||||
|
||||
// Add all the enabled overlay items back to the HashSet.
|
||||
if (EmulationMenuSettings.showOverlay) {
|
||||
if (noGameEdit || EmulationMenuSettings.showOverlay) {
|
||||
addOverlayControls(orientation)
|
||||
}
|
||||
invalidate()
|
||||
|
||||
@ -15,6 +15,10 @@
|
||||
app:argType="org.citra.citra_emu.model.Game"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
<argument
|
||||
android:name="noGameEditMode"
|
||||
app:argType="boolean"
|
||||
android:defaultValue="false" />
|
||||
</fragment>
|
||||
|
||||
<activity
|
||||
|
||||
@ -948,4 +948,11 @@
|
||||
<string name="decompress_failed">Decompression failed.</string>
|
||||
<string name="compress_decompress_installed_app">Already installed applications cannot be compressed or decompressed.</string>
|
||||
|
||||
<!-- Input Overlay Settings -->
|
||||
<string name="preferences_input_overlay">Input Overlay</string>
|
||||
<string name="enable_input_overlay">Enable Touch Input Overlay</string>
|
||||
<string name="edit_overlay_layout">Edit Touch Input Overlay</string>
|
||||
<string name="realtime_edit">Edit in realtime</string>
|
||||
<string name="edit_overlay_layout_description">Edit the touch overlay without having to open a game.</string>
|
||||
|
||||
</resources>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user