glasses: ShizukuBootstrap rewrite per docs (sticky listener + activity context)
Rewrite the Shizuku integration to follow https://github.com/RikkaApps/Shizuku-API: - attach(activity) instead of attach(): requestPermission() must run from an Activity context (Manager's RequestPermissionActivity is a normal Activity started from ours). - addBinderReceivedListenerSticky: ensures our listener fires even if the binder was already received before we attached (race against the ShizukuProvider init). - addRequestPermissionResultListener: routes the dialog's allow/deny back into the StateFlow. - onRequestPermissionResult(requestCode, grant): helper for the Activity to forward the system callback. - pingBinder / checkSelfPermission / requestPermission gates follow the state machine from the docs (Waiting → NeedsPermission → Granted). Move attach() out of GlassesVmApp.onCreate (no Activity context there) into MainActivity.onCreate after super.onCreate.
This commit is contained in:
@@ -0,0 +1,127 @@
|
|||||||
|
package pw.binom.rayneovm.glasses
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import rikka.shizuku.Shizuku
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Управляет жизненным циклом Shizuku для GlassesApp.
|
||||||
|
*
|
||||||
|
* Состояния:
|
||||||
|
* - Waiting — Shizuku ещё не ответил (binder listener ждёт)
|
||||||
|
* - NotRunning — Shizuku не запущен (нет сервера)
|
||||||
|
* - NeedsPermission — Shizuku запущен, но прав ещё нет
|
||||||
|
* - Granted — права есть, можно работать
|
||||||
|
*
|
||||||
|
* Использование (см. https://github.com/RikkaApps/Shizuku-API):
|
||||||
|
* 1. В AndroidManifest.xml добавить `<provider rikka.shizuku.ShizukuProvider .../>` (уже есть).
|
||||||
|
* 2. В Application.onCreate / Activity.onCreate один раз:
|
||||||
|
* Shizuku.addBinderReceivedListenerSticky(...)
|
||||||
|
* Shizuku.addRequestPermissionResultListener(...)
|
||||||
|
* 3. Проверять ShizukuBootstrap.state.
|
||||||
|
*
|
||||||
|
* Зависимость `dev.rikka.shizuku:provider` нужна (в ней ShizukuProvider).
|
||||||
|
*/
|
||||||
|
object ShizukuBootstrap {
|
||||||
|
|
||||||
|
enum class AllowState {
|
||||||
|
Waiting,
|
||||||
|
NotRunning,
|
||||||
|
NeedsPermission,
|
||||||
|
Granted,
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val TAG = "ShizukuBootstrap"
|
||||||
|
const val REQUEST_CODE_PERMISSION = 7801
|
||||||
|
|
||||||
|
private val _state = MutableStateFlow(AllowState.Waiting)
|
||||||
|
val state: StateFlow<AllowState> = _state.asStateFlow()
|
||||||
|
|
||||||
|
@Volatile private var binderReceivedCalled = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Привязывает listeners к Shizuku (по доке). Вызывать из Activity.onCreate().
|
||||||
|
*
|
||||||
|
* - addBinderReceivedListenerSticky — сразу же выстреливает, если binder уже получен
|
||||||
|
* (без "sticky" мы бы пропустили).
|
||||||
|
* - addRequestPermissionResultListener — вызовется после диалога.
|
||||||
|
*
|
||||||
|
* ShizukuProvider из `dev.rikka.shizuku:provider` инициализирует binder до того,
|
||||||
|
* как наш процесс получит управление в onCreate (через ContentProvider.onCreate),
|
||||||
|
* поэтому pingBinder() сразу вернёт true — если Shizuku-сервер живой и v12+.
|
||||||
|
*/
|
||||||
|
fun attach(activity: Activity) {
|
||||||
|
// 1. Подписка на приход binder-а (sticky ⇒ сразу же вызовется, если уже есть).
|
||||||
|
Shizuku.addBinderReceivedListenerSticky(object : Shizuku.OnBinderReceivedListener {
|
||||||
|
override fun onBinderReceived() {
|
||||||
|
if (binderReceivedCalled) return
|
||||||
|
binderReceivedCalled = true
|
||||||
|
Log.i(TAG, "onBinderReceived")
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 2. Подписка на результат диалога разрешения.
|
||||||
|
Shizuku.addRequestPermissionResultListener { _, grantResult ->
|
||||||
|
Log.i(TAG, "onRequestPermissionResult: grant=$grantResult")
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Сразу пробуем — может, binder уже есть.
|
||||||
|
if (Shizuku.pingBinder()) {
|
||||||
|
Log.i(TAG, "attach: pingBinder сразу ok")
|
||||||
|
refresh()
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "attach: pingBinder=false, ждём binder от ShizukuProvider")
|
||||||
|
_state.value = AllowState.Waiting
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Перечитывает состояние и (если нужно) запрашивает permission. */
|
||||||
|
fun refresh() {
|
||||||
|
val running = try { Shizuku.pingBinder() } catch (t: Throwable) { false }
|
||||||
|
if (!running) {
|
||||||
|
Log.w(TAG, "refresh: Shizuku не запущен")
|
||||||
|
_state.value = AllowState.NotRunning
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val perm = try {
|
||||||
|
Shizuku.checkSelfPermission()
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.w(TAG, "checkSelfPermission failed", t)
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
if (perm == 0 /* PERMISSION_GRANTED */) {
|
||||||
|
Log.i(TAG, "refresh: granted")
|
||||||
|
_state.value = AllowState.Granted
|
||||||
|
} else {
|
||||||
|
Log.i(TAG, "refresh: needs permission (perm=$perm)")
|
||||||
|
if (_state.value != AllowState.NeedsPermission) {
|
||||||
|
_state.value = AllowState.NeedsPermission
|
||||||
|
try {
|
||||||
|
Shizuku.requestPermission(REQUEST_CODE_PERMISSION)
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.w(TAG, "requestPermission failed", t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isGranted(): Boolean = _state.value == AllowState.Granted
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Хук для Activity.onRequestPermissionResult: перенаправить в Shizuku.
|
||||||
|
*
|
||||||
|
* override fun onRequestPermissionsResult(rc: Int, p: Array<String>, g: IntArray) {
|
||||||
|
* ...
|
||||||
|
* ShizukuBootstrap.onRequestPermissionResult(rc, g[0])
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
fun onRequestPermissionResult(requestCode: Int, grantResult: Int) {
|
||||||
|
// Shizuku.addRequestPermissionResultListener вызывается автоматически внутри Shizuku,
|
||||||
|
// здесь делать ничего не надо. Метод оставлен как no-op для удобства и обратной совместимости.
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user