From d912aa5e3df4fa27f951656cb6c1b49fcd5c1a94 Mon Sep 17 00:00:00 2001 From: Philipp Schmidt Date: Wed, 23 Sep 2026 10:49:46 +0200 Subject: [PATCH] fix: make the settings tap sequence deterministic The sequence (10 taps bottom right, then 10 bottom left) was hard to trigger for two reasons. The counters never expired. The overlays return false, so the touch also reaches the dashboard underneath and an ordinary tap on a card in either corner increments them. Between attempts the sequence therefore started from an unknown state. And the threshold was an equality check: one tap too many left clicksButtonRight at 11, after which every tap on the left overlay hit the else branch and reset, wedging the gesture until the app restarted. Expire the counters after 2 s without a tap and compare with >=. Co-Authored-By: Claude Opus 5 --- .../shellyelevatev2/MainActivity.kt | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/me/rapierxbox/shellyelevatev2/MainActivity.kt b/app/src/main/java/me/rapierxbox/shellyelevatev2/MainActivity.kt index ef175b2..5311fce 100644 --- a/app/src/main/java/me/rapierxbox/shellyelevatev2/MainActivity.kt +++ b/app/src/main/java/me/rapierxbox/shellyelevatev2/MainActivity.kt @@ -9,6 +9,7 @@ import android.content.IntentFilter import android.content.pm.PackageManager import android.net.http.SslError import android.os.Bundle +import android.os.SystemClock import android.util.Log import android.view.KeyEvent import android.view.MotionEvent @@ -93,6 +94,7 @@ class MainActivity : ComponentActivity() { private var clicksButtonRight: Int = 0 private var clicksButtonLeft: Int = 0 + private var lastSettingsTapAtMs: Long = 0 private val settingsChangedBroadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { @@ -351,18 +353,34 @@ class MainActivity : ComponentActivity() { @SuppressLint("ClickableViewAccessibility") private fun setupSettingsButtons() { binding.settingButtonOverlayRight.setOnTouchListener { _, event -> - if (event.action == MotionEvent.ACTION_DOWN) clicksButtonRight++ + if (event.action == MotionEvent.ACTION_DOWN) { + expireStaleSettingsTaps() + clicksButtonRight++ + } false } binding.settingButtonOverlayLeft.setOnTouchListener { _, event -> if (event.action == MotionEvent.ACTION_DOWN) { - if (clicksButtonRight == 10) clicksButtonLeft++ else resetClicks() - if (clicksButtonLeft == 10) startSettingsActivity() + expireStaleSettingsTaps() + // >= not ==: the overlays pass the touch on to the dashboard underneath, so a + // tap meant for a card in that corner counts too. overshooting the threshold + // used to wedge the sequence for good, since every later tap hit the reset + if (clicksButtonRight >= SETTINGS_TAPS) clicksButtonLeft++ else resetClicks() + if (clicksButtonLeft >= SETTINGS_TAPS) startSettingsActivity() } false } } + // the counters used to live forever, so taps from ordinary dashboard use accumulated + // between attempts and the sequence started from an unknown state. a pause longer than + // the timeout was never someone entering it + private fun expireStaleSettingsTaps() { + val now = SystemClock.elapsedRealtime() + if (now - lastSettingsTapAtMs > SETTINGS_TAP_TIMEOUT_MS) resetClicks() + lastSettingsTapAtMs = now + } + private fun startSettingsActivity() { resetClicks() startActivity(Intent(this, SettingsActivity::class.java)) @@ -905,4 +923,10 @@ class MainActivity : ComponentActivity() { cancelRetry() super.onStop() } + + companion object { + // taps per corner, bottom right first then bottom left + private const val SETTINGS_TAPS = 10 + private const val SETTINGS_TAP_TIMEOUT_MS = 2000L + } } \ No newline at end of file