2023-04-20 19:50:30 +02:00
|
|
|
// ignore_for_file: use_build_context_synchronously
|
|
|
|
|
2023-11-21 15:33:21 +01:00
|
|
|
import 'dart:io';
|
|
|
|
|
|
|
|
import 'package:flutter/foundation.dart';
|
2022-09-15 02:15:13 +02:00
|
|
|
import 'package:flutter/material.dart';
|
2023-11-21 15:33:21 +01:00
|
|
|
import 'package:flutter/services.dart';
|
2022-09-15 02:15:13 +02:00
|
|
|
import 'package:flutter_i18n/flutter_i18n.dart';
|
2022-08-18 16:33:33 +02:00
|
|
|
import 'package:injectable/injectable.dart';
|
2022-09-05 16:30:26 +02:00
|
|
|
import 'package:revanced_manager/app/app.locator.dart';
|
|
|
|
import 'package:revanced_manager/app/app.router.dart';
|
2022-08-18 16:33:33 +02:00
|
|
|
import 'package:revanced_manager/models/patch.dart';
|
|
|
|
import 'package:revanced_manager/models/patched_application.dart';
|
2022-09-23 16:31:24 +02:00
|
|
|
import 'package:revanced_manager/services/manager_api.dart';
|
2022-09-15 02:15:13 +02:00
|
|
|
import 'package:revanced_manager/services/patcher_api.dart';
|
2022-09-23 09:30:30 +02:00
|
|
|
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
2023-04-20 19:50:30 +02:00
|
|
|
import 'package:revanced_manager/utils/about_info.dart';
|
2023-08-06 07:10:57 +02:00
|
|
|
import 'package:revanced_manager/utils/check_for_supported_patch.dart';
|
2022-08-06 14:13:28 +02:00
|
|
|
import 'package:stacked/stacked.dart';
|
2022-09-05 16:30:26 +02:00
|
|
|
import 'package:stacked_services/stacked_services.dart';
|
2022-08-06 14:13:28 +02:00
|
|
|
|
2022-08-18 16:33:33 +02:00
|
|
|
@lazySingleton
|
2022-08-06 14:13:28 +02:00
|
|
|
class PatcherViewModel extends BaseViewModel {
|
2022-09-05 16:30:26 +02:00
|
|
|
final NavigationService _navigationService = locator<NavigationService>();
|
2022-09-23 16:31:24 +02:00
|
|
|
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
2022-09-15 02:15:13 +02:00
|
|
|
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
2022-08-18 16:33:33 +02:00
|
|
|
PatchedApplication? selectedApp;
|
2023-10-12 02:00:39 +02:00
|
|
|
BuildContext? ctx;
|
2022-08-18 16:33:33 +02:00
|
|
|
List<Patch> selectedPatches = [];
|
2023-08-09 21:32:13 +02:00
|
|
|
List<String> removedPatches = [];
|
2022-08-18 16:33:33 +02:00
|
|
|
|
2022-09-05 16:30:26 +02:00
|
|
|
void navigateToAppSelector() {
|
|
|
|
_navigationService.navigateTo(Routes.appSelectorView);
|
|
|
|
}
|
|
|
|
|
|
|
|
void navigateToPatchesSelector() {
|
|
|
|
_navigationService.navigateTo(Routes.patchesSelectorView);
|
|
|
|
}
|
|
|
|
|
|
|
|
void navigateToInstaller() {
|
|
|
|
_navigationService.navigateTo(Routes.installerView);
|
|
|
|
}
|
|
|
|
|
2022-08-31 10:32:10 +02:00
|
|
|
bool showPatchButton() {
|
2022-08-18 16:33:33 +02:00
|
|
|
return selectedPatches.isNotEmpty;
|
2022-08-17 13:48:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool dimPatchesCard() {
|
2022-08-18 16:33:33 +02:00
|
|
|
return selectedApp == null;
|
2022-08-17 13:48:03 +02:00
|
|
|
}
|
2022-09-15 02:15:13 +02:00
|
|
|
|
2023-10-12 02:00:39 +02:00
|
|
|
bool showRemovedPatchesDialog(BuildContext context) {
|
2023-08-09 21:32:13 +02:00
|
|
|
if (removedPatches.isNotEmpty) {
|
2023-10-12 02:00:39 +02:00
|
|
|
showDialog(
|
2023-08-09 21:32:13 +02:00
|
|
|
context: context,
|
|
|
|
builder: (context) => AlertDialog(
|
|
|
|
title: I18nText('notice'),
|
|
|
|
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
|
|
|
content: I18nText(
|
|
|
|
'patcherView.removedPatchesWarningDialogText',
|
|
|
|
translationParams: {'patches': removedPatches.join('\n')},
|
|
|
|
),
|
|
|
|
actions: <Widget>[
|
|
|
|
CustomMaterialButton(
|
|
|
|
isFilled: false,
|
|
|
|
label: I18nText('noButton'),
|
2023-10-12 02:00:39 +02:00
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
},
|
2023-08-09 21:32:13 +02:00
|
|
|
),
|
|
|
|
CustomMaterialButton(
|
|
|
|
label: I18nText('yesButton'),
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop();
|
2023-10-12 02:00:39 +02:00
|
|
|
showArmv7WarningDialog(context);
|
2023-08-09 21:32:13 +02:00
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
2023-10-12 02:00:39 +02:00
|
|
|
return false;
|
2023-08-09 21:32:13 +02:00
|
|
|
}
|
2023-10-12 02:00:39 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool checkRequiredPatchOption(BuildContext context) {
|
2023-10-15 19:16:58 +02:00
|
|
|
if (getNullRequiredOptions(selectedPatches, selectedApp!.packageName)
|
|
|
|
.isNotEmpty) {
|
2023-10-12 02:00:39 +02:00
|
|
|
showRequiredOptionDialog(context);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void showRequiredOptionDialog([context]) {
|
|
|
|
showDialog(
|
|
|
|
context: context ?? ctx,
|
|
|
|
builder: (context) => AlertDialog(
|
|
|
|
title: I18nText('notice'),
|
|
|
|
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
|
|
|
content: I18nText('patcherView.requiredOptionDialogText'),
|
|
|
|
actions: <Widget>[
|
|
|
|
CustomMaterialButton(
|
|
|
|
isFilled: false,
|
|
|
|
label: I18nText('cancelButton'),
|
|
|
|
onPressed: () => {
|
|
|
|
Navigator.of(context).pop(),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
CustomMaterialButton(
|
|
|
|
label: I18nText('okButton'),
|
|
|
|
onPressed: () => {
|
|
|
|
Navigator.pop(context),
|
|
|
|
navigateToPatchesSelector(),
|
|
|
|
},
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
2023-08-09 21:32:13 +02:00
|
|
|
}
|
|
|
|
|
2023-04-20 19:50:30 +02:00
|
|
|
Future<void> showArmv7WarningDialog(BuildContext context) async {
|
2023-05-07 00:44:57 +02:00
|
|
|
final bool armv7 = await AboutInfo.getInfo().then((info) {
|
2023-06-09 10:40:54 +02:00
|
|
|
final List<String> archs = info['supportedArch'];
|
2023-05-07 00:44:57 +02:00
|
|
|
final supportedAbis = ['arm64-v8a', 'x86', 'x86_64'];
|
|
|
|
return !archs.any((arch) => supportedAbis.contains(arch));
|
|
|
|
});
|
2023-04-20 19:50:30 +02:00
|
|
|
if (context.mounted && armv7) {
|
|
|
|
return showDialog(
|
|
|
|
context: context,
|
|
|
|
builder: (context) => AlertDialog(
|
|
|
|
title: I18nText('warning'),
|
|
|
|
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
|
|
|
content: I18nText('patcherView.armv7WarningDialogText'),
|
|
|
|
actions: <Widget>[
|
|
|
|
CustomMaterialButton(
|
|
|
|
label: I18nText('noButton'),
|
|
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
|
|
),
|
|
|
|
CustomMaterialButton(
|
|
|
|
label: I18nText('yesButton'),
|
|
|
|
isFilled: false,
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.of(context).pop();
|
|
|
|
navigateToInstaller();
|
|
|
|
},
|
2023-08-06 09:39:46 +02:00
|
|
|
),
|
2023-04-20 19:50:30 +02:00
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
navigateToInstaller();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-18 04:14:48 +02:00
|
|
|
String getAppSelectionString() {
|
|
|
|
String text = '${selectedApp!.name} (${selectedApp!.packageName})';
|
|
|
|
if (text.length > 32) {
|
|
|
|
text = '${text.substring(0, 32)}...)';
|
|
|
|
}
|
|
|
|
return text;
|
|
|
|
}
|
|
|
|
|
2023-11-21 15:33:21 +01:00
|
|
|
String getCurrentVersionString(BuildContext context) {
|
|
|
|
return '${FlutterI18n.translate(
|
|
|
|
context,
|
|
|
|
'appSelectorCard.currentVersion',
|
|
|
|
)}: v${selectedApp!.version}';
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> searchSuggestedVersionOnWeb() async {
|
|
|
|
final String suggestedVersion =
|
|
|
|
_patcherAPI.getSuggestedVersion(selectedApp!.packageName);
|
|
|
|
|
|
|
|
if (suggestedVersion.isNotEmpty) {
|
|
|
|
await openDefaultBrowser(
|
|
|
|
'${selectedApp!.packageName} apk version v$suggestedVersion');
|
|
|
|
} else {
|
|
|
|
await openDefaultBrowser('${selectedApp!.packageName} apk');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
String getSuggestedVersion() {
|
|
|
|
return _patcherAPI.getSuggestedVersion(selectedApp!.packageName);
|
|
|
|
}
|
|
|
|
|
2023-04-29 15:00:00 +02:00
|
|
|
String getSuggestedVersionString(BuildContext context) {
|
|
|
|
String suggestedVersion =
|
|
|
|
_patcherAPI.getSuggestedVersion(selectedApp!.packageName);
|
|
|
|
if (suggestedVersion.isEmpty) {
|
|
|
|
suggestedVersion = FlutterI18n.translate(
|
2022-09-18 04:14:48 +02:00
|
|
|
context,
|
2023-06-23 15:39:12 +02:00
|
|
|
'appSelectorCard.allVersions',
|
2022-09-18 04:14:48 +02:00
|
|
|
);
|
|
|
|
} else {
|
2023-04-29 15:00:00 +02:00
|
|
|
suggestedVersion = 'v$suggestedVersion';
|
2022-09-18 04:14:48 +02:00
|
|
|
}
|
|
|
|
return '${FlutterI18n.translate(
|
|
|
|
context,
|
2023-04-29 15:00:00 +02:00
|
|
|
'appSelectorCard.suggestedVersion',
|
|
|
|
)}: $suggestedVersion';
|
2022-09-18 04:14:48 +02:00
|
|
|
}
|
2022-11-09 08:36:04 +01:00
|
|
|
|
2023-11-21 15:33:21 +01:00
|
|
|
Future<void> openDefaultBrowser(String query) async {
|
|
|
|
if (Platform.isAndroid) {
|
|
|
|
try {
|
|
|
|
const platform = MethodChannel('app.revanced.manager.flutter/browser');
|
|
|
|
await platform.invokeMethod('openBrowser', {'query': query});
|
|
|
|
} catch (e) {
|
|
|
|
if (kDebugMode) {
|
|
|
|
print(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
throw 'Platform not supported';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-09 08:36:04 +01:00
|
|
|
Future<void> loadLastSelectedPatches() async {
|
|
|
|
this.selectedPatches.clear();
|
2023-08-09 21:32:13 +02:00
|
|
|
removedPatches.clear();
|
2023-01-30 13:35:06 +01:00
|
|
|
final List<String> selectedPatches =
|
2023-09-30 20:11:45 +02:00
|
|
|
await _managerAPI.getSelectedPatches(selectedApp!.packageName);
|
2023-01-30 13:35:06 +01:00
|
|
|
final List<Patch> patches =
|
2023-09-30 20:11:45 +02:00
|
|
|
_patcherAPI.getFilteredPatches(selectedApp!.packageName);
|
2023-02-19 04:16:49 +01:00
|
|
|
this
|
|
|
|
.selectedPatches
|
2022-11-09 08:36:04 +01:00
|
|
|
.addAll(patches.where((patch) => selectedPatches.contains(patch.name)));
|
2023-08-15 11:05:27 +02:00
|
|
|
if (!_managerAPI.isPatchesChangeEnabled()) {
|
|
|
|
this.selectedPatches.clear();
|
|
|
|
this.selectedPatches.addAll(patches.where((patch) => !patch.excluded));
|
|
|
|
}
|
2023-10-15 19:16:58 +02:00
|
|
|
if (_managerAPI.isVersionCompatibilityCheckEnabled()) {
|
2023-08-06 07:10:57 +02:00
|
|
|
this.selectedPatches.removeWhere((patch) => !isPatchSupported(patch));
|
|
|
|
}
|
|
|
|
if (!_managerAPI.areUniversalPatchesEnabled()) {
|
|
|
|
this
|
|
|
|
.selectedPatches
|
|
|
|
.removeWhere((patch) => patch.compatiblePackages.isEmpty);
|
|
|
|
}
|
2023-09-30 20:11:45 +02:00
|
|
|
final usedPatches = _managerAPI.getUsedPatches(selectedApp!.packageName);
|
2023-10-15 19:16:58 +02:00
|
|
|
for (final patch in usedPatches) {
|
|
|
|
if (!patches.any((p) => p.name == patch.name)) {
|
2023-10-12 02:00:39 +02:00
|
|
|
removedPatches.add('• ${patch.name}');
|
|
|
|
for (final option in patch.options) {
|
2023-10-15 19:16:58 +02:00
|
|
|
_managerAPI.clearPatchOption(
|
|
|
|
selectedApp!.packageName, patch.name, option.key);
|
2023-10-12 02:00:39 +02:00
|
|
|
}
|
2023-08-09 21:32:13 +02:00
|
|
|
}
|
|
|
|
}
|
2022-11-09 08:36:04 +01:00
|
|
|
notifyListeners();
|
|
|
|
}
|
2022-08-06 14:13:28 +02:00
|
|
|
}
|