diff --git a/assets/i18n/en.json b/assets/i18n/en.json index e19930ec..47182e67 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -70,6 +70,7 @@ "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" + "grantPermission": "Grant Root Permission", + "grantedPermission": "Magisk permission granted: {isRooted}" } } \ No newline at end of file diff --git a/lib/app/app.dart b/lib/app/app.dart index d42f7bb8..6d7ab8ec 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,8 +1,10 @@ +import 'package:revanced_manager/main.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/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'; @@ -16,7 +18,7 @@ import 'package:stacked_themes/stacked_themes.dart'; @StackedApp( routes: [ - MaterialRoute(page: HomeView), + MaterialRoute(page: Navigation), MaterialRoute(page: AppSelectorView), MaterialRoute(page: PatchesSelectorView), MaterialRoute(page: InstallerView), @@ -27,12 +29,16 @@ import 'package:stacked_themes/stacked_themes.dart'; dependencies: [ LazySingleton(classType: NavigationService), LazySingleton(classType: PatcherAPI), + LazySingleton(classType: ManagerAPI), + LazySingleton(classType: RootAPI), LazySingleton(classType: PatcherViewModel), LazySingleton(classType: AppSelectorViewModel), LazySingleton(classType: PatchesSelectorViewModel), LazySingleton(classType: InstallerViewModel), LazySingleton( - classType: ThemeService, resolveUsing: ThemeService.getInstance), + classType: ThemeService, + resolveUsing: ThemeService.getInstance, + ), ], ) class AppSetup {} diff --git a/lib/app/app.locator.dart b/lib/app/app.locator.dart index 7b2ee8cf..6476d9a5 100644 --- a/lib/app/app.locator.dart +++ b/lib/app/app.locator.dart @@ -4,13 +4,15 @@ // StackedLocatorGenerator // ************************************************************************** -// ignore_for_file: public_member_api_docs +// ignore_for_file: public_member_api_docs, depend_on_referenced_packages import 'package:stacked_core/stacked_core.dart'; import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_themes/stacked_themes.dart'; +import '../services/manager_api.dart'; import '../services/patcher_api.dart'; +import '../services/root_api.dart'; import '../ui/views/app_selector/app_selector_viewmodel.dart'; import '../ui/views/installer/installer_viewmodel.dart'; import '../ui/views/patcher/patcher_viewmodel.dart'; @@ -27,6 +29,8 @@ Future setupLocator( // Register dependencies locator.registerLazySingleton(() => NavigationService()); locator.registerLazySingleton(() => PatcherAPI()); + locator.registerLazySingleton(() => ManagerAPI()); + locator.registerLazySingleton(() => RootAPI()); locator.registerLazySingleton(() => PatcherViewModel()); locator.registerLazySingleton(() => AppSelectorViewModel()); locator.registerLazySingleton(() => PatchesSelectorViewModel()); diff --git a/lib/app/app.router.dart b/lib/app/app.router.dart index 2105676d..62e381df 100644 --- a/lib/app/app.router.dart +++ b/lib/app/app.router.dart @@ -10,16 +10,16 @@ import 'package:flutter/material.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; +import '../main.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 navigation = '/Navigation'; static const String appSelectorView = '/app-selector-view'; static const String patchesSelectorView = '/patches-selector-view'; static const String installerView = '/installer-view'; @@ -27,7 +27,7 @@ class Routes { static const String contributorsView = '/contributors-view'; static const String rootCheckerView = '/root-checker-view'; static const all = { - homeView, + navigation, appSelectorView, patchesSelectorView, installerView, @@ -41,7 +41,7 @@ class StackedRouter extends RouterBase { @override List get routes => _routes; final _routes = [ - RouteDef(Routes.homeView, page: HomeView), + RouteDef(Routes.navigation, page: Navigation), RouteDef(Routes.appSelectorView, page: AppSelectorView), RouteDef(Routes.patchesSelectorView, page: PatchesSelectorView), RouteDef(Routes.installerView, page: InstallerView), @@ -52,9 +52,9 @@ class StackedRouter extends RouterBase { @override Map get pagesMap => _pagesMap; final _pagesMap = { - HomeView: (data) { + Navigation: (data) { return MaterialPageRoute( - builder: (context) => const HomeView(), + builder: (context) => const Navigation(), settings: data, ); }, @@ -115,7 +115,7 @@ class InstallerViewArguments { /// ************************************************************************* extension NavigatorStateExtension on NavigationService { - Future navigateToHomeView({ + Future navigateToNavigation({ int? routerId, bool preventDuplicates = true, Map? parameters, @@ -123,7 +123,7 @@ extension NavigatorStateExtension on NavigationService { transition, }) async { return navigateTo( - Routes.homeView, + Routes.navigation, id: routerId, preventDuplicates: preventDuplicates, parameters: parameters, diff --git a/lib/main.dart b/lib/main.dart index 5534ac1b..c545be8c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,7 +8,9 @@ import 'package:revanced_manager/main_viewmodel.dart'; import 'package:revanced_manager/theme.dart'; import 'package:revanced_manager/ui/views/home/home_view.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_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:shared_preferences/shared_preferences.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; import 'package:stacked_themes/stacked_themes.dart'; @@ -37,7 +39,20 @@ class MyApp extends StatelessWidget { themeMode: themeMode, navigatorKey: StackedService.navigatorKey, onGenerateRoute: StackedRouter().onGenerateRoute, - home: const Navigation(), + home: FutureBuilder( + future: _init(), + builder: (context, snapshot) { + if (snapshot.hasData) { + return snapshot.data!; + } else { + return Center( + child: CircularProgressIndicator( + color: Theme.of(context).colorScheme.secondary, + ), + ); + } + }, + ), localizationsDelegates: [ FlutterI18nDelegate( translationLoader: FileTranslationLoader( @@ -51,6 +66,15 @@ class MyApp extends StatelessWidget { ), ); } + + Future _init() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + bool? isRooted = prefs.getBool('isRooted'); + if (isRooted != null) { + return const Navigation(); + } + return const RootCheckerView(); + } } class Navigation extends StatelessWidget { diff --git a/lib/models/application_info.dart b/lib/models/application_info.dart deleted file mode 100644 index a332fd98..00000000 --- a/lib/models/application_info.dart +++ /dev/null @@ -1,13 +0,0 @@ -class ApplicationInfo { - final String name; - final String packageName; - final String version; - final String apkFilePath; - - ApplicationInfo({ - required this.name, - required this.packageName, - required this.version, - required this.apkFilePath, - }); -} diff --git a/lib/models/patched_application.dart b/lib/models/patched_application.dart new file mode 100644 index 00000000..ecb7a62c --- /dev/null +++ b/lib/models/patched_application.dart @@ -0,0 +1,21 @@ +import 'package:revanced_manager/models/patch.dart'; + +class PatchedApplication { + final String name; + final String packageName; + final String version; + final String apkFilePath; + final bool isRooted; + final bool isFromStorage; + final List appliedPatches; + + PatchedApplication({ + required this.name, + required this.packageName, + required this.version, + required this.apkFilePath, + required this.isRooted, + required this.isFromStorage, + this.appliedPatches = const [], + }); +} diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index 85611cc7..2d2223b0 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -7,8 +7,8 @@ import 'package:revanced_manager/services/github_api.dart'; @lazySingleton class ManagerAPI { - Dio dio = Dio(); - GithubAPI githubAPI = GithubAPI(); + final Dio dio = Dio(); + final GithubAPI githubAPI = GithubAPI(); Future getPath() async { final path = await p.getApplicationSupportDirectory(); diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index 159ed8cd..cd6e11ea 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -6,9 +6,10 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:injectable/injectable.dart'; import 'package:path_provider/path_provider.dart'; import 'package:revanced_manager/app/app.locator.dart'; -import 'package:revanced_manager/models/application_info.dart'; import 'package:revanced_manager/models/patch.dart'; +import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/github_api.dart'; +import 'package:revanced_manager/services/root_api.dart'; import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart'; import 'package:revanced_manager/utils/string.dart'; import 'package:share_extend/share_extend.dart'; @@ -17,9 +18,10 @@ import 'package:share_extend/share_extend.dart'; class PatcherAPI { static const platform = MethodChannel('app.revanced.manager/patcher'); final GithubAPI githubAPI = GithubAPI(); + final RootAPI rootAPI = RootAPI(); final List _filteredPackages = []; final Map> _filteredPatches = >{}; - bool isRoot = false; + Directory? _tmpDir; Directory? _workDir; Directory? _cacheDir; File? _patchBundleFile; @@ -89,7 +91,9 @@ class PatcherAPI { return _filteredPackages; } - Future?> getFilteredPatches(ApplicationInfo? selectedApp) async { + Future?> getFilteredPatches( + PatchedApplication? selectedApp, + ) async { if (_patchBundleFile != null && selectedApp != null) { if (_filteredPatches[selectedApp.packageName] == null || _filteredPatches[selectedApp.packageName]!.isEmpty) { @@ -146,8 +150,8 @@ class PatcherAPI { try { _integrations = await downloadIntegrations(); if (_integrations != null) { - Directory tmpDir = await getTemporaryDirectory(); - _workDir = tmpDir.createTempSync('tmp-'); + _tmpDir = await getTemporaryDirectory(); + _workDir = _tmpDir!.createTempSync('tmp-'); _inputFile = File('${_workDir!.path}/base.apk'); _patchedFile = File('${_workDir!.path}/patched.apk'); _outFile = File('${_workDir!.path}/out.apk'); @@ -256,15 +260,19 @@ class PatcherAPI { return false; } - Future installPatchedFile() async { + Future installPatchedFile(PatchedApplication patchedApp) async { if (_outFile != null) { try { - if (isRoot) { - // TBD + if (patchedApp.isRooted && !patchedApp.isFromStorage) { + return rootAPI.installApp( + patchedApp.packageName, + patchedApp.apkFilePath, + _outFile!.path, + ); } else { await AppInstaller.installApk(_outFile!.path); + return true; } - return true; } on Exception { return false; } @@ -280,11 +288,11 @@ class PatcherAPI { bool sharePatchedFile(String appName, String version) { if (_outFile != null) { - String path = _outFile!.parent.path; + String path = _tmpDir!.path; String prefix = appName.toLowerCase().replaceAll(' ', '-'); String sharePath = '$path/$prefix-revanced_v$version.apk'; File share = _outFile!.copySync(sharePath); - ShareExtend.share(share.path, "file"); + ShareExtend.share(share.path, 'file'); return true; } else { return false; diff --git a/lib/services/root_api.dart b/lib/services/root_api.dart new file mode 100644 index 00000000..f187e74b --- /dev/null +++ b/lib/services/root_api.dart @@ -0,0 +1,85 @@ +import 'dart:io'; + +import 'package:injectable/injectable.dart'; +import 'package:root/root.dart'; + +@lazySingleton +class RootAPI { + final String managerDirPath = "/data/adb/revanced_manager"; + final String postFsDataDirPath = "/data/adb/post-fs-data.d"; + final String serviceDDirPath = "/data/adb/service.d"; + + bool deleteApp(String packageName) { + try { + File('$managerDirPath/$packageName.apk').deleteSync(); + File('$serviceDDirPath/$packageName.sh').deleteSync(); + File('$postFsDataDirPath/$packageName.sh').deleteSync(); + return true; + } on Exception { + return false; + } + } + + Future installApp( + String packageName, + String originalFilePath, + String patchedFilePath, + ) async { + try { + Directory managerDir = Directory(managerDirPath); + managerDir.createSync(); + String newPatchedFilePath = '$managerDirPath/$packageName.apk'; + installServiceDScript( + packageName, + originalFilePath, + newPatchedFilePath, + ); + installPostFsDataScript( + packageName, + originalFilePath, + newPatchedFilePath, + ); + File(patchedFilePath).renameSync(newPatchedFilePath); + await Root.exec( + cmd: 'chmod 644 $newPatchedFilePath', + ); + await Root.exec( + cmd: 'chown system:system $newPatchedFilePath', + ); + await Root.exec( + cmd: 'chcon u:object_r:apk_data_file:s0 $newPatchedFilePath', + ); + return true; + } on Exception { + return false; + } + } + + Future installServiceDScript( + String packageName, + String originalFilePath, + String patchedFilePath, + ) async { + String content = '#!/system/bin/sh\n' + 'while [ "\$(getprop sys.boot_completed | tr -d \'\r\')" != "1" ]; do sleep 1; done\n' + 'sleep 1\n' + 'chcon u:object_r:apk_data_file:s0 $patchedFilePath\n' + 'mount -o bind $patchedFilePath $originalFilePath'; + File scriptFile = File('$serviceDDirPath/$packageName.sh'); + await scriptFile.writeAsString(content); + await Root.exec(cmd: 'chmod 744 ${scriptFile.path}'); + } + + Future installPostFsDataScript( + String packageName, + String originalFilePath, + String patchedFilePath, + ) async { + String content = '#!/system/bin/sh\n' + 'while read line; do echo \$line | grep $originalFilePath | ' + 'awk \'{print \$2}\' | xargs umount -l; done< /proc/mounts'; + File scriptFile = File('$postFsDataDirPath/$packageName.sh'); + await scriptFile.writeAsString(content); + await Root.exec(cmd: 'chmod 744 ${scriptFile.path}'); + } +} diff --git a/lib/ui/views/app_selector/app_selector_viewmodel.dart b/lib/ui/views/app_selector/app_selector_viewmodel.dart index 93a4eada..a3541144 100644 --- a/lib/ui/views/app_selector/app_selector_viewmodel.dart +++ b/lib/ui/views/app_selector/app_selector_viewmodel.dart @@ -6,19 +6,24 @@ import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:package_archive_info/package_archive_info.dart'; import 'package:revanced_manager/app/app.locator.dart'; -import 'package:revanced_manager/models/application_info.dart'; +import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:stacked/stacked.dart'; class AppSelectorViewModel extends BaseViewModel { final PatcherAPI patcherAPI = locator(); + bool isRooted = false; + bool isFromStorage = false; List apps = []; - ApplicationInfo? selectedApp; + PatchedApplication? selectedApp; Future initialize() async { await getApps(); + SharedPreferences prefs = await SharedPreferences.getInstance(); + isRooted = prefs.getBool('isRooted') ?? false; notifyListeners(); } @@ -27,12 +32,15 @@ class AppSelectorViewModel extends BaseViewModel { apps = await patcherAPI.getFilteredInstalledApps(); } - void selectApp(ApplicationWithIcon application) { - ApplicationInfo app = ApplicationInfo( + void selectApp(ApplicationWithIcon application) async { + isFromStorage = false; + PatchedApplication app = PatchedApplication( name: application.appName, packageName: application.packageName, version: application.versionName!, apkFilePath: application.apkFilePath, + isRooted: isRooted, + isFromStorage: isFromStorage, ); locator().selectedApp = app; locator().selectedPatches.clear(); @@ -41,6 +49,7 @@ class AppSelectorViewModel extends BaseViewModel { } Future selectAppFromStorage(BuildContext context) async { + isFromStorage = true; try { FilePickerResult? result = await FilePicker.platform.pickFiles( type: FileType.custom, @@ -50,11 +59,13 @@ class AppSelectorViewModel extends BaseViewModel { File apkFile = File(result.files.single.path!); PackageArchiveInfo? packageArchiveInfo = await PackageArchiveInfo.fromPath(apkFile.path); - ApplicationInfo app = ApplicationInfo( + PatchedApplication app = PatchedApplication( name: packageArchiveInfo.appName, packageName: packageArchiveInfo.packageName, version: packageArchiveInfo.version, apkFilePath: result.files.single.path!, + isRooted: isRooted, + isFromStorage: isFromStorage, ); locator().selectedApp = app; locator().selectedPatches.clear(); diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index eacf67af..82a080c2 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -1,6 +1,6 @@ import 'package:revanced_manager/app/app.locator.dart'; -import 'package:revanced_manager/models/application_info.dart'; import 'package:revanced_manager/models/patch.dart'; +import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; @@ -38,7 +38,8 @@ class InstallerViewModel extends BaseViewModel { Future runPatcher() async { updateProgress(0.0); - ApplicationInfo? selectedApp = locator().selectedApp; + PatchedApplication? selectedApp = + locator().selectedApp; if (selectedApp != null) { String apkFilePath = selectedApp.apkFilePath; List selectedPatches = @@ -111,11 +112,16 @@ class InstallerViewModel extends BaseViewModel { } void installResult() async { - await locator().installPatchedFile(); + PatchedApplication? selectedApp = + locator().selectedApp; + if (selectedApp != null) { + await locator().installPatchedFile(selectedApp); + } } void shareResult() { - ApplicationInfo? selectedApp = locator().selectedApp; + PatchedApplication? selectedApp = + locator().selectedApp; if (selectedApp != null) { locator().sharePatchedFile( selectedApp.name, diff --git a/lib/ui/views/patches_selector/patches_selector_viewmodel.dart b/lib/ui/views/patches_selector/patches_selector_viewmodel.dart index 25986c40..df1bf875 100644 --- a/lib/ui/views/patches_selector/patches_selector_viewmodel.dart +++ b/lib/ui/views/patches_selector/patches_selector_viewmodel.dart @@ -1,6 +1,6 @@ import 'package:revanced_manager/app/app.locator.dart'; -import 'package:revanced_manager/models/application_info.dart'; import 'package:revanced_manager/models/patch.dart'; +import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; @@ -18,7 +18,7 @@ class PatchesSelectorViewModel extends BaseViewModel { } Future getPatches() async { - ApplicationInfo? app = locator().selectedApp; + PatchedApplication? app = locator().selectedApp; patches = await patcherAPI.getFilteredPatches(app); } diff --git a/lib/ui/views/root_checker/root_checker_view.dart b/lib/ui/views/root_checker/root_checker_view.dart index 4ec4e9ca..40b48b78 100644 --- a/lib/ui/views/root_checker/root_checker_view.dart +++ b/lib/ui/views/root_checker/root_checker_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/widgets/I18nText.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:revanced_manager/app/app.locator.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'; @@ -11,8 +12,9 @@ class RootCheckerView extends StatelessWidget { @override Widget build(BuildContext context) { return ViewModelBuilder.reactive( + disposeViewModel: false, onModelReady: (model) => model.initialize, - viewModelBuilder: () => RootCheckerViewModel(), + viewModelBuilder: () => locator(), builder: (context, model, child) => Scaffold( floatingActionButton: Column( mainAxisAlignment: MainAxisAlignment.end, @@ -64,15 +66,20 @@ class RootCheckerView extends StatelessWidget { const SizedBox(height: 170), MagiskButton( onPressed: () { - model.getMagiskPermissions(); - Future.delayed(const Duration(seconds: 5), () { - model.checkRoot(); - }); + model + .getMagiskPermissions() + .then((value) => model.checkRoot()); }, ), - Text( - "Magisk permission granted: ${model.isRooted.toString()}", - style: GoogleFonts.poppins(), + I18nText( + 'rootCheckerView.grantedPermission', + translationParams: { + 'isRooted': model.isRooted.toString(), + }, + child: Text( + '', + 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 index 7aff462d..1b674fdd 100644 --- a/lib/ui/views/root_checker/root_checker_viewmodel.dart +++ b/lib/ui/views/root_checker/root_checker_viewmodel.dart @@ -1,7 +1,5 @@ -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'; @@ -18,21 +16,25 @@ class RootCheckerViewModel extends BaseViewModel { Future checkRoot() async { isRooted = await Root.isRooted(); + if (isRooted == true) { + navigateToHome(); + } notifyListeners(); } - Future getMagiskPermissions() async { - if (isRooted == true) { - Fluttertoast.showToast(msg: 'Magisk permission already granted!'); + Future getMagiskPermissions() async { + try { + await Root.exec(cmd: 'cat /proc/version'); + } on Exception { + return false; } - await Root.exec(cmd: "adb shell su -c exit"); - notifyListeners(); + return true; } Future navigateToHome() async { final prefs = await SharedPreferences.getInstance(); - prefs.setBool('showHome', true); - _navigationService.navigateTo(Routes.homeView); + prefs.setBool('isRooted', isRooted!); + _navigationService.navigateTo(Routes.navigation); notifyListeners(); } } diff --git a/lib/ui/widgets/latest_commit_card.dart b/lib/ui/widgets/latest_commit_card.dart index 2e1e5158..e33d4946 100644 --- a/lib/ui/widgets/latest_commit_card.dart +++ b/lib/ui/widgets/latest_commit_card.dart @@ -17,7 +17,7 @@ class LatestCommitCard extends StatefulWidget { } class _LatestCommitCardState extends State { - GithubAPI githubAPI = GithubAPI(); + final GithubAPI githubAPI = GithubAPI(); @override Widget build(BuildContext context) {