Compare commits

...

16 Commits

Author SHA1 Message Date
semantic-release-bot
65feb34242 chore(release): 1.21.0-dev.7 [skip ci]
# [1.21.0-dev.7](https://github.com/ReVanced/revanced-manager/compare/v1.21.0-dev.6...v1.21.0-dev.7) (2024-07-29)

### Features

* Include primary architecture in external search ([#2068](https://github.com/ReVanced/revanced-manager/issues/2068)) ([23690a9](23690a98df))
2024-07-29 16:04:12 +00:00
Pun Butrach
23690a98df feat: Include primary architecture in external search (#2068) 2024-07-29 22:56:00 +07:00
Pun Butrach
7449d4e318 docs: Link user to the latest version of app (#2077) 2024-07-25 09:27:29 +07:00
validcube
c6f9e36f4b refactor: Migrate deprecated member 2024-07-13 17:32:34 +07:00
Pun Butrach
e9cee0abe2 ci: Prefer installing NPM dependencies from lock 2024-07-13 17:04:43 +07:00
Benjamin
9440f23b55 chore: Remove NDK constraint (#2016)
Co-authored-by: Pun Butrach <pun.butrach@gmail.com>
2024-07-13 16:55:02 +07:00
dependabot[bot]
c67b4b438c build(deps): bump flutter_markdown from 0.7.1 to 0.7.3 (#2022)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-12 14:07:16 +07:00
kitadai31
1bdb820aed refactor: Remove unused strings (#2010) 2024-07-11 11:28:02 +07:00
semantic-release-bot
a28d77bc65 chore(release): 1.21.0-dev.6 [skip ci]
# [1.21.0-dev.6](https://github.com/ReVanced/revanced-manager/compare/v1.21.0-dev.5...v1.21.0-dev.6) (2024-06-30)

### Bug Fixes

* Add missing import to patch options field ([d60f9aa](d60f9aa1d8))
* Follow system theme immediately ([#1942](https://github.com/ReVanced/revanced-manager/issues/1942)) ([694f2a9](694f2a9fae))
* Handle selecting files and folders for patch options correctly ([#1941](https://github.com/ReVanced/revanced-manager/issues/1941)) ([b26760b](b26760b216))
* Increase dashboard RefreshIndicator edge offset ([#1859](https://github.com/ReVanced/revanced-manager/issues/1859)) ([232b702](232b702789))
* Select previously applied patches when loading patch selection ([#1865](https://github.com/ReVanced/revanced-manager/issues/1865)) ([7ef8f04](7ef8f0454b))
* Unsupported patch toast says "patchItem.unsupportedPatchVersion" ([#2011](https://github.com/ReVanced/revanced-manager/issues/2011)) ([3209c0e](3209c0e430))

### Features

* Save last patched app ([#1414](https://github.com/ReVanced/revanced-manager/issues/1414)) ([7720408](77204087bb))
2024-06-30 20:45:38 +00:00
Benjamin Halko
d60f9aa1d8 fix: Add missing import to patch options field
Patch options fields was missing the `patch_options_viewmodel` import, preventing building to complete successfully
2024-06-30 13:38:01 -07:00
kitadai31
3209c0e430 fix: Unsupported patch toast says "patchItem.unsupportedPatchVersion" (#2011) 2024-06-30 15:24:25 +07:00
aAbed
7ef8f0454b fix: Select previously applied patches when loading patch selection (#1865) 2024-06-29 14:40:20 +02:00
DMzS
232b702789 fix: Increase dashboard RefreshIndicator edge offset (#1859) 2024-06-29 14:40:04 +02:00
Snehith
694f2a9fae fix: Follow system theme immediately (#1942)
Co-authored-by: surya-technovert <surya.m@technovert.com>
2024-06-29 14:39:00 +02:00
Benjamin
77204087bb feat: Save last patched app (#1414)
Co-authored-by: aAbed <39409020+TheAabedKhan@users.noreply.github.com>
Co-authored-by: Ushie <ushiekane@gmail.com>
Co-authored-by: oSumAtrIX <johan.melkonyan1@web.de>
Co-authored-by: Mr. X <79870712+n30mrx@users.noreply.github.com>
Co-authored-by: festry0 <153519925+festry0@users.noreply.github.com>
2024-06-29 14:38:00 +02:00
Francesco Marastoni
b26760b216 fix: Handle selecting files and folders for patch options correctly (#1941) 2024-06-29 14:34:34 +02:00
28 changed files with 573 additions and 129 deletions

View File

@@ -39,7 +39,7 @@ jobs:
flutter-version: 3.22.x
- name: Install dependencies
run: npm install
run: npm ci
- name: Get dependencies
run: flutter pub get

View File

@@ -80,7 +80,7 @@ Some of the features ReVanced Manager provides are:
## 🔽 Download
You can download the most recent version of ReVanced Manager at [revanced.app/download](https://revanced.app/download) or from [GitHub releases](https://github.com/ReVanced/revanced-manager/releases).
You can download the most recent version of ReVanced Manager at [revanced.app/download](https://revanced.app/download) or from [GitHub releases](https://github.com/ReVanced/revanced-manager/releases/latest).
Learn how to use ReVanced Manager by following the [documentation](/docs).
## 📚 Everything else

View File

@@ -24,7 +24,6 @@ if (flutterVersionName == null) {
android {
compileSdk 34
ndkVersion flutter.ndkVersion
compileOptions {
coreLibraryDesugaringEnabled true

View File

@@ -4,14 +4,10 @@
"dismissButton": "Dismiss",
"quitButton": "Quit",
"updateButton": "Update",
"enabledLabel": "Enabled",
"disabledLabel": "Disabled",
"installed": "Installed: ${version}",
"suggested": "Suggested: ${version}",
"yesButton": "Yes",
"noButton": "No",
"warning": "Warning",
"options": "Options",
"notice": "Notice",
"noShowAgain": "Don't show this again",
"add": "Add",
@@ -27,10 +23,10 @@
"refreshSuccess": "Refreshed successfully",
"widgetTitle": "Dashboard",
"updatesSubtitle": "Updates",
"patchedSubtitle": "Patched apps",
"lastPatchedAppSubtitle": "Last patched app",
"patchedSubtitle": "Installed apps",
"changeLaterSubtitle": "You can change this in the settings at a later time.",
"noUpdates": "No updates available",
"WIP": "Work in progress...",
"noSavedAppFound": "No app found",
"noInstallations": "No patched apps installed",
"installUpdate": "Continue to install the update?",
"updateSheetTitle": "Update ReVanced Manager",
@@ -41,27 +37,19 @@
"downloadConsentDialogTitle": "Download necessary files?",
"downloadConsentDialogText": "ReVanced Manager needs to download necessary files to work properly.",
"downloadConsentDialogText2": "This will connect you to ${url}.",
"checkUpdateDialogTitle": "Check for updates?",
"checkUpdateDialogText": "Do you want ReVanced Manager to check for updates automatically?",
"notificationTitle": "Update downloaded",
"notificationText": "Tap to install the update",
"downloadingMessage": "Downloading update...",
"downloadedMessage": "Update downloaded",
"installingMessage": "Installing update...",
"errorDownloadMessage": "Unable to download update",
"errorInstallMessage": "Unable to install update",
"noConnection": "No internet connection",
"updatesDisabled": "Updating a patched app is currently disabled. Repatch the app again."
"noConnection": "No internet connection"
},
"applicationItem": {
"infoButton": "Info"
},
"latestCommitCard": {
"loadingLabel": "Loading...",
"timeagoLabel": "${time} ago",
"patcherLabel": "Patcher: ",
"managerLabel": "Manager: ",
"updateButton": "Update Manager"
"timeagoLabel": "${time} ago"
},
"patcherView": {
"widgetTitle": "Patcher",
@@ -75,8 +63,6 @@
"widgetTitleSelected": "Selected app",
"widgetSubtitle": "No app selected",
"noAppsLabel": "No applications found",
"currentVersion": "Current",
"suggestedVersion": "Suggested",
"anyVersion": "Any version"
},
"patchSelectorCard": {
@@ -120,15 +106,12 @@
"customValue": "Custom value",
"setToNull": "Set to null",
"nullValue": "This option value is currently null",
"resetOptionsTooltip": "Reset patch options",
"viewTitle": "Patch options",
"saveOptions": "Save",
"addOptions": "Add options",
"unselectPatch": "Unselect patch",
"tooltip": "More input options",
"selectFilePath": "Select file path",
"selectFolder": "Select folder",
"selectOption": "Select option",
"requiredOption": "Setting this option is required",
"unsupportedOption": "This option is not supported",
"requiredOptionNull": "The following options have to be set:\n\n${options}"
@@ -141,7 +124,6 @@
"patchesChangeWarningDialogButton": "Use default selection"
},
"installerView": {
"widgetTitle": "Installer",
"installType": "Select install type",
"installTypeDescription": "Select the installation type to continue with.",
"installButton": "Install",
@@ -150,7 +132,6 @@
"warning": "Disable auto updates for the patched app to avoid unexpected issues.",
"pressBackAgain": "Press back again to cancel",
"openButton": "Open",
"shareButton": "Share file",
"notificationTitle": "ReVanced Manager is patching",
"notificationText": "Tap to return to the installer",
"exportApkButtonTooltip": "Export patched APK",
@@ -175,7 +156,6 @@
"dynamicThemeHint": "Enjoy an experience closer to your device",
"languageLabel": "Language",
"languageUpdated": "Language updated",
"englishOption": "English",
"sourcesLabel": "Alternative sources",
"sourcesLabelHint": "Configure the alternative sources for ReVanced Patches and ReVanced Integrations",
"sourcesIntegrationsLabel": "Integrations source",
@@ -205,6 +185,8 @@
"showUpdateDialogHint": "Show a dialog when a new update is available",
"universalPatchesLabel": "Show universal patches",
"universalPatchesHint": "Display all apps and universal patches (may slow down the app list)",
"lastPatchedAppLabel": "Save patched app",
"lastPatchedAppHint": "Save the last patch to install or export later",
"versionCompatibilityCheckLabel": "Version compatibility check",
"versionCompatibilityCheckHint": "Prevent selecting patches that are not compatible with the selected app version",
"requireSuggestedAppVersionLabel": "Require suggested app version",
@@ -256,18 +238,25 @@
"appInfoView": {
"widgetTitle": "App info",
"openButton": "Open",
"installButton": "Install",
"uninstallButton": "Uninstall",
"unmountButton": "Unmount",
"exportButton": "Export",
"deleteButton": "Delete",
"rootDialogTitle": "Error",
"lastPatchedAppDescription": "This is a backup of the app that was last patched.",
"unmountDialogText": "Are you sure you want to unmount this app?",
"uninstallDialogText": "Are you sure you want to uninstall this app?",
"rootDialogText": "App was installed with superuser permissions, but currently ReVanced Manager has no permissions.\nPlease grant superuser permissions first.",
"removeAppDialogTitle": "Delete app?",
"removeAppDialogText": "Are you sure you want to delete this backup?",
"packageNameLabel": "Package name",
"installTypeLabel": "Installation type",
"mountTypeLabel": "Mount",
"regularTypeLabel": "Regular",
"patchedDateLabel": "Patched date",
"appliedPatchesLabel": "Applied patches",
"sizeLabel": "File size",
"patchedDateHint": "${date} at ${time}",
"appliedPatchesHint": "${quantity} applied patches",
"updateNotImplemented": "This feature has not been implemented yet"

View File

@@ -4,7 +4,7 @@ To use ReVanced on your Android device, ReVanced Manager must be first installed
## ✅ Installation steps
1. Download the latest version of ReVanced Manager at [revanced.app/download](https://revanced.app/download) or from [GitHub releases](https://github.com/ReVanced/revanced-manager/releases)
1. Download the latest version of ReVanced Manager at [revanced.app/download](https://revanced.app/download) or from [GitHub releases](https://github.com/ReVanced/revanced-manager/releases/latest)
2. Install ReVanced Manager
## ⏭️ What's next

View File

@@ -16,6 +16,8 @@ class PatchedApplication {
this.isRooted = false,
this.isFromStorage = false,
this.appliedPatches = const [],
this.patchedFilePath = '',
this.fileSize = 0,
});
factory PatchedApplication.fromJson(Map<String, dynamic> json) =>
@@ -33,6 +35,8 @@ class PatchedApplication {
bool isRooted;
bool isFromStorage;
List<String> appliedPatches;
String patchedFilePath;
int fileSize;
Map<String, dynamic> toJson() => _$PatchedApplicationToJson(this);

View File

@@ -38,6 +38,8 @@ class ManagerAPI {
bool releaseBuild = false;
bool suggestedAppVersionSelected = true;
bool isDynamicThemeAvailable = false;
bool isScopedStorageAvailable = false;
int sdkVersion = 0;
String storedPatchesFile = '/selected-patches.json';
String keystoreFile =
'/sdcard/Android/data/app.revanced.manager.flutter/files/revanced-manager.keystore';
@@ -55,8 +57,13 @@ class ManagerAPI {
Future<void> initialize() async {
_prefs = await SharedPreferences.getInstance();
isRooted = await _rootAPI.isRooted();
if (sdkVersion == 0) {
sdkVersion = await getSdkVersion();
}
isDynamicThemeAvailable =
(await getSdkVersion()) >= 31; // ANDROID_12_SDK_VERSION = 31
sdkVersion >= 31; // ANDROID_12_SDK_VERSION = 31
isScopedStorageAvailable =
sdkVersion >= 30; // ANDROID_11_SDK_VERSION = 30
storedPatchesFile =
(await getApplicationDocumentsDirectory()).path + storedPatchesFile;
if (kReleaseMode) {
@@ -295,6 +302,14 @@ class ManagerAPI {
await _prefs.setBool('requireSuggestedAppVersionEnabled', value);
}
bool isLastPatchedAppEnabled() {
return _prefs.getBool('lastPatchedAppEnabled') ?? true;
}
Future<void> enableLastPatchedAppStatus(bool value) async {
await _prefs.setBool('lastPatchedAppEnabled', value);
}
Future<void> setKeystorePassword(String password) async {
await _prefs.setString('keystorePassword', password);
}
@@ -327,6 +342,34 @@ class ManagerAPI {
}
}
PatchedApplication? getLastPatchedApp() {
final String? app = _prefs.getString('lastPatchedApp');
return app != null ? PatchedApplication.fromJson(jsonDecode(app)) : null;
}
Future<void> deleteLastPatchedApp() async {
final PatchedApplication? app = getLastPatchedApp();
if (app != null) {
final File file = File(app.patchedFilePath);
await file.delete();
await _prefs.remove('lastPatchedApp');
}
}
Future<void> setLastPatchedApp(
PatchedApplication app,
File outFile,
) async {
deleteLastPatchedApp();
final Directory appCache = await getApplicationSupportDirectory();
app.patchedFilePath = outFile.copySync('${appCache.path}/lastPatchedApp.apk').path;
app.fileSize = outFile.lengthSync();
await _prefs.setString(
'lastPatchedApp',
json.encode(app.toJson()),
);
}
List<PatchedApplication> getPatchedApps() {
final List<String> apps = _prefs.getStringList('patchedApps') ?? [];
return apps.map((a) => PatchedApplication.fromJson(jsonDecode(a))).toList();
@@ -685,6 +728,16 @@ class ManagerAPI {
patchedApps.addAll(mountedApps);
await setPatchedApps(patchedApps);
// Delete the saved app if the file is not found.
final PatchedApplication? lastPatchedApp = getLastPatchedApp();
if (lastPatchedApp != null) {
final File file = File(lastPatchedApp.patchedFilePath);
if (!file.existsSync()) {
deleteLastPatchedApp();
_prefs.remove('lastPatchedApp');
}
}
}
Future<bool> isAppUninstalled(PatchedApplication app) async {
@@ -779,4 +832,82 @@ class ManagerAPI {
selectedPatchesFile.deleteSync();
}
}
Future<bool> installTypeDialog(BuildContext context) async {
final ValueNotifier<int> installType = ValueNotifier(0);
if (isRooted) {
await showDialog(
context: context,
barrierDismissible: false,
builder: (context) => AlertDialog(
title: Text(t.installerView.installType),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
icon: const Icon(Icons.file_download_outlined),
contentPadding: const EdgeInsets.symmetric(vertical: 16),
content: SingleChildScrollView(
child: ValueListenableBuilder(
valueListenable: installType,
builder: (context, value, child) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
child: Text(
t.installerView.installTypeDescription,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Theme.of(context).colorScheme.secondary,
),
),
),
RadioListTile(
title: Text(t.installerView.installNonRootType),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16),
value: 0,
groupValue: value,
onChanged: (selected) {
installType.value = selected!;
},
),
RadioListTile(
title: Text(t.installerView.installRootType),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16),
value: 1,
groupValue: value,
onChanged: (selected) {
installType.value = selected!;
},
),
],
);
},
),
),
actions: [
OutlinedButton(
child: Text(t.cancelButton),
onPressed: () {
Navigator.of(context).pop();
},
),
FilledButton(
child: Text(t.installerView.installButton),
onPressed: () {
Navigator.of(context).pop();
},
),
],
),
);
}
return false;
}
}

View File

@@ -217,7 +217,7 @@ class PatcherAPI {
BuildContext context,
PatchedApplication patchedApp,
) async {
if (outFile != null) {
if (patchedApp.patchedFilePath != '') {
_managerAPI.ctx = context;
try {
if (patchedApp.isRooted) {
@@ -232,7 +232,7 @@ class PatcherAPI {
return await _rootAPI.install(
patchedApp.packageName,
patchedApp.apkFilePath,
outFile!.path,
patchedApp.patchedFilePath,
)
? 0
: 1;
@@ -246,7 +246,7 @@ class PatcherAPI {
if (context.mounted) {
return await installApk(
context,
outFile!.path,
patchedApp.patchedFilePath,
);
}
}
@@ -368,13 +368,13 @@ class PatcherAPI {
return cleanInstall ? 10 : 1;
}
void exportPatchedFile(String appName, String version) {
void exportPatchedFile(PatchedApplication app) {
try {
if (outFile != null) {
final String newName = _getFileName(appName, version);
final String newName = _getFileName(app.name, app.version);
FlutterFileDialog.saveFile(
params: SaveFileDialogParams(
sourceFilePath: outFile!.path,
sourceFilePath: app.patchedFilePath,
fileName: newName,
mimeTypesFilter: ['application/vnd.android.package-archive'],
),
@@ -387,14 +387,14 @@ class PatcherAPI {
}
}
void sharePatchedFile(String appName, String version) {
void sharePatchedFile(PatchedApplication app) {
try {
if (outFile != null) {
final String newName = _getFileName(appName, version);
final int lastSeparator = outFile!.path.lastIndexOf('/');
final String newName = _getFileName(app.name, app.version);
final int lastSeparator = app.patchedFilePath.lastIndexOf('/');
final String newPath =
outFile!.path.substring(0, lastSeparator + 1) + newName;
final File shareFile = outFile!.copySync(newPath);
app.patchedFilePath.substring(0, lastSeparator + 1) + newName;
final File shareFile = File(app.patchedFilePath).copySync(newPath);
Share.shareXFiles([XFile(shareFile.path)]);
}
} on Exception catch (e) {

View File

@@ -10,7 +10,7 @@ var lightCustomTheme = ThemeData(
useMaterial3: true,
colorScheme: lightCustomColorScheme,
navigationBarTheme: NavigationBarThemeData(
labelTextStyle: MaterialStateProperty.all(
labelTextStyle: WidgetStateProperty.all(
TextStyle(
color: lightCustomColorScheme.onSurface,
fontWeight: FontWeight.w500,
@@ -31,7 +31,7 @@ var darkCustomTheme = ThemeData(
useMaterial3: true,
colorScheme: darkCustomColorScheme,
navigationBarTheme: NavigationBarThemeData(
labelTextStyle: MaterialStateProperty.all(
labelTextStyle: WidgetStateProperty.all(
TextStyle(
color: darkCustomColorScheme.onSurface,
fontWeight: FontWeight.w500,

View File

@@ -24,14 +24,26 @@ class DynamicThemeBuilder extends StatefulWidget {
class _DynamicThemeBuilderState extends State<DynamicThemeBuilder>
with WidgetsBindingObserver {
Brightness brightness = PlatformDispatcher.instance.platformBrightness;
late Brightness brightness;
@override
void initState() {
super.initState();
brightness = PlatformDispatcher.instance.platformBrightness;
WidgetsBinding.instance.addObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
final systemBrightness = PlatformDispatcher.instance.platformBrightness;
if (brightness != systemBrightness) {
brightness = systemBrightness;
setState(() {});
}
}
}
@override
Widget build(BuildContext context) {
return DynamicColorBuilder(
@@ -39,7 +51,7 @@ class _DynamicThemeBuilderState extends State<DynamicThemeBuilder>
final ThemeData lightDynamicTheme = ThemeData(
useMaterial3: true,
navigationBarTheme: NavigationBarThemeData(
labelTextStyle: MaterialStateProperty.all(
labelTextStyle: WidgetStateProperty.all(
GoogleFonts.roboto(
color: lightColorScheme?.onSurface,
fontWeight: FontWeight.w500,
@@ -53,7 +65,7 @@ class _DynamicThemeBuilderState extends State<DynamicThemeBuilder>
brightness: Brightness.dark,
useMaterial3: true,
navigationBarTheme: NavigationBarThemeData(
labelTextStyle: MaterialStateProperty.all(
labelTextStyle: WidgetStateProperty.all(
GoogleFonts.roboto(
color: darkColorScheme?.onSurface,
fontWeight: FontWeight.w500,

View File

@@ -13,6 +13,7 @@ import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/services/toast.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/utils/about_info.dart';
import 'package:revanced_manager/utils/check_for_supported_patch.dart';
import 'package:stacked/stacked.dart';
@@ -74,11 +75,14 @@ class AppSelectorViewModel extends BaseViewModel {
required String packageName,
}) async {
final String suggestedVersion = getSuggestedVersion(packageName);
final String architecture = await AboutInfo.getInfo().then((info) {
return info['supportedArch'][0];
});
if (suggestedVersion.isNotEmpty) {
await openDefaultBrowser('$packageName apk version $suggestedVersion');
await openDefaultBrowser('$packageName apk version $suggestedVersion $architecture');
} else {
await openDefaultBrowser('$packageName apk');
await openDefaultBrowser('$packageName apk $architecture');
}
}
@@ -178,7 +182,6 @@ class AppSelectorViewModel extends BaseViewModel {
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
actions: [
FilledButton(

View File

@@ -4,6 +4,7 @@ import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/homeView/installed_apps_card.dart';
import 'package:revanced_manager/ui/widgets/homeView/last_patched_app_card.dart';
import 'package:revanced_manager/ui/widgets/homeView/latest_commit_card.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
import 'package:stacked/stacked.dart';
@@ -20,7 +21,9 @@ class HomeView extends StatelessWidget {
viewModelBuilder: () => locator<HomeViewModel>(),
builder: (context, model, child) => Scaffold(
body: RefreshIndicator(
onRefresh: () => model.forceRefresh(context),
edgeOffset: 110.0,
displacement: 10.0,
onRefresh: () async => await model.forceRefresh(context),
child: CustomScrollView(
slivers: <Widget>[
CustomSliverAppBar(
@@ -44,6 +47,21 @@ class HomeView extends StatelessWidget {
const SizedBox(height: 10),
LatestCommitCard(model: model, parentContext: context),
const SizedBox(height: 23),
Visibility(
visible: model.isLastPatchedAppEnabled(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
t.homeView.lastPatchedAppSubtitle,
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 10),
LastPatchedAppCard(),
const SizedBox(height: 10),
],
),
),
Text(
t.homeView.patchedSubtitle,
style: Theme.of(context).textTheme.titleLarge,

View File

@@ -35,6 +35,7 @@ class HomeViewModel extends BaseViewModel {
final Toast _toast = locator<Toast>();
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
bool showUpdatableApps = false;
PatchedApplication? lastPatchedApp;
bool releaseBuild = false;
List<PatchedApplication> patchedInstalledApps = [];
String _currentManagerVersion = '';
@@ -102,10 +103,10 @@ class HomeViewModel extends BaseViewModel {
}
}
void navigateToAppInfo(PatchedApplication app) {
void navigateToAppInfo(PatchedApplication app, bool isLastPatchedApp) {
_navigationService.navigateTo(
Routes.appInfoView,
arguments: AppInfoViewArguments(app: app),
arguments: AppInfoViewArguments(app: app, isLastPatchedApp: isLastPatchedApp),
);
}
@@ -123,10 +124,15 @@ class HomeViewModel extends BaseViewModel {
}
void getPatchedApps() {
lastPatchedApp = _managerAPI.getLastPatchedApp();
patchedInstalledApps = _managerAPI.getPatchedApps().toList();
notifyListeners();
}
bool isLastPatchedAppEnabled() {
return _managerAPI.isLastPatchedAppEnabled();
}
Future<bool> hasManagerUpdates() async {
if (!_managerAPI.releaseBuild) {
return false;
@@ -452,10 +458,6 @@ class HomeViewModel extends BaseViewModel {
}
}
void updatesAreDisabled() {
_toast.showBottom(t.homeView.updatesDisabled);
}
Future<void> showUpdateConfirmationDialog(
BuildContext parentContext,
bool isPatches, [
@@ -494,8 +496,8 @@ class HomeViewModel extends BaseViewModel {
}
Future<void> forceRefresh(BuildContext context) async {
_managerAPI.clearAllData();
await _managerAPI.clearAllData();
await initialize(context);
_toast.showBottom(t.homeView.refreshSuccess);
initialize(context);
}
}

View File

@@ -123,7 +123,7 @@ class InstallerViewModel extends BaseViewModel {
});
await WakelockPlus.enable();
await handlePlatformChannelMethods();
await runPatcher();
await runPatcher(context);
}
Future<dynamic> handlePlatformChannelMethods() async {
@@ -182,13 +182,20 @@ class InstallerViewModel extends BaseViewModel {
notifyListeners();
}
Future<void> runPatcher() async {
Future<void> runPatcher(BuildContext context) async {
try {
await _patcherAPI.runPatcher(
_app.packageName,
_app.apkFilePath,
_patches,
);
_app.appliedPatches = _patches.map((p) => p.name).toList();
if (_managerAPI.isLastPatchedAppEnabled()) {
await _managerAPI.setLastPatchedApp(_app, _patcherAPI.outFile!);
} else {
_app.patchedFilePath = _patcherAPI.outFile!.path;
}
locator<HomeViewModel>().initialize(context);
} on Exception catch (e) {
update(
-100.0,
@@ -488,7 +495,7 @@ class InstallerViewModel extends BaseViewModel {
Future<void> installResult(BuildContext context, bool installAsRoot) async {
isInstalling = true;
try {
_app.isRooted = installAsRoot;
_app.isRooted = await _managerAPI.installTypeDialog(context);
if (headerLogs != 'Installing...') {
update(
.85,
@@ -501,17 +508,15 @@ class InstallerViewModel extends BaseViewModel {
isInstalled = true;
_app.isFromStorage = false;
_app.patchDate = DateTime.now();
_app.appliedPatches = _patches.map((p) => p.name).toList();
// In case a patch changed the app name or package name,
// update the app info.
final app =
await DeviceApps.getAppFromStorage(_patcherAPI.outFile!.path);
await DeviceApps.getAppFromStorage(_patcherAPI.outFile!.path);
if (app != null) {
_app.name = app.appName;
_app.packageName = app.packageName;
}
await _managerAPI.savePatchedApp(_app);
_managerAPI
@@ -544,7 +549,7 @@ class InstallerViewModel extends BaseViewModel {
void exportResult() {
try {
_patcherAPI.exportPatchedFile(_app.name, _app.version);
_patcherAPI.exportPatchedFile(_app);
} on Exception catch (e) {
if (kDebugMode) {
print(e);

View File

@@ -301,9 +301,16 @@ class PatchesSelectorViewModel extends BaseViewModel {
Future<void> loadSelectedPatches(BuildContext context) async {
if (_managerAPI.isPatchesChangeEnabled()) {
final List<String> selectedPatches = await _managerAPI.getSelectedPatches(
locator<PatcherViewModel>().selectedApp!.packageName,
);
final List<String>? appliedPatches = _managerAPI
.getPatchedApps()
.firstWhereOrNull(
(app) => app.packageName == selectedApp!.packageName,
)
?.appliedPatches;
final List<String> selectedPatches = appliedPatches ??
await _managerAPI.getSelectedPatches(
selectedApp!.packageName,
);
if (selectedPatches.isNotEmpty) {
this.selectedPatches.clear();
this.selectedPatches.addAll(

View File

@@ -141,6 +141,18 @@ class SettingsViewModel extends BaseViewModel {
notifyListeners();
}
bool isLastPatchedAppEnabled() {
return _managerAPI.isLastPatchedAppEnabled();
}
void useLastPatchedApp(bool value) {
_managerAPI.enableLastPatchedAppStatus(value);
if (!value) {
_managerAPI.deleteLastPatchedApp();
}
notifyListeners();
}
bool isVersionCompatibilityCheckEnabled() {
return _managerAPI.isVersionCompatibilityCheckEnabled();
}

View File

@@ -11,8 +11,10 @@ class AppInfoView extends StatelessWidget {
const AppInfoView({
super.key,
required this.app,
required this.isLastPatchedApp,
});
final PatchedApplication app;
final bool isLastPatchedApp;
@override
Widget build(BuildContext context) {
@@ -57,6 +59,14 @@ class AppInfoView extends StatelessWidget {
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 20),
if (isLastPatchedApp) ...[
ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 20.0),
subtitle: Text(t.appInfoView.lastPatchedAppDescription),
),
const SizedBox(height: 4),
],
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: CustomCard(
@@ -71,20 +81,26 @@ class AppInfoView extends StatelessWidget {
type: MaterialType.transparency,
child: InkWell(
borderRadius: BorderRadius.circular(16.0),
onTap: () => model.openApp(app),
onTap: () => isLastPatchedApp
? model.installApp(context, app)
: model.openApp(app),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.open_in_new_outlined,
isLastPatchedApp
? Icons.download_outlined
: Icons.open_in_new_outlined,
color: Theme.of(context)
.colorScheme
.primary,
),
const SizedBox(height: 10),
Text(
t.appInfoView.openButton,
isLastPatchedApp
? t.appInfoView.installButton
: t.appInfoView.openButton,
style: TextStyle(
color: Theme.of(context)
.colorScheme
@@ -108,24 +124,30 @@ class AppInfoView extends StatelessWidget {
type: MaterialType.transparency,
child: InkWell(
borderRadius: BorderRadius.circular(16.0),
onTap: () => model.showUninstallDialog(
context,
app,
false,
),
onTap: () => isLastPatchedApp
? model.exportApp(app)
: model.showUninstallDialog(
context,
app,
false,
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons.delete_outline,
isLastPatchedApp
? Icons.save
: Icons.delete_outline,
color: Theme.of(context)
.colorScheme
.primary,
),
const SizedBox(height: 10),
Text(
t.appInfoView.uninstallButton,
isLastPatchedApp
? t.appInfoView.exportButton
: t.appInfoView.uninstallButton,
style: TextStyle(
color: Theme.of(context)
.colorScheme
@@ -144,14 +166,57 @@ class AppInfoView extends StatelessWidget {
endIndent: 12.0,
width: 1.0,
),
if (app.isRooted)
if (isLastPatchedApp)
VerticalDivider(
color: Theme.of(context).canvasColor,
indent: 12.0,
endIndent: 12.0,
width: 1.0,
),
if (app.isRooted)
if (isLastPatchedApp)
Expanded(
child: Material(
type: MaterialType.transparency,
child: InkWell(
borderRadius: BorderRadius.circular(16.0),
onTap: () => model.showDeleteDialog(
context,
app,
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons
.delete_forever_outlined,
color: Theme.of(context)
.colorScheme
.primary,
),
const SizedBox(height: 10),
Text(
t.appInfoView.deleteButton,
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
),
if (!isLastPatchedApp && app.isRooted)
VerticalDivider(
color: Theme.of(context).canvasColor,
indent: 12.0,
endIndent: 12.0,
width: 1.0,
),
if (!isLastPatchedApp && app.isRooted)
Expanded(
child: Material(
type: MaterialType.transparency,
@@ -240,6 +305,23 @@ class AppInfoView extends StatelessWidget {
),
),
const SizedBox(height: 4),
if (isLastPatchedApp) ...[
ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 20.0),
title: Text(
t.appInfoView.sizeLabel,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
subtitle: Text(
model.getFileSizeString(app.fileSize),
),
),
const SizedBox(height: 4),
],
ListTile(
contentPadding:
const EdgeInsets.symmetric(horizontal: 20.0),

View File

@@ -1,4 +1,5 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:math';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
@@ -20,6 +21,23 @@ class AppInfoViewModel extends BaseViewModel {
final RootAPI _rootAPI = RootAPI();
final Toast _toast = locator<Toast>();
Future<void> installApp(
BuildContext context,
PatchedApplication app,
) async {
app.isRooted = await _managerAPI.installTypeDialog(context);
final int statusCode = await _patcherAPI.installPatchedFile(context, app);
if (statusCode == 0) {
locator<HomeViewModel>().initialize(context);
}
}
Future<void> exportApp(
PatchedApplication app,
) async {
_patcherAPI.exportPatchedFile(app);
}
Future<void> uninstallApp(
BuildContext context,
PatchedApplication app,
@@ -123,6 +141,34 @@ class AppInfoViewModel extends BaseViewModel {
}
}
Future<void> showDeleteDialog(
BuildContext context,
PatchedApplication app,
) async {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(t.appInfoView.removeAppDialogTitle),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: Text(t.appInfoView.removeAppDialogText),
actions: <Widget>[
OutlinedButton(
child: Text(t.cancelButton),
onPressed: () => Navigator.of(context).pop(),
),
FilledButton(
child: Text(t.okButton),
onPressed: () => {
_managerAPI.deleteLastPatchedApp(),
Navigator.of(context)..pop()..pop(),
locator<HomeViewModel>().initialize(context),
},
),
],
),
);
}
String getPrettyDate(BuildContext context, DateTime dateTime) {
return DateFormat.yMMMMd(Localizations.localeOf(context).languageCode)
.format(dateTime);
@@ -133,6 +179,12 @@ class AppInfoViewModel extends BaseViewModel {
.format(dateTime);
}
String getFileSizeString(int bytes) {
const suffixes = ['B', 'KB', 'MB', 'GB', 'TB'];
final i = (log(bytes) / log(1024)).floor();
return '${(bytes / pow(1024, i)).toStringAsFixed(2)} ${suffixes[i]}';
}
Future<void> showAppliedPatchesDialog(
BuildContext context,
PatchedApplication app,

View File

@@ -76,7 +76,7 @@ class InstalledAppsCard extends StatelessWidget {
name: app.name,
patchDate: app.patchDate,
onPressed: () =>
locator<HomeViewModel>().navigateToAppInfo(app),
locator<HomeViewModel>().navigateToAppInfo(app, false),
),
)
.toList(),

View File

@@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/gen/strings.g.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/shared/application_item.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
//ignore: must_be_immutable
class LastPatchedAppCard extends StatelessWidget {
LastPatchedAppCard({super.key});
PatchedApplication? app = locator<HomeViewModel>().lastPatchedApp;
@override
Widget build(BuildContext context) {
return app == null
? CustomCard(
child: Center(
child: Column(
children: <Widget>[
Icon(
size: 40,
Icons.update_disabled,
color: Theme.of(context).colorScheme.secondary,
),
const SizedBox(height: 16),
Text(
t.homeView.noSavedAppFound,
style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(
color:
Theme.of(context).colorScheme.secondary,
),
),
],
),
),
)
: ApplicationItem(
icon: app!.icon,
name: app!.name,
patchDate: app!.patchDate,
onPressed: () =>
locator<HomeViewModel>().navigateToAppInfo(app!, true),
);
}
}

View File

@@ -68,7 +68,7 @@ class _PatchItemState extends State<PatchItem> {
if (widget.isUnsupported &&
widget._managerAPI.isVersionCompatibilityCheckEnabled()) {
widget.isSelected = false;
widget.toast.showBottom('patchItem.unsupportedPatchVersion');
widget.toast.showBottom(t.patchItem.unsupportedPatchVersion);
} else if (widget.isChangeEnabled) {
if (!widget.isSelected) {
if (widget.hasUnsupportedPatchOption) {
@@ -103,7 +103,7 @@ class _PatchItemState extends State<PatchItem> {
.isVersionCompatibilityCheckEnabled()) {
widget.isSelected = false;
widget.toast.showBottom(
'patchItem.unsupportedPatchVersion',
t.patchItem.unsupportedPatchVersion,
);
} else if (widget.isChangeEnabled) {
if (!widget.isSelected) {
@@ -170,7 +170,7 @@ class _PatchItemState extends State<PatchItem> {
onPressed: () =>
_showUnsupportedWarningDialog(),
style: ButtonStyle(
shape: MaterialStateProperty.all(
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(8),
@@ -181,12 +181,10 @@ class _PatchItemState extends State<PatchItem> {
),
),
),
backgroundColor:
MaterialStateProperty.all(
backgroundColor: WidgetStateProperty.all(
Colors.transparent,
),
foregroundColor:
MaterialStateProperty.all(
foregroundColor: WidgetStateProperty.all(
Theme.of(context).colorScheme.secondary,
),
),

View File

@@ -1,8 +1,11 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_file_dialog/flutter_file_dialog.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/ui/views/patch_options/patch_options_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
@@ -398,6 +401,7 @@ class TextFieldForPatchOption extends StatefulWidget {
}
class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
final ManagerAPI _managerAPI = locator<ManagerAPI>();
final TextEditingController controller = TextEditingController();
String? selectedKey;
String? defaultValue;
@@ -524,29 +528,49 @@ class _TextFieldForPatchOptionState extends State<TextFieldForPatchOption> {
];
},
onSelected: (String selection) async {
switch (selection) {
case 'file':
final String? result = await FlutterFileDialog.pickFile();
if (result != null) {
controller.text = result;
widget.onChanged(controller.text);
// manageExternalStorage permission is required for file/folder selection
// otherwise, the app will not complain, but the patches will error out
// the same way as if the user selected an empty file/folder.
// Android 11 and above requires the manageExternalStorage permission
final Map<String, dynamic> availableActions = {
t.patchOptionsView.selectFilePath: () async {
if (_managerAPI.isScopedStorageAvailable) {
final permission =
await Permission.manageExternalStorage.request();
if (!permission.isGranted) {
return;
}
}
break;
case 'folder':
final DirectoryLocation? result =
await FlutterFileDialog.pickDirectory();
if (result != null) {
controller.text = result.toString();
widget.onChanged(controller.text);
final FilePickerResult? result =
await FilePicker.platform.pickFiles();
if (result == null) {
return;
}
break;
case 'remove':
controller.text = result.files.single.path!;
widget.onChanged(controller.text);
},
t.patchOptionsView.selectFolder: () async {
if (_managerAPI.isScopedStorageAvailable) {
final permission =
await Permission.manageExternalStorage.request();
if (!permission.isGranted) {
return;
}
}
final String? result =
await FilePicker.platform.getDirectoryPath();
if (result == null) {
return;
}
controller.text = result;
widget.onChanged(controller.text);
},
t.remove: () {
widget.removeValue!();
break;
case 'null':
controller.text = '';
widget.onChanged(null);
break;
},
};
if (availableActions.containsKey(selection)) {
await availableActions[selection]!();
}
},
),

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_auto_update_patches.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_enable_patches_selection.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_last_patched_app.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_require_suggested_app_version.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_section.dart';
import 'package:revanced_manager/ui/widgets/settingsView/settings_show_update_dialog.dart';
@@ -24,6 +25,7 @@ class SAdvancedSection extends StatelessWidget {
SRequireSuggestedAppVersion(),
SVersionCompatibilityCheck(),
SUniversalPatches(),
SLastPatchedApp(),
],
);
}

View File

@@ -0,0 +1,37 @@
import 'package:flutter/material.dart';
import 'package:revanced_manager/gen/strings.g.dart';
import 'package:revanced_manager/ui/views/settings/settings_viewmodel.dart';
class SLastPatchedApp extends StatefulWidget {
const SLastPatchedApp({super.key});
@override
State<SLastPatchedApp> createState() =>
_SLastPatchedAppState();
}
final _settingsViewModel = SettingsViewModel();
class _SLastPatchedAppState
extends State<SLastPatchedApp> {
@override
Widget build(BuildContext context) {
return SwitchListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 20.0),
title: Text(
t.settingsView.lastPatchedAppLabel,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
subtitle: Text(t.settingsView.lastPatchedAppHint),
value: _settingsViewModel.isLastPatchedAppEnabled(),
onChanged: (value) {
setState(() {
_settingsViewModel.useLastPatchedApp(value);
});
},
);
}
}

View File

@@ -33,6 +33,7 @@ class _ApplicationItemState extends State<ApplicationItem> {
return Container(
margin: const EdgeInsets.only(bottom: 16.0),
child: CustomCard(
onTap: widget.onPressed,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [

View File

@@ -38,8 +38,8 @@ class CustomSliverAppBar extends StatelessWidget {
onPressed:
onBackButtonPressed ?? () => Navigator.of(context).pop(),
),
backgroundColor: MaterialStateColor.resolveWith(
(states) => states.contains(MaterialState.scrolledUnder)
backgroundColor: WidgetStateColor.resolveWith(
(states) => states.contains(WidgetState.scrolledUnder)
? Theme.of(context).colorScheme.surface
: Theme.of(context).canvasColor,
),

View File

@@ -306,6 +306,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "7.0.0"
file_picker:
dependency: "direct main"
description:
name: file_picker
sha256: "2ca051989f69d1b2ca012b2cf3ccf78c70d40144f0861ff2c063493f7c8c3d45"
url: "https://pub.dev"
source: hosted
version: "8.0.5"
fixnum:
dependency: transitive
description:
@@ -385,10 +393,18 @@ packages:
dependency: "direct main"
description:
name: flutter_markdown
sha256: "9921f9deda326f8a885e202b1e35237eadfc1345239a0f6f0f1ff287e047547f"
sha256: "2e8a801b1ded5ea001a4529c97b1f213dcb11c6b20668e081cafb23468593514"
url: "https://pub.dev"
source: hosted
version: "0.7.1"
version: "0.7.3"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: c6b0b4c05c458e1c01ad9bcc14041dd7b1f6783d487be4386f793f47a8a4d03e
url: "https://pub.dev"
source: hosted
version: "2.0.20"
flutter_test:
dependency: transitive
description: flutter
@@ -571,18 +587,18 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
version: "10.0.5"
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
version: "3.0.5"
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
@@ -644,18 +660,18 @@ packages:
dependency: transitive
description:
name: material_color_utilities
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev"
source: hosted
version: "0.11.1"
version: "0.8.0"
meta:
dependency: transitive
description:
name: meta
sha256: "25dfcaf170a0190f47ca6355bdd4552cb8924b430512ff0cafb8db9bd41fe33b"
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
version: "1.14.0"
version: "1.12.0"
mime:
dependency: transitive
description:
@@ -1148,10 +1164,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "2419f20b0c8677b2d67c8ac4d1ac7372d862dc6c460cdbb052b40155408cd794"
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
version: "0.7.1"
version: "0.7.0"
timeago:
dependency: "direct main"
description:
@@ -1276,10 +1292,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "7475cb4dd713d57b6f7464c0e13f06da0d535d8b2067e188962a59bac2cf280b"
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "14.2.2"
version: "14.2.1"
wakelock_plus:
dependency: "direct main"
description:
@@ -1361,5 +1377,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.19.2"
dart: ">=3.4.0 <4.0.0"
flutter: ">=3.22.0"

View File

@@ -4,7 +4,7 @@ homepage: https://github.com/ReVanced/revanced-manager
publish_to: 'none'
version: 1.21.0-dev.5+101800021
version: 1.21.0-dev.7+101800023
environment:
sdk: '>=3.0.0 <4.0.0'
@@ -23,6 +23,7 @@ dependencies:
dynamic_color: ^1.7.0
dynamic_themes: ^1.1.0
expandable: ^5.0.1
file_picker: ^8.0.3
flutter:
sdk: flutter
flutter_background:
@@ -34,7 +35,7 @@ dependencies:
flutter_local_notifications: ^17.1.0
flutter_localizations:
sdk: flutter
flutter_markdown: ^0.7.1
flutter_markdown: ^0.7.3
fluttertoast: ^8.2.5
font_awesome_flutter: ^10.7.0
google_fonts: ^6.2.1