Recently Written · git

assistkey

Hardware key remapper for the Viwoods AiPaper Reader: AI key, volume keys and power, with multi-tap, hold and chords

git clone https://github.com/equwal/assistkey

Log | Files | Refs


app/src/main/java/dev/equwal/assistkey/display/ExtraDim.kt (5451 bytes)

1 package dev.equwal.assistkey.display
2 
3 import android.content.Context
4 import dev.equwal.assistkey.device.Device
5 import dev.equwal.assistkey.shell.Shell
6 
7 /**
8  * Dimmer than the dimmest the system allows.
9  *
10  * Measured on the Viwoods reader: the frontlight is an ordinary backlight LED
11  * whose driver accepts 1..255 (and has 2047 real steps behind that), but the
12  * framework will not go below 5. Ask for 4 through any official route - the
13  * brightness setting, even the shell's `cmd display set-brightness` - and it
14  * snaps to zero. Writing the LED node directly gets under the floor, and the
15  * value then sticks until the system slider is next moved.
16  *
17  * The node belongs to `system`, so this needs more than the shell user: it
18  * works when Shizuku itself runs as root, or on firmware whose shell may `su`
19  * (userdebug builds, which is how these readers ship). Otherwise the write is
20  * refused and the screen says so.
21  */
22 object ExtraDim {
23 
24     private const val PREFS = "assistkey_display"
25     private const val K_LEVEL = "extra_dim"
26 
27     /** The light nodes to try, in order. Detection looks for the same ones. */
28     val nodes = listOf(
29         "/sys/class/leds/lcd-backlight/brightness",
30         "/sys/class/backlight/panel0-backlight/brightness"
31     )
32 
33     /** 0 is off; otherwise the raw value to hold the light at. */
34     fun level(c: Context): Int =
35         c.getSharedPreferences(PREFS, Context.MODE_PRIVATE).getInt(K_LEVEL, 0)
36 
37     /** The raw values on offer: everything under the system's floor. */
38     fun levels(): List<Int> = (1 until (Device.brightnessFloor ?: 5)).toList().reversed()
39 
40     fun set(c: Context, level: Int, done: (Boolean) -> Unit) {
41         c.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit().putInt(K_LEVEL, level).apply()
42         if (level == 0) restore(done) else write(level, done)
43     }
44 
45     // ---- from a key ---------------------------------------------------------------------
46 
47     /**
48      * The order of brightness, darkest last: off (the system level), then the
49      * levels under the floor from high to low. [darker] is one step down that
50      * order and [brighter] one step up; both stop at the ends.
51      */
52     fun darker(level: Int, levels: List<Int>): Int = when {
53         levels.isEmpty() -> 0
54         level == 0 -> levels.first()
55         else -> levels.getOrElse(levels.indexOf(level) + 1) { levels.last() }
56     }
57 
58     fun brighter(level: Int, levels: List<Int>): Int {
59         val i = levels.indexOf(level)
60         return if (i <= 0) 0 else levels[i - 1]
61     }
62 
63     /**
64      * On and off. On is the lowest level there is. Off gives the light back to
65      * the brightness the user set in the system.
66      */
67     fun toggled(level: Int, levels: List<Int>): Int = if (level > 0) 0 else levels.lastOrNull() ?: 0
68 
69     /**
70      * One step of the system brightness, 0..255. A step is a quarter of the
71      * present value and at least 5, so steps are fine where the light is low and
72      * coarse where it is high. The result stays between [floor] and 255.
73      */
74     fun systemStep(value: Int, up: Boolean, floor: Int): Int {
75         val step = maxOf(5, value / 4)
76         return (if (up) value + step else value - step).coerceIn(floor, 255)
77     }
78 
79     private fun systemBrightness(c: Context, up: Boolean, done: (Boolean) -> Unit) {
80         // Brighter from an extra-dim level means: leave extra-dim first.
81         if (up && level(c) > 0) return set(c, 0, done)
82         Shell.run("settings get system screen_brightness") { r ->
83             val now = r.output.trim().toIntOrNull() ?: return@run done(false)
84             val next = systemStep(now, up, Device.brightnessFloor ?: 1)
85             Shell.run("settings put system screen_brightness_mode 0; settings put system screen_brightness $next") {
86                 done(it.ok)
87             }
88         }
89     }
90 
91     /** Runs a key action: "toggle", "darker", "brighter", "system_up" or "system_down". */
92     fun act(c: Context, what: String, done: (Boolean) -> Unit = {}) {
93         if (what == "system_up") return systemBrightness(c, true, done)
94         if (what == "system_down") return systemBrightness(c, false, done)
95         val now = level(c)
96         val next = when (what) {
97             "darker" -> darker(now, levels())
98             "brighter" -> brighter(now, levels())
99             else -> toggled(now, levels())
100         }
101         if (next == now) return done(true)
102         set(c, next, done)
103     }
104 
105     /** Re-asserts the chosen level; called when the screen comes on. */
106     fun reapply(c: Context) {
107         val l = level(c)
108         if (l > 0 && Shell.ready) write(l) {}
109     }
110 
111     private fun write(value: Int, done: (Boolean) -> Unit) {
112         // First node that exists; plain write, then through su if refused.
113         val script = nodes.joinToString(" ") { "'$it'" }.let { list ->
114             "for n in $list; do [ -e \$n ] || continue; " +
115                 "echo $value > \$n 2>/dev/null || su 0 sh -c \"echo $value > \$n\"; " +
116                 "[ \"\$(cat \$n)\" = \"$value\" ] && exit 0; exit 1; done; exit 2"
117         }
118         Shell.run(script) { done(it.ok) }
119     }
120 
121     /** Hands the light back by nudging the system's own brightness setting. */
122     private fun restore(done: (Boolean) -> Unit) {
123         Shell.run(
124             "v=\$(settings get system screen_brightness); " +
125                 "settings put system screen_brightness \$((v+1)); settings put system screen_brightness \$v"
126         ) { done(it.ok) }
127     }
128 }