mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 14:04:26 +02:00
feat: root installation (wip)
This commit is contained in:
parent
6061d900ed
commit
9ce0f81a89
@ -70,6 +70,7 @@
|
|||||||
"rootCheckerView": {
|
"rootCheckerView": {
|
||||||
"widgetTitle": "Is your device rooted?",
|
"widgetTitle": "Is your device rooted?",
|
||||||
"widgetDescription": "Don't know what's this or prefer to use non-root version? Just click button below!",
|
"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/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_view.dart';
|
||||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.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/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_view.dart';
|
||||||
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.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/patcher/patcher_viewmodel.dart';
|
||||||
@ -16,7 +18,7 @@ import 'package:stacked_themes/stacked_themes.dart';
|
|||||||
|
|
||||||
@StackedApp(
|
@StackedApp(
|
||||||
routes: [
|
routes: [
|
||||||
MaterialRoute(page: HomeView),
|
MaterialRoute(page: Navigation),
|
||||||
MaterialRoute(page: AppSelectorView),
|
MaterialRoute(page: AppSelectorView),
|
||||||
MaterialRoute(page: PatchesSelectorView),
|
MaterialRoute(page: PatchesSelectorView),
|
||||||
MaterialRoute(page: InstallerView),
|
MaterialRoute(page: InstallerView),
|
||||||
@ -27,12 +29,16 @@ import 'package:stacked_themes/stacked_themes.dart';
|
|||||||
dependencies: [
|
dependencies: [
|
||||||
LazySingleton(classType: NavigationService),
|
LazySingleton(classType: NavigationService),
|
||||||
LazySingleton(classType: PatcherAPI),
|
LazySingleton(classType: PatcherAPI),
|
||||||
|
LazySingleton(classType: ManagerAPI),
|
||||||
|
LazySingleton(classType: RootAPI),
|
||||||
LazySingleton(classType: PatcherViewModel),
|
LazySingleton(classType: PatcherViewModel),
|
||||||
LazySingleton(classType: AppSelectorViewModel),
|
LazySingleton(classType: AppSelectorViewModel),
|
||||||
LazySingleton(classType: PatchesSelectorViewModel),
|
LazySingleton(classType: PatchesSelectorViewModel),
|
||||||
LazySingleton(classType: InstallerViewModel),
|
LazySingleton(classType: InstallerViewModel),
|
||||||
LazySingleton(
|
LazySingleton(
|
||||||
classType: ThemeService, resolveUsing: ThemeService.getInstance),
|
classType: ThemeService,
|
||||||
|
resolveUsing: ThemeService.getInstance,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
class AppSetup {}
|
class AppSetup {}
|
||||||
|
@ -4,13 +4,15 @@
|
|||||||
// StackedLocatorGenerator
|
// 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_core/stacked_core.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';
|
||||||
|
|
||||||
|
import '../services/manager_api.dart';
|
||||||
import '../services/patcher_api.dart';
|
import '../services/patcher_api.dart';
|
||||||
|
import '../services/root_api.dart';
|
||||||
import '../ui/views/app_selector/app_selector_viewmodel.dart';
|
import '../ui/views/app_selector/app_selector_viewmodel.dart';
|
||||||
import '../ui/views/installer/installer_viewmodel.dart';
|
import '../ui/views/installer/installer_viewmodel.dart';
|
||||||
import '../ui/views/patcher/patcher_viewmodel.dart';
|
import '../ui/views/patcher/patcher_viewmodel.dart';
|
||||||
@ -27,6 +29,8 @@ Future<void> setupLocator(
|
|||||||
// Register dependencies
|
// Register dependencies
|
||||||
locator.registerLazySingleton(() => NavigationService());
|
locator.registerLazySingleton(() => NavigationService());
|
||||||
locator.registerLazySingleton(() => PatcherAPI());
|
locator.registerLazySingleton(() => PatcherAPI());
|
||||||
|
locator.registerLazySingleton(() => ManagerAPI());
|
||||||
|
locator.registerLazySingleton(() => RootAPI());
|
||||||
locator.registerLazySingleton(() => PatcherViewModel());
|
locator.registerLazySingleton(() => PatcherViewModel());
|
||||||
locator.registerLazySingleton(() => AppSelectorViewModel());
|
locator.registerLazySingleton(() => AppSelectorViewModel());
|
||||||
locator.registerLazySingleton(() => PatchesSelectorViewModel());
|
locator.registerLazySingleton(() => PatchesSelectorViewModel());
|
||||||
|
@ -10,16 +10,16 @@ import 'package:flutter/material.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 '../main.dart';
|
||||||
import '../ui/views/app_selector/app_selector_view.dart';
|
import '../ui/views/app_selector/app_selector_view.dart';
|
||||||
import '../ui/views/contributors/contributors_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/installer/installer_view.dart';
|
||||||
import '../ui/views/patches_selector/patches_selector_view.dart';
|
import '../ui/views/patches_selector/patches_selector_view.dart';
|
||||||
import '../ui/views/root_checker/root_checker_view.dart';
|
import '../ui/views/root_checker/root_checker_view.dart';
|
||||||
import '../ui/views/settings/settings_view.dart';
|
import '../ui/views/settings/settings_view.dart';
|
||||||
|
|
||||||
class Routes {
|
class Routes {
|
||||||
static const String homeView = '/home-view';
|
static const String navigation = '/Navigation';
|
||||||
static const String appSelectorView = '/app-selector-view';
|
static const String appSelectorView = '/app-selector-view';
|
||||||
static const String patchesSelectorView = '/patches-selector-view';
|
static const String patchesSelectorView = '/patches-selector-view';
|
||||||
static const String installerView = '/installer-view';
|
static const String installerView = '/installer-view';
|
||||||
@ -27,7 +27,7 @@ class Routes {
|
|||||||
static const String contributorsView = '/contributors-view';
|
static const String contributorsView = '/contributors-view';
|
||||||
static const String rootCheckerView = '/root-checker-view';
|
static const String rootCheckerView = '/root-checker-view';
|
||||||
static const all = <String>{
|
static const all = <String>{
|
||||||
homeView,
|
navigation,
|
||||||
appSelectorView,
|
appSelectorView,
|
||||||
patchesSelectorView,
|
patchesSelectorView,
|
||||||
installerView,
|
installerView,
|
||||||
@ -41,7 +41,7 @@ class StackedRouter extends RouterBase {
|
|||||||
@override
|
@override
|
||||||
List<RouteDef> get routes => _routes;
|
List<RouteDef> get routes => _routes;
|
||||||
final _routes = <RouteDef>[
|
final _routes = <RouteDef>[
|
||||||
RouteDef(Routes.homeView, page: HomeView),
|
RouteDef(Routes.navigation, page: Navigation),
|
||||||
RouteDef(Routes.appSelectorView, page: AppSelectorView),
|
RouteDef(Routes.appSelectorView, page: AppSelectorView),
|
||||||
RouteDef(Routes.patchesSelectorView, page: PatchesSelectorView),
|
RouteDef(Routes.patchesSelectorView, page: PatchesSelectorView),
|
||||||
RouteDef(Routes.installerView, page: InstallerView),
|
RouteDef(Routes.installerView, page: InstallerView),
|
||||||
@ -52,9 +52,9 @@ class StackedRouter extends RouterBase {
|
|||||||
@override
|
@override
|
||||||
Map<Type, StackedRouteFactory> get pagesMap => _pagesMap;
|
Map<Type, StackedRouteFactory> get pagesMap => _pagesMap;
|
||||||
final _pagesMap = <Type, StackedRouteFactory>{
|
final _pagesMap = <Type, StackedRouteFactory>{
|
||||||
HomeView: (data) {
|
Navigation: (data) {
|
||||||
return MaterialPageRoute<dynamic>(
|
return MaterialPageRoute<dynamic>(
|
||||||
builder: (context) => const HomeView(),
|
builder: (context) => const Navigation(),
|
||||||
settings: data,
|
settings: data,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -115,7 +115,7 @@ class InstallerViewArguments {
|
|||||||
/// *************************************************************************
|
/// *************************************************************************
|
||||||
|
|
||||||
extension NavigatorStateExtension on NavigationService {
|
extension NavigatorStateExtension on NavigationService {
|
||||||
Future<dynamic> navigateToHomeView({
|
Future<dynamic> navigateToNavigation({
|
||||||
int? routerId,
|
int? routerId,
|
||||||
bool preventDuplicates = true,
|
bool preventDuplicates = true,
|
||||||
Map<String, String>? parameters,
|
Map<String, String>? parameters,
|
||||||
@ -123,7 +123,7 @@ extension NavigatorStateExtension on NavigationService {
|
|||||||
transition,
|
transition,
|
||||||
}) async {
|
}) async {
|
||||||
return navigateTo(
|
return navigateTo(
|
||||||
Routes.homeView,
|
Routes.navigation,
|
||||||
id: routerId,
|
id: routerId,
|
||||||
preventDuplicates: preventDuplicates,
|
preventDuplicates: preventDuplicates,
|
||||||
parameters: parameters,
|
parameters: parameters,
|
||||||
|
@ -8,7 +8,9 @@ import 'package:revanced_manager/main_viewmodel.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/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';
|
||||||
@ -37,7 +39,20 @@ class MyApp extends StatelessWidget {
|
|||||||
themeMode: themeMode,
|
themeMode: themeMode,
|
||||||
navigatorKey: StackedService.navigatorKey,
|
navigatorKey: StackedService.navigatorKey,
|
||||||
onGenerateRoute: StackedRouter().onGenerateRoute,
|
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: [
|
localizationsDelegates: [
|
||||||
FlutterI18nDelegate(
|
FlutterI18nDelegate(
|
||||||
translationLoader: FileTranslationLoader(
|
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 {
|
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
|
@lazySingleton
|
||||||
class ManagerAPI {
|
class ManagerAPI {
|
||||||
Dio dio = Dio();
|
final Dio dio = Dio();
|
||||||
GithubAPI githubAPI = GithubAPI();
|
final GithubAPI githubAPI = GithubAPI();
|
||||||
|
|
||||||
Future<String?> getPath() async {
|
Future<String?> getPath() async {
|
||||||
final path = await p.getApplicationSupportDirectory();
|
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: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/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/models/application_info.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/services/github_api.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/ui/views/installer/installer_viewmodel.dart';
|
||||||
import 'package:revanced_manager/utils/string.dart';
|
import 'package:revanced_manager/utils/string.dart';
|
||||||
import 'package:share_extend/share_extend.dart';
|
import 'package:share_extend/share_extend.dart';
|
||||||
@ -17,9 +18,10 @@ import 'package:share_extend/share_extend.dart';
|
|||||||
class PatcherAPI {
|
class PatcherAPI {
|
||||||
static const platform = MethodChannel('app.revanced.manager/patcher');
|
static const platform = MethodChannel('app.revanced.manager/patcher');
|
||||||
final GithubAPI githubAPI = GithubAPI();
|
final GithubAPI githubAPI = GithubAPI();
|
||||||
|
final RootAPI rootAPI = RootAPI();
|
||||||
final List<ApplicationWithIcon> _filteredPackages = [];
|
final List<ApplicationWithIcon> _filteredPackages = [];
|
||||||
final Map<String, List<Patch>> _filteredPatches = <String, List<Patch>>{};
|
final Map<String, List<Patch>> _filteredPatches = <String, List<Patch>>{};
|
||||||
bool isRoot = false;
|
Directory? _tmpDir;
|
||||||
Directory? _workDir;
|
Directory? _workDir;
|
||||||
Directory? _cacheDir;
|
Directory? _cacheDir;
|
||||||
File? _patchBundleFile;
|
File? _patchBundleFile;
|
||||||
@ -89,7 +91,9 @@ class PatcherAPI {
|
|||||||
return _filteredPackages;
|
return _filteredPackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<Patch>?> getFilteredPatches(ApplicationInfo? selectedApp) async {
|
Future<List<Patch>?> getFilteredPatches(
|
||||||
|
PatchedApplication? selectedApp,
|
||||||
|
) async {
|
||||||
if (_patchBundleFile != null && selectedApp != null) {
|
if (_patchBundleFile != null && selectedApp != null) {
|
||||||
if (_filteredPatches[selectedApp.packageName] == null ||
|
if (_filteredPatches[selectedApp.packageName] == null ||
|
||||||
_filteredPatches[selectedApp.packageName]!.isEmpty) {
|
_filteredPatches[selectedApp.packageName]!.isEmpty) {
|
||||||
@ -146,8 +150,8 @@ class PatcherAPI {
|
|||||||
try {
|
try {
|
||||||
_integrations = await downloadIntegrations();
|
_integrations = await downloadIntegrations();
|
||||||
if (_integrations != null) {
|
if (_integrations != null) {
|
||||||
Directory tmpDir = await getTemporaryDirectory();
|
_tmpDir = await getTemporaryDirectory();
|
||||||
_workDir = tmpDir.createTempSync('tmp-');
|
_workDir = _tmpDir!.createTempSync('tmp-');
|
||||||
_inputFile = File('${_workDir!.path}/base.apk');
|
_inputFile = File('${_workDir!.path}/base.apk');
|
||||||
_patchedFile = File('${_workDir!.path}/patched.apk');
|
_patchedFile = File('${_workDir!.path}/patched.apk');
|
||||||
_outFile = File('${_workDir!.path}/out.apk');
|
_outFile = File('${_workDir!.path}/out.apk');
|
||||||
@ -256,15 +260,19 @@ class PatcherAPI {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> installPatchedFile() async {
|
Future<bool> installPatchedFile(PatchedApplication patchedApp) async {
|
||||||
if (_outFile != null) {
|
if (_outFile != null) {
|
||||||
try {
|
try {
|
||||||
if (isRoot) {
|
if (patchedApp.isRooted && !patchedApp.isFromStorage) {
|
||||||
// TBD
|
return rootAPI.installApp(
|
||||||
|
patchedApp.packageName,
|
||||||
|
patchedApp.apkFilePath,
|
||||||
|
_outFile!.path,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
await AppInstaller.installApk(_outFile!.path);
|
await AppInstaller.installApk(_outFile!.path);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
} on Exception {
|
} on Exception {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -280,11 +288,11 @@ class PatcherAPI {
|
|||||||
|
|
||||||
bool sharePatchedFile(String appName, String version) {
|
bool sharePatchedFile(String appName, String version) {
|
||||||
if (_outFile != null) {
|
if (_outFile != null) {
|
||||||
String path = _outFile!.parent.path;
|
String path = _tmpDir!.path;
|
||||||
String prefix = appName.toLowerCase().replaceAll(' ', '-');
|
String prefix = appName.toLowerCase().replaceAll(' ', '-');
|
||||||
String sharePath = '$path/$prefix-revanced_v$version.apk';
|
String sharePath = '$path/$prefix-revanced_v$version.apk';
|
||||||
File share = _outFile!.copySync(sharePath);
|
File share = _outFile!.copySync(sharePath);
|
||||||
ShareExtend.share(share.path, "file");
|
ShareExtend.share(share.path, 'file');
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
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:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:package_archive_info/package_archive_info.dart';
|
import 'package:package_archive_info/package_archive_info.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.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/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:revanced_manager/ui/views/patches_selector/patches_selector_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';
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
class AppSelectorViewModel extends BaseViewModel {
|
class AppSelectorViewModel extends BaseViewModel {
|
||||||
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
||||||
|
bool isRooted = false;
|
||||||
|
bool isFromStorage = false;
|
||||||
List<ApplicationWithIcon> apps = [];
|
List<ApplicationWithIcon> apps = [];
|
||||||
ApplicationInfo? selectedApp;
|
PatchedApplication? selectedApp;
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
await getApps();
|
await getApps();
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
isRooted = prefs.getBool('isRooted') ?? false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,12 +32,15 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||||||
apps = await patcherAPI.getFilteredInstalledApps();
|
apps = await patcherAPI.getFilteredInstalledApps();
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectApp(ApplicationWithIcon application) {
|
void selectApp(ApplicationWithIcon application) async {
|
||||||
ApplicationInfo app = ApplicationInfo(
|
isFromStorage = false;
|
||||||
|
PatchedApplication app = PatchedApplication(
|
||||||
name: application.appName,
|
name: application.appName,
|
||||||
packageName: application.packageName,
|
packageName: application.packageName,
|
||||||
version: application.versionName!,
|
version: application.versionName!,
|
||||||
apkFilePath: application.apkFilePath,
|
apkFilePath: application.apkFilePath,
|
||||||
|
isRooted: isRooted,
|
||||||
|
isFromStorage: isFromStorage,
|
||||||
);
|
);
|
||||||
locator<AppSelectorViewModel>().selectedApp = app;
|
locator<AppSelectorViewModel>().selectedApp = app;
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
||||||
@ -41,6 +49,7 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> selectAppFromStorage(BuildContext context) async {
|
Future<void> selectAppFromStorage(BuildContext context) async {
|
||||||
|
isFromStorage = true;
|
||||||
try {
|
try {
|
||||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
@ -50,11 +59,13 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||||||
File apkFile = File(result.files.single.path!);
|
File apkFile = File(result.files.single.path!);
|
||||||
PackageArchiveInfo? packageArchiveInfo =
|
PackageArchiveInfo? packageArchiveInfo =
|
||||||
await PackageArchiveInfo.fromPath(apkFile.path);
|
await PackageArchiveInfo.fromPath(apkFile.path);
|
||||||
ApplicationInfo app = ApplicationInfo(
|
PatchedApplication app = PatchedApplication(
|
||||||
name: packageArchiveInfo.appName,
|
name: packageArchiveInfo.appName,
|
||||||
packageName: packageArchiveInfo.packageName,
|
packageName: packageArchiveInfo.packageName,
|
||||||
version: packageArchiveInfo.version,
|
version: packageArchiveInfo.version,
|
||||||
apkFilePath: result.files.single.path!,
|
apkFilePath: result.files.single.path!,
|
||||||
|
isRooted: isRooted,
|
||||||
|
isFromStorage: isFromStorage,
|
||||||
);
|
);
|
||||||
locator<AppSelectorViewModel>().selectedApp = app;
|
locator<AppSelectorViewModel>().selectedApp = app;
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:revanced_manager/app/app.locator.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/patch.dart';
|
||||||
|
import 'package:revanced_manager/models/patched_application.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_viewmodel.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/patcher/patcher_viewmodel.dart';
|
||||||
@ -38,7 +38,8 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
|
|
||||||
Future<void> runPatcher() async {
|
Future<void> runPatcher() async {
|
||||||
updateProgress(0.0);
|
updateProgress(0.0);
|
||||||
ApplicationInfo? selectedApp = locator<AppSelectorViewModel>().selectedApp;
|
PatchedApplication? selectedApp =
|
||||||
|
locator<AppSelectorViewModel>().selectedApp;
|
||||||
if (selectedApp != null) {
|
if (selectedApp != null) {
|
||||||
String apkFilePath = selectedApp.apkFilePath;
|
String apkFilePath = selectedApp.apkFilePath;
|
||||||
List<Patch> selectedPatches =
|
List<Patch> selectedPatches =
|
||||||
@ -111,11 +112,16 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installResult() async {
|
void installResult() async {
|
||||||
await locator<PatcherAPI>().installPatchedFile();
|
PatchedApplication? selectedApp =
|
||||||
|
locator<AppSelectorViewModel>().selectedApp;
|
||||||
|
if (selectedApp != null) {
|
||||||
|
await locator<PatcherAPI>().installPatchedFile(selectedApp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void shareResult() {
|
void shareResult() {
|
||||||
ApplicationInfo? selectedApp = locator<AppSelectorViewModel>().selectedApp;
|
PatchedApplication? selectedApp =
|
||||||
|
locator<AppSelectorViewModel>().selectedApp;
|
||||||
if (selectedApp != null) {
|
if (selectedApp != null) {
|
||||||
locator<PatcherAPI>().sharePatchedFile(
|
locator<PatcherAPI>().sharePatchedFile(
|
||||||
selectedApp.name,
|
selectedApp.name,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:revanced_manager/app/app.locator.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/patch.dart';
|
||||||
|
import 'package:revanced_manager/models/patched_application.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_viewmodel.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/patcher/patcher_viewmodel.dart';
|
||||||
@ -18,7 +18,7 @@ class PatchesSelectorViewModel extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getPatches() async {
|
Future<void> getPatches() async {
|
||||||
ApplicationInfo? app = locator<AppSelectorViewModel>().selectedApp;
|
PatchedApplication? app = locator<AppSelectorViewModel>().selectedApp;
|
||||||
patches = await patcherAPI.getFilteredPatches(app);
|
patches = await patcherAPI.getFilteredPatches(app);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_i18n/widgets/I18nText.dart';
|
import 'package:flutter_i18n/widgets/I18nText.dart';
|
||||||
import 'package:google_fonts/google_fonts.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/views/root_checker/root_checker_viewmodel.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/magisk_button.dart';
|
import 'package:revanced_manager/ui/widgets/magisk_button.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
@ -11,8 +12,9 @@ class RootCheckerView extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ViewModelBuilder<RootCheckerViewModel>.reactive(
|
return ViewModelBuilder<RootCheckerViewModel>.reactive(
|
||||||
|
disposeViewModel: false,
|
||||||
onModelReady: (model) => model.initialize,
|
onModelReady: (model) => model.initialize,
|
||||||
viewModelBuilder: () => RootCheckerViewModel(),
|
viewModelBuilder: () => locator<RootCheckerViewModel>(),
|
||||||
builder: (context, model, child) => Scaffold(
|
builder: (context, model, child) => Scaffold(
|
||||||
floatingActionButton: Column(
|
floatingActionButton: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
@ -64,15 +66,20 @@ class RootCheckerView extends StatelessWidget {
|
|||||||
const SizedBox(height: 170),
|
const SizedBox(height: 170),
|
||||||
MagiskButton(
|
MagiskButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
model.getMagiskPermissions();
|
model
|
||||||
Future.delayed(const Duration(seconds: 5), () {
|
.getMagiskPermissions()
|
||||||
model.checkRoot();
|
.then((value) => model.checkRoot());
|
||||||
});
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Text(
|
I18nText(
|
||||||
"Magisk permission granted: ${model.isRooted.toString()}",
|
'rootCheckerView.grantedPermission',
|
||||||
style: GoogleFonts.poppins(),
|
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.locator.dart';
|
||||||
import 'package:revanced_manager/app/app.router.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:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:root/root.dart';
|
import 'package:root/root.dart';
|
||||||
@ -18,21 +16,25 @@ class RootCheckerViewModel extends BaseViewModel {
|
|||||||
|
|
||||||
Future<void> checkRoot() async {
|
Future<void> checkRoot() async {
|
||||||
isRooted = await Root.isRooted();
|
isRooted = await Root.isRooted();
|
||||||
|
if (isRooted == true) {
|
||||||
|
navigateToHome();
|
||||||
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getMagiskPermissions() async {
|
Future<bool> getMagiskPermissions() async {
|
||||||
if (isRooted == true) {
|
try {
|
||||||
Fluttertoast.showToast(msg: 'Magisk permission already granted!');
|
await Root.exec(cmd: 'cat /proc/version');
|
||||||
|
} on Exception {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
await Root.exec(cmd: "adb shell su -c exit");
|
return true;
|
||||||
notifyListeners();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> navigateToHome() async {
|
Future<void> navigateToHome() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
prefs.setBool('showHome', true);
|
prefs.setBool('isRooted', isRooted!);
|
||||||
_navigationService.navigateTo(Routes.homeView);
|
_navigationService.navigateTo(Routes.navigation);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ class LatestCommitCard extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LatestCommitCardState extends State<LatestCommitCard> {
|
class _LatestCommitCardState extends State<LatestCommitCard> {
|
||||||
GithubAPI githubAPI = GithubAPI();
|
final GithubAPI githubAPI = GithubAPI();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user