fix: improve installed apps check

This commit is contained in:
Alberto Ponces 2022-08-25 00:51:47 +01:00
parent 56e85ca7cf
commit 9f82b9b275
10 changed files with 145 additions and 35 deletions

View File

@ -396,7 +396,7 @@ class MainActivity : FlutterActivity() {
"update", "update",
mapOf( mapOf(
"progress" to 1.0, "progress" to 1.0,
"header" to "Finished", "header" to "Finished!",
"log" to "Finished" "log" to "Finished"
) )
) )

View File

@ -1,5 +1,6 @@
import 'package:revanced_manager/main.dart'; import 'package:revanced_manager/main.dart';
import 'package:revanced_manager/main_viewmodel.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/services/patcher_api.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_view.dart'; import 'package:revanced_manager/ui/views/app_selector/app_selector_view.dart';
import 'package:revanced_manager/ui/views/contributors/contributors_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, classType: ThemeService,
resolveUsing: ThemeService.getInstance, resolveUsing: ThemeService.getInstance,
), ),
LazySingleton(classType: ManagerAPI),
LazySingleton(classType: PatcherAPI), LazySingleton(classType: PatcherAPI),
], ],
) )

View File

@ -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.locator.dart';
import 'package:revanced_manager/app/app.router.dart'; import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/main_viewmodel.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/theme.dart';
import 'package:revanced_manager/ui/views/home/home_view.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/patcher/patcher_view.dart';
import 'package:revanced_manager/ui/views/root_checker/root_checker_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:revanced_manager/ui/views/settings/settings_view.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_services/stacked_services.dart';
import 'package:stacked_themes/stacked_themes.dart'; import 'package:stacked_themes/stacked_themes.dart';
Future main() async { Future main() async {
await ThemeManager.initialise(); await ThemeManager.initialise();
setupLocator(); await setupLocator();
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp()); runApp(const MyApp());
} }
@ -68,8 +68,8 @@ class MyApp extends StatelessWidget {
} }
Future<Widget> _init() async { Future<Widget> _init() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); await locator<ManagerAPI>().initialize();
bool? isRooted = prefs.getBool('isRooted'); bool? isRooted = locator<ManagerAPI>().isRooted();
if (isRooted != null) { if (isRooted != null) {
return const Navigation(); return const Navigation();
} }

View File

