From 106d77ea0c9dc2883cbc59bfe0779a5f604ae9e5 Mon Sep 17 00:00:00 2001 From: Aunali321 <48486084+Aunali321@users.noreply.github.com> Date: Tue, 16 Aug 2022 18:36:56 +0530 Subject: [PATCH] feat: "New dashboard UI". (#3) * feat: "New dashboard UI". * fix: some improvenents * fix: fix inconsistencies and light theme. * fix: save patched and installed apps on prefs, improve installer log, improve dashboard with real data (wip) Co-authored-by: Aunali321 * feat: move chips to a separate widget. Co-authored-by: Alberto Ponces --- .github/workflows/release-build.yml | 2 + .gitignore | 2 + .vscode/tasks.json | 8 +- .../app/revanced/manager/MainActivity.kt | 35 ++- .../src/main/res/drawable/ic_notification.xml | 33 +++ assets/i18n/en.json | 14 +- assets/images/reddit.png | Bin 22247 -> 0 bytes assets/images/revanced.svg | 6 - lib/app/app.dart | 2 + lib/app/app.locator.dart | 39 --- lib/app/app.router.dart | 231 ------------------ lib/models/patched_application.dart | 28 ++- lib/theme.dart | 4 +- .../app_selector/app_selector_viewmodel.dart | 36 +-- lib/ui/views/home/home_view.dart | 42 +++- lib/ui/views/home/home_viewmodel.dart | 20 ++ lib/ui/views/installer/installer_view.dart | 6 +- .../views/installer/installer_viewmodel.dart | 65 ++--- lib/ui/widgets/application_item.dart | 121 ++++++--- lib/ui/widgets/available_updates_card.dart | 104 ++------ lib/ui/widgets/dashboard_raw_chip.dart | 51 ++++ lib/ui/widgets/installed_apps_card.dart | 80 ++---- lib/ui/widgets/latest_commit_card.dart | 1 + lib/ui/widgets/patch_text_button.dart | 37 ++- pubspec.yaml | 7 +- 25 files changed, 434 insertions(+), 540 deletions(-) create mode 100644 android/app/src/main/res/drawable/ic_notification.xml delete mode 100644 assets/images/reddit.png delete mode 100644 assets/images/revanced.svg delete mode 100644 lib/app/app.locator.dart delete mode 100644 lib/app/app.router.dart create mode 100644 lib/ui/widgets/dashboard_raw_chip.dart diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index 201a79ff..261a84bb 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -19,6 +19,8 @@ jobs: channel: 'stable' - name: Set up Flutter run: flutter pub get + - name: Generate files with Builder + run: flutter packages pub run build_runner build --delete-conflicting-outputs - name: Build with Flutter env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index ab6de429..a78395b8 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,8 @@ version # Flutter/Dart/Pub related **/doc/api/ **/*.g.dart +**/*.locator.dart +**/*.router.dart .dart_tool/ .flutter-plugins .flutter-plugins-dependencies diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 490ed99c..0fa4713f 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,7 +2,7 @@ "version": "2.0.0", "tasks": [ { - "label": "Build (Serializer)", + "label": "Generate (Builder)", "type": "shell", "command": "flutter packages pub run build_runner build --delete-conflicting-outputs", "problemMatcher": [] @@ -30,7 +30,7 @@ "problemMatcher": [] }, { - "label": "Clean (Serializer)", + "label": "Clean (Builder)", "type": "shell", "command": "flutter packages pub run build_runner clean", "problemMatcher": [] @@ -39,7 +39,7 @@ "label": "Build all (Android)", "dependsOrder": "sequence", "dependsOn": [ - "Build (Serializer)", + "Generate (Builder)", "Build (Android)" ], "problemMatcher": [] @@ -49,7 +49,7 @@ "dependsOrder": "sequence", "dependsOn": [ "Clean (Flutter)", - "Clean (Serializer)" + "Clean (Builder)" ], "problemMatcher": [] }, diff --git a/android/app/src/main/kotlin/app/revanced/manager/MainActivity.kt b/android/app/src/main/kotlin/app/revanced/manager/MainActivity.kt index 971836df..ba725783 100644 --- a/android/app/src/main/kotlin/app/revanced/manager/MainActivity.kt +++ b/android/app/src/main/kotlin/app/revanced/manager/MainActivity.kt @@ -179,10 +179,41 @@ class MainActivity : FlutterActivity() { return true } - fun createPatcher(inputFilePath: String, cacheDirPath: String, resourcePatching: Boolean): Boolean { + fun createPatcher( + inputFilePath: String, + cacheDirPath: String, + resourcePatching: Boolean + ): Boolean { val inputFile = File(inputFilePath) val aaptPath = Aapt.binary(applicationContext).absolutePath - patcher = Patcher(PatcherOptions(inputFile, cacheDirPath, resourcePatching, aaptPath, cacheDirPath)) + patcher = + Patcher( + PatcherOptions( + inputFile, + cacheDirPath, + resourcePatching, + aaptPath, + cacheDirPath, + logger = + object : app.revanced.patcher.logging.Logger { + override fun error(msg: String) { + methodChannel.invokeMethod("updateInstallerLog", msg) + } + + override fun warn(msg: String) { + methodChannel.invokeMethod("updateInstallerLog", msg) + } + + override fun info(msg: String) { + methodChannel.invokeMethod("updateInstallerLog", msg) + } + + override fun trace(msg: String) { + methodChannel.invokeMethod("updateInstallerLog", msg) + } + } + ) + ) return true } diff --git a/android/app/src/main/res/drawable/ic_notification.xml b/android/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 00000000..17e031d9 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 795c7db0..638a6fd6 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -7,18 +7,12 @@ "homeView": { "widgetTitle": "Dashboard", "updatesSubtitle": "ReVanced Updates", - "patchedSubtitle": "Patched Applications" - }, - "availableUpdatesCard": { - "widgetTitle": "Updates Available", - "patchButton": "Patch All", - "changelogLabel": "Changelog" + "patchedSubtitle": "Patched Applications", + "updatesAvailable": "Updates Available", + "installed": "Installed" }, "applicationItem": { - "patchButton": "Patch" - }, - "installedAppsCard": { - "widgetTitle": "Total Installed", + "patchButton": "Patch", "changelogLabel": "Changelog" }, "latestCommitCard": { diff --git a/assets/images/reddit.png b/assets/images/reddit.png deleted file mode 100644 index 941a12e6c3a588bee3fe6e097300b1a4018249a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22247 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 H?4ADs98>Qd diff --git a/assets/images/revanced.svg b/assets/images/revanced.svg deleted file mode 100644 index 7318abbd..00000000 --- a/assets/images/revanced.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/lib/app/app.dart b/lib/app/app.dart index 6d7ab8ec..8765ae4f 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -5,6 +5,7 @@ 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_viewmodel.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'; @@ -31,6 +32,7 @@ import 'package:stacked_themes/stacked_themes.dart'; LazySingleton(classType: PatcherAPI), LazySingleton(classType: ManagerAPI), LazySingleton(classType: RootAPI), + LazySingleton(classType: HomeViewModel), LazySingleton(classType: PatcherViewModel), LazySingleton(classType: AppSelectorViewModel), LazySingleton(classType: PatchesSelectorViewModel), diff --git a/lib/app/app.locator.dart b/lib/app/app.locator.dart deleted file mode 100644 index 6476d9a5..00000000 --- a/lib/app/app.locator.dart +++ /dev/null @@ -1,39 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ************************************************************************** -// StackedLocatorGenerator -// ************************************************************************** - -// 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'; -import '../ui/views/patches_selector/patches_selector_viewmodel.dart'; - -final locator = StackedLocator.instance; - -Future setupLocator( - {String? environment, EnvironmentFilter? environmentFilter}) async { -// Register environments - locator.registerEnvironment( - environment: environment, environmentFilter: environmentFilter); - -// Register dependencies - locator.registerLazySingleton(() => NavigationService()); - locator.registerLazySingleton(() => PatcherAPI()); - locator.registerLazySingleton(() => ManagerAPI()); - locator.registerLazySingleton(() => RootAPI()); - locator.registerLazySingleton(() => PatcherViewModel()); - locator.registerLazySingleton(() => AppSelectorViewModel()); - locator.registerLazySingleton(() => PatchesSelectorViewModel()); - locator.registerLazySingleton(() => InstallerViewModel()); - locator.registerLazySingleton(() => ThemeService.getInstance()); -} diff --git a/lib/app/app.router.dart b/lib/app/app.router.dart deleted file mode 100644 index 62e381df..00000000 --- a/lib/app/app.router.dart +++ /dev/null @@ -1,231 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -// ************************************************************************** -// StackedRouterGenerator -// ************************************************************************** - -// ignore_for_file: public_member_api_docs, unused_import, non_constant_identifier_names - -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/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 navigation = '/Navigation'; - 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 = { - navigation, - appSelectorView, - patchesSelectorView, - installerView, - settingsView, - contributorsView, - rootCheckerView, - }; -} - -class StackedRouter extends RouterBase { - @override - List get routes => _routes; - final _routes = [ - RouteDef(Routes.navigation, page: Navigation), - 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 = { - Navigation: (data) { - return MaterialPageRoute( - builder: (context) => const Navigation(), - settings: data, - ); - }, - AppSelectorView: (data) { - return MaterialPageRoute( - builder: (context) => const AppSelectorView(), - settings: data, - ); - }, - PatchesSelectorView: (data) { - return MaterialPageRoute( - builder: (context) => const PatchesSelectorView(), - settings: data, - ); - }, - InstallerView: (data) { - var args = data.getArgs( - orElse: () => InstallerViewArguments(), - ); - return MaterialPageRoute( - builder: (context) => InstallerView(key: args.key), - settings: data, - ); - }, - SettingsView: (data) { - return MaterialPageRoute( - builder: (context) => const SettingsView(), - settings: data, - ); - }, - ContributorsView: (data) { - return MaterialPageRoute( - builder: (context) => const ContributorsView(), - settings: data, - ); - }, - RootCheckerView: (data) { - return MaterialPageRoute( - builder: (context) => const RootCheckerView(), - settings: data, - ); - }, - }; -} - -/// ************************************************************************ -/// Arguments holder classes -/// ************************************************************************* - -/// InstallerView arguments holder class -class InstallerViewArguments { - final Key? key; - InstallerViewArguments({this.key}); -} - -/// ************************************************************************ -/// Extension for strongly typed navigation -/// ************************************************************************* - -extension NavigatorStateExtension on NavigationService { - Future navigateToNavigation({ - int? routerId, - bool preventDuplicates = true, - Map? parameters, - Widget Function(BuildContext, Animation, Animation, Widget)? - transition, - }) async { - return navigateTo( - Routes.navigation, - id: routerId, - preventDuplicates: preventDuplicates, - parameters: parameters, - transition: transition, - ); - } - - Future navigateToAppSelectorView({ - int? routerId, - bool preventDuplicates = true, - Map? parameters, - Widget Function(BuildContext, Animation, Animation, Widget)? - transition, - }) async { - return navigateTo( - Routes.appSelectorView, - id: routerId, - preventDuplicates: preventDuplicates, - parameters: parameters, - transition: transition, - ); - } - - Future navigateToPatchesSelectorView({ - int? routerId, - bool preventDuplicates = true, - Map? parameters, - Widget Function(BuildContext, Animation, Animation, Widget)? - transition, - }) async { - return navigateTo( - Routes.patchesSelectorView, - id: routerId, - preventDuplicates: preventDuplicates, - parameters: parameters, - transition: transition, - ); - } - - Future navigateToInstallerView({ - Key? key, - int? routerId, - bool preventDuplicates = true, - Map? parameters, - Widget Function(BuildContext, Animation, Animation, Widget)? - transition, - }) async { - return navigateTo( - Routes.installerView, - arguments: InstallerViewArguments(key: key), - id: routerId, - preventDuplicates: preventDuplicates, - parameters: parameters, - transition: transition, - ); - } - - Future navigateToSettingsView({ - int? routerId, - bool preventDuplicates = true, - Map? parameters, - Widget Function(BuildContext, Animation, Animation, Widget)? - transition, - }) async { - return navigateTo( - Routes.settingsView, - id: routerId, - preventDuplicates: preventDuplicates, - parameters: parameters, - transition: transition, - ); - } - - Future navigateToContributorsView({ - int? routerId, - bool preventDuplicates = true, - Map? parameters, - Widget Function(BuildContext, Animation, Animation, Widget)? - transition, - }) async { - return navigateTo( - Routes.contributorsView, - id: routerId, - preventDuplicates: preventDuplicates, - parameters: parameters, - 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/models/patched_application.dart b/lib/models/patched_application.dart index ecb7a62c..ea530fa6 100644 --- a/lib/models/patched_application.dart +++ b/lib/models/patched_application.dart @@ -1,21 +1,43 @@ -import 'package:revanced_manager/models/patch.dart'; +import 'dart:typed_data'; +import 'package:json_annotation/json_annotation.dart'; +part 'patched_application.g.dart'; + +@JsonSerializable() class PatchedApplication { final String name; final String packageName; final String version; final String apkFilePath; + @JsonKey( + fromJson: bytesFromString, + toJson: bytesToString, + ) + final Uint8List icon; + final DateTime patchDate; final bool isRooted; final bool isFromStorage; - final List appliedPatches; + final List appliedPatches; PatchedApplication({ required this.name, required this.packageName, required this.version, required this.apkFilePath, + required this.icon, + required this.patchDate, required this.isRooted, required this.isFromStorage, - this.appliedPatches = const [], + this.appliedPatches = const [], }); + + factory PatchedApplication.fromJson(Map json) => + _$PatchedApplicationFromJson(json); + + Map toJson() => _$PatchedApplicationToJson(this); + + static Uint8List bytesFromString(String pictureUrl) => + Uint8List.fromList(pictureUrl.codeUnits); + + static String bytesToString(Uint8List bytes) => String.fromCharCodes(bytes); } diff --git a/lib/theme.dart b/lib/theme.dart index 75eda588..354276c7 100644 --- a/lib/theme.dart +++ b/lib/theme.dart @@ -24,7 +24,7 @@ var lightTheme = ThemeData.light().copyWith( padding: MaterialStateProperty.all( const EdgeInsets.symmetric( vertical: 8, - horizontal: 12, + horizontal: 14, ), ), backgroundColor: MaterialStateProperty.all( @@ -70,7 +70,7 @@ var darkTheme = ThemeData.dark().copyWith( ), ), colorScheme: const ColorScheme.dark( - primary: Color(0x1B222B6B), + primary: Color(0xff11161C), secondary: Color(0xff7792BA), tertiary: Color(0xff8691A0), background: Color(0xff0A0D11), diff --git a/lib/ui/views/app_selector/app_selector_viewmodel.dart b/lib/ui/views/app_selector/app_selector_viewmodel.dart index a3541144..27cf1c4a 100644 --- a/lib/ui/views/app_selector/app_selector_viewmodel.dart +++ b/lib/ui/views/app_selector/app_selector_viewmodel.dart @@ -4,7 +4,6 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/cupertino.dart'; 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/patched_application.dart'; import 'package:revanced_manager/services/patcher_api.dart'; @@ -39,6 +38,8 @@ class AppSelectorViewModel extends BaseViewModel { packageName: application.packageName, version: application.versionName!, apkFilePath: application.apkFilePath, + icon: application.icon, + patchDate: DateTime.now(), isRooted: isRooted, isFromStorage: isFromStorage, ); @@ -57,20 +58,25 @@ class AppSelectorViewModel extends BaseViewModel { ); if (result != null && result.files.single.path != null) { File apkFile = File(result.files.single.path!); - PackageArchiveInfo? packageArchiveInfo = - await PackageArchiveInfo.fromPath(apkFile.path); - 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(); - locator().dimPatchCard = false; - locator().notifyListeners(); + ApplicationWithIcon? application = + await DeviceApps.getAppFromStorage(apkFile.path, true) + as ApplicationWithIcon?; + if (application != null) { + PatchedApplication app = PatchedApplication( + name: application.appName, + packageName: application.packageName, + version: application.versionName!, + apkFilePath: result.files.single.path!, + icon: application.icon, + patchDate: DateTime.now(), + isRooted: isRooted, + isFromStorage: isFromStorage, + ); + locator().selectedApp = app; + locator().selectedPatches.clear(); + locator().dimPatchCard = false; + locator().notifyListeners(); + } } } on Exception { Fluttertoast.showToast( diff --git a/lib/ui/views/home/home_view.dart b/lib/ui/views/home/home_view.dart index 551f0c48..2c27937b 100644 --- a/lib/ui/views/home/home_view.dart +++ b/lib/ui/views/home/home_view.dart @@ -1,9 +1,11 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/theme.dart'; import 'package:revanced_manager/ui/views/home/home_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/available_updates_card.dart'; +import 'package:revanced_manager/ui/widgets/dashboard_raw_chip.dart'; import 'package:revanced_manager/ui/widgets/installed_apps_card.dart'; import 'package:revanced_manager/ui/widgets/latest_commit_card.dart'; import 'package:stacked/stacked.dart'; @@ -13,8 +15,9 @@ class HomeView extends StatelessWidget { @override Widget build(BuildContext context) { - return ViewModelBuilder.reactive( - viewModelBuilder: () => HomeViewModel(), + return ViewModelBuilder.reactive( + disposeViewModel: false, + viewModelBuilder: () => locator(), builder: (context, model, child) => Scaffold( body: SafeArea( child: SingleChildScrollView( @@ -30,6 +33,7 @@ class HomeView extends StatelessWidget { '', style: GoogleFonts.inter( fontSize: 28, + fontWeight: FontWeight.w600, ), ), ), @@ -40,6 +44,7 @@ class HomeView extends StatelessWidget { '', style: GoogleFonts.inter( fontSize: 20, + fontWeight: FontWeight.w500, color: isDark ? const Color(0xffD1E1FA) : const Color(0xff384E6E), @@ -48,7 +53,8 @@ class HomeView extends StatelessWidget { ), const SizedBox(height: 10), LatestCommitCard( - color: Theme.of(context).colorScheme.primary), + color: Theme.of(context).colorScheme.primary, + ), const SizedBox(height: 14), I18nText( 'homeView.patchedSubtitle', @@ -62,14 +68,30 @@ class HomeView extends StatelessWidget { ), ), ), + const SizedBox(height: 8), + Row( + children: [ + DashboardChip( + label: "homeView.updatesAvailable", + isSelected: model.showUpdatableApps, + onSelected: (value) { + model.toggleUpdatableApps(value); + }, + ), + const SizedBox(width: 10), + DashboardChip( + label: "homeView.installed", + isSelected: !model.showUpdatableApps, + onSelected: (value) { + model.toggleUpdatableApps(false); + }, + ) + ], + ), const SizedBox(height: 14), - AvailableUpdatesCard( - color: Theme.of(context).colorScheme.primary, - ), - const SizedBox(height: 15), - InstalledAppsCard( - color: Theme.of(context).colorScheme.primary, - ), + model.showUpdatableApps + ? const AvailableUpdatesCard() + : const InstalledAppsCard() ], ), ), diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart index 79882510..e9929eed 100644 --- a/lib/ui/views/home/home_viewmodel.dart +++ b/lib/ui/views/home/home_viewmodel.dart @@ -1,8 +1,28 @@ +import 'dart:convert'; +import 'package:injectable/injectable.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:shared_preferences/shared_preferences.dart'; import 'package:stacked/stacked.dart'; +@lazySingleton class HomeViewModel extends BaseViewModel { + bool showUpdatableApps = true; + + void toggleUpdatableApps(bool value) { + showUpdatableApps = value; + notifyListeners(); + } + Future downloadPatches() => locator().downloadPatches(); Future downloadIntegrations() => locator().downloadIntegrations(); + + Future> getPatchedApps(bool isUpdatable) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List patchedApps = prefs.getStringList('patchedApps') ?? []; + return patchedApps + .map((app) => PatchedApplication.fromJson(json.decode(app))) + .toList(); + } } diff --git a/lib/ui/views/installer/installer_view.dart b/lib/ui/views/installer/installer_view.dart index f915a568..ef359925 100644 --- a/lib/ui/views/installer/installer_view.dart +++ b/lib/ui/views/installer/installer_view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart'; import 'package:stacked/stacked.dart'; @@ -81,9 +82,8 @@ class InstallerView extends StatelessWidget { ), child: SelectableText( model.logs, - style: const TextStyle( - fontFamily: 'monospace', - fontSize: 15, + style: GoogleFonts.jetBrainsMono( + fontSize: 12, height: 1.5, ), ), diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index 708c3ff4..557a898c 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -7,6 +7,7 @@ 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'; 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 InstallerViewModel extends BaseViewModel { @@ -23,7 +24,7 @@ class InstallerViewModel extends BaseViewModel { notificationText: 'ReVanced Manager is patching', notificationImportance: AndroidNotificationImportance.Default, notificationIcon: AndroidResource( - name: 'ic_launcher_foreground', + name: 'ic_notification', defType: 'drawable', ), ), @@ -34,11 +35,13 @@ class InstallerViewModel extends BaseViewModel { } void addLog(String message) { - if (logs.isNotEmpty) { - logs += '\n'; + if (message.isNotEmpty && !message.startsWith('Merging L')) { + if (logs.isNotEmpty) { + logs += '\n'; + } + logs += message; + notifyListeners(); } - logs += message; - notifyListeners(); } void updateProgress(double value) { @@ -61,29 +64,25 @@ class InstallerViewModel extends BaseViewModel { List selectedPatches = locator().selectedPatches; if (selectedPatches.isNotEmpty) { - addLog('Initializing installer...'); + addLog('Initializing installer'); if (selectedApp.isRooted && !selectedApp.isFromStorage) { - addLog('Checking if an old patched version exists...'); + addLog('Checking if an old patched version exists'); bool oldExists = await locator().checkOldPatch(selectedApp); - addLog('Done'); if (oldExists) { - addLog('Deleting old patched version...'); + addLog('Deleting old patched version'); await locator().deleteOldPatch(selectedApp); - addLog('Done'); } } - addLog('Creating working directory...'); + addLog('Creating working directory'); bool? isSuccess = await locator().initPatcher(); if (isSuccess != null && isSuccess) { - addLog('Done'); updateProgress(0.1); - addLog('Copying original apk...'); + addLog('Copying original apk'); isSuccess = await locator().copyInputFile(apkFilePath); if (isSuccess != null && isSuccess) { - addLog('Done'); updateProgress(0.2); - addLog('Creating patcher...'); + addLog('Creating patcher'); bool resourcePatching = false; if (selectedApp.packageName == 'com.google.android.youtube' || selectedApp.packageName == @@ -95,31 +94,26 @@ class InstallerViewModel extends BaseViewModel { ); if (isSuccess != null && isSuccess) { if (selectedApp.packageName == 'com.google.android.youtube') { - addLog('Done'); updateProgress(0.3); - addLog('Merging integrations...'); + addLog('Merging integrations'); isSuccess = await locator().mergeIntegrations(); } if (isSuccess != null && isSuccess) { - addLog('Done'); updateProgress(0.5); - addLog('Applying patches...'); isSuccess = await locator().applyPatches(selectedPatches); if (isSuccess != null && isSuccess) { - addLog('Done'); updateProgress(0.7); - addLog('Repacking patched apk...'); + addLog('Repacking patched apk'); isSuccess = await locator().repackPatchedFile(); if (isSuccess != null && isSuccess) { - addLog('Done'); updateProgress(0.9); - addLog('Signing patched apk...'); + addLog('Signing patched apk'); isSuccess = await locator().signPatchedFile(); if (isSuccess != null && isSuccess) { - addLog('Done'); showButtons = true; updateProgress(1.0); + addLog('Finished'); } } } @@ -128,13 +122,13 @@ class InstallerViewModel extends BaseViewModel { } } if (isSuccess == null || !isSuccess) { - addLog('An error occurred! Aborting...'); + addLog('An error occurred! Aborting'); } } else { - addLog('No patches selected! Aborting...'); + addLog('No patches selected! Aborting'); } } else { - addLog('No app selected! Aborting...'); + addLog('No app selected! Aborting'); } await FlutterBackground.disableBackgroundExecution(); isPatching = false; @@ -145,13 +139,14 @@ class InstallerViewModel extends BaseViewModel { locator().selectedApp; if (selectedApp != null) { addLog(selectedApp.isRooted - ? 'Installing patched file using root method...' - : 'Installing patched file using nonroot method...'); + ? 'Installing patched file using root method' + : 'Installing patched file using nonroot method'); isInstalled = await locator().installPatchedFile(selectedApp); if (isInstalled) { addLog('Done'); + await saveApp(selectedApp); } else { - addLog('An error occurred! Aborting...'); + addLog('An error occurred! Aborting'); } } } @@ -181,4 +176,14 @@ class InstallerViewModel extends BaseViewModel { DeviceApps.openApp(selectedApp.packageName); } } + + Future saveApp(PatchedApplication selectedApp) async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + List patchedApps = prefs.getStringList('patchedApps') ?? []; + String app = selectedApp.toJson().toString(); + if (!patchedApps.contains(app)) { + patchedApps.add(app); + prefs.setStringList('patchedApps', patchedApps); + } + } } diff --git a/lib/ui/widgets/application_item.dart b/lib/ui/widgets/application_item.dart index 805e8a24..d9f462ad 100644 --- a/lib/ui/widgets/application_item.dart +++ b/lib/ui/widgets/application_item.dart @@ -1,57 +1,114 @@ +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:revanced_manager/constants.dart'; +import 'package:revanced_manager/theme.dart'; import 'package:revanced_manager/ui/widgets/patch_text_button.dart'; +import 'package:expandable/expandable.dart'; +import 'package:timeago/timeago.dart'; class ApplicationItem extends StatelessWidget { - final String asset; + final Uint8List icon; final String name; - final String releaseDate; + final DateTime patchDate; + final String? changelog; + final bool isUpdatableApp; final Function()? onPressed; const ApplicationItem({ Key? key, - required this.asset, + required this.icon, required this.name, - required this.releaseDate, + required this.patchDate, + this.changelog = '', + required this.isUpdatableApp, required this.onPressed, }) : super(key: key); @override Widget build(BuildContext context) { - final isSVG = asset.endsWith('.svg'); - return ListTile( - horizontalTitleGap: 12.0, - leading: isSVG - ? SvgPicture.asset( - asset, - height: 26, - width: 26, - ) - : Image.asset( - asset, - height: 39, - width: 39, + return ExpandablePanel( + theme: const ExpandableThemeData( + hasIcon: false, + animationDuration: Duration(milliseconds: 450), + ), + header: Container( + height: 60, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all( + Radius.circular(16), + ), + color: Theme.of(context).colorScheme.primary, + ), + padding: const EdgeInsets.symmetric(horizontal: 10.0, vertical: 12.0), + child: Row( + children: [ + SizedBox( + width: 60, + child: Image.memory( + icon, + height: 39, + width: 39, + ), ), - title: Text( - name, - style: GoogleFonts.roboto( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.w600, + const SizedBox(width: 4), + SizedBox( + width: MediaQuery.of(context).size.width - 250, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + name, + style: GoogleFonts.roboto( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.w600, + ), + ), + Text( + format(patchDate), + style: robotoTextStyle.copyWith( + color: Theme.of(context).colorScheme.tertiary, + ), + ), + ], + ), + ), + const Spacer(), + isUpdatableApp + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: PatchTextButton( + text: 'applicationItem.patchButton', + onPressed: onPressed, + borderColor: isDark + ? const Color(0xff4D5054) + : const Color.fromRGBO(119, 146, 168, 1), + ), + ) + : const SizedBox(), + ], ), ), - subtitle: Text( - releaseDate, - style: robotoTextStyle, - ), - trailing: PatchTextButton( - text: FlutterI18n.translate( - context, - 'applicationItem.patchButton', + collapsed: const Text(""), + expanded: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + I18nText( + 'applicationItem.changelogLabel', + child: Text( + '', + style: robotoTextStyle.copyWith(fontWeight: FontWeight.w700), + ), + ), + Text( + changelog!, + style: robotoTextStyle, + ), + ], ), - onPressed: onPressed, ), ); } diff --git a/lib/ui/widgets/available_updates_card.dart b/lib/ui/widgets/available_updates_card.dart index d2e05b7f..00a7e0c0 100644 --- a/lib/ui/widgets/available_updates_card.dart +++ b/lib/ui/widgets/available_updates_card.dart @@ -1,94 +1,36 @@ import 'package:flutter/material.dart'; -import 'package:flutter_i18n/flutter_i18n.dart'; -import 'package:google_fonts/google_fonts.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/application_item.dart'; -import 'package:revanced_manager/ui/widgets/patch_text_button.dart'; class AvailableUpdatesCard extends StatelessWidget { - final Color? color; const AvailableUpdatesCard({ Key? key, - this.color = const Color(0xff1B222B), }) : super(key: key); @override Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: color, - ), - padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - I18nText( - 'availableUpdatesCard.widgetTitle', - child: Text( - '', - style: GoogleFonts.inter( - fontSize: 16, - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.w500, - ), - ), - ), - PatchTextButton( - text: FlutterI18n.translate( - context, - 'availableUpdatesCard.patchButton', - ), - onPressed: () => {}, - backgroundColor: Theme.of(context).colorScheme.secondary, - ), - ], - ), - ), - ApplicationItem( - asset: 'assets/images/revanced.svg', - name: 'ReVanced', - releaseDate: '2 days ago', - onPressed: () => {}, - ), - ApplicationItem( - asset: 'assets/images/reddit.png', - name: 'ReReddit', - releaseDate: 'Released 1 month ago', - onPressed: () => {}, - ), - const SizedBox(height: 4), - I18nText( - 'availableUpdatesCard.changelogLabel', - child: Text( - '', - style: GoogleFonts.roboto( - color: Theme.of(context).colorScheme.tertiary, - fontWeight: FontWeight.w700, - ), - ), - ), - const SizedBox(height: 4), - Text( - 'fix: we made the player even worse (you love)', - style: GoogleFonts.roboto( - color: Theme.of(context).colorScheme.tertiary, - ), - ), - const SizedBox(height: 4), - Text( - 'chore: guhhughghu', - style: GoogleFonts.roboto( - color: Theme.of(context).colorScheme.tertiary, - ), - ), - ], - ), + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + FutureBuilder>( + future: locator().getPatchedApps(true), + builder: (context, snapshot) => + snapshot.hasData && snapshot.data!.length > 1 + ? ListView.builder( + itemBuilder: (context, index) => ApplicationItem( + icon: snapshot.data![index].icon, + name: snapshot.data![index].name, + patchDate: snapshot.data![index].patchDate, + isUpdatableApp: true, + onPressed: () => {}, + ), + ) + : Container(), + ), + ], ); } } diff --git a/lib/ui/widgets/dashboard_raw_chip.dart b/lib/ui/widgets/dashboard_raw_chip.dart new file mode 100644 index 00000000..960a339f --- /dev/null +++ b/lib/ui/widgets/dashboard_raw_chip.dart @@ -0,0 +1,51 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:revanced_manager/theme.dart'; + +class DashboardChip extends StatelessWidget { + final String label; + final bool isSelected; + final Function(bool)? onSelected; + const DashboardChip({ + Key? key, + required this.label, + required this.isSelected, + this.onSelected, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return RawChip( + showCheckmark: false, + label: I18nText(label), + selected: isSelected, + labelStyle: GoogleFonts.inter( + color: isSelected + ? isDark + ? const Color(0xff95C0FE) + : Theme.of(context).colorScheme.secondary + : isDark + ? Colors.grey + : Colors.grey[700], + fontWeight: FontWeight.w500, + ), + backgroundColor: Colors.transparent, + selectedColor: const Color.fromRGBO(118, 155, 209, 0.42), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: BorderSide( + width: 1, + color: isDark + ? isSelected + ? const Color.fromRGBO(118, 155, 209, 0.42) + : Colors.grey + : isSelected + ? const Color.fromRGBO(118, 155, 209, 0.42) + : Colors.grey, + ), + ), + onSelected: onSelected, + ); + } +} diff --git a/lib/ui/widgets/installed_apps_card.dart b/lib/ui/widgets/installed_apps_card.dart index b77deba3..1e991975 100644 --- a/lib/ui/widgets/installed_apps_card.dart +++ b/lib/ui/widgets/installed_apps_card.dart @@ -1,70 +1,36 @@ import 'package:flutter/material.dart'; -import 'package:flutter_i18n/flutter_i18n.dart'; -import 'package:google_fonts/google_fonts.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/application_item.dart'; class InstalledAppsCard extends StatelessWidget { - final Color? color; const InstalledAppsCard({ Key? key, - this.color = const Color(0xff1B222B), }) : super(key: key); @override Widget build(BuildContext context) { - return Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: color, - ), - padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - I18nText( - 'installedAppsCard.widgetTitle', - child: Text( - '', - style: GoogleFonts.inter( - fontSize: 16, - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.w500, - ), - ), - ), - ApplicationItem( - asset: 'assets/images/revanced.svg', - name: 'ReVanced', - releaseDate: '2 days ago', - onPressed: () => {}, - ), - I18nText( - 'installedAppsCard.changelogLabel', - child: Text( - '', - style: GoogleFonts.roboto( - color: Theme.of(context).colorScheme.tertiary, - fontWeight: FontWeight.w700, - ), - ), - ), - const SizedBox(height: 4), - Text( - 'fix: we made the player even worse (you love)', - style: GoogleFonts.roboto( - color: Theme.of(context).colorScheme.tertiary, - ), - ), - const SizedBox(height: 4), - Text( - 'chore: guhhughghu', - style: GoogleFonts.roboto( - color: Theme.of(context).colorScheme.tertiary, - ), - ), - ], - ), + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + FutureBuilder>( + future: locator().getPatchedApps(false), + builder: (context, snapshot) => + snapshot.hasData && snapshot.data!.length > 1 + ? ListView.builder( + itemBuilder: (context, index) => ApplicationItem( + icon: snapshot.data![index].icon, + name: snapshot.data![index].name, + patchDate: snapshot.data![index].patchDate, + isUpdatableApp: false, + onPressed: () => {}, + ), + ) + : Container(), + ), + ], ); } } diff --git a/lib/ui/widgets/latest_commit_card.dart b/lib/ui/widgets/latest_commit_card.dart index e33d4946..e59d9668 100644 --- a/lib/ui/widgets/latest_commit_card.dart +++ b/lib/ui/widgets/latest_commit_card.dart @@ -89,6 +89,7 @@ class _LatestCommitCardState extends State { ), onPressed: () => {}, backgroundColor: Theme.of(context).colorScheme.secondary, + borderColor: Theme.of(context).colorScheme.secondary, ), ], ), diff --git a/lib/ui/widgets/patch_text_button.dart b/lib/ui/widgets/patch_text_button.dart index 8f242f85..003a76cb 100644 --- a/lib/ui/widgets/patch_text_button.dart +++ b/lib/ui/widgets/patch_text_button.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:revanced_manager/constants.dart'; +import 'package:revanced_manager/theme.dart'; class PatchTextButton extends StatelessWidget { final String text; @@ -19,21 +21,30 @@ class PatchTextButton extends StatelessWidget { return TextButton( onPressed: onPressed, style: Theme.of(context).textButtonTheme.style?.copyWith( - backgroundColor: MaterialStateProperty.all(backgroundColor), - side: MaterialStateProperty.all( - BorderSide( - color: borderColor, - width: 1, - ), + backgroundColor: MaterialStateProperty.all(backgroundColor), + side: MaterialStateProperty.all( + BorderSide( + color: borderColor, + width: 1, ), ), - child: Text( - text, - style: interTextStyle.copyWith( - color: backgroundColor == Colors.transparent - ? const Color.fromRGBO(119, 146, 186, 1) - : Colors.white), - ), + padding: MaterialStateProperty.all( + const EdgeInsets.symmetric( + horizontal: 16, + vertical: 4, + ), + )), + child: I18nText(text, + child: Text( + '', + style: interTextStyle.copyWith( + color: backgroundColor == Colors.transparent + ? const Color.fromRGBO(119, 146, 186, 1) + : isDark + ? Colors.black + : Colors.white, + ), + )), ); } } diff --git a/pubspec.yaml b/pubspec.yaml index bb504833..e924b44b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,8 +12,12 @@ environment: dependencies: app_installer: ^1.1.0 cupertino_icons: ^1.0.2 - device_apps: ^2.2.0 + device_apps: + git: + url: https://github.com/ponces/flutter_plugin_device_apps + ref: appinfo-from-storage dio: ^4.0.6 + expandable: ^5.0.1 file_picker: ^5.0.1 flutter: sdk: flutter @@ -28,7 +32,6 @@ dependencies: http: ^0.13.4 injectable: ^1.5.3 json_annotation: ^4.6.0 - package_archive_info: ^0.1.0 path_provider: ^2.0.11 root: ^2.0.2 share_extend: ^2.0.0