mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
feat: add App Info View
This commit is contained in:
parent
5e8e090e34
commit
2944a2b788
@ -1,5 +1,8 @@
|
|||||||
{
|
{
|
||||||
"okButton": "OK",
|
"okButton": "OK",
|
||||||
|
"cancelButton": "Cancel",
|
||||||
|
"enabledLabel": "Enabled",
|
||||||
|
"disabledLabel": "Disabled",
|
||||||
"main": {
|
"main": {
|
||||||
"dashboardTab": "Dashboard",
|
"dashboardTab": "Dashboard",
|
||||||
"patcherTab": "Patcher",
|
"patcherTab": "Patcher",
|
||||||
@ -22,7 +25,7 @@
|
|||||||
},
|
},
|
||||||
"applicationItem": {
|
"applicationItem": {
|
||||||
"patchButton": "Patch",
|
"patchButton": "Patch",
|
||||||
"openButton": "Open",
|
"infoButton": "Info",
|
||||||
"changelogLabel": "Changelog"
|
"changelogLabel": "Changelog"
|
||||||
},
|
},
|
||||||
"latestCommitCard": {
|
"latestCommitCard": {
|
||||||
@ -107,5 +110,20 @@
|
|||||||
"grantPermission": "Grant Root Permission",
|
"grantPermission": "Grant Root Permission",
|
||||||
"grantedPermission": "Magisk permission granted: {isRooted}",
|
"grantedPermission": "Magisk permission granted: {isRooted}",
|
||||||
"nonRootButton": "Nonroot"
|
"nonRootButton": "Nonroot"
|
||||||
|
},
|
||||||
|
"appInfoView": {
|
||||||
|
"widgetTitle": "App Info",
|
||||||
|
"openButton": "Open",
|
||||||
|
"uninstallButton": "Uninstall",
|
||||||
|
"patchButton": "Patch",
|
||||||
|
"alertDialogTitle": "Uninstall",
|
||||||
|
"alertDialogText": "Are you sure you want to uninstall this app?",
|
||||||
|
"errorDialogText": "App was installed with root mode enabled but currently root mode is disabled.\nPlease enable root mode first.",
|
||||||
|
"packageNameLabel": "Package Name",
|
||||||
|
"rootModeLabel": "Root Mode",
|
||||||
|
"patchedDateLabel": "Patched Date",
|
||||||
|
"patchedDateHint": "{date} at {time}",
|
||||||
|
"appliedPatchesLabel": "Applied Patches",
|
||||||
|
"appliedPatchesHint": "{quantity} applied patches"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,6 @@ class PatchedApplication {
|
|||||||
Uint8List icon;
|
Uint8List icon;
|
||||||
DateTime patchDate;
|
DateTime patchDate;
|
||||||
final bool isRooted;
|
final bool isRooted;
|
||||||
final bool isFromStorage;
|
|
||||||
bool hasUpdates;
|
bool hasUpdates;
|
||||||
List<String> appliedPatches;
|
List<String> appliedPatches;
|
||||||
List<String> changelog;
|
List<String> changelog;
|
||||||
@ -30,7 +29,6 @@ class PatchedApplication {
|
|||||||
required this.icon,
|
required this.icon,
|
||||||
required this.patchDate,
|
required this.patchDate,
|
||||||
this.isRooted = false,
|
this.isRooted = false,
|
||||||
this.isFromStorage = false,
|
|
||||||
this.hasUpdates = false,
|
this.hasUpdates = false,
|
||||||
this.appliedPatches = const [],
|
this.appliedPatches = const [],
|
||||||
this.changelog = const [],
|
this.changelog = const [],
|
||||||
|
@ -102,12 +102,18 @@ class ManagerAPI {
|
|||||||
await setPatchedApps(patchedApps);
|
await setPatchedApps(patchedApps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> deletePatchedApp(PatchedApplication app) async {
|
||||||
|
List<PatchedApplication> patchedApps = getPatchedApps();
|
||||||
|
patchedApps.removeWhere((a) => a.packageName == app.packageName);
|
||||||
|
await setPatchedApps(patchedApps);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> reAssessSavedApps() async {
|
Future<void> reAssessSavedApps() async {
|
||||||
bool isRoot = isRooted() ?? false;
|
bool isRooted = this.isRooted() ?? false;
|
||||||
List<PatchedApplication> patchedApps = getPatchedApps();
|
List<PatchedApplication> patchedApps = getPatchedApps();
|
||||||
List<PatchedApplication> toRemove = [];
|
List<PatchedApplication> toRemove = [];
|
||||||
for (PatchedApplication app in patchedApps) {
|
for (PatchedApplication app in patchedApps) {
|
||||||
bool isRemove = await isAppUninstalled(app, isRoot);
|
bool isRemove = await isAppUninstalled(app, isRooted);
|
||||||
if (isRemove) {
|
if (isRemove) {
|
||||||
toRemove.add(app);
|
toRemove.add(app);
|
||||||
} else {
|
} else {
|
||||||
@ -133,9 +139,9 @@ class ManagerAPI {
|
|||||||
await setPatchedApps(patchedApps);
|
await setPatchedApps(patchedApps);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> isAppUninstalled(PatchedApplication app, bool isRoot) async {
|
Future<bool> isAppUninstalled(PatchedApplication app, bool isRooted) async {
|
||||||
bool existsRoot = false;
|
bool existsRoot = false;
|
||||||
if (isRoot) {
|
if (isRooted) {
|
||||||
existsRoot = await _rootAPI.isAppInstalled(app.packageName);
|
existsRoot = await _rootAPI.isAppInstalled(app.packageName);
|
||||||
}
|
}
|
||||||
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
|
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
|
||||||
|
@ -146,7 +146,7 @@ class PatcherAPI {
|
|||||||
Future<bool> installPatchedFile(PatchedApplication patchedApp) async {
|
Future<bool> installPatchedFile(PatchedApplication patchedApp) async {
|
||||||
if (_outFile != null) {
|
if (_outFile != null) {
|
||||||
try {
|
try {
|
||||||
if (patchedApp.isRooted && !patchedApp.isFromStorage) {
|
if (patchedApp.isRooted) {
|
||||||
return _rootAPI.installApp(
|
return _rootAPI.installApp(
|
||||||
patchedApp.packageName,
|
patchedApp.packageName,
|
||||||
patchedApp.apkFilePath,
|
patchedApp.apkFilePath,
|
||||||
|
@ -59,8 +59,7 @@ 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: _isRooted,
|
isRooted: false,
|
||||||
isFromStorage: true,
|
|
||||||
);
|
);
|
||||||
locator<PatcherViewModel>().selectedPatches.clear();
|
locator<PatcherViewModel>().selectedPatches.clear();
|
||||||
locator<PatcherViewModel>().notifyListeners();
|
locator<PatcherViewModel>().notifyListeners();
|
||||||
|
@ -70,7 +70,6 @@ class InstallerView extends StatelessWidget {
|
|||||||
label: model.isInstalled
|
label: model.isInstalled
|
||||||
? I18nText('installerView.openButton')
|
? I18nText('installerView.openButton')
|
||||||
: I18nText('installerView.installButton'),
|
: I18nText('installerView.installButton'),
|
||||||
isFilled: true,
|
|
||||||
isExpanded: true,
|
isExpanded: true,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (model.isInstalled) {
|
if (model.isInstalled) {
|
||||||
|
@ -99,7 +99,7 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
if (_app != null && _patches.isNotEmpty) {
|
if (_app != null && _patches.isNotEmpty) {
|
||||||
String apkFilePath = _app!.apkFilePath;
|
String apkFilePath = _app!.apkFilePath;
|
||||||
try {
|
try {
|
||||||
if (_app!.isRooted && !_app!.isFromStorage) {
|
if (_app!.isRooted) {
|
||||||
update(0.0, '', 'Checking if an old patched version exists');
|
update(0.0, '', 'Checking if an old patched version exists');
|
||||||
bool oldExists = await _patcherAPI.checkOldPatch(_app!);
|
bool oldExists = await _patcherAPI.checkOldPatch(_app!);
|
||||||
if (oldExists) {
|
if (oldExists) {
|
||||||
|
247
lib/ui/widgets/appInfoView/app_info_view.dart
Normal file
247
lib/ui/widgets/appInfoView/app_info_view.dart
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
import 'package:device_apps/device_apps.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||||
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
import 'package:revanced_manager/models/patched_application.dart';
|
||||||
|
import 'package:revanced_manager/ui/widgets/appInfoView/app_info_viewmodel.dart';
|
||||||
|
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
|
||||||
|
import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
|
class AppInfoView extends StatelessWidget {
|
||||||
|
final PatchedApplication app;
|
||||||
|
|
||||||
|
const AppInfoView({
|
||||||
|
Key? key,
|
||||||
|
required this.app,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ViewModelBuilder<AppInfoViewModel>.reactive(
|
||||||
|
onModelReady: (model) => model.initialize(),
|
||||||
|
viewModelBuilder: () => AppInfoViewModel(),
|
||||||
|
builder: (context, model, child) => Scaffold(
|
||||||
|
body: CustomScrollView(
|
||||||
|
slivers: <Widget>[
|
||||||
|
CustomSliverAppBar(
|
||||||
|
title: I18nText(
|
||||||
|
'appInfoView.widgetTitle',
|
||||||
|
child: Text(
|
||||||
|
'',
|
||||||
|
style: GoogleFonts.inter(
|
||||||
|
color: Theme.of(context).textTheme.headline6!.color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SliverPadding(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
sliver: SliverList(
|
||||||
|
delegate: SliverChildListDelegate.fixed(
|
||||||
|
<Widget>[
|
||||||
|
SizedBox(
|
||||||
|
height: 64.0,
|
||||||
|
child: CircleAvatar(
|
||||||
|
child: Image.memory(
|
||||||
|
app.icon,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Text(
|
||||||
|
app.name,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.headline6,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
app.version,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.subtitle1,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
CustomCard(
|
||||||
|
child: IntrinsicHeight(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
InkWell(
|
||||||
|
onTap: () => DeviceApps.openApp(
|
||||||
|
app.packageName,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.open_in_new_outlined,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
I18nText(
|
||||||
|
'appInfoView.openButton',
|
||||||
|
child: Text(
|
||||||
|
'',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
VerticalDivider(
|
||||||
|
color: Theme.of(context).canvasColor,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () =>
|
||||||
|
model.showUninstallAlertDialog(context, app),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.delete_outline,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
I18nText(
|
||||||
|
'appInfoView.uninstallButton',
|
||||||
|
child: Text(
|
||||||
|
'',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
VerticalDivider(
|
||||||
|
color: Theme.of(context).canvasColor,
|
||||||
|
),
|
||||||
|
InkWell(
|
||||||
|
onTap: () {
|
||||||
|
model.navigateToPatcher(app);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.build_outlined,
|
||||||
|
color:
|
||||||
|
Theme.of(context).colorScheme.primary,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
I18nText(
|
||||||
|
'appInfoView.patchButton',
|
||||||
|
child: Text(
|
||||||
|
'',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: I18nText(
|
||||||
|
'appInfoView.packageNameLabel',
|
||||||
|
child: const Text(
|
||||||
|
'',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: Text(app.packageName),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: I18nText(
|
||||||
|
'appInfoView.rootModeLabel',
|
||||||
|
child: const Text(
|
||||||
|
'',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: model.isRooted
|
||||||
|
? I18nText('enabledLabel')
|
||||||
|
: I18nText('disabledLabel'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: I18nText(
|
||||||
|
'appInfoView.patchedDateLabel',
|
||||||
|
child: const Text(
|
||||||
|
'',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: I18nText(
|
||||||
|
'appInfoView.patchedDateHint',
|
||||||
|
translationParams: {
|
||||||
|
'date': model.getPrettyDate(context, app.patchDate),
|
||||||
|
'time': model.getPrettyTime(context, app.patchDate),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
ListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
title: I18nText(
|
||||||
|
'appInfoView.appliedPatchesLabel',
|
||||||
|
child: const Text(
|
||||||
|
'',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
subtitle: I18nText(
|
||||||
|
'appInfoView.appliedPatchesHint',
|
||||||
|
translationParams: {
|
||||||
|
'quantity': app.appliedPatches.length.toString(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
onTap: () => model.showAppliedPatchesDialog(context, app),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
132
lib/ui/widgets/appInfoView/app_info_viewmodel.dart
Normal file
132
lib/ui/widgets/appInfoView/app_info_viewmodel.dart
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
import 'package:device_apps/device_apps.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:revanced_manager/app/app.locator.dart';
|
||||||
|
import 'package:revanced_manager/models/patched_application.dart';
|
||||||
|
import 'package:revanced_manager/services/manager_api.dart';
|
||||||
|
import 'package:revanced_manager/services/patcher_api.dart';
|
||||||
|
import 'package:revanced_manager/services/root_api.dart';
|
||||||
|
import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart';
|
||||||
|
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||||
|
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||||
|
import 'package:revanced_manager/utils/string.dart';
|
||||||
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
|
class AppInfoViewModel extends BaseViewModel {
|
||||||
|
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||||
|
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||||
|
final RootAPI _rootAPI = RootAPI();
|
||||||
|
bool isRooted = false;
|
||||||
|
|
||||||
|
void initialize() {
|
||||||
|
isRooted = _managerAPI.isRooted() ?? false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uninstallApp(PatchedApplication app) {
|
||||||
|
if (app.isRooted) {
|
||||||
|
_rootAPI.deleteApp(app.packageName, app.apkFilePath);
|
||||||
|
_managerAPI.deletePatchedApp(app);
|
||||||
|
} else {
|
||||||
|
DeviceApps.uninstallApp(app.packageName);
|
||||||
|
_managerAPI.deletePatchedApp(app);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void navigateToPatcher(PatchedApplication app) async {
|
||||||
|
locator<PatcherViewModel>().selectedApp = app;
|
||||||
|
locator<PatcherViewModel>().selectedPatches =
|
||||||
|
await _patcherAPI.getAppliedPatches(app.appliedPatches);
|
||||||
|
locator<PatcherViewModel>().notifyListeners();
|
||||||
|
locator<NavigationViewModel>().setIndex(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> showUninstallAlertDialog(
|
||||||
|
BuildContext context,
|
||||||
|
PatchedApplication app,
|
||||||
|
) async {
|
||||||
|
if (app.isRooted && !isRooted) {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: I18nText('appInfoView.alertDialogTitle'),
|
||||||
|
content: I18nText('appInfoView.errorDialogText'),
|
||||||
|
actions: [
|
||||||
|
CustomMaterialButton(
|
||||||
|
label: I18nText('okButton'),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: I18nText('appInfoView.alertDialogTitle'),
|
||||||
|
content: I18nText('appInfoView.alertDialogText'),
|
||||||
|
actions: [
|
||||||
|
CustomMaterialButton(
|
||||||
|
isFilled: false,
|
||||||
|
label: I18nText('cancelButton'),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
CustomMaterialButton(
|
||||||
|
label: I18nText('okButton'),
|
||||||
|
onPressed: () {
|
||||||
|
uninstallApp(app);
|
||||||
|
locator<NavigationViewModel>().notifyListeners();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPrettyDate(BuildContext context, DateTime dateTime) {
|
||||||
|
return DateFormat.yMMMMd(Localizations.localeOf(context).languageCode)
|
||||||
|
.format(dateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPrettyTime(BuildContext context, DateTime dateTime) {
|
||||||
|
return DateFormat.jm(Localizations.localeOf(context).languageCode)
|
||||||
|
.format(dateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> showAppliedPatchesDialog(
|
||||||
|
BuildContext context,
|
||||||
|
PatchedApplication app,
|
||||||
|
) async {
|
||||||
|
return showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: I18nText('appInfoView.appliedPatchesLabel'),
|
||||||
|
content: Text(getAppliedPatchesString(app.appliedPatches)),
|
||||||
|
actions: [
|
||||||
|
CustomMaterialButton(
|
||||||
|
label: I18nText('okButton'),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAppliedPatchesString(List<String> appliedPatches) {
|
||||||
|
List<String> names = appliedPatches
|
||||||
|
.map((p) => p
|
||||||
|
.replaceAll('-', ' ')
|
||||||
|
.split('-')
|
||||||
|
.join(' ')
|
||||||
|
.toTitleCase()
|
||||||
|
.replaceFirst('Microg', 'MicroG'))
|
||||||
|
.toList();
|
||||||
|
return '\u2022 ${names.join('\n\u2022 ')}';
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,12 @@
|
|||||||
import 'package:device_apps/device_apps.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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/models/patched_application.dart';
|
import 'package:revanced_manager/models/patched_application.dart';
|
||||||
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
||||||
|
import 'package:revanced_manager/ui/widgets/appInfoView/app_info_view.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/shared/application_item.dart';
|
import 'package:revanced_manager/ui/widgets/shared/application_item.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
|
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
|
||||||
|
import 'package:revanced_manager/ui/widgets/shared/open_container_wrapper.dart';
|
||||||
|
|
||||||
class InstalledAppsCard extends StatelessWidget {
|
class InstalledAppsCard extends StatelessWidget {
|
||||||
InstalledAppsCard({Key? key}) : super(key: key);
|
InstalledAppsCard({Key? key}) : super(key: key);
|
||||||
@ -39,14 +40,19 @@ class InstalledAppsCard extends StatelessWidget {
|
|||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
children: apps
|
children: apps
|
||||||
.map((app) => ApplicationItem(
|
.map(
|
||||||
|
(app) => OpenContainerWrapper(
|
||||||
|
openBuilder: (_, __) => AppInfoView(app: app),
|
||||||
|
closedBuilder: (_, openContainer) => ApplicationItem(
|
||||||
icon: app.icon,
|
icon: app.icon,
|
||||||
name: app.name,
|
name: app.name,
|
||||||
patchDate: app.patchDate,
|
patchDate: app.patchDate,
|
||||||
changelog: app.changelog,
|
changelog: app.changelog,
|
||||||
isUpdatableApp: false,
|
isUpdatableApp: false,
|
||||||
onPressed: () => DeviceApps.openApp(app.packageName),
|
onPressed: openContainer,
|
||||||
))
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -148,7 +148,6 @@ class _PatchItemState extends State<PatchItem> {
|
|||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
CustomMaterialButton(
|
CustomMaterialButton(
|
||||||
isFilled: true,
|
|
||||||
label: I18nText('okButton'),
|
label: I18nText('okButton'),
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
)
|
)
|
||||||
|
@ -53,14 +53,17 @@ class ApplicationItem extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
Padding(
|
Column(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: CustomMaterialButton(
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: <Widget>[
|
||||||
|
CustomMaterialButton(
|
||||||
label: isUpdatableApp
|
label: isUpdatableApp
|
||||||
? I18nText('applicationItem.patchButton')
|
? I18nText('applicationItem.patchButton')
|
||||||
: I18nText('applicationItem.openButton'),
|
: I18nText('applicationItem.infoButton'),
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
),
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -37,6 +37,7 @@ dependencies:
|
|||||||
google_fonts: ^3.0.1
|
google_fonts: ^3.0.1
|
||||||
http: ^0.13.4
|
http: ^0.13.4
|
||||||
injectable: ^1.5.3
|
injectable: ^1.5.3
|
||||||
|
intl: ^0.17.0
|
||||||
json_annotation: ^4.6.0
|
json_annotation: ^4.6.0
|
||||||
package_info_plus: ^1.4.3+1
|
package_info_plus: ^1.4.3+1
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
|
Loading…
Reference in New Issue
Block a user