Move to plugins🐱🎉

This commit is contained in:
Gregory Velichko
2024-04-14 18:40:32 +03:00
parent fb06babb96
commit 7b882d9bd8
17 changed files with 141 additions and 484 deletions

View File

@@ -92,18 +92,6 @@ repositories {
maven { url 'https://jitpack.io' } maven { url 'https://jitpack.io' }
} }
dependencies {
def shizuku_version = '13.1.5'
implementation "dev.rikka.shizuku:api:$shizuku_version"
implementation "dev.rikka.shizuku:provider:$shizuku_version"
def hidden_api_version = '4.3.1'
implementation "dev.rikka.tools.refine:runtime:$hidden_api_version"
implementation "dev.rikka.hidden:compat:$hidden_api_version"
compileOnly "dev.rikka.hidden:stub:$hidden_api_version"
implementation "org.lsposed.hiddenapibypass:hiddenapibypass:4.3"
}
ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3] ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3]
import com.android.build.OutputFile import com.android.build.OutputFile
android.applicationVariants.all { variant -> android.applicationVariants.all { variant ->

View File

@@ -1,156 +1,5 @@
package dev.imranr.obtainium package dev.imranr.obtainium
import android.content.Intent
import android.content.IntentSender
import android.content.pm.IPackageInstaller
import android.content.pm.IPackageInstallerSession
import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Process
import androidx.annotation.NonNull
import dev.imranr.obtainium.util.IIntentSenderAdaptor
import dev.imranr.obtainium.util.IntentSenderUtils
import dev.imranr.obtainium.util.PackageInstallerUtils
import dev.imranr.obtainium.util.ShizukuSystemServerApi
import io.flutter.embedding.android.FlutterActivity 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.util.concurrent.CountDownLatch
import org.lsposed.hiddenapibypass.HiddenApiBypass
import kotlinx.coroutines.async
import kotlinx.coroutines.GlobalScope
import rikka.shizuku.Shizuku
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener
import rikka.shizuku.ShizukuBinderWrapper
class MainActivity: FlutterActivity() { class MainActivity: FlutterActivity()
private var nativeChannel: MethodChannel? = null
private val SHIZUKU_PERMISSION_REQUEST_CODE = (1000..2000).random()
private fun shizukuCheckPermission(result: Result) {
try {
if (Shizuku.isPreV11()) { // Unsupported
result.success(-1)
} else if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
result.success(1)
} else if (Shizuku.shouldShowRequestPermissionRationale()) { // Deny and don't ask again
result.success(0)
} else {
Shizuku.requestPermission(SHIZUKU_PERMISSION_REQUEST_CODE)
result.success(-2)
}
} catch (_: Exception) { // If shizuku binder not found
result.success(-1)
}
}
private val shizukuRequestPermissionResultListener = OnRequestPermissionResultListener {
requestCode: Int, grantResult: Int ->
if (requestCode == SHIZUKU_PERMISSION_REQUEST_CODE) {
val res = if (grantResult == PackageManager.PERMISSION_GRANTED) 1 else 0
nativeChannel!!.invokeMethod("resPermShizuku", mapOf("res" to res))
}
}
private suspend fun shizukuInstallApk(apkFileUri: String, result: Result) {
val uri = Uri.parse(apkFileUri)
var res = false
var session: PackageInstaller.Session? = null
try {
val iPackageInstaller: IPackageInstaller =
ShizukuSystemServerApi.PackageManager_getPackageInstaller()
val isRoot = Shizuku.getUid() == 0
// The reason for use "com.android.shell" as installer package under adb
// is that getMySessions will check installer package's owner
val installerPackageName = if (isRoot) packageName else "com.android.shell"
var installerAttributionTag: String? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
installerAttributionTag = attributionTag
}
val userId = if (isRoot) Process.myUserHandle().hashCode() else 0
val packageInstaller = PackageInstallerUtils.createPackageInstaller(
iPackageInstaller, installerPackageName, installerAttributionTag, userId)
val params =
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
var installFlags: Int = PackageInstallerUtils.getInstallFlags(params)
installFlags = installFlags or (0x00000002/*PackageManager.INSTALL_REPLACE_EXISTING*/
or 0x00000004 /*PackageManager.INSTALL_ALLOW_TEST*/)
PackageInstallerUtils.setInstallFlags(params, installFlags)
val sessionId = packageInstaller.createSession(params)
val iSession = IPackageInstallerSession.Stub.asInterface(
ShizukuBinderWrapper(iPackageInstaller.openSession(sessionId).asBinder()))
session = PackageInstallerUtils.createSession(iSession)
val inputStream = contentResolver.openInputStream(uri)
val openedSession = session.openWrite("apk.apk", 0, -1)
val buffer = ByteArray(8192)
var length: Int
try {
while (inputStream!!.read(buffer).also { length = it } > 0) {
openedSession.write(buffer, 0, length)
openedSession.flush()
session.fsync(openedSession)
}
} finally {
try {
inputStream!!.close()
openedSession.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
val results = arrayOf<Intent?>(null)
val countDownLatch = CountDownLatch(1)
val intentSender: IntentSender =
IntentSenderUtils.newInstance(object : IIntentSenderAdaptor() {
override fun send(intent: Intent?) {
results[0] = intent
countDownLatch.countDown()
}
})
session.commit(intentSender)
countDownLatch.await()
res = results[0]!!.getIntExtra(
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE) == 0
} catch (_: Exception) {
res = false
} finally {
if (session != null) {
try {
session.close()
} catch (_: Exception) {
res = false
}
}
}
result.success(res)
}
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
HiddenApiBypass.addHiddenApiExemptions("")
}
Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener)
nativeChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger, "native")
nativeChannel!!.setMethodCallHandler {
call, result ->
if (call.method == "checkPermissionShizuku") {
shizukuCheckPermission(result)
} else if (call.method == "installWithShizuku") {
val apkFileUri: String = call.argument("apkFileUri")!!
GlobalScope.async { shizukuInstallApk(apkFileUri, result) }
}
}
}
override fun onDestroy() {
super.onDestroy()
Shizuku.removeRequestPermissionResultListener(shizukuRequestPermissionResultListener)
}
}

