app: tell the user before the run when Shizuku is installed but not started
Distinguishes not-installed (say nothing — don't nag users who don't use Shizuku) from installed-but-stopped (amber banner: start it to include shell-tier tests), plus running-unauthorised and ready. Detection is listener-based since pingBinder() only becomes truthful once ShizukuProvider delivers the binder; a launch-time poll would show a false "not running". Installed-vs-not needs the <queries> entry on Android 11+. Verified on-device: with shizuku_server stopped, the banner shows before pressing Run; the title also now clears the status bar/cutout after the safeDrawingPadding fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
217818f7b3
commit
d7dda40e4e
@@ -5,6 +5,12 @@
|
||||
|
||||
<uses-permission android:name="moe.shizuku.manager.permission.API_V23" />
|
||||
|
||||
<!-- Android 11+ package visibility: needed to tell "Shizuku installed but stopped"
|
||||
(worth a reminder) apart from "not installed" (say nothing). -->
|
||||
<queries>
|
||||
<package android:name="moe.shizuku.privileged.api" />
|
||||
</queries>
|
||||
|
||||
<application>
|
||||
<!-- Shizuku binder provider (merged into the host app). -->
|
||||
<provider
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-FileCopyrightText: 2026 Echolot contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package app.echo_lot.shizuku
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import rikka.shizuku.Shizuku
|
||||
|
||||
/**
|
||||
* Tells the UI whether the shell tier is usable **before** a run starts, so the app can say
|
||||
* "Shizuku isn't running, shell-tier tests will be skipped" instead of silently producing an
|
||||
* UNSUPPORTED result minutes later.
|
||||
*
|
||||
* Detection is reliable but *asynchronous*: `Shizuku.pingBinder()` answers truthfully only once
|
||||
* ShizukuProvider has delivered the binder to this process, which happens shortly after start.
|
||||
* [observe] therefore reports the current state immediately and again whenever the binder
|
||||
* arrives or dies — a poll at t=0 alone would show a false "not running".
|
||||
*/
|
||||
object ShizukuAvailability {
|
||||
|
||||
const val SHIZUKU_PACKAGE = "moe.shizuku.privileged.api"
|
||||
|
||||
enum class State {
|
||||
/** Running and this app may use it — shell tier will execute. */
|
||||
READY,
|
||||
/** Running, but the user hasn't granted this app permission yet (we can ask). */
|
||||
NEEDS_PERMISSION,
|
||||
/** Shizuku IS installed but its service isn't started — worth reminding this user. */
|
||||
INSTALLED_NOT_RUNNING,
|
||||
/** Shizuku isn't installed at all — say nothing; this user doesn't use the shell tier. */
|
||||
NOT_INSTALLED,
|
||||
}
|
||||
|
||||
/** Is the Shizuku manager installed? Needs the <queries> entry on Android 11+. */
|
||||
fun isInstalled(context: Context): Boolean = runCatching {
|
||||
context.packageManager.getPackageInfo(SHIZUKU_PACKAGE, 0); true
|
||||
}.getOrDefault(false)
|
||||
|
||||
fun current(context: Context): State = when {
|
||||
runCatching { Shizuku.pingBinder() }.getOrDefault(false) ->
|
||||
if (runCatching { Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED }
|
||||
.getOrDefault(false)
|
||||
) State.READY else State.NEEDS_PERMISSION
|
||||
isInstalled(context) -> State.INSTALLED_NOT_RUNNING
|
||||
else -> State.NOT_INSTALLED
|
||||
}
|
||||
|
||||
/**
|
||||
* UI one-liner, or null when nothing should be said. Users without Shizuku get no nag; users
|
||||
* who have it installed but stopped get the reminder that makes the difference between a
|
||||
* full run and a silently skipped shell tier.
|
||||
*/
|
||||
fun describe(s: State): String? = when (s) {
|
||||
State.READY -> "Shizuku ready — shell-tier tests will run"
|
||||
State.NEEDS_PERMISSION -> "Shizuku is running but not authorised — it will ask on first use"
|
||||
State.INSTALLED_NOT_RUNNING -> "Shizuku is installed but not running — start it to include shell-tier tests"
|
||||
State.NOT_INSTALLED -> null
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports the state now and on every binder transition. Returns a function that removes the
|
||||
* listeners again (call it from onCleared).
|
||||
*/
|
||||
fun observe(context: Context, onChange: (State) -> Unit): () -> Unit {
|
||||
val app = context.applicationContext
|
||||
val received = Shizuku.OnBinderReceivedListener { onChange(current(app)) }
|
||||
val dead = Shizuku.OnBinderDeadListener { onChange(current(app)) }
|
||||
// "Sticky" fires immediately if the binder already arrived before we registered.
|
||||
runCatching { Shizuku.addBinderReceivedListenerSticky(received) }
|
||||
runCatching { Shizuku.addBinderDeadListener(dead) }
|
||||
onChange(current(app))
|
||||
return {
|
||||
runCatching { Shizuku.removeBinderReceivedListener(received) }
|
||||
runCatching { Shizuku.removeBinderDeadListener(dead) }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user