feat: add App Info View

This commit is contained in:
Alberto Ponces
2022-09-05 13:43:13 +01:00
parent 5e8e090e34
commit 2944a2b788
13 changed files with 433 additions and 25 deletions

View 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),
),
],
),
),
),
],
),
),
);
}
}

View 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 ')}';
}
}

View File

@ -1,11 +1,12 @@
import 'package:device_apps/device_apps.dart';
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/app/app.locator.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/appInfoView/app_info_view.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/open_container_wrapper.dart';
class InstalledAppsCard extends StatelessWidget {
InstalledAppsCard({Key? key}) : super(key: key);
@ -39,14 +40,19 @@ class InstalledAppsCard extends StatelessWidget {
padding: EdgeInsets.zero,
physics: const NeverScrollableScrollPhysics(),
children: apps
.map((app) => ApplicationItem(
.map(
(app) => OpenContainerWrapper(
openBuilder: (_, __) => AppInfoView(app: app),
closedBuilder: (_, openContainer) => ApplicationItem(
icon: app.icon,
name: app.name,
patchDate: app.patchDate,
changelog: app.changelog,
isUpdatableApp: false,
onPressed: () => DeviceApps.openApp(app.packageName),
))
onPressed: openContainer,
),
),
)
.toList(),
);
}

View File

@ -148,7 +148,6 @@ class _PatchItemState extends State<PatchItem> {
),
actions: [
CustomMaterialButton(
isFilled: true,
label: I18nText('okButton'),
onPressed: () => Navigator.of(context).pop(),
)

View File

@ -53,14 +53,17 @@ class ApplicationItem extends StatelessWidget {
],
),
const Spacer(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: CustomMaterialButton(
label: isUpdatableApp
? I18nText('applicationItem.patchButton')
: I18nText('applicationItem.openButton'),
onPressed: onPressed,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
CustomMaterialButton(
label: isUpdatableApp
? I18nText('applicationItem.patchButton')
: I18nText('applicationItem.infoButton'),
onPressed: onPressed,
),
],
),
],
),