mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-10 01:01:56 +01:00
feat: root installation (wip)
This commit is contained in:
parent
6061d900ed
commit
9ce0f81a89
16 changed files with 231 additions and 69 deletions
|
@ -70,6 +70,7 @@
|
|||
"rootCheckerView": {
|
||||
"widgetTitle": "Is your device rooted?",
|
||||
"widgetDescription": "Don't know what's this or prefer to use non-root version? Just click button below!",
|
||||
"grantPermission": "Grant Root Permission"
|
||||
"grantPermission": "Grant Root Permission",
|
||||
"grantedPermission": "Magisk permission granted: {isRooted}"
|
||||
}
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
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_view.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';
|
||||
|
@ -16,7 +18,7 @@ import 'package:stacked_themes/stacked_themes.dart';
|
|||
|
||||
@StackedApp(
|
||||
routes: [
|
||||
MaterialRoute(page: HomeView),
|
||||
MaterialRoute(page: Navigation),
|
||||
MaterialRoute(page: AppSelectorView),
|
||||
MaterialRoute(page: PatchesSelectorView),
|
||||
MaterialRoute(page: InstallerView),
|
||||
|
@ -27,12 +29,16 @@ import 'package:stacked_themes/stacked_themes.dart';
|
|||
dependencies: [
|
||||
LazySingleton(classType: NavigationService),
|
||||
LazySingleton(classType: PatcherAPI),
|
||||
LazySingleton(classType: ManagerAPI),
|
||||
LazySingleton(classType: RootAPI),
|
||||
LazySingleton(classType: PatcherViewModel),
|
||||
LazySingleton(classType: AppSelectorViewModel),
|
||||
LazySingleton(classType: PatchesSelectorViewModel),
|
||||
LazySingleton(classType: InstallerViewModel),
|
||||
LazySingleton(
|
||||
classType: ThemeService, resolveUsing: ThemeService.getInstance),
|
||||
classType: ThemeService,
|
||||
resolveUsing: ThemeService.getInstance,
|
||||
),
|
||||
],
|
||||
)
|
||||
class AppSetup {}
|
||||
|
|
|
@ -4,13 +4,15 @@
|
|||
// StackedLocatorGenerator
|
||||
// **************************************************************************
|
||||
|
||||
// ignore_for_file: public_member_api_docs
|
||||
// ignore_for_file: public_member_api_docs, depend_on_referenced_packages
|
||||
|
||||
import 'package:stacked_core/stacked_core.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:stacked_themes/stacked_themes.dart';
|
||||
|
||||
import '../services/manager_api.dart';
|
||||
import '../services/patcher_api.dart';
|
||||
import '../services/root_api.dart';
|
||||
import '../ui/views/app_selector/app_selector_viewmodel.dart';
|
||||
import '../ui/views/installer/installer_viewmodel.dart';
|
||||
import '../ui/views/patcher/patcher_viewmodel.dart';
|
||||
|
@ -27,6 +29,8 @@ Future<void> setupLocator(
|
|||
// Register dependencies
|
||||
locator.registerLazySingleton(() => NavigationService());
|
||||
locator.registerLazySingleton(() => PatcherAPI());
|
||||
locator.registerLazySingleton(() => ManagerAPI());
|
||||
locator.registerLazySingleton(() => RootAPI());
|
||||
locator.registerLazySingleton(() => PatcherViewModel());
|
||||
locator.registerLazySingleton(() => AppSelectorViewModel());
|
||||
locator.registerLazySingleton(() => PatchesSelectorViewModel());
|
||||
|
|
|
@ -10,16 +10,16 @@ import 'package:flutter/material.dart';
|
|||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
|
||||
import '../main.dart';
|
||||
import '../ui/views/app_selector/app_selector_view.dart';
|
||||
import '../ui/views/contributors/contributors_view.dart';
|
||||
import '../ui/views/home/home_view.dart';
|
||||
import '../ui/views/installer/installer_view.dart';
|
||||
import '../ui/views/patches_selector/patches_selector_view.dart';
|
||||
import '../ui/views/root_checker/root_checker_view.dart';
|
||||
import '../ui/views/settings/settings_view.dart';
|
||||
|
||||
class Routes {
|
||||
static const String homeView = '/home-view';
|
||||
static const String navigation = '/Navigation';
|
||||
static const String appSelectorView = '/app-selector-view';
|
||||
static const String patchesSelectorView = '/patches-selector-view';
|
||||
static const String installerView = '/installer-view';
|
||||
|
@ -27,7 +27,7 @@ class Routes {
|
|||
static const String contributorsView = '/contributors-view';
|
||||
static const String rootCheckerView = '/root-checker-view';
|
||||
static const all = <String>{
|
||||
homeView,
|
||||
navigation,
|
||||
appSelectorView,
|
||||
patchesSelectorView,
|
||||
installerView,
|
||||
|
@ -41,7 +41,7 @@ class StackedRouter extends RouterBase {
|
|||
@override
|
||||
List<RouteDef> get routes => _routes;
|
||||
final _routes = <RouteDef>[
|
||||
RouteDef(Routes.homeView, page: HomeView),
|
||||
RouteDef(Routes.navigation, page: Navigation),
|
||||
RouteDef(Routes.appSelectorView, page: AppSelectorView),
|
||||
RouteDef(Routes.patchesSelectorView, page: PatchesSelectorView),
|
||||
RouteDef(Routes.installerView, page: InstallerView),
|
||||
|
@ -52,9 +52,9 @@ class StackedRouter extends RouterBase {
|
|||
@override
|
||||
Map<Type, StackedRouteFactory> get pagesMap => _pagesMap;
|
||||
final _pagesMap = <Type, StackedRouteFactory>{
|
||||
HomeView: (data) {
|
||||
Navigation: (data) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (context) => const HomeView(),
|
||||
builder: (context) => const Navigation(),
|
||||
settings: data,
|
||||
);
|
||||
},
|
||||
|
@ -115,7 +115,7 @@ class InstallerViewArguments {
|
|||
/// *************************************************************************
|
||||
|
||||
extension NavigatorStateExtension on NavigationService {
|
||||
Future<dynamic> navigateToHomeView({
|
||||
Future<dynamic> navigateToNavigation({
|
||||
int? routerId,
|
||||
bool preventDuplicates = true,
|
||||
Map<String, String>? parameters,
|
||||
|
@ -123,7 +123,7 @@ extension NavigatorStateExtension on NavigationService {
|
|||
transition,
|
||||
}) async {
|
||||
return navigateTo(
|
||||
Routes.homeView,
|
||||
Routes.navigation,
|
||||
id: routerId,
|
||||
preventDuplicates: preventDuplicates,
|
||||
parameters: parameters,
|
||||
|
|
|
@ -8,7 +8,9 @@ import 'package:revanced_manager/main_viewmodel.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';
|
||||
|
@ -37,7 +39,20 @@ class MyApp extends StatelessWidget {
|
|||
themeMode: themeMode,
|
||||
navigatorKey: StackedService.navigatorKey,
|
||||
onGenerateRoute: StackedRouter().onGenerateRoute,
|
||||
home: const Navigation(),
|
||||
home: FutureBuilder<Widget>(
|
||||
future: _init(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.hasData) {
|
||||
return snapshot.data!;
|
||||
} else {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
localizationsDelegates: [
|
||||
FlutterI18nDelegate(
|
||||
translationLoader: FileTranslationLoader(
|
||||
|
@ -51,6 +66,15 @@ class MyApp extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Widget> _init() async {
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
bool? isRooted = prefs.getBool('isRooted');
|
||||
if (isRooted != null) {
|
||||
return const Navigation();
|
||||
}
|
||||
return const RootCheckerView();
|
||||
}
|
||||
}
|
||||
|
||||
class Navigation extends StatelessWidget {
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
class ApplicationInfo {
|
||||
final String name;
|
||||
final String packageName;
|
||||
final String version;
|
||||
final String apkFilePath;
|
||||
|
||||
ApplicationInfo({
|
||||
required this.name,
|
||||
required this.packageName,
|
||||
required this.version,
|
||||
required this.apkFilePath,
|
||||
});
|
||||
}
|
21
lib/models/patched_application.dart
Normal file
21
lib/models/patched_application.dart
Normal file
|
@ -0,0 +1,21 @@
|
|||
import 'package:revanced_manager/models/patch.dart';
|
||||
|
||||
class PatchedApplication {
|
||||
final String name;
|
||||
final String packageName;
|
||||
final String version;
|
||||
final String apkFilePath;
|
||||
final bool isRooted;
|
||||
final bool isFromStorage;
|
||||
final List<Patch> appliedPatches;
|
||||
|
||||
PatchedApplication({
|
||||
required this.name,
|
||||
required this.packageName,
|
||||
required this.version,
|
||||
required this.apkFilePath,
|
||||
required this.isRooted,
|
||||
required this.isFromStorage,
|
||||
this.appliedPatches = const <Patch>[],
|
||||
});
|
||||
}
|
|
@ -7,8 +7,8 @@ import 'package:revanced_manager/services/github_api.dart';
|
|||
|
||||
@lazySingleton
|
||||
class ManagerAPI {
|
||||
Dio dio = Dio();
|
||||
GithubAPI githubAPI = GithubAPI();
|
||||
final Dio dio = Dio();
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
|
||||
Future<String?> getPath() async {
|
||||
final path = await p.getApplicationSupportDirectory();
|
||||
|
|
|
@ -6,9 +6,10 @@ 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/application_info.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/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';
|
||||
|
@ -17,9 +18,10 @@ import 'package:share_extend/share_extend.dart';
|
|||
class PatcherAPI {
|
||||
static const platform = MethodChannel('app.revanced.manager/patcher');
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
final RootAPI rootAPI = RootAPI();
|
||||
final List<ApplicationWithIcon> _filteredPackages = [];
|
||||
final Map<String, List<Patch>> _filteredPatches = <String, List<Patch>>{};
|
||||
bool isRoot = false;
|
||||
Directory? _tmpDir;
|
||||
Directory? _workDir;
|
||||
Directory? _cacheDir;
|
||||
File? _patchBundleFile;
|
||||
|
@ -89,7 +91,9 @@ class PatcherAPI {
|
|||
return _filteredPackages;
|
||||
}
|
||||
|
||||
Future<List<Patch>?> getFilteredPatches(ApplicationInfo? selectedApp) async {
|
||||
Future<List<Patch>?> getFilteredPatches(
|
||||
PatchedApplication? selectedApp,
|
||||
) async {
|
||||
if (_patchBundleFile != null && selectedApp != null) {
|
||||
if (_filteredPatches[selectedApp.packageName] == null ||
|
||||
_filteredPatches[selectedApp.packageName]!.isEmpty) {
|
||||
|
@ -146,8 +150,8 @@ class PatcherAPI {
|
|||
try {
|
||||
_integrations = await downloadIntegrations();
|
||||
if (_integrations != null) {
|
||||
Directory tmpDir = await getTemporaryDirectory();
|
||||
_workDir = tmpDir.createTempSync('tmp-');
|
||||
_tmpDir = await getTemporaryDirectory();
|
||||
_workDir = _tmpDir!.createTempSync('tmp-');
|
||||
_inputFile = File('${_workDir!.path}/base.apk');
|
||||
_patchedFile = File('${_workDir!.path}/patched.apk');
|
||||
_outFile = File('${_workDir!.path}/out.apk');
|
||||
|
@ -256,15 +260,19 @@ class PatcherAPI {
|
|||
return false;
|
||||
}
|
||||
|
||||
Future<bool> installPatchedFile() async {
|
||||
Future<bool> installPatchedFile(PatchedApplication patchedApp) async {
|
||||
if (_outFile != null) {
|
||||
try {
|
||||
if (isRoot) {
|
||||
// TBD
|
||||
if (patchedApp.isRooted && !patchedApp.isFromStorage) {
|
||||
return rootAPI.installApp(
|
||||
patchedApp.packageName,
|
||||
patchedApp.apkFilePath,
|
||||
_outFile!.path,
|
||||
);
|
||||
} else {
|
||||
await AppInstaller.installApk(_outFile!.path);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
} on Exception {
|
||||
return false;
|
||||
}
|
||||
|
@ -280,11 +288,11 @@ class PatcherAPI {
|
|||
|
||||
bool sharePatchedFile(String appName, String version) {
|
||||
if (_outFile != null) {
|
||||
String path = _outFile!.parent.path;
|
||||
String path = _tmpDir!.path;
|
||||
String prefix = appName.toLowerCase().replaceAll(' ', '-');
|
||||
String sharePath = '$path/$prefix-revanced_v$version.apk';
|
||||
File share = _outFile!.copySync(sharePath);
|
||||
ShareExtend.share(share.path, "file");
|
||||
ShareExtend.share(share.path, 'file');
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
|
85
lib/services/root_api.dart
Normal file
85
lib/services/root_api.dart
Normal file
|
@ -0,0 +1,85 @@
|
|||
import 'dart:io';
|
||||
|
||||
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";
|
||||
|
||||
bool deleteApp(String packageName) {
|
||||
try {
|
||||
File('$managerDirPath/$packageName.apk').deleteSync();
|
||||
File('$serviceDDirPath/$packageName.sh').deleteSync();
|
||||
File('$postFsDataDirPath/$packageName.sh').deleteSync();
|
||||
return true;
|
||||
} on Exception {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> installApp(
|
||||
String packageName,
|
||||
String originalFilePath,
|
||||
String patchedFilePath,
|
||||
) async {
|
||||
try {
|
||||
Directory managerDir = Directory(managerDirPath);
|
||||
managerDir.createSync();
|
||||
String newPatchedFilePath = '$managerDirPath/$packageName.apk';
|
||||
installServiceDScript(
|
||||
packageName,
|
||||
originalFilePath,
|
||||
newPatchedFilePath,
|
||||
);
|
||||
installPostFsDataScript(
|
||||
packageName,
|
||||
originalFilePath,
|
||||
newPatchedFilePath,
|
||||
);
|
||||
File(patchedFilePath).renameSync(newPatchedFilePath);
|
||||
await Root.exec(
|
||||
cmd: 'chmod 644 $newPatchedFilePath',
|
||||
);
|
||||
await Root.exec(
|
||||
cmd: 'chown system:system $newPatchedFilePath',
|
||||
);
|
||||
await Root.exec(
|
||||
cmd: 'chcon u:object_r:apk_data_file:s0 $newPatchedFilePath',
|
||||
);
|
||||
return true;
|
||||
} on Exception {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> installServiceDScript(
|
||||
String packageName,
|
||||
String originalFilePath,
|
||||
String patchedFilePath,
|
||||
) async {
|
||||
String content = '#!/system/bin/sh\n'
|
||||
'while [ "\$(getprop sys.boot_completed | tr -d \'\r\')" != "1" ]; do sleep 1; done\n'
|
||||
'sleep 1\n'
|
||||
'chcon u:object_r:apk_data_file:s0 $patchedFilePath\n'
|
||||
'mount -o bind $patchedFilePath $originalFilePath';
|
||||
File scriptFile = File('$serviceDDirPath/$packageName.sh');
|
||||
await scriptFile.writeAsString(content);
|
||||
await Root.exec(cmd: 'chmod 744 ${scriptFile.path}');
|
||||
}
|
||||
|
||||
Future<void> installPostFsDataScript(
|
||||
String packageName,
|
||||
String originalFilePath,
|
||||
String patchedFilePath,
|
||||
) async {
|
||||
String content = '#!/system/bin/sh\n'
|
||||
'while read line; do echo \$line | grep $originalFilePath | '
|
||||
'awk \'{print \$2}\' | xargs umount -l; done< /proc/mounts';
|
||||
File scriptFile = File('$postFsDataDirPath/$packageName.sh');
|
||||
await scriptFile.writeAsString(content);
|
||||
await Root.exec(cmd: 'chmod 744 ${scriptFile.path}');
|
||||
}
|
||||
}
|
|
@ -6,19 +6,24 @@ import 'package:flutter_i18n/flutter_i18n.dart';
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:package_archive_info/package_archive_info.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/models/application_info.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 = [];
|
||||
ApplicationInfo? selectedApp;
|
||||
PatchedApplication? selectedApp;
|
||||
|
||||
Future<void> initialize() async {
|
||||
await getApps();
|
||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
isRooted = prefs.getBool('isRooted') ?? false;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
@ -27,12 +32,15 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||
apps = await patcherAPI.getFilteredInstalledApps();
|
||||
}
|
||||
|
||||
void selectApp(ApplicationWithIcon application) {
|
||||
ApplicationInfo app = ApplicationInfo(
|
||||
void selectApp(ApplicationWithIcon application) async {
|
||||
isFromStorage = false;
|
||||
PatchedApplication app = PatchedApplication(
|
||||
name: application.appName,
|
||||
packageName: application.packageName,
|
||||
version: application.versionName!,
|
||||
apkFilePath: application.apkFilePath,
|
||||
isRooted: isRooted,
|
||||
isFromStorage: isFromStorage,
|
||||
);
|
||||
locator<AppSelectorViewModel>().selectedApp = app;
|
||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
||||
|
@ -41,6 +49,7 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||
}
|
||||
|
||||
Future<void> selectAppFromStorage(BuildContext context) async {
|
||||
isFromStorage = true;
|
||||
try {
|
||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||
type: FileType.custom,
|
||||
|
@ -50,11 +59,13 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||
File apkFile = File(result.files.single.path!);
|
||||
PackageArchiveInfo? packageArchiveInfo =
|
||||
await PackageArchiveInfo.fromPath(apkFile.path);
|
||||
ApplicationInfo app = ApplicationInfo(
|
||||
PatchedApplication app = PatchedApplication(
|
||||
name: packageArchiveInfo.appName,
|
||||
packageName: packageArchiveInfo.packageName,
|
||||
version: packageArchiveInfo.version,
|
||||
apkFilePath: result.files.single.path!,
|
||||
isRooted: isRooted,
|
||||
isFromStorage: isFromStorage,
|
||||
);
|
||||
locator<AppSelectorViewModel>().selectedApp = app;
|
||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/models/application_info.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';
|
||||
|
@ -38,7 +38,8 @@ class InstallerViewModel extends BaseViewModel {
|
|||
|
||||
Future<void> runPatcher() async {
|
||||
updateProgress(0.0);
|
||||
ApplicationInfo? selectedApp = locator<AppSelectorViewModel>().selectedApp;
|
||||
PatchedApplication? selectedApp =
|
||||
locator<AppSelectorViewModel>().selectedApp;
|
||||
if (selectedApp != null) {
|
||||
String apkFilePath = selectedApp.apkFilePath;
|
||||
List<Patch> selectedPatches =
|
||||
|
@ -111,11 +112,16 @@ class InstallerViewModel extends BaseViewModel {
|
|||
}
|
||||
|
||||
void installResult() async {
|
||||
await locator<PatcherAPI>().installPatchedFile();
|
||||
PatchedApplication? selectedApp =
|
||||
locator<AppSelectorViewModel>().selectedApp;
|
||||
if (selectedApp != null) {
|
||||
await locator<PatcherAPI>().installPatchedFile(selectedApp);
|
||||
}
|
||||
}
|
||||
|
||||
void shareResult() {
|
||||
ApplicationInfo? selectedApp = locator<AppSelectorViewModel>().selectedApp;
|
||||
PatchedApplication? selectedApp =
|
||||
locator<AppSelectorViewModel>().selectedApp;
|
||||
if (selectedApp != null) {
|
||||
locator<PatcherAPI>().sharePatchedFile(
|
||||
selectedApp.name,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/models/application_info.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';
|
||||
|
@ -18,7 +18,7 @@ class PatchesSelectorViewModel extends BaseViewModel {
|
|||
}
|
||||
|
||||
Future<void> getPatches() async {
|
||||
ApplicationInfo? app = locator<AppSelectorViewModel>().selectedApp;
|
||||
PatchedApplication? app = locator<AppSelectorViewModel>().selectedApp;
|
||||
patches = await patcherAPI.getFilteredPatches(app);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/widgets/I18nText.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/ui/views/root_checker/root_checker_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/magisk_button.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
@ -11,8 +12,9 @@ class RootCheckerView extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ViewModelBuilder<RootCheckerViewModel>.reactive(
|
||||
disposeViewModel: false,
|
||||
onModelReady: (model) => model.initialize,
|
||||
viewModelBuilder: () => RootCheckerViewModel(),
|
||||
viewModelBuilder: () => locator<RootCheckerViewModel>(),
|
||||
builder: (context, model, child) => Scaffold(
|
||||
floatingActionButton: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
|
@ -64,15 +66,20 @@ class RootCheckerView extends StatelessWidget {
|
|||
const SizedBox(height: 170),
|
||||
MagiskButton(
|
||||
onPressed: () {
|
||||
model.getMagiskPermissions();
|
||||
Future.delayed(const Duration(seconds: 5), () {
|
||||
model.checkRoot();
|
||||
});
|
||||
model
|
||||
.getMagiskPermissions()
|
||||
.then((value) => model.checkRoot());
|
||||
},
|
||||
),
|
||||
Text(
|
||||
"Magisk permission granted: ${model.isRooted.toString()}",
|
||||
style: GoogleFonts.poppins(),
|
||||
I18nText(
|
||||
'rootCheckerView.grantedPermission',
|
||||
translationParams: {
|
||||
'isRooted': model.isRooted.toString(),
|
||||
},
|
||||
child: Text(
|
||||
'',
|
||||
style: GoogleFonts.poppins(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import 'package:fluttertoast/fluttertoast.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/app/app.router.dart';
|
||||
import 'package:revanced_manager/ui/views/home/home_view.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:root/root.dart';
|
||||
|
@ -18,21 +16,25 @@ class RootCheckerViewModel extends BaseViewModel {
|
|||
|
||||
Future<void> checkRoot() async {
|
||||
isRooted = await Root.isRooted();
|
||||
if (isRooted == true) {
|
||||
navigateToHome();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> getMagiskPermissions() async {
|
||||
if (isRooted == true) {
|
||||
Fluttertoast.showToast(msg: 'Magisk permission already granted!');
|
||||
Future<bool> getMagiskPermissions() async {
|
||||
try {
|
||||
await Root.exec(cmd: 'cat /proc/version');
|
||||
} on Exception {
|
||||
return false;
|
||||
}
|
||||
await Root.exec(cmd: "adb shell su -c exit");
|
||||
notifyListeners();
|
||||
return true;
|
||||
}
|
||||
|
||||
Future<void> navigateToHome() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.setBool('showHome', true);
|
||||
_navigationService.navigateTo(Routes.homeView);
|
||||
prefs.setBool('isRooted', isRooted!);
|
||||
_navigationService.navigateTo(Routes.navigation);
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ class LatestCommitCard extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _LatestCommitCardState extends State<LatestCommitCard> {
|
||||
GithubAPI githubAPI = GithubAPI();
|
||||
final GithubAPI githubAPI = GithubAPI();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
|
Loading…
Reference in a new issue