import 'dart:convert'; import 'dart:io'; import 'package:device_apps/device_apps.dart'; import 'package:github/github.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); } Future downloadIntegrations(String extension) async { return await _githubAPI.latestReleaseFile( extension, ghOrg, integrationsRepo, ); } Future downloadManager(String extension) async { return await _githubAPI.latestReleaseFile(extension, ghOrg, managerRepo); } Future getLatestPatchesVersion() async { return await _githubAPI.latestReleaseVersion(ghOrg, patchesRepo); } Future getLatestManagerVersion() async { return await _githubAPI.latestReleaseVersion(ghOrg, managerRepo); } Future getCurrentManagerVersion() async { PackageInfo packageInfo = await PackageInfo.fromPlatform(); return packageInfo.version; } bool getUseDynamicTheme() { return _prefs.getBool('useDynamicTheme') ?? false; } Future setUseDynamicTheme(bool value) async { await _prefs.setBool('useDynamicTheme', value); } bool getUseDarkTheme() { return _prefs.getBool('useDarkTheme') ?? false; } Future setUseDarkTheme(bool value) async { await _prefs.setBool('useDarkTheme', value); } bool? isRooted() { return _prefs.getBool('isRooted'); } Future setIsRooted(bool value) async { await _prefs.setBool('isRooted', value); } List getPatchedApps() { List apps = _prefs.getStringList('patchedApps') ?? []; return apps .map((a) => PatchedApplication.fromJson(json.decode(a))) .toList(); } Future setPatchedApps(List patchedApps) async { if (patchedApps.length > 1) { patchedApps.sort((a, b) => a.name.compareTo(b.name)); } await _prefs.setStringList('patchedApps', patchedApps.map((a) => json.encode(a.toJson())).toList()); } Future savePatchedApp(PatchedApplication app) async { List patchedApps = getPatchedApps(); patchedApps.removeWhere((a) => a.packageName == app.packageName); ApplicationWithIcon? installed = await DeviceApps.getApp(app.packageName, true) as ApplicationWithIcon?; if (installed != null) { app.name = installed.appName; app.version = installed.versionName!; app.icon = installed.icon; } patchedApps.add(app); await setPatchedApps(patchedApps); } Future deletePatchedApp(PatchedApplication app) async { List patchedApps = getPatchedApps(); patchedApps.removeWhere((a) => a.packageName == app.packageName); await setPatchedApps(patchedApps); } Future reAssessSavedApps() async { bool isRooted = this.isRooted() ?? false; List patchedApps = getPatchedApps(); List toRemove = []; for (PatchedApplication app in patchedApps) { bool isRemove = await isAppUninstalled(app, isRooted); if (isRemove) { toRemove.add(app); } else { app.hasUpdates = await hasAppUpdates(app.packageName, app.patchDate); app.changelog = await getAppChangelog(app.packageName, app.patchDate); if (!app.hasUpdates) { String? currentInstalledVersion = (await DeviceApps.getApp(app.packageName))?.versionName; if (currentInstalledVersion != null) { String currentSavedVersion = app.version; int currentInstalledVersionInt = int.parse( currentInstalledVersion.replaceAll(RegExp('[^0-9]'), '')); int currentSavedVersionInt = int.parse(currentSavedVersion.replaceAll(RegExp('[^0-9]'), '')); if (currentInstalledVersionInt > currentSavedVersionInt) { app.hasUpdates = true; } } } } } patchedApps.removeWhere((a) => toRemove.contains(a)); await setPatchedApps(patchedApps); } Future isAppUninstalled(PatchedApplication app, bool isRooted) async { bool existsRoot = false; if (isRooted) { existsRoot = await _rootAPI.isAppInstalled(app.packageName); } bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName); return !existsRoot && !existsNonRoot; } Future hasAppUpdates(String packageName, DateTime patchDate) async { List commits = await _githubAPI.getCommits(packageName, ghOrg, patchesRepo); return commits.any((c) => c.commit != null && c.commit!.author != null && c.commit!.author!.date != null && c.commit!.author!.date!.isAfter(patchDate)); } Future> getAppChangelog( String packageName, DateTime patchDate, ) async { List commits = await _githubAPI.getCommits(packageName, ghOrg, patchesRepo); List newCommits = commits .where((c) => c.commit != null && c.commit!.author != null && c.commit!.author!.date != null && c.commit!.author!.date!.isAfter(patchDate) && c.commit!.message != null) .map((c) => c.commit!.message!) .toList(); if (newCommits.isEmpty) { newCommits = commits .where((c) => c.commit != null && c.commit!.message != null) .take(3) .map((c) => c.commit!.message!) .toList(); } return newCommits; } }