View File

@@ -1,37 +0,0 @@
package dev.imranr.obtainium.util;
import android.annotation.SuppressLint;
import android.app.Application;
import android.os.Build;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ApplicationUtils {
private static Application application;
public static Application getApplication() {
return application;
}
public static void setApplication(Application application) {
ApplicationUtils.application = application;
}
public static String getProcessName() {
if (Build.VERSION.SDK_INT >= 28)
return Application.getProcessName();
else {
try {
@SuppressLint("PrivateApi")
Class<?> activityThread = Class.forName("android.app.ActivityThread");
@SuppressLint("DiscouragedPrivateApi")
Method method = activityThread.getDeclaredMethod("currentProcessName");
return (String) method.invoke(null);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
}

View File

@@ -1,23 +0,0 @@
package dev.imranr.obtainium.util;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
public abstract class IIntentSenderAdaptor extends IIntentSender.Stub {
public abstract void send(Intent intent);
@Override
public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
send(intent);
return 0;
}
@Override
public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
send(intent);
}
}

View File

@@ -1,14 +0,0 @@
package dev.imranr.obtainium.util;
import android.content.IIntentSender;
import android.content.IntentSender;
import java.lang.reflect.InvocationTargetException;
public class IntentSenderUtils {
public static IntentSender newInstance(IIntentSender binder) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//noinspection JavaReflectionMemberAccess
return IntentSender.class.getConstructor(IIntentSender.class).newInstance(binder);
}
}

View File

@@ -1,41 +0,0 @@
package dev.imranr.obtainium.util;
import android.content.Context;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.os.Build;
import java.lang.reflect.InvocationTargetException;
@SuppressWarnings({"JavaReflectionMemberAccess"})
public class PackageInstallerUtils {
public static PackageInstaller createPackageInstaller(IPackageInstaller installer, String installerPackageName, String installerAttributionTag, int userId) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, String.class, int.class)
.newInstance(installer, installerPackageName, installerAttributionTag, userId);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, int.class)
.newInstance(installer, installerPackageName, userId);
} else {
return PackageInstaller.class.getConstructor(Context.class, PackageManager.class, IPackageInstaller.class, String.class, int.class)
.newInstance(ApplicationUtils.getApplication(), ApplicationUtils.getApplication().getPackageManager(), installer, installerPackageName, userId);
}
}
public static PackageInstaller.Session createSession(IPackageInstallerSession session) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
return PackageInstaller.Session.class.getConstructor(IPackageInstallerSession.class)
.newInstance(session);
}
public static int getInstallFlags(PackageInstaller.SessionParams params) throws NoSuchFieldException, IllegalAccessException {
return (int) PackageInstaller.SessionParams.class.getDeclaredField("installFlags").get(params);
}
public static void setInstallFlags(PackageInstaller.SessionParams params, int newValue) throws NoSuchFieldException, IllegalAccessException {
PackageInstaller.SessionParams.class.getDeclaredField("installFlags").set(params, newValue);
}
}

View File

@@ -1,68 +0,0 @@
package dev.imranr.obtainium.util;
import android.content.Context;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.os.Build;
import android.os.IUserManager;
import android.os.RemoteException;
import java.util.List;
import rikka.shizuku.ShizukuBinderWrapper;
import rikka.shizuku.SystemServiceHelper;
public class ShizukuSystemServerApi {
private static final Singleton<IPackageManager> PACKAGE_MANAGER = new Singleton<IPackageManager>() {
@Override
protected IPackageManager create() {
return IPackageManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));
}
};
private static final Singleton<IUserManager> USER_MANAGER = new Singleton<IUserManager>() {
@Override
protected IUserManager create() {
return IUserManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService(Context.USER_SERVICE)));
}
};
public static IPackageInstaller PackageManager_getPackageInstaller() throws RemoteException {
IPackageInstaller packageInstaller = PACKAGE_MANAGER.get().getPackageInstaller();
return IPackageInstaller.Stub.asInterface(new ShizukuBinderWrapper(packageInstaller.asBinder()));
}
public static List<UserInfo> UserManager_getUsers(boolean excludePartial, boolean excludeDying, boolean excludePreCreated) throws RemoteException {
if (Build.VERSION.SDK_INT >= 30) {
return USER_MANAGER.get().getUsers(excludePartial, excludeDying, excludePreCreated);
} else {
try {
return USER_MANAGER.get().getUsers(excludeDying);
} catch (NoSuchFieldError e) {
return USER_MANAGER.get().getUsers(excludePartial, excludeDying, excludePreCreated);
}
}
}
// method 2: use transactRemote directly
/*public static List<UserInfo> UserManager_getUsers(boolean excludeDying) {
Parcel data = SystemServiceHelper.obtainParcel(Context.USER_SERVICE, "android.os.IUserManager", "getUsers");
Parcel reply = Parcel.obtain();
data.writeInt(excludeDying ? 1 : 0);
List<UserInfo> res = null;
try {
ShizukuService.transactRemote(data, reply, 0);
reply.readException();
res = reply.createTypedArrayList(UserInfo.CREATOR);
} catch (RemoteException e) {
Log.e("ShizukuSample", "UserManager#getUsers", e);
} finally {
data.recycle();
reply.recycle();
}
return res;
}*/
}

View File

@@ -1,17 +0,0 @@
package dev.imranr.obtainium.util;
public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}

View File

@@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx2048M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true

View File

@@ -283,9 +283,11 @@
"selectX": "Select {}", "selectX": "Select {}",
"parallelDownloads": "Allow parallel downloads", "parallelDownloads": "Allow parallel downloads",
"useShizuku": "Use Shizuku or Sui to install", "useShizuku": "Use Shizuku or Sui to install",
"shizukuBinderNotFound": "Сompatible Shizuku service wasn't found", "shizukuBinderNotFound": "Shizuku service not found, probably it's not launched",
"shizukuOld": "Old Shizuku version (<11), update it",
"shizukuOldAndroidWithADB": "Shizuku running on Android < 8.1 with ADB, update Android or use Sui instead",
"shizukuPretendToBeGooglePlay": "Set Google Play as the installation source",
"useSystemFont": "Use the system font", "useSystemFont": "Use the system font",
"systemFontError": "Error loading the system font: {}",
"useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version",
"requestHeader": "Request header", "requestHeader": "Request header",
"useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date",

View File

@@ -283,9 +283,11 @@
"selectX": "Выбрать {}", "selectX": "Выбрать {}",
"parallelDownloads": "Разрешить параллельные загрузки", "parallelDownloads": "Разрешить параллельные загрузки",
"useShizuku": "Использовать Shizuku или Sui для установки", "useShizuku": "Использовать Shizuku или Sui для установки",
"shizukuBinderNotFound": "Совместимый сервис Shizuku не найден", "shizukuBinderNotFound": "Совместимый сервис Shizuku не найден, возможно он не запущен",
"shizukuOld": "Устаревшая версия Shizuku (<11), обновите",
"shizukuOldAndroidWithADB": "Shizuku работает на Android < 8.1 с ADB, обновите Android или используйте Sui",
"shizukuPretendToBeGooglePlay": "Указать Google Play как источник установки",
"useSystemFont": "Использовать системный шрифт", "useSystemFont": "Использовать системный шрифт",
"systemFontError": "Ошибка загрузки системного шрифта: {}",
"useVersionCodeAsOSVersion": "Использовать код версии приложения как версию, обнаруженную ОС", "useVersionCodeAsOSVersion": "Использовать код версии приложения как версию, обнаруженную ОС",
"requestHeader": "Заголовок запроса", "requestHeader": "Заголовок запроса",
"useLatestAssetDateAsReleaseDate": "Использовать последнюю загрузку ресурса в качестве даты выпуска", "useLatestAssetDateAsReleaseDate": "Использовать последнюю загрузку ресурса в качестве даты выпуска",

View File

