diff --git a/assets/i18n/en.json b/assets/i18n/en.json index e4b8ffeb..5e611243 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -3,6 +3,8 @@ "cancelButton": "Cancel", "enabledLabel": "Enabled", "disabledLabel": "Disabled", + "yesLabel": "Yes", + "noLabel": "No", "main": { "dashboardTab": "Dashboard", "patcherTab": "Patcher", @@ -79,10 +81,13 @@ "installerView": { "widgetTitle": "Installer", "installButton": "Install", + "installRootButton": "Install as Root", "openButton": "Open", "shareButton": "Share file", "notificationTitle": "ReVanced Manager is patching", - "notificationText": "Tap to return to the installer" + "notificationText": "Tap to return to the installer", + "shareApkMenuOption": "Share APK", + "shareLogMenuOption": "Share log" }, "settingsView": { "widgetTitle": "Settings", @@ -97,20 +102,11 @@ "languageLabel": "Language", "englishOption": "English", "frenchOption": "French", - "rootModeLabel": "Root Mode", - "rootModeHint": "Do you want to patch applications as rooted?", "contributorsLabel": "Contributors", "contributorsHint": "A list of contributors of ReVanced", "aboutLabel": "About", "versionLabel": "Version" }, - "rootCheckerView": { - "widgetTitle": "Is your device rooted?", - "widgetDescription": "Don't know what this means or prefer to use non-root version? Just click on the button below!", - "grantPermission": "Grant Root Permission", - "grantedPermission": "Magisk permission granted: {isRooted}", - "nonRootButton": "Nonroot" - }, "appInfoView": { "widgetTitle": "App Info", "openButton": "Open", @@ -120,7 +116,9 @@ "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", + "installTypeLabel": "Installation Type", + "rootTypeLabel": "Root", + "nonRootTypeLabel": "Non-root", "patchedDateLabel": "Patched Date", "patchedDateHint": "{date} at {time}", "appliedPatchesLabel": "Applied Patches", diff --git a/assets/images/magisk.svg b/assets/images/magisk.svg deleted file mode 100644 index 8395e80c..00000000 --- a/assets/images/magisk.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/lib/app/app.dart b/lib/app/app.dart index 95405a24..653162dd 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -9,7 +9,6 @@ import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_view.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patches_selector/patches_selector_view.dart'; -import 'package:revanced_manager/ui/views/root_checker/root_checker_view.dart'; import 'package:revanced_manager/ui/views/settings/settings_view.dart'; import 'package:revanced_manager/ui/widgets/appInfoView/app_info_view.dart'; import 'package:stacked/stacked_annotations.dart'; @@ -24,7 +23,6 @@ import 'package:stacked_services/stacked_services.dart'; MaterialRoute(page: InstallerView), MaterialRoute(page: SettingsView), MaterialRoute(page: ContributorsView), - MaterialRoute(page: RootCheckerView), MaterialRoute(page: AppInfoView), ], dependencies: [ diff --git a/lib/main.dart b/lib/main.dart index a44aa11e..925d9415 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,7 +7,6 @@ import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/ui/theme/dynamic_theme_builder.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_view.dart'; -import 'package:revanced_manager/ui/views/root_checker/root_checker_view.dart'; import 'package:stacked_themes/stacked_themes.dart'; Future main() async { @@ -15,6 +14,7 @@ Future main() async { await setupLocator(); WidgetsFlutterBinding.ensureInitialized(); await locator().initialize(); + await locator().initialize(); runApp(const MyApp()); } @@ -25,20 +25,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return DynamicThemeBuilder( title: 'ReVanced Manager', - home: FutureBuilder( - future: _init(context), - builder: (context, snapshot) { - if (snapshot.hasData) { - return snapshot.data!; - } else { - return Center( - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.secondary, - ), - ); - } - }, - ), + home: const NavigationView(), localizationsDelegates: [ FlutterI18nDelegate( translationLoader: FileTranslationLoader( @@ -51,14 +38,4 @@ class MyApp extends StatelessWidget { ], ); } - - Future _init(BuildContext context) async { - await locator().initialize(); - await locator().initialize(); - bool? isRooted = locator().isRooted(); - if (isRooted != null) { - return const NavigationView(); - } - return const RootCheckerView(); - } } diff --git a/lib/models/patched_application.dart b/lib/models/patched_application.dart index 3b97a642..35066087 100644 --- a/lib/models/patched_application.dart +++ b/lib/models/patched_application.dart @@ -16,7 +16,7 @@ class PatchedApplication { ) Uint8List icon; DateTime patchDate; - final bool isRooted; + bool isRooted; bool hasUpdates; List appliedPatches; List changelog; diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index 70f7fc1b..31fc7f8a 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -65,14 +65,6 @@ class ManagerAPI { await _prefs.setBool('useDarkTheme', value); } - bool? isRooted() { - return _prefs.getBool('isRooted'); - } - - Future setIsRooted(bool value) async { - await _prefs.setBool('isRooted', value); - } - List getPatchedApps() { List apps = _prefs.getStringList('patchedApps') ?? []; return apps @@ -109,11 +101,10 @@ class ManagerAPI { } Future reAssessSavedApps() async { - bool isRooted = this.isRooted() ?? false; List patchedApps = getPatchedApps(); List toRemove = []; for (PatchedApplication app in patchedApps) { - bool isRemove = await isAppUninstalled(app, isRooted); + bool isRemove = await isAppUninstalled(app); if (isRemove) { toRemove.add(app); } else { @@ -139,9 +130,9 @@ class ManagerAPI { await setPatchedApps(patchedApps); } - Future isAppUninstalled(PatchedApplication app, bool isRooted) async { + Future isAppUninstalled(PatchedApplication app) async { bool existsRoot = false; - if (isRooted) { + if (app.isRooted) { existsRoot = await _rootAPI.isAppInstalled(app.packageName); } bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName); diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index a70cc2f2..9c5cb9ea 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -147,11 +147,16 @@ class PatcherAPI { if (_outFile != null) { try { if (patchedApp.isRooted) { - return _rootAPI.installApp( - patchedApp.packageName, - patchedApp.apkFilePath, - _outFile!.path, - ); + bool hasRootPermissions = await _rootAPI.hasRootPermissions(); + if (hasRootPermissions) { + return _rootAPI.installApp( + patchedApp.packageName, + patchedApp.apkFilePath, + _outFile!.path, + ); + } else { + return false; + } } else { await AppInstaller.installApk(_outFile!.path); return await DeviceApps.isAppInstalled(patchedApp.packageName); @@ -163,14 +168,15 @@ class PatcherAPI { return false; } - bool sharePatchedFile(String appName, String version) { + void sharePatchedFile(String appName, String version) { if (_outFile != null) { String prefix = appName.toLowerCase().replaceAll(' ', '-'); - File share = _outFile!.renameSync('$prefix-revanced_v$version.apk'); + String newName = '$prefix-revanced_v$version.apk'; + int lastSeparator = _outFile!.path.lastIndexOf('/'); + File share = _outFile!.renameSync( + _outFile!.path.substring(0, lastSeparator + 1) + newName, + ); ShareExtend.share(share.path, 'file'); - return true; - } else { - return false; } } @@ -186,4 +192,8 @@ class PatcherAPI { await _rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath); } } + + void shareLog(String logs) { + ShareExtend.share(logs, 'text'); + } } diff --git a/lib/services/root_api.dart b/lib/services/root_api.dart index 33735a04..8e454ab7 100644 --- a/lib/services/root_api.dart +++ b/lib/services/root_api.dart @@ -5,6 +5,11 @@ class RootAPI { final String _postFsDataDirPath = '/data/adb/post-fs-data.d'; final String _serviceDDirPath = '/data/adb/service.d'; + Future hasRootPermissions() async { + bool? isRooted = await Root.isRooted(); + return isRooted != null && isRooted; + } + Future isAppInstalled(String packageName) async { if (packageName.isNotEmpty) { String? res = await Root.exec( diff --git a/lib/ui/views/app_selector/app_selector_viewmodel.dart b/lib/ui/views/app_selector/app_selector_viewmodel.dart index 329993fc..95b8063f 100644 --- a/lib/ui/views/app_selector/app_selector_viewmodel.dart +++ b/lib/ui/views/app_selector/app_selector_viewmodel.dart @@ -6,23 +6,19 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:fluttertoast/fluttertoast.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/ui/views/patcher/patcher_viewmodel.dart'; import 'package:stacked/stacked.dart'; class AppSelectorViewModel extends BaseViewModel { - final ManagerAPI _managerAPI = locator(); final PatcherAPI _patcherAPI = locator(); final List apps = []; bool noApps = false; - bool _isRooted = false; Future initialize() async { apps.addAll(await _patcherAPI.getFilteredInstalledApps()); apps.sort((a, b) => a.appName.compareTo(b.appName)); noApps = apps.isEmpty; - _isRooted = _managerAPI.isRooted() ?? false; notifyListeners(); } @@ -34,7 +30,7 @@ class AppSelectorViewModel extends BaseViewModel { apkFilePath: application.apkFilePath, icon: application.icon, patchDate: DateTime.now(), - isRooted: _isRooted, + isRooted: false, ); locator().selectedPatches.clear(); locator().notifyListeners(); diff --git a/lib/ui/views/installer/installer_view.dart b/lib/ui/views/installer/installer_view.dart index 8729c735..8c875b11 100644 --- a/lib/ui/views/installer/installer_view.dart +++ b/lib/ui/views/installer/installer_view.dart @@ -4,6 +4,7 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.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'; @@ -27,6 +28,34 @@ class InstallerView extends StatelessWidget { color: Theme.of(context).textTheme.headline6!.color, ), ), + actions: [ + Visibility( + visible: !model.isPatching, + child: CustomPopupMenu( + onSelected: (value) => model.onMenuSelection(value), + children: { + 0: I18nText( + 'installerView.shareApkMenuOption', + child: const Text( + '', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + 1: I18nText( + 'installerView.shareLogMenuOption', + child: const Text( + '', + style: TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ), + }, + ), + ), + ], bottom: PreferredSize( preferredSize: const Size(double.infinity, 1.0), child: LinearProgressIndicator( @@ -60,26 +89,42 @@ class InstallerView extends StatelessWidget { child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - CustomMaterialButton( - label: I18nText('installerView.shareButton'), - isFilled: false, - onPressed: () => model.shareResult(), - ), - const SizedBox(width: 16), - CustomMaterialButton( - label: model.isInstalled - ? I18nText('installerView.openButton') - : I18nText('installerView.installButton'), - isExpanded: true, - onPressed: () { - if (model.isInstalled) { + Visibility( + visible: model.isInstalled, + child: CustomMaterialButton( + label: I18nText('installerView.openButton'), + isExpanded: true, + onPressed: () { model.openApp(); model.cleanPatcher(); Navigator.of(context).pop(); - } else { - model.installResult(); - } - }, + }, + ), + ), + Visibility( + visible: !model.isInstalled, + child: CustomMaterialButton( + isFilled: false, + label: + I18nText('installerView.installButton'), + isExpanded: true, + onPressed: () => model.installResult(false), + ), + ), + Visibility( + visible: !model.isInstalled, + child: const SizedBox( + width: 16, + ), + ), + Visibility( + visible: !model.isInstalled, + child: CustomMaterialButton( + label: I18nText( + 'installerView.installRootButton'), + isExpanded: true, + onPressed: () => model.installResult(true), + ), ), ], ), diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index 71683708..4463c604 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -14,7 +14,7 @@ import 'package:stacked/stacked.dart'; class InstallerViewModel extends BaseViewModel { final ManagerAPI _managerAPI = locator(); final PatcherAPI _patcherAPI = locator(); - final PatchedApplication? _app = locator().selectedApp; + final PatchedApplication _app = locator().selectedApp!; final List _patches = locator().selectedPatches; static const _installerChannel = MethodChannel( 'app.revanced.manager/installer', @@ -98,19 +98,19 @@ class InstallerViewModel extends BaseViewModel { Future runPatcher() async { update(0.0, 'Initializing...', 'Initializing installer'); - if (_app != null && _patches.isNotEmpty) { - String apkFilePath = _app!.apkFilePath; + if (_patches.isNotEmpty) { + String apkFilePath = _app.apkFilePath; try { - if (_app!.isRooted) { + if (_app.isRooted) { update(0.0, '', 'Checking if an old patched version exists'); - bool oldExists = await _patcherAPI.checkOldPatch(_app!); + bool oldExists = await _patcherAPI.checkOldPatch(_app); if (oldExists) { update(0.0, '', 'Deleting old patched version'); - await _patcherAPI.deleteOldPatch(_app!); + await _patcherAPI.deleteOldPatch(_app); } } update(0.0, '', 'Creating working directory'); - await _patcherAPI.runPatcher(_app!.packageName, apkFilePath, _patches); + await _patcherAPI.runPatcher(_app.packageName, apkFilePath, _patches); } on Exception { update(1.0, 'Aborting...', 'An error occurred! Aborting'); } @@ -124,31 +124,32 @@ class InstallerViewModel extends BaseViewModel { } } - void installResult() async { - if (_app != null) { - update( - 1.0, - 'Installing...', - _app!.isRooted - ? 'Installing patched file using root method' - : 'Installing patched file using nonroot method', - ); - isInstalled = await _patcherAPI.installPatchedFile(_app!); - if (isInstalled) { - update(1.0, 'Installed!', 'Installed!'); - _app!.patchDate = DateTime.now(); - _app!.appliedPatches = _patches.map((p) => p.name).toList(); - await _managerAPI.savePatchedApp(_app!); - } else { - update(1.0, 'Aborting...', 'An error occurred! Aborting'); - } + void installResult(bool installAsRoot) async { + _app.isRooted = installAsRoot; + update( + 1.0, + 'Installing...', + _app.isRooted + ? 'Installing patched file using root method' + : 'Installing patched file using nonroot method', + ); + isInstalled = await _patcherAPI.installPatchedFile(_app); + if (isInstalled) { + update(1.0, 'Installed!', 'Installed!'); + _app.patchDate = DateTime.now(); + _app.appliedPatches = _patches.map((p) => p.name).toList(); + await _managerAPI.savePatchedApp(_app); + } else { + update(1.0, 'Aborting...', 'An error occurred! Aborting'); } } void shareResult() { - if (_app != null) { - _patcherAPI.sharePatchedFile(_app!.name, _app!.version); - } + _patcherAPI.sharePatchedFile(_app.name, _app.version); + } + + void shareLog() { + _patcherAPI.shareLog(logs); } Future cleanPatcher() async { @@ -159,8 +160,17 @@ class InstallerViewModel extends BaseViewModel { } void openApp() { - if (_app != null) { - DeviceApps.openApp(_app!.packageName); + DeviceApps.openApp(_app.packageName); + } + + void onMenuSelection(int value) { + switch (value) { + case 0: + shareResult(); + break; + case 1: + shareLog(); + break; } } } diff --git a/lib/ui/views/root_checker/root_checker_view.dart b/lib/ui/views/root_checker/root_checker_view.dart deleted file mode 100644 index 4879848c..00000000 --- a/lib/ui/views/root_checker/root_checker_view.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_i18n/flutter_i18n.dart'; -import 'package:google_fonts/google_fonts.dart'; -import 'package:revanced_manager/ui/views/root_checker/root_checker_viewmodel.dart'; -import 'package:revanced_manager/ui/widgets/rootCheckerView/magisk_button.dart'; -import 'package:stacked/stacked.dart'; - -class RootCheckerView extends StatelessWidget { - const RootCheckerView({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return ViewModelBuilder.reactive( - onModelReady: (model) => model.initialize(), - viewModelBuilder: () => RootCheckerViewModel(), - builder: (context, model, child) => Scaffold( - floatingActionButton: FloatingActionButton.extended( - label: I18nText('rootCheckerView.nonRootButton'), - icon: const Icon(Icons.keyboard_arrow_right), - onPressed: () => model.navigateAsNonRoot(), - ), - body: Container( - height: double.infinity, - padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 28.0), - child: Column( - children: [ - const SizedBox(height: 120), - I18nText( - 'rootCheckerView.widgetTitle', - child: Text( - '', - style: GoogleFonts.jetBrainsMono( - fontSize: 24, - ), - ), - ), - const SizedBox(height: 24), - I18nText( - 'rootCheckerView.widgetDescription', - child: const Text( - '', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 17, - letterSpacing: 1.1, - ), - ), - ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - MagiskButton( - onPressed: () => model.navigateAsRoot(), - ), - I18nText( - 'rootCheckerView.grantedPermission', - translationParams: { - 'isRooted': model.isRooted.toString(), - }, - ), - ], - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/ui/views/root_checker/root_checker_viewmodel.dart b/lib/ui/views/root_checker/root_checker_viewmodel.dart deleted file mode 100644 index 93684b70..00000000 --- a/lib/ui/views/root_checker/root_checker_viewmodel.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:revanced_manager/app/app.locator.dart'; -import 'package:revanced_manager/app/app.router.dart'; -import 'package:revanced_manager/services/manager_api.dart'; -import 'package:stacked/stacked.dart'; -import 'package:root/root.dart'; -import 'package:stacked_services/stacked_services.dart'; - -class RootCheckerViewModel extends BaseViewModel { - final NavigationService _navigationService = locator(); - final ManagerAPI _managerAPI = locator(); - bool isRooted = false; - - void initialize() { - isRooted = _managerAPI.isRooted() ?? false; - } - - Future navigateAsRoot() async { - bool? res = await Root.isRooted(); - isRooted = res != null && res == true; - if (isRooted) { - await navigateToHome(); - } else { - notifyListeners(); - } - } - - Future navigateAsNonRoot() async { - isRooted = false; - await navigateToHome(); - } - - Future navigateToHome() async { - _managerAPI.setIsRooted(isRooted); - _navigationService.navigateTo(Routes.navigationView); - } -} diff --git a/lib/ui/views/settings/settings_view.dart b/lib/ui/views/settings/settings_view.dart index d1a4ba0d..cef8871e 100644 --- a/lib/ui/views/settings/settings_view.dart +++ b/lib/ui/views/settings/settings_view.dart @@ -113,21 +113,6 @@ class SettingsView extends StatelessWidget { SettingsSection( title: 'settingsView.patcherSectionTitle', children: [ - ListTile( - contentPadding: EdgeInsets.zero, - title: I18nText( - 'settingsView.rootModeLabel', - child: const Text( - '', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.w500, - ), - ), - ), - subtitle: I18nText('settingsView.rootModeHint'), - onTap: () => model.navigateToRootChecker(), - ), SourcesWidget( title: 'settingsView.sourcesLabel', organizationController: organizationController, diff --git a/lib/ui/views/settings/settings_viewmodel.dart b/lib/ui/views/settings/settings_viewmodel.dart index 53c3c4a6..bec01446 100644 --- a/lib/ui/views/settings/settings_viewmodel.dart +++ b/lib/ui/views/settings/settings_viewmodel.dart @@ -18,10 +18,6 @@ class SettingsViewModel extends BaseViewModel { notifyListeners(); } - void navigateToRootChecker() { - _navigationService.navigateTo(Routes.rootCheckerView); - } - void navigateToContributors() { _navigationService.navigateTo(Routes.contributorsView); } diff --git a/lib/ui/widgets/appInfoView/app_info_view.dart b/lib/ui/widgets/appInfoView/app_info_view.dart index 2e5cb676..77445822 100644 --- a/lib/ui/widgets/appInfoView/app_info_view.dart +++ b/lib/ui/widgets/appInfoView/app_info_view.dart @@ -1,4 +1,3 @@ -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'; @@ -19,7 +18,6 @@ class AppInfoView extends StatelessWidget { @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( - onModelReady: (model) => model.initialize(), viewModelBuilder: () => AppInfoViewModel(), builder: (context, model, child) => Scaffold( body: CustomScrollView( @@ -68,9 +66,7 @@ class AppInfoView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ InkWell( - onTap: () => DeviceApps.openApp( - app.packageName, - ), + onTap: () => model.openApp(app), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -180,7 +176,7 @@ class AppInfoView extends StatelessWidget { ListTile( contentPadding: EdgeInsets.zero, title: I18nText( - 'appInfoView.rootModeLabel', + 'appInfoView.installTypeLabel', child: const Text( '', style: TextStyle( @@ -189,9 +185,9 @@ class AppInfoView extends StatelessWidget { ), ), ), - subtitle: model.isRooted - ? I18nText('enabledLabel') - : I18nText('disabledLabel'), + subtitle: app.isRooted + ? I18nText('appInfoView.rootTypeLabel') + : I18nText('appInfoView.nonRootTypeLabel'), ), const SizedBox(height: 4), ListTile( diff --git a/lib/ui/widgets/appInfoView/app_info_viewmodel.dart b/lib/ui/widgets/appInfoView/app_info_viewmodel.dart index 179442d5..0ae8172a 100644 --- a/lib/ui/widgets/appInfoView/app_info_viewmodel.dart +++ b/lib/ui/widgets/appInfoView/app_info_viewmodel.dart @@ -17,11 +17,6 @@ class AppInfoViewModel extends BaseViewModel { final ManagerAPI _managerAPI = locator(); final PatcherAPI _patcherAPI = locator(); final RootAPI _rootAPI = RootAPI(); - bool isRooted = false; - - void initialize() { - isRooted = _managerAPI.isRooted() ?? false; - } void uninstallApp(PatchedApplication app) { if (app.isRooted) { @@ -45,7 +40,8 @@ class AppInfoViewModel extends BaseViewModel { BuildContext context, PatchedApplication app, ) async { - if (app.isRooted && !isRooted) { + bool hasRootPermissions = await _rootAPI.hasRootPermissions(); + if (app.isRooted && !hasRootPermissions) { return showDialog( context: context, builder: (context) => AlertDialog( @@ -129,4 +125,8 @@ class AppInfoViewModel extends BaseViewModel { .toList(); return '\u2022 ${names.join('\n\u2022 ')}'; } + + void openApp(PatchedApplication app) { + DeviceApps.openApp(app.packageName); + } } diff --git a/lib/ui/widgets/installerView/custom_material_button.dart b/lib/ui/widgets/installerView/custom_material_button.dart index 772e66a2..b8b0690e 100644 --- a/lib/ui/widgets/installerView/custom_material_button.dart +++ b/lib/ui/widgets/installerView/custom_material_button.dart @@ -23,7 +23,16 @@ class CustomMaterialButton extends StatelessWidget { ? const EdgeInsets.symmetric(horizontal: 24, vertical: 12) : const EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), - shape: MaterialStateProperty.all(const StadiumBorder()), + shape: MaterialStateProperty.all( + StadiumBorder( + side: isFilled + ? BorderSide.none + : BorderSide( + width: 1, + color: Theme.of(context).colorScheme.primary, + ), + ), + ), backgroundColor: MaterialStateProperty.all( isFilled ? Theme.of(context).colorScheme.primary : Colors.transparent, ), diff --git a/lib/ui/widgets/rootCheckerView/magisk_button.dart b/lib/ui/widgets/rootCheckerView/magisk_button.dart deleted file mode 100644 index cf678f3f..00000000 --- a/lib/ui/widgets/rootCheckerView/magisk_button.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_i18n/flutter_i18n.dart'; -import 'package:flutter_svg/flutter_svg.dart'; - -class MagiskButton extends StatelessWidget { - final Function() onPressed; - - const MagiskButton({ - Key? key, - required this.onPressed, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - GestureDetector( - onTap: onPressed, - child: CircleAvatar( - radius: 32, - backgroundColor: Theme.of(context).colorScheme.primary, - child: SvgPicture.asset( - 'assets/images/magisk.svg', - color: Theme.of(context).colorScheme.surface, - height: 40, - width: 40, - ), - ), - ), - const SizedBox(height: 8), - I18nText( - 'rootCheckerView.grantPermission', - child: const Text( - '', - style: TextStyle(fontSize: 15), - ), - ), - ], - ); - } -} diff --git a/lib/ui/widgets/shared/custom_popup_menu.dart b/lib/ui/widgets/shared/custom_popup_menu.dart new file mode 100644 index 00000000..67dbd784 --- /dev/null +++ b/lib/ui/widgets/shared/custom_popup_menu.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class CustomPopupMenu extends StatelessWidget { + final Function(dynamic) onSelected; + final Map children; + + const CustomPopupMenu({ + Key? key, + required this.onSelected, + required this.children, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Theme( + data: Theme.of(context).copyWith(useMaterial3: false), + child: PopupMenuButton( + onSelected: onSelected, + itemBuilder: (context) => children.entries + .map( + (entry) => PopupMenuItem( + padding: const EdgeInsets.all(16.0).copyWith(right: 20), + value: entry.key, + child: entry.value, + ), + ) + .toList(), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(24), + ), + color: Theme.of(context).colorScheme.secondaryContainer, + position: PopupMenuPosition.under, + ), + ); + } +} diff --git a/lib/ui/widgets/shared/custom_sliver_app_bar.dart b/lib/ui/widgets/shared/custom_sliver_app_bar.dart index 34304f09..be683e79 100644 --- a/lib/ui/widgets/shared/custom_sliver_app_bar.dart +++ b/lib/ui/widgets/shared/custom_sliver_app_bar.dart @@ -2,11 +2,13 @@ import 'package:flutter/material.dart'; class CustomSliverAppBar extends StatelessWidget { final Widget title; + final List? actions; final PreferredSizeWidget? bottom; const CustomSliverAppBar({ Key? key, required this.title, + this.actions, this.bottom, }) : super(key: key); @@ -30,6 +32,7 @@ class CustomSliverAppBar extends StatelessWidget { ), title: title, ), + actions: actions, bottom: bottom, ); } diff --git a/pubspec.yaml b/pubspec.yaml index 04d60209..b39b6d6f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -63,5 +63,4 @@ dev_dependencies: flutter: uses-material-design: true assets: - - assets/images/ - assets/i18n/