mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 05:54:26 +02:00
fix: Improve root installations management to fix patching of already patched apps
This commit is contained in:
parent
779b659108
commit
d613cece15
@ -28,13 +28,21 @@ class MainActivity : FlutterActivity() {
|
|||||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||||
super.configureFlutterEngine(flutterEngine)
|
super.configureFlutterEngine(flutterEngine)
|
||||||
val mainChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, PATCHER_CHANNEL)
|
val mainChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, PATCHER_CHANNEL)
|
||||||
installerChannel =
|
installerChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, INSTALLER_CHANNEL)
|
||||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, INSTALLER_CHANNEL)
|
|
||||||
mainChannel.setMethodCallHandler { call, result ->
|
mainChannel.setMethodCallHandler { call, result ->
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
|
"copyOriginalApk" -> {
|
||||||
|
val originalFilePath = call.argument<String>("originalFilePath")
|
||||||
|
val backupFilePath = call.argument<String>("backupFilePath")
|
||||||
|
if (originalFilePath != null && backupFilePath != null) {
|
||||||
|
File(originalFilePath).copyTo(File(backupFilePath), true)
|
||||||
|
result.success(null)
|
||||||
|
} else {
|
||||||
|
result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
"runPatcher" -> {
|
"runPatcher" -> {
|
||||||
val patchBundleFilePath = call.argument<String>("patchBundleFilePath")
|
val patchBundleFilePath = call.argument<String>("patchBundleFilePath")
|
||||||
val originalFilePath = call.argument<String>("originalFilePath")
|
|
||||||
val inputFilePath = call.argument<String>("inputFilePath")
|
val inputFilePath = call.argument<String>("inputFilePath")
|
||||||
val patchedFilePath = call.argument<String>("patchedFilePath")
|
val patchedFilePath = call.argument<String>("patchedFilePath")
|
||||||
val outFilePath = call.argument<String>("outFilePath")
|
val outFilePath = call.argument<String>("outFilePath")
|
||||||
@ -45,7 +53,6 @@ class MainActivity : FlutterActivity() {
|
|||||||
val resourcePatching = call.argument<Boolean>("resourcePatching")
|
val resourcePatching = call.argument<Boolean>("resourcePatching")
|
||||||
val keyStoreFilePath = call.argument<String>("keyStoreFilePath")
|
val keyStoreFilePath = call.argument<String>("keyStoreFilePath")
|
||||||
if (patchBundleFilePath != null &&
|
if (patchBundleFilePath != null &&
|
||||||
originalFilePath != null &&
|
|
||||||
inputFilePath != null &&
|
inputFilePath != null &&
|
||||||
patchedFilePath != null &&
|
patchedFilePath != null &&
|
||||||
outFilePath != null &&
|
outFilePath != null &&
|
||||||
@ -59,7 +66,6 @@ class MainActivity : FlutterActivity() {
|
|||||||
runPatcher(
|
runPatcher(
|
||||||
result,
|
result,
|
||||||
patchBundleFilePath,
|
patchBundleFilePath,
|
||||||
originalFilePath,
|
|
||||||
inputFilePath,
|
inputFilePath,
|
||||||
patchedFilePath,
|
patchedFilePath,
|
||||||
outFilePath,
|
outFilePath,
|
||||||
@ -82,7 +88,6 @@ class MainActivity : FlutterActivity() {
|
|||||||
fun runPatcher(
|
fun runPatcher(
|
||||||
result: MethodChannel.Result,
|
result: MethodChannel.Result,
|
||||||
patchBundleFilePath: String,
|
patchBundleFilePath: String,
|
||||||
originalFilePath: String,
|
|
||||||
inputFilePath: String,
|
inputFilePath: String,
|
||||||
patchedFilePath: String,
|
patchedFilePath: String,
|
||||||
outFilePath: String,
|
outFilePath: String,
|
||||||
@ -93,7 +98,6 @@ class MainActivity : FlutterActivity() {
|
|||||||
resourcePatching: Boolean,
|
resourcePatching: Boolean,
|
||||||
keyStoreFilePath: String
|
keyStoreFilePath: String
|
||||||
) {
|
) {
|
||||||
val originalFile = File(originalFilePath)
|
|
||||||
val inputFile = File(inputFilePath)
|
val inputFile = File(inputFilePath)
|
||||||
val patchedFile = File(patchedFilePath)
|
val patchedFile = File(patchedFilePath)
|
||||||
val outFile = File(outFilePath)
|
val outFile = File(outFilePath)
|
||||||
@ -115,25 +119,13 @@ class MainActivity : FlutterActivity() {
|
|||||||
|
|
||||||
Thread(
|
Thread(
|
||||||
Runnable {
|
Runnable {
|
||||||
handler.post {
|
|
||||||
installerChannel.invokeMethod(
|
|
||||||
"update",
|
|
||||||
mapOf(
|
|
||||||
"progress" to 0.1,
|
|
||||||
"header" to "",
|
|
||||||
"log" to "Copying original apk"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
originalFile.copyTo(inputFile, true)
|
|
||||||
|
|
||||||
handler.post {
|
handler.post {
|
||||||
installerChannel.invokeMethod(
|
installerChannel.invokeMethod(
|
||||||
"update",
|
"update",
|
||||||
mapOf(
|
mapOf(
|
||||||
"progress" to 0.2,
|
"progress" to 0.2,
|
||||||
"header" to "Unpacking apk...",
|
"header" to "Unpacking apk...",
|
||||||
"log" to "Unpacking copied apk"
|
"log" to "Unpacking input apk"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -198,7 +198,10 @@ class ManagerAPI {
|
|||||||
Future<bool> isAppUninstalled(PatchedApplication app) async {
|
Future<bool> isAppUninstalled(PatchedApplication app) async {
|
||||||
bool existsRoot = false;
|
bool existsRoot = false;
|
||||||
if (app.isRooted) {
|
if (app.isRooted) {
|
||||||
existsRoot = await _rootAPI.isAppInstalled(app.packageName);
|
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||||
|
if (hasRootPermissions) {
|
||||||
|
existsRoot = await _rootAPI.isAppInstalled(app.packageName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
|
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
|
||||||
return !existsRoot && !existsNonRoot;
|
return !existsRoot && !existsNonRoot;
|
||||||
|
@ -82,9 +82,31 @@ class PatcherAPI {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> runPatcher(
|
Future<String> copyOriginalApk(
|
||||||
String packageName,
|
String packageName,
|
||||||
String originalFilePath,
|
String originalFilePath,
|
||||||
|
) async {
|
||||||
|
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||||
|
if (hasRootPermissions) {
|
||||||
|
String originalRootPath = await _rootAPI.getOriginalFilePath(packageName);
|
||||||
|
if (File(originalRootPath).existsSync()) {
|
||||||
|
originalFilePath = originalRootPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String backupFilePath = '${_tmpDir.path}/$packageName.apk';
|
||||||
|
await patcherChannel.invokeMethod(
|
||||||
|
'copyOriginalApk',
|
||||||
|
{
|
||||||
|
'originalFilePath': originalFilePath,
|
||||||
|
'backupFilePath': backupFilePath,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
return backupFilePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> runPatcher(
|
||||||
|
String packageName,
|
||||||
|
String inputFilePath,
|
||||||
List<Patch> selectedPatches,
|
List<Patch> selectedPatches,
|
||||||
) async {
|
) async {
|
||||||
bool mergeIntegrations = selectedPatches.any(
|
bool mergeIntegrations = selectedPatches.any(
|
||||||
@ -118,7 +140,6 @@ class PatcherAPI {
|
|||||||
if (patchBundleFile != null) {
|
if (patchBundleFile != null) {
|
||||||
_tmpDir.createSync();
|
_tmpDir.createSync();
|
||||||
Directory workDir = _tmpDir.createTempSync('tmp-');
|
Directory workDir = _tmpDir.createTempSync('tmp-');
|
||||||
File inputFile = File('${workDir.path}/base.apk');
|
|
||||||
File patchedFile = File('${workDir.path}/patched.apk');
|
File patchedFile = File('${workDir.path}/patched.apk');
|
||||||
_outFile = File('${workDir.path}/out.apk');
|
_outFile = File('${workDir.path}/out.apk');
|
||||||
Directory cacheDir = Directory('${workDir.path}/cache');
|
Directory cacheDir = Directory('${workDir.path}/cache');
|
||||||
@ -127,8 +148,7 @@ class PatcherAPI {
|
|||||||
'runPatcher',
|
'runPatcher',
|
||||||
{
|
{
|
||||||
'patchBundleFilePath': patchBundleFile.path,
|
'patchBundleFilePath': patchBundleFile.path,
|
||||||
'originalFilePath': originalFilePath,
|
'inputFilePath': inputFilePath,
|
||||||
'inputFilePath': inputFile.path,
|
|
||||||
'patchedFilePath': patchedFile.path,
|
'patchedFilePath': patchedFile.path,
|
||||||
'outFilePath': _outFile!.path,
|
'outFilePath': _outFile!.path,
|
||||||
'integrationsPath': mergeIntegrations ? integrationsFile!.path : '',
|
'integrationsPath': mergeIntegrations ? integrationsFile!.path : '',
|
||||||
@ -153,8 +173,6 @@ class PatcherAPI {
|
|||||||
patchedApp.apkFilePath,
|
patchedApp.apkFilePath,
|
||||||
_outFile!.path,
|
_outFile!.path,
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await AppInstaller.installApk(_outFile!.path);
|
await AppInstaller.installApk(_outFile!.path);
|
||||||
@ -179,19 +197,6 @@ class PatcherAPI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> checkOldPatch(PatchedApplication patchedApp) async {
|
|
||||||
if (patchedApp.isRooted) {
|
|
||||||
return await _rootAPI.isAppInstalled(patchedApp.packageName);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> deleteOldPatch(PatchedApplication patchedApp) async {
|
|
||||||
if (patchedApp.isRooted) {
|
|
||||||
await _rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void shareLog(String logs) {
|
void shareLog(String logs) {
|
||||||
ShareExtend.share(logs, 'text');
|
ShareExtend.share(logs, 'text');
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:root/root.dart';
|
import 'package:root/root.dart';
|
||||||
|
|
||||||
class RootAPI {
|
class RootAPI {
|
||||||
final String _managerDirPath = '/data/adb/revanced_manager';
|
final String _managerDirPath = '/data/adb/revanced-manager';
|
||||||
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';
|
||||||
|
|
||||||
@ -72,13 +72,15 @@ class RootAPI {
|
|||||||
String patchedFilePath,
|
String patchedFilePath,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
|
await deleteApp(packageName, originalFilePath);
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'mkdir -p "$_managerDirPath/$packageName"',
|
cmd: 'mkdir -p "$_managerDirPath/$packageName"',
|
||||||
);
|
);
|
||||||
installServiceDScript(packageName);
|
await saveOriginalFilePath(packageName, originalFilePath);
|
||||||
installPostFsDataScript(packageName);
|
await installServiceDScript(packageName);
|
||||||
installApk(packageName, patchedFilePath);
|
await installPostFsDataScript(packageName);
|
||||||
mountApk(packageName, originalFilePath, patchedFilePath);
|
await installApk(packageName, patchedFilePath);
|
||||||
|
await mountApk(packageName, originalFilePath);
|
||||||
return true;
|
return true;
|
||||||
} on Exception {
|
} on Exception {
|
||||||
return false;
|
return false;
|
||||||
@ -129,11 +131,7 @@ class RootAPI {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> mountApk(
|
Future<void> mountApk(String packageName, String originalFilePath) async {
|
||||||
String packageName,
|
|
||||||
String originalFilePath,
|
|
||||||
String patchedFilePath,
|
|
||||||
) async {
|
|
||||||
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'am force-stop "$packageName"',
|
cmd: 'am force-stop "$packageName"',
|
||||||
@ -145,4 +143,18 @@ class RootAPI {
|
|||||||
cmd: 'su -mm -c "mount -o bind $newPatchedFilePath $originalFilePath"',
|
cmd: 'su -mm -c "mount -o bind $newPatchedFilePath $originalFilePath"',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String> getOriginalFilePath(String packageName) async {
|
||||||
|
return '$_managerDirPath/$packageName/original.apk';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> saveOriginalFilePath(
|
||||||
|
String packageName,
|
||||||
|
String originalFilePath,
|
||||||
|
) async {
|
||||||
|
String originalRootPath = '$_managerDirPath/$packageName/original.apk';
|
||||||
|
await Root.exec(
|
||||||
|
cmd: 'cp "$originalFilePath" "$originalRootPath"',
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||||||
apkFilePath: application.apkFilePath,
|
apkFilePath: application.apkFilePath,
|
||||||
icon: application.icon,
|
icon: application.icon,
|
||||||
patchDate: DateTime.now(),
|
patchDate: DateTime.now(),
|
||||||
isRooted: false,
|
|
||||||
);
|
);
|
||||||
locator<PatcherViewModel>().selectedPatches.clear();
|
locator<PatcherViewModel>().selectedPatches.clear();
|
||||||
locator<PatcherViewModel>().notifyListeners();
|
locator<PatcherViewModel>().notifyListeners();
|
||||||
@ -55,7 +54,6 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||||||
apkFilePath: result.files.single.path!,
|
apkFilePath: result.files.single.path!,
|
||||||
icon: application.icon,
|
icon: application.icon,
|
||||||
patchDate: DateTime.now(),
|
patchDate: DateTime.now(),
|
||||||
isRooted: false,
|
|
||||||
);
|
);
|
||||||
locator<PatcherViewModel>().selectedPatches.clear();
|
locator<PatcherViewModel>().selectedPatches.clear();
|
||||||
locator<PatcherViewModel>().notifyListeners();
|
locator<PatcherViewModel>().notifyListeners();
|
||||||
|
@ -104,25 +104,25 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
Future<void> runPatcher() async {
|
Future<void> runPatcher() async {
|
||||||
update(0.0, 'Initializing...', 'Initializing installer');
|
update(0.0, 'Initializing...', 'Initializing installer');
|
||||||
if (_patches.isNotEmpty) {
|
if (_patches.isNotEmpty) {
|
||||||
String apkFilePath = _app.apkFilePath;
|
|
||||||
try {
|
try {
|
||||||
if (_app.isRooted) {
|
update(0.1, '', 'Copying original apk');
|
||||||
update(0.0, '', 'Checking if an old patched version exists');
|
String inputFilePath = await _patcherAPI.copyOriginalApk(
|
||||||
bool oldExists = await _patcherAPI.checkOldPatch(_app);
|
_app.packageName,
|
||||||
if (oldExists) {
|
_app.apkFilePath,
|
||||||
update(0.0, '', 'Deleting old patched version');
|
);
|
||||||
await _patcherAPI.deleteOldPatch(_app);
|
update(0.1, '', 'Creating working directory');
|
||||||
}
|
await _patcherAPI.runPatcher(
|
||||||
}
|
_app.packageName,
|
||||||
update(0.0, '', 'Creating working directory');
|
inputFilePath,
|
||||||
await _patcherAPI.runPatcher(_app.packageName, apkFilePath, _patches);
|
_patches,
|
||||||
} on Exception {
|
);
|
||||||
|
} catch (e) {
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
update(1.0, 'Aborting...', 'An error occurred! Aborting');
|
update(-1.0, 'Aborting...', 'An error occurred! Aborting\nError: $e');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hasErrors = true;
|
hasErrors = true;
|
||||||
update(1.0, 'Aborting...', 'No app or patches selected! Aborting');
|
update(-1.0, 'Aborting...', 'No app or patches selected! Aborting');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await FlutterBackground.disableBackgroundExecution();
|
await FlutterBackground.disableBackgroundExecution();
|
||||||
|
@ -19,10 +19,13 @@ class AppInfoViewModel extends BaseViewModel {
|
|||||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||||
final RootAPI _rootAPI = RootAPI();
|
final RootAPI _rootAPI = RootAPI();
|
||||||
|
|
||||||
void uninstallApp(PatchedApplication app) {
|
Future<void> uninstallApp(PatchedApplication app) async {
|
||||||
if (app.isRooted) {
|
if (app.isRooted) {
|
||||||
_rootAPI.deleteApp(app.packageName, app.apkFilePath);
|
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||||
_managerAPI.deletePatchedApp(app);
|
if (hasRootPermissions) {
|
||||||
|
_rootAPI.deleteApp(app.packageName, app.apkFilePath);
|
||||||
|
_managerAPI.deletePatchedApp(app);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
DeviceApps.uninstallApp(app.packageName);
|
DeviceApps.uninstallApp(app.packageName);
|
||||||
_managerAPI.deletePatchedApp(app);
|
_managerAPI.deletePatchedApp(app);
|
||||||
@ -41,22 +44,24 @@ class AppInfoViewModel extends BaseViewModel {
|
|||||||
BuildContext context,
|
BuildContext context,
|
||||||
PatchedApplication app,
|
PatchedApplication app,
|
||||||
) async {
|
) async {
|
||||||
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
if (app.isRooted) {
|
||||||
if (app.isRooted && !hasRootPermissions) {
|
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
|
||||||
return showDialog(
|
if (!hasRootPermissions) {
|
||||||
context: context,
|
return showDialog(
|
||||||
builder: (context) => AlertDialog(
|
context: context,
|
||||||
title: I18nText('appInfoView.rootDialogTitle'),
|
builder: (context) => AlertDialog(
|
||||||
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
title: I18nText('appInfoView.rootDialogTitle'),
|
||||||
content: I18nText('appInfoView.rootDialogText'),
|
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
actions: <Widget>[
|
content: I18nText('appInfoView.rootDialogText'),
|
||||||
CustomMaterialButton(
|
actions: <Widget>[
|
||||||
label: I18nText('okButton'),
|
CustomMaterialButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
label: I18nText('okButton'),
|
||||||
)
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return showDialog(
|
return showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user