mirror of
https://github.com/izzy2lost/WeeU.git
synced 2026-07-06 00:19:59 -07:00
Add option for pad position & removed side menu button
This commit is contained in:
@@ -9,20 +9,44 @@ class PreferenceDelegate<T>(
|
||||
private val sharedPreferences: SharedPreferences,
|
||||
private val defaultValue: T,
|
||||
private val getter: SharedPreferences.(String, T) -> T,
|
||||
private val setter: SharedPreferences.Editor.(String, T) -> SharedPreferences.Editor
|
||||
private val setter: SharedPreferences.Editor.(String, T) -> Unit
|
||||
) : ReadWriteProperty<Any, T> {
|
||||
|
||||
private var cachedValue: T? = null
|
||||
private var isCached = false
|
||||
|
||||
override fun getValue(thisRef: Any, property: KProperty<*>): T {
|
||||
val key = "${thisRef::class.simpleName}_${property.name}".uppercase()
|
||||
return sharedPreferences.getter(key, defaultValue)
|
||||
|
||||
if (!isCached) {
|
||||
cachedValue = sharedPreferences.getter(key, defaultValue)
|
||||
isCached = true
|
||||
}
|
||||
|
||||
return cachedValue ?: defaultValue
|
||||
}
|
||||
|
||||
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
|
||||
val key = "${thisRef::class.simpleName}_${property.name}".uppercase()
|
||||
cachedValue = value
|
||||
isCached = true
|
||||
sharedPreferences.edit { setter(key, value) }
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : Enum<T>> SharedPreferences.enumPref(default: T) =
|
||||
PreferenceDelegate(
|
||||
this,
|
||||
default,
|
||||
{ key, default ->
|
||||
val enumOrdinal = getInt(key, default.ordinal)
|
||||
enumValues<T>().firstOrNull { it.ordinal == enumOrdinal } ?: default
|
||||
},
|
||||
{ key, value ->
|
||||
putInt(key, value.ordinal)
|
||||
}
|
||||
)
|
||||
|
||||
fun SharedPreferences.booleanPref(default: Boolean) =
|
||||
PreferenceDelegate(
|
||||
this,
|
||||
|
||||
@@ -5,8 +5,18 @@ import android.content.SharedPreferences
|
||||
import info.cemu.cemu.common.ui.localization.DEFAULT_LANGUAGE
|
||||
import kotlin.getValue
|
||||
|
||||
class EmulationScreenSettings(sharedPreferences: SharedPreferences) {
|
||||
var isDrawerButtonVisible by sharedPreferences.booleanPref(false)
|
||||
enum class GamePadPosition {
|
||||
ABOVE,
|
||||
BELOW,
|
||||
LEFT,
|
||||
RIGHT;
|
||||
|
||||
fun isVertical() = this == ABOVE || this == BELOW
|
||||
fun appearsAfterTV() = this == BELOW || this == RIGHT
|
||||
}
|
||||
|
||||
class EmulationSettings(sharedPreferences: SharedPreferences) {
|
||||
var gamePadPosition by sharedPreferences.enumPref(GamePadPosition.RIGHT)
|
||||
}
|
||||
|
||||
class GuiSettings(sharedPreferences: SharedPreferences) {
|
||||
@@ -28,7 +38,7 @@ object SettingsManager {
|
||||
|
||||
private lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
val emulationScreenSettings by lazy { EmulationScreenSettings(sharedPreferences) }
|
||||
val emulationSettings by lazy { EmulationSettings(sharedPreferences) }
|
||||
|
||||
val guiSettings by lazy { GuiSettings(sharedPreferences) }
|
||||
|
||||
|
||||
@@ -29,13 +29,15 @@ import info.cemu.cemu.databinding.LayoutSideMenuEmulationBinding
|
||||
import info.cemu.cemu.databinding.LayoutSideMenuTextItemBinding
|
||||
import info.cemu.cemu.nativeinterface.NativeEmulation
|
||||
import info.cemu.cemu.nativeinterface.NativeException
|
||||
import info.cemu.cemu.common.settings.EmulationScreenSettings
|
||||
import info.cemu.cemu.common.settings.EmulationSettings
|
||||
import info.cemu.cemu.common.settings.GamePadPosition
|
||||
import info.cemu.cemu.common.settings.InputOverlaySettings
|
||||
import info.cemu.cemu.common.settings.SettingsManager
|
||||
import info.cemu.cemu.emulation.inputoverlay.InputOverlaySurfaceView
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
class EmulationActivity : AppCompatActivity() {
|
||||
private inner class CanvasSurfaceHolderCallback(val isMainCanvas: Boolean) :
|
||||
SurfaceHolder.Callback {
|
||||
@@ -68,21 +70,25 @@ class EmulationActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private var emulationTextInputDialog: AlertDialog? = null
|
||||
private var isGameRunning = false
|
||||
private var padCanvas: SurfaceView? = null
|
||||
private var toast: Toast? = null
|
||||
private lateinit var binding: ActivityEmulationBinding
|
||||
private var isMotionEnabled = false
|
||||
private lateinit var inputOverlaySettings: InputOverlaySettings
|
||||
private lateinit var emulationScreenSettings: EmulationScreenSettings
|
||||
private lateinit var emulationSettings: EmulationSettings
|
||||
private lateinit var inputOverlaySurfaceView: InputOverlaySurfaceView
|
||||
private lateinit var sensorManager: SensorManager
|
||||
private var toast: Toast? = null
|
||||
|
||||
private var isGameRunning = false
|
||||
private var isMotionEnabled = false
|
||||
private var isDrawerLocked = false
|
||||
|
||||
private var hasEmulationError = false
|
||||
|
||||
override fun onGenericMotionEvent(event: MotionEvent): Boolean {
|
||||
if (InputHandler.onMotionEvent(event)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return super.onGenericMotionEvent(event)
|
||||
}
|
||||
|
||||
@@ -90,6 +96,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||
if (InputHandler.onKeyEvent(event)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return super.dispatchKeyEvent(event)
|
||||
}
|
||||
|
||||
@@ -97,15 +104,19 @@ class EmulationActivity : AppCompatActivity() {
|
||||
val extras = intent.extras
|
||||
val data = intent.data
|
||||
var launchPath: String? = null
|
||||
|
||||
if (extras != null) {
|
||||
launchPath = extras.getString(EXTRA_LAUNCH_PATH)
|
||||
}
|
||||
|
||||
if (launchPath == null && data != null) {
|
||||
launchPath = data.toString()
|
||||
}
|
||||
|
||||
if (launchPath == null) {
|
||||
throw RuntimeException("launchPath is null")
|
||||
}
|
||||
|
||||
return launchPath
|
||||
}
|
||||
|
||||
@@ -117,20 +128,27 @@ class EmulationActivity : AppCompatActivity() {
|
||||
emulationActivityInstance = WeakReference(this)
|
||||
|
||||
inputOverlaySettings = SettingsManager.inputOverlaySettings
|
||||
emulationScreenSettings = SettingsManager.emulationScreenSettings
|
||||
emulationSettings = SettingsManager.emulationSettings
|
||||
sensorManager = SensorManager(this)
|
||||
sensorManager.setDeviceRotationProvider(deviceRotationProvider = { display.rotation })
|
||||
|
||||
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
showExitConfirmationDialog()
|
||||
}
|
||||
override fun handleOnBackPressed() = toggleDrawer()
|
||||
})
|
||||
|
||||
initializeView(getLaunchPath())
|
||||
|
||||
setContentView(binding.root)
|
||||
}
|
||||
|
||||
private fun toggleDrawer() {
|
||||
if (binding.drawerLayout.isOpen) {
|
||||
binding.drawerLayout.close()
|
||||
} else {
|
||||
binding.drawerLayout.open()
|
||||
}
|
||||
}
|
||||
|
||||
private fun destroyPadCanvas() {
|
||||
if (padCanvas == null) {
|
||||
return
|
||||
@@ -190,18 +208,30 @@ class EmulationActivity : AppCompatActivity() {
|
||||
|
||||
private fun LayoutSideMenuEmulationBinding.configureSideMenu() {
|
||||
val isInputOverlayEnabled = inputOverlaySettings.isOverlayEnabled
|
||||
|
||||
enableMotionCheckbox.configure(
|
||||
label = tr("Enable motion"),
|
||||
onCheckChanged = ::setMotionEnabled
|
||||
)
|
||||
|
||||
lockDrawerCheckbox.configure(
|
||||
tr("Lock drawer"),
|
||||
onCheckChanged = {
|
||||
isDrawerLocked = !isDrawerLocked
|
||||
binding.drawerLayout.setLockedMode(isDrawerLocked)
|
||||
}
|
||||
)
|
||||
|
||||
replaceTvWithPadCheckbox.configure(
|
||||
label = tr("Replace TV with PAD"),
|
||||
onCheckChanged = NativeEmulation::setReplaceTVWithPadView
|
||||
)
|
||||
|
||||
showPadCheckbox.configure(
|
||||
label = tr(text = "Show PAD"),
|
||||
onCheckChanged = ::setPadViewVisibility
|
||||
)
|
||||
|
||||
showInputOverlayCheckbox.configure(
|
||||
tr(text = "Show input overlay"),
|
||||
initialCheckedStatus = isInputOverlayEnabled,
|
||||
@@ -211,6 +241,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||
inputOverlaySurfaceView.setVisible(showInputOverlay)
|
||||
}
|
||||
)
|
||||
|
||||
editInputsMenuItem.configure(
|
||||
label = tr("Edit inputs"),
|
||||
isEnabled = isInputOverlayEnabled,
|
||||
@@ -219,15 +250,16 @@ class EmulationActivity : AppCompatActivity() {
|
||||
binding.finishEditInputsButton.visibility = View.VISIBLE
|
||||
binding.moveInputsButton.performClick()
|
||||
})
|
||||
|
||||
resetInputOverlayMenuItem.configure(
|
||||
tr(text = "Reset input overlay"),
|
||||
isEnabled = isInputOverlayEnabled,
|
||||
onClick = inputOverlaySurfaceView::resetInputs
|
||||
)
|
||||
exitMenuItem.configure(tr("Exit"), onClick = onBackPressedDispatcher::onBackPressed)
|
||||
|
||||
exitMenuItem.configure(tr("Exit"), onClick = ::showExitConfirmationDialog)
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun initializeView(launchPath: String) {
|
||||
setFullscreen()
|
||||
|
||||
@@ -263,14 +295,6 @@ class EmulationActivity : AppCompatActivity() {
|
||||
toastMessage(tr("Exited input edit mode"))
|
||||
}
|
||||
|
||||
if (emulationScreenSettings.isDrawerButtonVisible) {
|
||||
binding.emulationSettingsButton.setOnClickListener { binding.drawerLayout.open() }
|
||||
binding.drawerLayout.setLockedMode(true)
|
||||
} else {
|
||||
binding.emulationSettingsButton.visibility = View.GONE
|
||||
}
|
||||
|
||||
val mainCanvas = binding.mainCanvas
|
||||
try {
|
||||
val testSurfaceTexture = SurfaceTexture(0)
|
||||
val testSurface = Surface(testSurfaceTexture)
|
||||
@@ -282,6 +306,7 @@ class EmulationActivity : AppCompatActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
val mainCanvas = binding.mainCanvas
|
||||
val mainCanvasHolder = mainCanvas.holder
|
||||
mainCanvasHolder.addCallback(CanvasSurfaceHolderCallback(isMainCanvas = true))
|
||||
mainCanvasHolder.addCallback(object : SurfaceChangedListener() {
|
||||
@@ -312,9 +337,11 @@ class EmulationActivity : AppCompatActivity() {
|
||||
|
||||
private fun startGame(launchPath: String) {
|
||||
val result = NativeEmulation.startGame(launchPath)
|
||||
|
||||
if (result == NativeEmulation.StartGameStatusCode.SUCCESSFUL) {
|
||||
return
|
||||
}
|
||||
|
||||
val errorMessage = when (result) {
|
||||
NativeEmulation.StartGameStatusCode.ERROR_GAME_BASE_FILES_NOT_FOUND -> tr("Unable to launch game because the base files were not found.")
|
||||
NativeEmulation.StartGameStatusCode.ERROR_NO_DISC_KEY -> tr("Could not decrypt title. Make sure that keys.txt contains the correct disc key for this title.")
|
||||
@@ -343,15 +370,36 @@ class EmulationActivity : AppCompatActivity() {
|
||||
sensorManager.pauseListening()
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun createPadCanvas() {
|
||||
if (padCanvas != null) {
|
||||
return
|
||||
}
|
||||
val padCanvas = SurfaceView(this)
|
||||
|
||||
val padCanvasViewIndex: Int
|
||||
val canvasLayoutParams: ViewGroup.LayoutParams
|
||||
val orientation: Int
|
||||
|
||||
val position = emulationSettings.gamePadPosition
|
||||
|
||||
if (position.isVertical()) {
|
||||
orientation = LinearLayout.VERTICAL
|
||||
canvasLayoutParams =
|
||||
LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f)
|
||||
} else {
|
||||
orientation = LinearLayout.HORIZONTAL
|
||||
canvasLayoutParams =
|
||||
LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f)
|
||||
}
|
||||
|
||||
padCanvasViewIndex = if (position.appearsAfterTV()) 1 else 0
|
||||
|
||||
binding.mainCanvas.layoutParams = canvasLayoutParams
|
||||
binding.canvasesLayout.orientation = orientation
|
||||
binding.canvasesLayout.addView(
|
||||
padCanvas,
|
||||
LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f)
|
||||
padCanvasViewIndex,
|
||||
canvasLayoutParams
|
||||
)
|
||||
padCanvas.holder.addCallback(CanvasSurfaceHolderCallback(false))
|
||||
padCanvas.setOnTouchListener(CanvasOnTouchListener(false))
|
||||
|
||||
+15
-8
@@ -4,10 +4,10 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.lifecycle.compose.dropUnlessResumed
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import info.cemu.cemu.common.settings.GamePadPosition
|
||||
import info.cemu.cemu.common.ui.components.Button
|
||||
import info.cemu.cemu.common.ui.components.ScreenContent
|
||||
import info.cemu.cemu.common.ui.components.SingleSelection
|
||||
import info.cemu.cemu.common.ui.components.Toggle
|
||||
import info.cemu.cemu.common.ui.localization.tr
|
||||
import info.cemu.cemu.nativeinterface.NativeSettings
|
||||
|
||||
@@ -55,17 +55,24 @@ fun GeneralSettingsScreen(
|
||||
NativeSettings.ConsoleLanguage.TAIWANESE,
|
||||
),
|
||||
)
|
||||
Toggle(
|
||||
label = tr("Side menu button"),
|
||||
description = tr("Show the emulation side menu button"),
|
||||
initialCheckedState = { generalSettingsViewModel.emulationScreenSettings.isDrawerButtonVisible },
|
||||
onCheckedChanged = {
|
||||
generalSettingsViewModel.emulationScreenSettings.isDrawerButtonVisible = it
|
||||
}
|
||||
|
||||
SingleSelection(
|
||||
label = tr("GamePad position"),
|
||||
initialChoice = { generalSettingsViewModel.emulationSettings.gamePadPosition },
|
||||
onChoiceChanged = { generalSettingsViewModel.emulationSettings.gamePadPosition = it },
|
||||
choiceToString = { gamePadPositionToString(it) },
|
||||
choices = GamePadPosition.entries,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun gamePadPositionToString(position: GamePadPosition) = when (position) {
|
||||
GamePadPosition.ABOVE -> tr("Above")
|
||||
GamePadPosition.BELOW -> tr("Below")
|
||||
GamePadPosition.LEFT -> tr("Left")
|
||||
GamePadPosition.RIGHT -> tr("Right")
|
||||
}
|
||||
|
||||
private fun consoleLanguageToString(channels: Int): String = when (channels) {
|
||||
NativeSettings.ConsoleLanguage.JAPANESE -> tr("Japanese")
|
||||
NativeSettings.ConsoleLanguage.ENGLISH -> tr("English")
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@ package info.cemu.cemu.settings.general
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import info.cemu.cemu.common.settings.EmulationScreenSettings
|
||||
import info.cemu.cemu.common.settings.EmulationSettings
|
||||
import info.cemu.cemu.common.settings.GuiSettings
|
||||
import info.cemu.cemu.common.settings.SettingsManager
|
||||
import info.cemu.cemu.common.ui.localization.getAvailableLanguages
|
||||
@@ -10,7 +10,7 @@ import info.cemu.cemu.common.ui.localization.getAvailableLanguages
|
||||
class GeneralSettingsViewModel : ViewModel() {
|
||||
val languages: List<String>
|
||||
val languageToDisplayNameMap: Map<String, String>
|
||||
val emulationScreenSettings: EmulationScreenSettings = SettingsManager.emulationScreenSettings
|
||||
val emulationSettings: EmulationSettings = SettingsManager.emulationSettings
|
||||
val guiSettings: GuiSettings = SettingsManager.guiSettings
|
||||
|
||||
init {
|
||||
|
||||
@@ -61,13 +61,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_resize" />
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/emulation_settings_button"
|
||||
style="?attr/materialIconButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_settings" />
|
||||
</RelativeLayout>
|
||||
|
||||
<include
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
android:id="@+id/enable_motion_checkbox"
|
||||
layout="@layout/layout_side_menu_checkbox_item" />
|
||||
|
||||
<include
|
||||
android:id="@+id/lock_drawer_checkbox"
|
||||
layout="@layout/layout_side_menu_checkbox_item" />
|
||||
|
||||
<include
|
||||
android:id="@+id/replace_tv_with_pad_checkbox"
|
||||
layout="@layout/layout_side_menu_checkbox_item" />
|
||||
|
||||
Reference in New Issue
Block a user