Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: patch history #1414

Open
wants to merge 32 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
72a99bc
feat: patch history
BenjaminHalko Oct 21, 2023
5279513
Update manager_api.dart
BenjaminHalko Oct 21, 2023
18885a1
Update home_view.dart
BenjaminHalko Oct 21, 2023
b53bc28
fix: unable to use custom API (#1435)
TheAabedKhan Oct 26, 2023
7019d0c
build: bump version to v1.14.2
Ushie Oct 26, 2023
073acda
build: Bump dependencies to support patch option values (#1431)
TheAabedKhan Oct 26, 2023
163160c
fix: Hide empty patches category labels (#1439)
TheAabedKhan Oct 27, 2023
ee8c7f2
docs: use correct directory path (#1440)
haie-grain Oct 28, 2023
d63185b
docs: Remove unneeded steps to build from source
oSumAtrIX Oct 28, 2023
52da64b
chore: upgrade dependencies (#1404)
BenjaminHalko Oct 28, 2023
d0103a3
Merge branch 'dev' into feat/patch-history
BenjaminHalko Nov 1, 2023
36572ab
Merge branch 'dev' into feat/patch-history
oSumAtrIX Nov 5, 2023
443f594
switching to single app history
BenjaminHalko Nov 25, 2023
f375fa9
Merge branch 'dev' into feat/patch-history
BenjaminHalko Nov 25, 2023
8ac25bf
Limit patch history to 1 app
BenjaminHalko Nov 25, 2023
cebf974
Added setting
BenjaminHalko Nov 25, 2023
7e22fa2
Update en_US.json
BenjaminHalko Nov 25, 2023
d620272
Merge branch 'dev' into feat/patch-history
BenjaminHalko Nov 28, 2023
5f03a8b
Update assets/i18n/en_US.json
BenjaminHalko Nov 30, 2023
1fee561
fix: Grammar in readme mistake (#1569)
festry0 Dec 14, 2023
14dd4a0
Merge branch 'dev' into feat/patch-history
BenjaminHalko Dec 14, 2023
b4e21ae
Merge branch 'dev' into feat/patch-history
BenjaminHalko Dec 26, 2023
a3c4c8e
Merge branch 'dev' into feat/patch-history
BenjaminHalko Dec 26, 2023
a92b69b
Update en_US.json
BenjaminHalko Dec 26, 2023
cb5641a
Update assets/i18n/en_US.json
BenjaminHalko Jan 14, 2024
ffa8847
Update assets/i18n/en_US.json
BenjaminHalko Jan 14, 2024
0cb84be
Merge branch 'dev' into feat/patch-history
BenjaminHalko Feb 17, 2024
aa7a8c4
Fixed merge conflicts
BenjaminHalko Feb 17, 2024
cb686da
Merge branch 'dev' into feat/patch-history
BenjaminHalko Feb 28, 2024
a8bdafe
Update installer_view.dart
BenjaminHalko Feb 28, 2024
d83869d
Merge branch 'dev' into feat/patch-history
BenjaminHalko Mar 5, 2024
979cbfe
Added description
BenjaminHalko Mar 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Empty file removed assets/i18n/en_US.json
Empty file.
13 changes: 12 additions & 1 deletion assets/i18n/strings.i18n.json
Expand Up @@ -27,10 +27,12 @@
"refreshSuccess": "Refreshed successfully",
"widgetTitle": "Dashboard",
"updatesSubtitle": "Updates",
"patchedSubtitle": "Patched apps",
"patchHistorySubtitle": "Last patch",
"patchedSubtitle": "Installed apps",
"changeLaterSubtitle": "You can change this in the settings at a later time.",
"noUpdates": "No updates available",
"WIP": "Work in progress...",
"noHistory": "No app found",
"noInstallations": "No patched apps installed",
"installUpdate": "Continue to install the update?",
"updateSheetTitle": "Update ReVanced Manager",
Expand Down Expand Up @@ -203,6 +205,8 @@
"showUpdateDialogHint": "Show a dialog when a new update is available",
"universalPatchesLabel": "Show universal patches",
"universalPatchesHint": "Display all apps and universal patches (may slow down the app list)",
"patchHistoryLabel": "Save patched app",
"patchHistoryHint": "Save the last patch to install or export later",
"versionCompatibilityCheckLabel": "Version compatibility check",
"versionCompatibilityCheckHint": "Prevent selecting patches that are not compatible with the selected app version",
"requireSuggestedAppVersionLabel": "Require suggested app version",
Expand Down Expand Up @@ -254,18 +258,25 @@
"appInfoView": {
"widgetTitle": "App info",
"openButton": "Open",
"installButton": "Install",
"uninstallButton": "Uninstall",
"unmountButton": "Unmount",
"exportButton": "Export",
"deleteButton": "Delete",
"rootDialogTitle": "Error",
"patchHistoryDescription": "This is a backup of the last patched app. You can install or export it later.",
"unmountDialogText": "Are you sure you want to unmount this app?",
"uninstallDialogText": "Are you sure you want to uninstall this app?",
"rootDialogText": "App was installed with superuser permissions, but currently ReVanced Manager has no permissions.\nPlease grant superuser permissions first.",
"removeAppDialogTitle": "Delete app?",
"removeAppDialogText": "Are you sure you want to delete this app?",
"packageNameLabel": "Package name",
"installTypeLabel": "Installation type",
"mountTypeLabel": "Mount",
"regularTypeLabel": "Regular",
"patchedDateLabel": "Patched date",
"appliedPatchesLabel": "Applied patches",
"sizeLabel": "File size",
"patchedDateHint": "${date} at ${time}",
"appliedPatchesHint": "${quantity} applied patches",
"updateNotImplemented": "This feature has not been implemented yet"
Expand Down
4 changes: 4 additions & 0 deletions lib/models/patched_application.dart
Expand Up @@ -16,6 +16,8 @@ class PatchedApplication {
this.isRooted = false,
this.isFromStorage = false,
this.appliedPatches = const [],
this.patchedFilePath = '',
this.fileSize = 0,
});

factory PatchedApplication.fromJson(Map<String, dynamic> json) =>
Expand All @@ -33,6 +35,8 @@ class PatchedApplication {
bool isRooted;
bool isFromStorage;
List<String> appliedPatches;
String patchedFilePath;
int fileSize;

Map<String, dynamic> toJson() => _$PatchedApplicationToJson(this);

Expand Down
124 changes: 124 additions & 0 deletions lib/services/manager_api.dart
Expand Up @@ -296,6 +296,14 @@ class ManagerAPI {
await _prefs.setBool('requireSuggestedAppVersionEnabled', value);
}

bool isPatchHistoryEnabled() {
return _prefs.getBool('patchHistoryEnabled') ?? true;
}

Future<void> enablePatchHistoryStatus(bool value) async {
await _prefs.setBool('patchHistoryEnabled', value);
}

Future<void> setKeystorePassword(String password) async {
await _prefs.setString('keystorePassword', password);
}
Expand Down Expand Up @@ -328,6 +336,34 @@ class ManagerAPI {
}
}

PatchedApplication? getLastPatchedApp() {
final String? app = _prefs.getString('lastPatchedApp');
return app != null ? PatchedApplication.fromJson(jsonDecode(app)) : null;
}

Future<void> deleteLastPatchedApp() async {
final PatchedApplication? app = getLastPatchedApp();
if (app != null) {
final File file = File(app.patchedFilePath);
await file.delete();
await _prefs.remove('lastPatchedApp');
}
}

Future<void> setLastPatchedApp(
PatchedApplication app,
File outFile,
) async {
deleteLastPatchedApp();
final Directory appCache = await getApplicationSupportDirectory();
app.patchedFilePath = outFile.copySync('${appCache.path}/lastPatchedApp.apk').path;
app.fileSize = outFile.lengthSync();
await _prefs.setString(
'lastPatchedApp',
json.encode(app.toJson()),
);
}

List<PatchedApplication> getPatchedApps() {
final List<String> apps = _prefs.getStringList('patchedApps') ?? [];
return apps.map((a) => PatchedApplication.fromJson(jsonDecode(a))).toList();
Expand Down Expand Up @@ -686,6 +722,16 @@ class ManagerAPI {
patchedApps.addAll(mountedApps);

await setPatchedApps(patchedApps);

// Removed saved app history if the file is not found.
final PatchedApplication? lastPatchedApp = getLastPatchedApp();
if (lastPatchedApp != null) {
final File file = File(lastPatchedApp.patchedFilePath);
if (!file.existsSync()) {
deleteLastPatchedApp();
_prefs.remove('lastPatchedApp');
}
}
}

Future<bool> isAppUninstalled(PatchedApplication app) async {
Expand Down Expand Up @@ -780,4 +826,82 @@ class ManagerAPI {
selectedPatchesFile.deleteSync();
}
}

Future<bool> installTypeDialog(BuildContext context) async {
final ValueNotifier<int> installType = ValueNotifier(0);
if (isRooted) {
await showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text(t.installerView.installType),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
icon: const Icon(Icons.file_download_outlined),
contentPadding: const EdgeInsets.symmetric(vertical: 16),
content: SingleChildScrollView(
child: ValueListenableBuilder(
valueListenable: installType,
builder: (context, value, child) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
child: Text(
t.installerView.installTypeDescription,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.secondary,
),
),
),
RadioListTile(
title: Text(t.installerView.installNonRootType),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16),
value: 0,
groupValue: value,
onChanged: (selected) {
installType.value = selected!;
},
),
RadioListTile(
title: Text(t.installerView.installRootType),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16),
value: 1,
groupValue: value,
onChanged: (selected) {
installType.value = selected!;
},
),
],
);
},
),
),
actions: [
OutlinedButton(
child: Text(t.cancelButton),
onPressed: () {
Navigator.of(context).pop();
},
),
FilledButton(
child: Text(t.installerView.installButton),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
}
return false;
}
}
22 changes: 11 additions & 11 deletions lib/services/patcher_api.dart
Expand Up @@ -214,7 +214,7 @@ class PatcherAPI {
BuildContext context,
PatchedApplication patchedApp,
) async {
if (outFile != null) {
if (patchedApp.patchedFilePath != '') {
_managerAPI.ctx = context;
try {
if (patchedApp.isRooted) {
Expand All @@ -229,7 +229,7 @@ class PatcherAPI {
return await _rootAPI.install(
patchedApp.packageName,
patchedApp.apkFilePath,
outFile!.path,
patchedApp.patchedFilePath,
)
? 0
: 1;
Expand All @@ -243,7 +243,7 @@ class PatcherAPI {
if (context.mounted) {
return await installApk(
context,
outFile!.path,
patchedApp.patchedFilePath,
);
}
}
Expand Down Expand Up @@ -361,13 +361,13 @@ class PatcherAPI {
return cleanInstall ? 10 : 1;
}

void exportPatchedFile(String appName, String version) {
void exportPatchedFile(PatchedApplication app) {
try {
if (outFile != null) {
final String newName = _getFileName(appName, version);
final String newName = _getFileName(app.name, app.version);
FlutterFileDialog.saveFile(
params: SaveFileDialogParams(
sourceFilePath: outFile!.path,
sourceFilePath: app.patchedFilePath,
fileName: newName,
mimeTypesFilter: ['application/vnd.android.package-archive'],
),
Expand All @@ -380,14 +380,14 @@ class PatcherAPI {
}
}

void sharePatchedFile(String appName, String version) {
void sharePatchedFile(PatchedApplication app) {
try {
if (outFile != null) {
final String newName = _getFileName(appName, version);
final int lastSeparator = outFile!.path.lastIndexOf('/');
final String newName = _getFileName(app.name, app.version);
final int lastSeparator = app.patchedFilePath.lastIndexOf('/');
final String newPath =
outFile!.path.substring(0, lastSeparator + 1) + newName;
final File shareFile = outFile!.copySync(newPath);
app.patchedFilePath.substring(0, lastSeparator + 1) + newName;
final File shareFile = File(app.patchedFilePath).copySync(newPath);
Share.shareXFiles([XFile(shareFile.path)]);
}
} on Exception catch (e) {
Expand Down
17 changes: 17 additions & 0 deletions lib/ui/views/home/home_view.dart
Expand Up @@ -5,6 +5,7 @@ import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/homeView/installed_apps_card.dart';
import 'package:revanced_manager/ui/widgets/homeView/latest_commit_card.dart';
import 'package:revanced_manager/ui/widgets/homeView/patch_history_card.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
import 'package:stacked/stacked.dart';

Expand Down Expand Up @@ -44,6 +45,22 @@ class HomeView extends StatelessWidget {
const SizedBox(height: 10),
LatestCommitCard(model: model, parentContext: context),
const SizedBox(height: 23),
Visibility(
visible: model.isPatchHistoryEnabled(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
t.homeView.patchHistorySubtitle,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 10),
PatchHistoryCard(),
const SizedBox(height: 10),

],
),
),
Text(
t.homeView.patchedSubtitle,
style: Theme.of(context).textTheme.titleLarge,
Expand Down
10 changes: 8 additions & 2 deletions lib/ui/views/home/home_viewmodel.dart
Expand Up @@ -35,6 +35,7 @@ class HomeViewModel extends BaseViewModel {
final Toast _toast = locator<Toast>();
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
bool showUpdatableApps = false;
PatchedApplication? lastPatchedApp;
bool releaseBuild = false;
List<PatchedApplication> patchedInstalledApps = [];
String _currentManagerVersion = '';
Expand Down Expand Up @@ -102,10 +103,10 @@ class HomeViewModel extends BaseViewModel {
}
}

void navigateToAppInfo(PatchedApplication app) {
void navigateToAppInfo(PatchedApplication app, bool isHistory) {
_navigationService.navigateTo(
Routes.appInfoView,
arguments: AppInfoViewArguments(app: app),
arguments: AppInfoViewArguments(app: app, isHistory: isHistory),
);
}

Expand All @@ -123,10 +124,15 @@ class HomeViewModel extends BaseViewModel {
}

void getPatchedApps() {
lastPatchedApp = _managerAPI.getLastPatchedApp();
patchedInstalledApps = _managerAPI.getPatchedApps().toList();
notifyListeners();
}

bool isPatchHistoryEnabled() {
return _managerAPI.isPatchHistoryEnabled();
}

Future<bool> hasManagerUpdates() async {
if (!_managerAPI.releaseBuild) {
return false;
Expand Down