mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
fix: code refactoring (#5)
This commit is contained in:
parent
6153e1f9e8
commit
389eae1447
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion flutter.compileSdkVersion
|
||||
compileSdkVersion 33
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
@ -45,7 +45,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId "app.revanced.manager"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion flutter.targetSdkVersion
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
}
|
||||
|
@ -9,7 +9,9 @@
|
||||
"updatesSubtitle": "Updates",
|
||||
"patchedSubtitle": "Patched Applications",
|
||||
"updatesAvailable": "Updates Available",
|
||||
"installed": "Installed"
|
||||
"installed": "Installed",
|
||||
"notificationTitle": "ReVanced Manager was updated!",
|
||||
"notificationText": "Tap to open the app"
|
||||
},
|
||||
"applicationItem": {
|
||||
"patchButton": "Patch",
|
||||
@ -50,7 +52,9 @@
|
||||
"installerView": {
|
||||
"widgetTitle": "Installer",
|
||||
"fabInstallButton": "Install",
|
||||
"fabOpenButton": "Open"
|
||||
"fabOpenButton": "Open",
|
||||
"notificationTitle": "ReVanced Manager is patching",
|
||||
"notificationText": "Tap to return to the installer"
|
||||
},
|
||||
"settingsView": {
|
||||
"widgetTitle": "Settings",
|
||||
|
@ -1,16 +1,11 @@
|
||||
import 'package:revanced_manager/main.dart';
|
||||
import 'package:revanced_manager/services/manager_api.dart';
|
||||
import 'package:revanced_manager/services/patcher_api.dart';
|
||||
import 'package:revanced_manager/services/root_api.dart';
|
||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_view.dart';
|
||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/contributors/contributors_view.dart';
|
||||
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/installer/installer_view.dart';
|
||||
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_view.dart';
|
||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.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:stacked/stacked_annotations.dart';
|
||||
@ -30,13 +25,8 @@ import 'package:stacked_themes/stacked_themes.dart';
|
||||
dependencies: [
|
||||
LazySingleton(classType: NavigationService),
|
||||
LazySingleton(classType: PatcherAPI),
|
||||
LazySingleton(classType: ManagerAPI),
|
||||
LazySingleton(classType: RootAPI),
|
||||
LazySingleton(classType: HomeViewModel),
|
||||
LazySingleton(classType: PatcherViewModel),
|
||||
LazySingleton(classType: AppSelectorViewModel),
|
||||
LazySingleton(classType: PatchesSelectorViewModel),
|
||||
LazySingleton(classType: InstallerViewModel),
|
||||
LazySingleton(
|
||||
classType: ThemeService,
|
||||
resolveUsing: ThemeService.getInstance,
|
||||
|
@ -1,32 +1,36 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:github/github.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:revanced_manager/models/patched_application.dart';
|
||||
import 'package:timeago/timeago.dart';
|
||||
|
||||
@lazySingleton
|
||||
class GithubAPI {
|
||||
var github = GitHub();
|
||||
final GitHub _github = GitHub();
|
||||
|
||||
Future<String?> latestRelease(String org, repoName) async {
|
||||
Future<File?> latestRelease(String org, repoName) async {
|
||||
try {
|
||||
var latestRelease = await github.repositories.getLatestRelease(
|
||||
var latestRelease = await _github.repositories.getLatestRelease(
|
||||
RepositorySlug(org, repoName),
|
||||
);
|
||||
return latestRelease.assets
|
||||
String? url = latestRelease.assets
|
||||
?.firstWhere((asset) =>
|
||||
asset.name != null &&
|
||||
(asset.name!.endsWith('.dex') || asset.name!.endsWith('.apk')) &&
|
||||
!asset.name!.contains('-sources') &&
|
||||
!asset.name!.contains('-javadoc'))
|
||||
.browserDownloadUrl;
|
||||
if (url != null) {
|
||||
return await DefaultCacheManager().getSingleFile(url);
|
||||
}
|
||||
} on Exception {
|
||||
return '';
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<String> latestCommitTime(String org, repoName) async {
|
||||
try {
|
||||
var repo = await github.repositories.getRepository(
|
||||
var repo = await _github.repositories.getRepository(
|
||||
RepositorySlug(org, repoName),
|
||||
);
|
||||
return repo.pushedAt != null
|
||||
@ -39,7 +43,7 @@ class GithubAPI {
|
||||
|
||||
Future<List<Contributor>> getContributors(String org, repoName) async {
|
||||
try {
|
||||
var contributors = github.repositories.listContributors(
|
||||
var contributors = _github.repositories.listContributors(
|
||||
RepositorySlug(org, repoName),
|
||||
);
|
||||
return contributors.toList();
|
||||
|
@ -1,52 +1,19 @@
|
||||
import 'dart:io';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:path_provider/path_provider.dart' as p;
|
||||
import 'package:revanced_manager/constants.dart';
|
||||
import 'package:revanced_manager/services/github_api.dart';
|
||||
|
||||
@lazySingleton
|
||||
class ManagerAPI {
|
||||
final Dio dio = Dio();
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
final GithubAPI _githubAPI = GithubAPI();
|
||||
|
||||
Future<String?> getPath() async {
|
||||
final path = await p.getApplicationSupportDirectory();
|
||||
Directory('${path.path}/revanced').createSync();
|
||||
final workDirPath = '${path.path}/revanced';
|
||||
return workDirPath;
|
||||
Future<File?> downloadPatches() async {
|
||||
return await _githubAPI.latestRelease(ghOrg, patchesRepo);
|
||||
}
|
||||
|
||||
Future<File?> downloadAssets(String repo) async {
|
||||
try {
|
||||
final workDir = await getPath();
|
||||
final dlUrl = await githubAPI.latestRelease(ghOrg, repo);
|
||||
final name = dlUrl
|
||||
?.split('/')
|
||||
.lastWhere((element) => element.contains('revanced'));
|
||||
final assetFile = File('$workDir/$name');
|
||||
final response = await dio.get(
|
||||
dlUrl!,
|
||||
options: Options(
|
||||
responseType: ResponseType.bytes,
|
||||
followRedirects: true,
|
||||
receiveTimeout: 0,
|
||||
),
|
||||
);
|
||||
final raf = assetFile.openSync(mode: FileMode.write);
|
||||
raf.writeFromSync(response.data);
|
||||
raf.closeSync();
|
||||
return assetFile;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
Future<File?> downloadIntegrations() async {
|
||||
return await _githubAPI.latestRelease(ghOrg, integrationsRepo);
|
||||
}
|
||||
|
||||
Future<void> downloadPatches() async {
|
||||
await downloadAssets(patchesRepo);
|
||||
}
|
||||
|
||||
Future<void> downloadIntegrations() async {
|
||||
await downloadAssets(integrationsRepo);
|
||||
Future<File?> downloadManager() async {
|
||||
return await _githubAPI.latestRelease(ghOrg, managerRepo);
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,12 @@ import 'dart:io';
|
||||
import 'package:app_installer/app_installer.dart';
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.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/github_api.dart';
|
||||
import 'package:revanced_manager/services/manager_api.dart';
|
||||
import 'package:revanced_manager/services/root_api.dart';
|
||||
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
|
||||
import 'package:revanced_manager/utils/string.dart';
|
||||
import 'package:share_extend/share_extend.dart';
|
||||
|
||||
@ -19,11 +16,8 @@ class PatcherAPI {
|
||||
static const patcherChannel = MethodChannel(
|
||||
'app.revanced.manager/patcher',
|
||||
);
|
||||
static const installerChannel = MethodChannel(
|
||||
'app.revanced.manager/installer',
|
||||
);
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
final RootAPI rootAPI = RootAPI();
|
||||
final ManagerAPI _managerAPI = ManagerAPI();
|
||||
final RootAPI _rootAPI = RootAPI();
|
||||
Directory? _tmpDir;
|
||||
Directory? _workDir;
|
||||
Directory? _cacheDir;
|
||||
@ -33,45 +27,18 @@ class PatcherAPI {
|
||||
File? _patchedFile;
|
||||
File? _outFile;
|
||||
|
||||
Future<dynamic> handlePlatformChannelMethods() async {
|
||||
installerChannel.setMethodCallHandler((call) async {
|
||||
switch (call.method) {
|
||||
case 'updateProgress':
|
||||
if (call.arguments != null) {
|
||||
locator<InstallerViewModel>().updateProgress(call.arguments);
|
||||
}
|
||||
break;
|
||||
case 'updateLog':
|
||||
if (call.arguments != null) {
|
||||
locator<InstallerViewModel>().updateLog(call.arguments);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool?> loadPatches() async {
|
||||
Future<void> loadPatches() async {
|
||||
if (_patchBundleFile == null) {
|
||||
String? dexFileUrl =
|
||||
await githubAPI.latestRelease('revanced', 'revanced-patches');
|
||||
if (dexFileUrl != null && dexFileUrl.isNotEmpty) {
|
||||
try {
|
||||
_patchBundleFile =
|
||||
await DefaultCacheManager().getSingleFile(dexFileUrl);
|
||||
return await patcherChannel.invokeMethod<bool>(
|
||||
'loadPatches',
|
||||
{
|
||||
'pathBundlesPaths': <String>[_patchBundleFile!.absolute.path],
|
||||
},
|
||||
);
|
||||
} on Exception {
|
||||
_patchBundleFile = null;
|
||||
return false;
|
||||
}
|
||||
_patchBundleFile = await _managerAPI.downloadPatches();
|
||||
if (_patchBundleFile != null) {
|
||||
await patcherChannel.invokeMethod<bool>(
|
||||
'loadPatches',
|
||||
{
|
||||
'pathBundlesPaths': <String>[_patchBundleFile!.absolute.path],
|
||||
},
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<List<ApplicationWithIcon>> getFilteredInstalledApps() async {
|
||||
@ -181,20 +148,9 @@ class PatcherAPI {
|
||||
return appliedPatches;
|
||||
}
|
||||
|
||||
Future<File?> downloadIntegrations() async {
|
||||
String? apkFileUrl =
|
||||
await githubAPI.latestRelease('revanced', 'revanced-integrations');
|
||||
if (apkFileUrl != null && apkFileUrl.isNotEmpty) {
|
||||
return await DefaultCacheManager().getSingleFile(apkFileUrl);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> initPatcher(bool mergeIntegrations) async {
|
||||
if (mergeIntegrations) {
|
||||
_integrations = await downloadIntegrations();
|
||||
} else {
|
||||
_integrations = File('');
|
||||
_integrations = await _managerAPI.downloadIntegrations();
|
||||
}
|
||||
_tmpDir = await getTemporaryDirectory();
|
||||
_workDir = _tmpDir!.createTempSync('tmp-');
|
||||
@ -218,7 +174,7 @@ class PatcherAPI {
|
||||
'inputFilePath': _inputFile!.path,
|
||||
'patchedFilePath': _patchedFile!.path,
|
||||
'outFilePath': _outFile!.path,
|
||||
'integrationsPath': _integrations!.path,
|
||||
'integrationsPath': _integrations != null ? _integrations!.path : '',
|
||||
'selectedPatches': selectedPatches.map((p) => p.name).toList(),
|
||||
'cacheDirPath': _cacheDir!.path,
|
||||
'mergeIntegrations': mergeIntegrations,
|
||||
@ -231,7 +187,7 @@ class PatcherAPI {
|
||||
if (_outFile != null) {
|
||||
try {
|
||||
if (patchedApp.isRooted && !patchedApp.isFromStorage) {
|
||||
return rootAPI.installApp(
|
||||
return _rootAPI.installApp(
|
||||
patchedApp.packageName,
|
||||
patchedApp.apkFilePath,
|
||||
_outFile!.path,
|
||||
@ -268,14 +224,14 @@ class PatcherAPI {
|
||||
|
||||
Future<bool> checkOldPatch(PatchedApplication patchedApp) async {
|
||||
if (patchedApp.isRooted) {
|
||||
return await rootAPI.checkApp(patchedApp.packageName);
|
||||
return await _rootAPI.checkApp(patchedApp.packageName);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<void> deleteOldPatch(PatchedApplication patchedApp) async {
|
||||
if (patchedApp.isRooted) {
|
||||
await rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath);
|
||||
await _rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:root/root.dart';
|
||||
|
||||
@lazySingleton
|
||||
class RootAPI {
|
||||
final String managerDirPath = "/data/adb/revanced_manager";
|
||||
final String postFsDataDirPath = "/data/adb/post-fs-data.d";
|
||||
final String serviceDDirPath = "/data/adb/service.d";
|
||||
final String _managerDirPath = "/data/adb/revanced_manager";
|
||||
final String _postFsDataDirPath = "/data/adb/post-fs-data.d";
|
||||
final String _serviceDDirPath = "/data/adb/service.d";
|
||||
|
||||
Future<bool> checkApp(String packageName) async {
|
||||
try {
|
||||
String? res = await Root.exec(
|
||||
cmd: 'ls -la "$managerDirPath/$packageName"',
|
||||
cmd: 'ls -la "$_managerDirPath/$packageName"',
|
||||
);
|
||||
return res != null && res.isNotEmpty;
|
||||
} on Exception {
|
||||
@ -26,13 +24,13 @@ class RootAPI {
|
||||
cmd: 'su -mm -c "umount -l $originalFilePath"',
|
||||
);
|
||||
await Root.exec(
|
||||
cmd: 'rm -rf "$managerDirPath/$packageName"',
|
||||
cmd: 'rm -rf "$_managerDirPath/$packageName"',
|
||||
);
|
||||
await Root.exec(
|
||||
cmd: 'rm -rf "$serviceDDirPath/$packageName.sh"',
|
||||
cmd: 'rm -rf "$_serviceDDirPath/$packageName.sh"',
|
||||
);
|
||||
await Root.exec(
|
||||
cmd: 'rm -rf "$postFsDataDirPath/$packageName.sh"',
|
||||
cmd: 'rm -rf "$_postFsDataDirPath/$packageName.sh"',
|
||||
);
|
||||
}
|
||||
|
||||
@ -43,7 +41,7 @@ class RootAPI {
|
||||
) async {
|
||||
try {
|
||||
await Root.exec(
|
||||
cmd: 'mkdir -p "$managerDirPath/$packageName"',
|
||||
cmd: 'mkdir -p "$_managerDirPath/$packageName"',
|
||||
);
|
||||
installServiceDScript(packageName);
|
||||
installPostFsDataScript(packageName);
|
||||
@ -58,10 +56,10 @@ class RootAPI {
|
||||
Future<void> installServiceDScript(String packageName) async {
|
||||
String content = '#!/system/bin/sh\n'
|
||||
'while [ "\$(getprop sys.boot_completed | tr -d \'"\'"\'\\\\r\'"\'"\')" != "1" ]; do sleep 1; done\n'
|
||||
'base_path=$managerDirPath/$packageName/base.apk\n'
|
||||
'base_path=$_managerDirPath/$packageName/base.apk\n'
|
||||
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
|
||||
'[ ! -z \$stock_path ] && mount -o bind \$base_path \$stock_path';
|
||||
String scriptFilePath = '$serviceDDirPath/$packageName.sh';
|
||||
String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
|
||||
await Root.exec(
|
||||
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
||||
);
|
||||
@ -74,7 +72,7 @@ class RootAPI {
|
||||
String content = '#!/system/bin/sh\n'
|
||||
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
|
||||
'[ ! -z \$stock_path ] && umount -l \$stock_path';
|
||||
String scriptFilePath = '$postFsDataDirPath/$packageName.sh';
|
||||
String scriptFilePath = '$_postFsDataDirPath/$packageName.sh';
|
||||
await Root.exec(
|
||||
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
||||
);
|
||||
@ -84,7 +82,7 @@ class RootAPI {
|
||||
}
|
||||
|
||||
Future<void> installApk(String packageName, String patchedFilePath) async {
|
||||
String newPatchedFilePath = '$managerDirPath/$packageName/base.apk';
|
||||
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||
await Root.exec(
|
||||
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
|
||||
);
|
||||
@ -104,7 +102,7 @@ class RootAPI {
|
||||
String originalFilePath,
|
||||
String patchedFilePath,
|
||||
) async {
|
||||
String newPatchedFilePath = '$managerDirPath/$packageName/base.apk';
|
||||
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||
await Root.exec(
|
||||
cmd: 'am force-stop "$packageName"',
|
||||
);
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/theme.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installed_app_item.dart';
|
||||
import 'package:revanced_manager/ui/widgets/search_bar.dart';
|
||||
@ -15,14 +14,13 @@ class AppSelectorView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _AppSelectorViewState extends State<AppSelectorView> {
|
||||
String query = '';
|
||||
String _query = '';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<AppSelectorViewModel>.reactive(
|
||||
disposeViewModel: false,
|
||||
onModelReady: (model) => model.initialize(),
|
||||
viewModelBuilder: () => locator<AppSelectorViewModel>(),
|
||||
viewModelBuilder: () => AppSelectorViewModel(),
|
||||
builder: (context, model, child) => Scaffold(
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
@ -51,17 +49,17 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
||||
hintTextColor: Theme.of(context).colorScheme.tertiary,
|
||||
onQueryChanged: (searchQuery) {
|
||||
setState(() {
|
||||
query = searchQuery;
|
||||
_query = searchQuery;
|
||||
});
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
query.isEmpty || query.length < 2
|
||||
_query.isEmpty || _query.length < 2
|
||||
? _getAllResults(model)
|
||||
: _getFilteredResults(model)
|
||||
],
|
||||
)
|
||||
: query.isEmpty || query.length < 2
|
||||
: _query.isEmpty || _query.length < 2
|
||||
? Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
@ -105,7 +103,7 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
||||
itemBuilder: (context, index) {
|
||||
model.apps.sort((a, b) => a.appName.compareTo(b.appName));
|
||||
if (model.apps[index].appName.toLowerCase().contains(
|
||||
query.toLowerCase(),
|
||||
_query.toLowerCase(),
|
||||
)) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
|
@ -8,30 +8,22 @@ import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/models/patched_application.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/patches_selector/patches_selector_viewmodel.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
class AppSelectorViewModel extends BaseViewModel {
|
||||
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
||||
bool isRooted = false;
|
||||
bool isFromStorage = false;
|
||||
List<ApplicationWithIcon> apps = [];
|
||||
PatchedApplication? selectedApp;
|
||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||
final List<ApplicationWithIcon> apps = [];
|
||||
bool _isRooted = false;
|
||||
|
||||
Future<void> initialize() async {
|
||||
await getApps();
|
||||
apps.addAll(await _patcherAPI.getFilteredInstalledApps());
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
isRooted = prefs.getBool('isRooted') ?? false;
|
||||
_isRooted = prefs.getBool('isRooted') ?? false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getApps() async {
|
||||
apps = await patcherAPI.getFilteredInstalledApps();
|
||||
}
|
||||
|
||||
void selectApp(ApplicationWithIcon application) async {
|
||||
isFromStorage = false;
|
||||
PatchedApplication app = PatchedApplication(
|
||||
name: application.appName,
|
||||
packageName: application.packageName,
|
||||
@ -39,18 +31,16 @@ class AppSelectorViewModel extends BaseViewModel {
|
||||
apkFilePath: application.apkFilePath,
|
||||
icon: application.icon,
|
||||
patchDate: DateTime.now(),
|
||||
isRooted: isRooted,
|
||||
isFromStorage: isFromStorage,
|
||||
isRooted: _isRooted,
|
||||
isFromStorage: false,
|
||||
appliedPatches: [],
|
||||
);
|
||||
locator<AppSelectorViewModel>().selectedApp = app;
|
||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
||||
locator<PatchesSelectorViewModel>().notifyListeners();
|
||||
locator<PatcherViewModel>().selectedApp = app;
|
||||
locator<PatcherViewModel>().selectedPatches.clear();
|
||||
locator<PatcherViewModel>().notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> selectAppFromStorage(BuildContext context) async {
|
||||
isFromStorage = true;
|
||||
try {
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
@ -69,13 +59,12 @@ class AppSelectorViewModel extends BaseViewModel {
|
||||
apkFilePath: result.files.single.path!,
|
||||
icon: application.icon,
|
||||
patchDate: DateTime.now(),
|
||||
isRooted: isRooted,
|
||||
isFromStorage: isFromStorage,
|
||||
isRooted: _isRooted,
|
||||
isFromStorage: true,
|
||||
appliedPatches: [],
|
||||
);
|
||||
locator<AppSelectorViewModel>().selectedApp = app;
|
||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
||||
locator<PatchesSelectorViewModel>().notifyListeners();
|
||||
locator<PatcherViewModel>().selectedApp = app;
|
||||
locator<PatcherViewModel>().selectedPatches.clear();
|
||||
locator<PatcherViewModel>().notifyListeners();
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ class ContributorsView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<ContributorsViewModel>.reactive(
|
||||
disposeViewModel: false,
|
||||
viewModelBuilder: () => ContributorsViewModel(),
|
||||
onModelReady: (model) => model.getContributors(),
|
||||
builder: (context, model, child) => Scaffold(
|
||||
|
@ -1,27 +1,36 @@
|
||||
import 'package:github/github.dart';
|
||||
import 'package:revanced_manager/constants.dart';
|
||||
import 'package:revanced_manager/services/github_api.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
class ContributorsViewModel extends BaseViewModel {
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
final GithubAPI _githubAPI = GithubAPI();
|
||||
List<Contributor> patchesContributors = [];
|
||||
List<Contributor> integrationsContributors = [];
|
||||
List<Contributor> patcherContributors = [];
|
||||
List<Contributor> cliContributors = [];
|
||||
List<Contributor> managerContributors = [];
|
||||
|
||||
Future<List<Contributor>> getContributors() async {
|
||||
patchesContributors =
|
||||
await githubAPI.getContributors('revanced', 'revanced-patches');
|
||||
integrationsContributors =
|
||||
await githubAPI.getContributors('revanced', 'revanced-integrations');
|
||||
patcherContributors =
|
||||
await githubAPI.getContributors('revanced', 'revanced-patcher');
|
||||
cliContributors =
|
||||
await githubAPI.getContributors('revanced', 'revanced-cli');
|
||||
managerContributors =
|
||||
await githubAPI.getContributors('revanced', 'revanced-manager');
|
||||
|
||||
return [];
|
||||
Future<void> getContributors() async {
|
||||
patchesContributors = await _githubAPI.getContributors(
|
||||
ghOrg,
|
||||
patchesRepo,
|
||||
);
|
||||
integrationsContributors = await _githubAPI.getContributors(
|
||||
ghOrg,
|
||||
integrationsRepo,
|
||||
);
|
||||
patcherContributors = await _githubAPI.getContributors(
|
||||
ghOrg,
|
||||
patcherRepo,
|
||||
);
|
||||
cliContributors = await _githubAPI.getContributors(
|
||||
ghOrg,
|
||||
cliRepo,
|
||||
);
|
||||
managerContributors = await _githubAPI.getContributors(
|
||||
ghOrg,
|
||||
managerRepo,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,7 @@ class HomeView extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
LatestCommitCard(
|
||||
onPressed: () => model.updateManager(context),
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const SizedBox(height: 23),
|
||||
@ -77,7 +78,7 @@ class HomeView extends StatelessWidget {
|
||||
label: "homeView.updatesAvailable",
|
||||
isSelected: model.showUpdatableApps,
|
||||
onSelected: (value) {
|
||||
model.toggleUpdatableApps(value);
|
||||
model.toggleUpdatableApps(true);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
|
@ -1,26 +1,41 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:app_installer/app_installer.dart';
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/app/app.router.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/manager_api.dart';
|
||||
import 'package:revanced_manager/services/patcher_api.dart';
|
||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_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_services/stacked_services.dart';
|
||||
|
||||
@lazySingleton
|
||||
class HomeViewModel extends BaseViewModel {
|
||||
final _navigationService = locator<NavigationService>();
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
||||
final NavigationService _navigationService = locator<NavigationService>();
|
||||
final ManagerAPI _managerAPI = ManagerAPI();
|
||||
final GithubAPI _githubAPI = GithubAPI();
|
||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||
FlutterLocalNotificationsPlugin();
|
||||
bool showUpdatableApps = true;
|
||||
|
||||
Future<void> initialize() async {
|
||||
await patcherAPI.loadPatches();
|
||||
await _patcherAPI.loadPatches();
|
||||
await flutterLocalNotificationsPlugin.initialize(
|
||||
const InitializationSettings(
|
||||
android: AndroidInitializationSettings('ic_notification'),
|
||||
),
|
||||
onSelectNotification: (p) => DeviceApps.openApp('app.revanced.manager'),
|
||||
);
|
||||
}
|
||||
|
||||
void toggleUpdatableApps(bool value) {
|
||||
@ -28,13 +43,10 @@ class HomeViewModel extends BaseViewModel {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future downloadPatches() => locator<ManagerAPI>().downloadPatches();
|
||||
Future downloadIntegrations() => locator<ManagerAPI>().downloadIntegrations();
|
||||
|
||||
void navigateToInstaller(PatchedApplication app) async {
|
||||
locator<AppSelectorViewModel>().selectedApp = app;
|
||||
locator<PatchesSelectorViewModel>().selectedPatches =
|
||||
await patcherAPI.getAppliedPatches(app);
|
||||
locator<PatcherViewModel>().selectedApp = app;
|
||||
locator<PatcherViewModel>().selectedPatches =
|
||||
await _patcherAPI.getAppliedPatches(app);
|
||||
_navigationService.navigateTo(Routes.installerView);
|
||||
}
|
||||
|
||||
@ -44,15 +56,37 @@ class HomeViewModel extends BaseViewModel {
|
||||
List<String> patchedApps = prefs.getStringList('patchedApps') ?? [];
|
||||
for (String str in patchedApps) {
|
||||
PatchedApplication app = PatchedApplication.fromJson(json.decode(str));
|
||||
bool hasUpdates = await githubAPI.hasUpdates(
|
||||
app,
|
||||
'revanced',
|
||||
'revanced-patches',
|
||||
);
|
||||
bool hasUpdates = await _githubAPI.hasUpdates(app, ghOrg, patchesRepo);
|
||||
if (hasUpdates == isUpdatable) {
|
||||
list.add(app);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void updateManager(BuildContext context) async {
|
||||
File? managerApk = await _managerAPI.downloadManager();
|
||||
if (managerApk != null) {
|
||||
flutterLocalNotificationsPlugin.show(
|
||||
0,
|
||||
// ignore: use_build_context_synchronously
|
||||
FlutterI18n.translate(
|
||||
context,
|
||||
'homeView.notificationTitle',
|
||||
),
|
||||
// ignore: use_build_context_synchronously
|
||||
FlutterI18n.translate(
|
||||
context,
|
||||
'homeView.notificationText',
|
||||
),
|
||||
const NotificationDetails(
|
||||
android: AndroidNotificationDetails(
|
||||
'revanced_manager_channel',
|
||||
'ReVanced Manager Channel',
|
||||
),
|
||||
),
|
||||
);
|
||||
AppInstaller.installApk(managerApk.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
@ -15,9 +14,8 @@ class InstallerView extends StatelessWidget {
|
||||
(_) => _controller.jumpTo(_controller.position.maxScrollExtent),
|
||||
);
|
||||
return ViewModelBuilder<InstallerViewModel>.reactive(
|
||||
disposeViewModel: false,
|
||||
onModelReady: (model) => model.initialize(),
|
||||
viewModelBuilder: () => locator<InstallerViewModel>(),
|
||||
onModelReady: (model) => model.initialize(context),
|
||||
viewModelBuilder: () => InstallerViewModel(),
|
||||
builder: (context, model, child) => WillPopScope(
|
||||
child: Scaffold(
|
||||
floatingActionButton: Visibility(
|
||||
|
@ -1,30 +1,43 @@
|
||||
import 'dart:convert';
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_background/flutter_background.dart';
|
||||
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/patcher_api.dart';
|
||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
class InstallerViewModel extends BaseViewModel {
|
||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||
final PatchedApplication? _app = locator<PatcherViewModel>().selectedApp;
|
||||
final List<Patch> _patches = locator<PatcherViewModel>().selectedPatches;
|
||||
static const _installerChannel = MethodChannel(
|
||||
'app.revanced.manager/installer',
|
||||
);
|
||||
double? progress = 0.0;
|
||||
String logs = '';
|
||||
bool isPatching = false;
|
||||
bool isInstalled = false;
|
||||
|
||||
Future<void> initialize() async {
|
||||
Future<void> initialize(BuildContext context) async {
|
||||
try {
|
||||
await FlutterBackground.initialize(
|
||||
androidConfig: const FlutterBackgroundAndroidConfig(
|
||||
notificationTitle: 'Patching',
|
||||
notificationText: 'ReVanced Manager is patching',
|
||||
androidConfig: FlutterBackgroundAndroidConfig(
|
||||
notificationTitle: FlutterI18n.translate(
|
||||
context,
|
||||
'installerView.notificationTitle',
|
||||
),
|
||||
notificationText: FlutterI18n.translate(
|
||||
context,
|
||||
'installerView.notificationText',
|
||||
),
|
||||
notificationImportance: AndroidNotificationImportance.Default,
|
||||
notificationIcon: AndroidResource(
|
||||
notificationIcon: const AndroidResource(
|
||||
name: 'ic_notification',
|
||||
defType: 'drawable',
|
||||
),
|
||||
@ -32,11 +45,28 @@ class InstallerViewModel extends BaseViewModel {
|
||||
);
|
||||
await FlutterBackground.enableBackgroundExecution();
|
||||
} finally {
|
||||
await locator<PatcherAPI>().handlePlatformChannelMethods();
|
||||
await handlePlatformChannelMethods();
|
||||
await runPatcher();
|
||||
}
|
||||
}
|
||||
|
||||
Future<dynamic> handlePlatformChannelMethods() async {
|
||||
_installerChannel.setMethodCallHandler((call) async {
|
||||
switch (call.method) {
|
||||
case 'updateProgress':
|
||||
if (call.arguments != null) {
|
||||
updateProgress(call.arguments);
|
||||
}
|
||||
break;
|
||||
case 'updateLog':
|
||||
if (call.arguments != null) {
|
||||
updateLog(call.arguments);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void updateProgress(double value) {
|
||||
progress = value;
|
||||
isInstalled = false;
|
||||
@ -59,37 +89,32 @@ class InstallerViewModel extends BaseViewModel {
|
||||
|
||||
Future<void> runPatcher() async {
|
||||
updateProgress(0.0);
|
||||
PatchedApplication? selectedApp =
|
||||
locator<AppSelectorViewModel>().selectedApp;
|
||||
List<Patch> selectedPatches =
|
||||
locator<PatchesSelectorViewModel>().selectedPatches;
|
||||
if (selectedApp != null && selectedPatches.isNotEmpty) {
|
||||
String apkFilePath = selectedApp.apkFilePath;
|
||||
if (_app != null && _patches.isNotEmpty) {
|
||||
String apkFilePath = _app!.apkFilePath;
|
||||
try {
|
||||
updateLog('Initializing installer');
|
||||
if (selectedApp.isRooted && !selectedApp.isFromStorage) {
|
||||
if (_app!.isRooted && !_app!.isFromStorage) {
|
||||
updateLog('Checking if an old patched version exists');
|
||||
bool oldExists =
|
||||
await locator<PatcherAPI>().checkOldPatch(selectedApp);
|
||||
bool oldExists = await _patcherAPI.checkOldPatch(_app!);
|
||||
if (oldExists) {
|
||||
updateLog('Deleting old patched version');
|
||||
await locator<PatcherAPI>().deleteOldPatch(selectedApp);
|
||||
await _patcherAPI.deleteOldPatch(_app!);
|
||||
}
|
||||
}
|
||||
updateLog('Creating working directory');
|
||||
bool mergeIntegrations = false;
|
||||
bool resourcePatching = false;
|
||||
if (selectedApp.packageName == 'com.google.android.youtube') {
|
||||
if (_app!.packageName == 'com.google.android.youtube') {
|
||||
mergeIntegrations = true;
|
||||
resourcePatching = true;
|
||||
} else if (selectedApp.packageName ==
|
||||
} else if (_app!.packageName ==
|
||||
'com.google.android.apps.youtube.music') {
|
||||
resourcePatching = true;
|
||||
}
|
||||
await locator<PatcherAPI>().initPatcher(mergeIntegrations);
|
||||
await locator<PatcherAPI>().runPatcher(
|
||||
await _patcherAPI.initPatcher(mergeIntegrations);
|
||||
await _patcherAPI.runPatcher(
|
||||
apkFilePath,
|
||||
selectedPatches,
|
||||
_patches,
|
||||
mergeIntegrations,
|
||||
resourcePatching,
|
||||
);
|
||||
@ -107,21 +132,16 @@ class InstallerViewModel extends BaseViewModel {
|
||||
}
|
||||
|
||||
void installResult() async {
|
||||
PatchedApplication? selectedApp =
|
||||
locator<AppSelectorViewModel>().selectedApp;
|
||||
List<Patch> selectedPatches =
|
||||
locator<PatchesSelectorViewModel>().selectedPatches;
|
||||
if (selectedApp != null) {
|
||||
updateLog(selectedApp.isRooted
|
||||
if (_app != null) {
|
||||
updateLog(_app!.isRooted
|
||||
? 'Installing patched file using root method'
|
||||
: 'Installing patched file using nonroot method');
|
||||
isInstalled = await locator<PatcherAPI>().installPatchedFile(selectedApp);
|
||||
isInstalled = await _patcherAPI.installPatchedFile(_app!);
|
||||
if (isInstalled) {
|
||||
updateLog('Done');
|
||||
selectedApp.patchDate = DateTime.now();
|
||||
selectedApp.appliedPatches
|
||||
.addAll(selectedPatches.map((p) => p.name).toList());
|
||||
await saveApp(selectedApp);
|
||||
_app!.patchDate = DateTime.now();
|
||||
_app!.appliedPatches.addAll(_patches.map((p) => p.name).toList());
|
||||
await saveApp();
|
||||
} else {
|
||||
updateLog('An error occurred! Aborting');
|
||||
}
|
||||
@ -129,39 +149,33 @@ class InstallerViewModel extends BaseViewModel {
|
||||
}
|
||||
|
||||
void shareResult() {
|
||||
PatchedApplication? selectedApp =
|
||||
locator<AppSelectorViewModel>().selectedApp;
|
||||
if (selectedApp != null) {
|
||||
locator<PatcherAPI>().sharePatchedFile(
|
||||
selectedApp.name,
|
||||
selectedApp.version,
|
||||
);
|
||||
if (_app != null) {
|
||||
_patcherAPI.sharePatchedFile(_app!.name, _app!.version);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> cleanWorkplace() async {
|
||||
locator<PatcherAPI>().cleanPatcher();
|
||||
locator<AppSelectorViewModel>().selectedApp = null;
|
||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
||||
locator<PatchesSelectorViewModel>().notifyListeners();
|
||||
_patcherAPI.cleanPatcher();
|
||||
locator<PatcherViewModel>().selectedApp = null;
|
||||
locator<PatcherViewModel>().selectedPatches.clear();
|
||||
locator<PatcherViewModel>().notifyListeners();
|
||||
}
|
||||
|
||||
void openApp() {
|
||||
PatchedApplication? selectedApp =
|
||||
locator<AppSelectorViewModel>().selectedApp;
|
||||
if (selectedApp != null) {
|
||||
DeviceApps.openApp(selectedApp.packageName);
|
||||
if (_app != null) {
|
||||
DeviceApps.openApp(_app!.packageName);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> saveApp(PatchedApplication selectedApp) async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
List<String> patchedApps = prefs.getStringList('patchedApps') ?? [];
|
||||
String app = json.encode(selectedApp.toJson());
|
||||
patchedApps.removeWhere(
|
||||
(a) => json.decode(a)['packageName'] == selectedApp.packageName);
|
||||
patchedApps.add(app);
|
||||
prefs.setStringList('patchedApps', patchedApps);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,17 @@
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/app/app.router.dart';
|
||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:revanced_manager/models/patched_application.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
|
||||
@lazySingleton
|
||||
class PatcherViewModel extends BaseViewModel {
|
||||
final _navigationService = locator<NavigationService>();
|
||||
PatchedApplication? selectedApp;
|
||||
List<Patch> selectedPatches = [];
|
||||
|
||||
final NavigationService _navigationService = locator<NavigationService>();
|
||||
|
||||
void navigateToAppSelector() {
|
||||
_navigationService.navigateTo(Routes.appSelectorView);
|
||||
@ -21,10 +26,10 @@ class PatcherViewModel extends BaseViewModel {
|
||||
}
|
||||
|
||||
bool showFabButton() {
|
||||
return locator<PatchesSelectorViewModel>().selectedPatches.isNotEmpty;
|
||||
return selectedPatches.isNotEmpty;
|
||||
}
|
||||
|
||||
bool dimPatchesCard() {
|
||||
return locator<AppSelectorViewModel>().selectedApp == null;
|
||||
return selectedApp == null;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/theme.dart';
|
||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/patch_item.dart';
|
||||
@ -15,16 +14,14 @@ class PatchesSelectorView extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
||||
final List<PatchItem> patches = [];
|
||||
final List<PatchItem> _items = [];
|
||||
String query = '';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<PatchesSelectorViewModel>.reactive(
|
||||
disposeViewModel: false,
|
||||
fireOnModelReadyOnce: true,
|
||||
onModelReady: (model) => model.initialize(),
|
||||
viewModelBuilder: () => locator<PatchesSelectorViewModel>(),
|
||||
viewModelBuilder: () => PatchesSelectorViewModel(),
|
||||
builder: (context, model, child) => Scaffold(
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
@ -62,7 +59,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
onPressed: () {
|
||||
model.selectPatches();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: I18nText('patchesSelectorView.fabButton'),
|
||||
),
|
||||
],
|
||||
@ -79,7 +79,7 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
||||
}
|
||||
|
||||
Widget _getAllResults(PatchesSelectorViewModel model) {
|
||||
patches.clear();
|
||||
_items.clear();
|
||||
return Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: model.patches.length,
|
||||
@ -93,8 +93,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
||||
isSelected: model.selectedPatches.any(
|
||||
(element) => element.name == model.patches[index].name,
|
||||
),
|
||||
onChanged: (value) =>
|
||||
model.selectPatch(model.patches[index].name, value),
|
||||
);
|
||||
patches.add(item);
|
||||
_items.add(item);
|
||||
return item;
|
||||
},
|
||||
),
|
||||
@ -102,7 +104,7 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
||||
}
|
||||
|
||||
Widget _getFilteredResults(PatchesSelectorViewModel model) {
|
||||
patches.clear();
|
||||
_items.clear();
|
||||
return Expanded(
|
||||
child: ListView.builder(
|
||||
itemCount: model.patches.length,
|
||||
@ -119,8 +121,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
||||
isSelected: model.selectedPatches.any(
|
||||
(element) => element.name == model.patches[index].name,
|
||||
),
|
||||
onChanged: (value) =>
|
||||
model.selectPatch(model.patches[index].name, value),
|
||||
);
|
||||
patches.add(item);
|
||||
_items.add(item);
|
||||
return item;
|
||||
} else {
|
||||
return const SizedBox();
|
||||
|
@ -1,38 +1,34 @@
|
||||
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/patcher_api.dart';
|
||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/patch_item.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
class PatchesSelectorViewModel extends BaseViewModel {
|
||||
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
||||
List<Patch> patches = [];
|
||||
List<Patch> selectedPatches = [];
|
||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||
final List<Patch> patches = [];
|
||||
final List<Patch> selectedPatches =
|
||||
locator<PatcherViewModel>().selectedPatches;
|
||||
|
||||
Future<void> initialize() async {
|
||||
await getPatches();
|
||||
patches.addAll(await _patcherAPI.getFilteredPatches(
|
||||
locator<PatcherViewModel>().selectedApp,
|
||||
));
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getPatches() async {
|
||||
PatchedApplication? app = locator<AppSelectorViewModel>().selectedApp;
|
||||
patches = await patcherAPI.getFilteredPatches(app);
|
||||
void selectPatch(String name, bool isSelected) {
|
||||
Patch patch = patches.firstWhere((p) => p.name == name);
|
||||
if (isSelected && !selectedPatches.contains(patch)) {
|
||||
selectedPatches.add(patch);
|
||||
} else {
|
||||
selectedPatches.remove(patch);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void selectPatch(PatchItem item) {
|
||||
Patch patch = locator<PatchesSelectorViewModel>()
|
||||
.patches
|
||||
.firstWhere((p) => p.name == item.name);
|
||||
if (item.isSelected &&
|
||||
!locator<PatchesSelectorViewModel>().selectedPatches.contains(patch)) {
|
||||
locator<PatchesSelectorViewModel>().selectedPatches.add(patch);
|
||||
} else {
|
||||
locator<PatchesSelectorViewModel>().selectedPatches.remove(patch);
|
||||
}
|
||||
locator<PatchesSelectorViewModel>().notifyListeners();
|
||||
void selectPatches() {
|
||||
locator<PatcherViewModel>().selectedPatches = selectedPatches;
|
||||
locator<PatcherViewModel>().notifyListeners();
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@ class RootCheckerView extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<RootCheckerViewModel>.reactive(
|
||||
disposeViewModel: false,
|
||||
viewModelBuilder: () => RootCheckerViewModel(),
|
||||
builder: (context, model, child) => Scaffold(
|
||||
floatingActionButton: Column(
|
||||
|
@ -6,12 +6,13 @@ import 'package:root/root.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
|
||||
class RootCheckerViewModel extends BaseViewModel {
|
||||
final _navigationService = locator<NavigationService>();
|
||||
bool? isRooted = false;
|
||||
final NavigationService _navigationService = locator<NavigationService>();
|
||||
bool isRooted = false;
|
||||
|
||||
Future<void> checkRoot() async {
|
||||
isRooted = await Root.isRooted();
|
||||
if (isRooted == true) {
|
||||
bool? res = await Root.isRooted();
|
||||
isRooted = res != null && res == true;
|
||||
if (isRooted) {
|
||||
navigateToHome();
|
||||
}
|
||||
notifyListeners();
|
||||
@ -19,7 +20,7 @@ class RootCheckerViewModel extends BaseViewModel {
|
||||
|
||||
Future<void> navigateToHome() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool('isRooted', isRooted!);
|
||||
prefs.setBool('isRooted', isRooted);
|
||||
_navigationService.navigateTo(Routes.navigation);
|
||||
notifyListeners();
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
|
||||
class SettingsViewModel extends BaseViewModel {
|
||||
final _navigationService = locator<NavigationService>();
|
||||
final NavigationService _navigationService = locator<NavigationService>();
|
||||
|
||||
void setLanguage(String language) {
|
||||
notifyListeners();
|
||||
|
@ -1,25 +1,21 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/constants.dart';
|
||||
import 'package:revanced_manager/services/patcher_api.dart';
|
||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
|
||||
class AppSelectorCard extends StatelessWidget {
|
||||
final Function()? onPressed;
|
||||
final Function() onPressed;
|
||||
final Color? color;
|
||||
|
||||
AppSelectorCard({
|
||||
const AppSelectorCard({
|
||||
Key? key,
|
||||
this.onPressed,
|
||||
required this.onPressed,
|
||||
this.color = const Color(0xff1B222B),
|
||||
}) : super(key: key);
|
||||
|
||||
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
@ -36,7 +32,7 @@ class AppSelectorCard extends StatelessWidget {
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
I18nText(
|
||||
locator<AppSelectorViewModel>().selectedApp == null
|
||||
locator<PatcherViewModel>().selectedApp == null
|
||||
? 'appSelectorCard.widgetTitle'
|
||||
: 'appSelectorCard.widgetTitleSelected',
|
||||
child: Text(
|
||||
@ -48,7 +44,7 @@ class AppSelectorCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
locator<AppSelectorViewModel>().selectedApp == null
|
||||
locator<PatcherViewModel>().selectedApp == null
|
||||
? I18nText(
|
||||
'appSelectorCard.widgetSubtitle',
|
||||
child: Text(
|
||||
@ -62,11 +58,9 @@ class AppSelectorCard extends StatelessWidget {
|
||||
height: 16.0,
|
||||
child: ClipOval(
|
||||
child: Image.memory(
|
||||
locator<AppSelectorViewModel>().selectedApp == null
|
||||
locator<PatcherViewModel>().selectedApp == null
|
||||
? Uint8List(0)
|
||||
: locator<AppSelectorViewModel>()
|
||||
.selectedApp!
|
||||
.icon,
|
||||
: locator<PatcherViewModel>().selectedApp!.icon,
|
||||
fit: BoxFit.cover,
|
||||
),
|
||||
),
|
||||
@ -85,8 +79,8 @@ class AppSelectorCard extends StatelessWidget {
|
||||
}
|
||||
|
||||
String _getAppSelection() {
|
||||
String name = locator<AppSelectorViewModel>().selectedApp!.name;
|
||||
String version = locator<AppSelectorViewModel>().selectedApp!.version;
|
||||
String name = locator<PatcherViewModel>().selectedApp!.name;
|
||||
String version = locator<PatcherViewModel>().selectedApp!.version;
|
||||
return '$name (v$version)';
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ class ApplicationItem extends StatelessWidget {
|
||||
final DateTime patchDate;
|
||||
final String changelog;
|
||||
final bool isUpdatableApp;
|
||||
final Function()? onPressed;
|
||||
final Function() onPressed;
|
||||
|
||||
const ApplicationItem({
|
||||
Key? key,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:revanced_manager/app/app.locator.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/ui/views/home/home_viewmodel.dart';
|
||||
@ -10,7 +11,7 @@ class AvailableUpdatesCard extends StatelessWidget {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
final GithubAPI _githubAPI = GithubAPI();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,10 +28,10 @@ class AvailableUpdatesCard extends StatelessWidget {
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (context, index) => FutureBuilder<String>(
|
||||
future: githubAPI.getChangelog(
|
||||
future: _githubAPI.getChangelog(
|
||||
snapshot.data![index],
|
||||
'revanced',
|
||||
'revanced-patches',
|
||||
ghOrg,
|
||||
patchesRepo,
|
||||
),
|
||||
initialData: '',
|
||||
builder: (context, snapshot2) => ApplicationItem(
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:revanced_manager/app/app.locator.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/ui/views/home/home_viewmodel.dart';
|
||||
@ -10,7 +11,7 @@ class InstalledAppsCard extends StatelessWidget {
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
final GithubAPI _githubAPI = GithubAPI();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -27,10 +28,10 @@ class InstalledAppsCard extends StatelessWidget {
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: snapshot.data!.length,
|
||||
itemBuilder: (context, index) => FutureBuilder<String>(
|
||||
future: githubAPI.getChangelog(
|
||||
future: _githubAPI.getChangelog(
|
||||
snapshot.data![index],
|
||||
'revanced',
|
||||
'revanced-patches',
|
||||
ghOrg,
|
||||
patchesRepo,
|
||||
),
|
||||
initialData: '',
|
||||
builder: (context, snapshot2) => ApplicationItem(
|
||||
|
@ -6,9 +6,11 @@ import 'package:revanced_manager/constants.dart';
|
||||
import 'package:revanced_manager/ui/widgets/patch_text_button.dart';
|
||||
|
||||
class LatestCommitCard extends StatefulWidget {
|
||||
final Function() onPressed;
|
||||
final Color? color;
|
||||
const LatestCommitCard({
|
||||
Key? key,
|
||||
required this.onPressed,
|
||||
this.color = const Color(0xff1B222B),
|
||||
}) : super(key: key);
|
||||
|
||||
@ -17,7 +19,7 @@ class LatestCommitCard extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LatestCommitCardState extends State<LatestCommitCard> {
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
final GithubAPI _githubAPI = GithubAPI();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -45,10 +47,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
|
||||
),
|
||||
),
|
||||
FutureBuilder<String>(
|
||||
future: githubAPI.latestCommitTime(
|
||||
'revanced',
|
||||
'revanced-patcher',
|
||||
),
|
||||
future: _githubAPI.latestCommitTime(ghOrg, patcherRepo),
|
||||
builder: (context, snapshot) => Text(
|
||||
snapshot.hasData && snapshot.data!.isNotEmpty
|
||||
? FlutterI18n.translate(
|
||||
@ -78,10 +77,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
|
||||
),
|
||||
),
|
||||
FutureBuilder<String>(
|
||||
future: githubAPI.latestCommitTime(
|
||||
'revanced',
|
||||
'revanced-manager',
|
||||
),
|
||||
future: _githubAPI.latestCommitTime(ghOrg, managerRepo),
|
||||
builder: (context, snapshot) => Text(
|
||||
snapshot.hasData && snapshot.data!.isNotEmpty
|
||||
? FlutterI18n.translate(
|
||||
@ -105,7 +101,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
|
||||
context,
|
||||
'latestCommitCard.updateButton',
|
||||
),
|
||||
onPressed: () => {},
|
||||
onPressed: widget.onPressed,
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
borderColor: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
|
@ -5,10 +5,10 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:revanced_manager/theme.dart';
|
||||
|
||||
class MagiskButton extends StatelessWidget {
|
||||
final Function()? onPressed;
|
||||
final Function() onPressed;
|
||||
const MagiskButton({
|
||||
Key? key,
|
||||
this.onPressed,
|
||||
required this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
|
@ -1,7 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class PatchItem extends StatefulWidget {
|
||||
@ -10,6 +8,7 @@ class PatchItem extends StatefulWidget {
|
||||
final String description;
|
||||
final String version;
|
||||
bool isSelected;
|
||||
final Function(bool) onChanged;
|
||||
|
||||
PatchItem({
|
||||
Key? key,
|
||||
@ -18,6 +17,7 @@ class PatchItem extends StatefulWidget {
|
||||
required this.description,
|
||||
required this.version,
|
||||
required this.isSelected,
|
||||
required this.onChanged,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -30,7 +30,7 @@ class _PatchItemState extends State<PatchItem> {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
setState(() => widget.isSelected = !widget.isSelected);
|
||||
locator<PatchesSelectorViewModel>().selectPatch(widget);
|
||||
widget.onChanged(widget.isSelected);
|
||||
},
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
@ -82,7 +82,7 @@ class _PatchItemState extends State<PatchItem> {
|
||||
activeColor: Colors.blueGrey[500],
|
||||
onChanged: (newValue) {
|
||||
setState(() => widget.isSelected = newValue!);
|
||||
locator<PatchesSelectorViewModel>().selectPatch(widget);
|
||||
widget.onChanged(widget.isSelected);
|
||||
},
|
||||
),
|
||||
)
|
||||
|
@ -4,16 +4,15 @@ import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/constants.dart';
|
||||
import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
|
||||
class PatchSelectorCard extends StatelessWidget {
|
||||
final Function()? onPressed;
|
||||
final Function() onPressed;
|
||||
final Color? color;
|
||||
|
||||
const PatchSelectorCard({
|
||||
Key? key,
|
||||
this.onPressed,
|
||||
required this.onPressed,
|
||||
this.color = const Color(0xff1B222B),
|
||||
}) : super(key: key);
|
||||
|
||||
@ -32,7 +31,7 @@ class PatchSelectorCard extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
I18nText(
|
||||
locator<PatchesSelectorViewModel>().selectedPatches.isEmpty
|
||||
locator<PatcherViewModel>().selectedPatches.isEmpty
|
||||
? 'patchSelectorCard.widgetTitle'
|
||||
: 'patchSelectorCard.widgetTitleSelected',
|
||||
child: Text(
|
||||
@ -44,7 +43,7 @@ class PatchSelectorCard extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
locator<AppSelectorViewModel>().selectedApp == null
|
||||
locator<PatcherViewModel>().selectedApp == null
|
||||
? I18nText(
|
||||
'patchSelectorCard.widgetSubtitle',
|
||||
child: Text(
|
||||
@ -52,7 +51,7 @@ class PatchSelectorCard extends StatelessWidget {
|
||||
style: robotoTextStyle,
|
||||
),
|
||||
)
|
||||
: locator<PatchesSelectorViewModel>().selectedPatches.isEmpty
|
||||
: locator<PatcherViewModel>().selectedPatches.isEmpty
|
||||
? I18nText(
|
||||
'patchSelectorCard.widgetEmptySubtitle',
|
||||
child: Text(
|
||||
@ -72,7 +71,7 @@ class PatchSelectorCard extends StatelessWidget {
|
||||
|
||||
String _getPatchesSelection() {
|
||||
String text = '';
|
||||
for (Patch p in locator<PatchesSelectorViewModel>().selectedPatches) {
|
||||
for (Patch p in locator<PatcherViewModel>().selectedPatches) {
|
||||
text += '${p.simpleName} (v${p.version})\n';
|
||||
}
|
||||
return text.substring(0, text.length - 1);
|
||||
|
@ -5,13 +5,13 @@ import 'package:revanced_manager/theme.dart';
|
||||
|
||||
class PatchTextButton extends StatelessWidget {
|
||||
final String text;
|
||||
final Function()? onPressed;
|
||||
final Function() onPressed;
|
||||
final Color borderColor;
|
||||
final Color backgroundColor;
|
||||
const PatchTextButton({
|
||||
Key? key,
|
||||
required this.text,
|
||||
this.onPressed,
|
||||
required this.onPressed,
|
||||
this.borderColor = const Color(0xff7792BA),
|
||||
this.backgroundColor = Colors.transparent,
|
||||
}) : super(key: key);
|
||||
|
@ -4,7 +4,7 @@ homepage: https://github.com/Aunali321/revanced-manager
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 1.0.0+1
|
||||
version: 0.0.1+1
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.5 <3.0.0"
|
||||
@ -16,7 +16,6 @@ dependencies:
|
||||
git:
|
||||
url: https://github.com/ponces/flutter_plugin_device_apps
|
||||
ref: appinfo-from-storage
|
||||
dio: ^4.0.6
|
||||
expandable: ^5.0.1
|
||||
file_picker: ^5.0.1
|
||||
flutter:
|
||||
@ -24,6 +23,7 @@ dependencies:
|
||||
flutter_background: ^1.1.0
|
||||
flutter_cache_manager: ^3.3.0
|
||||
flutter_i18n: ^0.32.4
|
||||
flutter_local_notifications: ^9.8.0+1
|
||||
flutter_svg: ^1.1.1+1
|
||||
fluttertoast: ^8.0.9
|
||||
get_it: ^7.2.0
|
||||
|
Loading…
Reference in New Issue
Block a user