app: accurate Shizuku handoff — name the steps, add developer-options shortcut

Verified against Shizuku 13.6's manifest (pulled APK, aapt2 xmltree): its
wireless-debugging entry points (AdbPairingTutorialActivity,
AdbPairingService, StarterActivity) have no intent filters, so they are
not exported and cannot be launched externally; MainActivity answers only
MAIN/LAUNCHER with no deep link. Starting wireless debugging from another
app is therefore not possible, which is why the handoff lands on the
root-start screen.

Instead the hint now names the exact steps inside Shizuku ("Pairing", then
"Start"), and a second tap opens Developer options — that action IS public
and exported, and Wireless debugging has to be on before Shizuku's
wireless start works.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mrambossek
2026-08-01 10:03:03 +02:00
co-authored by Claude Opus 5
parent 1d9e2063bf
commit c75a9f5eb7
3 changed files with 54 additions and 6 deletions
+12
View File
@@ -531,3 +531,15 @@ Verified on-device in one screenshot: progress bar at "test 4 of 8 · icmp.ping6
Cancel button beside the disabled Run button, title clear of the status bar/cutout, and the banner Cancel button beside the disabled Run button, title clear of the status bar/cutout, and the banner
having live-switched from "installed but not running" to "running but not authorised" via the having live-switched from "installed but not running" to "running but not authorised" via the
binder listener when Shizuku was started mid-session. binder listener when Shizuku was started mid-session.
### Why the Shizuku banner can't start wireless debugging directly (verified, 2026-08-01)
Checked against Shizuku 13.6's own manifest (pulled the APK, `aapt2 dump xmltree`): the
wireless-debugging entry points — `moe.shizuku.manager.adb.AdbPairingTutorialActivity`,
`moe.shizuku.manager.adb.AdbPairingService`, `moe.shizuku.manager.starter.StarterActivity` —
declare **no intent filters**, so they are not exported and a third-party app cannot launch them.
`MainActivity` is the only reachable entry and answers MAIN/LAUNCHER only (no deep link), which is
why the handoff lands on the screen whose primary action is the root start.
Best available behavior, now implemented: the banner still opens Shizuku, but the hint names the
exact steps there ("Pairing", then "Start"), and a second tap target opens **Developer options**
(`Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS` — public and exported) since Wireless
debugging must be enabled first for Shizuku's wireless start to work at all.
@@ -65,6 +65,13 @@ class MainActivity : ComponentActivity() {
state = vm.state, state = vm.state,
onRun = { vm.run() }, onRun = { vm.run() },
onCancel = vm::cancel, onCancel = vm::cancel,
onDeveloperOptions = {
runCatching {
startActivity(
app.echo_lot.shizuku.ShizukuAvailability.developerOptionsIntent()
)
}
},
onShizukuAction = { onShizukuAction = {
// Shizuku can only be started from its own app (the pairing flow lives // Shizuku can only be started from its own app (the pairing flow lives
// there), so send the user straight to it; if it is already running we // there), so send the user straight to it; if it is already running we
@@ -114,6 +121,7 @@ private fun EcholotScreen(
onRun: () -> Unit, onRun: () -> Unit,
onCancel: () -> Unit, onCancel: () -> Unit,
onShizukuAction: () -> Unit, onShizukuAction: () -> Unit,
onDeveloperOptions: () -> Unit,
onExport: (MeasurementDocument) -> Unit, onExport: (MeasurementDocument) -> Unit,
) { ) {
Column( Column(
@@ -150,6 +158,17 @@ private fun EcholotScreen(
Text(it, fontSize = 11.sp, fontWeight = FontWeight.SemiBold, Text(it, fontSize = 11.sp, fontWeight = FontWeight.SemiBold,
color = Color(0xFFFFB454)) color = Color(0xFFFFB454))
} }
// Wireless debugging must be ON before Shizuku's wireless start works, and
// that screen (unlike Shizuku's pairing activity) is publicly launchable.
if (state.shizukuState ==
app.echo_lot.shizuku.ShizukuAvailability.State.INSTALLED_NOT_RUNNING
) {
Text(
"Wireless debugging settings →",
Modifier.padding(top = 6.dp).clickable { onDeveloperOptions() },
fontSize = 11.sp, color = Color(0xFF35E0C4),
)
}
} }
} }
} }
@@ -59,10 +59,14 @@ object ShizukuAvailability {
} }
/** /**
* Intent that opens the Shizuku manager, where the user starts the service ("Start with * Intent that opens the Shizuku manager.
* wireless debugging" / root). A third-party app cannot start Shizuku itself — the pairing *
* flow lives inside that app — so the best we can do is take the user straight there. * Only `MainActivity` is reachable: verified against Shizuku 13.6's own manifest, the
* Null when Shizuku isn't installed or exposes no launcher activity. * wireless-debugging entry points — `moe.shizuku.manager.adb.AdbPairingTutorialActivity`,
* `moe.shizuku.manager.adb.AdbPairingService` and `moe.shizuku.manager.starter.StarterActivity`
* — declare no intent filters, so they are NOT exported and a third-party app cannot launch
* them (MainActivity itself only answers MAIN/LAUNCHER, no deep link). Landing on the main
* screen and telling the user which button to press is therefore the best available handoff.
*/ */
fun launchIntent(context: Context): android.content.Intent? = fun launchIntent(context: Context): android.content.Intent? =
context.packageManager.getLaunchIntentForPackage(SHIZUKU_PACKAGE) context.packageManager.getLaunchIntentForPackage(SHIZUKU_PACKAGE)
@@ -73,13 +77,26 @@ object ShizukuAvailability {
runCatching { Shizuku.requestPermission(requestCode) } runCatching { Shizuku.requestPermission(requestCode) }
} }
/** What tapping the notice should do, so the UI can label it honestly. */ /**
* What tapping the notice does. For the stopped case the label names the exact steps inside
* Shizuku, because we can only hand the user to its main screen (see [launchIntent]).
*/
fun actionHint(s: State): String? = when (s) { fun actionHint(s: State): String? = when (s) {
State.INSTALLED_NOT_RUNNING -> "Tap to open Shizuku and start it" State.INSTALLED_NOT_RUNNING ->
"Tap to open Shizuku → \"Pairing\" to pair, then \"Start\" (wireless debugging)"
State.NEEDS_PERMISSION -> "Tap to grant permission" State.NEEDS_PERMISSION -> "Tap to grant permission"
else -> null else -> null
} }
/**
* Developer-options screen, where Wireless debugging is enabled — the prerequisite Shizuku's
* wireless start depends on. This action IS public and exported, unlike Shizuku's own
* pairing screen.
*/
fun developerOptionsIntent(): android.content.Intent =
android.content.Intent(android.provider.Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)
.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
/** /**
* Reports the state now and on every binder transition. Returns a function that removes the * Reports the state now and on every binder transition. Returns a function that removes the
* listeners again (call it from onCleared). * listeners again (call it from onCleared).