feat: Save last patched app (#1414)

Co-authored-by: aAbed <39409020+TheAabedKhan@users.noreply.github.com>
Co-authored-by: Ushie <ushiekane@gmail.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: Mr. X <79870712+n30mrx@users.noreply.github.com>
Co-authored-by: festry0 <153519925+festry0@users.noreply.github.com>
This commit is contained in:
Benjamin
2024-06-29 05:38:00 -07:00
committed by GitHub
parent b26760b216
commit 77204087bb
16 changed files with 448 additions and 46 deletions

View File

@ -11,8 +11,10 @@ class AppInfoView extends StatelessWidget {
const AppInfoView({
super.key,
required this.app,
required this.isLastPatchedApp,
});
final PatchedApplication app;
final bool isLastPatchedApp;
@override
Widget build(BuildContext context) {
@ -57,6 +59,14 @@ class AppInfoView extends StatelessWidget {
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 20),
if (isLastPatchedApp) ...[
ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 20.0),
subtitle: Text(t.appInfoView.lastPatchedAppDescription),
),
const SizedBox(height: 4),
],
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: CustomCard(
@ -71,20 +81,26 @@ class AppInfoView extends StatelessWidget {
type: MaterialType.transparency,
child: InkWell(
borderRadius: BorderRadius.circular(16.0),
onTap: () => model.openApp(app),
onTap: () => isLastPatchedApp
? model.installApp(context, app)
: model.openApp(app),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.open_in_new_outlined,
isLastPatchedApp
? Icons.download_outlined
: Icons.open_in_new_outlined,
color: Theme.of(context)
.colorScheme
.primary,
),
const SizedBox(height: 10),
Text(
t.appInfoView.openButton,
isLastPatchedApp
? t.appInfoView.installButton
: t.appInfoView.openButton,
style: TextStyle(
color: Theme.of(context)
.colorScheme
@ -108,24 +124,30 @@ class AppInfoView extends StatelessWidget {
type: MaterialType.transparency,
child: InkWell(
borderRadius: BorderRadius.circular(16.0),
onTap: () => model.showUninstallDialog(
context,
app,
false,
),
onTap: () => isLastPatchedApp
? model.exportApp(app)
: model.showUninstallDialog(
context,
app,
false,
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.delete_outline,
isLastPatchedApp
? Icons.save
: Icons.delete_outline,
color: Theme.of(context)
.colorScheme
.primary,
),
const SizedBox(height: 10),
Text(
t.appInfoView.uninstallButton,
isLastPatchedApp
? t.appInfoView.exportButton
: t.appInfoView.uninstallButton,
style: TextStyle(
color: Theme.of(context)
.colorScheme
@ -144,14 +166,57 @@ class AppInfoView extends StatelessWidget {
endIndent: 12.0,
width: 1.0,
),
if (app.isRooted)
if (isLastPatchedApp)
VerticalDivider(
color: Theme.of(context).canvasColor,
indent: 12.0,
endIndent: 12.0,
width: 1.0,
),
if (app.isRooted)
if (isLastPatchedApp)
Expanded(
child: Material(
type: MaterialType.transparency,
child: InkWell(
borderRadius: BorderRadius.circular(16.0),
onTap: () => model.showDeleteDialog(
context,
app,
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons
.delete_forever_outlined,
color: Theme.of(context)
.colorScheme
.primary,
),
const SizedBox(height: 10),
Text(
t.appInfoView.deleteButton,
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
),
if (!isLastPatchedApp && app.isRooted)
VerticalDivider(
color: Theme.of(context).canvasColor,
indent: 12.0,
endIndent: 12.0,
width: 1.0,
),
if (!isLastPatchedApp && app.isRooted)
Expanded(
child: Material(
type: MaterialType.transparency,
@ -240,6 +305,23 @@ class AppInfoView extends StatelessWidget {
),
),
const SizedBox(height: 4),
if (isLastPatchedApp) ...[
ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 20.0),
title: Text(
t.appInfoView.sizeLabel,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
subtitle: Text(
model.getFileSizeString(app.fileSize),
),
),
const SizedBox(height: 4),
],
ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 20.0),

View File

@ -1,4 +1,5 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:math';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
@ -20,6 +21,23 @@ class AppInfoViewModel extends BaseViewModel {
final RootAPI _rootAPI = RootAPI();
final Toast _toast = locator<Toast>();
Future<void> installApp(
BuildContext context,
PatchedApplication app,
) async {
app.isRooted = await _managerAPI.installTypeDialog(context);
final int statusCode = await _patcherAPI.installPatchedFile(context, app);
if (statusCode == 0) {
locator<HomeViewModel>().initialize(context);
}
}
Future<void> exportApp(
PatchedApplication app,
) async {
_patcherAPI.exportPatchedFile(app);
}
Future<void> uninstallApp(
BuildContext context,
PatchedApplication app,
@ -123,6 +141,34 @@ class AppInfoViewModel extends BaseViewModel {
}
}
Future<void> showDeleteDialog(
BuildContext context,
PatchedApplication app,
) async {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(t.appInfoView.removeAppDialogTitle),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: Text(t.appInfoView.removeAppDialogText),
actions: <Widget>[
OutlinedButton(
child: Text(t.cancelButton),
onPressed: () => Navigator.of(context).pop(),
),
FilledButton(
child: Text(t.okButton),
onPressed: () => {
_managerAPI.deleteLastPatchedApp(),
Navigator.of(context)..pop()..pop(),
locator<HomeViewModel>().initialize(context),
},
),
],
),
);
}
String getPrettyDate(BuildContext context, DateTime dateTime) {
return DateFormat.yMMMMd(Localizations.localeOf(context).languageCode)
.format(dateTime);
@ -133,6 +179,12 @@ class AppInfoViewModel extends BaseViewModel {
.format(dateTime);
}
String getFileSizeString(int bytes) {
const suffixes = ['B', 'KB', 'MB', 'GB', 'TB'];
final i = (log(bytes) / log(1024)).floor();
return '${(bytes / pow(1024, i)).toStringAsFixed(2)} ${suffixes[i]}';
}
Future<void> showAppliedPatchesDialog(
BuildContext context,
PatchedApplication app,

View File

@ -76,7 +76,7 @@ class InstalledAppsCard extends StatelessWidget {
name: app.name,
patchDate: app.patchDate,
onPressed: () =>
locator<HomeViewModel>().navigateToAppInfo(app),
locator<HomeViewModel>().navigateToAppInfo(app, false),
),
)
.toList(),

View File

@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/application_item.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
//ignore: must_be_immutable
class LastPatchedAppCard extends StatelessWidget {
LastPatchedAppCard({super.key});
PatchedApplication? app = locator<HomeViewModel>().lastPatchedApp;
@override
Widget build(BuildContext context) {
return app == null
? CustomCard(
child: Center(
child: Column(
children: <Widget>[
Icon(
size: 40,
Icons.update_disabled,
color: Theme.of(context).colorScheme.secondary,
),
const SizedBox(height: 16),
Text(
t.homeView.noSavedAppFound,
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(
color:
Theme.of(context).colorScheme.secondary,
),
),
],
),
),
)
: ApplicationItem(
icon: app!.icon,
name: app!.name,
patchDate: app!.patchDate,
onPressed: () =>
locator<HomeViewModel>().navigateToAppInfo(app!, true),
);
}
}

View File

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_auto_update_patches.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_enable_patches_selection.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_last_patched_app.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_require_suggested_app_version.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_show_update_dialog.dart';
@ -24,6 +25,7 @@ class SAdvancedSection extends StatelessWidget {
SRequireSuggestedAppVersion(),
SVersionCompatibilityCheck(),
SUniversalPatches(),
SLastPatchedApp(),
],
);
}

View File

@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
class SLastPatchedApp extends StatefulWidget {
const SLastPatchedApp({super.key});
@override
State<SLastPatchedApp> createState() =>
_SLastPatchedAppState();
}
final _settingsViewModel = SettingsViewModel();
class _SLastPatchedAppState
extends State<SLastPatchedApp> {
@override
Widget build(BuildContext context) {
return SwitchListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: Text(
t.settingsView.lastPatchedAppLabel,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
subtitle: Text(t.settingsView.lastPatchedAppHint),
value: _settingsViewModel.isLastPatchedAppEnabled(),
onChanged: (value) {
setState(() {
_settingsViewModel.useLastPatchedApp(value);
});
},
);
}
}

View File

@ -33,6 +33,7 @@ class _ApplicationItemState extends State<ApplicationItem> {
return Container(
margin: const EdgeInsets.only(bottom: 16.0),
child: CustomCard(
onTap: widget.onPressed,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [