Compare commits

...

42 Commits

Author SHA1 Message Date
Aunali321
4c6b93320f build: Bump bump version to v0.0.13 2022-09-18 12:22:39 +05:30
Aunali321
2f3bb6cfe4 refactor: better wording. 2022-09-18 12:18:47 +05:30
oSumAtrIX
0e1fa1a5d6 build: bump patcher dependency 2022-09-18 08:38:03 +02:00
Alberto Ponces
38fb3444e4 build: Bump version to v0.0.12 2022-09-18 05:07:12 +01:00
Alberto Ponces
f10b5aebac fix: Decrease app name space a bit to prevent overflowing 2022-09-18 05:06:51 +01:00
Alberto Ponces
5a3884e159 fix: Fix uninstall apps 2022-09-18 05:06:23 +01:00
Alberto Ponces
41ac2b0df8 build: Bump version to v0.0.11 2022-09-18 04:47:23 +01:00
Alberto Ponces
52e7d76c9d fix: Fix spacing of App Info buttons 2022-09-18 04:46:21 +01:00
Alberto Ponces
3aa80cacc0 fix: Fix duplicate entries on non-root installations 2022-09-18 04:44:44 +01:00
Alberto Ponces
dd52c379b4 fix: Show updatable app item only on updatable apps listing 2022-09-18 04:12:33 +01:00
Alberto Ponces
5d073bddf2 fix: Minor i18n typo 2022-09-18 03:41:16 +01:00
Alberto Ponces
7d09169c06 build: Bump version to v0.0.10 2022-09-18 03:35:32 +01:00
Alberto Ponces
ce6f11f3f2 feat: Show recommendation version when possible 2022-09-18 03:14:48 +01:00
Alberto Ponces
d0fb6ac3c0 fix: Ignore empty results on root installed apps listing 2022-09-18 02:41:11 +01:00
Alberto Ponces
ae801a2918 fix: Fix apps reassess on root 2022-09-18 01:54:25 +01:00
Alberto Ponces
257fd46e27 fix: Improve quality of launcher and splash icons 2022-09-18 01:23:21 +01:00
Alberto Ponces
bb96c3ed63 fix: Make sure aapt lib gets extracted to native lib dir during install 2022-09-17 22:54:38 +01:00
Alberto Ponces
6bd218277d build: Bump version to v0.0.9 2022-09-17 19:40:44 +01:00
Alberto Ponces
83ad7605c4 fix: Minor improvements on App Info view 2022-09-17 19:30:04 +01:00
Alberto Ponces
5fbc8ff7a0 feat: Retrieve unsaved apps from manager's root folder and known packages from user's apps list 2022-09-17 19:29:46 +01:00
Alberto Ponces
bb4b59eee6 Disable permission_handler package for now 2022-09-17 17:42:29 +01:00
Aunali321
4cfb620d63 build: Bump version to v0.0.8. 2022-09-17 20:58:19 +05:30
Aunali321
55739a9c78 fix: background should be more opaque. 2022-09-17 20:56:44 +05:30
Alberto Ponces
482599bb4e fix: Do not import dart:html 2022-09-17 14:59:23 +01:00
Alberto Ponces
3aaf49fee0 fix: Only init foreground service if user allowed IGNORE_BATTERY_OPTIMIZATIONS permission 2022-09-17 14:55:00 +01:00
Alberto Ponces
0424ee235c build: Bump patcher dependency version 2022-09-17 14:51:09 +01:00
Alberto Ponces
a178afce99 fix: Fix typo on menu options visibility 2022-09-17 14:47:03 +01:00
Alberto Ponces
ef3685c817 fix: Add Unpatch option on App Info view 2022-09-17 14:45:43 +01:00
Alberto Ponces
e74fce8574 Merge branch 'flutter' of github.com:revanced/revanced-manager into flutter 2022-09-17 14:42:58 +01:00
Aunali321
73e4ae1416 build: restrict manager to android 8. 2022-09-17 16:14:24 +03:00
afnzmn
bcf3b36b13 feat: tweak UI 2022-09-17 16:13:41 +03:00
Alberto Ponces
9c3626c8ed fix: Request permissions a single time at boot 2022-09-16 17:29:32 +01:00
Alberto Ponces
a3dca8c142 fix: Add build mode to about widget 2022-09-16 16:39:57 +01:00
Alberto Ponces
ef0c59f693 fix: Surround isRooted with a try/catch just to be safe 2022-09-16 16:39:37 +01:00
Aunali321
31756884b6 build: Bump version to v0.0.7. 2022-09-16 03:48:23 +05:30
Aunali321
0a2a495ab0 feat: custom animated progressbar. 2022-09-16 03:46:46 +05:30
Aunali321
8300cc4071 feat: dropdown for changelogs. 2022-09-16 00:49:11 +05:30
Alberto Ponces
9f58757caf feat: Apply dark/light mode to system navigation bar too 2022-09-15 13:27:48 +01:00
Alberto Ponces
5d296038b7 fix: Increase API's maxAge to 1 day 2022-09-15 12:33:19 +01:00
Alberto Ponces
9f85450c6c build: Bump version to v0.0.6 2022-09-15 09:14:43 +01:00
Alberto Ponces
cba4e175a3 build: Bump version to v0.0.5 2022-09-15 02:27:36 +01:00
Alberto Ponces
cf3fa935ce fix: Fix select from storage for Android 13 devices 2022-09-15 02:18:07 +01:00
38 changed files with 538 additions and 167 deletions

View File

@@ -44,7 +44,7 @@ android {
defaultConfig {
applicationId "app.revanced.manager.flutter"
minSdkVersion 21
minSdkVersion 26
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
@@ -71,7 +71,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// ReVanced
implementation "app.revanced:revanced-patcher:4.4.0"
implementation "app.revanced:revanced-patcher:4.4.2"
// Signing & aligning
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")

View File

@@ -1,18 +1,23 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="app.revanced.manager.flutter">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:label="ReVanced Manager"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:largeHeap="true">
android:largeHeap="true"
android:extractNativeLibs="true">
<activity
android:name=".MainActivity"
android:exported="true"

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_launcher_round</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:windowSplashScreenAnimatedIcon">@drawable/ic_launcher_round</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -25,7 +25,7 @@
"downloadingMessage": "Downloading update!",
"installingMessage": "Installing update... Hang on!",
"errorDownloadMessage": "Unable to download update!",
"errorInstallMessage": "Unable to download update!",
"errorInstallMessage": "Unable to install update!",
"noConnection": "No internet connection"
},
"applicationItem": {
@@ -50,7 +50,10 @@
"widgetTitle": "Select application",
"widgetTitleSelected": "Selected application",
"widgetSubtitle": "No application selected.",
"noAppsLabel": "No applications found."
"noAppsLabel": "No applications found.",
"currentVersion": "Current",
"recommendedVersion": "Recommended",
"anyVersion": "any"
},
"patchSelectorCard": {
"widgetTitle": "Select patches",
@@ -69,12 +72,13 @@
},
"patchesSelectorView": {
"searchBarHint": "Search patches",
"doneButton": "Done"
"doneButton": "Done",
"noPatchesFound": "No patches found for the selected app."
},
"patchItem": {
"unsupportedWarningButton": "Unsupported version",
"unsupportedDialogTitle": "Warning",
"unsupportedDialogText": "Selecting this patch may or may not result in patching errors.\n\nApp version: {packageVersion}\nCurrent supported versions:\n{supportedVersions}"
"unsupportedDialogText": "Selecting this patch may result in patching errors.\n\nApp version: {packageVersion}\nCurrent supported versions:\n{supportedVersions}"
},
"installerView": {
"widgetTitle": "Installer",
@@ -120,8 +124,11 @@
"openButton": "Open",
"uninstallButton": "Uninstall",
"patchButton": "Patch",
"unpatchButton": "Unpatch",
"uninstallDialogTitle": "Uninstall",
"uninstallDialogText": "Are you sure you want to uninstall this app?",
"unpatchDialogTitle": "Unpatch",
"unpatchDialogText": "Are you sure you want to unpatch this app?",
"rootDialogTitle": "Error",
"rootDialogText": "App was installed with root mode enabled but currently root mode is disabled.\nPlease enable root mode first.",
"packageNameLabel": "Package Name",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -13,7 +13,7 @@ class GithubAPI {
final Dio _dio = Dio();
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
final Options _cacheOptions = buildCacheOptions(
const Duration(hours: 1),
const Duration(days: 1),
maxStale: const Duration(days: 7),
);
final Map<String, String> repoAppPath = {

View File

@@ -165,46 +165,114 @@ class ManagerAPI {
return packageInfo.version;
}
Future<void> reAssessSavedApps() async {
List<PatchedApplication> patchedApps = getPatchedApps();
Future<List<PatchedApplication>> getAppsToRemove(
List<PatchedApplication> patchedApps,
) async {
List<PatchedApplication> toRemove = [];
for (PatchedApplication app in patchedApps) {
bool isRemove = await isAppUninstalled(app);
if (isRemove) {
toRemove.add(app);
} else {
app.hasUpdates = await hasAppUpdates(app.packageName, app.patchDate);
app.changelog = await getAppChangelog(app.packageName, app.patchDate);
if (!app.hasUpdates) {
String? currentInstalledVersion =
(await DeviceApps.getApp(app.packageName))?.versionName;
if (currentInstalledVersion != null) {
String currentSavedVersion = app.version;
int currentInstalledVersionInt = int.parse(
currentInstalledVersion.replaceAll(RegExp('[^0-9]'), ''));
int currentSavedVersionInt =
int.parse(currentSavedVersion.replaceAll(RegExp('[^0-9]'), ''));
if (currentInstalledVersionInt > currentSavedVersionInt) {
app.hasUpdates = true;
}
}
}
return toRemove;
}
Future<List<PatchedApplication>> getUnsavedApps(
List<PatchedApplication> patchedApps,
) async {
List<PatchedApplication> unsavedApps = [];
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) {
List<String> installedApps = await _rootAPI.getInstalledApps();
for (String packageName in installedApps) {
if (!patchedApps.any((app) => app.packageName == packageName)) {
ApplicationWithIcon? application =
await DeviceApps.getApp(packageName, true)
as ApplicationWithIcon?;
if (application != null) {
unsavedApps.add(
PatchedApplication(
name: application.appName,
packageName: application.packageName,
version: application.versionName!,
apkFilePath: application.apkFilePath,
icon: application.icon,
patchDate: DateTime.now(),
isRooted: true,
),
);
}
}
}
}
List<Application> userApps = await DeviceApps.getInstalledApplications(
includeSystemApps: false,
includeAppIcons: false,
);
for (Application app in userApps) {
if (app.packageName.startsWith('app.revanced') &&
!app.packageName.startsWith('app.revanced.manager.') &&
!patchedApps.any((uapp) => uapp.packageName == app.packageName)) {
ApplicationWithIcon? application =
await DeviceApps.getApp(app.packageName, true)
as ApplicationWithIcon?;
if (application != null) {
unsavedApps.add(
PatchedApplication(
name: application.appName,
packageName: application.packageName,
version: application.versionName!,
apkFilePath: application.apkFilePath,
icon: application.icon,
patchDate: DateTime.now(),
isRooted: false,
),
);
}
}
}
return unsavedApps;
}
Future<void> reAssessSavedApps() async {
List<PatchedApplication> patchedApps = getPatchedApps();
List<PatchedApplication> unsavedApps = await getUnsavedApps(patchedApps);
patchedApps.addAll(unsavedApps);
List<PatchedApplication> toRemove = await getAppsToRemove(patchedApps);
patchedApps.removeWhere((a) => toRemove.contains(a));
for (PatchedApplication app in patchedApps) {
app.hasUpdates = await hasAppUpdates(app.packageName, app.patchDate);
app.changelog = await getAppChangelog(app.packageName, app.patchDate);
if (!app.hasUpdates) {
String? currentInstalledVersion =
(await DeviceApps.getApp(app.packageName))?.versionName;
if (currentInstalledVersion != null) {
String currentSavedVersion = app.version;
int currentInstalledVersionInt = int.parse(
currentInstalledVersion.replaceAll(RegExp('[^0-9]'), ''));
int currentSavedVersionInt =
int.parse(currentSavedVersion.replaceAll(RegExp('[^0-9]'), ''));
if (currentInstalledVersionInt > currentSavedVersionInt) {
app.hasUpdates = true;
}
}
}
}
await setPatchedApps(patchedApps);
}
Future<bool> isAppUninstalled(PatchedApplication app) async {
bool existsRoot = false;
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
if (app.isRooted) {
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) {
existsRoot = await _rootAPI.isAppInstalled(app.packageName);
}
return !existsRoot || !existsNonRoot;
}
bool existsNonRoot = await DeviceApps.isAppInstalled(app.packageName);
return !existsRoot && !existsNonRoot;
return !existsNonRoot;
}
Future<bool> hasAppUpdates(String packageName, DateTime patchDate) async {

View File

@@ -220,4 +220,32 @@ class PatcherAPI {
log.writeAsStringSync(logs);
ShareExtend.share(log.path, 'file');
}
String getRecommendedVersion(String packageName) {
Map<String, int> versions = {};
for (Patch patch in _patches) {
Package? package = patch.compatiblePackages.firstWhereOrNull(
(pack) => pack.name == packageName,
);
if (package != null) {
for (String version in package.versions) {
versions.update(
version,
(value) => versions[version]! + 1,
ifAbsent: () => 1,
);
}
}
}
if (versions.isNotEmpty) {
var entries = versions.entries.toList()
..sort((a, b) => a.value.compareTo(b.value));
versions
..clear()
..addEntries(entries);
versions.removeWhere((key, value) => value != versions.values.last);
return (versions.keys.toList()..sort()).last;
}
return '';
}
}

View File

@@ -13,7 +13,7 @@ class RevancedAPI {
final Dio _dio = Dio();
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
final Options _cacheOptions = buildCacheOptions(
const Duration(hours: 1),
const Duration(days: 1),
maxStale: const Duration(days: 7),
);

View File

@@ -6,8 +6,12 @@ class RootAPI {
final String _serviceDDirPath = '/data/adb/service.d';
Future<bool> hasRootPermissions() async {
bool? isRooted = await Root.isRooted();
return isRooted != null && isRooted;
try {
bool? isRooted = await Root.isRooted();
return isRooted != null && isRooted;
} on Exception {
return false;
}
}
Future<void> setPermissions(
@@ -55,15 +59,8 @@ class RootAPI {
);
if (res != null) {
List<String> apps = res.split('\n');
List<String> toRemove = [];
for (String packageName in apps) {
bool isInstalled = await isAppInstalled(packageName);
if (!isInstalled) {
toRemove.add(packageName);
}
}
apps.removeWhere((a) => toRemove.contains(a));
return apps;
apps.removeWhere((pack) => pack.isEmpty);
return apps.map((pack) => pack.trim()).toList();
}
} on Exception {
return List.empty();

View File

@@ -4,6 +4,7 @@ import 'package:google_fonts/google_fonts.dart';
var lightCustomColorScheme = ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.light,
primary: const Color(0xff1B73E8),
);
var lightCustomTheme = ThemeData(
@@ -23,8 +24,8 @@ var lightCustomTheme = ThemeData(
var darkCustomColorScheme = ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
primary: const Color(0xff7792BA),
surface: const Color(0xff0A0D11),
primary: const Color(0xffA5CAFF),
surface: const Color(0xff1B1A1D),
);
var darkCustomTheme = ThemeData(
@@ -38,8 +39,8 @@ var darkCustomTheme = ThemeData(
),
),
),
canvasColor: const Color(0xff0A0D11),
scaffoldBackgroundColor: const Color(0xff0A0D11),
toggleableActiveColor: const Color(0xff7792BA),
canvasColor: const Color(0xff1B1A1D),
scaffoldBackgroundColor: const Color(0xff1B1A1D),
toggleableActiveColor: const Color(0xffA5CAFF),
textTheme: GoogleFonts.robotoTextTheme(ThemeData.dark().textTheme),
);

View File

@@ -74,7 +74,10 @@ class HomeViewModel extends BaseViewModel {
}
void _getPatchedApps() {
patchedInstalledApps = _managerAPI.getPatchedApps().toList();
patchedInstalledApps = _managerAPI
.getPatchedApps()
.where((app) => app.hasUpdates == false)
.toList();
patchedUpdatableApps = _managerAPI
.getPatchedApps()
.where((app) => app.hasUpdates == true)

View File

@@ -3,6 +3,7 @@ import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
import 'package:revanced_manager/ui/widgets/installerView/gradient_progress_indicator.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_popup_menu.dart';
import 'package:revanced_manager/ui/widgets/shared/custom_sliver_app_bar.dart';
@@ -30,7 +31,7 @@ class InstallerView extends StatelessWidget {
),
actions: <Widget>[
Visibility(
visible: !model.isPatching && model.hasErrors,
visible: !model.isPatching && !model.hasErrors,
child: CustomPopupMenu(
onSelected: (value) => model.onMenuSelection(value),
children: {
@@ -57,14 +58,9 @@ class InstallerView extends StatelessWidget {
),
],
bottom: PreferredSize(
preferredSize: const Size(double.infinity, 1.0),
child: LinearProgressIndicator(
color: Theme.of(context).colorScheme.primary,
backgroundColor:
Theme.of(context).colorScheme.primaryContainer,
value: model.progress,
),
),
preferredSize: const Size(double.infinity, 1.0),
child:
GradientProgressIndicator(progress: model.progress!)),
),
SliverPadding(
padding: const EdgeInsets.all(20.0),

View File

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background/flutter_background.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
//import 'package:permission_handler/permission_handler.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart';
@@ -29,29 +30,31 @@ class InstallerViewModel extends BaseViewModel {
bool hasErrors = false;
Future<void> initialize(BuildContext context) async {
try {
await FlutterBackground.initialize(
androidConfig: FlutterBackgroundAndroidConfig(
notificationTitle: FlutterI18n.translate(
context,
'installerView.notificationTitle',
if (true /*await Permission.ignoreBatteryOptimizations.isGranted*/) {
try {
await FlutterBackground.initialize(
androidConfig: FlutterBackgroundAndroidConfig(
notificationTitle: FlutterI18n.translate(
context,
'installerView.notificationTitle',
),
notificationText: FlutterI18n.translate(
context,
'installerView.notificationText',
),
notificationImportance: AndroidNotificationImportance.Default,
notificationIcon: const AndroidResource(
name: 'ic_notification',
defType: 'drawable',
),
),
notificationText: FlutterI18n.translate(
context,
'installerView.notificationText',
),
notificationImportance: AndroidNotificationImportance.Default,
notificationIcon: const AndroidResource(
name: 'ic_notification',
defType: 'drawable',
),
),
);
await FlutterBackground.enableBackgroundExecution();
await Wakelock.enable();
} on Exception {
// ignore
);
await FlutterBackground.enableBackgroundExecution();
} on Exception {
// ignore
}
}
await Wakelock.enable();
await handlePlatformChannelMethods();
await runPatcher();
}
@@ -119,12 +122,14 @@ class InstallerViewModel extends BaseViewModel {
hasErrors = true;
update(-1.0, 'Aborting...', 'No app or patches selected! Aborting');
}
try {
await FlutterBackground.disableBackgroundExecution();
await Wakelock.disable();
} on Exception {
// ignore
if (true /*await Permission.ignoreBatteryOptimizations.isGranted*/) {
try {
await FlutterBackground.disableBackgroundExecution();
} on Exception {
// ignore
}
}
await Wakelock.disable();
isPatching = false;
}

View File

@@ -42,6 +42,7 @@ class NavigationView extends StatelessWidget {
context,
'navigationView.dashboardTab',
),
tooltip: '',
),
NavigationDestination(
icon: model.isIndexSelected(1)
@@ -51,6 +52,7 @@ class NavigationView extends StatelessWidget {
context,
'navigationView.patcherTab',
),
tooltip: '',
),
NavigationDestination(
icon: model.isIndexSelected(2)
@@ -60,6 +62,7 @@ class NavigationView extends StatelessWidget {
context,
'navigationView.settingsTab',
),
tooltip: '',
),
],
),

View File

@@ -1,8 +1,9 @@
// ignore_for_file: use_build_context_synchronously
import 'package:dynamic_themes/dynamic_themes.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:injectable/injectable.dart';
import 'package:permission_handler/permission_handler.dart';
//import 'package:permission_handler/permission_handler.dart';
import 'package:revanced_manager/services/root_api.dart';
import 'package:revanced_manager/ui/views/home/home_view.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_view.dart';
@@ -15,17 +16,27 @@ class NavigationViewModel extends IndexTrackingViewModel {
void initialize(BuildContext context) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getBool('useDarkTheme') == null) {
if (MediaQuery.of(context).platformBrightness == Brightness.light) {
await prefs.setBool('useDarkTheme', false);
DynamicTheme.of(context)!.setTheme(0);
} else {
await prefs.setBool('useDarkTheme', true);
DynamicTheme.of(context)!.setTheme(1);
}
bool isDark =
MediaQuery.of(context).platformBrightness != Brightness.light;
await prefs.setBool('useDarkTheme', isDark);
await DynamicTheme.of(context)!.setTheme(isDark ? 1 : 0);
}
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
systemNavigationBarColor:
DynamicTheme.of(context)!.theme.colorScheme.surface,
systemNavigationBarIconBrightness:
DynamicTheme.of(context)!.theme.brightness == Brightness.light
? Brightness.dark
: Brightness.light,
),
);
//if (prefs.getBool('permissionsRequested') == null) {
//await prefs.setBool('permissionsRequested', true);
RootAPI().hasRootPermissions();
Permission.requestInstallPackages.request();
Permission.ignoreBatteryOptimizations.request();
//Permission.requestInstallPackages.request();
//Permission.ignoreBatteryOptimizations.request();
//}
}
Widget getViewForIndex(int index) {

View File

@@ -79,4 +79,32 @@ class PatcherViewModel extends BaseViewModel {
);
}
}
String getAppSelectionString() {
String text = '${selectedApp!.name} (${selectedApp!.packageName})';
if (text.length > 32) {
text = '${text.substring(0, 32)}...)';
}
return text;
}
String getRecommendedVersionString(BuildContext context) {
String recommendedVersion =
_patcherAPI.getRecommendedVersion(selectedApp!.packageName);
if (recommendedVersion.isEmpty) {
recommendedVersion = FlutterI18n.translate(
context,
'appSelectorCard.anyVersion',
);
} else {
recommendedVersion = 'v$recommendedVersion';
}
return '${FlutterI18n.translate(
context,
'appSelectorCard.currentVersion',
)}: v${selectedApp!.version}\n${FlutterI18n.translate(
context,
'appSelectorCard.recommendedVersion',
)}: $recommendedVersion';
}
}

View File

@@ -55,9 +55,16 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
const SizedBox(height: 12),
Expanded(
child: model.patches.isEmpty
? Center(
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.primary,
? Padding(
padding: const EdgeInsets.all(8.0),
child: Center(
child: I18nText(
'patchesSelectorView.noPatchesFound',
child: Text(
'',
style: Theme.of(context).textTheme.bodyMedium,
),
),
),
)
: ListView(

View File

@@ -3,6 +3,7 @@ import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:dynamic_themes/dynamic_themes.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:logcat/logcat.dart';
import 'package:path_provider/path_provider.dart';
@@ -50,10 +51,16 @@ class SettingsViewModel extends BaseViewModel {
await _managerAPI.setUseDynamicTheme(value);
int currentTheme = DynamicTheme.of(context)!.themeId;
if (currentTheme.isEven) {
DynamicTheme.of(context)!.setTheme(value ? 2 : 0);
await DynamicTheme.of(context)!.setTheme(value ? 2 : 0);
} else {
DynamicTheme.of(context)!.setTheme(value ? 3 : 1);
await DynamicTheme.of(context)!.setTheme(value ? 3 : 1);
}
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
systemNavigationBarColor:
DynamicTheme.of(context)!.theme.colorScheme.surface,
),
);
notifyListeners();
}
@@ -65,10 +72,18 @@ class SettingsViewModel extends BaseViewModel {
await _managerAPI.setUseDarkTheme(value);
int currentTheme = DynamicTheme.of(context)!.themeId;
if (currentTheme < 2) {
DynamicTheme.of(context)!.setTheme(value ? 1 : 0);
await DynamicTheme.of(context)!.setTheme(value ? 1 : 0);
} else {
DynamicTheme.of(context)!.setTheme(value ? 3 : 2);
await DynamicTheme.of(context)!.setTheme(value ? 3 : 2);
}
SystemChrome.setSystemUIOverlayStyle(
SystemUiOverlayStyle(
systemNavigationBarColor:
DynamicTheme.of(context)!.theme.colorScheme.surface,
systemNavigationBarIconBrightness:
value ? Brightness.light : Brightness.dark,
),
);
notifyListeners();
}

View File

@@ -64,8 +64,9 @@ class AppInfoView extends StatelessWidget {
CustomCard(
child: IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
!app.isRooted ? const Spacer() : Container(),
InkWell(
onTap: () => model.openApp(app),
child: Column(
@@ -92,12 +93,17 @@ class AppInfoView extends StatelessWidget {
],
),
),
const Spacer(),
VerticalDivider(
color: Theme.of(context).canvasColor,
),
const Spacer(),
InkWell(
onTap: () =>
model.showUninstallAlertDialog(context, app),
onTap: () => model.showUninstallAlertDialog(
context,
app,
false,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
@@ -122,9 +128,11 @@ class AppInfoView extends StatelessWidget {
],
),
),
const Spacer(),
VerticalDivider(
color: Theme.of(context).canvasColor,
),
const Spacer(),
InkWell(
onTap: () {
model.navigateToPatcher(app);
@@ -154,6 +162,49 @@ class AppInfoView extends StatelessWidget {
],
),
),
app.isRooted ? const Spacer() : Container(),
app.isRooted
? VerticalDivider(
color: Theme.of(context).canvasColor,
)
: Container(),
app.isRooted ? const Spacer() : Container(),
app.isRooted
? InkWell(
onTap: () => model.showUninstallAlertDialog(
context,
app,
true,
),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Icon(
Icons
.settings_backup_restore_outlined,
color: Theme.of(context)
.colorScheme
.primary,
),
const SizedBox(height: 10),
I18nText(
'appInfoView.unpatchButton',
child: Text(
'',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary,
fontWeight: FontWeight.bold,
),
),
),
],
),
)
: Container(),
!app.isRooted ? const Spacer() : Container(),
],
),
),

View File

@@ -19,16 +19,20 @@ class AppInfoViewModel extends BaseViewModel {
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final RootAPI _rootAPI = RootAPI();
Future<void> uninstallApp(PatchedApplication app) async {
Future<void> uninstallApp(PatchedApplication app, bool onlyUnpatch) async {
if (app.isRooted) {
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (hasRootPermissions) {
_rootAPI.deleteApp(app.packageName, app.apkFilePath);
_managerAPI.deletePatchedApp(app);
if (!onlyUnpatch) {
DeviceApps.uninstallApp(app.packageName);
}
}
} else {
DeviceApps.uninstallApp(app.packageName);
_managerAPI.deletePatchedApp(app);
DeviceApps.uninstallApp(app.packageName).then(
(value) => _managerAPI.deletePatchedApp(app),
);
}
}
@@ -43,32 +47,39 @@ class AppInfoViewModel extends BaseViewModel {
Future<void> showUninstallAlertDialog(
BuildContext context,
PatchedApplication app,
bool onlyUnpatch,
) async {
if (app.isRooted) {
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (!hasRootPermissions) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: I18nText('appInfoView.rootDialogTitle'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: I18nText('appInfoView.rootDialogText'),
actions: <Widget>[
CustomMaterialButton(
label: I18nText('okButton'),
onPressed: () => Navigator.of(context).pop(),
)
],
),
);
}
bool hasRootPermissions = await _rootAPI.hasRootPermissions();
if (app.isRooted && !hasRootPermissions) {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: I18nText('appInfoView.rootDialogTitle'),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: I18nText('appInfoView.rootDialogText'),
actions: <Widget>[
CustomMaterialButton(
label: I18nText('okButton'),
onPressed: () => Navigator.of(context).pop(),
)
],
),
);
} else {
return showDialog(
context: context,
builder: (context) => AlertDialog(
title: I18nText('appInfoView.uninstallDialogTitle'),
title: I18nText(
onlyUnpatch
? 'appInfoView.unpatchDialogTitle'
: 'appInfoView.uninstallDialogTitle',
),
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
content: I18nText('appInfoView.uninstallDialogText'),
content: I18nText(
onlyUnpatch
? 'appInfoView.unpatchDialogText'
: 'appInfoView.uninstallDialogText',
),
actions: <Widget>[
CustomMaterialButton(
isFilled: false,
@@ -78,7 +89,7 @@ class AppInfoViewModel extends BaseViewModel {
CustomMaterialButton(
label: I18nText('okButton'),
onPressed: () {
uninstallApp(app);
uninstallApp(app, onlyUnpatch);
locator<HomeViewModel>().initialize(context);
Navigator.of(context).pop();
Navigator.of(context).pop();

View File

@@ -71,7 +71,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
future: locator<HomeViewModel>().hasManagerUpdates(),
initialData: false,
builder: (context, snapshot) => Opacity(
opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.5,
opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.25,
child: CustomMaterialButton(
isExpanded: false,
label: I18nText('latestCommitCard.updateButton'),

View File

@@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
class GradientProgressIndicator extends StatefulWidget {
final double? progress;
const GradientProgressIndicator({required this.progress, super.key});
@override
State<GradientProgressIndicator> createState() =>
_GradientProgressIndicatorState();
}
class _GradientProgressIndicatorState extends State<GradientProgressIndicator> {
@override
Widget build(BuildContext context) {
return Align(
alignment: Alignment.centerLeft,
child: AnimatedContainer(
duration: const Duration(milliseconds: 500),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.secondary,
],
),
),
height: 5,
width: MediaQuery.of(context).size.width * widget.progress!,
),
);
}
}

View File

@@ -39,7 +39,7 @@ class AppSelectorCard extends StatelessWidget {
: Row(
children: <Widget>[
SizedBox(
height: 16.0,
height: 18.0,
child: ClipOval(
child: Image.memory(
locator<PatcherViewModel>().selectedApp == null
@@ -49,8 +49,23 @@ class AppSelectorCard extends StatelessWidget {
),
),
),
const SizedBox(width: 4),
Text(_getAppSelection()),
const SizedBox(width: 6),
Text(locator<PatcherViewModel>().getAppSelectionString()),
],
),
locator<PatcherViewModel>().selectedApp == null
? Container()
: Column(
children: [
const SizedBox(height: 10),
Padding(
padding: const EdgeInsets.only(left: 20),
child: Text(
locator<PatcherViewModel>()
.getRecommendedVersionString(context),
style: const TextStyle(fontStyle: FontStyle.italic),
),
),
],
),
],
@@ -58,10 +73,4 @@ class AppSelectorCard extends StatelessWidget {
),
);
}
String _getAppSelection() {
String name = locator<PatcherViewModel>().selectedApp!.name;
String version = locator<PatcherViewModel>().selectedApp!.version;
return '$name (v$version)';
}
}

View File

@@ -61,6 +61,13 @@ class _AboutWidgetState extends State<AboutWidget> {
fontWeight: FontWeight.w300,
),
),
Text(
'Build: ${snapshot.data!['flavor']}',
style: const TextStyle(
fontSize: 13,
fontWeight: FontWeight.w300,
),
),
Text(
'Model: ${snapshot.data!['model']}',
style: const TextStyle(

View File

@@ -6,7 +6,7 @@ import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
import 'package:expandable/expandable.dart';
import 'package:timeago/timeago.dart';
class ApplicationItem extends StatelessWidget {
class ApplicationItem extends StatefulWidget {
final Uint8List icon;
final String name;
final DateTime patchDate;
@@ -24,10 +24,39 @@ class ApplicationItem extends StatelessWidget {
required this.onPressed,
}) : super(key: key);
@override
State<ApplicationItem> createState() => _ApplicationItemState();
}
class _ApplicationItemState extends State<ApplicationItem>
with TickerProviderStateMixin {
late AnimationController _animationController;
@override
initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
ExpandableController expController = ExpandableController();
return ExpandablePanel(
controller: expController,
theme: const ExpandableThemeData(
inkWellBorderRadius: BorderRadius.all(Radius.circular(16)),
tapBodyToCollapse: false,
tapBodyToExpand: false,
tapHeaderToExpand: false,
hasIcon: false,
animationDuration: Duration(milliseconds: 450),
),
@@ -35,33 +64,54 @@ class ApplicationItem extends StatelessWidget {
child: Row(
children: <Widget>[
SizedBox(
width: 60,
child: Image.memory(icon, height: 39, width: 39),
width: 40,
child: Image.memory(widget.icon, height: 40, width: 40),
),
const SizedBox(width: 4),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
Padding(
padding: const EdgeInsets.only(left: 15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
widget.name.length > 9
? '${widget.name.substring(0, 9)}...'
: widget.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
),
),
),
Text(format(patchDate)),
],
Text(format(widget.patchDate)),
],
),
),
const Spacer(),
Padding(
padding: const EdgeInsets.only(right: 5.0),
child: RotationTransition(
turns:
Tween(begin: 0.0, end: 0.50).animate(_animationController),
child: IconButton(
onPressed: () {
expController.toggle();
_animationController.isCompleted
? _animationController.reverse()
: _animationController.forward();
},
icon: const Icon(Icons.arrow_drop_down),
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
CustomMaterialButton(
label: isUpdatableApp
label: widget.isUpdatableApp
? I18nText('applicationItem.patchButton')
: I18nText('applicationItem.infoButton'),
onPressed: onPressed,
onPressed: widget.onPressed,
),
],
),
@@ -70,7 +120,7 @@ class ApplicationItem extends StatelessWidget {
),
collapsed: const Text(''),
expanded: Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0),
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
@@ -82,7 +132,7 @@ class ApplicationItem extends StatelessWidget {
),
),
const SizedBox(height: 4),
Text('\u2022 ${changelog.join('\n\u2022 ')}'),
Text('\u2022 ${widget.changelog.join('\n\u2022 ')}'),
],
),
),

View File

@@ -16,7 +16,7 @@ class CustomCard extends StatelessWidget {
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: isFilled
? Theme.of(context).colorScheme.secondaryContainer
? Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.40)
: Colors.transparent,
border: isFilled
? null

View File

@@ -22,7 +22,7 @@ class DashboardChip extends StatelessWidget {
color: isSelected
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.secondary,
fontWeight: FontWeight.bold,
fontWeight: FontWeight.w500,
),
backgroundColor: Colors.transparent,
selectedColor: Theme.of(context).colorScheme.secondaryContainer,

View File

@@ -1,3 +1,4 @@
import 'package:flutter/foundation.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:device_info_plus/device_info_plus.dart';
@@ -7,6 +8,7 @@ class AboutInfo {
final info = await DeviceInfoPlugin().androidInfo;
return {
'version': packageInfo.version,
'flavor': kReleaseMode ? 'release' : 'debug',
'model': info.model,
'androidVersion': info.version.release,
'arch': info.supportedAbis.first

View File

@@ -4,7 +4,7 @@ homepage: https://github.com/revanced/revanced-manager
publish_to: 'none'
version: 0.0.4+4
version: 0.0.13+13
environment:
sdk: ">=2.17.5 <3.0.0"
@@ -24,7 +24,10 @@ dependencies:
dynamic_color: ^1.5.4
dynamic_themes: ^1.1.0
expandable: ^5.0.1
file_picker: ^5.0.1
file_picker:
git:
url: https://github.com/alexmercerind/flutter_file_picker
ref: master
flex_color_scheme: ^6.0.0
flutter:
sdk: flutter
@@ -49,7 +52,7 @@ dependencies:
ref: feature/nullSafe
package_info_plus: ^1.4.3+1
path_provider: ^2.0.11
permission_handler: ^10.0.0
#permission_handler: ^10.0.0
pull_to_refresh: ^2.0.0
root: ^2.0.2
share_extend: ^2.0.0
@@ -72,13 +75,6 @@ dev_dependencies:
injectable_generator: ^1.5.4
json_serializable: ^6.3.1
flutter_icons:
android: true
ios: false
image_path: "assets/images/ic_launcher_round.png"
adaptive_icon_background: "#1B1B1B"
adaptive_icon_foreground: "assets/images/ic_launcher.png"
flutter:
uses-material-design: true
assets: