diff --git a/android/app/build.gradle b/android/app/build.gradle index a0cc750..c097c6d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -37,12 +37,12 @@ android { ndkVersion flutter.ndkVersion compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } sourceSets { @@ -96,16 +96,11 @@ repositories { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - 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.1.0' - // DO NOT UPDATE Hidden API without updating the Android tools - // and do not update Android tools without updating the whole Flutter - // (also in android/build.gradle) + 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" diff --git a/android/app/src/main/kotlin/dev/imranr/obtainium/DefaultSystemFont.kt b/android/app/src/main/kotlin/dev/imranr/obtainium/DefaultSystemFont.kt new file mode 100644 index 0000000..3a2edc8 --- /dev/null +++ b/android/app/src/main/kotlin/dev/imranr/obtainium/DefaultSystemFont.kt @@ -0,0 +1,33 @@ +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 (_: Exception) { + null + } + } + + private fun parseFontsFileStream(fileStream: FileInputStream): String { + fileStream.use { stream -> + val parser = Xml.newPullParser() + parser.setInput(stream, null) + parser.nextTag() + return parseFonts(parser) + } + } + + private fun parseFonts(parser: XmlPullParser): String { + while (parser.name != "font") { parser.next() } + parser.next() + return "/system/fonts/" + parser.text.trim() + } +} \ 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 b711892..f1c3659 100644 --- a/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt +++ b/android/app/src/main/kotlin/dev/imranr/obtainium/MainActivity.kt @@ -28,7 +28,7 @@ import rikka.shizuku.Shizuku.OnRequestPermissionResultListener import rikka.shizuku.ShizukuBinderWrapper class MainActivity: FlutterActivity() { - private var installersChannel: MethodChannel? = null + private var nativeChannel: MethodChannel? = null private val SHIZUKU_PERMISSION_REQUEST_CODE = (10..200).random() private fun shizukuCheckPermission(result: Result) { @@ -52,7 +52,7 @@ class MainActivity: FlutterActivity() { requestCode: Int, grantResult: Int -> if (requestCode == SHIZUKU_PERMISSION_REQUEST_CODE) { val res = if (grantResult == PackageManager.PERMISSION_GRANTED) 1 else 0 - installersChannel!!.invokeMethod("resPermShizuku", mapOf("res" to res)) + nativeChannel!!.invokeMethod("resPermShizuku", mapOf("res" to res)) } } @@ -151,11 +151,14 @@ class MainActivity: FlutterActivity() { HiddenApiBypass.addHiddenApiExemptions("") } Shizuku.addRequestPermissionResultListener(shizukuRequestPermissionResultListener) - installersChannel = MethodChannel( - flutterEngine.dartExecutor.binaryMessenger, "installers") - installersChannel!!.setMethodCallHandler { + nativeChannel = MethodChannel( + flutterEngine.dartExecutor.binaryMessenger, "native") + nativeChannel!!.setMethodCallHandler { call, result -> - if (call.method == "checkPermissionShizuku") { + if (call.method == "getSystemFont") { + val res = DefaultSystemFont().get() + result.success(res) + } else if (call.method == "checkPermissionShizuku") { shizukuCheckPermission(result) } else if (call.method == "checkPermissionRoot") { rootCheckPermission(result) diff --git a/android/build.gradle b/android/build.gradle index 288d38b..df86a54 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,9 +6,9 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.2.0' + classpath "com.android.tools.build:gradle:7.4.2" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath 'dev.rikka.tools.refine:gradle-plugin:4.1.0' // Do not update! + classpath "dev.rikka.tools.refine:gradle-plugin:4.3.1" } } diff --git a/assets/translations/en.json b/assets/translations/en.json index defd488..1fcaf93 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -285,7 +285,8 @@ "normal": "Normal", "shizuku": "Shizuku", "root": "Root", - "shizukuBinderNotFound": "Shizuku is not running", + "shizukuBinderNotFound": "Сompatible Shizuku service wasn't found", + "tryUseSystemFont": "Try to use a system font", "removeAppQuestion": { "one": "Remove App?", "other": "Remove Apps?" diff --git a/assets/translations/ru.json b/assets/translations/ru.json index fee1a28..f46cf67 100644 --- a/assets/translations/ru.json +++ b/assets/translations/ru.json @@ -285,7 +285,8 @@ "normal": "Нормальный", "shizuku": "Shizuku", "root": "Суперпользователь", - "shizukuBinderNotFound": "Shizuku не запущен", + "shizukuBinderNotFound": "Совместимый сервис Shizuku не найден", + "tryUseSystemFont": "Попытаться использовать системный шрифт", "removeAppQuestion": { "one": "Удалить приложение?", "other": "Удалить приложения?" diff --git a/lib/main.dart b/lib/main.dart index 21be97b..6d25a03 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'; @@ -185,6 +186,16 @@ class _ObtainiumState extends State { } existingUpdateInterval = actualUpdateInterval; } + settingsProvider.addListener(() async { + if (settingsProvider.tryUseSystemFont && + settingsProvider.appFont == "Metropolis") { + bool fontLoaded = await NativeFeatures.tryLoadSystemFont(); + if (fontLoaded) { settingsProvider.appFont = "SystemFont"; } + } else if (!settingsProvider.tryUseSystemFont && + settingsProvider.appFont != "Metropolis") { + settingsProvider.appFont = "Metropolis"; + } + }); } return DynamicColorBuilder( @@ -221,13 +232,13 @@ class _ObtainiumState extends State { colorScheme: settingsProvider.theme == ThemeSettings.dark ? darkColorScheme : lightColorScheme, - fontFamily: 'Metropolis'), + fontFamily: settingsProvider.appFont), darkTheme: ThemeData( useMaterial3: true, colorScheme: settingsProvider.theme == ThemeSettings.light ? lightColorScheme : darkColorScheme, - fontFamily: 'Metropolis'), + fontFamily: settingsProvider.appFont), home: Shortcuts(shortcuts: { LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), }, child: const HomePage())); diff --git a/lib/pages/settings.dart b/lib/pages/settings.dart index e83ddf3..c6c6896 100644 --- a/lib/pages/settings.dart +++ b/lib/pages/settings.dart @@ -351,8 +351,6 @@ class _SettingsPageState extends State { ], ), height16, - installMethodDropdown, - height16, Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -365,6 +363,7 @@ class _SettingsPageState extends State { }) ], ), + installMethodDropdown, height32, Text( tr('sourceSpecific'), @@ -409,6 +408,18 @@ class _SettingsPageState extends State { height16, localeDropdown, height16, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible(child: Text(tr('tryUseSystemFont'))), + Switch( + value: settingsProvider.tryUseSystemFont, + onChanged: (value) { + settingsProvider.tryUseSystemFont = value; + }) + ], + ), + height16, Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/lib/providers/apps_provider.dart b/lib/providers/apps_provider.dart index 97e6118..1db18a5 100644 --- a/lib/providers/apps_provider.dart +++ b/lib/providers/apps_provider.dart @@ -33,7 +33,7 @@ import 'package:http/http.dart'; import 'package:android_intent_plus/android_intent.dart'; import 'package:flutter_archive/flutter_archive.dart'; import 'package:shared_storage/shared_storage.dart' as saf; -import 'installers_provider.dart'; +import 'native_provider.dart'; final pm = AndroidPackageManager(); @@ -523,12 +523,12 @@ class AppsProvider with ChangeNotifier { code = await AndroidPackageInstaller.installApk( apkFilePath: file.file.path); case InstallMethodSettings.shizuku: - code = (await Installers.installWithShizuku( + code = (await NativeFeatures.installWithShizuku( apkFileUri: file.file.uri.toString())) ? 0 : 1; case InstallMethodSettings.root: - code = (await Installers.installWithRoot(apkFilePath: file.file.path)) + code = (await NativeFeatures.installWithRoot(apkFilePath: file.file.path)) ? 0 : 1; } @@ -694,14 +694,14 @@ class AppsProvider with ChangeNotifier { throw ObtainiumError(tr('cancelled')); } case InstallMethodSettings.shizuku: - int code = await Installers.checkPermissionShizuku(); + 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 Installers.checkPermissionRoot())) { + if (!(await NativeFeatures.checkPermissionRoot())) { throw ObtainiumError(tr('cancelled')); } } diff --git a/lib/providers/installers_provider.dart b/lib/providers/native_provider.dart similarity index 61% rename from lib/providers/installers_provider.dart rename to lib/providers/native_provider.dart index c42e1b0..7a154bf 100644 --- a/lib/providers/installers_provider.dart +++ b/lib/providers/native_provider.dart @@ -1,12 +1,25 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/services.dart'; -class Installers { - static const MethodChannel _channel = MethodChannel('installers'); +class NativeFeatures { + static const MethodChannel _channel = MethodChannel('native'); static bool _callbacksApplied = false; static int _resPermShizuku = -2; // not set - static Future waitWhile(bool Function() test, + static Future _readFileBytes(String path) async { + var file = File(path); + var bytes = await file.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() { @@ -20,20 +33,23 @@ class Installers { return completer.future; } - static Future handleCalls(MethodCall call) async { - if (call.method == 'resPermShizuku') { - _resPermShizuku = call.arguments['res']; - } + static Future tryLoadSystemFont() async { + var font = await _channel.invokeMethod('getSystemFont'); + if (font == null) { return false; } + var fontLoader = FontLoader('SystemFont'); + fontLoader.addFont(_readFileBytes(font)); + await fontLoader.load(); + return true; } static Future checkPermissionShizuku() async { if (!_callbacksApplied) { - _channel.setMethodCallHandler(handleCalls); + _channel.setMethodCallHandler(_handleCalls); _callbacksApplied = true; } int res = await _channel.invokeMethod('checkPermissionShizuku'); - if(res == -2) { - await waitWhile(() => _resPermShizuku == -2); + if (res == -2) { + await _waitWhile(() => _resPermShizuku == -2); res = _resPermShizuku; _resPermShizuku = -2; } diff --git a/lib/providers/settings_provider.dart b/lib/providers/settings_provider.dart index 266f244..74164eb 100644 --- a/lib/providers/settings_provider.dart +++ b/lib/providers/settings_provider.dart @@ -51,6 +51,24 @@ class SettingsProvider with ChangeNotifier { notifyListeners(); } + String get appFont { + return prefs?.getString('appFont') ?? 'Metropolis'; + } + + set appFont(String appFont) { + prefs?.setString('appFont', appFont); + notifyListeners(); + } + + bool get tryUseSystemFont { + return prefs?.getBool('tryUseSystemFont') ?? false; + } + + set tryUseSystemFont(bool tryUseSystemFont) { + prefs?.setBool('tryUseSystemFont', tryUseSystemFont); + notifyListeners(); + } + InstallMethodSettings get installMethod { return InstallMethodSettings .values[prefs?.getInt('installMethod') ?? InstallMethodSettings.normal.index];