@ -1,10 +1,23 @@
import 'dart:convert';
import 'dart:io'; 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:package_info_plus/package_info_plus.dart';
import 'package:revanced_manager/constants.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/github_api.dart';
import 'package:revanced_manager/services/root_api.dart';
import 'package:shared_preferences/shared_preferences.dart';
@lazySingleton
class ManagerAPI { class ManagerAPI {
final GithubAPI _githubAPI = GithubAPI(); final GithubAPI _githubAPI = GithubAPI();
final RootAPI _rootAPI = RootAPI();
late SharedPreferences _prefs;
Future<void> initialize() async {
_prefs = await SharedPreferences.getInstance();
}
Future<File?> downloadPatches(String extension) async { Future<File?> downloadPatches(String extension) async {
return await _githubAPI.latestReleaseFile(extension, ghOrg, patchesRepo); return await _githubAPI.latestReleaseFile(extension, ghOrg, patchesRepo);
@ -35,6 +48,89 @@ class ManagerAPI {
return packageInfo.version; return packageInfo.version;
} }
bool? isRooted() {
return _prefs.getBool('isRooted');
}
List<PatchedApplication> getPatchedApps() {
List<String> apps = _prefs.getStringList('patchedApps') ?? [];
return apps
.map((a) => PatchedApplication.fromJson(json.decode(a)))
.toList();
}
void setPatchedApps(List<PatchedApplication> patchedApps) {
_prefs.setStringList('patchedApps',
patchedApps.map((a) => json.encode(a.toJson())).toList());
}
void savePatchedApp(PatchedApplication app) {
List<PatchedApplication> 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<void> reAssessSavedApps() async {
List<PatchedApplication> 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<String> 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<bool> hasAppUpdates(String packageName) async { Future<bool> hasAppUpdates(String packageName) async {
// TODO: get status based on last update time on the folder of this app? // TODO: get status based on last update time on the folder of this app?
return false; return false;

View File

@ -4,6 +4,7 @@ import 'package:device_apps/device_apps.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:injectable/injectable.dart'; import 'package:injectable/injectable.dart';
import 'package:path_provider/path_provider.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/patch.dart';
import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/manager_api.dart';
@ -16,7 +17,7 @@ class PatcherAPI {
static const patcherChannel = MethodChannel( static const patcherChannel = MethodChannel(
'app.revanced.manager/patcher', 'app.revanced.manager/patcher',
); );
final ManagerAPI _managerAPI = ManagerAPI(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
final RootAPI _rootAPI = RootAPI(); final RootAPI _rootAPI = RootAPI();
Directory? _tmpDir; Directory? _tmpDir;
Directory? _workDir; Directory? _workDir;
@ -248,7 +249,7 @@ class PatcherAPI {
Future<bool> checkOldPatch(PatchedApplication patchedApp) async { Future<bool> checkOldPatch(PatchedApplication patchedApp) async {
if (patchedApp.isRooted) { if (patchedApp.isRooted) {
return await _rootAPI.checkApp(patchedApp.packageName); return await _rootAPI.isAppInstalled(patchedApp.packageName);
} }
return false; return false;
} }

View File

@ -5,15 +5,39 @@ class RootAPI {
final String _postFsDataDirPath = "/data/adb/post-fs-data.d"; final String _postFsDataDirPath = "/data/adb/post-fs-data.d";
final String _serviceDDirPath = "/data/adb/service.d"; final String _serviceDDirPath = "/data/adb/service.d";
Future<bool> checkApp(String packageName) async { Future<bool> 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<List<String>> getInstalledApps() async {
try { try {
String? res = await Root.exec( String? res = await Root.exec(
cmd: 'ls -la "$_managerDirPath/$packageName"', cmd: 'ls "$_managerDirPath"',
); );
return res != null && res.isNotEmpty; if (res != null) {
List<String> apps = res.split('\n');
for (String packageName in apps) {
bool isInstalled = await isAppInstalled(packageName);
if (!isInstalled) {
apps.remove(packageName);
}
}
}
} on Exception { } on Exception {
return false; return List.empty();
} }
return List.empty();
} }
Future<void> deleteApp(String packageName, String originalFilePath) async { Future<void> deleteApp(String packageName, String originalFilePath) async {

View File

@ -19,7 +19,7 @@ import 'package:stacked/stacked.dart';
@lazySingleton @lazySingleton
class HomeViewModel extends BaseViewModel { class HomeViewModel extends BaseViewModel {
final ManagerAPI _managerAPI = ManagerAPI(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
final PatcherAPI _patcherAPI = locator<PatcherAPI>(); final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin(); FlutterLocalNotificationsPlugin();
@ -49,11 +49,10 @@ class HomeViewModel extends BaseViewModel {
} }
Future<List<PatchedApplication>> getPatchedApps(bool isUpdatable) async { Future<List<PatchedApplication>> getPatchedApps(bool isUpdatable) async {
await _managerAPI.reAssessSavedApps();
List<PatchedApplication> list = []; List<PatchedApplication> list = [];
SharedPreferences prefs = await SharedPreferences.getInstance(); List<PatchedApplication> patchedApps = _managerAPI.getPatchedApps();
List<String> patchedApps = prefs.getStringList('patchedApps') ?? []; for (PatchedApplication app in patchedApps) {
for (String str in patchedApps) {
PatchedApplication app = PatchedApplication.fromJson(json.decode(str));
bool hasUpdates = await _managerAPI.hasAppUpdates(app.packageName); bool hasUpdates = await _managerAPI.hasAppUpdates(app.packageName);
if (hasUpdates == isUpdatable) { if (hasUpdates == isUpdatable) {
list.add(app); list.add(app);

View File

@ -1,4 +1,3 @@
import 'dart:convert';
import 'package:device_apps/device_apps.dart'; import 'package:device_apps/device_apps.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/services.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/app/app.locator.dart';
import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.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/services/patcher_api.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked/stacked.dart'; import 'package:stacked/stacked.dart';
class InstallerViewModel extends BaseViewModel { class InstallerViewModel extends BaseViewModel {
final ScrollController scrollController = ScrollController(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
final PatcherAPI _patcherAPI = locator<PatcherAPI>(); final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final PatchedApplication? _app = locator<PatcherViewModel>().selectedApp; final PatchedApplication? _app = locator<PatcherViewModel>().selectedApp;
final List<Patch> _patches = locator<PatcherViewModel>().selectedPatches; final List<Patch> _patches = locator<PatcherViewModel>().selectedPatches;
static const _installerChannel = MethodChannel( static const _installerChannel = MethodChannel(
'app.revanced.manager/installer', 'app.revanced.manager/installer',
); );
final ScrollController scrollController = ScrollController();
double? progress = 0.0; double? progress = 0.0;
String logs = ''; String logs = '';
String headerLogs = ''; String headerLogs = '';
@ -148,10 +148,10 @@ class InstallerViewModel extends BaseViewModel {
); );
isInstalled = await _patcherAPI.installPatchedFile(_app!); isInstalled = await _patcherAPI.installPatchedFile(_app!);
if (isInstalled) { if (isInstalled) {
update(1.0, 'Installed...', 'Installed'); update(1.0, 'Installed!', 'Installed');
_app!.patchDate = DateTime.now(); _app!.patchDate = DateTime.now();
_app!.appliedPatches.addAll(_patches.map((p) => p.name).toList()); _app!.appliedPatches.addAll(_patches.map((p) => p.name).toList());
await saveApp(); _managerAPI.savePatchedApp(_app!);
} else { } else {
update(1.0, 'Aborting...', 'An error occurred! Aborting'); update(1.0, 'Aborting...', 'An error occurred! Aborting');
} }
@ -176,16 +176,4 @@ class InstallerViewModel extends BaseViewModel {
DeviceApps.openApp(_app!.packageName); DeviceApps.openApp(_app!.packageName);
} }
} }
Future<void> saveApp() async {
if (_app != null) {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> 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);
}
}
} }

View File

@ -10,7 +10,7 @@ class AvailableUpdatesCard extends StatelessWidget {
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final ManagerAPI _managerAPI = ManagerAPI(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -11,7 +11,7 @@ class InstalledAppsCard extends StatelessWidget {
Key? key, Key? key,
}) : super(key: key); }) : super(key: key);
final ManagerAPI _managerAPI = ManagerAPI(); final ManagerAPI _managerAPI = locator<ManagerAPI>();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {