diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt index 184392f75f..1b23118de3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.kt @@ -8,6 +8,8 @@ import android.graphics.Rect import android.net.Uri import android.os.Build import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.util.SparseIntArray import android.view.KeyEvent import android.view.MenuItem @@ -61,6 +63,7 @@ import org.dolphinemu.dolphinemu.ui.main.ThemeProvider import org.dolphinemu.dolphinemu.utils.AfterDirectoryInitializationRunner import org.dolphinemu.dolphinemu.utils.DirectoryInitialization import org.dolphinemu.dolphinemu.utils.FileBrowserHelper +import org.dolphinemu.dolphinemu.utils.RateLimiter import org.dolphinemu.dolphinemu.utils.ThemeHelper import kotlin.math.roundToInt @@ -88,6 +91,10 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { private lateinit var binding: ActivityEmulationBinding + private val refreshInputOverlayRateLimiter = RateLimiter(Handler(Looper.getMainLooper()), 100) { + emulationFragment?.refreshInputOverlay() + } + private val requestChangeDisc = registerForActivityResult( ActivityResultContracts.GetContent() ) { uri: Uri? -> @@ -561,6 +568,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { ) { _: DialogInterface?, indexSelected: Int, isChecked: Boolean -> BooleanSetting.valueOf(gcSettingBase + indexSelected) .setBoolean(settings, isChecked) + emulationFragment?.refreshInputOverlay() } } InputOverlay.OVERLAY_WIIMOTE_CLASSIC -> { @@ -575,6 +583,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { ) { _: DialogInterface?, indexSelected: Int, isChecked: Boolean -> BooleanSetting.valueOf(classicSettingBase + indexSelected) .setBoolean(settings, isChecked) + emulationFragment?.refreshInputOverlay() } } InputOverlay.OVERLAY_WIIMOTE_NUNCHUK -> { @@ -596,6 +605,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { ) { _: DialogInterface?, indexSelected: Int, isChecked: Boolean -> BooleanSetting.valueOf(nunchukSettingBase + translateToSettingsIndex(indexSelected)) .setBoolean(settings, isChecked) + emulationFragment?.refreshInputOverlay() } } else -> { @@ -611,14 +621,13 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { ) { _: DialogInterface?, indexSelected: Int, isChecked: Boolean -> BooleanSetting.valueOf(wiimoteSettingBase + indexSelected) .setBoolean(settings, isChecked) + emulationFragment?.refreshInputOverlay() } } } builder - .setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> - emulationFragment?.refreshInputOverlay() - } + .setPositiveButton(R.string.ok, null) .show() } @@ -640,6 +649,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { ) { _: DialogInterface?, indexSelected: Int, isChecked: Boolean -> BooleanSetting .valueOf(gcSettingBase + indexSelected).setBoolean(settings, isChecked) + emulationFragment?.refreshInputOverlay() } } else if (currentController == InputOverlay.OVERLAY_WIIMOTE_CLASSIC) { val wiiClassicEnabledButtons = BooleanArray(14) @@ -653,6 +663,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { ) { _: DialogInterface?, indexSelected: Int, isChecked: Boolean -> BooleanSetting.valueOf(classicSettingBase + indexSelected) .setBoolean(settings, isChecked) + emulationFragment?.refreshInputOverlay() } } else { val wiiEnabledButtons = BooleanArray(11) @@ -667,6 +678,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { ) { _: DialogInterface?, indexSelected: Int, isChecked: Boolean -> BooleanSetting .valueOf(wiiSettingBase + indexSelected).setBoolean(settings, isChecked) + emulationFragment?.refreshInputOverlay() } } else { builder.setMultiChoiceItems( @@ -674,6 +686,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { ) { _: DialogInterface?, indexSelected: Int, isChecked: Boolean -> BooleanSetting .valueOf(wiiSettingBase + indexSelected).setBoolean(settings, isChecked) + emulationFragment?.refreshInputOverlay() } } } @@ -682,7 +695,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { .setNeutralButton(R.string.emulation_toggle_all) { _: DialogInterface?, _: Int -> emulationFragment!!.toggleInputOverlayVisibility(settings) } - .setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> emulationFragment?.refreshInputOverlay() } + .setPositiveButton(R.string.ok, null) .show() } @@ -707,10 +720,9 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { settings, InputOverlayPointer.DOUBLE_TAP_OPTIONS[which] ) - } - .setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> emulationFragment?.initInputPointer() } + .setPositiveButton(R.string.ok, null) .show() } @@ -721,9 +733,11 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { valueTo = 150f value = IntSetting.MAIN_CONTROL_SCALE.int.toFloat() stepSize = 1f - addOnChangeListener(Slider.OnChangeListener { _: Slider?, progress: Float, _: Boolean -> - dialogBinding.inputScaleValue.text = "${(progress.toInt() + 50)}%" - }) + addOnChangeListener { _: Slider?, value: Float, _: Boolean -> + dialogBinding.inputScaleValue.text = "${(value.toInt() + 50)}%" + IntSetting.MAIN_CONTROL_SCALE.setInt(settings, value.toInt()) + refreshInputOverlayRateLimiter.run() + } } inputScaleValue.text = "${(dialogBinding.inputScaleSlider.value.toInt() + 50)}%" @@ -732,9 +746,11 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { valueTo = 100f value = IntSetting.MAIN_CONTROL_OPACITY.int.toFloat() stepSize = 1f - addOnChangeListener(Slider.OnChangeListener { _: Slider?, progress: Float, _: Boolean -> - inputOpacityValue.text = progress.toInt().toString() + "%" - }) + addOnChangeListener { _: Slider?, value: Float, _: Boolean -> + inputOpacityValue.text = value.toInt().toString() + "%" + IntSetting.MAIN_CONTROL_OPACITY.setInt(settings, value.toInt()) + refreshInputOverlayRateLimiter.run() + } } inputOpacityValue.text = inputOpacitySlider.value.toInt().toString() + "%" } @@ -742,17 +758,7 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { MaterialAlertDialogBuilder(this) .setTitle(R.string.emulation_control_adjustments) .setView(dialogBinding.root) - .setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> - IntSetting.MAIN_CONTROL_SCALE.setInt( - settings, - dialogBinding.inputScaleSlider.value.toInt() - ) - IntSetting.MAIN_CONTROL_OPACITY.setInt( - settings, - dialogBinding.inputOpacitySlider.value.toInt() - ) - emulationFragment?.refreshInputOverlay() - } + .setPositiveButton(R.string.ok, null) .setNeutralButton(R.string.default_values) { _: DialogInterface?, _: Int -> IntSetting.MAIN_CONTROL_SCALE.delete(settings) IntSetting.MAIN_CONTROL_OPACITY.delete(settings) @@ -858,10 +864,9 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { entries.toArray(arrayOf()), checkedItem ) { _: DialogInterface?, indexSelected: Int -> controllerSetting.setInt(settings, values[indexSelected]) - } - .setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> emulationFragment?.refreshInputOverlay() } + .setPositiveButton(R.string.ok, null) .setNeutralButton( R.string.emulation_more_controller_settings ) { _: DialogInterface?, _: Int -> SettingsActivity.launch(this, MenuTag.SETTINGS) } @@ -876,10 +881,9 @@ class EmulationActivity : AppCompatActivity(), ThemeProvider { IntSetting.MAIN_IR_MODE.int ) { _: DialogInterface?, indexSelected: Int -> IntSetting.MAIN_IR_MODE.setInt(settings, indexSelected) - } - .setPositiveButton(R.string.ok) { _: DialogInterface?, _: Int -> emulationFragment?.refreshOverlayPointer() } + .setPositiveButton(R.string.ok, null) .show() } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/RateLimiter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/RateLimiter.kt new file mode 100644 index 0000000000..498e128875 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/RateLimiter.kt @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.utils + +import android.os.Handler +import android.os.SystemClock + +class RateLimiter( + private val handler: Handler, + private val delayBetweenRunsMs: Int, + private val runnable: Runnable +) { + private var nextAllowedRun = 0L + private var pendingRun = false + + fun run() { + if (!pendingRun) { + val time = SystemClock.uptimeMillis() + if (time >= nextAllowedRun) { + runnable.run() + nextAllowedRun = time + delayBetweenRunsMs + } else { + pendingRun = true + handler.postAtTime({ + runnable.run() + nextAllowedRun = SystemClock.uptimeMillis() + delayBetweenRunsMs + pendingRun = false + }, nextAllowedRun) + } + } + } +}