app: notice when Shizuku is authorised

The banner watched only for the binder arriving or dying, and granting
permission does neither. So after the user tapped the banner, answered
the dialog and came back, it still read "running but not authorised" —
wrong at precisely the moment they were looking for confirmation that it
had worked.

Two listeners were missing, because there are two ways this changes.
Answering our own request now fires OnRequestPermissionResultListener.
That is not enough on its own: permission can equally be granted inside
Shizuku's own app, and Shizuku started or stopped there, none of which
calls back into this process — so the state is re-read whenever the
screen comes forward, which is the only thing that covers every route.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
mrambossek
2026-08-02 08:37:45 +02:00
co-authored by Claude Opus 5
parent 416b783647
commit 5b02d40802
3 changed files with 45 additions and 2 deletions
@@ -149,6 +149,20 @@ class MainActivity : ComponentActivity() {
screen = Screen.SETTINGS
}
}
// Shizuku can be started, stopped or authorised in its own app, where nothing
// calls back into this process. Asking again each time this screen comes
// forward is what makes the banner right after the user has been away to fix
// it — which is exactly the moment they look at it.
val lifecycleOwner = androidx.compose.ui.platform.LocalLifecycleOwner.current
androidx.compose.runtime.DisposableEffect(lifecycleOwner) {
val obs = androidx.lifecycle.LifecycleEventObserver { _, event ->
if (event == androidx.lifecycle.Lifecycle.Event.ON_RESUME) {
vm.refreshShizuku()
}
}
lifecycleOwner.lifecycle.addObserver(obs)
onDispose { lifecycleOwner.lifecycle.removeObserver(obs) }
}
androidx.compose.runtime.LaunchedEffect(autorun) {
if (autorun) vm.run(devUpload = true)
}
@@ -103,6 +103,23 @@ class RunViewModel(app: Application) : AndroidViewModel(app) {
}
}
/**
* Re-reads the shell tier's state, for when it changed somewhere this process cannot see.
*
* Permission can be granted inside Shizuku's own app, and Shizuku can be started or stopped
* there too; none of that calls back here. Asking again on resume is the only way to be right
* after the user has been somewhere else to fix it.
*/
fun refreshShizuku() {
val st = ShizukuAvailability.current(getApplication())
state = state.copy(
shizukuNotice = ShizukuAvailability.describe(st),
shizukuReady = st == ShizukuAvailability.State.READY,
shizukuHint = ShizukuAvailability.actionHint(st),
shizukuState = st,
)
}
override fun onCleared() {
stopShizukuObserver()
super.onCleared()