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/assets/images/reddit.png b/assets/images/reddit.png
deleted file mode 100644
index 941a12e6..00000000
Binary files a/assets/images/reddit.png and /dev/null differ
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/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..295380ba 100644
--- a/lib/ui/views/home/home_view.dart
+++ b/lib/ui/views/home/home_view.dart
@@ -1,6 +1,7 @@
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';
@@ -14,7 +15,8 @@ class HomeView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelBuilder.reactive(
- viewModelBuilder: () => HomeViewModel(),
+ disposeViewModel: false,
+ viewModelBuilder: () => locator(),
builder: (context, model, child) => Scaffold(
body: SafeArea(
child: SingleChildScrollView(
diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart
index 79882510..c2e958e2 100644
--- a/lib/ui/views/home/home_viewmodel.dart
+++ b/lib/ui/views/home/home_viewmodel.dart
@@ -1,8 +1,22 @@
+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 {
Future downloadPatches() => locator().downloadPatches();
Future downloadIntegrations() => locator().downloadIntegrations();
+
+ Future> getPatchedApps() 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..7c30088a 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 {
@@ -61,29 +62,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,29 +92,23 @@ 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);
}
@@ -128,13 +119,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 +136,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 +173,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..4c5c49db 100644
--- a/lib/ui/widgets/application_item.dart
+++ b/lib/ui/widgets/application_item.dart
@@ -1,40 +1,30 @@
+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/ui/widgets/patch_text_button.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 Function()? onPressed;
const ApplicationItem({
Key? key,
- required this.asset,
+ required this.icon,
required this.name,
- required this.releaseDate,
+ required this.patchDate,
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,
- ),
+ leading: Image.memory(icon),
title: Text(
name,
style: GoogleFonts.roboto(
@@ -43,7 +33,7 @@ class ApplicationItem extends StatelessWidget {
),
),
subtitle: Text(
- releaseDate,
+ format(patchDate),
style: robotoTextStyle,
),
trailing: PatchTextButton(
diff --git a/lib/ui/widgets/available_updates_card.dart b/lib/ui/widgets/available_updates_card.dart
index d2e05b7f..075e578d 100644
--- a/lib/ui/widgets/available_updates_card.dart
+++ b/lib/ui/widgets/available_updates_card.dart
@@ -1,6 +1,9 @@
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';
@@ -50,17 +53,19 @@ class AvailableUpdatesCard extends StatelessWidget {
],
),
),
- 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: () => {},
+ FutureBuilder>(
+ future: locator().getPatchedApps(),
+ 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,
+ onPressed: () => {},
+ ),
+ )
+ : Container(),
),
const SizedBox(height: 4),
I18nText(
diff --git a/lib/ui/widgets/installed_apps_card.dart b/lib/ui/widgets/installed_apps_card.dart
index b77deba3..a69971b8 100644
--- a/lib/ui/widgets/installed_apps_card.dart
+++ b/lib/ui/widgets/installed_apps_card.dart
@@ -1,6 +1,9 @@
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 {
@@ -33,11 +36,19 @@ class InstalledAppsCard extends StatelessWidget {
),
),
),
- ApplicationItem(
- asset: 'assets/images/revanced.svg',
- name: 'ReVanced',
- releaseDate: '2 days ago',
- onPressed: () => {},
+ FutureBuilder>(
+ future: locator().getPatchedApps(),
+ 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,
+ onPressed: () => {},
+ ),
+ )
+ : Container(),
),
I18nText(
'installedAppsCard.changelogLabel',
diff --git a/pubspec.yaml b/pubspec.yaml
index bb504833..09ee05e3 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -12,7 +12,10 @@ 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
file_picker: ^5.0.1
flutter:
@@ -28,7 +31,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