From 9717db0ca4d07168d475544dad4ad67d24b73374 Mon Sep 17 00:00:00 2001 From: Gregory Date: Mon, 5 Feb 2024 18:26:17 +0300 Subject: [PATCH 01/16] Probably fixed --- android/app/build.gradle | 2 - .../dev/imranr/obtainium/MainActivity.kt | 34 +++--------- assets/translations/en.json | 4 +- assets/translations/ru.json | 4 +- lib/pages/settings.dart | 47 ++++++++-------- lib/providers/apps_provider.dart | 55 +++++++------------ lib/providers/native_provider.dart | 9 --- lib/providers/settings_provider.dart | 11 ++-- 8 files changed, 57 insertions(+), 109 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 249b190f..37484ead 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -105,8 +105,6 @@ dependencies { implementation "dev.rikka.hidden:compat:$hidden_api_version" compileOnly "dev.rikka.hidden:stub:$hidden_api_version" implementation "org.lsposed.hiddenapibypass:hiddenapibypass:4.3" - - implementation "com.github.topjohnwu.libsu:core:5.2.2" } ext.abiCodes = ["x86_64": 1, "armeabi-v7a": 2, "arm64-v8a": 3] diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt b/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt index 2991f002..bd92f43d 100644 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt +++ b/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt @@ -11,7 +11,6 @@ import android.os.Build import android.os.Bundle import android.os.Process import androidx.annotation.NonNull -import com.topjohnwu.superuser.Shell import dev.imranr.obtainium.util.IIntentSenderAdaptor import dev.imranr.obtainium.util.IntentSenderUtils import dev.imranr.obtainium.util.PackageInstallerUtils @@ -23,13 +22,15 @@ 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() { private var nativeChannel: MethodChannel? = null - private val SHIZUKU_PERMISSION_REQUEST_CODE = (10..200).random() + private val SHIZUKU_PERMISSION_REQUEST_CODE = 1492 private fun shizukuCheckPermission(result: Result) { try { @@ -43,7 +44,7 @@ class MainActivity: FlutterActivity() { Shizuku.requestPermission(SHIZUKU_PERMISSION_REQUEST_CODE) result.success(-2) } - } catch (_: Exception) { // If shizuku not running + } catch (_: Exception) { // If shizuku binder not found result.success(-1) } } @@ -56,7 +57,7 @@ class MainActivity: FlutterActivity() { } } - private fun shizukuInstallApk(apkFileUri: String, result: Result) { + private suspend fun shizukuInstallApk(apkFileUri: String, result: Result) { val uri = Uri.parse(apkFileUri) var res = false var session: PackageInstaller.Session? = null @@ -129,22 +130,6 @@ class MainActivity: FlutterActivity() { result.success(res) } - private fun rootCheckPermission(result: Result) { - Shell.getShell(Shell.GetShellCallback( - fun(shell: Shell) { - result.success(shell.isRoot) - } - )) - } - - 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) { super.configureFlutterEngine(flutterEngine) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { @@ -160,14 +145,9 @@ class MainActivity: FlutterActivity() { result.success(res) } else if (call.method == "checkPermissionShizuku") { shizukuCheckPermission(result) - } 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") { - val apkFilePath: String? = call.argument("apkFilePath") - rootInstallApk(apkFilePath!!, result) + val apkFileUri: String = call.argument("apkFileUri")!! + GlobalScope.async { shizukuInstallApk(apkFileUri, result) } } } } diff --git a/assets/translations/en.json b/assets/translations/en.json index fbc3b5fc..1a775cda 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -280,9 +280,7 @@ "supportFixedAPKURL": "Support fixed APK URLs", "selectX": "Select {}", "parallelDownloads": "Allow parallel downloads", - "installMethod": "Installation method", - "normal": "Normal", - "root": "Root", + "useShizuku": "Use Shizuku or Sui to install", "shizukuBinderNotFound": "Сompatible Shizuku service wasn't found", "useSystemFont": "Use the system font", "systemFontError": "Error loading the system font: {}", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 654d484d..07dddd84 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -281,9 +281,7 @@ "supportFixedAPKURL": "Поддержка фиксированных URL-адресов APK", "selectX": "Выбрать {}", "parallelDownloads": "Разрешить параллельные загрузки", - "installMethod": "Метод установки", - "normal": "Нормальный", - "root": "Суперпользователь", + "useShizuku": "Использовать Shizuku или Sui для установки", "shizukuBinderNotFound": "Совместимый сервис Shizuku не найден", "useSystemFont": "Использовать системный шрифт", "systemFontError": "Ошибка загрузки системного шрифта: {}", diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 5cc56a2f..521e019f 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -30,29 +30,6 @@ class _SettingsPageState extends State { settingsProvider.initializeSettings(); } - var installMethodDropdown = DropdownButtonFormField( - decoration: InputDecoration(labelText: tr('installMethod')), - value: settingsProvider.installMethod, - items: [ - DropdownMenuItem( - value: InstallMethodSettings.normal, - child: Text(tr('normal')), - ), - const DropdownMenuItem( - value: InstallMethodSettings.shizuku, - child: Text('Shizuku'), - ), - DropdownMenuItem( - value: InstallMethodSettings.root, - child: Text(tr('root')), - ) - ], - onChanged: (value) { - if (value != null) { - settingsProvider.installMethod = value; - } - }); - var themeDropdown = DropdownButtonFormField( decoration: InputDecoration(labelText: tr('theme')), value: settingsProvider.theme, @@ -363,7 +340,29 @@ class _SettingsPageState extends State { }) ], ), - installMethodDropdown, + height16, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible(child: Text(tr('useShizuku'))), + Switch( + value: settingsProvider.useShizuku, + onChanged: (useShizuku) { + if (useShizuku) { + NativeFeatures.checkPermissionShizuku().then((resCode) { + settingsProvider.useShizuku = resCode == 1; + if (resCode == 0) { + showError(ObtainiumError(tr('cancelled')), context); + } else if (resCode == -1) { + showError(ObtainiumError(tr('shizukuBinderNotFound')), context); + } + }); + } else { + settingsProvider.useShizuku = false; + } + }) + ], + ), height32, Text( tr('sourceSpecific'), diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index f6c28813..3b009b01 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -547,8 +547,7 @@ class AppsProvider with ChangeNotifier { !(await canDowngradeApps())) { throw DowngradeError(); } - if (needsBGWorkaround && - settingsProvider.installMethod == InstallMethodSettings.normal) { + if (needsBGWorkaround) { // 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 // So we update the app's installed version first as we will never get to the later code @@ -560,20 +559,14 @@ class AppsProvider with ChangeNotifier { attemptToCorrectInstallStatus: false); } int? code; - switch (settingsProvider.installMethod) { - case InstallMethodSettings.normal: - code = await AndroidPackageInstaller.installApk( - apkFilePath: file.file.path); - case InstallMethodSettings.shizuku: - code = (await NativeFeatures.installWithShizuku( - apkFileUri: file.file.uri.toString())) - ? 0 - : 1; - case InstallMethodSettings.root: - code = - (await NativeFeatures.installWithRoot(apkFilePath: file.file.path)) - ? 0 - : 1; + if (!settingsProvider.useShizuku) { + code = await AndroidPackageInstaller.installApk( + apkFilePath: file.file.path); + } else { + code = (await NativeFeatures.installWithShizuku( + apkFileUri: file.file.uri.toString())) + ? 0 + : 1; } bool installed = false; if (code != null && code != 0 && code != 3) { @@ -732,25 +725,19 @@ class AppsProvider with ChangeNotifier { } var appId = downloadedFile?.appId ?? downloadedDir!.appId; bool willBeSilent = await canInstallSilently(apps[appId]!.app); - switch (settingsProvider.installMethod) { - case InstallMethodSettings.normal: - if (!(await settingsProvider.getInstallPermission( - enforce: false))) { - throw ObtainiumError(tr('cancelled')); - } - case InstallMethodSettings.shizuku: - int code = await NativeFeatures.checkPermissionShizuku(); - if (code == -1) { - throw ObtainiumError(tr('shizukuBinderNotFound')); - } else if (code == 0) { - throw ObtainiumError(tr('cancelled')); - } - case InstallMethodSettings.root: - if (!(await NativeFeatures.checkPermissionRoot())) { - throw ObtainiumError(tr('cancelled')); - } + if (!settingsProvider.useShizuku) { + if (!(await settingsProvider.getInstallPermission(enforce: false))) { + throw ObtainiumError(tr('cancelled')); + } + } else { + int code = await NativeFeatures.checkPermissionShizuku(); + if (code == 0) { + throw ObtainiumError(tr('cancelled')); + } else if (code == -1) { + throw ObtainiumError(tr('shizukuBinderNotFound')); + } } - if (!willBeSilent && context != null) { + if (!willBeSilent && context != null && !settingsProvider.useShizuku) { // ignore: use_build_context_synchronously await waitForUserToReturnToForeground(context); } diff --git a/lib/providers/native_provider.dart b/lib/providers/native_provider.dart index 88e0b824..a32531d1 100644 --- a/lib/providers/native_provider.dart +++ b/lib/providers/native_provider.dart @@ -59,17 +59,8 @@ class NativeFeatures { return res; } - static Future checkPermissionRoot() async { - return await _channel.invokeMethod('checkPermissionRoot'); - } - static Future installWithShizuku({required String apkFileUri}) async { return await _channel.invokeMethod( 'installWithShizuku', {'apkFileUri': apkFileUri}); } - - static Future installWithRoot({required String apkFilePath}) async { - return await _channel.invokeMethod( - 'installWithRoot', {'apkFilePath': apkFilePath}); - } } diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 147dfc95..d9cbe004 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -18,8 +18,6 @@ String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}'; String obtainiumId = 'dev.imranr.obtainium'; String obtainiumUrl = 'https://github.com/ImranR98/Obtainium'; -enum InstallMethodSettings { normal, shizuku, root } - enum ThemeSettings { system, light, dark } enum ColourSettings { basic, materialYou } @@ -61,13 +59,12 @@ class SettingsProvider with ChangeNotifier { notifyListeners(); } - InstallMethodSettings get installMethod { - return InstallMethodSettings.values[ - prefs?.getInt('installMethod') ?? InstallMethodSettings.normal.index]; + bool get useShizuku{ + return prefs?.getBool('useShizuku') ?? false; } - set installMethod(InstallMethodSettings t) { - prefs?.setInt('installMethod', t.index); + set useShizuku(bool useShizuku) { + prefs?.setBool('useShizuku', useShizuku); notifyListeners(); } From 6545498c216b51bd6dbf412be1cb5bb4a1df5e56 Mon Sep 17 00:00:00 2001 From: Gregory Date: Sat, 30 Mar 2024 15:39:48 +0300 Subject: [PATCH 02/16] Return random request code --- .../app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt b/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt index bd92f43d..00480012 100644 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt +++ b/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt @@ -30,7 +30,7 @@ import rikka.shizuku.ShizukuBinderWrapper class MainActivity: FlutterActivity() { private var nativeChannel: MethodChannel? = null - private val SHIZUKU_PERMISSION_REQUEST_CODE = 1492 + private val SHIZUKU_PERMISSION_REQUEST_CODE = (1000..2000).random() private fun shizukuCheckPermission(result: Result) { try { From 049bcfbaf535e840c94daae8ff341b4d3a3bd0d5 Mon Sep 17 00:00:00 2001 From: Gregory Date: Sat, 30 Mar 2024 16:11:55 +0300 Subject: [PATCH 03/16] Ooops --- lib/providers/apps_provider.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index e69b6ece..2068d224 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -816,7 +816,7 @@ class AppsProvider with ChangeNotifier { } else { downloadedDir = downloadedArtifact as DownloadedXApkDir; } - var id = downloadedFile?.appId ?? downloadedDir!.appId; + id = downloadedFile?.appId ?? downloadedDir!.appId; bool willBeSilent = await canInstallSilently(apps[id]!.app); if (!settingsProvider.useShizuku) { if (!(await settingsProvider.getInstallPermission(enforce: false))) { From 2289e58dda87d9418d331aafab77692f9b550dee Mon Sep 17 00:00:00 2001 From: Gregory Date: Sun, 31 Mar 2024 14:18:21 +0300 Subject: [PATCH 04/16] Move to fonts plugin --- .../dev/imranr/obtainium/DefaultSystemFont.kt | 44 ------------------- .../dev/imranr/obtainium/MainActivity.kt | 5 +-- lib/main.dart | 3 ++ lib/pages/settings.dart | 13 +----- lib/providers/native_provider.dart | 16 +++---- pubspec.lock | 9 ++++ pubspec.yaml | 4 ++ 7 files changed, 26 insertions(+), 68 deletions(-) delete mode 100644 android/app/src/main/kotlin/dev/imranr/obtainium/DefaultSystemFont.kt diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/DefaultSystemFont.kt b/android/app/src/main/kotlin/dev/imranr/obtainium/DefaultSystemFont.kt deleted file mode 100644 index 048262b1..00000000 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/DefaultSystemFont.kt +++ /dev/null @@ -1,44 +0,0 @@ -package dev.imranr.obtainium - -import android.util.Xml -import org.xmlpull.v1.XmlPullParser -import java.io.File -import java.io.FileInputStream - -class DefaultSystemFont { - fun get(): String { - return try { - val file = File("/system/etc/fonts.xml") - val fileStream = FileInputStream(file) - parseFontsFileStream(fileStream) - } catch (e: Exception) { - e.message ?: "Unknown fonts.xml parsing exception" - } - } - - private fun parseFontsFileStream(fileStream: FileInputStream): String { - fileStream.use { stream -> - val parser = Xml.newPullParser() - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) - parser.setInput(stream, null) - parser.nextTag() - return parseFonts(parser) - } - } - - private fun parseFonts(parser: XmlPullParser): String { - while (!((parser.next() == XmlPullParser.END_TAG) && (parser.name == "family"))) { - if ((parser.eventType == XmlPullParser.START_TAG) && (parser.name == "font") - && (parser.getAttributeValue(null, "style") == "normal") - && (parser.getAttributeValue(null, "weight") == "400")) { - break - } - } - parser.next() - val fontFile = parser.text.trim() - if (fontFile == "") { - throw NoSuchFieldException("The font filename couldn't be found in fonts.xml") - } - return "/system/fonts/$fontFile" - } -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt b/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt index 00480012..eeeb3666 100644 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt +++ b/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt @@ -140,10 +140,7 @@ class MainActivity: FlutterActivity() { flutterEngine.dartExecutor.binaryMessenger, "native") nativeChannel!!.setMethodCallHandler { call, result -> - if (call.method == "getSystemFont") { - val res = DefaultSystemFont().get() - result.success(res) - } else if (call.method == "checkPermissionShizuku") { + if (call.method == "checkPermissionShizuku") { shizukuCheckPermission(result) } else if (call.method == "installWithShizuku") { val apkFileUri: String = call.argument("apkFileUri")!! diff --git a/lib/main.dart b/lib/main.dart index 32f2c0b3..f36bd8e0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,6 +5,7 @@ import 'package:flutter/services.dart'; import 'package:obtainium/pages/home.dart'; import 'package:obtainium/providers/apps_provider.dart'; import 'package:obtainium/providers/logs_provider.dart'; +import 'package:obtainium/providers/native_provider.dart'; import 'package:obtainium/providers/notifications_provider.dart'; import 'package:obtainium/providers/settings_provider.dart'; import 'package:obtainium/providers/source_provider.dart'; @@ -231,6 +232,8 @@ class _ObtainiumState extends State { .harmonized(); } + if (settingsProvider.useSystemFont) NativeFeatures.loadSystemFont(); + return MaterialApp( title: 'Obtainium', localizationsDelegates: context.localizationDelegates, diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index a8cde003..b8e4def7 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -469,17 +469,8 @@ class _SettingsPageState extends State { onChanged: (useSystemFont) { if (useSystemFont) { NativeFeatures.loadSystemFont() - .then((fontLoadRes) { - if (fontLoadRes == 'ok') { - settingsProvider.useSystemFont = - true; - } else { - showError( - ObtainiumError(tr( - 'systemFontError', - args: [fontLoadRes])), - context); - } + .then((val) { + settingsProvider.useSystemFont = true; }); } else { settingsProvider.useSystemFont = false; diff --git a/lib/providers/native_provider.dart b/lib/providers/native_provider.dart index a32531d1..428069f5 100644 --- a/lib/providers/native_provider.dart +++ b/lib/providers/native_provider.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:io'; +import 'package:android_system_font/android_system_font.dart'; import 'package:flutter/services.dart'; class NativeFeatures { @@ -9,8 +10,7 @@ class NativeFeatures { static int _resPermShizuku = -2; // not set static Future _readFileBytes(String path) async { - var file = File(path); - var bytes = await file.readAsBytes(); + var bytes = await File(path).readAsBytes(); return ByteData.view(bytes.buffer); } @@ -34,15 +34,13 @@ class NativeFeatures { return completer.future; } - static Future loadSystemFont() async { - if (_systemFontLoaded) { return "ok"; } - var getFontRes = await _channel.invokeMethod('getSystemFont'); - if (getFontRes[0] != '/') { return getFontRes; } // Error + static Future loadSystemFont() async { + if (_systemFontLoaded) return; var fontLoader = FontLoader('SystemFont'); - fontLoader.addFont(_readFileBytes(getFontRes)); - await fontLoader.load(); + var fontFilePath = await AndroidSystemFont().getFilePath(); + fontLoader.addFont(_readFileBytes(fontFilePath!)); + fontLoader.load(); _systemFontLoaded = true; - return "ok"; } static Future checkPermissionShizuku() async { diff --git a/pubspec.lock b/pubspec.lock index 28bb66db..262dad87 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -26,6 +26,15 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.1" + android_system_font: + dependency: "direct main" + description: + path: "." + ref: master + resolved-ref: "355f897e92a58a803f91d9270d389d9ec40ba550" + url: "https://github.com/re7gog/android_system_font" + source: git + version: "1.0.0" animations: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 3d8195a4..3c52c575 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,6 +68,10 @@ dependencies: crypto: ^3.0.3 app_links: ^4.0.0 background_fetch: ^1.2.1 + android_system_font: + git: + url: https://github.com/re7gog/android_system_font + ref: master dev_dependencies: flutter_test: From 7b882d9bd85b807df4ccb8ab38ca2c77015d303b Mon Sep 17 00:00:00 2001 From: Gregory Velichko Date: Sun, 14 Apr 2024 18:40:32 +0300 Subject: [PATCH 05/16] =?UTF-8?q?Move=20to=20plugins=F0=9F=90=B1?= =?UTF-8?q?=F0=9F=8E=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/build.gradle | 12 -- .../dev/imranr/obtainium/MainActivity.kt | 153 +----------------- .../obtainium/util/ApplicationUtils.java | 37 ----- .../obtainium/util/IIntentSenderAdaptor.java | 23 --- .../obtainium/util/IntentSenderUtils.java | 14 -- .../obtainium/util/PackageInstallerUtils.java | 41 ----- .../util/ShizukuSystemServerApi.java | 68 -------- .../dev/imranr/obtainium/util/Singleton.java | 17 -- android/gradle.properties | 2 +- assets/translations/en.json | 6 +- assets/translations/ru.json | 6 +- lib/pages/settings.dart | 84 +++++++--- lib/providers/apps_provider.dart | 28 ++-- lib/providers/native_provider.dart | 42 ----- lib/providers/settings_provider.dart | 9 ++ pubspec.lock | 79 +++++---- pubspec.yaml | 4 + 17 files changed, 141 insertions(+), 484 deletions(-) delete mode 100644 android/app/src/main/kotlin/dev/imranr/obtainium/util/ApplicationUtils.java delete mode 100644 android/app/src/main/kotlin/dev/imranr/obtainium/util/IIntentSenderAdaptor.java delete mode 100644 android/app/src/main/kotlin/dev/imranr/obtainium/util/IntentSenderUtils.java delete mode 100644 android/app/src/main/kotlin/dev/imranr/obtainium/util/PackageInstallerUtils.java delete mode 100644 android/app/src/main/kotlin/dev/imranr/obtainium/util/ShizukuSystemServerApi.java delete mode 100644 android/app/src/main/kotlin/dev/imranr/obtainium/util/Singleton.java diff --git a/android/app/build.gradle b/android/app/build.gradle index 6c35e02c..d461e410 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -92,18 +92,6 @@ repositories { 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] import com.android.build.OutputFile android.applicationVariants.all { variant -> diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt b/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt index eeeb3666..09cd3139 100644 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt +++ b/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt @@ -1,156 +1,5 @@ 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.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() { - 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(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) - } -} +class MainActivity: FlutterActivity() diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/util/ApplicationUtils.java b/android/app/src/main/kotlin/dev/imranr/obtainium/util/ApplicationUtils.java deleted file mode 100644 index 6aa74891..00000000 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/util/ApplicationUtils.java +++ /dev/null @@ -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); - } - } - } -} diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/util/IIntentSenderAdaptor.java b/android/app/src/main/kotlin/dev/imranr/obtainium/util/IIntentSenderAdaptor.java deleted file mode 100644 index 2178c773..00000000 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/util/IIntentSenderAdaptor.java +++ /dev/null @@ -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); - } -} diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/util/IntentSenderUtils.java b/android/app/src/main/kotlin/dev/imranr/obtainium/util/IntentSenderUtils.java deleted file mode 100644 index ab6acba6..00000000 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/util/IntentSenderUtils.java +++ /dev/null @@ -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); - } -} diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/util/PackageInstallerUtils.java b/android/app/src/main/kotlin/dev/imranr/obtainium/util/PackageInstallerUtils.java deleted file mode 100644 index 9d5ae14e..00000000 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/util/PackageInstallerUtils.java +++ /dev/null @@ -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); - } -} diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/util/ShizukuSystemServerApi.java b/android/app/src/main/kotlin/dev/imranr/obtainium/util/ShizukuSystemServerApi.java deleted file mode 100644 index 6dcdf757..00000000 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/util/ShizukuSystemServerApi.java +++ /dev/null @@ -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 PACKAGE_MANAGER = new Singleton() { - @Override - protected IPackageManager create() { - return IPackageManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package"))); - } - }; - - private static final Singleton USER_MANAGER = new Singleton() { - @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 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 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 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; - }*/ -} diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/util/Singleton.java b/android/app/src/main/kotlin/dev/imranr/obtainium/util/Singleton.java deleted file mode 100644 index e535245f..00000000 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/util/Singleton.java +++ /dev/null @@ -1,17 +0,0 @@ -package dev.imranr.obtainium.util; - -public abstract class Singleton { - - private T mInstance; - - protected abstract T create(); - - public final T get() { - synchronized (this) { - if (mInstance == null) { - mInstance = create(); - } - return mInstance; - } - } -} diff --git a/android/gradle.properties b/android/gradle.properties index 94adc3a3..f622fd8f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx2048M android.useAndroidX=true android.enableJetifier=true diff --git a/assets/translations/en.json b/assets/translations/en.json index 167fa062..c026b2ff 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -283,9 +283,11 @@ "selectX": "Select {}", "parallelDownloads": "Allow parallel downloads", "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", - "systemFontError": "Error loading the system font: {}", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", "useLatestAssetDateAsReleaseDate": "Use latest asset upload as release date", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 0dffae41..9b912b09 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -283,9 +283,11 @@ "selectX": "Выбрать {}", "parallelDownloads": "Разрешить параллельные загрузки", "useShizuku": "Использовать Shizuku или Sui для установки", - "shizukuBinderNotFound": "Совместимый сервис Shizuku не найден", + "shizukuBinderNotFound": "Совместимый сервис Shizuku не найден, возможно он не запущен", + "shizukuOld": "Устаревшая версия Shizuku (<11), обновите", + "shizukuOldAndroidWithADB": "Shizuku работает на Android < 8.1 с ADB, обновите Android или используйте Sui", + "shizukuPretendToBeGooglePlay": "Указать Google Play как источник установки", "useSystemFont": "Использовать системный шрифт", - "systemFontError": "Ошибка загрузки системного шрифта: {}", "useVersionCodeAsOSVersion": "Использовать код версии приложения как версию, обнаруженную ОС", "requestHeader": "Заголовок запроса", "useLatestAssetDateAsReleaseDate": "Использовать последнюю загрузку ресурса в качестве даты выпуска", diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index b8e4def7..68bad3a2 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -12,6 +12,7 @@ import 'package:obtainium/providers/settings_provider.dart'; import 'package:obtainium/providers/source_provider.dart'; import 'package:provider/provider.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'; class SettingsPage extends StatefulWidget { @@ -402,12 +403,17 @@ class _SettingsPageState extends State { value: settingsProvider.useShizuku, onChanged: (useShizuku) { if (useShizuku) { - NativeFeatures.checkPermissionShizuku().then((resCode) { - settingsProvider.useShizuku = resCode == 1; - if (resCode == 0) { - showError(ObtainiumError(tr('cancelled')), context); - } else if (resCode == -1) { - showError(ObtainiumError(tr('shizukuBinderNotFound')), context); + ShizukuApkInstaller.checkPermission().then((resCode) { + settingsProvider.useShizuku = resCode!.startsWith('granted'); + switch(resCode){ + case 'binder_not_found': + 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 { @@ -416,6 +422,24 @@ class _SettingsPageState extends State { }) ], ), + 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, Text( tr('sourceSpecific'), @@ -459,25 +483,35 @@ class _SettingsPageState extends State { ), height16, localeDropdown, - height16, - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible(child: Text(tr('useSystemFont'))), - Switch( - value: settingsProvider.useSystemFont, - onChanged: (useSystemFont) { - if (useSystemFont) { - NativeFeatures.loadSystemFont() - .then((val) { - settingsProvider.useSystemFont = true; - }); - } else { - settingsProvider.useSystemFont = false; - } - }) - ], - ), + FutureBuilder( + builder: (ctx, val) { + return (val.data?.version.sdkInt ?? 0) >= 34 + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + height16, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible(child: Text(tr('useSystemFont'))), + Switch( + value: settingsProvider.useSystemFont, + onChanged: (useSystemFont) { + if (useSystemFont) { + NativeFeatures.loadSystemFont().then((val) { + settingsProvider.useSystemFont = true; + }); + } else { + settingsProvider.useSystemFont = false; + } + }) + ] + ) + ] + ) + : const SizedBox.shrink(); + }, + future: DeviceInfoPlugin().androidInfo), height16, Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index ee8364fb..904ac8e9 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -34,7 +34,7 @@ import 'package:android_intent_plus/android_intent.dart'; import 'package:flutter_archive/flutter_archive.dart'; import 'package:share_plus/share_plus.dart'; 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(); @@ -634,7 +634,7 @@ class AppsProvider with ChangeNotifier { !(await canDowngradeApps())) { throw DowngradeError(); } - if (needsBGWorkaround) { + if (needsBGWorkaround && !settingsProvider.useShizuku) { // 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 // 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; if (!settingsProvider.useShizuku) { - code = await AndroidPackageInstaller.installApk( - apkFilePath: file.file.path); + code = await AndroidPackageInstaller.installApk(apkFilePath: file.file.path); } else { - code = (await NativeFeatures.installWithShizuku( - apkFileUri: file.file.uri.toString())) - ? 0 - : 1; + code = await ShizukuApkInstaller.installAPK(file.file.uri.toString(), + settingsProvider.pretendToBeGooglePlay ? "com.android.vending" : ""); } bool installed = false; if (code != null && code != 0 && code != 3) { @@ -828,11 +825,16 @@ class AppsProvider with ChangeNotifier { throw ObtainiumError(tr('cancelled')); } } else { - int code = await NativeFeatures.checkPermissionShizuku(); - if (code == 0) { - throw ObtainiumError(tr('cancelled')); - } else if (code == -1) { - throw ObtainiumError(tr('shizukuBinderNotFound')); + String? code = await ShizukuApkInstaller.checkPermission(); + switch(code!){ + case 'binder_not_found': + 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) { diff --git a/lib/providers/native_provider.dart b/lib/providers/native_provider.dart index 428069f5..1b9877a7 100644 --- a/lib/providers/native_provider.dart +++ b/lib/providers/native_provider.dart @@ -4,36 +4,13 @@ import 'package:android_system_font/android_system_font.dart'; import 'package:flutter/services.dart'; class NativeFeatures { - static const MethodChannel _channel = MethodChannel('native'); static bool _systemFontLoaded = false; - static bool _callbacksApplied = false; - static int _resPermShizuku = -2; // not set static Future _readFileBytes(String path) async { var bytes = await File(path).readAsBytes(); 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 { if (_systemFontLoaded) return; var fontLoader = FontLoader('SystemFont'); @@ -42,23 +19,4 @@ class NativeFeatures { fontLoader.load(); _systemFontLoaded = true; } - - static Future 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 installWithShizuku({required String apkFileUri}) async { - return await _channel.invokeMethod( - 'installWithShizuku', {'apkFileUri': apkFileUri}); - } } diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 5f888ec8..b2596b23 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -82,6 +82,15 @@ class SettingsProvider with ChangeNotifier { notifyListeners(); } + bool get pretendToBeGooglePlay{ + return prefs?.getBool('pretendToBeGooglePlay') ?? false; + } + + set pretendToBeGooglePlay(bool pretendToBeGooglePlay) { + prefs?.setBool('pretendToBeGooglePlay', pretendToBeGooglePlay); + notifyListeners(); + } + ThemeSettings get theme { return ThemeSettings .values[prefs?.getInt('theme') ?? ThemeSettings.system.index]; diff --git a/pubspec.lock b/pubspec.lock index 56163f3c..5b288dcb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: "direct main" description: name: android_intent_plus - sha256: e92d14009f3f6ebafca6a601958aaebb793559fb03a1961fe3c5596db95af2cb + sha256: "2bfdbee8d65e7c26f88b66f0a91f2863da4d3596d8a658b4162c8de5cf04b074" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.2" android_package_installer: dependency: "direct main" description: @@ -63,10 +63,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" async: dependency: transitive description: @@ -135,10 +135,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: e9feae83b1849f61bad9f6f33ee00646e3410d54ce0821e02f262f9901dad3c9 + sha256: ebe15d94de9dd7c31dc2ac54e42780acdf3384b1497c69290c9f3c5b0279fc57 url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.0.2" connectivity_plus_platform_interface: dependency: transitive description: @@ -199,10 +199,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "50fb435ed30c6d2525cbfaaa0f46851ea6131315f213c0d921b0e407b34e3b84" + sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91 url: "https://pub.dev" source: hosted - version: "10.0.1" + version: "10.1.0" device_info_plus_platform_interface: dependency: transitive description: @@ -284,10 +284,10 @@ packages: dependency: "direct main" description: name: flutter_archive - sha256: "004132780d382df5171589ab793e2efc9c3eef570fe72d78b4ccfbfbe52762ae" + sha256: "22e931ef6ef764edc922e425e46f4a4f888e864b976f4ecbe54aea9859abc090" url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "6.0.2" flutter_fgbg: dependency: "direct main" description: @@ -316,10 +316,10 @@ packages: dependency: "direct main" description: name: flutter_local_notifications - sha256: f9a05409385b77b06c18f200a41c7c2711ebf7415669350bb0f8474c07bd40d1 + sha256: a701df4866f9a38bb8e4450a54c143bbeeb0ce2381e7df5a36e1006f3b43bb28 url: "https://pub.dev" source: hosted - version: "17.0.0" + version: "17.0.1" flutter_local_notifications_linux: dependency: transitive description: @@ -353,10 +353,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da + sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.0.19" flutter_test: dependency: "direct dev" description: flutter @@ -371,10 +371,10 @@ packages: dependency: "direct main" description: name: fluttertoast - sha256: dfdde255317af381bfc1c486ed968d5a43a2ded9c931e87cbecd88767d6a71c1 + sha256: "81b68579e23fcbcada2db3d50302813d2371664afe6165bc78148050ab94bf66" url: "https://pub.dev" source: hosted - version: "8.2.4" + version: "8.2.5" gtk: dependency: transitive description: @@ -547,18 +547,18 @@ packages: dependency: "direct main" description: name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" path_provider_android: dependency: transitive description: name: path_provider_android - sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" + sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.4" path_provider_foundation: dependency: transitive description: @@ -683,10 +683,10 @@ packages: dependency: "direct main" description: name: share_plus - sha256: "05ec043470319bfbabe0adbc90d3a84cbff0426b9d9f3a6e2ad3e131fa5fa629" + sha256: fb5319f3aab4c5dda5ebb92dca978179ba21f8c783ee4380910ef4c1c6824f51 url: "https://pub.dev" source: hosted - version: "8.0.2" + version: "8.0.3" share_plus_platform_interface: dependency: transitive description: @@ -699,18 +699,18 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.2" shared_preferences_foundation: dependency: transitive description: @@ -759,6 +759,15 @@ packages: url: "https://pub.dev" source: hosted 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: dependency: transitive description: flutter @@ -864,18 +873,18 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" + sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" url: "https://pub.dev" source: hosted - version: "6.2.5" + version: "6.2.6" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 + sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.3.1" url_launcher_ios: dependency: transitive description: @@ -928,10 +937,10 @@ packages: dependency: transitive description: name: uuid - sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 + sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" url: "https://pub.dev" source: hosted - version: "4.3.3" + version: "4.4.0" vector_math: dependency: transitive description: @@ -1000,10 +1009,10 @@ packages: dependency: transitive description: name: win32_registry - sha256: "41fd8a189940d8696b1b810efb9abcf60827b6cbfab90b0c43e8439e3a39d85a" + sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.3" xdg_directories: dependency: transitive description: @@ -1029,5 +1038,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.3.3 <4.0.0" flutter: ">=3.19.0" diff --git a/pubspec.yaml b/pubspec.yaml index f9bc3e12..05c3970d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -72,6 +72,10 @@ dependencies: git: url: https://github.com/re7gog/android_system_font ref: master + shizuku_apk_installer: + git: + url: https://github.com/re7gog/shizuku_apk_installer + ref: master dev_dependencies: flutter_test: From 3131ef8c4e59075e9989ea9ea867598a5f3459d2 Mon Sep 17 00:00:00 2001 From: Gregory Velichko Date: Mon, 15 Apr 2024 12:55:34 +0300 Subject: [PATCH 06/16] =?UTF-8?q?Neat=20slider=20=E2=9C=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/settings.dart | 95 +++++++++++++++++++++++++--- lib/providers/settings_provider.dart | 44 ++++--------- pubspec.lock | 28 ++++++-- pubspec.yaml | 1 + 4 files changed, 121 insertions(+), 47 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 68bad3a2..51a50299 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -1,5 +1,6 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:easy_localization/easy_localization.dart'; +import 'package:equations/equations.dart'; import 'package:flutter/material.dart'; import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/components/generated_form.dart'; @@ -23,14 +24,64 @@ class SettingsPage extends StatefulWidget { } class _SettingsPageState extends State { + List updateIntervalNodes = [ + 15, 30, 60, 120, 180, 360, 720, 1440, 4320, 10080, 20160, 43200]; + int updateInterval = 0; + late SplineInterpolation updateIntervalInterpolator; // 🤓 + String updateIntervalLabel = tr('neverManualOnly'); + bool showIntervalLabel = true; + + void initUpdateIntervalInterpolator() { + List nodes = []; + for (final (index, element) in updateIntervalNodes.indexed) { + nodes.add(InterpolationNode(x: index.toDouble()+1, y: element.toDouble())); + } + updateIntervalInterpolator = SplineInterpolation(nodes: nodes); + } + + void processIntervalSliderValue(double val) { + if (val < 0.5) { + updateInterval = 0; + updateIntervalLabel = tr('neverManualOnly'); + return; + } + int valInterpolated = 0; + if (val < 1) { + valInterpolated = 15; + } else { + valInterpolated = updateIntervalInterpolator.compute(val).round(); + } + if (valInterpolated < 60) { + updateInterval = valInterpolated; + updateIntervalLabel = plural('minute', valInterpolated); + } else if (valInterpolated < 8 * 60) { + int valRounded = (valInterpolated / 15).ceil() * 15; + updateInterval = valRounded; + updateIntervalLabel = plural('hour', valRounded ~/ 60); + int mins = valRounded % 60; + if (mins != 0) updateIntervalLabel += " ${plural('minute', mins)}"; + } else if (valInterpolated < 24 * 60) { + int valRounded = (valInterpolated / 30).ceil() * 30; + updateInterval = valRounded; + updateIntervalLabel = plural('hour', valRounded / 60); + } else if (valInterpolated < 7 * 24 * 60){ + int valRounded = (valInterpolated / (12 * 60)).ceil() * 12 * 60; + updateInterval = valRounded; + updateIntervalLabel = plural('day', valRounded / (24 * 60)); + } else { + int valRounded = (valInterpolated / (24 * 60)).ceil() * 24 * 60; + updateInterval = valRounded; + updateIntervalLabel = plural('day', valRounded ~/ (24 * 60)); + } + } + @override Widget build(BuildContext context) { SettingsProvider settingsProvider = context.watch(); SourceProvider sourceProvider = SourceProvider(); - if (settingsProvider.prefs == null) { - settingsProvider.initializeSettings(); - } - + if (settingsProvider.prefs == null) settingsProvider.initializeSettings(); + initUpdateIntervalInterpolator(); + processIntervalSliderValue(settingsProvider.updateIntervalSliderVal); var themeDropdown = DropdownButtonFormField( decoration: InputDecoration(labelText: tr('theme')), value: settingsProvider.theme, @@ -143,7 +194,7 @@ class _SettingsPageState extends State { } }); - var intervalDropdown = DropdownButtonFormField( + /*var intervalDropdown = DropdownButtonFormField( decoration: InputDecoration(labelText: tr('bgUpdateCheckInterval')), value: settingsProvider.updateInterval, items: updateIntervals.map((e) { @@ -166,7 +217,31 @@ class _SettingsPageState extends State { if (value != null) { settingsProvider.updateInterval = value; } + });*/ + + var intervalSlider = Slider( + value: settingsProvider.updateIntervalSliderVal, + max: updateIntervalNodes.length.toDouble(), + divisions: updateIntervalNodes.length * 20, + label: updateIntervalLabel, + onChanged: (double value) { + setState(() { + settingsProvider.updateIntervalSliderVal = value; + processIntervalSliderValue(value); + }); + }, + onChangeStart: (double value) { + setState(() { + showIntervalLabel = false; }); + }, + onChangeEnd: (double value) { + setState(() { + showIntervalLabel = true; + settingsProvider.updateInterval = updateInterval; + }); + }, + ); var sourceSpecificFields = sourceProvider.sources.map((e) { if (e.sourceConfigSettingFormItems.isNotEmpty) { @@ -217,15 +292,19 @@ class _SettingsPageState extends State { fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.primary), ), - intervalDropdown, + //intervalDropdown, + height16, + if (showIntervalLabel) SizedBox( + child: Text("${tr('bgUpdateCheckInterval')}: $updateIntervalLabel") + ) else const SizedBox(height: 16), + intervalSlider, FutureBuilder( builder: (ctx, val) { - return (val.data?.version.sdkInt ?? 0) >= 30 + return ((val.data?.version.sdkInt ?? 0) >= 30) || settingsProvider.useShizuku ? Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - height16, Row( mainAxisAlignment: MainAxisAlignment diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index b2596b23..25c69f0c 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -28,27 +28,6 @@ enum SortOrderSettings { ascending, descending } const maxAPIRateLimitMinutes = 30; const minUpdateIntervalMinutes = maxAPIRateLimitMinutes + 30; -const maxUpdateIntervalMinutes = 43200; -List updateIntervals = [ - 15, - 30, - 60, - 120, - 180, - 360, - 720, - 1440, - 4320, - 10080, - 20160, - 43200, - 0 -] - .where((element) => - (element >= minUpdateIntervalMinutes && - element <= maxUpdateIntervalMinutes) || - element == 0) - .toList(); class SettingsProvider with ChangeNotifier { SharedPreferences? prefs; @@ -121,21 +100,20 @@ class SettingsProvider with ChangeNotifier { } int get updateInterval { - var min = prefs?.getInt('updateInterval') ?? 360; - if (!updateIntervals.contains(min)) { - var temp = updateIntervals[0]; - for (var i in updateIntervals) { - if (min > i && i != 0) { - temp = i; - } - } - min = temp; - } - return min; + return prefs?.getInt('updateInterval') ?? 360; } set updateInterval(int min) { - prefs?.setInt('updateInterval', (min < 15 && min != 0) ? 15 : min); + prefs?.setInt('updateInterval', min); + notifyListeners(); + } + + double get updateIntervalSliderVal { + return prefs?.getDouble('updateIntervalSliderVal') ?? 6.0; + } + + set updateIntervalSliderVal(double val) { + prefs?.setDouble('updateIntervalSliderVal', val); notifyListeners(); } diff --git a/pubspec.lock b/pubspec.lock index 5b288dcb..96f56cb7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -235,6 +235,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.0.2" + equations: + dependency: "direct main" + description: + name: equations + sha256: ae30e977d601e19aa1fc3409736c5eac01559d1d653a4c30141fbc4e86aa605c + url: "https://pub.dev" + source: hosted + version: "5.0.2" fake_async: dependency: transitive description: @@ -345,10 +353,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "31c12de79262b5431c5492e9c89948aa789158435f707d3519a7fdef6af28af7" + sha256: "04c4722cc36ec5af38acc38ece70d22d3c2123c61305d555750a091517bbe504" url: "https://pub.dev" source: hosted - version: "0.6.22+1" + version: "0.6.23" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -375,6 +383,14 @@ packages: url: "https://pub.dev" source: hosted version: "8.2.5" + fraction: + dependency: transitive + description: + name: fraction + sha256: "09e9504c9177bbd77df56e5d147abfbb3b43360e64bf61510059c14d6a82d524" + url: "https://pub.dev" + source: hosted + version: "5.0.2" gtk: dependency: transitive description: @@ -643,10 +659,10 @@ packages: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "5.4.0" platform: dependency: transitive description: @@ -1025,10 +1041,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.3.0" yaml: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 05c3970d..0f5796c4 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -68,6 +68,7 @@ dependencies: crypto: ^3.0.3 app_links: ^4.0.0 background_fetch: ^1.2.1 + equations: ^5.0.2 android_system_font: git: url: https://github.com/re7gog/android_system_font From 29e13efd669ca549e7235fed1504d088795927d6 Mon Sep 17 00:00:00 2001 From: Gregory Velichko Date: Wed, 17 Apr 2024 17:05:08 +0300 Subject: [PATCH 07/16] Change background shizuku logic --- lib/providers/apps_provider.dart | 39 ++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 904ac8e9..44793a0e 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -509,9 +509,6 @@ class AppsProvider with ChangeNotifier { .isNotEmpty; Future canInstallSilently(App app) async { - if (app.id == obtainiumId) { - return false; - } if (!settingsProvider.enableBackgroundUpdates) { return false; } @@ -519,8 +516,7 @@ class AppsProvider with ChangeNotifier { return false; } if (app.apkUrls.length > 1) { - // Manual API selection means silent install is not possible - return false; + return false; // Manual API selection means silent install is not possible } var osInfo = await DeviceInfoPlugin().androidInfo; @@ -531,20 +527,29 @@ class AppsProvider with ChangeNotifier { ?.installingPackageName : (await pm.getInstallerPackageName(packageName: app.id)); } catch (e) { - // Probably not installed - ignore + return false; // App probably not installed } - if (installerPackageName != obtainiumId) { - // If we did not install the app (or it isn't installed), silent install is not possible + + int? targetSDK = (await getInstalledInfo(app.id))?.applicationInfo?.targetSdkVersion; + // The APK should target a new enough API + // https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int) + if (!(targetSDK != null && targetSDK >= (osInfo.version.sdkInt - 3))) { return false; } - int? targetSDK = - (await getInstalledInfo(app.id))?.applicationInfo?.targetSdkVersion; - // The OS must also be new enough and the APK should target a new enough API - return osInfo.version.sdkInt >= 31 && - targetSDK != null && - targetSDK >= // https://developer.android.com/reference/android/content/pm/PackageInstaller.SessionParams#setRequireUserAction(int) - (osInfo.version.sdkInt - 3); + if (settingsProvider.useShizuku) { + return true; + } + + if (app.id == obtainiumId) { + return false; + } + if (installerPackageName != obtainiumId) { + // If we did not install the app, silent install is not possible + return false; + } + // The OS must also be new enough + return osInfo.version.sdkInt >= 31; } Future waitForUserToReturnToForeground(BuildContext context) async { @@ -634,7 +639,7 @@ class AppsProvider with ChangeNotifier { !(await canDowngradeApps())) { throw DowngradeError(); } - if (needsBGWorkaround && !settingsProvider.useShizuku) { + if (needsBGWorkaround) { // 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 // So we update the app's installed version first as we will never get to the later code @@ -837,7 +842,7 @@ class AppsProvider with ChangeNotifier { throw ObtainiumError(tr('cancelled')); } } - if (!willBeSilent && context != null && !settingsProvider.useShizuku) { + if (!willBeSilent && context != null) { // ignore: use_build_context_synchronously await waitForUserToReturnToForeground(context); } From a311894b9f776a27216fb6a71c2fcdf8262ca997 Mon Sep 17 00:00:00 2001 From: Gregory Velichko Date: Thu, 18 Apr 2024 20:01:40 +0300 Subject: [PATCH 08/16] Shizuku logic 2: Determination of installation --- assets/translations/en.json | 6 ++++++ assets/translations/ru.json | 6 ++++++ lib/providers/apps_provider.dart | 11 ++++++++--- lib/providers/notifications_provider.dart | 18 ++++++++++++------ 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index c026b2ff..16e2bb9a 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -143,8 +143,10 @@ "noNewUpdates": "No new updates.", "xHasAnUpdate": "{} has an update.", "appsUpdated": "Apps Updated", + "appsNotUpdated": "Failed to update applications", "appsUpdatedNotifDescription": "Notifies the user that updates to one or more Apps were applied in the background", "xWasUpdatedToY": "{} was updated to {}.", + "xWasNotUpdatedToY": "Failed to update {} to {}.", "errorCheckingUpdates": "Error Checking for Updates", "errorCheckingUpdatesNotifDescription": "A notification that shows when background update checking fails", "appsRemoved": "Apps Removed", @@ -352,6 +354,10 @@ "one": "{} and 1 more app was updated.", "other": "{} and {} more apps were updated." }, + "xAndNMoreUpdatesFailed": { + "one": "Failed to update {} and 1 more app.", + "other": "Failed to update {} and {} more apps." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} and 1 more app may have been updated.", "other": "{} and {} more apps may have been updated." diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 9b912b09..964eb855 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -143,8 +143,10 @@ "noNewUpdates": "Нет новых обновлений", "xHasAnUpdate": "{} есть обновление", "appsUpdated": "Приложения обновлены", + "appsNotUpdated": "Не удалось обновить приложения", "appsUpdatedNotifDescription": "Уведомляет об обновлении одного или нескольких приложений в фоновом режиме", "xWasUpdatedToY": "{} была обновлена до версии {}", + "xWasNotUpdatedToY": "Не удалось обновить {} до версии {}", "errorCheckingUpdates": "Ошибка при проверке обновлений", "errorCheckingUpdatesNotifDescription": "Уведомление о завершении проверки обновлений в фоновом режиме с ошибкой", "appsRemoved": "Приложение удалено", @@ -352,6 +354,10 @@ "one": "{} и ещё 1 приложение были обновлены", "other": "{} и ещё {} приложений были обновлены" }, + "xAndNMoreUpdatesFailed": { + "one": "Не удалось обновить {} и ещё 1 приложение", + "other": "Не удалось обновить {} и ещё {} приложений" + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} и ещё 1 приложение могли быть обновлены", "other": "{} и ещё {} приложений могли быть обновлены" diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 44793a0e..e90fa296 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -854,7 +854,7 @@ class AppsProvider with ChangeNotifier { var contextIfNewInstall = apps[id]?.installedInfo == null ? context : null; if (downloadedFile != null) { - if (willBeSilent && context == null) { + if (willBeSilent && context == null && !settingsProvider.useShizuku) { installApk(downloadedFile, contextIfNewInstall, needsBGWorkaround: true); } else { @@ -862,7 +862,7 @@ class AppsProvider with ChangeNotifier { await installApk(downloadedFile, contextIfNewInstall); } } else { - if (willBeSilent && context == null) { + if (willBeSilent && context == null && !settingsProvider.useShizuku) { installXApkDir(downloadedDir!, contextIfNewInstall, needsBGWorkaround: true); } else { @@ -870,7 +870,12 @@ class AppsProvider with ChangeNotifier { await installXApkDir(downloadedDir!, contextIfNewInstall); } } - if (willBeSilent && context == null) { + if (willBeSilent && settingsProvider.useShizuku) { + notificationsProvider?.notify(SilentUpdateNotification( + [apps[id]!.app], + sayInstalled, + id: id.hashCode)); + } else if (willBeSilent && context == null) { notificationsProvider?.notify(SilentUpdateAttemptNotification( [apps[id]!.app], id: id.hashCode)); diff --git a/lib/providers/notifications_provider.dart b/lib/providers/notifications_provider.dart index 898126b1..d0c51ed1 100644 --- a/lib/providers/notifications_provider.dart +++ b/lib/providers/notifications_provider.dart @@ -41,20 +41,26 @@ class UpdateNotification extends ObtainiumNotification { } class SilentUpdateNotification extends ObtainiumNotification { - SilentUpdateNotification(List updates, {int? id}) + SilentUpdateNotification(List updates, bool succeeded, {int? id}) : super( id ?? 3, - tr('appsUpdated'), + succeeded + ? tr('appsUpdated') + : tr('appsNotUpdated'), '', 'APPS_UPDATED', tr('appsUpdatedNotifChannel'), tr('appsUpdatedNotifDescription'), Importance.defaultImportance) { message = updates.length == 1 - ? tr('xWasUpdatedToY', - args: [updates[0].finalName, updates[0].latestVersion]) - : plural('xAndNMoreUpdatesInstalled', updates.length - 1, - args: [updates[0].finalName, (updates.length - 1).toString()]); + ? tr(succeeded + ? 'xWasUpdatedToY' + : 'xWasNotUpdatedToY', + args: [updates[0].finalName, updates[0].latestVersion]) + : plural(succeeded + ? 'xAndNMoreUpdatesInstalled' + : "xAndNMoreUpdatesFailed", + updates.length - 1, args: [updates[0].finalName, (updates.length - 1).toString()]); } } From 49022726d39415f19e94cf148cfb73ce3d5a86db Mon Sep 17 00:00:00 2001 From: Gregory Velichko Date: Fri, 19 Apr 2024 16:41:34 +0300 Subject: [PATCH 09/16] Shizuku logic 3: Prettify --- lib/providers/apps_provider.dart | 46 +++++++++++++++----------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index e90fa296..fc674b09 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -713,8 +713,8 @@ class AppsProvider with ChangeNotifier { List archs = (await DeviceInfoPlugin().androidInfo).supportedAbis; if (urlsToSelectFrom.length > 1 && context != null) { - // ignore: use_build_context_synchronously appFileUrl = await showDialog( + // ignore: use_build_context_synchronously context: context, builder: (BuildContext ctx) { return AppFilePicker( @@ -734,10 +734,9 @@ class AppsProvider with ChangeNotifier { if (appFileUrl != null && getHost(appFileUrl.value) != getHost(app.url) && context != null) { - // ignore: use_build_context_synchronously if (!(settingsProvider.hideAPKOriginWarning) && - // ignore: use_build_context_synchronously await showDialog( + // ignore: use_build_context_synchronously context: context, builder: (BuildContext ctx) { return APKOriginWarningDialog( @@ -830,8 +829,7 @@ class AppsProvider with ChangeNotifier { throw ObtainiumError(tr('cancelled')); } } else { - String? code = await ShizukuApkInstaller.checkPermission(); - switch(code!){ + switch((await ShizukuApkInstaller.checkPermission())!){ case 'binder_not_found': throw ObtainiumError(tr('shizukuBinderNotFound')); case 'old_shizuku': @@ -853,32 +851,32 @@ class AppsProvider with ChangeNotifier { bool sayInstalled = true; var contextIfNewInstall = apps[id]?.installedInfo == null ? context : null; + bool needBGWorkaround = willBeSilent && context == null && !settingsProvider.useShizuku; if (downloadedFile != null) { - if (willBeSilent && context == null && !settingsProvider.useShizuku) { - installApk(downloadedFile, contextIfNewInstall, - needsBGWorkaround: true); + if (needBGWorkaround) { + // ignore: use_build_context_synchronously + installApk(downloadedFile, contextIfNewInstall, needsBGWorkaround: true); } else { - sayInstalled = - await installApk(downloadedFile, contextIfNewInstall); + // ignore: use_build_context_synchronously + sayInstalled = await installApk(downloadedFile, contextIfNewInstall); } } else { - if (willBeSilent && context == null && !settingsProvider.useShizuku) { - installXApkDir(downloadedDir!, contextIfNewInstall, - needsBGWorkaround: true); + if (needBGWorkaround) { + // ignore: use_build_context_synchronously + installXApkDir(downloadedDir!, contextIfNewInstall, needsBGWorkaround: true); } else { - sayInstalled = - await installXApkDir(downloadedDir!, contextIfNewInstall); + // ignore: use_build_context_synchronously + sayInstalled = await installXApkDir(downloadedDir!, contextIfNewInstall); } } - if (willBeSilent && settingsProvider.useShizuku) { - notificationsProvider?.notify(SilentUpdateNotification( - [apps[id]!.app], - sayInstalled, - id: id.hashCode)); - } else if (willBeSilent && context == null) { - notificationsProvider?.notify(SilentUpdateAttemptNotification( - [apps[id]!.app], - id: id.hashCode)); + if (willBeSilent && context == null) { + if (!settingsProvider.useShizuku) { + notificationsProvider?.notify(SilentUpdateAttemptNotification( + [apps[id]!.app], id: id.hashCode)); + } else { + notificationsProvider?.notify(SilentUpdateNotification( + [apps[id]!.app], sayInstalled, id: id.hashCode)); + } } if (sayInstalled) { installedIds.add(id); From c16cda1962312ff4e8a33cf107c156828c8a3712 Mon Sep 17 00:00:00 2001 From: Gregory Velichko Date: Fri, 19 Apr 2024 17:57:22 +0300 Subject: [PATCH 10/16] =?UTF-8?q?Make=20pretending=20to=20be=20a=20Google?= =?UTF-8?q?=20Play=20setting=20per=20app=20based=E2=9A=99=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/translations/en.json | 2 +- assets/translations/ru.json | 2 +- lib/pages/settings.dart | 26 ++++---------------------- lib/providers/apps_provider.dart | 13 +++++++------ lib/providers/settings_provider.dart | 9 --------- lib/providers/source_provider.dart | 5 +++++ 6 files changed, 18 insertions(+), 39 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 16e2bb9a..7e2ce542 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -288,7 +288,7 @@ "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", + "shizukuPretendToBeGooglePlay": "Set Google Play as the installation source (if Shizuku is used)", "useSystemFont": "Use the system font", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", "requestHeader": "Request header", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index 964eb855..8f79902e 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -288,7 +288,7 @@ "shizukuBinderNotFound": "Совместимый сервис Shizuku не найден, возможно он не запущен", "shizukuOld": "Устаревшая версия Shizuku (<11), обновите", "shizukuOldAndroidWithADB": "Shizuku работает на Android < 8.1 с ADB, обновите Android или используйте Sui", - "shizukuPretendToBeGooglePlay": "Указать Google Play как источник установки", + "shizukuPretendToBeGooglePlay": "Указать Google Play как источник установки (если используется Shizuku)", "useSystemFont": "Использовать системный шрифт", "useVersionCodeAsOSVersion": "Использовать код версии приложения как версию, обнаруженную ОС", "requestHeader": "Заголовок запроса", diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 51a50299..f874b762 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -55,21 +55,21 @@ class _SettingsPageState extends State { updateInterval = valInterpolated; updateIntervalLabel = plural('minute', valInterpolated); } else if (valInterpolated < 8 * 60) { - int valRounded = (valInterpolated / 15).ceil() * 15; + int valRounded = (valInterpolated / 15).floor() * 15; updateInterval = valRounded; updateIntervalLabel = plural('hour', valRounded ~/ 60); int mins = valRounded % 60; if (mins != 0) updateIntervalLabel += " ${plural('minute', mins)}"; } else if (valInterpolated < 24 * 60) { - int valRounded = (valInterpolated / 30).ceil() * 30; + int valRounded = (valInterpolated / 30).floor() * 30; updateInterval = valRounded; updateIntervalLabel = plural('hour', valRounded / 60); } else if (valInterpolated < 7 * 24 * 60){ - int valRounded = (valInterpolated / (12 * 60)).ceil() * 12 * 60; + int valRounded = (valInterpolated / (12 * 60)).floor() * 12 * 60; updateInterval = valRounded; updateIntervalLabel = plural('day', valRounded / (24 * 60)); } else { - int valRounded = (valInterpolated / (24 * 60)).ceil() * 24 * 60; + int valRounded = (valInterpolated / (24 * 60)).floor() * 24 * 60; updateInterval = valRounded; updateIntervalLabel = plural('day', valRounded ~/ (24 * 60)); } @@ -501,24 +501,6 @@ class _SettingsPageState extends State { }) ], ), - 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, Text( tr('sourceSpecific'), diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index fc674b09..7d224ee4 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -573,7 +573,7 @@ class AppsProvider with ChangeNotifier { Future installXApkDir( DownloadedXApkDir dir, BuildContext? firstTimeWithContext, - {bool needsBGWorkaround = false}) async { + {bool needsBGWorkaround = false, bool shizukuPretendToBeGooglePlay = false}) async { // We don't know which APKs in an XAPK are supported by the user's device // So we try installing all of them and assume success if at least one installed // If 0 APKs installed, throw the first install error encountered @@ -588,7 +588,8 @@ class AppsProvider with ChangeNotifier { somethingInstalled = somethingInstalled || await installApk( DownloadedApk(dir.appId, file), firstTimeWithContext, - needsBGWorkaround: needsBGWorkaround); + needsBGWorkaround: needsBGWorkaround, + shizukuPretendToBeGooglePlay: shizukuPretendToBeGooglePlay); } catch (e) { logs.add( 'Could not install APK from XAPK \'${file.path}\': ${e.toString()}'); @@ -611,7 +612,7 @@ class AppsProvider with ChangeNotifier { Future installApk( DownloadedApk file, BuildContext? firstTimeWithContext, - {bool needsBGWorkaround = false}) async { + {bool needsBGWorkaround = false, bool shizukuPretendToBeGooglePlay = false}) async { if (firstTimeWithContext != null && settingsProvider.beforeNewInstallsShareToAppVerifier && (await getInstalledInfo('dev.soupslurpr.appverifier')) != null) { @@ -655,7 +656,7 @@ class AppsProvider with ChangeNotifier { code = await AndroidPackageInstaller.installApk(apkFilePath: file.file.path); } else { code = await ShizukuApkInstaller.installAPK(file.file.uri.toString(), - settingsProvider.pretendToBeGooglePlay ? "com.android.vending" : ""); + shizukuPretendToBeGooglePlay ? "com.android.vending" : ""); } bool installed = false; if (code != null && code != 0 && code != 3) { @@ -858,7 +859,7 @@ class AppsProvider with ChangeNotifier { installApk(downloadedFile, contextIfNewInstall, needsBGWorkaround: true); } else { // ignore: use_build_context_synchronously - sayInstalled = await installApk(downloadedFile, contextIfNewInstall); + sayInstalled = await installApk(downloadedFile, contextIfNewInstall, shizukuPretendToBeGooglePlay: apps[id]!.app.additionalSettings['shizukuPretendToBeGooglePlay'] == true); } } else { if (needBGWorkaround) { @@ -866,7 +867,7 @@ class AppsProvider with ChangeNotifier { installXApkDir(downloadedDir!, contextIfNewInstall, needsBGWorkaround: true); } else { // ignore: use_build_context_synchronously - sayInstalled = await installXApkDir(downloadedDir!, contextIfNewInstall); + sayInstalled = await installXApkDir(downloadedDir!, contextIfNewInstall, shizukuPretendToBeGooglePlay: apps[id]!.app.additionalSettings['shizukuPretendToBeGooglePlay'] == true); } } if (willBeSilent && context == null) { diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 25c69f0c..fc3e78ec 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -61,15 +61,6 @@ class SettingsProvider with ChangeNotifier { notifyListeners(); } - bool get pretendToBeGooglePlay{ - return prefs?.getBool('pretendToBeGooglePlay') ?? false; - } - - set pretendToBeGooglePlay(bool pretendToBeGooglePlay) { - prefs?.setBool('pretendToBeGooglePlay', pretendToBeGooglePlay); - notifyListeners(); - } - ThemeSettings get theme { return ThemeSettings .values[prefs?.getInt('theme') ?? ThemeSettings.system.index]; diff --git a/lib/providers/source_provider.dart b/lib/providers/source_provider.dart index d69b33e2..20de6732 100644 --- a/lib/providers/source_provider.dart +++ b/lib/providers/source_provider.dart @@ -521,6 +521,11 @@ abstract class AppSource { label: tr('autoApkFilterByArch'), defaultValue: true) ], [GeneratedFormTextField('appName', label: tr('appName'), required: false)], + [ + GeneratedFormSwitch('shizukuPretendToBeGooglePlay', + label: tr('shizukuPretendToBeGooglePlay'), + defaultValue: false) + ], [ GeneratedFormSwitch('exemptFromBackgroundUpdates', label: tr('exemptFromBackgroundUpdates')) From 285530784d2603e1c57aff159368465557421889 Mon Sep 17 00:00:00 2001 From: Gregory Velichko Date: Sat, 20 Apr 2024 11:30:34 +0300 Subject: [PATCH 11/16] little --- lib/pages/settings.dart | 25 ------------------------- lib/providers/apps_provider.dart | 10 +++++----- lib/providers/settings_provider.dart | 3 --- 3 files changed, 5 insertions(+), 33 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index f874b762..770d36a3 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -194,31 +194,6 @@ class _SettingsPageState extends State { } }); - /*var intervalDropdown = DropdownButtonFormField( - decoration: InputDecoration(labelText: tr('bgUpdateCheckInterval')), - value: settingsProvider.updateInterval, - items: updateIntervals.map((e) { - int displayNum = (e < 60 - ? e - : e < 1440 - ? e / 60 - : e / 1440) - .round(); - String display = e == 0 - ? tr('neverManualOnly') - : (e < 60 - ? plural('minute', displayNum) - : e < 1440 - ? plural('hour', displayNum) - : plural('day', displayNum)); - return DropdownMenuItem(value: e, child: Text(display)); - }).toList(), - onChanged: (value) { - if (value != null) { - settingsProvider.updateInterval = value; - } - });*/ - var intervalSlider = Slider( value: settingsProvider.updateIntervalSliderVal, max: updateIntervalNodes.length.toDouble(), diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index a58dfb0f..ac780f97 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -1708,7 +1708,7 @@ Future bgUpdateCheck(String taskId, Map? params) async { int maxRetryWaitSeconds = 5; var netResult = await (Connectivity().checkConnectivity()); - if (netResult == ConnectivityResult.none) { + if (netResult.contains(ConnectivityResult.none)) { logs.add('BG update task: No network.'); return; } @@ -1745,8 +1745,8 @@ Future bgUpdateCheck(String taskId, Map? params) async { var networkRestricted = false; if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) { - networkRestricted = (netResult != ConnectivityResult.wifi) && - (netResult != ConnectivityResult.ethernet); + networkRestricted = !netResult.contains(ConnectivityResult.wifi) && + !netResult.contains(ConnectivityResult.ethernet); } if (toCheck.isNotEmpty) { @@ -1790,8 +1790,8 @@ Future bgUpdateCheck(String taskId, Map? params) async { var networkRestricted = false; if (appsProvider.settingsProvider.bgUpdatesOnWiFiOnly) { var netResult = await (Connectivity().checkConnectivity()); - networkRestricted = (netResult != ConnectivityResult.wifi) && - (netResult != ConnectivityResult.ethernet); + networkRestricted = !netResult.contains(ConnectivityResult.wifi) && + !netResult.contains(ConnectivityResult.ethernet); } try { diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index fc3e78ec..36052b87 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -26,9 +26,6 @@ enum SortColumnSettings { added, nameAuthor, authorName, releaseDate } enum SortOrderSettings { ascending, descending } -const maxAPIRateLimitMinutes = 30; -const minUpdateIntervalMinutes = maxAPIRateLimitMinutes + 30; - class SettingsProvider with ChangeNotifier { SharedPreferences? prefs; String? defaultAppDir; From fb206aee84373636203d70d3e1e928999d13d14f Mon Sep 17 00:00:00 2001 From: Gregory Velichko Date: Sat, 20 Apr 2024 11:57:12 +0300 Subject: [PATCH 12/16] Add system theme api level check --- lib/pages/settings.dart | 50 ++++++++++++++++------------ lib/providers/settings_provider.dart | 4 +-- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index 770d36a3..ada33f8e 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -82,28 +82,34 @@ class _SettingsPageState extends State { if (settingsProvider.prefs == null) settingsProvider.initializeSettings(); initUpdateIntervalInterpolator(); processIntervalSliderValue(settingsProvider.updateIntervalSliderVal); - var themeDropdown = DropdownButtonFormField( - decoration: InputDecoration(labelText: tr('theme')), - value: settingsProvider.theme, - items: [ - DropdownMenuItem( - value: ThemeSettings.dark, - child: Text(tr('dark')), - ), - DropdownMenuItem( - value: ThemeSettings.light, - child: Text(tr('light')), - ), - DropdownMenuItem( - value: ThemeSettings.system, - child: Text(tr('followSystem')), - ) - ], - onChanged: (value) { - if (value != null) { - settingsProvider.theme = value; - } - }); + + var themeDropdown = FutureBuilder( + builder: (ctx, val) { + return DropdownButtonFormField( + decoration: InputDecoration(labelText: tr('theme')), + value: settingsProvider.theme, + items: [ + DropdownMenuItem( + value: ThemeSettings.light, + child: Text(tr('light')), + ), + DropdownMenuItem( + value: ThemeSettings.dark, + child: Text(tr('dark')), + ), + if ((val.data?.version.sdkInt ?? 0) >= 29) DropdownMenuItem( + value: ThemeSettings.system, + child: Text(tr('followSystem')), + ) + ], + onChanged: (value) { + if (value != null) { + settingsProvider.theme = value; + } + }); + }, + future: DeviceInfoPlugin().androidInfo + ); var colourDropdown = DropdownButtonFormField( decoration: InputDecoration(labelText: tr('colour')), diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 36052b87..cfb4a647 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -18,7 +18,7 @@ String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}'; String obtainiumId = 'dev.imranr.obtainium'; String obtainiumUrl = 'https://github.com/ImranR98/Obtainium'; -enum ThemeSettings { system, light, dark } +enum ThemeSettings { light, dark, system } enum ColourSettings { basic, materialYou } @@ -60,7 +60,7 @@ class SettingsProvider with ChangeNotifier { ThemeSettings get theme { return ThemeSettings - .values[prefs?.getInt('theme') ?? ThemeSettings.system.index]; + .values[prefs?.getInt('theme') ?? ThemeSettings.light.index]; } set theme(ThemeSettings t) { From ed732cfad26ae2ea591ec7a487ec540df2028c82 Mon Sep 17 00:00:00 2001 From: Gregory Velichko Date: Sat, 20 Apr 2024 18:24:08 +0300 Subject: [PATCH 13/16] =?UTF-8?q?Look=20at=20all=20these=20colors!?= =?UTF-8?q?=F0=9F=8C=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/translations/en.json | 3 + assets/translations/ru.json | 3 + lib/main.dart | 10 +-- lib/pages/settings.dart | 123 +++++++++++++++++++++++---- lib/providers/settings_provider.dart | 23 +++-- pubspec.lock | 26 ++++-- pubspec.yaml | 1 + 7 files changed, 152 insertions(+), 37 deletions(-) diff --git a/assets/translations/en.json b/assets/translations/en.json index 7e2ce542..8bf91f4e 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Required)", "dropdownNoOptsError": "ERROR: DROPDOWN MUST HAVE AT LEAST ONE OPT", "colour": "Colour", + "standard": "Standard", + "custom": "Custom", + "useMaterialYou": "Use Material You", "githubStarredRepos": "GitHub Starred Repos", "uname": "Username", "wrongArgNum": "Wrong number of arguments provided", diff --git a/assets/translations/ru.json b/assets/translations/ru.json index e1344e15..6ce3b472 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(обязательно)", "dropdownNoOptsError": "Ошибка: в выпадающем списке должна быть выбрана хотя бы одна настройка", "colour": "Цвет", + "standard": "Стандартный", + "custom": "Индивидуальный", + "useMaterialYou": "Использовать Material You", "githubStarredRepos": "Избранные репозитории GitHub", "uname": "Имя пользователя", "wrongArgNum": "Неправильное количество предоставленных аргументов", diff --git a/lib/main.dart b/lib/main.dart index f36bd8e0..2b61de84 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -119,8 +119,6 @@ void main() async { BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask); } -var defaultThemeColour = Colors.deepPurple; - class Obtainium extends StatefulWidget { const Obtainium({super.key}); @@ -214,15 +212,13 @@ class _ObtainiumState extends State { // Decide on a colour/brightness scheme based on OS and user settings ColorScheme lightColorScheme; ColorScheme darkColorScheme; - if (lightDynamic != null && - darkDynamic != null && - settingsProvider.colour == ColourSettings.materialYou) { + if (lightDynamic != null && darkDynamic != null && settingsProvider.useMaterialYou) { lightColorScheme = lightDynamic.harmonized(); darkColorScheme = darkDynamic.harmonized(); } else { - lightColorScheme = ColorScheme.fromSeed(seedColor: defaultThemeColour); + lightColorScheme = ColorScheme.fromSeed(seedColor: settingsProvider.themeColor); darkColorScheme = ColorScheme.fromSeed( - seedColor: defaultThemeColour, brightness: Brightness.dark); + seedColor: settingsProvider.themeColor, brightness: Brightness.dark); } // set the background and surface colors to pure black in the amoled theme diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index ada33f8e..b4f94529 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -1,6 +1,7 @@ import 'package:device_info_plus/device_info_plus.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:equations/equations.dart'; +import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; import 'package:obtainium/components/custom_app_bar.dart'; import 'package:obtainium/components/generated_form.dart'; @@ -30,6 +31,10 @@ class _SettingsPageState extends State { late SplineInterpolation updateIntervalInterpolator; // 🤓 String updateIntervalLabel = tr('neverManualOnly'); bool showIntervalLabel = true; + final Map, String> colorsNameMap = + , String> { + ColorTools.createPrimarySwatch(obtainiumThemeColor): 'Obtainium' + }; void initUpdateIntervalInterpolator() { List nodes = []; @@ -111,24 +116,105 @@ class _SettingsPageState extends State { future: DeviceInfoPlugin().androidInfo ); - var colourDropdown = DropdownButtonFormField( - decoration: InputDecoration(labelText: tr('colour')), - value: settingsProvider.colour, - items: const [ - DropdownMenuItem( - value: ColourSettings.basic, - child: Text('Obtainium'), - ), - DropdownMenuItem( - value: ColourSettings.materialYou, - child: Text('Material You'), - ) - ], - onChanged: (value) { - if (value != null) { - settingsProvider.colour = value; + Future colorPickerDialog() async { + return ColorPicker( + color: settingsProvider.themeColor, + onColorChanged: (Color color) => + setState(() => + settingsProvider.themeColor = color + ), + actionButtons: const ColorPickerActionButtons( + okButton: true, + closeButton: true, + dialogActionButtons: false, + ), + pickersEnabled: const { + ColorPickerType.both: false, + ColorPickerType.primary: false, + ColorPickerType.accent: false, + ColorPickerType.bw: false, + ColorPickerType.custom: true, + ColorPickerType.wheel: true, + }, + pickerTypeLabels: { + ColorPickerType.custom: tr('standard'), + ColorPickerType.wheel: tr('custom') + }, + title: Text(tr('selectX', args: [tr('colour')]), + style: Theme.of(context).textTheme.titleLarge), + wheelDiameter: 192, + wheelSquareBorderRadius: 32, + width: 48, + height: 48, + borderRadius: 24, + spacing: 8, + runSpacing: 8, + enableShadesSelection: false, + customColorSwatchesAndNames: colorsNameMap, + showMaterialName: true, + showColorName: true, + materialNameTextStyle: Theme.of(context).textTheme.bodySmall, + colorNameTextStyle: Theme.of(context).textTheme.bodySmall, + copyPasteBehavior: const ColorPickerCopyPasteBehavior(longPressMenu: true), + ).showPickerDialog( + context, + transitionBuilder: (BuildContext context, + Animation a1, Animation a2, Widget widget) { + final double curvedValue = Curves.easeInCubic.transform(a1.value); + return Transform( + alignment: Alignment.center, + transform: Matrix4.diagonal3Values(curvedValue, curvedValue, 1), + child: Opacity( + opacity: curvedValue, + child: widget + ), + ); + }, + transitionDuration: const Duration(milliseconds: 250), + ); + } + + var colorPicker = ListTile( + dense: true, + contentPadding: EdgeInsets.zero, + title: Text(tr('selectX', args: [tr('colour')])), + subtitle: Text("${ColorTools.nameThatColor(settingsProvider.themeColor)} " + "(${ColorTools.materialNameAndCode(settingsProvider.themeColor, + colorSwatchNameMap: colorsNameMap)})"), + trailing: ColorIndicator( + width: 40, + height: 40, + borderRadius: 20, + color: settingsProvider.themeColor, + onSelectFocus: false, + onSelect: () async { + final Color colorBeforeDialog = settingsProvider.themeColor; + if (!(await colorPickerDialog())) { + setState(() { + settingsProvider.themeColor = colorBeforeDialog; + }); } - }); + } + ) + ); + + var useMaterialThemeSwitch = FutureBuilder( + builder: (ctx, val) { + return ((val.data?.version.sdkInt ?? 0) >= 31) ? + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible(child: Text(tr('useMaterialYou'))), + Switch( + value: settingsProvider.useMaterialYou, + onChanged: (value) { + settingsProvider.useMaterialYou = value; + }) + ], + ) : const SizedBox.shrink(); + }, + future: DeviceInfoPlugin().androidInfo + ); var sortDropdown = DropdownButtonFormField( isExpanded: true, @@ -510,8 +596,9 @@ class _SettingsPageState extends State { }) ], ), - colourDropdown, height16, + useMaterialThemeSwitch, + if (!settingsProvider.useMaterialYou) colorPicker, Row( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index cfb4a647..31838bb1 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -17,11 +17,10 @@ import 'package:shared_storage/shared_storage.dart' as saf; String obtainiumTempId = 'imranr98_obtainium_${GitHub().hosts[0]}'; String obtainiumId = 'dev.imranr.obtainium'; String obtainiumUrl = 'https://github.com/ImranR98/Obtainium'; +Color obtainiumThemeColor = const Color(0xFF6438B5); enum ThemeSettings { light, dark, system } -enum ColourSettings { basic, materialYou } - enum SortColumnSettings { added, nameAuthor, authorName, releaseDate } enum SortOrderSettings { ascending, descending } @@ -68,13 +67,23 @@ class SettingsProvider with ChangeNotifier { notifyListeners(); } - ColourSettings get colour { - return ColourSettings - .values[prefs?.getInt('colour') ?? ColourSettings.basic.index]; + Color get themeColor { + int? colorCode = prefs?.getInt('themeColor'); + return (colorCode != null) ? + Color(colorCode) : obtainiumThemeColor; + } + + set themeColor(Color themeColor) { + prefs?.setInt('themeColor', themeColor.value); + notifyListeners(); + } + + bool get useMaterialYou { + return prefs?.getBool('useMaterialYou') ?? false; } - set colour(ColourSettings t) { - prefs?.setInt('colour', t.index); + set useMaterialYou(bool useMaterialYou) { + prefs?.setBool('useMaterialYou', useMaterialYou); notifyListeners(); } diff --git a/pubspec.lock b/pubspec.lock index da986ac3..fce098b1 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -283,6 +283,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + flex_color_picker: + dependency: "direct main" + description: + name: flex_color_picker + sha256: "5c846437069fb7afdd7ade6bf37e628a71d2ab0787095ddcb1253bf9345d5f3a" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + flex_seed_scheme: + dependency: transitive + description: + name: flex_seed_scheme + sha256: "4cee2f1d07259f77e8b36f4ec5f35499d19e74e17c7dce5b819554914082bc01" + url: "https://pub.dev" + source: hosted + version: "1.5.0" flutter: dependency: "direct main" description: flutter @@ -659,10 +675,10 @@ packages: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: cb3798bef7fc021ac45b308f4b51208a152792445cce0448c9a4ba5879dd8750 url: "https://pub.dev" source: hosted - version: "6.0.2" + version: "5.4.0" platform: dependency: transitive description: @@ -1041,10 +1057,10 @@ packages: dependency: transitive description: name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + sha256: "5bc72e1e45e941d825fd7468b9b4cc3b9327942649aeb6fc5cdbf135f0a86e84" url: "https://pub.dev" source: hosted - version: "6.5.0" + version: "6.3.0" yaml: dependency: transitive description: @@ -1054,5 +1070,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.3.0 <4.0.0" + dart: ">=3.3.3 <4.0.0" flutter: ">=3.19.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8c284861..71ff1102 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -69,6 +69,7 @@ dependencies: app_links: ^4.0.0 background_fetch: ^1.2.1 equations: ^5.0.2 + flex_color_picker: ^3.4.1 android_system_font: git: url: https://github.com/re7gog/android_system_font From b92d1541bfc28b7905e760bce890e64bf629762e Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sat, 20 Apr 2024 19:36:02 -0400 Subject: [PATCH 14/16] Restore Flutter branch --- .flutter | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.flutter b/.flutter index 41456452..300451ad 160000 --- a/.flutter +++ b/.flutter @@ -1 +1 @@ -Subproject commit 41456452f29d64e8deb623a3c927524bcf9f111b +Subproject commit 300451adae589accbece3490f4396f10bdf15e6e From 61038a89697156faa6a244b7a28c0d4e574ab3c4 Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sat, 20 Apr 2024 19:38:59 -0400 Subject: [PATCH 15/16] Updated translations --- assets/translations/bs.json | 17 +++++++++++++---- assets/translations/cs.json | 17 +++++++++++++---- assets/translations/de.json | 17 +++++++++++++---- assets/translations/en.json | 6 +++--- assets/translations/es.json | 17 +++++++++++++---- assets/translations/fa.json | 17 +++++++++++++---- assets/translations/fr.json | 17 +++++++++++++---- assets/translations/hu.json | 17 +++++++++++++---- assets/translations/it.json | 17 +++++++++++++---- assets/translations/ja.json | 17 +++++++++++++---- assets/translations/nl.json | 17 +++++++++++++---- assets/translations/pl.json | 17 +++++++++++++---- assets/translations/pt.json | 17 +++++++++++++---- assets/translations/sv.json | 17 +++++++++++++---- assets/translations/tr.json | 17 +++++++++++++---- assets/translations/uk.json | 17 +++++++++++++---- assets/translations/vi.json | 17 +++++++++++++---- assets/translations/zh.json | 17 +++++++++++++---- 18 files changed, 224 insertions(+), 71 deletions(-) diff --git a/assets/translations/bs.json b/assets/translations/bs.json index 62b5b32e..de0923ae 100644 --- a/assets/translations/bs.json +++ b/assets/translations/bs.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(obavezno)", "dropdownNoOptsError": "GREŠKA: PADAJUĆI MENI MORA IMATI NAJMANJE JEDNU OPCIJU", "colour": "Boja", + "standard": "Standard", + "custom": "Custom", + "useMaterialYou": "Use Material You", "githubStarredRepos": "GitHub repo-i sa zvjezdicom", "uname": "Korisničko ime", "wrongArgNum": "Naveden je pogrešan broj argumenata", @@ -143,8 +146,10 @@ "noNewUpdates": "Nema novih ažuriranja.", "xHasAnUpdate": "{} ima ažuriranje.", "appsUpdated": "Aplikacije su ažurirane", + "appsNotUpdated": "Failed to update applications", "appsUpdatedNotifDescription": "Obavještava korisnika da su u pozadini primijenjena ažuriranja na jednu ili više aplikacija", "xWasUpdatedToY": "{} je ažuriran na {}.", + "xWasNotUpdatedToY": "Failed to update {} to {}.", "errorCheckingUpdates": "Greška pri provjeri ažuriranja", "errorCheckingUpdatesNotifDescription": "Obavijest koja se prikazuje kada provjera sigurnosnog ažuriranja ne uspije", "appsRemoved": "Aplikacije su uklonjene", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Podržite fiksne APK URL-ove", "selectX": "Izaberite {}", "parallelDownloads": "Dozvoli paralelna preuzimanja", - "installMethod": "Način instalacije", - "normal": "normalno", - "root": "korijen", + "useShizuku": "Use Shizuku or Sui to install", "shizukuBinderNotFound": "Shizuku is not running", + "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 (if Shizuku is used)", "useSystemFont": "Koristite sistemski font", - "systemFontError": "Greška pri učitavanju sistemskog fonta: {}", "useVersionCodeAsOSVersion": "Koristite kod verzije aplikacije kao verziju koju je otkrio OS", "requestHeader": "Zaglavlje zahtjeva", "useLatestAssetDateAsReleaseDate": "Koristite najnovije otpremanje materijala kao datum izdavanja", @@ -352,6 +357,10 @@ "one": "{} i još 1 aplikacija je ažurirana.", "other": "{} i još {} aplikacija je ažurirano." }, + "xAndNMoreUpdatesFailed": { + "one": "Failed to update {} and 1 more app.", + "other": "Failed to update {} and {} more apps." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} i još jedna aplikacija je vjerovatno ažurirana.", "other": "{} i još {} aplikacija su vjerovatno ažurirane." diff --git a/assets/translations/cs.json b/assets/translations/cs.json index 52d23b49..e001b6ed 100644 --- a/assets/translations/cs.json +++ b/assets/translations/cs.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Požadované)", "dropdownNoOptsError": "ERROR: DROPDOWN MUSÍ MÍT AŽ JEDNU MOŽNOST", "colour": "Barva", + "standard": "Standardní", + "custom": "Vlastní", + "useMaterialYou": "Použijte materiál, který jste", "githubStarredRepos": "GitHub označená hvězdičkou", "uname": "Uživatelské jméno", "wrongArgNum": "Nesprávný počet zadaných argumentů", @@ -143,8 +146,10 @@ "noNewUpdates": "Žádné nové aktualizace.", "xHasAnUpdate": "{} má aktualizaci.", "appsUpdated": "Aplikace aktualizovány", + "appsNotUpdated": "Nepodařilo se aktualizovat aplikace", "appsUpdatedNotifDescription": "Upozornit, že byly provedeny aktualizace jedné nebo více aplikací na pozadí", "xWasUpdatedToY": "{} byla aktualizována na {}", + "xWasNotUpdatedToY": "Nepodařilo se aktualizovat {} na {}.", "errorCheckingUpdates": "Chyba kontroly aktualizací", "errorCheckingUpdatesNotifDescription": "Zobrazit oznámení při neúspěšné kontrole aktualizací na pozadí", "appsRemoved": "Odstraněné aplikace", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Odhadnout novější verzi na základě prvních třiceti číslic kontrolního součtu adresy URL APK, pokud není podporována jinak", "selectX": "Vybrat {}", "parallelDownloads": "Povolit souběžné stahování", - "installMethod": "Metoda instalace", - "normal": "Normální", - "root": "Správce", + "useShizuku": "K instalaci použijte Shizuku nebo Sui", "shizukuBinderNotFound": "Shizuku neběží", + "shizukuOld": "Stará verze Shizuku (<11) - aktualizujte ji", + "shizukuOldAndroidWithADB": "Shizuku běží na Androidu < 8.1 s ADB - aktualizujte Android nebo místo toho použijte Sui", + "shizukuPretendToBeGooglePlay": "Nastavení Google Play jako zdroje instalace (pokud se používá Shizuku)", "useSystemFont": "Použít systémové písmo", - "systemFontError": "Chyba při načítání systémového písma: {}", "useVersionCodeAsOSVersion": "Použít kód verze aplikace jako verzi zjištěnou OS", "requestHeader": "Hlavička požadavku", "useLatestAssetDateAsReleaseDate": "Použít poslední nahrané dílo jako datum vydání", @@ -352,6 +357,10 @@ "one": "{} a 1 další aplikace mají aktualizace.", "other": "{} a {} další aplikace byly aktualizovány." }, + "xAndNMoreUpdatesFailed": { + "one": "Nepodařilo se aktualizovat {} a 1 další aplikaci.", + "other": "Nepodařilo se aktualizovat {} a {} další aplikace." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} a 1 další aplikace možno aktualizovat", "other": "{} a {} další aplikace mohou být aktualizovány." diff --git a/assets/translations/de.json b/assets/translations/de.json index 6af12aaa..cf86c04b 100644 --- a/assets/translations/de.json +++ b/assets/translations/de.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(wird benötigt)", "dropdownNoOptsError": "FEHLER: DROPDOWN MUSS MINDESTENS EINE OPTION HABEN", "colour": "Farbe", + "standard": "Standard", + "custom": "Benutzerdefiniert", + "useMaterialYou": "Verwenden Sie Material, das Sie", "githubStarredRepos": "GitHub Starred Repos", "uname": "Benutzername", "wrongArgNum": "Falsche Anzahl von Argumenten (Parametern) übermittelt", @@ -143,8 +146,10 @@ "noNewUpdates": "Keine neuen Aktualisierungen.", "xHasAnUpdate": "{} hat eine Aktualisierung.", "appsUpdated": "Apps aktualisiert", + "appsNotUpdated": "Aktualisierung der Anwendungen fehlgeschlagen", "appsUpdatedNotifDescription": "Benachrichtigt den Benutzer, dass Aktualisierungen für eine oder mehrere Apps im Hintergrund durchgeführt wurden", "xWasUpdatedToY": "{} wurde auf {} aktualisiert.", + "xWasNotUpdatedToY": "Die Aktualisierung von {} auf {} ist fehlgeschlagen.", "errorCheckingUpdates": "Fehler beim Prüfen auf Aktualisierungen", "errorCheckingUpdatesNotifDescription": "Eine Benachrichtigung, die angezeigt wird, wenn die Prüfung der Hintergrundaktualisierung fehlschlägt", "appsRemoved": "Apps entfernt", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "neuere Version anhand der ersten dreißig Zahlen der Checksumme der APK URL erraten, wenn anderweitig nicht unterstützt", "selectX": "Wähle {}", "parallelDownloads": "Erlaube parallele Downloads", - "installMethod": "Installationsmethode", - "normal": "Normal", - "root": "Root", + "useShizuku": "Verwenden Sie Shizuku oder Sui zur Installation", "shizukuBinderNotFound": "Kompatibler Shizukudienst wurde nicht gefunden", + "shizukuOld": "Alte Shizuku-Version (<11) - aktualisieren Sie sie", + "shizukuOldAndroidWithADB": "Shizuku läuft auf Android < 8.1 mit ADB - aktualisieren Sie Android oder verwenden Sie stattdessen Sui", + "shizukuPretendToBeGooglePlay": "Google Play als Installationsquelle festlegen (wenn Shizuku verwendet wird)", "useSystemFont": "Verwende die Systemschriftart", - "systemFontError": "Fehler beim Laden der Systemschriftart: {}", "useVersionCodeAsOSVersion": "Verwende die Appversion als erkannte Version vom Betriebssystem", "requestHeader": "Request Header", "useLatestAssetDateAsReleaseDate": "Den letzten Asset-Upload als Veröffentlichungsdatum verwenden", @@ -352,6 +357,10 @@ "one": "{} und 1 weitere Anwendung wurden aktualisiert.", "other": "{} und {} weitere Anwendungen wurden aktualisiert." }, + "xAndNMoreUpdatesFailed": { + "one": "Aktualisierung fehlgeschlagen {} und 1 weitere Anwendung.", + "other": "Die Aktualisierung von {} und {} weiteren Anwendungen ist fehlgeschlagen." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} und 1 weitere Anwendung wurden möglicherweise aktualisiert.", "other": "{} und {} weitere Anwendungen wurden möglicherweise aktualisiert." diff --git a/assets/translations/en.json b/assets/translations/en.json index 8bf91f4e..8006c94d 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -288,9 +288,9 @@ "selectX": "Select {}", "parallelDownloads": "Allow parallel downloads", "useShizuku": "Use Shizuku or Sui to install", - "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", + "shizukuBinderNotFound": "Shizuku service not running", + "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 (if Shizuku is used)", "useSystemFont": "Use the system font", "useVersionCodeAsOSVersion": "Use app versionCode as OS-detected version", diff --git a/assets/translations/es.json b/assets/translations/es.json index e0020fb4..8065a88a 100644 --- a/assets/translations/es.json +++ b/assets/translations/es.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Requerido)", "dropdownNoOptsError": "ERROR: EL DESPLEGABLE DEBE TENER AL MENOS UNA OPCIÓN", "colour": "Color", + "standard": "Estándar", + "custom": "A medida", + "useMaterialYou": "Utilice el material que", "githubStarredRepos": "Repositorios favoritos en GitHub", "uname": "Nombre de usuario", "wrongArgNum": "Número de argumentos provistos inválido", @@ -143,8 +146,10 @@ "noNewUpdates": "No hay nuevas actualizaciones.", "xHasAnUpdate": "{} tiene una actualización.", "appsUpdated": "Aplicaciones actualizadas", + "appsNotUpdated": "Error al actualizar las aplicaciones", "appsUpdatedNotifDescription": "Notifica al usuario de que una o más aplicaciones han sido actualizadas en segundo plano", "xWasUpdatedToY": "{} ha sido actualizada a {}.", + "xWasNotUpdatedToY": "Error al actualizar {} a {}.", "errorCheckingUpdates": "Error al buscar actualizaciones", "errorCheckingUpdatesNotifDescription": "Una notificación que muestra cuándo la comprobación de actualizaciones en segundo plano falla", "appsRemoved": "Aplicaciones eliminadas", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Soporte para URLs fijas de APK", "selectX": "Selecciona {}", "parallelDownloads": "Permitir descargas paralelas", - "installMethod": "Método de instalación", - "normal": "Normal", - "root": "Raíz", + "useShizuku": "Utilice Shizuku o Sui para instalar", "shizukuBinderNotFound": "Shizuku no funciona", + "shizukuOld": "Versión antigua de Shizuku (<11) - actualízala", + "shizukuOldAndroidWithADB": "Shizuku corriendo en Android < 8.1 con ADB - actualiza Android o usa Sui en su lugar", + "shizukuPretendToBeGooglePlay": "Establecer Google Play como fuente de instalación (si se utiliza Shizuku)", "useSystemFont": "Usar la fuente de impresión del sistema", - "systemFontError": "Error al cargar la fuente de impresión del sistema: {}", "useVersionCodeAsOSVersion": "Usar la versión de la aplicación como versión detectada por el sistema operativo", "requestHeader": "Encabezado de solicitud", "useLatestAssetDateAsReleaseDate": "Usar la última carga de recursos como fecha de lanzamiento", @@ -352,6 +357,10 @@ "one": "{} y 1 aplicación más se han actualizado.", "other": "{} y {} aplicaciones más se han actualizado." }, + "xAndNMoreUpdatesFailed": { + "one": "Error al actualizar {} y 1 aplicación más.", + "other": "No se han podido actualizar {} y {} aplicaciones más." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} y 1 aplicación más podría haber sido actualizada.", "other": "{} y {} aplicaciones más podrían haber sido actualizadas." diff --git a/assets/translations/fa.json b/assets/translations/fa.json index 3b785eb8..f13367fc 100644 --- a/assets/translations/fa.json +++ b/assets/translations/fa.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(ضروری)", "dropdownNoOptsError": "خطا: کشویی باید حداقل یک گزینه داشته باشد", "colour": "رنگ", + "standard": "Standard", + "custom": "Custom", + "useMaterialYou": "Use Material You", "githubStarredRepos": "مخازن ستاره دار گیتهاب", "uname": "نام کاربری", "wrongArgNum": "تعداد آرگومان های ارائه شده اشتباه است", @@ -143,8 +146,10 @@ "noNewUpdates": "به روز رسانی جدیدی وجود ندارد.", "xHasAnUpdate": "{} یک به روز رسانی دارد.", "appsUpdated": "برنامه ها به روز شدند", + "appsNotUpdated": "Failed to update applications", "appsUpdatedNotifDescription": "به کاربر اطلاع می دهد که به روز رسانی یک یا چند برنامه در پس زمینه اعمال شده است", "xWasUpdatedToY": "{} به {} به روز شد.", + "xWasNotUpdatedToY": "Failed to update {} to {}.", "errorCheckingUpdates": "خطا در بررسی به‌روزرسانی‌ها", "errorCheckingUpdatesNotifDescription": "اعلانی که وقتی بررسی به‌روزرسانی پس‌زمینه ناموفق است نشان می‌دهد", "appsRemoved": "برنامه ها حذف شدند", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "پشتیبانی از URL های APK ثابت", "selectX": "انتخاب کنید {}", "parallelDownloads": "اجازه دانلود موازی", - "installMethod": "روش نصب", - "normal": "طبیعی", - "root": "ریشه", + "useShizuku": "Use Shizuku or Sui to install", "shizukuBinderNotFound": "Shizuku در حال اجرا نیست", + "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 (if Shizuku is used)", "useSystemFont": "استفاده از فونت سیستم", - "systemFontError": "خطا در بارگیری فونت سیستم: {}", "useVersionCodeAsOSVersion": "استفاده کد نسخه برنامه به جای نسخه شناسایی شده توسط سیستم عامل استفاده کنید", "requestHeader": "درخواست سطر بالایی", "useLatestAssetDateAsReleaseDate": "استفاده از آخرین بارگذاری دارایی به عنوان تاریخ انتشار", @@ -352,6 +357,10 @@ "one": "{} و 1 برنامه دیگر به روز شدند.", "other": "{} و {} برنامه دیگر به روز شدند." }, + "xAndNMoreUpdatesFailed": { + "one": "Failed to update {} and 1 more app.", + "other": "Failed to update {} and {} more apps." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} و 1 برنامه دیگر ممکن است به روز شده باشند.", "other": "ممکن است {} و {} برنامه های دیگر به روز شده باشند." diff --git a/assets/translations/fr.json b/assets/translations/fr.json index 46f7bf30..2dc0d7ce 100644 --- a/assets/translations/fr.json +++ b/assets/translations/fr.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Requis)", "dropdownNoOptsError": "ERREUR : LE DÉROULEMENT DOIT AVOIR AU MOINS UNE OPT", "colour": "Couleur", + "standard": "Standard", + "custom": "Sur mesure", + "useMaterialYou": "Utiliser le matériel que vous", "githubStarredRepos": "Dépôts étoilés GitHub", "uname": "Nom d'utilisateur", "wrongArgNum": "Mauvais nombre d'arguments fournis", @@ -143,8 +146,10 @@ "noNewUpdates": "Aucune nouvelle mise à jour.", "xHasAnUpdate": "{} a une mise à jour.", "appsUpdated": "Applications mises à jour", + "appsNotUpdated": "Échec de la mise à jour des applications", "appsUpdatedNotifDescription": "Avertit l'utilisateur que les mises à jour d'une ou plusieurs applications ont été appliquées en arrière-plan", "xWasUpdatedToY": "{} a été mis à jour pour {}.", + "xWasNotUpdatedToY": "Échec de la mise à jour de {} vers {}.", "errorCheckingUpdates": "Erreur lors de la vérification des mises à jour", "errorCheckingUpdatesNotifDescription": "Une notification qui s'affiche lorsque la vérification de la mise à jour en arrière-plan échoue", "appsRemoved": "Applications supprimées", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Prise en charge des URL APK fixes", "selectX": "Sélectionner {}", "parallelDownloads": "Autoriser les téléchargements parallèles", - "installMethod": "Méthode d'installation", - "normal": "Normale", - "root": "Racine", + "useShizuku": "Utiliser Shizuku ou Sui pour l'installation", "shizukuBinderNotFound": "Service Shizuku compatible non trouvé", + "shizukuOld": "Ancienne version de Shizuku (<11) - la mettre à jour", + "shizukuOldAndroidWithADB": "Shizuku fonctionne sur Android < 8.1 avec ADB - mettre à jour Android ou utiliser Sui à la place", + "shizukuPretendToBeGooglePlay": "Définir Google Play comme source d'installation (si Shizuku est utilisé)", "useSystemFont": "Utiliser la police du système", - "systemFontError": "Erreur de chargement de la police du système : {}", "useVersionCodeAsOSVersion": "Utiliser le code de version de l'application comme version détectée par le système d'exploitation", "requestHeader": "En-tête de demande", "useLatestAssetDateAsReleaseDate": "Utiliser le dernier élément téléversé comme date de sortie", @@ -352,6 +357,10 @@ "one": "{} et 1 autre application ont été mises à jour.", "other": "{} et {} autres applications ont été mises à jour." }, + "xAndNMoreUpdatesFailed": { + "one": "Échec de la mise à jour de {} et d'une autre application.", + "other": "Échec de la mise à jour de {} et {} autres applications." + }, "xAndNMoreUpdatesPossiblyInstalled": { "une": "{} et 1 application supplémentaire ont peut-être été mises à jour.", "other": "{} et {} autres applications peuvent avoir été mises à jour." diff --git a/assets/translations/hu.json b/assets/translations/hu.json index 04360263..3a24c2f8 100644 --- a/assets/translations/hu.json +++ b/assets/translations/hu.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Kötelező)", "dropdownNoOptsError": "HIBA: A LEDOBÁST LEGALÁBB EGY OPCIÓHOZ KELL RENDELNI", "colour": "Szín", + "standard": "Standard", + "custom": "Custom", + "useMaterialYou": "Használja az Ön által használt anyagot", "githubStarredRepos": "GitHub Csillagos Repo-k", "uname": "Felh.név", "wrongArgNum": "Rossz számú argumentumot adott meg", @@ -143,8 +146,10 @@ "noNewUpdates": "Nincsenek új frissítések.", "xHasAnUpdate": "A(z) {} frissítést kapott.", "appsUpdated": "Alkalmazások frissítve", + "appsNotUpdated": "Nem sikerült frissíteni az alkalmazásokat", "appsUpdatedNotifDescription": "Értesíti a felhasználót, hogy egy/több app frissítése megtörtént a háttérben", "xWasUpdatedToY": "{} frissítve a következőre: {}.", + "xWasNotUpdatedToY": "A {} frissítése a {}-ra nem sikerült.", "errorCheckingUpdates": "Hiba a frissítések keresésekor", "errorCheckingUpdatesNotifDescription": "Értesítés, amely akkor jelenik meg, ha a háttérbeli frissítések ellenőrzése sikertelen", "appsRemoved": "Alkalmazások eltávolítva", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Támogatja a rögzített APK URL-eket", "selectX": "Kiválaszt {}", "parallelDownloads": "Párhuzamos letöltéseket enged", - "installMethod": "Telepítési mód", - "normal": "Normál", - "root": "Root", + "useShizuku": "Használja Shizuku vagy Sui telepítéséhez", "shizukuBinderNotFound": "A Shizuku nem fut", + "shizukuOld": "Régi Shizuku verzió (<11) - frissítsd!", + "shizukuOldAndroidWithADB": "Shizuku fut Android < 8.1 ADB-vel - frissítse az Androidot vagy használja a Sui-t helyette", + "shizukuPretendToBeGooglePlay": "Állítsa be a Google Play-t telepítési forrásként (ha Shizuku-t használ)", "useSystemFont": "Használja a rendszer betűtípusát", - "systemFontError": "Hiba a rendszer betűtípusának betöltésekor: {}", "useVersionCodeAsOSVersion": "Az app verziókód használata a rendszer által észlelt verzióként", "requestHeader": "Kérelem fejléc", "useLatestAssetDateAsReleaseDate": "Használja a legújabb tartalomfeltöltést megjelenési dátumként", @@ -352,6 +357,10 @@ "one": "A(z) {} és 1 további alkalmazás frissítve.", "other": "{} és {} további alkalmazás frissítve." }, + "xAndNMoreUpdatesFailed": { + "one": "Nem sikerült frissíteni {} és még 1 alkalmazást.", + "other": "Nem sikerült frissíteni {} és {} további alkalmazásokat." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} és 1 további alkalmazás is frissült.", "other": "{} és {} további alkalmazás is frissült." diff --git a/assets/translations/it.json b/assets/translations/it.json index e0b6af0b..18187d76 100644 --- a/assets/translations/it.json +++ b/assets/translations/it.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(richiesto)", "dropdownNoOptsError": "ERRORE: LA TENDINA DEVE AVERE ALMENO UN'OPZIONE", "colour": "Colore", + "standard": "Standard", + "custom": "Personalizzato", + "useMaterialYou": "Utilizzate il materiale che avete a disposizione", "githubStarredRepos": "repository stellati da GitHub", "uname": "Nome utente", "wrongArgNum": "Numero di argomenti forniti errato", @@ -143,8 +146,10 @@ "noNewUpdates": "Nessun nuovo aggiornamento.", "xHasAnUpdate": "Aggiornamento disponibile per {}", "appsUpdated": "App aggiornate", + "appsNotUpdated": "Impossibile aggiornare le applicazioni", "appsUpdatedNotifDescription": "Notifica all'utente che una o più app sono state aggiornate in secondo piano", "xWasUpdatedToY": "{} è stato aggiornato alla {}.", + "xWasNotUpdatedToY": "Impossibile aggiornare {} a {}.", "errorCheckingUpdates": "Controllo degli errori per gli aggiornamenti", "errorCheckingUpdatesNotifDescription": "Una notifica che mostra quando il controllo degli aggiornamenti in secondo piano fallisce", "appsRemoved": "App rimosse", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Supporta URL fissi di APK", "selectX": "Seleziona {}", "parallelDownloads": "Permetti download paralleli", - "installMethod": "Metodo d'installazione", - "normal": "Normale", - "root": "Root", + "useShizuku": "Utilizzare Shizuku o Sui per installare", "shizukuBinderNotFound": "Shizuku non è in esecuzione", + "shizukuOld": "Vecchia versione di Shizuku (<11) - aggiornarla", + "shizukuOldAndroidWithADB": "Shizuku funziona su Android < 8.1 con ADB - aggiornare Android o utilizzare Sui al suo posto", + "shizukuPretendToBeGooglePlay": "Impostare Google Play come fonte di installazione (se si usa Shizuku)", "useSystemFont": "Usa i caratteri di sistema", - "systemFontError": "Errore durante il caricamento dei caratteri di sistema: {}", "useVersionCodeAsOSVersion": "Usa il codice versione dell'app come versione rilevata dal sistema operativo", "requestHeader": "Intestazione della richiesta", "useLatestAssetDateAsReleaseDate": "Usa l'ultimo caricamento della risorsa come data di rilascio", @@ -352,6 +357,10 @@ "one": "{} e un'altra app sono state aggiornate.", "other": "{} e altre {} app sono state aggiornate." }, + "xAndNMoreUpdatesFailed": { + "one": "Non è riuscito ad aggiornare {} e altre 1 app.", + "other": "Non è riuscito ad aggiornare {} e {} altre applicazioni." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} e un'altra app potrebbero essere state aggiornate.", "other": "{} e altre {} app potrebbero essere state aggiornate." diff --git a/assets/translations/ja.json b/assets/translations/ja.json index dba7734a..b9e6b330 100644 --- a/assets/translations/ja.json +++ b/assets/translations/ja.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(必須)", "dropdownNoOptsError": "エラー: ドロップダウンには、少なくとも1つのオプションが必要です", "colour": "カラー", + "standard": "スタンダード", + "custom": "カスタム", + "useMaterialYou": "使用素材", "githubStarredRepos": "Githubでスターしたリポジトリ", "uname": "ユーザー名", "wrongArgNum": "提供する引数の数が間違っています", @@ -143,8 +146,10 @@ "noNewUpdates": "新しいアップデートはありません", "xHasAnUpdate": "{} のアップデートが利用可能です。", "appsUpdated": "アプリをアップデートしました", + "appsNotUpdated": "アプリケーションの更新に失敗", "appsUpdatedNotifDescription": "1つまたは複数のAppのアップデートがバックグラウンドで適用されたことをユーザーに通知する", "xWasUpdatedToY": "{} が {} にアップデートされました", + "xWasNotUpdatedToY": "への更新に失敗しました。", "errorCheckingUpdates": "アップデート確認中のエラー", "errorCheckingUpdatesNotifDescription": "バックグラウンドでのアップデート確認に失敗した際に表示される通知", "appsRemoved": "削除されたアプリ", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "固定されたAPKのURLをサポートする", "selectX": "{} 選択", "parallelDownloads": "並行ダウンロードを許可する", - "installMethod": "インストール方法", - "normal": "通常", - "root": "Root", + "useShizuku": "シズクまたはスイを使って設置する", "shizukuBinderNotFound": "Shizukuが起動していません", + "shizukuOld": "古い雫バージョン (<11) - アップデートしてください。", + "shizukuOldAndroidWithADB": "雫、Android < 8.1でADB動作 - Androidをアップデートするか、代わりにSuiを使うか", + "shizukuPretendToBeGooglePlay": "インストール元をGoogle Playに設定する(雫を使用する場合)", "useSystemFont": "システムフォントを使用する", - "systemFontError": "システムフォントの読み込みエラー: {}", "useVersionCodeAsOSVersion": "アプリのバージョンコードをOSで検出されたバージョンとして使用する", "requestHeader": "リクエストヘッダー", "useLatestAssetDateAsReleaseDate": "最新のアセットアップロードをリリース日として使用する", @@ -352,6 +357,10 @@ "one": "{} とさらに {} 個のアプリがアップデートされました。", "other": "{} とさらに {} 個のアプリがアップデートされました。" }, + "xAndNMoreUpdatesFailed": { + "one": "更新に失敗しました。", + "other": "アプリのアップデートに失敗しました。" + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} とさらに 1 個のアプリがアップデートされた可能性があります。", "other": "{} とさらに {} 個のアプリがアップデートされた可能性があります。" diff --git a/assets/translations/nl.json b/assets/translations/nl.json index 417ec3b9..1582e3ac 100644 --- a/assets/translations/nl.json +++ b/assets/translations/nl.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Verplicht)", "dropdownNoOptsError": "FOUTMELDING: DROPDOWN MOET TENMINSTE ÉÉN OPT HEBBEN", "colour": "Kleur", + "standard": "Standaard", + "custom": "Aangepast", + "useMaterialYou": "Gebruik materiaal", "githubStarredRepos": "GitHub-repo's met ster", "uname": "Gebruikersnaam", "wrongArgNum": "Onjuist aantal argumenten verstrekt.", @@ -143,8 +146,10 @@ "noNewUpdates": "Geen nieuwe updates.", "xHasAnUpdate": "{} heeft een update.", "appsUpdated": "Apps bijgewerkt", + "appsNotUpdated": "Applicaties konden niet worden bijgewerkt", "appsUpdatedNotifDescription": "Stelt de gebruiker op de hoogte dat updates voor één of meer apps in de achtergrond zijn toegepast.", "xWasUpdatedToY": "{} is bijgewerkt naar {}.", + "xWasNotUpdatedToY": "Het bijwerken van {} naar {} is mislukt.", "errorCheckingUpdates": "Fout bij het controleren op updates", "errorCheckingUpdatesNotifDescription": "Een melding die verschijnt wanneer het controleren op updates in de achtergrond mislukt", "appsRemoved": "Apps verwijderd", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Ondersteuning vaste APK URL's", "selectX": "Selecteer {}", "parallelDownloads": "Parallelle downloads toestaan", - "installMethod": "Installatiemethode", - "normal": "Normaal", - "root": "Wortel", + "useShizuku": "Gebruik Shizuku of Sui om te installeren", "shizukuBinderNotFound": "Shizuku draait niet", + "shizukuOld": "Oude Shizuku-versie (<11) - bijwerken", + "shizukuOldAndroidWithADB": "Shizuku draait op Android < 8.1 met ADB - update Android of gebruik Sui in plaats daarvan", + "shizukuPretendToBeGooglePlay": "Google Play instellen als installatiebron (als Shizuku wordt gebruikt)", "useSystemFont": "Gebruik het systeemlettertype", - "systemFontError": "Fout bij het laden van het systeemlettertype: {}", "useVersionCodeAsOSVersion": "Gebruik app versieCode als door OS gedetecteerde versie", "requestHeader": "Verzoekkoptekst", "useLatestAssetDateAsReleaseDate": "Gebruik laatste upload als releasedatum", @@ -352,6 +357,10 @@ "one": "{} en nog 1 app is bijgewerkt.", "other": "{} en {} meer apps zijn bijgewerkt." }, + "xAndNMoreUpdatesFailed": { + "one": "Bijwerken mislukt {} en nog 1 app.", + "other": "Mislukt om {} en {} meer apps bij te werken." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} en nog 1 app zijn mogelijk bijgewerkt.", "other": "{} en {} meer apps zijn mogelijk bijgwerkt." diff --git a/assets/translations/pl.json b/assets/translations/pl.json index 3886a0c7..dc282a39 100644 --- a/assets/translations/pl.json +++ b/assets/translations/pl.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Wymagane)", "dropdownNoOptsError": "BŁĄD: LISTA ROZWIJANA MUSI MIEĆ CO NAJMNIEJ JEDNĄ OPCJĘ", "colour": "Kolor", + "standard": "Standard", + "custom": "Niestandardowe", + "useMaterialYou": "Używaj materiałów", "githubStarredRepos": "Repozytoria GitHub oznaczone gwiazdką", "uname": "Nazwa użytkownika", "wrongArgNum": "Nieprawidłowa liczba podanych argumentów", @@ -143,8 +146,10 @@ "noNewUpdates": "Brak nowych aktualizacji.", "xHasAnUpdate": "{} ma aktualizację.", "appsUpdated": "Zaktualizowano aplikacje", + "appsNotUpdated": "Nie udało się zaktualizować aplikacji", "appsUpdatedNotifDescription": "Informuje, gdy co najmniej jedna aplikacja została zaktualizowana w tle", "xWasUpdatedToY": "{} zaktualizowano do {}.", + "xWasNotUpdatedToY": "Nie udało się zaktualizować {} do {}.", "errorCheckingUpdates": "Błąd sprawdzania aktualizacji", "errorCheckingUpdatesNotifDescription": "Jest wyświetlane, gdy sprawdzanie aktualizacji w tle nie powiedzie się", "appsRemoved": "Usunięte aplikacje", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Obsługuj stałe adresy URL APK", "selectX": "Wybierz {}", "parallelDownloads": "Zezwól na pobieranie równoległe", - "installMethod": "Metoda instalacji", - "normal": "Normalna", - "root": "Źródło", + "useShizuku": "Użyj Shizuku lub Sui, aby zainstalować", "shizukuBinderNotFound": "Shizuku is not running", + "shizukuOld": "Stara wersja Shizuku (<11) - zaktualizuj ją", + "shizukuOldAndroidWithADB": "Shizuku działa na Androidzie < 8.1 z ADB - zaktualizuj Androida lub użyj zamiast tego Sui", + "shizukuPretendToBeGooglePlay": "Ustaw Google Play jako źródło instalacji (jeśli używana jest aplikacja Shizuku).", "useSystemFont": "Użyj czcionki systemowej", - "systemFontError": "Błąd podczas ładowania czcionki systemowej: {}", "useVersionCodeAsOSVersion": "Użyj kodu wersji aplikacji jako wersji wykrytej przez system operacyjny", "requestHeader": "Nagłówek żądania", "useLatestAssetDateAsReleaseDate": "Użyj najnowszego przesłanego zasobu jako daty wydania", @@ -376,6 +381,10 @@ "many": "{} i {} innych apek zostało zaktualizowanych.", "other": "{} i {} inne apki zostały zaktualizowane." }, + "xAndNMoreUpdatesFailed": { + "one": "Nie udało się zaktualizować {} i 1 innej aplikacji.", + "other": "Nie udało się zaktualizować {} i {} więcej aplikacji." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} i 1 inna apka mogły zostać zaktualizowane.", "few": "{} i {} inne apki mogły zostać zaktualizowane.", diff --git a/assets/translations/pt.json b/assets/translations/pt.json index a8b170fc..d6be6914 100644 --- a/assets/translations/pt.json +++ b/assets/translations/pt.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Necessário)", "dropdownNoOptsError": "ERRO: O DROPDOWN DEVE TER PELO MENOS UMA OPÇÃO", "colour": "Cor", + "standard": "Padrão", + "custom": "Personalizado", + "useMaterialYou": "Utilizar o material que", "githubStarredRepos": "repositórios favoritos no GitHub", "uname": "Nome de usuário", "wrongArgNum": "Número de argumentos errado", @@ -143,8 +146,10 @@ "noNewUpdates": "Sem novas atualizações.", "xHasAnUpdate": "{} tem uma atualização.", "appsUpdated": "Aplicativos atualizados", + "appsNotUpdated": "Falha na atualização das aplicações", "appsUpdatedNotifDescription": "Notifica o usuário quando atualizações foram aplicadas em segundo-plano para um ou mais aplicativos ", "xWasUpdatedToY": "{} foi atualizado para {}.", + "xWasNotUpdatedToY": "Falha ao atualizar {} para {}.", "errorCheckingUpdates": "Erro ao procurar por atualizações", "errorCheckingUpdatesNotifDescription": "Uma notificação que mostra quando a checagem por atualizações em segundo-plano falha", "appsRemoved": "Aplicativos removidos", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Suporte a APK com URLs fixas", "selectX": "Selecionar {}", "parallelDownloads": "Permitir downloads paralelos", - "installMethod": "Método de instalação", - "normal": "Normal", - "root": "Root", + "useShizuku": "Utilizar Shizuku ou Sui para instalar", "shizukuBinderNotFound": "O Shizuku não está rodando", + "shizukuOld": "Versão antiga do Shizuku (<11) - atualizar", + "shizukuOldAndroidWithADB": "Shizuku a funcionar no Android < 8.1 com ADB - atualizar o Android ou utilizar o Sui", + "shizukuPretendToBeGooglePlay": "Definir o Google Play como fonte de instalação (se for utilizado o Shizuku)", "useSystemFont": "Usar fonte padrão do sistema", - "systemFontError": "Erro ao carregar a fonte do sistema: {}", "useVersionCodeAsOSVersion": "Usar versionCode do aplicativo como versão detectada pelo sistema operacional", "requestHeader": "Requisitar cabeçalho", "useLatestAssetDateAsReleaseDate": "Use o último upload de recursos como data de lançamento", @@ -352,6 +357,10 @@ "one": "{} e um outro aplicativo foram atualizado.", "other": "{} e {} outros aplicativos foram atualizados." }, + "xAndNMoreUpdatesFailed": { + "one": "Falha ao atualizar {} e mais 1 aplicação.", + "other": "Falha ao atualizar {} e {} mais aplicações." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} e um outro aplicativo podem ter sido atualizados.", "other": "{} e {} outros aplicativos podem ter sido atualizados." diff --git a/assets/translations/sv.json b/assets/translations/sv.json index f77c5d3b..579e58ca 100644 --- a/assets/translations/sv.json +++ b/assets/translations/sv.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Kräver)", "dropdownNoOptsError": "FEL: DROPDOWN MÅSTE HA MINST ETT OPT", "colour": "Färg", + "standard": "Standard", + "custom": "Anpassad", + "useMaterialYou": "Använd material Du", "githubStarredRepos": "GitHub Stjärnmärkta Förråd", "uname": "Användarnamn", "wrongArgNum": "Fel antal argument har angetts", @@ -143,8 +146,10 @@ "noNewUpdates": "Inga nya uppdateringar.", "xHasAnUpdate": "{} har en uppdatering.", "appsUpdated": "Appar Uppdaterade", + "appsNotUpdated": "Misslyckades med att uppdatera applikationer", "appsUpdatedNotifDescription": "Meddelar användaren att uppdateringar av en eller flera appar har tillämpats i bakgrunden", "xWasUpdatedToY": "{} uppdaterades till {}.", + "xWasNotUpdatedToY": "Det gick inte att uppdatera {} till {}.", "errorCheckingUpdates": "Fel vid uppdateringskoll", "errorCheckingUpdatesNotifDescription": "En aviserings som visar när bakgrundsuppdateringarkollar misslyckas", "appsRemoved": "Appar borttagna", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Stöd fasta APK-webbadresser", "selectX": "Välj {}", "parallelDownloads": "Tillåt parallella nedladdningar", - "installMethod": "Installationsmetod", - "normal": "Vanligt", - "root": "Rot", + "useShizuku": "Använd Shizuku eller Sui för att installera", "shizukuBinderNotFound": "Shizuku is not running", + "shizukuOld": "Gammal Shizuku-version (<11) - uppdatera den", + "shizukuOldAndroidWithADB": "Shizuku körs på Android < 8.1 med ADB - uppdatera Android eller använd Sui istället", + "shizukuPretendToBeGooglePlay": "Ange Google Play som installationskälla (om Shizuku används)", "useSystemFont": "Använd systemteckensnittet", - "systemFontError": "Fel vid laddning av systemteckensnittet: {}", "useVersionCodeAsOSVersion": "Använd appversionskoden som OS-upptäckt version", "requestHeader": "Rubrik för begäran", "useLatestAssetDateAsReleaseDate": "Använd senaste tillgångsuppladdning som releasedatum", @@ -352,6 +357,10 @@ "one": "{} och 1 till app uppdaterades.", "other": "{} och {} appar till uppdaterades." }, + "xAndNMoreUpdatesFailed": { + "one": "Misslyckades med att uppdatera {} och ytterligare 1 app.", + "other": "Det gick inte att uppdatera {} och {} fler appar." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} och 1 till app kan ha uppdaterats.", "other": "{} och {} appar till kan ha uppdaterats." diff --git a/assets/translations/tr.json b/assets/translations/tr.json index db1df8e4..2c8ff70f 100644 --- a/assets/translations/tr.json +++ b/assets/translations/tr.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Gerekli)", "dropdownNoOptsError": "HATA: DİPLOMADA EN AZ BİR SEÇENEK OLMALI", "colour": "Renk", + "standard": "Standart", + "custom": "Özel", + "useMaterialYou": "Sizin Malzemenizi Kullanın", "githubStarredRepos": "GitHub'a Yıldızlı Depolar", "uname": "Kullanıcı Adı", "wrongArgNum": "Hatalı argüman sayısı sağlandı", @@ -143,8 +146,10 @@ "noNewUpdates": "Yeni güncelleme yok.", "xHasAnUpdate": "{} güncelleme alıyor.", "appsUpdated": "Uygulamalar Güncellendi", + "appsNotUpdated": "Uygulamalar güncellenemedi", "appsUpdatedNotifDescription": "Kullanıcıya bir veya daha fazla uygulamanın arka planda güncellendiğine dair bilgi verir", "xWasUpdatedToY": "{} şu sürüme güncellendi: {}.", + "xWasNotUpdatedToY": "{} öğesi {} olarak güncellenemedi.", "errorCheckingUpdates": "Güncellemeler Kontrol Edilirken Hata Oluştu", "errorCheckingUpdatesNotifDescription": "Arka planda güncelleme kontrolü sırasında hata oluştuğunda görünen bir bildirim", "appsRemoved": "Uygulamalar Kaldırıldı", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Sabit APK URL'lerini destekleyin", "selectX": "Seçme {}", "parallelDownloads": "Paralel indirmelere izin ver", - "installMethod": "Kurulum yöntemi", - "normal": "Normal", - "root": "Kök", + "useShizuku": "Yüklemek için Shizuku veya Sui'yi kullanın", "shizukuBinderNotFound": "Shizuku is not running", + "shizukuOld": "Eski Shizuku sürümü (<11) - güncelleyin", + "shizukuOldAndroidWithADB": "Shizuku ADB ile Android < 8.1 üzerinde çalışıyor - Android'i güncelleyin veya bunun yerine Sui kullanın", + "shizukuPretendToBeGooglePlay": "Google Play'i yükleme kaynağı olarak ayarlayın (Shizuku kullanılıyorsa)", "useSystemFont": "Sistem yazı tipini kullan", - "systemFontError": "Sistem yazı tipi yüklenirken hata oluştu: {}", "useVersionCodeAsOSVersion": "Uygulama versionCode'unu işletim sistemi tarafından algılanan sürüm olarak kullan", "requestHeader": "Başlık talep et", "useLatestAssetDateAsReleaseDate": "Yayın tarihi olarak en son öğe yüklemesini kullan", @@ -352,6 +357,10 @@ "one": "{} ve 1 diğer uygulama güncellendi.", "other": "{} ve {} daha fazla uygulama güncellendi." }, + "xAndNMoreUpdatesFailed": { + "one": "{} ve 1 uygulama daha güncellenemedi.", + "other": "{} ve {} daha fazla uygulama güncellenemedi." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} ve 1 diğer uygulama muhtemelen güncellendi.", "other": "{} ve {} daha fazla uygulama muhtemelen güncellendi." diff --git a/assets/translations/uk.json b/assets/translations/uk.json index b2de35cf..a087232a 100644 --- a/assets/translations/uk.json +++ b/assets/translations/uk.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Обов'язково)", "dropdownNoOptsError": "ПОМИЛКА: В ВИПАДАЮЧОМУ СПИСКУ МАЄ БУТИ ХОЧА Б ОДИН ЕЛЕМЕНТ", "colour": "Колір", + "standard": "Стандартний", + "custom": "Нестандартний", + "useMaterialYou": "Використовуйте матеріал, який ви", "githubStarredRepos": "Відзначені репозиторії GitHub", "uname": "Ім'я користувача", "wrongArgNum": "Надано неправильну кількість аргументів", @@ -143,8 +146,10 @@ "noNewUpdates": "Немає нових оновлень.", "xHasAnUpdate": "{} має оновлення.", "appsUpdated": "Застосунки оновлено", + "appsNotUpdated": "Не вдалося оновити програми", "appsUpdatedNotifDescription": "Повідомляє користувача, що оновлення одного чи декількох застосунків було застосовано в фоновому режимі", "xWasUpdatedToY": "{} було оновлено до {}.", + "xWasNotUpdatedToY": "Не вдалося оновити {} на {}.", "errorCheckingUpdates": "Помилка перевірки оновлень", "errorCheckingUpdatesNotifDescription": "Повідомлення, яке з'являється, коли перевірка оновлень в фоновому режимі завершується невдачею", "appsRemoved": "Застосунки видалено", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Підтримка фіксованих посилань на APK", "selectX": "Вибрати {}", "parallelDownloads": "Дозволити паралельні завантаження", - "installMethod": "Метод встановлення", - "normal": "Звичайний", - "root": "Root", + "useShizuku": "Використовуйте Shizuku або Sui для встановлення", "shizukuBinderNotFound": "Сумісний сервіс Shizuku не було знайдено", + "shizukuOld": "Стара версія Shizuku (<11) - оновіть її", + "shizukuOldAndroidWithADB": "Shizuku працює на Android < 8.1 з ADB - оновіть Android або використовуйте Sui замість нього", + "shizukuPretendToBeGooglePlay": "Виберіть Google Play як джерело встановлення (якщо використовується Shizuku)", "useSystemFont": "Використовувати системний шрифт", - "systemFontError": "Помилка завантаження системного шрифту: {}", "useVersionCodeAsOSVersion": "Використовувати код версії застосунку як версію, визначену операційною системою", "requestHeader": "Заголовок запиту", "useLatestAssetDateAsReleaseDate": "Використовувати останню дату завантаження ресурсу як дату випуску", @@ -352,6 +357,10 @@ "one": "{} та ще 1 застосунок було оновлено.", "other": "{} та ще {} застосунків було оновлено." }, + "xAndNMoreUpdatesFailed": { + "one": "Не вдалося оновити {} та ще 1 програму.", + "other": "Не вдалося оновити {} і {} та інші програми." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} та ще 1 застосунок можливо було оновлено.", "other": "{} та ще {} застосунків можливо було оновлено." diff --git a/assets/translations/vi.json b/assets/translations/vi.json index 4259cf00..571badb8 100644 --- a/assets/translations/vi.json +++ b/assets/translations/vi.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(Yêu cầu)", "dropdownNoOptsError": "LỖI: TẢI XUỐNG PHẢI CÓ ÍT NHẤT MỘT LỰA CHỌN", "colour": "Màu sắc", + "standard": "Standard", + "custom": "Custom", + "useMaterialYou": "Use Material You", "githubStarredRepos": "Kho lưu trữ có gắn dấu sao GitHub", "uname": "Tên người dùng", "wrongArgNum": "Số lượng đối số được cung cấp sai", @@ -143,8 +146,10 @@ "noNewUpdates": "Không có bản cập nhật mới.", "xHasAnUpdate": "{} có bản cập nhật.", "appsUpdated": "Ứng dụng đã cập nhật ", + "appsNotUpdated": "Failed to update applications", "appsUpdatedNotifDescription": "Thông báo cho người dùng rằng các bản cập nhật cho một hoặc nhiều Ứng dụng đã được áp dụng trong nền", "xWasUpdatedToY": "{} đã được cập nhật thành {}.", + "xWasNotUpdatedToY": "Failed to update {} to {}.", "errorCheckingUpdates": "Lỗi kiểm tra bản cập nhật", "errorCheckingUpdatesNotifDescription": "Thông báo hiển thị khi kiểm tra cập nhật nền không thành công", "appsRemoved": "Ứng dụng đã loại bỏ", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "Hỗ trợ URL APK cố định", "selectX": "Lựa chọn {}", "parallelDownloads": "Cho phép tải đa luồng", - "installMethod": "Phương thức cài đặt", - "normal": "Mặc định", - "root": "Root", + "useShizuku": "Use Shizuku or Sui to install", "shizukuBinderNotFound": "Shizuku chưa khởi động", + "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 (if Shizuku is used)", "useSystemFont": "Sử dụng phông chữ hệ thống", - "systemFontError": "Lỗi tải phông chữ hệ thống: {}", "useVersionCodeAsOSVersion": "Sử dụng Mã phiên bản ứng dụng làm phiên bản do hệ điều hành phát hiện", "requestHeader": "Tiêu đề yêu cầu", "useLatestAssetDateAsReleaseDate": "Sử dụng nội dung tải lên mới nhất làm ngày phát hành", @@ -352,6 +357,10 @@ "one": "{} và 1 ứng dụng khác đã được cập nhật.", "other": "{} và {} ứng dụng khác đã được cập nhật." }, + "xAndNMoreUpdatesFailed": { + "one": "Failed to update {} and 1 more app.", + "other": "Failed to update {} and {} more apps." + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} và 1 ứng dụng khác có thể đã được cập nhật.", "other": "{} và {} ứng dụng khác có thể đã được cập nhật." diff --git a/assets/translations/zh.json b/assets/translations/zh.json index 8ee9682e..e4f4b03a 100644 --- a/assets/translations/zh.json +++ b/assets/translations/zh.json @@ -22,6 +22,9 @@ "requiredInBrackets": "(必填)", "dropdownNoOptsError": "错误:下拉菜单必须包含至少一个选项", "colour": "配色", + "standard": "标准", + "custom": "定制", + "useMaterialYou": "使用您的材料", "githubStarredRepos": "已星标的 GitHub 仓库", "uname": "用户名", "wrongArgNum": "参数数量错误", @@ -143,8 +146,10 @@ "noNewUpdates": "全部应用已是最新。", "xHasAnUpdate": "“{}”可以更新了。", "appsUpdated": "应用已更新", + "appsNotUpdated": "更新应用程序失败", "appsUpdatedNotifDescription": "当应用在后台安装更新时发送通知", "xWasUpdatedToY": "“{}”已更新至 {}。", + "xWasNotUpdatedToY": "未能将 {} 更新为 {}。", "errorCheckingUpdates": "检查更新出错", "errorCheckingUpdatesNotifDescription": "当后台检查更新失败时显示的通知", "appsRemoved": "应用已删除", @@ -282,12 +287,12 @@ "supportFixedAPKURL": "支持固定的 APK 文件链接", "selectX": "选择{}", "parallelDownloads": "启用并行下载", - "installMethod": "安装方式", - "normal": "常规", - "root": "root", + "useShizuku": "使用 Shizuku 或 Sui 安装", "shizukuBinderNotFound": "未发现兼容的 Shizuku 服务", + "shizukuOld": "旧的 Shizuku 版本 (<11) - 更新它", + "shizukuOldAndroidWithADB": "使用 ADB 在 Android < 8.1 上运行 Shizuku - 更新 Android 或使用 Sui 代替", + "shizukuPretendToBeGooglePlay": "将 Google Play 设置为安装源(如果使用 Shizuku)", "useSystemFont": "使用系统字体", - "systemFontError": "加载系统字体出错:{}", "useVersionCodeAsOSVersion": "使用内部版本号代替应用定义的版本号", "requestHeader": "请求标头", "useLatestAssetDateAsReleaseDate": "使用最近文件上传时间作为发行日期", @@ -352,6 +357,10 @@ "one": "{} 和另外 1 个应用已更新。", "other": "“{}”和另外 {} 个应用已更新。" }, + "xAndNMoreUpdatesFailed": { + "one": "更新 {} 和另外 1 个应用程序失败。", + "other": "未能更新 {} 和 {} 更多应用程序。" + }, "xAndNMoreUpdatesPossiblyInstalled": { "one": "{} 和另外 1 个应用已尝试更新。", "other": "“{}”和另外 {} 个应用已尝试更新。" From b293b1e9ff8fa2c0de4ddb5ecedb5414e0b94acf Mon Sep 17 00:00:00 2001 From: Imran Remtulla Date: Sat, 20 Apr 2024 19:44:32 -0400 Subject: [PATCH 16/16] Update Flutter, packages, increment version --- .flutter | 2 +- pubspec.lock | 16 ++++++++-------- pubspec.yaml | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.flutter b/.flutter index 300451ad..54e66469 160000 --- a/.flutter +++ b/.flutter @@ -1 +1 @@ -Subproject commit 300451adae589accbece3490f4396f10bdf15e6e +Subproject commit 54e66469a933b60ddf175f858f82eaeb97e48c8d diff --git a/pubspec.lock b/pubspec.lock index fce098b1..86884926 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -183,10 +183,10 @@ packages: dependency: "direct main" description: name: cupertino_icons - sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 url: "https://pub.dev" source: hosted - version: "1.0.6" + version: "1.0.8" dbus: dependency: transitive description: @@ -271,10 +271,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: d1d0ac3966b36dc3e66eeefb40280c17feb87fa2099c6e22e6a1fc959327bd03 + sha256: b6283d7387310ad83bc4f3bc245b75d223a032ae6eba275afcd585de2b9a1476 url: "https://pub.dev" source: hosted - version: "8.0.0+1" + version: "8.0.1" fixnum: dependency: transitive description: @@ -369,10 +369,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: "04c4722cc36ec5af38acc38ece70d22d3c2123c61305d555750a091517bbe504" + sha256: "9921f9deda326f8a885e202b1e35237eadfc1345239a0f6f0f1ff287e047547f" url: "https://pub.dev" source: hosted - version: "0.6.23" + version: "0.7.1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -699,10 +699,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333" + sha256: "79fbafed02cfdbe85ef3fd06c7f4bc2cbcba0177e61b765264853d4253b21744" url: "https://pub.dev" source: hosted - version: "3.8.0" + version: "3.9.0" provider: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 71ff1102..d4ea3a2e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,7 +17,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.1.4+2261 +version: 1.1.5+2262 environment: sdk: '>=3.0.0 <4.0.0' @@ -60,7 +60,7 @@ dependencies: sqflite: ^2.2.0+3 easy_localization: ^3.0.1 android_intent_plus: ^5.0.1 - flutter_markdown: ^0.6.14 + flutter_markdown: ^0.7.1 flutter_archive: ^6.0.0 hsluv: ^1.1.3 connectivity_plus: ^6.0.1