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


commit 14ef6461af9264c077df23e292d93f8e6e0dd3ec
truex <truex@equwal.com>
2026-09-21 03:06:30 -0700

Give the hardware hacks a tile and a screen of their own

The main screen has a Hardware hacks tile. HacksActivity holds the rows
that were a section of Advanced. The row for the double tap of Power
says that it uses the camera intent and needs no camera button.

 CHANGELOG.md                                       |  7 +++
 app/src/main/AndroidManifest.xml                   |  1 +
 .../dev/equwal/assistkey/ui/AdvancedActivity.kt    | 43 --------------
 .../java/dev/equwal/assistkey/ui/HacksActivity.kt  | 69 ++++++++++++++++++++++
 .../java/dev/equwal/assistkey/ui/MainActivity.kt   |  3 +
 5 files changed, 80 insertions(+), 43 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 890f6e2..899411d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## Unreleased
+
+- **Hardware hacks** is a tile on the main screen with a screen of its own:
+  hold Power (assistant role), double tap Power (camera intent), the wallet
+  button, the full Power button (shell access), extra-dim light, and the
+  button settings of the device. Advanced no longer holds them.
+
 ## 0.0.11-alpha - 2026-09-21
 
 Version code 11.
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b24282a..1385157 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -91,6 +91,7 @@
         <activity android:name=".ui.SetupActivity" android:exported="false" />
         <activity android:name=".ui.HomeHubActivity" android:exported="false" />
         <activity android:name=".ui.AdvancedActivity" android:exported="false" />
+        <activity android:name=".ui.HacksActivity" android:exported="false" />
         <!-- The guided flow the hub opens first. -->
         <activity android:name=".setup.GuidedSetupActivity" android:exported="false" />
         <activity android:name=".menu.MenuEditActivity" android:exported="false" />
diff --git a/app/src/main/java/dev/equwal/assistkey/ui/AdvancedActivity.kt b/app/src/main/java/dev/equwal/assistkey/ui/AdvancedActivity.kt
index 5435f1d..3e80661 100644
--- a/app/src/main/java/dev/equwal/assistkey/ui/AdvancedActivity.kt
+++ b/app/src/main/java/dev/equwal/assistkey/ui/AdvancedActivity.kt
@@ -3,12 +3,9 @@ package dev.equwal.assistkey.ui
 import android.app.Activity
 import android.content.Intent
 import android.widget.LinearLayout
-import dev.equwal.assistkey.channel.Channel
-import dev.equwal.assistkey.channel.Channels
 import dev.equwal.assistkey.device.Detect
 import dev.equwal.assistkey.device.Device
 import dev.equwal.assistkey.model.HwKey
-import dev.equwal.assistkey.native.ViwoodsBridge
 import dev.equwal.assistkey.shell.Shell
 import dev.equwal.assistkey.store.Store
 import dev.equwal.assistkey.ui.Ui.header
@@ -41,7 +38,6 @@ class AdvancedActivity : Activity() {
         val col = Ui.page(this, "Advanced")
         fullControl(col)
         tools(col)
-        hacks(col)
         settingsFile(col)
     }
 
@@ -90,45 +86,6 @@ class AdvancedActivity : Activity() {
         col.row("Export and import") { startActivity(Intent(this, BackupActivity::class.java)) }
     }
 
