From 5de7bd235b51a5197f0e58dee1292bcd04d21fe7 Mon Sep 17 00:00:00 2001 From: FineFindus Date: Tue, 6 Aug 2024 15:18:03 +0200 Subject: [PATCH 1/3] feat(tiles/dimmer): restart cycle when torch is disabled externally Changes the DimmerSettingsTile to always start from the dimmest mode if the toch is disabled. Previously it would continue from the last mode, which was confusing to the user, since suddenly the tile acted different when tapped. --- .../com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt b/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt index abcd065..e088ee5 100644 --- a/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt +++ b/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt @@ -28,11 +28,15 @@ import com.cyb3rko.flashdim.utils.Safe class DimmerSettingsTile : TileService() { private var description = "" + private var enabled = false override fun onClick() { if (qsTile.state == Tile.STATE_UNAVAILABLE) return Safe.initialize(applicationContext) - val mode = Safe.getInt(Safe.QUICKTILE_DIM_MODE, DIMMER_MIN) + var mode = Safe.getInt(Safe.QUICKTILE_DIM_MODE, DIMMER_MIN) + if (!enabled) + // torch was disabled externally, continue again from initial state + mode = DIMMER_MIN description = mode.description() val maxLevel = Safe.getInt(Safe.MAX_LEVEL, -1) @@ -73,6 +77,7 @@ class DimmerSettingsTile : TileService() { if (description.isNotEmpty()) qsTile.subtitle = "State: $description" qsTile.state = if (enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE qsTile.updateTile() + this@DimmerSettingsTile.enabled = enabled } }, Handler(Looper.getMainLooper()) From 02f06e0879b9756ab4a7fa086a1951d5634d3230 Mon Sep 17 00:00:00 2001 From: FineFindus Date: Tue, 6 Aug 2024 15:21:16 +0200 Subject: [PATCH 2/3] fix(Tiles/Dimmer): always display off state if torch is disabled Fixes an issue, where if the torch was externally disabled, the mode should continue to show the (now outdated) mode instead of off. --- .../kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt b/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt index e088ee5..e7d45af 100644 --- a/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt +++ b/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt @@ -74,7 +74,9 @@ class DimmerSettingsTile : TileService() { object : CameraManager.TorchCallback() { override fun onTorchModeChanged(cameraId: String, enabled: Boolean) { if (qsTile == null) return - if (description.isNotEmpty()) qsTile.subtitle = "State: $description" + qsTile.subtitle = + if (description.isNotEmpty() && enabled) "State: $description" + else "State: ${DIMMER_OFF.description()}" qsTile.state = if (enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE qsTile.updateTile() this@DimmerSettingsTile.enabled = enabled From 52ab477e78f09d91de960367d2b5a0f7012c7c9a Mon Sep 17 00:00:00 2001 From: FineFindus Date: Sun, 11 Aug 2024 10:52:11 +0200 Subject: [PATCH 3/3] feat(QsTile/Dimmer): continue cycle from current strength level Continues the cycle from the current strength level, by mapping the it to the nearest cycle level. For example, if the user has already enabled the torch through another app, and e.g. set it to 2 (out of 10) the tile will be show the min state. --- .../flashdim/tiles/DimmerSettingsTile.kt | 72 +++++++++---------- .../kotlin/com/cyb3rko/flashdim/utils/Safe.kt | 1 - 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt b/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt index e7d45af..6fa4cb3 100644 --- a/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt +++ b/app/src/main/kotlin/com/cyb3rko/flashdim/tiles/DimmerSettingsTile.kt @@ -25,44 +25,34 @@ import android.service.quicksettings.TileService import android.util.Log import com.cyb3rko.flashdim.handleFlashlightException import com.cyb3rko.flashdim.utils.Safe +import kotlin.math.abs class DimmerSettingsTile : TileService() { - private var description = "" - private var enabled = false + private var strength = 0 override fun onClick() { if (qsTile.state == Tile.STATE_UNAVAILABLE) return Safe.initialize(applicationContext) - var mode = Safe.getInt(Safe.QUICKTILE_DIM_MODE, DIMMER_MIN) - if (!enabled) - // torch was disabled externally, continue again from initial state - mode = DIMMER_MIN - description = mode.description() val maxLevel = Safe.getInt(Safe.MAX_LEVEL, -1) - val newLevel = when (mode) { - DIMMER_MAX -> maxLevel - DIMMER_HALF -> maxLevel / 2 - DIMMER_MIN -> 1 - DIMMER_OFF -> 0 - else -> DIMMER_MAX - } + val nextStrength = findStrengthLevel(strength, maxLevel, nextLevel = true) try { val cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager - sendFlashlightSignal(cameraManager, newLevel, newLevel != DIMMER_OFF) + sendFlashlightSignal(cameraManager, nextStrength, nextStrength != 0) } catch (e: Exception) { Log.e("FlashDim", "Camera access failed in DimmerSettingsTile") handleFlashlightException(e) e.printStackTrace() } - qsTile.subtitle = "State: ${mode.description()}" + this.strength = nextStrength + qsTile.subtitle = getModeDesc(strength, maxLevel) qsTile.updateTile() - Safe.writeInt(Safe.QUICKTILE_DIM_MODE, mode.next()) } override fun onStartListening() { Safe.initialize(this) + val maxLevel = Safe.getInt(Safe.MAX_LEVEL, -1) if (Safe.getInt(Safe.MAX_LEVEL, -1) < 2) { qsTile.state = Tile.STATE_UNAVAILABLE qsTile.updateTile() @@ -74,12 +64,13 @@ class DimmerSettingsTile : TileService() { object : CameraManager.TorchCallback() { override fun onTorchModeChanged(cameraId: String, enabled: Boolean) { if (qsTile == null) return - qsTile.subtitle = - if (description.isNotEmpty() && enabled) "State: $description" - else "State: ${DIMMER_OFF.description()}" + val strength = + if (enabled) cameraManager.getTorchStrengthLevel(cameraId) else 0 + this@DimmerSettingsTile.strength = strength + + qsTile.subtitle = getModeDesc(strength, maxLevel) qsTile.state = if (enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE qsTile.updateTile() - this@DimmerSettingsTile.enabled = enabled } }, Handler(Looper.getMainLooper()) @@ -99,26 +90,27 @@ class DimmerSettingsTile : TileService() { } } - private fun Int.next() = when (this) { - DIMMER_OFF -> DIMMER_MIN - DIMMER_MIN -> DIMMER_HALF - DIMMER_HALF -> DIMMER_MAX - DIMMER_MAX -> DIMMER_OFF - else -> DIMMER_MAX - } - - private fun Int.description() = when (this) { - DIMMER_OFF -> "Off" - DIMMER_MIN -> "Min" - DIMMER_HALF -> "Half" - DIMMER_MAX -> "Max" - else -> "Unknown" + private fun findStrengthLevel(value: Int, maxLevel: Int, nextLevel: Boolean = false): Int { + val levels = listOf(0, 1, maxLevel / 2, maxLevel) + // find nearest strength level + // this avoids cases where a strength level of 2 is mapped to a half (since it is higher than min) + val nearestValue = levels.minByOrNull { abs(it - value) } ?: 0 + // find the next higher strength or 0 + val comparisonOp: (Int, Int) -> Boolean = if (nextLevel) { + { a, b -> a > b } + } else { + { a, b -> a >= b } + } + return levels.iterator().asSequence().firstOrNull { comparisonOp(it, nearestValue) } ?: 0 } - companion object { - const val DIMMER_MAX = 3 - const val DIMMER_HALF = 2 - const val DIMMER_MIN = 1 - const val DIMMER_OFF = 0 + private fun getModeDesc(value: Int, maxLevel: Int): String { + return "State: ${when (findStrengthLevel(value, maxLevel)) { + 0 -> "Off" + 1 -> "Min" + maxLevel / 2 -> "Half" + maxLevel -> "Max" + else -> "Unknown" + }}" } } diff --git a/app/src/main/kotlin/com/cyb3rko/flashdim/utils/Safe.kt b/app/src/main/kotlin/com/cyb3rko/flashdim/utils/Safe.kt index 7c26b9e..9419264 100644 --- a/app/src/main/kotlin/com/cyb3rko/flashdim/utils/Safe.kt +++ b/app/src/main/kotlin/com/cyb3rko/flashdim/utils/Safe.kt @@ -31,7 +31,6 @@ internal object Safe { const val MORSE_VIBRATION = "morse_vibration" const val MULTILEVEL = "multilevel" const val PAUSE_FLASH = "pause_flash" - const val QUICKTILE_DIM_MODE = "dim_mode" const val QUICK_SETTINGS_LINK = "quick_settings_link" const val REPORT_DIALOG_SHOWN = "${BuildConfig.VERSION_CODE}-report_dialog" const val STARTUP_COUNTER = "startup_counter"