mirror of
https://github.com/ImranR98/Obtainium.git
synced 2025-07-13 13:26:43 +02:00
Now it looks good
This commit is contained in:
@ -1,10 +1,5 @@
|
|||||||
package dev.imranr.obtainium
|
package dev.imranr.obtainium
|
||||||
|
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
|
||||||
import io.flutter.plugin.common.MethodChannel
|
|
||||||
import io.flutter.plugin.common.MethodChannel.Result
|
|
||||||
import androidx.annotation.NonNull
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentSender
|
import android.content.IntentSender
|
||||||
import android.content.pm.IPackageInstaller
|
import android.content.pm.IPackageInstaller
|
||||||
@ -15,119 +10,95 @@ import android.net.Uri
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Process
|
import android.os.Process
|
||||||
import rikka.shizuku.Shizuku
|
import androidx.annotation.NonNull
|
||||||
import rikka.shizuku.Shizuku.OnBinderDeadListener
|
import com.topjohnwu.superuser.Shell
|
||||||
import rikka.shizuku.Shizuku.OnBinderReceivedListener
|
|
||||||
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
|
|
||||||
import rikka.shizuku.ShizukuBinderWrapper
|
|
||||||
import dev.imranr.obtainium.util.IIntentSenderAdaptor
|
import dev.imranr.obtainium.util.IIntentSenderAdaptor
|
||||||
import dev.imranr.obtainium.util.IntentSenderUtils
|
import dev.imranr.obtainium.util.IntentSenderUtils
|
||||||
import dev.imranr.obtainium.util.PackageInstallerUtils
|
import dev.imranr.obtainium.util.PackageInstallerUtils
|
||||||
import dev.imranr.obtainium.util.ShizukuSystemServerApi
|
import dev.imranr.obtainium.util.ShizukuSystemServerApi
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
|
import io.flutter.plugin.common.MethodChannel
|
||||||
|
import io.flutter.plugin.common.MethodChannel.Result
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import com.topjohnwu.superuser.Shell
|
import rikka.shizuku.Shizuku
|
||||||
|
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
|
||||||
|
import rikka.shizuku.ShizukuBinderWrapper
|
||||||
|
|
||||||
class MainActivity: FlutterActivity() {
|
class MainActivity: FlutterActivity() {
|
||||||
private val installersChannel = "installers"
|
private var installersChannel: MethodChannel? = null
|
||||||
private val SHIZUKU_PERMISSION_REQUEST_CODE = 839 // random num
|
private val SHIZUKU_PERMISSION_REQUEST_CODE = (10..200).random()
|
||||||
private var shizukuBinderAlive = false
|
|
||||||
private var shizukuPermissionGranted = false
|
|
||||||
|
|
||||||
private val shizukuBinderReceivedListener = OnBinderReceivedListener {
|
private fun shizukuCheckPermission() {
|
||||||
if(!Shizuku.isPreV11()) { // pre 11 unsupported
|
try {
|
||||||
shizukuBinderAlive = true
|
if (Shizuku.isPreV11()) { // Unsupported
|
||||||
|
installersChannel!!.invokeMethod("resPermShizuku", mapOf("res" to -1))
|
||||||
|
} else if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
installersChannel!!.invokeMethod("resPermShizuku", mapOf("res" to 1))
|
||||||
|
} else if (Shizuku.shouldShowRequestPermissionRationale()) { // Deny and don't ask again
|
||||||
|
installersChannel!!.invokeMethod("resPermShizuku", mapOf("res" to 0))
|
||||||
|
} else {
|
||||||
|
Shizuku.requestPermission(SHIZUKU_PERMISSION_REQUEST_CODE)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) { // If shizuku not running
|
||||||
|
installersChannel!!.invokeMethod("resPermShizuku", mapOf("res" to -1))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val shizukuBinderDeadListener = OnBinderDeadListener { shizukuBinderAlive = false }
|
|
||||||
|
|
||||||
private val shizukuRequestPermissionResultListener = OnRequestPermissionResultListener {
|
private val shizukuRequestPermissionResultListener = OnRequestPermissionResultListener {
|
||||||
requestCode: Int, grantResult: Int ->
|
requestCode: Int, grantResult: Int ->
|
||||||
if(requestCode == SHIZUKU_PERMISSION_REQUEST_CODE) {
|
if (requestCode == SHIZUKU_PERMISSION_REQUEST_CODE) {
|
||||||
shizukuPermissionGranted = grantResult == PackageManager.PERMISSION_GRANTED
|
val res = if (grantResult == PackageManager.PERMISSION_GRANTED) 1 else 0
|
||||||
|
installersChannel!!.invokeMethod("resPermShizuku", mapOf("res" to res))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shizukuCheckPermission() {
|
private fun shizukuInstallApk(apkFileUri: String, result: Result) {
|
||||||
if(Shizuku.isPreV11()) {
|
val uri = Uri.parse(apkFileUri)
|
||||||
shizukuPermissionGranted = false
|
var res = false
|
||||||
} else if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
|
|
||||||
shizukuPermissionGranted = true
|
|
||||||
} else if (Shizuku.shouldShowRequestPermissionRationale()) { // Deny and don't ask again
|
|
||||||
shizukuPermissionGranted = false
|
|
||||||
} else {
|
|
||||||
Shizuku.requestPermission(SHIZUKU_PERMISSION_REQUEST_CODE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun shizukuInstallApk(uri: Uri) {
|
|
||||||
val packageInstaller: PackageInstaller
|
|
||||||
var session: PackageInstaller.Session? = null
|
var session: PackageInstaller.Session? = null
|
||||||
val cr = contentResolver
|
|
||||||
val res = StringBuilder()
|
|
||||||
val installerPackageName: String
|
|
||||||
var installerAttributionTag: String? = null
|
|
||||||
val userId: Int
|
|
||||||
val isRoot: Boolean
|
|
||||||
try {
|
try {
|
||||||
val _packageInstaller: IPackageInstaller =
|
val iPackageInstaller: IPackageInstaller =
|
||||||
ShizukuSystemServerApi.PackageManager_getPackageInstaller()
|
ShizukuSystemServerApi.PackageManager_getPackageInstaller()
|
||||||
isRoot = Shizuku.getUid() == 0
|
val isRoot = Shizuku.getUid() == 0
|
||||||
|
// The reason for use "com.android.shell" as installer package under adb
|
||||||
// the reason for use "com.android.shell" as installer package under adb is that getMySessions will check installer package's owner
|
// is that getMySessions will check installer package's owner
|
||||||
installerPackageName = if (isRoot) packageName else "com.android.shell"
|
val installerPackageName = if (isRoot) packageName else "com.android.shell"
|
||||||
|
var installerAttributionTag: String? = null
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
installerAttributionTag = attributionTag
|
installerAttributionTag = attributionTag
|
||||||
}
|
}
|
||||||
userId = if (isRoot) Process.myUserHandle().hashCode() else 0
|
val userId = if (isRoot) Process.myUserHandle().hashCode() else 0
|
||||||
packageInstaller = PackageInstallerUtils.createPackageInstaller(
|
val packageInstaller = PackageInstallerUtils.createPackageInstaller(
|
||||||
_packageInstaller,
|
iPackageInstaller, installerPackageName, installerAttributionTag, userId)
|
||||||
installerPackageName,
|
|
||||||
installerAttributionTag,
|
|
||||||
userId
|
|
||||||
)
|
|
||||||
val sessionId: Int
|
|
||||||
res.append("createSession: ")
|
|
||||||
val params =
|
val params =
|
||||||
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||||
var installFlags: Int = PackageInstallerUtils.getInstallFlags(params)
|
var installFlags: Int = PackageInstallerUtils.getInstallFlags(params)
|
||||||
installFlags =
|
installFlags = installFlags or 0x00000004 // PackageManager.INSTALL_ALLOW_TEST
|
||||||
installFlags or (0x00000004 /*PackageManager.INSTALL_ALLOW_TEST*/ or 0x00000002) /*PackageManager.INSTALL_REPLACE_EXISTING*/
|
|
||||||
PackageInstallerUtils.setInstallFlags(params, installFlags)
|
PackageInstallerUtils.setInstallFlags(params, installFlags)
|
||||||
sessionId = packageInstaller.createSession(params)
|
val sessionId = packageInstaller.createSession(params)
|
||||||
res.append(sessionId).append('\n')
|
val iSession = IPackageInstallerSession.Stub.asInterface(
|
||||||
res.append('\n').append("write: ")
|
ShizukuBinderWrapper(iPackageInstaller.openSession(sessionId).asBinder()))
|
||||||
val _session = IPackageInstallerSession.Stub.asInterface(
|
session = PackageInstallerUtils.createSession(iSession)
|
||||||
ShizukuBinderWrapper(
|
val inputStream = contentResolver.openInputStream(uri)
|
||||||
_packageInstaller.openSession(sessionId).asBinder()
|
val openedSession = session.openWrite("apk.apk", 0, -1)
|
||||||
)
|
val buffer = ByteArray(8192)
|
||||||
)
|
var length: Int
|
||||||
session = PackageInstallerUtils.createSession(_session)
|
|
||||||
val name = "apk.apk"
|
|
||||||
val `is` = cr.openInputStream(uri)
|
|
||||||
val os = session.openWrite(name, 0, -1)
|
|
||||||
val buf = ByteArray(8192)
|
|
||||||
var len: Int
|
|
||||||
try {
|
try {
|
||||||
while (`is`!!.read(buf).also { len = it } > 0) {
|
while (inputStream!!.read(buffer).also { length = it } > 0) {
|
||||||
os.write(buf, 0, len)
|
openedSession.write(buffer, 0, length)
|
||||||
os.flush()
|
openedSession.flush()
|
||||||
session.fsync(os)
|
session.fsync(openedSession)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
`is`!!.close()
|
inputStream!!.close()
|
||||||
} catch (e: IOException) {
|
openedSession.close()
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
os.close()
|
|
||||||
} catch (e: IOException) {
|
} catch (e: IOException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.append('\n').append("commit: ")
|
|
||||||
val results = arrayOf<Intent?>(null)
|
val results = arrayOf<Intent?>(null)
|
||||||
val countDownLatch = CountDownLatch(1)
|
val countDownLatch = CountDownLatch(1)
|
||||||
val intentSender: IntentSender =
|
val intentSender: IntentSender =
|
||||||
@ -139,66 +110,62 @@ class MainActivity: FlutterActivity() {
|
|||||||
})
|
})
|
||||||
session.commit(intentSender)
|
session.commit(intentSender)
|
||||||
countDownLatch.await()
|
countDownLatch.await()
|
||||||
val result = results[0]
|
res = results[0]!!.getIntExtra(
|
||||||
val status =
|
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE) == 0
|
||||||
result!!.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE)
|
} catch (_: Exception) {
|
||||||
val message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
|
res = false
|
||||||
res.append('\n').append("status: ").append(status).append(" (").append(message)
|
|
||||||
.append(")")
|
|
||||||
} catch (tr: Throwable) {
|
|
||||||
tr.printStackTrace()
|
|
||||||
res.append(tr)
|
|
||||||
} finally {
|
} finally {
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
try {
|
try {
|
||||||
session.close()
|
session.close()
|
||||||
} catch (tr: Throwable) {
|
} catch (_: Exception) {
|
||||||
res.append(tr)
|
res = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
result.success(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun installWithShizuku(apkFilePath: String, result: Result) {
|
private fun rootCheckPermission(result: Result) {
|
||||||
shizukuCheckPermission()
|
Shell.getShell(Shell.GetShellCallback(
|
||||||
shizukuInstallApk(Uri.parse("file://$apkFilePath"))
|
fun(shell: Shell) {
|
||||||
result.success(0)
|
result.success(shell.isRoot)
|
||||||
}
|
|
||||||
|
|
||||||
private fun installWithRoot(apkFilePath: String, result: Result) {
|
|
||||||
Shell.sh("pm install -r -t " + apkFilePath).submit { out ->
|
|
||||||
val builder = StringBuilder()
|
|
||||||
for (data in out.getOut()) {
|
|
||||||
builder.append(data)
|
|
||||||
}
|
}
|
||||||
result.success(if (builder.toString().endsWith("Success")) 0 else 1)
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rootInstallApk(apkFilePath: String, result: Result) {
|
||||||
|
Shell.sh("pm install -R -t " + apkFilePath).submit { out ->
|
||||||
|
val builder = StringBuilder()
|
||||||
|
for (data in out.getOut()) { builder.append(data) }
|
||||||
|
result.success(builder.toString().endsWith("Success"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||||
super.configureFlutterEngine(flutterEngine)
|
super.configureFlutterEngine(flutterEngine)
|
||||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, installersChannel).setMethodCallHandler {
|
Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
||||||
|
installersChannel = MethodChannel(
|
||||||
|
flutterEngine.dartExecutor.binaryMessenger, "installers")
|
||||||
|
installersChannel!!.setMethodCallHandler {
|
||||||
call, result ->
|
call, result ->
|
||||||
var apkFilePath: String? = call.argument("apkFilePath")
|
if (call.method == "checkPermissionShizuku") {
|
||||||
if (call.method == "installWithShizuku") {
|
shizukuCheckPermission()
|
||||||
installWithShizuku(apkFilePath.toString(), result)
|
result.success(0)
|
||||||
|
} else if (call.method == "checkPermissionRoot") {
|
||||||
|
rootCheckPermission(result)
|
||||||
|
} else if (call.method == "installWithShizuku") {
|
||||||
|
val apkFileUri: String? = call.argument("apkFileUri")
|
||||||
|
shizukuInstallApk(apkFileUri!!, result)
|
||||||
} else if (call.method == "installWithRoot") {
|
} else if (call.method == "installWithRoot") {
|
||||||
installWithRoot(apkFilePath.toString(), result)
|
val apkFilePath: String? = call.argument("apkFilePath")
|
||||||
|
rootInstallApk(apkFilePath!!, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
Shizuku.addBinderReceivedListener(shizukuBinderReceivedListener)
|
|
||||||
Shizuku.addBinderDeadListener(shizukuBinderDeadListener)
|
|
||||||
Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
Shizuku.removeBinderReceivedListener(shizukuBinderReceivedListener)
|
|
||||||
Shizuku.removeBinderDeadListener(shizukuBinderDeadListener)
|
|
||||||
Shizuku.removeRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
Shizuku.removeRequestPermissionResultListener(shizukuRequestPermissionResultListener)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -282,6 +282,7 @@
|
|||||||
"normal": "Normal",
|
"normal": "Normal",
|
||||||
"shizuku": "Shizuku",
|
"shizuku": "Shizuku",
|
||||||
"root": "Root",
|
"root": "Root",
|
||||||
|
"shizukuBinderNotFound": "Shizuku is not running",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Remove App?",
|
"one": "Remove App?",
|
||||||
"other": "Remove Apps?"
|
"other": "Remove Apps?"
|
||||||
|
@ -275,13 +275,14 @@
|
|||||||
"downloadingXNotifChannel": "Загрузка {}",
|
"downloadingXNotifChannel": "Загрузка {}",
|
||||||
"completeAppInstallationNotifChannel": "Завершение установки приложения",
|
"completeAppInstallationNotifChannel": "Завершение установки приложения",
|
||||||
"checkingForUpdatesNotifChannel": "Проверка обновлений",
|
"checkingForUpdatesNotifChannel": "Проверка обновлений",
|
||||||
"onlyCheckInstalledOrTrackOnlyApps": "Only check installed and Track-Only apps for updates",
|
"onlyCheckInstalledOrTrackOnlyApps": "Проверять обновления только у установленных или отслеживаемых приложений",
|
||||||
"supportFixedAPKURL": "Support fixed APK URLs",
|
"supportFixedAPKURL": "Поддержка фиксированных URL-адресов APK",
|
||||||
"selectX": "Select {}",
|
"selectX": "Выбрать {}",
|
||||||
"installMethod": "Метод установки",
|
"installMethod": "Метод установки",
|
||||||
"normal": "Нормальный",
|
"normal": "Нормальный",
|
||||||
"shizuku": "Shizuku",
|
"shizuku": "Shizuku",
|
||||||
"root": "Суперпользователь",
|
"root": "Суперпользователь",
|
||||||
|
"shizukuBinderNotFound": "Shizuku не запущен",
|
||||||
"removeAppQuestion": {
|
"removeAppQuestion": {
|
||||||
"one": "Удалить приложение?",
|
"one": "Удалить приложение?",
|
||||||
"other": "Удалить приложения?"
|
"other": "Удалить приложения?"
|
||||||
|
@ -505,7 +505,8 @@ class AppsProvider with ChangeNotifier {
|
|||||||
!(await canDowngradeApps())) {
|
!(await canDowngradeApps())) {
|
||||||
throw DowngradeError();
|
throw DowngradeError();
|
||||||
}
|
}
|
||||||
if (needsBGWorkaround) {
|
if (needsBGWorkaround &&
|
||||||
|
settingsProvider.installMethod == InstallMethodSettings.normal) {
|
||||||
// The below 'await' will never return if we are in a background process
|
// The below 'await' will never return if we are in a background process
|
||||||
// To work around this, we should assume the install will be successful
|
// To work around this, we should assume the install will be successful
|
||||||
// So we update the app's installed version first as we will never get to the later code
|
// So we update the app's installed version first as we will never get to the later code
|
||||||
@ -517,12 +518,13 @@ class AppsProvider with ChangeNotifier {
|
|||||||
attemptToCorrectInstallStatus: false);
|
attemptToCorrectInstallStatus: false);
|
||||||
}
|
}
|
||||||
int? code;
|
int? code;
|
||||||
if (settingsProvider.installMethod == InstallMethodSettings.normal) {
|
switch (settingsProvider.installMethod) {
|
||||||
code = await AndroidPackageInstaller.installApk(apkFilePath: file.file.path);
|
case InstallMethodSettings.normal:
|
||||||
} else if (settingsProvider.installMethod == InstallMethodSettings.shizuku) {
|
code = await AndroidPackageInstaller.installApk(apkFilePath: file.file.path);
|
||||||
code = await Installers.installWithShizuku(apkFilePath: file.file.path);
|
case InstallMethodSettings.shizuku:
|
||||||
} else if (settingsProvider.installMethod == InstallMethodSettings.root) {
|
code = (await Installers.installWithShizuku(apkFileUri: file.file.uri.toString())) ? 0 : 1;
|
||||||
code = await Installers.installWithRoot(apkFilePath: file.file.path);
|
case InstallMethodSettings.root:
|
||||||
|
code = (await Installers.installWithRoot(apkFilePath: file.file.path)) ? 0 : 1;
|
||||||
}
|
}
|
||||||
bool installed = false;
|
bool installed = false;
|
||||||
if (code != null && code != 0 && code != 3) {
|
if (code != null && code != 0 && code != 3) {
|
||||||
@ -679,8 +681,22 @@ class AppsProvider with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
var appId = downloadedFile?.appId ?? downloadedDir!.appId;
|
var appId = downloadedFile?.appId ?? downloadedDir!.appId;
|
||||||
bool willBeSilent = await canInstallSilently(apps[appId]!.app);
|
bool willBeSilent = await canInstallSilently(apps[appId]!.app);
|
||||||
if (!(await settingsProvider.getInstallPermission(enforce: false))) {
|
switch (settingsProvider.installMethod) {
|
||||||
throw ObtainiumError(tr('cancelled'));
|
case InstallMethodSettings.normal:
|
||||||
|
if (!(await settingsProvider.getInstallPermission(enforce: false))) {
|
||||||
|
throw ObtainiumError(tr('cancelled'));
|
||||||
|
}
|
||||||
|
case InstallMethodSettings.shizuku:
|
||||||
|
int code = await Installers.checkPermissionShizuku();
|
||||||
|
if (code == -1) {
|
||||||
|
throw ObtainiumError(tr('shizukuBinderNotFound'));
|
||||||
|
} else if (code == 0) {
|
||||||
|
throw ObtainiumError(tr('cancelled'));
|
||||||
|
}
|
||||||
|
case InstallMethodSettings.root:
|
||||||
|
if (!(await Installers.checkPermissionRoot())) {
|
||||||
|
throw ObtainiumError(tr('cancelled'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!willBeSilent && context != null) {
|
if (!willBeSilent && context != null) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
|
@ -3,12 +3,52 @@ import 'package:flutter/services.dart';
|
|||||||
|
|
||||||
class Installers {
|
class Installers {
|
||||||
static const MethodChannel _channel = MethodChannel('installers');
|
static const MethodChannel _channel = MethodChannel('installers');
|
||||||
|
static bool _callbacksApplied = false;
|
||||||
|
static int _resPermShizuku = -2; // not set
|
||||||
|
|
||||||
static Future<int?> installWithShizuku({required String apkFilePath}) async {
|
static Future waitWhile(bool Function() test,
|
||||||
return await _channel.invokeMethod('installWithShizuku', {'apkFilePath': apkFilePath});
|
[Duration pollInterval = const Duration(milliseconds: 100)]) {
|
||||||
|
var completer = Completer();
|
||||||
|
check() {
|
||||||
|
if (test()) {
|
||||||
|
Timer(pollInterval, check);
|
||||||
|
} else {
|
||||||
|
completer.complete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
check();
|
||||||
|
return completer.future;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<int?> installWithRoot({required String apkFilePath}) async {
|
static Future handleCalls(MethodCall call) async {
|
||||||
return await _channel.invokeMethod('installWithRoot', {'apkFilePath': apkFilePath});
|
if (call.method == 'resPermShizuku') {
|
||||||
|
_resPermShizuku = call.arguments['res'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<int> checkPermissionShizuku() async {
|
||||||
|
if (!_callbacksApplied) {
|
||||||
|
_channel.setMethodCallHandler(handleCalls);
|
||||||
|
_callbacksApplied = true;
|
||||||
|
}
|
||||||
|
await _channel.invokeMethod('checkPermissionShizuku');
|
||||||
|
await waitWhile(() => _resPermShizuku == -2);
|
||||||
|
int res = _resPermShizuku;
|
||||||
|
_resPermShizuku = -2;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> checkPermissionRoot() async {
|
||||||
|
return await _channel.invokeMethod('checkPermissionRoot');
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> installWithShizuku({required String apkFileUri}) async {
|
||||||
|
return await _channel.invokeMethod(
|
||||||
|
'installWithShizuku', {'apkFileUri': apkFileUri});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> installWithRoot({required String apkFilePath}) async {
|
||||||
|
return await _channel.invokeMethod(
|
||||||
|
'installWithRoot', {'apkFilePath': apkFilePath});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user