-    /**
-     * The tricks that get more out of the hardware than Android offers an app.
-     * They are in one place, so that a user who wants none of them can pass by.
-     */
-    private fun hacks(col: LinearLayout) {
-        col.header("Hardware hacks")
-        fun door(title: String, hint: String, ch: Channel) = col.row(
-            title, hint, state = if (Channels.isSatisfied(this, ch)) "On" else "Off"
-        ) { startActivity(Intent(this, PowerActivity::class.java)) }
-        door("Hold Power", "Rebind stands in for the assistant app", Channel.ASSISTANT)
-        door("Double press Power", "Rebind stands in for the camera app", Channel.CAMERA)
-        door("Wallet button", "Rebind stands in for the wallet app", Channel.WALLET)
-        if (Shell.SUPPORTED) {
-            col.row(
-                "Full Power button",
-                "Every Power press, through shell access",
-                state = Shell.describe(this)
-            ) { startActivity(Intent(this, ShellActivity::class.java)) }
-            // No light node, no extra-dim. Detection says so; do not offer it.
-            if (Shell.ready && Detect.stored(this)?.capability(Detect.FRONTLIGHT) != Detect.State.NO) {
-                col.row(
-                    "Extra-dim light",
-                    "Below the lowest the system slider allows"
-                ) { startActivity(Intent(this, DisplayActivity::class.java)) }
-            }
-        }
-        if (Device.hasFirmwareKeyHooks) {
-            val hidden = ViwoodsBridge.keys().filter { ViwoodsBridge.hidesFromFilter(this, it) }
-            col.row(
-                "Device button settings",
-                if (hidden.isEmpty()) "What this device does with each button"
-                else "Hiding " + hidden.joinToString(" and ") { it.label.lowercase() } +
-                    " from this app",
-                state = if (hidden.isEmpty()) null else "Hiding a button"
-            ) { startActivity(Intent(this, ViwoodsActivity::class.java)) }
-        }
-
-    }
-
     private fun boundOnKeys(): Int {
         val keys = Device.keys(this).toSet()
         return Store.bindings(this).all().keys.count { t ->
diff --git a/app/src/main/java/dev/equwal/assistkey/ui/HacksActivity.kt b/app/src/main/java/dev/equwal/assistkey/ui/HacksActivity.kt
new file mode 100644
index 0000000..56c7e18
--- /dev/null
+++ b/app/src/main/java/dev/equwal/assistkey/ui/HacksActivity.kt
@@ -0,0 +1,69 @@
+package dev.equwal.assistkey.ui
+
+import android.app.Activity
+import android.content.Intent
+import dev.equwal.assistkey.channel.Channel
+import dev.equwal.assistkey.channel.Channels
+import dev.equwal.assistkey.device.Detect
+import dev.equwal.assistkey.device.Device
+import dev.equwal.assistkey.native.ViwoodsBridge
+import dev.equwal.assistkey.shell.Shell
+import dev.equwal.assistkey.ui.Ui.row
+
+/**
+ * The tricks that get more out of the hardware than Android offers an app:
+ * the app stands in for the assistant, the camera and the wallet to get the
+ * Power button, reads Power through shell access, sets the light below its
+ * floor, and shows what the device itself does with each button.
+ *
+ * They are in one place, so that a user who wants none of them can pass by.
+ */
+class HacksActivity : Activity() {
+
+    private val redraw: () -> Unit = { if (!isFinishing) build() }
+
+    override fun onResume() {
+        super.onResume()
+        Shell.onChange(redraw)
+        build()
+    }
+
+    override fun onPause() {
+        super.onPause()
+        Shell.removeOnChange(redraw)
+    }
+
+    private fun build() {
+        val col = Ui.page(this, "Hardware hacks")
+        fun door(title: String, hint: String, ch: Channel) = col.row(
+            title, hint, state = if (Channels.isSatisfied(this, ch)) "On" else "Off"
+        ) { startActivity(Intent(this, PowerActivity::class.java)) }
+        door("Hold Power", "Through the assistant role", Channel.ASSISTANT)
+        door("Double tap Power", "Through the camera intent. No camera button.", Channel.CAMERA)
+        door("Wallet button", "Through the wallet role", Channel.WALLET)
+        if (Shell.SUPPORTED) {
+            col.row(
+                "Full Power button",
+                "Every Power press, through shell access",
+                state = Shell.describe(this)
+            ) { startActivity(Intent(this, ShellActivity::class.java)) }
+            // No light node, no extra-dim. Detection says so; do not offer it.
+            if (Shell.ready && Detect.stored(this)?.capability(Detect.FRONTLIGHT) != Detect.State.NO) {
+                col.row(
+                    "Extra-dim light",
+                    "Below the lowest the system slider allows"
+                ) { startActivity(Intent(this, DisplayActivity::class.java)) }
+            }
+        }
+        if (Device.hasFirmwareKeyHooks) {
+            val hidden = ViwoodsBridge.keys().filter { ViwoodsBridge.hidesFromFilter(this, it) }
+            col.row(
+                "Device button settings",
+                if (hidden.isEmpty()) "What this device does with each button"
+                else "Hiding " + hidden.joinToString(" and ") { it.label.lowercase() } +
+                    " from this app",
+                state = if (hidden.isEmpty()) null else "Hiding a button"
+            ) { startActivity(Intent(this, ViwoodsActivity::class.java)) }
+        }
+    }
+}
diff --git a/app/src/main/java/dev/equwal/assistkey/ui/MainActivity.kt b/app/src/main/java/dev/equwal/assistkey/ui/MainActivity.kt
index b92adff..80e6162 100644
--- a/app/src/main/java/dev/equwal/assistkey/ui/MainActivity.kt
+++ b/app/src/main/java/dev/equwal/assistkey/ui/MainActivity.kt
@@ -143,6 +143,9 @@ class MainActivity : Activity() {
                 Ui.Tile("Setup", setupSummary()) {
                     startActivity(Intent(this, SetupActivity::class.java))
                 },
+                Ui.Tile("Hardware hacks", "Power tricks, extra-dim light") {
+                    startActivity(Intent(this, HacksActivity::class.java))
+                },
                 Ui.Tile("Advanced", "Every button, tools and backup") {
                     startActivity(Intent(this, AdvancedActivity::class.java))
                 }