@@ -12,6 +12,7 @@ import 'package:obtainium/providers/settings_provider.dart';
import 'package:obtainium/providers/source_provider.dart'; import 'package:obtainium/providers/source_provider.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:shizuku_apk_installer/shizuku_apk_installer.dart';
import 'package:url_launcher/url_launcher_string.dart'; import 'package:url_launcher/url_launcher_string.dart';
class SettingsPage extends StatefulWidget { class SettingsPage extends StatefulWidget {
@@ -402,12 +403,17 @@ class _SettingsPageState extends State<SettingsPage> {
value: settingsProvider.useShizuku, value: settingsProvider.useShizuku,
onChanged: (useShizuku) { onChanged: (useShizuku) {
if (useShizuku) { if (useShizuku) {
NativeFeatures.checkPermissionShizuku().then((resCode) { ShizukuApkInstaller.checkPermission().then((resCode) {
settingsProvider.useShizuku = resCode == 1; settingsProvider.useShizuku = resCode!.startsWith('granted');
if (resCode == 0) { switch(resCode){
showError(ObtainiumError(tr('cancelled')), context); case 'binder_not_found':
} else if (resCode == -1) { showError(ObtainiumError(tr('shizukuBinderNotFound')), context);
showError(ObtainiumError(tr('shizukuBinderNotFound')), context); case 'old_shizuku':
showError(ObtainiumError(tr('shizukuOld')), context);
case 'old_android_with_adb':
showError(ObtainiumError(tr('shizukuOldAndroidWithADB')), context);
case 'denied':
showError(ObtainiumError(tr('cancelled')), context);
} }
}); });
} else { } else {
@@ -416,6 +422,24 @@ class _SettingsPageState extends State<SettingsPage> {
}) })
], ],
), ),
if (settingsProvider.useShizuku)
Column(
children: [
height16,
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: Text(tr('shizukuPretendToBeGooglePlay'))),
Switch(
value: settingsProvider.pretendToBeGooglePlay,
onChanged: (value) {
settingsProvider.pretendToBeGooglePlay = value;
})
],
)
],
),
height32, height32,
Text( Text(
tr('sourceSpecific'), tr('sourceSpecific'),
@@ -459,25 +483,35 @@ class _SettingsPageState extends State<SettingsPage> {
), ),
height16, height16,
localeDropdown, localeDropdown,
height16, FutureBuilder(
Row( builder: (ctx, val) {
mainAxisAlignment: MainAxisAlignment.spaceBetween, return (val.data?.version.sdkInt ?? 0) >= 34
children: [ ? Column(
Flexible(child: Text(tr('useSystemFont'))), crossAxisAlignment: CrossAxisAlignment.start,
Switch( children: [
value: settingsProvider.useSystemFont, height16,
onChanged: (useSystemFont) { Row(
if (useSystemFont) { mainAxisAlignment: MainAxisAlignment.spaceBetween,
NativeFeatures.loadSystemFont() children: [
.then((val) { Flexible(child: Text(tr('useSystemFont'))),
settingsProvider.useSystemFont = true; Switch(
}); value: settingsProvider.useSystemFont,
} else { onChanged: (useSystemFont) {
settingsProvider.useSystemFont = false; if (useSystemFont) {
} NativeFeatures.loadSystemFont().then((val) {
}) settingsProvider.useSystemFont = true;
], });
), } else {
settingsProvider.useSystemFont = false;
}
})
]
)
]
)
: const SizedBox.shrink();
},
future: DeviceInfoPlugin().androidInfo),
height16, height16,
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,

View File

@@ -34,7 +34,7 @@ import 'package:android_intent_plus/android_intent.dart';
import 'package:flutter_archive/flutter_archive.dart'; import 'package:flutter_archive/flutter_archive.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import 'package:shared_storage/shared_storage.dart' as saf; import 'package:shared_storage/shared_storage.dart' as saf;
import 'native_provider.dart'; import 'package:shizuku_apk_installer/shizuku_apk_installer.dart';
final pm = AndroidPackageManager(); final pm = AndroidPackageManager();
@@ -634,7 +634,7 @@ class AppsProvider with ChangeNotifier {
!(await canDowngradeApps())) { !(await canDowngradeApps())) {
throw DowngradeError(); throw DowngradeError();
} }
if (needsBGWorkaround) { if (needsBGWorkaround && !settingsProvider.useShizuku) {
// 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
@@ -647,13 +647,10 @@ class AppsProvider with ChangeNotifier {
} }
int? code; int? code;
if (!settingsProvider.useShizuku) { if (!settingsProvider.useShizuku) {
code = await AndroidPackageInstaller.installApk( code = await AndroidPackageInstaller.installApk(apkFilePath: file.file.path);
apkFilePath: file.file.path);
} else { } else {
code = (await NativeFeatures.installWithShizuku( code = await ShizukuApkInstaller.installAPK(file.file.uri.toString(),
apkFileUri: file.file.uri.toString())) settingsProvider.pretendToBeGooglePlay ? "com.android.vending" : "");
? 0
: 1;
} }
bool installed = false; bool installed = false;
if (code != null && code != 0 && code != 3) { if (code != null && code != 0 && code != 3) {
@@ -828,11 +825,16 @@ class AppsProvider with ChangeNotifier {
throw ObtainiumError(tr('cancelled')); throw ObtainiumError(tr('cancelled'));
} }
} else { } else {
int code = await NativeFeatures.checkPermissionShizuku(); String? code = await ShizukuApkInstaller.checkPermission();
if (code == 0) { switch(code!){
throw ObtainiumError(tr('cancelled')); case 'binder_not_found':
} else if (code == -1) { throw ObtainiumError(tr('shizukuBinderNotFound'));
throw ObtainiumError(tr('shizukuBinderNotFound')); case 'old_shizuku':
throw ObtainiumError(tr('shizukuOld'));
case 'old_android_with_adb':
throw ObtainiumError(tr('shizukuOldAndroidWithADB'));
case 'denied':
throw ObtainiumError(tr('cancelled'));
} }
} }
if (!willBeSilent && context != null && !settingsProvider.useShizuku) { if (!willBeSilent && context != null && !settingsProvider.useShizuku) {

View File

@@ -4,36 +4,13 @@ import 'package:android_system_font/android_system_font.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
class NativeFeatures { class NativeFeatures {
static const MethodChannel _channel = MethodChannel('native');
static bool _systemFontLoaded = false; static bool _systemFontLoaded = false;
static bool _callbacksApplied = false;
static int _resPermShizuku = -2; // not set
static Future<ByteData> _readFileBytes(String path) async { static Future<ByteData> _readFileBytes(String path) async {
var bytes = await File(path).readAsBytes(); var bytes = await File(path).readAsBytes();
return ByteData.view(bytes.buffer); return ByteData.view(bytes.buffer);
} }
static Future _handleCalls(MethodCall call) async {
if (call.method == 'resPermShizuku') {
_resPermShizuku = call.arguments['res'];
}
}
static Future _waitWhile(bool Function() test,
[Duration pollInterval = const Duration(milliseconds: 250)]) {
var completer = Completer();
check() {
if (test()) {
Timer(pollInterval, check);
} else {
completer.complete();
}
}
check();
return completer.future;
}
static Future loadSystemFont() async { static Future loadSystemFont() async {
if (_systemFontLoaded) return; if (_systemFontLoaded) return;
var fontLoader = FontLoader('SystemFont'); var fontLoader = FontLoader('SystemFont');
@@ -42,23 +19,4 @@ class NativeFeatures {
fontLoader.load(); fontLoader.load();
_systemFontLoaded = true; _systemFontLoaded = true;
} }
static Future<int> checkPermissionShizuku() async {
if (!_callbacksApplied) {
_channel.setMethodCallHandler(_handleCalls);
_callbacksApplied = true;
}
int res = await _channel.invokeMethod('checkPermissionShizuku');
if (res == -2) {
await _waitWhile(() => _resPermShizuku == -2);
res = _resPermShizuku;
_resPermShizuku = -2;
}
return res;
}
static Future<bool> installWithShizuku({required String apkFileUri}) async {
return await _channel.invokeMethod(
'installWithShizuku', {'apkFileUri': apkFileUri});
}
} }

View File

@@ -82,6 +82,15 @@ class SettingsProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
bool get pretendToBeGooglePlay{
return prefs?.getBool('pretendToBeGooglePlay') ?? false;
}
set pretendToBeGooglePlay(bool pretendToBeGooglePlay) {
prefs?.setBool('pretendToBeGooglePlay', pretendToBeGooglePlay);
notifyListeners();
}
ThemeSettings get theme { ThemeSettings get theme {
return ThemeSettings return ThemeSettings
.values[prefs?.getInt('theme') ?? ThemeSettings.system.index]; .values[prefs?.getInt('theme') ?? ThemeSettings.system.index];

View File

@@ -5,10 +5,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: android_intent_plus name: android_intent_plus
sha256: e92d14009f3f6ebafca6a601958aaebb793559fb03a1961fe3c5596db95af2cb sha256: "2bfdbee8d65e7c26f88b66f0a91f2863da4d3596d8a658b4162c8de5cf04b074"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.1" version: "5.0.2"
android_package_installer: android_package_installer:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -63,10 +63,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: args name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.2" version: "2.5.0"
async: async:
dependency: transitive dependency: transitive
description: description:
@@ -135,10 +135,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: connectivity_plus name: connectivity_plus
sha256: e9feae83b1849f61bad9f6f33ee00646e3410d54ce0821e02f262f9901dad3c9 sha256: ebe15d94de9dd7c31dc2ac54e42780acdf3384b1497c69290c9f3c5b0279fc57
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.1" version: "6.0.2"
connectivity_plus_platform_interface: connectivity_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -199,10 +199,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: device_info_plus name: device_info_plus
sha256: "50fb435ed30c6d2525cbfaaa0f46851ea6131315f213c0d921b0e407b34e3b84" sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "10.0.1" version: "10.1.0"
device_info_plus_platform_interface: device_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -284,10 +284,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_archive name: flutter_archive
sha256: "004132780d382df5171589ab793e2efc9c3eef570fe72d78b4ccfbfbe52762ae" sha256: "22e931ef6ef764edc922e425e46f4a4f888e864b976f4ecbe54aea9859abc090"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.0" version: "6.0.2"
flutter_fgbg: flutter_fgbg:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -316,10 +316,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_local_notifications name: flutter_local_notifications
sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1 sha256: a701df4866f9a38bb8e4450a54c143bbeeb0ce2381e7df5a36e1006f3b43bb28
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "17.0.0" version: "17.0.1"
flutter_local_notifications_linux: flutter_local_notifications_linux:
dependency: transitive dependency: transitive
description: description:
@@ -353,10 +353,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.17" version: "2.0.19"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@@ -371,10 +371,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: fluttertoast name: fluttertoast
sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1 sha256: "81b68579e23fcbcada2db3d50302813d2371664afe6165bc78148050ab94bf66"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.2.4" version: "8.2.5"
gtk: gtk:
dependency: transitive dependency: transitive
description: description:
@@ -547,18 +547,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: path_provider name: path_provider
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.3"
path_provider_android: path_provider_android:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.2.4"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
@@ -683,10 +683,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: share_plus name: share_plus
sha256: "05ec043470319bfbabe0adbc90d3a84cbff0426b9d9f3a6e2ad3e131fa5fa629" sha256: fb5319f3aab4c5dda5ebb92dca978179ba21f8c783ee4380910ef4c1c6824f51
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.0.2" version: "8.0.3"
share_plus_platform_interface: share_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@@ -699,18 +699,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.2.3"
shared_preferences_android: shared_preferences_android:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "2.2.2"
shared_preferences_foundation: shared_preferences_foundation:
dependency: transitive dependency: transitive
description: description:
@@ -759,6 +759,15 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.1" version: "0.8.1"
shizuku_apk_installer:
dependency: "direct main"
description:
path: "."
ref: master
resolved-ref: "25acc02612c2e0fcae40d312e047ac48106f8f6b"
url: "https://github.com/re7gog/shizuku_apk_installer"
source: git
version: "0.0.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@@ -864,18 +873,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.5" version: "6.2.6"
url_launcher_android: url_launcher_android:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.0" version: "6.3.1"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
@@ -928,10 +937,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: uuid name: uuid
sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.3.3" version: "4.4.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@@ -1000,10 +1009,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: win32_registry name: win32_registry
sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.1.3"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@@ -1029,5 +1038,5 @@ packages:
source: hosted source: hosted
version: "3.1.2" version: "3.1.2"
sdks: sdks:
dart: ">=3.3.0 <4.0.0" dart: ">=3.3.3 <4.0.0"
flutter: ">=3.19.0" flutter: ">=3.19.0"

View File

@@ -72,6 +72,10 @@ dependencies:
git: git:
url: https://github.com/re7gog/android_system_font url: https://github.com/re7gog/android_system_font
ref: master ref: master
shizuku_apk_installer:
git:
url: https://github.com/re7gog/shizuku_apk_installer
ref: master
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: