From b77d46b2c26953ef0e352d947abed04c76104350 Mon Sep 17 00:00:00 2001 From: Pun Butrach Date: Thu, 10 Aug 2023 19:02:30 +0700 Subject: [PATCH] feat(installer): redesign utility options (#1062) Co-authored-by: Ushie --- assets/i18n/en_US.json | 7 +- lib/services/patcher_api.dart | 13 +- lib/ui/views/installer/installer_view.dart | 140 +++++------------- .../views/installer/installer_viewmodel.dart | 103 ++++++++++--- 4 files changed, 132 insertions(+), 131 deletions(-) diff --git a/assets/i18n/en_US.json b/assets/i18n/en_US.json index 2f7e1b64..fb3004c7 100644 --- a/assets/i18n/en_US.json +++ b/assets/i18n/en_US.json @@ -140,6 +140,8 @@ }, "installerView": { "widgetTitle": "Installer", + "installType": "Select install type", + "installTypeDescription": "Select the installation type to proceed with.", "installButton": "Install", "installRootButton": "Install as Root", "pressBackAgain": "Press back again to cancel", @@ -149,9 +151,8 @@ "notificationTitle": "ReVanced Manager is patching", "notificationText": "Tap to return to the installer", - "shareApkMenuOption": "Share APK", - "exportApkMenuOption": "Export APK", - "shareLogMenuOption": "Share log", + "exportApkButtonTooltip": "Export patched APK", + "exportLogButtonTooltip": "Export log", "installErrorDialogTitle": "Error", "installErrorDialogText1": "Root install is not possible with the current patches selection.\nRepatch your app or choose non-root install.", diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index df99db8b..c449cbc5 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -283,7 +283,7 @@ class PatcherAPI { return newName; } - Future sharePatcherLog(String logs) async { + Future exportPatcherLog(String logs) async { final Directory appCache = await getTemporaryDirectory(); final Directory logDir = Directory('${appCache.path}/logs'); logDir.createSync(); @@ -293,10 +293,15 @@ class PatcherAPI { .replaceAll(':', '') .replaceAll('T', '') .replaceAll('.', ''); - final File log = - File('${logDir.path}/revanced-manager_patcher_$dateTime.log'); + final String fileName = 'revanced-manager_patcher_$dateTime.log'; + final File log = File('${logDir.path}/$fileName'); log.writeAsStringSync(logs); - ShareExtend.share(log.path, 'file'); + CRFileSaver.saveFileWithDialog( + SaveFileDialogParams( + sourceFilePath: log.path, + destinationFileName: fileName, + ), + ); } String getSuggestedVersion(String packageName) { diff --git a/lib/ui/views/installer/installer_view.dart b/lib/ui/views/installer/installer_view.dart index 981d0f55..6f020189 100644 --- a/lib/ui/views/installer/installer_view.dart +++ b/lib/ui/views/installer/installer_view.dart @@ -4,8 +4,6 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/installerView/gradient_progress_indicator.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; -import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; -import 'package:revanced_manager/ui/widgets/shared/custom_popup_menu.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart'; import 'package:stacked/stacked.dart'; @@ -22,6 +20,40 @@ class InstallerView extends StatelessWidget { top: false, bottom: false, child: Scaffold( + floatingActionButton: Visibility( + visible: !model.isPatching, + child: FloatingActionButton.extended( + label: I18nText('installerView.installButton'), + icon: const Icon(Icons.file_download_outlined), + onPressed: () => model.installTypeDialog(context), + ), + ), + floatingActionButtonLocation: + FloatingActionButtonLocation.endContained, + bottomNavigationBar: Visibility( + visible: !model.isPatching, + child: BottomAppBar( + child: Row( + children: [ + Visibility( + visible: !model.hasErrors, + child: IconButton.filledTonal( + tooltip: FlutterI18n.translate( + context, 'installerView.exportApkButtonTooltip'), + icon: const Icon(Icons.save), + onPressed: () => model.onButtonPressed(0), + ), + ), + IconButton.filledTonal( + tooltip: FlutterI18n.translate( + context, 'installerView.exportLogButtonTooltip'), + icon: const Icon(Icons.post_add), + onPressed: () => model.onButtonPressed(1), + ), + ], + ), + ), + ), body: CustomScrollView( controller: model.scrollController, slivers: [ @@ -35,44 +67,6 @@ class InstallerView extends StatelessWidget { overflow: TextOverflow.ellipsis, ), onBackButtonPressed: () => model.onWillPop(context), - actions: [ - Visibility( - visible: !model.isPatching, - child: CustomPopupMenu( - onSelected: (value) => model.onMenuSelection(value), - children: { - if (!model.hasErrors) - 0: I18nText( - 'installerView.shareApkMenuOption', - child: const Text( - '', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ), - 1: I18nText( - 'installerView.exportApkMenuOption', - child: const Text( - '', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ), - 2: I18nText( - 'installerView.shareLogMenuOption', - child: const Text( - '', - style: TextStyle( - fontWeight: FontWeight.bold, - ), - ), - ), - }, - ), - ), - ], bottom: PreferredSize( preferredSize: const Size(double.infinity, 1.0), child: GradientProgressIndicator(progress: model.progress), @@ -96,72 +90,6 @@ class InstallerView extends StatelessWidget { ), ), ), - SliverFillRemaining( - hasScrollBody: false, - child: Align( - alignment: Alignment.bottomCenter, - child: Visibility( - visible: !model.isPatching && !model.hasErrors, - child: Padding( - padding: const EdgeInsets.all(20.0).copyWith(top: 0.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Visibility( - visible: model.isInstalled, - child: CustomMaterialButton( - label: I18nText('installerView.openButton'), - isExpanded: true, - onPressed: () { - model.openApp(); - model.cleanPatcher(); - Navigator.of(context).pop(); - }, - ), - ), - Visibility( - visible: !model.isInstalled && model.isRooted, - child: CustomMaterialButton( - isFilled: false, - label: - I18nText('installerView.installRootButton'), - isExpanded: true, - onPressed: () => model.installResult( - context, - true, - ), - ), - ), - Visibility( - visible: !model.isInstalled, - child: const SizedBox( - width: 16, - ), - ), - Visibility( - visible: !model.isInstalled, - child: CustomMaterialButton( - label: I18nText('installerView.installButton'), - isExpanded: true, - onPressed: () => model.installResult( - context, - false, - ), - ), - ), - ], - ), - ), - ), - ), - ), - SliverFillRemaining( - hasScrollBody: false, - child: SizedBox( - height: MediaQuery.of(context).viewPadding.bottom, - ), - ), ], ), ), diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index 3258e9ba..047c9a88 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -169,6 +169,86 @@ class InstallerViewModel extends BaseViewModel { } } + Future installTypeDialog(BuildContext context) async { + final ValueNotifier installType = ValueNotifier(0); + if (isRooted) { + await showDialog( + context: context, + barrierDismissible: false, + builder: (context) => AlertDialog( + title: I18nText( + 'installerView.installType', + ), + icon: const Icon(Icons.file_download_outlined), + contentPadding: const EdgeInsets.symmetric(vertical: 16), + content: 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: I18nText( + 'installerView.installTypeDescription', + child: Text( + '', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.secondary, + ), + ), + ), + ), + RadioListTile( + title: const Text('Non-root'), + subtitle: const Text('Recommended'), + contentPadding: const EdgeInsets.symmetric(horizontal: 10), + value: 0, + groupValue: value, + onChanged: (selected) { + installType.value = selected!; + }, + ), + RadioListTile( + title: const Text('Root'), + contentPadding: const EdgeInsets.symmetric(horizontal: 10), + value: 1, + groupValue: value, + onChanged: (selected) { + installType.value = selected!; + }, + ), + ], + ); + }, + ), + actions: [ + CustomMaterialButton( + label: I18nText('cancelButton'), + isFilled: false, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + CustomMaterialButton( + label: I18nText('installerView.installButton'), + onPressed: () { + Navigator.of(context).pop(); + installResult(context, installType.value == 1); + }, + ) + ], + ), + ); + } else { + installResult(context, false); + } + } + Future stopPatcher() async { try { isCanceled = true; @@ -253,18 +333,8 @@ class InstallerViewModel extends BaseViewModel { } } - void shareResult() { - try { - _patcherAPI.sharePatchedFile(_app.name, _app.version); - } on Exception catch (e) { - if (kDebugMode) { - print(e); - } - } - } - - void shareLog() { - _patcherAPI.sharePatcherLog(logs); + void exportLog() { + _patcherAPI.exportPatcherLog(logs); } Future cleanPatcher() async { @@ -284,16 +354,13 @@ class InstallerViewModel extends BaseViewModel { DeviceApps.openApp(_app.packageName); } - void onMenuSelection(int value) { + void onButtonPressed(int value) { switch (value) { case 0: - shareResult(); - break; - case 1: exportResult(); break; - case 2: - shareLog(); + case 1: + exportLog(); break; } }