diff --git a/android/app/src/main/kotlin/app/revanced/manager/MainActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/MainActivity.kt index 74dddf2f..db5594d5 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/MainActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/MainActivity.kt @@ -396,7 +396,7 @@ class MainActivity : FlutterActivity() { "update", mapOf( "progress" to 1.0, - "header" to "Finished", + "header" to "Finished!", "log" to "Finished" ) ) diff --git a/lib/app/app.dart b/lib/app/app.dart index fbc19f6c..69d38c95 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,5 +1,6 @@ import 'package:revanced_manager/main.dart'; import 'package:revanced_manager/main_viewmodel.dart'; +import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/ui/views/app_selector/app_selector_view.dart'; import 'package:revanced_manager/ui/views/contributors/contributors_view.dart'; @@ -34,6 +35,7 @@ import 'package:stacked_themes/stacked_themes.dart'; classType: ThemeService, resolveUsing: ThemeService.getInstance, ), + LazySingleton(classType: ManagerAPI), LazySingleton(classType: PatcherAPI), ], ) diff --git a/lib/main.dart b/lib/main.dart index 536c3d8b..a70f56de 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,19 +5,19 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.router.dart'; import 'package:revanced_manager/main_viewmodel.dart'; +import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/theme.dart'; import 'package:revanced_manager/ui/views/home/home_view.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_view.dart'; import 'package:revanced_manager/ui/views/root_checker/root_checker_view.dart'; import 'package:revanced_manager/ui/views/settings/settings_view.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_themes/stacked_themes.dart'; Future main() async { await ThemeManager.initialise(); - setupLocator(); + await setupLocator(); WidgetsFlutterBinding.ensureInitialized(); runApp(const MyApp()); } @@ -68,8 +68,8 @@ class MyApp extends StatelessWidget { } Future _init() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - bool? isRooted = prefs.getBool('isRooted'); + await locator().initialize(); + bool? isRooted = locator().isRooted(); if (isRooted != null) { return const Navigation(); } diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index da0b72f2..c301a2f2 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -1,10 +1,23 @@ +import 'dart:convert'; import 'dart:io'; +import 'package:device_apps/device_apps.dart'; +import 'package:injectable/injectable.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:revanced_manager/constants.dart'; +import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/github_api.dart'; +import 'package:revanced_manager/services/root_api.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +@lazySingleton class ManagerAPI { final GithubAPI _githubAPI = GithubAPI(); + final RootAPI _rootAPI = RootAPI(); + late SharedPreferences _prefs; + + Future initialize() async { + _prefs = await SharedPreferences.getInstance(); + } Future downloadPatches(String extension) async { return await _githubAPI.latestReleaseFile(extension, ghOrg, patchesRepo); @@ -35,6 +48,89 @@ class ManagerAPI { return packageInfo.version; } + bool? isRooted() { + return _prefs.getBool('isRooted'); + } + + List getPatchedApps() { + List apps = _prefs.getStringList('patchedApps') ?? []; + return apps + .map((a) => PatchedApplication.fromJson(json.decode(a))) + .toList(); + } + + void setPatchedApps(List patchedApps) { + _prefs.setStringList('patchedApps', + patchedApps.map((a) => json.encode(a.toJson())).toList()); + } + + void savePatchedApp(PatchedApplication app) { + List patchedApps = getPatchedApps(); + patchedApps.removeWhere((a) => a.packageName == app.packageName); + patchedApps.add(app); + setPatchedApps(patchedApps); + } + + void saveApp( + ApplicationWithIcon application, + bool isRooted, + bool isFromStorage, + ) { + savePatchedApp( + PatchedApplication( + name: application.appName, + packageName: application.packageName, + version: application.versionName!, + apkFilePath: application.apkFilePath, + icon: application.icon, + patchDate: DateTime.now(), + isRooted: isRooted, + isFromStorage: isFromStorage, + appliedPatches: [], + ), + ); + } + + Future reAssessSavedApps() async { + List patchedApps = getPatchedApps(); + bool isRoot = isRooted() ?? false; + for (PatchedApplication app in patchedApps) { + bool existsRoot = false; + if (isRoot) { + existsRoot = await _rootAPI.isAppInstalled(app.packageName); + } + bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName); + if (!existsRoot && !existsNonRoot) { + patchedApps.remove(app); + } else if (existsNonRoot) { + ApplicationWithIcon? application = + await DeviceApps.getApp(app.packageName, true) + as ApplicationWithIcon?; + if (application != null) { + int savedVersionInt = + int.parse(app.version.replaceFirst('v', '').replaceAll('.', '')); + int currentVersionInt = int.parse(application.versionName! + .replaceFirst('v', '') + .replaceAll('.', '')); + if (savedVersionInt < currentVersionInt) { + patchedApps.remove(app); + } + } + } + } + setPatchedApps(patchedApps); + List apps = await _rootAPI.getInstalledApps(); + for (String packageName in apps) { + if (!patchedApps.any((a) => a.packageName == packageName)) { + ApplicationWithIcon? application = + await DeviceApps.getApp(packageName, true) as ApplicationWithIcon?; + if (application != null) { + saveApp(application, true, false); + } + } + } + } + Future hasAppUpdates(String packageName) async { // TODO: get status based on last update time on the folder of this app? return false; diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index b5b1130e..c4c81c10 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -4,6 +4,7 @@ import 'package:device_apps/device_apps.dart'; import 'package:flutter/services.dart'; import 'package:injectable/injectable.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/manager_api.dart'; @@ -16,7 +17,7 @@ class PatcherAPI { static const patcherChannel = MethodChannel( 'app.revanced.manager/patcher', ); - final ManagerAPI _managerAPI = ManagerAPI(); + final ManagerAPI _managerAPI = locator(); final RootAPI _rootAPI = RootAPI(); Directory? _tmpDir; Directory? _workDir; @@ -248,7 +249,7 @@ class PatcherAPI { Future checkOldPatch(PatchedApplication patchedApp) async { if (patchedApp.isRooted) { - return await _rootAPI.checkApp(patchedApp.packageName); + return await _rootAPI.isAppInstalled(patchedApp.packageName); } return false; } diff --git a/lib/services/root_api.dart b/lib/services/root_api.dart index b2fa2ec4..c1926e52 100644 --- a/lib/services/root_api.dart +++ b/lib/services/root_api.dart @@ -5,15 +5,39 @@ class RootAPI { final String _postFsDataDirPath = "/data/adb/post-fs-data.d"; final String _serviceDDirPath = "/data/adb/service.d"; - Future checkApp(String packageName) async { + Future isAppInstalled(String packageName) async { + if (packageName.isNotEmpty) { + String? res = await Root.exec( + cmd: 'ls "$_managerDirPath/$packageName"', + ); + if (res != null && res.isNotEmpty) { + res = await Root.exec( + cmd: 'ls "$_serviceDDirPath/$packageName.sh"', + ); + return res != null && res.isNotEmpty; + } + } + return false; + } + + Future> getInstalledApps() async { try { String? res = await Root.exec( - cmd: 'ls -la "$_managerDirPath/$packageName"', + cmd: 'ls "$_managerDirPath"', ); - return res != null && res.isNotEmpty; + if (res != null) { + List apps = res.split('\n'); + for (String packageName in apps) { + bool isInstalled = await isAppInstalled(packageName); + if (!isInstalled) { + apps.remove(packageName); + } + } + } } on Exception { - return false; + return List.empty(); } + return List.empty(); } Future deleteApp(String packageName, String originalFilePath) async { diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart index 460bae38..5fb86a10 100644 --- a/lib/ui/views/home/home_viewmodel.dart +++ b/lib/ui/views/home/home_viewmodel.dart @@ -19,7 +19,7 @@ import 'package:stacked/stacked.dart'; @lazySingleton class HomeViewModel extends BaseViewModel { - final ManagerAPI _managerAPI = ManagerAPI(); + final ManagerAPI _managerAPI = locator(); final PatcherAPI _patcherAPI = locator(); final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); @@ -49,11 +49,10 @@ class HomeViewModel extends BaseViewModel { } Future> getPatchedApps(bool isUpdatable) async { + await _managerAPI.reAssessSavedApps(); List list = []; - SharedPreferences prefs = await SharedPreferences.getInstance(); - List patchedApps = prefs.getStringList('patchedApps') ?? []; - for (String str in patchedApps) { - PatchedApplication app = PatchedApplication.fromJson(json.decode(str)); + List patchedApps = _managerAPI.getPatchedApps(); + for (PatchedApplication app in patchedApps) { bool hasUpdates = await _managerAPI.hasAppUpdates(app.packageName); if (hasUpdates == isUpdatable) { list.add(app); diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index f7043309..f4fca502 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'package:device_apps/device_apps.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/services.dart'; @@ -7,19 +6,20 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patched_application.dart'; +import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:stacked/stacked.dart'; class InstallerViewModel extends BaseViewModel { - final ScrollController scrollController = ScrollController(); + final ManagerAPI _managerAPI = locator(); final PatcherAPI _patcherAPI = locator(); final PatchedApplication? _app = locator().selectedApp; final List _patches = locator().selectedPatches; static const _installerChannel = MethodChannel( 'app.revanced.manager/installer', ); + final ScrollController scrollController = ScrollController(); double? progress = 0.0; String logs = ''; String headerLogs = ''; @@ -148,10 +148,10 @@ class InstallerViewModel extends BaseViewModel { ); isInstalled = await _patcherAPI.installPatchedFile(_app!); if (isInstalled) { - update(1.0, 'Installed...', 'Installed'); + update(1.0, 'Installed!', 'Installed'); _app!.patchDate = DateTime.now(); _app!.appliedPatches.addAll(_patches.map((p) => p.name).toList()); - await saveApp(); + _managerAPI.savePatchedApp(_app!); } else { update(1.0, 'Aborting...', 'An error occurred! Aborting'); } @@ -176,16 +176,4 @@ class InstallerViewModel extends BaseViewModel { DeviceApps.openApp(_app!.packageName); } } - - Future saveApp() async { - if (_app != null) { - SharedPreferences prefs = await SharedPreferences.getInstance(); - List patchedApps = prefs.getStringList('patchedApps') ?? []; - String appStr = json.encode(_app!.toJson()); - patchedApps.removeWhere( - (a) => json.decode(a)['packageName'] == _app!.packageName); - patchedApps.add(appStr); - prefs.setStringList('patchedApps', patchedApps); - } - } } diff --git a/lib/ui/widgets/homeView/available_updates_card.dart b/lib/ui/widgets/homeView/available_updates_card.dart index 84d205da..a61fb382 100644 --- a/lib/ui/widgets/homeView/available_updates_card.dart +++ b/lib/ui/widgets/homeView/available_updates_card.dart @@ -10,7 +10,7 @@ class AvailableUpdatesCard extends StatelessWidget { Key? key, }) : super(key: key); - final ManagerAPI _managerAPI = ManagerAPI(); + final ManagerAPI _managerAPI = locator(); @override Widget build(BuildContext context) { diff --git a/lib/ui/widgets/homeView/installed_apps_card.dart b/lib/ui/widgets/homeView/installed_apps_card.dart index 197a60b7..bb4078dc 100644 --- a/lib/ui/widgets/homeView/installed_apps_card.dart +++ b/lib/ui/widgets/homeView/installed_apps_card.dart @@ -11,7 +11,7 @@ class InstalledAppsCard extends StatelessWidget { Key? key, }) : super(key: key); - final ManagerAPI _managerAPI = ManagerAPI(); + final ManagerAPI _managerAPI = locator(); @override Widget build(BuildContext context) {