mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-05-02 23:04:25 +02:00
fix: root installation and foreground task and improve installer a bit
This commit is contained in:
parent
8fd942a808
commit
5c71930ec1
@ -4,10 +4,11 @@
|
|||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="ReVanced Manager"
|
android:label="ReVanced Manager"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
@ -34,7 +35,6 @@
|
|||||||
<meta-data
|
<meta-data
|
||||||
android:name="flutterEmbedding"
|
android:name="flutterEmbedding"
|
||||||
android:value="2" />
|
android:value="2" />
|
||||||
<service android:name="com.pravera.flutter_foreground_task.service.ForegroundService" />
|
|
||||||
<provider
|
<provider
|
||||||
android:name="androidx.core.content.FileProvider"
|
android:name="androidx.core.content.FileProvider"
|
||||||
android:authorities="${applicationId}.fileProvider"
|
android:authorities="${applicationId}.fileProvider"
|
||||||
|
@ -38,9 +38,8 @@
|
|||||||
},
|
},
|
||||||
"patchSelectorCard": {
|
"patchSelectorCard": {
|
||||||
"widgetTitle": "Select patches",
|
"widgetTitle": "Select patches",
|
||||||
"widgetFirstSubtitle": "Select an application first.",
|
"widgetSubtitle": "Select an application first.",
|
||||||
"widgetSecondSubtitle": "No patches selected.",
|
"widgetEmptySubtitle": "No patches selected."
|
||||||
"widgetThirdSubtitle": "{selected} patch(es) selected."
|
|
||||||
},
|
},
|
||||||
"appSelectorView": {
|
"appSelectorView": {
|
||||||
"searchBarHint": "Search applications",
|
"searchBarHint": "Search applications",
|
||||||
@ -53,8 +52,8 @@
|
|||||||
},
|
},
|
||||||
"installerView": {
|
"installerView": {
|
||||||
"widgetTitle": "Installer",
|
"widgetTitle": "Installer",
|
||||||
"installButton": "Install",
|
"fabInstallButton": "Install",
|
||||||
"shareButton": "Share"
|
"fabOpenButton": "Open"
|
||||||
},
|
},
|
||||||
"settingsView": {
|
"settingsView": {
|
||||||
"widgetTitle": "Settings",
|
"widgetTitle": "Settings",
|
||||||
|
@ -40,13 +40,12 @@ class GithubAPI {
|
|||||||
|
|
||||||
Future<List<Contributor>> getContributors(String org, repoName) async {
|
Future<List<Contributor>> getContributors(String org, repoName) async {
|
||||||
try {
|
try {
|
||||||
var contributors = await github.repositories.listContributors(
|
var contributors = github.repositories.listContributors(
|
||||||
RepositorySlug(org, repoName),
|
RepositorySlug(org, repoName),
|
||||||
);
|
);
|
||||||
return contributors.toList();
|
return contributors.toList();
|
||||||
} on Exception {
|
} on Exception {
|
||||||
print(Exception);
|
return List.empty();
|
||||||
return [];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,8 @@ class PatcherAPI {
|
|||||||
|
|
||||||
Future<dynamic> handlePlatformChannelMethods() async {
|
Future<dynamic> handlePlatformChannelMethods() async {
|
||||||
platform.setMethodCallHandler((call) async {
|
platform.setMethodCallHandler((call) async {
|
||||||
switch (call.method) {
|
if (call.method == 'updateInstallerLog' && call.arguments != null) {
|
||||||
case 'updateInstallerLog':
|
locator<InstallerViewModel>().addLog(call.arguments);
|
||||||
var message = call.arguments<String>('message');
|
|
||||||
locator<InstallerViewModel>().addLog(message);
|
|
||||||
return 'OK';
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -298,4 +295,17 @@ class PatcherAPI {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> checkOldPatch(PatchedApplication patchedApp) async {
|
||||||
|
if (patchedApp.isRooted) {
|
||||||
|
return await rootAPI.checkApp(patchedApp.packageName);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> deleteOldPatch(PatchedApplication patchedApp) async {
|
||||||
|
if (patchedApp.isRooted) {
|
||||||
|
await rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
import 'package:root/root.dart';
|
import 'package:root/root.dart';
|
||||||
|
|
||||||
@ -9,17 +7,35 @@ class RootAPI {
|
|||||||
final String postFsDataDirPath = "/data/adb/post-fs-data.d";
|
final String postFsDataDirPath = "/data/adb/post-fs-data.d";
|
||||||
final String serviceDDirPath = "/data/adb/service.d";
|
final String serviceDDirPath = "/data/adb/service.d";
|
||||||
|
|
||||||
bool deleteApp(String packageName) {
|
Future<bool> checkApp(String packageName) async {
|
||||||
try {
|
try {
|
||||||
File('$managerDirPath/$packageName.apk').deleteSync();
|
String? res = await Root.exec(
|
||||||
File('$serviceDDirPath/$packageName.sh').deleteSync();
|
cmd: 'ls -la "$managerDirPath/$packageName"',
|
||||||
File('$postFsDataDirPath/$packageName.sh').deleteSync();
|
);
|
||||||
return true;
|
return res != null && res.isNotEmpty;
|
||||||
} on Exception {
|
} on Exception {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deleteApp(String packageName, String originalFilePath) async {
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'am force-stop "$packageName"',
|
||||||
|
);
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'su -mm -c "umount -l $originalFilePath"',
|
||||||
|
);
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'rm -rf "$managerDirPath/$packageName"',
|
||||||
|
);
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'rm -rf "$serviceDDirPath/$packageName.sh"',
|
||||||
|
);
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'rm -rf "$postFsDataDirPath/$packageName.sh"',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<bool> installApp(
|
Future<bool> installApp(
|
||||||
String packageName,
|
String packageName,
|
||||||
String originalFilePath,
|
String originalFilePath,
|
||||||
@ -27,21 +43,50 @@ class RootAPI {
|
|||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'mkdir "$managerDirPath"',
|
cmd: 'mkdir -p "$managerDirPath/$packageName"',
|
||||||
);
|
);
|
||||||
String newPatchedFilePath = '$managerDirPath/$packageName.apk';
|
installServiceDScript(packageName);
|
||||||
installServiceDScript(
|
installPostFsDataScript(packageName);
|
||||||
packageName,
|
installApk(packageName, patchedFilePath);
|
||||||
originalFilePath,
|
mountApk(packageName, originalFilePath, patchedFilePath);
|
||||||
newPatchedFilePath,
|
return true;
|
||||||
);
|
} on Exception {
|
||||||
installPostFsDataScript(
|
return false;
|
||||||
packageName,
|
}
|
||||||
originalFilePath,
|
}
|
||||||
newPatchedFilePath,
|
|
||||||
|
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'
|
||||||
|
'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';
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
||||||
);
|
);
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'cp $patchedFilePath $newPatchedFilePath',
|
cmd: 'chmod 744 "$scriptFilePath"',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> installPostFsDataScript(String packageName) async {
|
||||||
|
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';
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
||||||
|
);
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'chmod 744 "$scriptFilePath"',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> installApk(String packageName, String patchedFilePath) async {
|
||||||
|
String newPatchedFilePath = '$managerDirPath/$packageName/base.apk';
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
|
||||||
);
|
);
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'chmod 644 "$newPatchedFilePath"',
|
cmd: 'chmod 644 "$newPatchedFilePath"',
|
||||||
@ -52,41 +97,22 @@ class RootAPI {
|
|||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'chcon u:object_r:apk_data_file:s0 "$newPatchedFilePath"',
|
cmd: 'chcon u:object_r:apk_data_file:s0 "$newPatchedFilePath"',
|
||||||
);
|
);
|
||||||
return true;
|
|
||||||
} on Exception {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> installServiceDScript(
|
Future<void> mountApk(
|
||||||
String packageName,
|
String packageName,
|
||||||
String originalFilePath,
|
String originalFilePath,
|
||||||
String patchedFilePath,
|
String patchedFilePath,
|
||||||
) async {
|
) async {
|
||||||
String content = '#!/system/bin/sh\n'
|
String newPatchedFilePath = '$managerDirPath/$packageName/base.apk';
|
||||||
'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';
|
|
||||||
String scriptFilePath = '$serviceDDirPath/$packageName.sh';
|
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'echo "$content" > "$scriptFilePath"',
|
cmd: 'am force-stop "$packageName"',
|
||||||
);
|
);
|
||||||
await Root.exec(cmd: 'chmod 744 "$scriptFilePath"');
|
|
||||||
}
|
|
||||||
|
|
||||||
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';
|
|
||||||
String scriptFilePath = '$postFsDataDirPath/$packageName.sh';
|
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'echo "$content" > "$scriptFilePath"',
|
cmd: 'su -mm -c "umount -l $originalFilePath"',
|
||||||
|
);
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'su -mm -c "mount -o bind $newPatchedFilePath $originalFilePath"',
|
||||||
);
|
);
|
||||||
await Root.exec(cmd: 'chmod 744 $scriptFilePath');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
|
|
||||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.dart';
|
import 'package:revanced_manager/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
|
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
|
||||||
@ -18,35 +17,34 @@ class InstallerView extends StatelessWidget {
|
|||||||
disposeViewModel: false,
|
disposeViewModel: false,
|
||||||
onModelReady: (model) => model.initialize(),
|
onModelReady: (model) => model.initialize(),
|
||||||
viewModelBuilder: () => locator<InstallerViewModel>(),
|
viewModelBuilder: () => locator<InstallerViewModel>(),
|
||||||
builder: (context, model, child) => WillStartForegroundTask(
|
builder: (context, model, child) => WillPopScope(
|
||||||
onWillStart: () async => model.isPatching,
|
|
||||||
androidNotificationOptions: AndroidNotificationOptions(
|
|
||||||
channelId: 'revanced-patcher-patching',
|
|
||||||
channelName: 'Patching',
|
|
||||||
channelDescription: 'This notification appears when the patching '
|
|
||||||
'foreground service is running.',
|
|
||||||
channelImportance: NotificationChannelImportance.LOW,
|
|
||||||
priority: NotificationPriority.LOW,
|
|
||||||
),
|
|
||||||
notificationTitle: 'Patching',
|
|
||||||
notificationText: 'ReVanced Manager is patching',
|
|
||||||
callback: () => {},
|
|
||||||
child: WillPopScope(
|
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
floatingActionButton: Visibility(
|
||||||
|
visible: model.showButtons,
|
||||||
|
child: FloatingActionButton.extended(
|
||||||
|
onPressed: () =>
|
||||||
|
model.isInstalled ? model.openApp() : model.installResult(),
|
||||||
|
label: I18nText(model.isInstalled
|
||||||
|
? 'installerView.fabOpenButton'
|
||||||
|
: 'installerView.fabInstallButton'),
|
||||||
|
icon: model.isInstalled
|
||||||
|
? const Icon(Icons.open_in_new)
|
||||||
|
: const Icon(Icons.install_mobile),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: LayoutBuilder(
|
child: SingleChildScrollView(
|
||||||
builder: (context, constraints) => SingleChildScrollView(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||||
controller: _controller,
|
controller: _controller,
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
minWidth: constraints.maxWidth,
|
|
||||||
minHeight: constraints.maxHeight,
|
|
||||||
),
|
|
||||||
child: IntrinsicHeight(
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
I18nText(
|
I18nText(
|
||||||
'installerView.widgetTitle',
|
'installerView.widgetTitle',
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -54,6 +52,15 @@ class InstallerView extends StatelessWidget {
|
|||||||
style: Theme.of(context).textTheme.headline5,
|
style: Theme.of(context).textTheme.headline5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: model.showButtons,
|
||||||
|
child: IconButton(
|
||||||
|
icon: const Icon(Icons.share),
|
||||||
|
onPressed: () => model.shareResult(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
vertical: 16.0,
|
vertical: 16.0,
|
||||||
@ -81,57 +88,9 @@ class InstallerView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
|
||||||
Visibility(
|
|
||||||
visible: model.showButtons,
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: MaterialButton(
|
|
||||||
textColor: Colors.white,
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.secondary,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12,
|
|
||||||
horizontal: 8,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
onPressed: () => model.installResult(),
|
|
||||||
child: I18nText(
|
|
||||||
'installerView.installButton',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Expanded(
|
|
||||||
child: MaterialButton(
|
|
||||||
textColor: Colors.white,
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.secondary,
|
|
||||||
padding: const EdgeInsets.symmetric(
|
|
||||||
vertical: 12,
|
|
||||||
horizontal: 8,
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
onPressed: () => model.shareResult(),
|
|
||||||
child: I18nText(
|
|
||||||
'installerView.shareButton',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
@ -142,7 +101,6 @@ class InstallerView extends StatelessWidget {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'package:device_apps/device_apps.dart';
|
||||||
|
import 'package:flutter_background/flutter_background.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.dart';
|
import 'package:revanced_manager/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/models/patch.dart';
|
import 'package:revanced_manager/models/patch.dart';
|
||||||
import 'package:revanced_manager/models/patched_application.dart';
|
import 'package:revanced_manager/models/patched_application.dart';
|
||||||
@ -11,9 +13,22 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
double? progress = 0.2;
|
double? progress = 0.2;
|
||||||
String logs = '';
|
String logs = '';
|
||||||
bool isPatching = false;
|
bool isPatching = false;
|
||||||
|
bool isInstalled = false;
|
||||||
bool showButtons = false;
|
bool showButtons = false;
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
|
await FlutterBackground.initialize(
|
||||||
|
androidConfig: const FlutterBackgroundAndroidConfig(
|
||||||
|
notificationTitle: 'Patching',
|
||||||
|
notificationText: 'ReVanced Manager is patching',
|
||||||
|
notificationImportance: AndroidNotificationImportance.Default,
|
||||||
|
notificationIcon: AndroidResource(
|
||||||
|
name: 'ic_launcher_foreground',
|
||||||
|
defType: 'drawable',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
await FlutterBackground.enableBackgroundExecution();
|
||||||
await locator<PatcherAPI>().handlePlatformChannelMethods();
|
await locator<PatcherAPI>().handlePlatformChannelMethods();
|
||||||
runPatcher();
|
runPatcher();
|
||||||
}
|
}
|
||||||
@ -28,6 +43,7 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
|
|
||||||
void updateProgress(double value) {
|
void updateProgress(double value) {
|
||||||
progress = value;
|
progress = value;
|
||||||
|
isInstalled = false;
|
||||||
isPatching = progress == 1.0 ? false : true;
|
isPatching = progress == 1.0 ? false : true;
|
||||||
showButtons = progress == 1.0 ? true : false;
|
showButtons = progress == 1.0 ? true : false;
|
||||||
if (progress == 0.0) {
|
if (progress == 0.0) {
|
||||||
@ -46,6 +62,18 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
locator<PatchesSelectorViewModel>().selectedPatches;
|
locator<PatchesSelectorViewModel>().selectedPatches;
|
||||||
if (selectedPatches.isNotEmpty) {
|
if (selectedPatches.isNotEmpty) {
|
||||||
addLog('Initializing installer...');
|
addLog('Initializing installer...');
|
||||||
|
if (selectedApp.isRooted) {
|
||||||
|
addLog('Checking if an old patched version exists...');
|
||||||
|
bool oldExists =
|
||||||
|
await locator<PatcherAPI>().checkOldPatch(selectedApp);
|
||||||
|
addLog('Done');
|
||||||
|
if (oldExists) {
|
||||||
|
addLog('Deleting old patched version...');
|
||||||
|
await locator<PatcherAPI>().deleteOldPatch(selectedApp);
|
||||||
|
addLog('Done');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addLog('Creating working directory...');
|
||||||
bool? isSuccess = await locator<PatcherAPI>().initPatcher();
|
bool? isSuccess = await locator<PatcherAPI>().initPatcher();
|
||||||
if (isSuccess != null && isSuccess) {
|
if (isSuccess != null && isSuccess) {
|
||||||
addLog('Done');
|
addLog('Done');
|
||||||
@ -108,6 +136,7 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
} else {
|
} else {
|
||||||
addLog('No app selected! Aborting...');
|
addLog('No app selected! Aborting...');
|
||||||
}
|
}
|
||||||
|
await FlutterBackground.disableBackgroundExecution();
|
||||||
isPatching = false;
|
isPatching = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,9 +147,8 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
addLog(selectedApp.isRooted
|
addLog(selectedApp.isRooted
|
||||||
? 'Installing patched file using root method...'
|
? 'Installing patched file using root method...'
|
||||||
: 'Installing patched file using nonroot method...');
|
: 'Installing patched file using nonroot method...');
|
||||||
bool isSucess =
|
isInstalled = await locator<PatcherAPI>().installPatchedFile(selectedApp);
|
||||||
await locator<PatcherAPI>().installPatchedFile(selectedApp);
|
if (isInstalled) {
|
||||||
if (isSucess) {
|
|
||||||
addLog('Done');
|
addLog('Done');
|
||||||
} else {
|
} else {
|
||||||
addLog('An error occurred! Aborting...');
|
addLog('An error occurred! Aborting...');
|
||||||
@ -139,10 +167,18 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanWorkplace() {
|
Future<void> cleanWorkplace() async {
|
||||||
locator<PatcherAPI>().cleanPatcher();
|
locator<PatcherAPI>().cleanPatcher();
|
||||||
locator<AppSelectorViewModel>().selectedApp = null;
|
locator<AppSelectorViewModel>().selectedApp = null;
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
||||||
locator<PatcherViewModel>().notifyListeners();
|
locator<PatcherViewModel>().notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void openApp() {
|
||||||
|
PatchedApplication? selectedApp =
|
||||||
|
locator<AppSelectorViewModel>().selectedApp;
|
||||||
|
if (selectedApp != null) {
|
||||||
|
DeviceApps.openApp(selectedApp.packageName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,13 @@ class PatchesSelectorViewModel extends BaseViewModel {
|
|||||||
void selectPatches(List<PatchItem> patchItems) {
|
void selectPatches(List<PatchItem> patchItems) {
|
||||||
selectedPatches.clear();
|
selectedPatches.clear();
|
||||||
if (patches != null) {
|
if (patches != null) {
|
||||||
for (PatchItem patch in patchItems) {
|
for (PatchItem item in patchItems) {
|
||||||
if (patch.isSelected) {
|
if (item.isSelected) {
|
||||||
selectedPatches.add(
|
Patch patch =
|
||||||
patches!.firstWhere((element) => element.name == patch.name),
|
patches!.firstWhere((element) => element.name == item.name);
|
||||||
);
|
if (!selectedPatches.contains(patch)) {
|
||||||
|
selectedPatches.add(patch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@ class PatchSelectorCard extends StatelessWidget {
|
|||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
locator<AppSelectorViewModel>().selectedApp == null
|
locator<AppSelectorViewModel>().selectedApp == null
|
||||||
? I18nText(
|
? I18nText(
|
||||||
'patchSelectorCard.widgetFirstSubtitle',
|
'patchSelectorCard.widgetSubtitle',
|
||||||
child: Text(
|
child: Text(
|
||||||
'',
|
'',
|
||||||
style: robotoTextStyle,
|
style: robotoTextStyle,
|
||||||
@ -51,24 +51,18 @@ class PatchSelectorCard extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
: locator<PatchesSelectorViewModel>().selectedPatches.isEmpty
|
: locator<PatchesSelectorViewModel>().selectedPatches.isEmpty
|
||||||
? I18nText(
|
? I18nText(
|
||||||
'patchSelectorCard.widgetSecondSubtitle',
|
'patchSelectorCard.widgetEmptySubtitle',
|
||||||
child: Text(
|
child: Text(
|
||||||
'',
|
'',
|
||||||
style: robotoTextStyle,
|
style: robotoTextStyle,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: I18nText(
|
: Text(
|
||||||
'patchSelectorCard.widgetThirdSubtitle',
|
locator<PatchesSelectorViewModel>()
|
||||||
translationParams: {
|
|
||||||
'selected': locator<PatchesSelectorViewModel>()
|
|
||||||
.selectedPatches
|
.selectedPatches
|
||||||
.length
|
.map((e) => e.simpleName)
|
||||||
.toString()
|
.toList()
|
||||||
},
|
.join('\n'),
|
||||||
child: Text(
|
|
||||||
'',
|
|
||||||
style: robotoTextStyle,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -17,8 +17,8 @@ dependencies:
|
|||||||
file_picker: ^5.0.1
|
file_picker: ^5.0.1
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
flutter_background: ^1.1.0
|
||||||
flutter_cache_manager: ^3.3.0
|
flutter_cache_manager: ^3.3.0
|
||||||
flutter_foreground_task: ^3.8.1
|
|
||||||
flutter_i18n: ^0.32.4
|
flutter_i18n: ^0.32.4
|
||||||
flutter_svg: ^1.1.1+1
|
flutter_svg: ^1.1.1+1
|
||||||
fluttertoast: ^8.0.9
|
fluttertoast: ^8.0.9
|
||||||
|
Loading…
x
Reference in New Issue
Block a user