diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 45cc0b79..e19930ec 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -66,5 +66,10 @@ "versionLabel": "Version", "aboutLabel": "About", "contributorsLabel": "Contributors" + }, + "rootCheckerView": { + "widgetTitle": "Is your device rooted?", + "widgetDescription": "Don't know what's this or prefer to use non-root version? Just click button below!", + "grantPermission": "Grant Root Permission" } } \ No newline at end of file diff --git a/assets/images/magisk.svg b/assets/images/magisk.svg new file mode 100644 index 00000000..8395e80c --- /dev/null +++ b/assets/images/magisk.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/app/app.dart b/lib/app/app.dart index f94544a1..d42f7bb8 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -2,11 +2,13 @@ import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/ui/views/app_selector/app_selector_view.dart'; import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/contributors/contributors_view.dart'; +import 'package:revanced_manager/ui/views/home/home_view.dart'; import 'package:revanced_manager/ui/views/installer/installer_view.dart'; import 'package:revanced_manager/ui/views/installer/installer_viewmodel.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/patches_selector/patches_selector_viewmodel.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:stacked/stacked_annotations.dart'; import 'package:stacked_services/stacked_services.dart'; @@ -14,11 +16,13 @@ import 'package:stacked_themes/stacked_themes.dart'; @StackedApp( routes: [ + MaterialRoute(page: HomeView), MaterialRoute(page: AppSelectorView), MaterialRoute(page: PatchesSelectorView), MaterialRoute(page: InstallerView), MaterialRoute(page: SettingsView), - MaterialRoute(page: ContributorsView) + MaterialRoute(page: ContributorsView), + MaterialRoute(page: RootCheckerView), ], dependencies: [ LazySingleton(classType: NavigationService), diff --git a/lib/app/app.locator.dart b/lib/app/app.locator.dart index 8a5d9aab..7b2ee8cf 100644 --- a/lib/app/app.locator.dart +++ b/lib/app/app.locator.dart @@ -4,7 +4,7 @@ // StackedLocatorGenerator // ************************************************************************** -// ignore_for_file: public_member_api_docs, depend_on_referenced_packages +// ignore_for_file: public_member_api_docs import 'package:stacked_core/stacked_core.dart'; import 'package:stacked_services/stacked_services.dart'; diff --git a/lib/app/app.router.dart b/lib/app/app.router.dart index 1d074b86..2105676d 100644 --- a/lib/app/app.router.dart +++ b/lib/app/app.router.dart @@ -12,22 +12,28 @@ import 'package:stacked_services/stacked_services.dart'; import '../ui/views/app_selector/app_selector_view.dart'; import '../ui/views/contributors/contributors_view.dart'; +import '../ui/views/home/home_view.dart'; import '../ui/views/installer/installer_view.dart'; import '../ui/views/patches_selector/patches_selector_view.dart'; +import '../ui/views/root_checker/root_checker_view.dart'; import '../ui/views/settings/settings_view.dart'; class Routes { + static const String homeView = '/home-view'; static const String appSelectorView = '/app-selector-view'; static const String patchesSelectorView = '/patches-selector-view'; static const String installerView = '/installer-view'; static const String settingsView = '/settings-view'; static const String contributorsView = '/contributors-view'; + static const String rootCheckerView = '/root-checker-view'; static const all = { + homeView, appSelectorView, patchesSelectorView, installerView, settingsView, contributorsView, + rootCheckerView, }; } @@ -35,15 +41,23 @@ class StackedRouter extends RouterBase { @override List get routes => _routes; final _routes = [ + RouteDef(Routes.homeView, page: HomeView), RouteDef(Routes.appSelectorView, page: AppSelectorView), RouteDef(Routes.patchesSelectorView, page: PatchesSelectorView), RouteDef(Routes.installerView, page: InstallerView), RouteDef(Routes.settingsView, page: SettingsView), RouteDef(Routes.contributorsView, page: ContributorsView), + RouteDef(Routes.rootCheckerView, page: RootCheckerView), ]; @override Map get pagesMap => _pagesMap; final _pagesMap = { + HomeView: (data) { + return MaterialPageRoute( + builder: (context) => const HomeView(), + settings: data, + ); + }, AppSelectorView: (data) { return MaterialPageRoute( builder: (context) => const AppSelectorView(), @@ -77,6 +91,12 @@ class StackedRouter extends RouterBase { settings: data, ); }, + RootCheckerView: (data) { + return MaterialPageRoute( + builder: (context) => const RootCheckerView(), + settings: data, + ); + }, }; } @@ -95,6 +115,22 @@ class InstallerViewArguments { /// ************************************************************************* extension NavigatorStateExtension on NavigationService { + Future navigateToHomeView({ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + }) async { + return navigateTo( + Routes.homeView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition, + ); + } + Future navigateToAppSelectorView({ int? routerId, bool preventDuplicates = true, @@ -176,4 +212,20 @@ extension NavigatorStateExtension on NavigationService { transition: transition, ); } + + Future navigateToRootCheckerView({ + int? routerId, + bool preventDuplicates = true, + Map? parameters, + Widget Function(BuildContext, Animation, Animation, Widget)? + transition, + }) async { + return navigateTo( + Routes.rootCheckerView, + id: routerId, + preventDuplicates: preventDuplicates, + parameters: parameters, + transition: transition, + ); + } } diff --git a/lib/main.dart b/lib/main.dart index af039fdf..5534ac1b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -54,7 +54,9 @@ class MyApp extends StatelessWidget { } class Navigation extends StatelessWidget { - const Navigation({Key? key}) : super(key: key); + const Navigation({ + Key? key, + }) : super(key: key); @override Widget build(BuildContext context) { diff --git a/lib/ui/views/root_checker/root_checker_view.dart b/lib/ui/views/root_checker/root_checker_view.dart new file mode 100644 index 00000000..4ec4e9ca --- /dev/null +++ b/lib/ui/views/root_checker/root_checker_view.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/widgets/I18nText.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/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: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + const Text('nonroot'), + const SizedBox(height: 8), + FloatingActionButton( + onPressed: model.navigateToHome, + backgroundColor: Theme.of(context).colorScheme.secondary, + foregroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(48), + ), + child: const Icon( + Icons.keyboard_arrow_right, + size: 32, + ), + ), + ], + ), + body: Container( + height: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 28.0), + child: Column( + mainAxisSize: MainAxisSize.max, + children: [ + const SizedBox(height: 120), + I18nText( + 'rootCheckerView.widgetTitle', + child: Text( + '', + style: GoogleFonts.jetBrainsMono( + fontSize: 24, + ), + ), + ), + const SizedBox(height: 24), + I18nText( + 'rootCheckerView.widgetDescription', + child: Text( + '', + textAlign: TextAlign.center, + style: GoogleFonts.roboto( + fontSize: 17, + letterSpacing: 1.1, + ), + ), + ), + const SizedBox(height: 170), + MagiskButton( + onPressed: () { + model.getMagiskPermissions(); + Future.delayed(const Duration(seconds: 5), () { + model.checkRoot(); + }); + }, + ), + Text( + "Magisk permission granted: ${model.isRooted.toString()}", + style: GoogleFonts.poppins(), + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/ui/views/root_checker/root_checker_viewmodel.dart b/lib/ui/views/root_checker/root_checker_viewmodel.dart new file mode 100644 index 00000000..7aff462d --- /dev/null +++ b/lib/ui/views/root_checker/root_checker_viewmodel.dart @@ -0,0 +1,38 @@ +import 'package:fluttertoast/fluttertoast.dart'; +import 'package:revanced_manager/app/app.locator.dart'; +import 'package:revanced_manager/app/app.router.dart'; +import 'package:revanced_manager/ui/views/home/home_view.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:stacked/stacked.dart'; +import 'package:root/root.dart'; +import 'package:stacked_services/stacked_services.dart'; + +class RootCheckerViewModel extends BaseViewModel { + final _navigationService = locator(); + bool? isRooted = false; + + Future initialize() async { + await checkRoot(); + notifyListeners(); + } + + Future checkRoot() async { + isRooted = await Root.isRooted(); + notifyListeners(); + } + + Future getMagiskPermissions() async { + if (isRooted == true) { + Fluttertoast.showToast(msg: 'Magisk permission already granted!'); + } + await Root.exec(cmd: "adb shell su -c exit"); + notifyListeners(); + } + + Future navigateToHome() async { + final prefs = await SharedPreferences.getInstance(); + prefs.setBool('showHome', true); + _navigationService.navigateTo(Routes.homeView); + notifyListeners(); + } +} diff --git a/lib/ui/widgets/magisk_button.dart b/lib/ui/widgets/magisk_button.dart new file mode 100644 index 00000000..973eea65 --- /dev/null +++ b/lib/ui/widgets/magisk_button.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/widgets/I18nText.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:revanced_manager/theme.dart'; + +class MagiskButton extends StatelessWidget { + final Function()? onPressed; + const MagiskButton({ + Key? key, + this.onPressed, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + GestureDetector( + onTap: onPressed, + child: CircleAvatar( + radius: 32, + backgroundColor: isDark + ? Theme.of(context).colorScheme.secondary + : const Color(0xffCBDFFC), + child: SvgPicture.asset( + 'assets/images/magisk.svg', + color: isDark ? Colors.white70 : Colors.grey[900], + height: 50, + width: 50, + ), + ), + ), + const SizedBox(height: 8), + I18nText( + 'rootCheckerView.grantPermission', + child: Text( + '', + style: GoogleFonts.poppins( + fontSize: 15, + ), + ), + ), + ], + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 7841562e..86a98033 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,6 +32,7 @@ dependencies: path_provider: ^2.0.11 root: ^2.0.2 share_extend: ^2.0.0 + shared_preferences: ^2.0.15 stacked: ^2.3.15 stacked_generator: ^0.7.14 stacked_services: ^0.9.3