fix: code refactoring (#5)

This commit is contained in:
Alberto Ponces 2022-08-18 15:33:33 +01:00 committed by GitHub
parent 6153e1f9e8
commit 389eae1447
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 318 additions and 361 deletions

View File

@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
compileSdkVersion 33
ndkVersion flutter.ndkVersion
compileOptions {
@ -45,7 +45,7 @@ android {
defaultConfig {
applicationId "app.revanced.manager"
minSdkVersion 21
targetSdkVersion flutter.targetSdkVersion
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

View File

@ -9,7 +9,9 @@
"updatesSubtitle": "Updates",
"patchedSubtitle": "Patched Applications",
"updatesAvailable": "Updates Available",
"installed": "Installed"
"installed": "Installed",
"notificationTitle": "ReVanced Manager was updated!",
"notificationText": "Tap to open the app"
},
"applicationItem": {
"patchButton": "Patch",
@ -50,7 +52,9 @@
"installerView": {
"widgetTitle": "Installer",
"fabInstallButton": "Install",
"fabOpenButton": "Open"
"fabOpenButton": "Open",
"notificationTitle": "ReVanced Manager is patching",
"notificationText": "Tap to return to the installer"
},
"settingsView": {
"widgetTitle": "Settings",

View File

@ -1,16 +1,11 @@
import 'package:revanced_manager/main.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/services/root_api.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_view.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/contributors/contributors_view.dart';
import 'package:revanced_manager/ui/views/home/home_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';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_view.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/root_checker/root_checker_view.dart';
import 'package:revanced_manager/ui/views/settings/settings_view.dart';
import 'package:stacked/stacked_annotations.dart';
@ -30,13 +25,8 @@ import 'package:stacked_themes/stacked_themes.dart';
dependencies: [
LazySingleton(classType: NavigationService),
LazySingleton(classType: PatcherAPI),
LazySingleton(classType: ManagerAPI),
LazySingleton(classType: RootAPI),
LazySingleton(classType: HomeViewModel),
LazySingleton(classType: PatcherViewModel),
LazySingleton(classType: AppSelectorViewModel),
LazySingleton(classType: PatchesSelectorViewModel),
LazySingleton(classType: InstallerViewModel),
LazySingleton(
classType: ThemeService,
resolveUsing: ThemeService.getInstance,

View File

@ -1,32 +1,36 @@
import 'dart:io';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:github/github.dart';
import 'package:injectable/injectable.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:timeago/timeago.dart';
@lazySingleton
class GithubAPI {
var github = GitHub();
final GitHub _github = GitHub();
Future<String?> latestRelease(String org, repoName) async {
Future<File?> latestRelease(String org, repoName) async {
try {
var latestRelease = await github.repositories.getLatestRelease(
var latestRelease = await _github.repositories.getLatestRelease(
RepositorySlug(org, repoName),
);
return latestRelease.assets
String? url = latestRelease.assets
?.firstWhere((asset) =>
asset.name != null &&
(asset.name!.endsWith('.dex') || asset.name!.endsWith('.apk')) &&
!asset.name!.contains('-sources') &&
!asset.name!.contains('-javadoc'))
.browserDownloadUrl;
if (url != null) {
return await DefaultCacheManager().getSingleFile(url);
}
} on Exception {
return '';
return null;
}
return null;
}
Future<String> latestCommitTime(String org, repoName) async {
try {
var repo = await github.repositories.getRepository(
var repo = await _github.repositories.getRepository(
RepositorySlug(org, repoName),
);
return repo.pushedAt != null
@ -39,7 +43,7 @@ class GithubAPI {
Future<List<Contributor>> getContributors(String org, repoName) async {
try {
var contributors = github.repositories.listContributors(
var contributors = _github.repositories.listContributors(
RepositorySlug(org, repoName),
);
return contributors.toList();

View File

@ -1,52 +1,19 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
import 'package:path_provider/path_provider.dart' as p;
import 'package:revanced_manager/constants.dart';
import 'package:revanced_manager/services/github_api.dart';
@lazySingleton
class ManagerAPI {
final Dio dio = Dio();
final GithubAPI githubAPI = GithubAPI();
final GithubAPI _githubAPI = GithubAPI();
Future<String?> getPath() async {
final path = await p.getApplicationSupportDirectory();
Directory('${path.path}/revanced').createSync();
final workDirPath = '${path.path}/revanced';
return workDirPath;
Future<File?> downloadPatches() async {
return await _githubAPI.latestRelease(ghOrg, patchesRepo);
}
Future<File?> downloadAssets(String repo) async {
try {
final workDir = await getPath();
final dlUrl = await githubAPI.latestRelease(ghOrg, repo);
final name = dlUrl
?.split('/')
.lastWhere((element) => element.contains('revanced'));
final assetFile = File('$workDir/$name');
final response = await dio.get(
dlUrl!,
options: Options(
responseType: ResponseType.bytes,
followRedirects: true,
receiveTimeout: 0,
),
);
final raf = assetFile.openSync(mode: FileMode.write);
raf.writeFromSync(response.data);
raf.closeSync();
return assetFile;
} catch (e) {
return null;
}
Future<File?> downloadIntegrations() async {
return await _githubAPI.latestRelease(ghOrg, integrationsRepo);
}
Future<void> downloadPatches() async {
await downloadAssets(patchesRepo);
}
Future<void> downloadIntegrations() async {
await downloadAssets(integrationsRepo);
Future<File?> downloadManager() async {
return await _githubAPI.latestRelease(ghOrg, managerRepo);
}
}

View File

@ -2,15 +2,12 @@ import 'dart:io';
import 'package:app_installer/app_installer.dart';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/services.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:injectable/injectable.dart';
import 'package:path_provider/path_provider.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/root_api.dart';
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
import 'package:revanced_manager/utils/string.dart';
import 'package:share_extend/share_extend.dart';
@ -19,11 +16,8 @@ class PatcherAPI {
static const patcherChannel = MethodChannel(
'app.revanced.manager/patcher',
);
static const installerChannel = MethodChannel(
'app.revanced.manager/installer',
);
final GithubAPI githubAPI = GithubAPI();
final RootAPI rootAPI = RootAPI();
final ManagerAPI _managerAPI = ManagerAPI();
final RootAPI _rootAPI = RootAPI();
Directory? _tmpDir;
Directory? _workDir;
Directory? _cacheDir;
@ -33,45 +27,18 @@ class PatcherAPI {
File? _patchedFile;
File? _outFile;
Future<dynamic> handlePlatformChannelMethods() async {
installerChannel.setMethodCallHandler((call) async {
switch (call.method) {
case 'updateProgress':
if (call.arguments != null) {
locator<InstallerViewModel>().updateProgress(call.arguments);
}
break;
case 'updateLog':
if (call.arguments != null) {
locator<InstallerViewModel>().updateLog(call.arguments);
}
break;
}
});
}
Future<bool?> loadPatches() async {
Future<void> loadPatches() async {
if (_patchBundleFile == null) {
String? dexFileUrl =
await githubAPI.latestRelease('revanced', 'revanced-patches');
if (dexFileUrl != null && dexFileUrl.isNotEmpty) {
try {
_patchBundleFile =
await DefaultCacheManager().getSingleFile(dexFileUrl);
return await patcherChannel.invokeMethod<bool>(
'loadPatches',
{
'pathBundlesPaths': <String>[_patchBundleFile!.absolute.path],
},
);
} on Exception {
_patchBundleFile = null;
return false;
}
_patchBundleFile = await _managerAPI.downloadPatches();
if (_patchBundleFile != null) {
await patcherChannel.invokeMethod<bool>(
'loadPatches',
{
'pathBundlesPaths': <String>[_patchBundleFile!.absolute.path],
},
);
}
return false;
}
return true;
}
Future<List<ApplicationWithIcon>> getFilteredInstalledApps() async {
@ -181,20 +148,9 @@ class PatcherAPI {
return appliedPatches;
}
Future<File?> downloadIntegrations() async {
String? apkFileUrl =
await githubAPI.latestRelease('revanced', 'revanced-integrations');
if (apkFileUrl != null && apkFileUrl.isNotEmpty) {
return await DefaultCacheManager().getSingleFile(apkFileUrl);
}
return null;
}
Future<void> initPatcher(bool mergeIntegrations) async {
if (mergeIntegrations) {
_integrations = await downloadIntegrations();
} else {
_integrations = File('');
_integrations = await _managerAPI.downloadIntegrations();
}
_tmpDir = await getTemporaryDirectory();
_workDir = _tmpDir!.createTempSync('tmp-');
@ -218,7 +174,7 @@ class PatcherAPI {
'inputFilePath': _inputFile!.path,
'patchedFilePath': _patchedFile!.path,
'outFilePath': _outFile!.path,
'integrationsPath': _integrations!.path,
'integrationsPath': _integrations != null ? _integrations!.path : '',
'selectedPatches': selectedPatches.map((p) => p.name).toList(),
'cacheDirPath': _cacheDir!.path,
'mergeIntegrations': mergeIntegrations,
@ -231,7 +187,7 @@ class PatcherAPI {
if (_outFile != null) {
try {
if (patchedApp.isRooted && !patchedApp.isFromStorage) {
return rootAPI.installApp(
return _rootAPI.installApp(
patchedApp.packageName,
patchedApp.apkFilePath,
_outFile!.path,
@ -268,14 +224,14 @@ class PatcherAPI {
Future<bool> checkOldPatch(PatchedApplication patchedApp) async {
if (patchedApp.isRooted) {
return await rootAPI.checkApp(patchedApp.packageName);
return await _rootAPI.checkApp(patchedApp.packageName);
}
return false;
}
Future<void> deleteOldPatch(PatchedApplication patchedApp) async {
if (patchedApp.isRooted) {
await rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath);
await _rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath);
}
}
}

View File

@ -1,16 +1,14 @@
import 'package:injectable/injectable.dart';
import 'package:root/root.dart';
@lazySingleton
class RootAPI {
final String managerDirPath = "/data/adb/revanced_manager";
final String postFsDataDirPath = "/data/adb/post-fs-data.d";
final String serviceDDirPath = "/data/adb/service.d";
final String _managerDirPath = "/data/adb/revanced_manager";
final String _postFsDataDirPath = "/data/adb/post-fs-data.d";
final String _serviceDDirPath = "/data/adb/service.d";
Future<bool> checkApp(String packageName) async {
try {
String? res = await Root.exec(
cmd: 'ls -la "$managerDirPath/$packageName"',
cmd: 'ls -la "$_managerDirPath/$packageName"',
);
return res != null && res.isNotEmpty;
} on Exception {
@ -26,13 +24,13 @@ class RootAPI {
cmd: 'su -mm -c "umount -l $originalFilePath"',
);
await Root.exec(
cmd: 'rm -rf "$managerDirPath/$packageName"',
cmd: 'rm -rf "$_managerDirPath/$packageName"',
);
await Root.exec(
cmd: 'rm -rf "$serviceDDirPath/$packageName.sh"',
cmd: 'rm -rf "$_serviceDDirPath/$packageName.sh"',
);
await Root.exec(
cmd: 'rm -rf "$postFsDataDirPath/$packageName.sh"',
cmd: 'rm -rf "$_postFsDataDirPath/$packageName.sh"',
);
}
@ -43,7 +41,7 @@ class RootAPI {
) async {
try {
await Root.exec(
cmd: 'mkdir -p "$managerDirPath/$packageName"',
cmd: 'mkdir -p "$_managerDirPath/$packageName"',
);
installServiceDScript(packageName);
installPostFsDataScript(packageName);
@ -58,10 +56,10 @@ class RootAPI {
Future<void> installServiceDScript(String packageName) async {
String content = '#!/system/bin/sh\n'
'while [ "\$(getprop sys.boot_completed | tr -d \'"\'"\'\\\\r\'"\'"\')" != "1" ]; do sleep 1; done\n'
'base_path=$managerDirPath/$packageName/base.apk\n'
'base_path=$_managerDirPath/$packageName/base.apk\n'
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
'[ ! -z \$stock_path ] && mount -o bind \$base_path \$stock_path';
String scriptFilePath = '$serviceDDirPath/$packageName.sh';
String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
await Root.exec(
cmd: 'echo \'$content\' > "$scriptFilePath"',
);
@ -74,7 +72,7 @@ class RootAPI {
String content = '#!/system/bin/sh\n'
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
'[ ! -z \$stock_path ] && umount -l \$stock_path';
String scriptFilePath = '$postFsDataDirPath/$packageName.sh';
String scriptFilePath = '$_postFsDataDirPath/$packageName.sh';
await Root.exec(
cmd: 'echo \'$content\' > "$scriptFilePath"',
);
@ -84,7 +82,7 @@ class RootAPI {
}
Future<void> installApk(String packageName, String patchedFilePath) async {
String newPatchedFilePath = '$managerDirPath/$packageName/base.apk';
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
await Root.exec(
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
);
@ -104,7 +102,7 @@ class RootAPI {
String originalFilePath,
String patchedFilePath,
) async {
String newPatchedFilePath = '$managerDirPath/$packageName/base.apk';
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
await Root.exec(
cmd: 'am force-stop "$packageName"',
);

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/theme.dart';
import 'package:revanced_manager/ui/widgets/installed_app_item.dart';
import 'package:revanced_manager/ui/widgets/search_bar.dart';
@ -15,14 +14,13 @@ class AppSelectorView extends StatefulWidget {
}
class _AppSelectorViewState extends State<AppSelectorView> {
String query = '';
String _query = '';
@override
Widget build(BuildContext context) {
return ViewModelBuilder<AppSelectorViewModel>.reactive(
disposeViewModel: false,
onModelReady: (model) => model.initialize(),
viewModelBuilder: () => locator<AppSelectorViewModel>(),
viewModelBuilder: () => AppSelectorViewModel(),
builder: (context, model, child) => Scaffold(
floatingActionButton: FloatingActionButton.extended(
onPressed: () {
@ -51,17 +49,17 @@ class _AppSelectorViewState extends State<AppSelectorView> {
hintTextColor: Theme.of(context).colorScheme.tertiary,
onQueryChanged: (searchQuery) {
setState(() {
query = searchQuery;
_query = searchQuery;
});
},
),
const SizedBox(height: 12),
query.isEmpty || query.length < 2
_query.isEmpty || _query.length < 2
? _getAllResults(model)
: _getFilteredResults(model)
],
)
: query.isEmpty || query.length < 2
: _query.isEmpty || _query.length < 2
? Center(
child: CircularProgressIndicator(
color: Theme.of(context).colorScheme.secondary,
@ -105,7 +103,7 @@ class _AppSelectorViewState extends State<AppSelectorView> {
itemBuilder: (context, index) {
model.apps.sort((a, b) => a.appName.compareTo(b.appName));
if (model.apps[index].appName.toLowerCase().contains(
query.toLowerCase(),
_query.toLowerCase(),
)) {
return InkWell(
onTap: () {

View File

@ -8,30 +8,22 @@ import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked/stacked.dart';
class AppSelectorViewModel extends BaseViewModel {
final PatcherAPI patcherAPI = locator<PatcherAPI>();
bool isRooted = false;
bool isFromStorage = false;
List<ApplicationWithIcon> apps = [];
PatchedApplication? selectedApp;
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final List<ApplicationWithIcon> apps = [];
bool _isRooted = false;
Future<void> initialize() async {
await getApps();
apps.addAll(await _patcherAPI.getFilteredInstalledApps());
SharedPreferences prefs = await SharedPreferences.getInstance();
isRooted = prefs.getBool('isRooted') ?? false;
_isRooted = prefs.getBool('isRooted') ?? false;
notifyListeners();
}
Future<void> getApps() async {
apps = await patcherAPI.getFilteredInstalledApps();
}
void selectApp(ApplicationWithIcon application) async {
isFromStorage = false;
PatchedApplication app = PatchedApplication(
name: application.appName,
packageName: application.packageName,
@ -39,18 +31,16 @@ class AppSelectorViewModel extends BaseViewModel {
apkFilePath: application.apkFilePath,
icon: application.icon,
patchDate: DateTime.now(),
isRooted: isRooted,
isFromStorage: isFromStorage,
isRooted: _isRooted,
isFromStorage: false,
appliedPatches: [],
);
locator<AppSelectorViewModel>().selectedApp = app;
locator<PatchesSelectorViewModel>().selectedPatches.clear();
locator<PatchesSelectorViewModel>().notifyListeners();
locator<PatcherViewModel>().selectedApp = app;
locator<PatcherViewModel>().selectedPatches.clear();
locator<PatcherViewModel>().notifyListeners();
}
Future<void> selectAppFromStorage(BuildContext context) async {
isFromStorage = true;
try {
FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.custom,
@ -69,13 +59,12 @@ class AppSelectorViewModel extends BaseViewModel {
apkFilePath: result.files.single.path!,
icon: application.icon,
patchDate: DateTime.now(),
isRooted: isRooted,
isFromStorage: isFromStorage,
isRooted: _isRooted,
isFromStorage: true,
appliedPatches: [],
);
locator<AppSelectorViewModel>().selectedApp = app;
locator<PatchesSelectorViewModel>().selectedPatches.clear();
locator<PatchesSelectorViewModel>().notifyListeners();
locator<PatcherViewModel>().selectedApp = app;
locator<PatcherViewModel>().selectedPatches.clear();
locator<PatcherViewModel>().notifyListeners();
}
}

View File

@ -9,7 +9,6 @@ class ContributorsView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelBuilder<ContributorsViewModel>.reactive(
disposeViewModel: false,
viewModelBuilder: () => ContributorsViewModel(),
onModelReady: (model) => model.getContributors(),
builder: (context, model, child) => Scaffold(

View File

@ -1,27 +1,36 @@
import 'package:github/github.dart';
import 'package:revanced_manager/constants.dart';
import 'package:revanced_manager/services/github_api.dart';
import 'package:stacked/stacked.dart';
class ContributorsViewModel extends BaseViewModel {
final GithubAPI githubAPI = GithubAPI();
final GithubAPI _githubAPI = GithubAPI();
List<Contributor> patchesContributors = [];
List<Contributor> integrationsContributors = [];
List<Contributor> patcherContributors = [];
List<Contributor> cliContributors = [];
List<Contributor> managerContributors = [];
Future<List<Contributor>> getContributors() async {
patchesContributors =
await githubAPI.getContributors('revanced', 'revanced-patches');
integrationsContributors =
await githubAPI.getContributors('revanced', 'revanced-integrations');
patcherContributors =
await githubAPI.getContributors('revanced', 'revanced-patcher');
cliContributors =
await githubAPI.getContributors('revanced', 'revanced-cli');
managerContributors =
await githubAPI.getContributors('revanced', 'revanced-manager');
return [];
Future<void> getContributors() async {
patchesContributors = await _githubAPI.getContributors(
ghOrg,
patchesRepo,
);
integrationsContributors = await _githubAPI.getContributors(
ghOrg,
integrationsRepo,
);
patcherContributors = await _githubAPI.getContributors(
ghOrg,
patcherRepo,
);
cliContributors = await _githubAPI.getContributors(
ghOrg,
cliRepo,
);
managerContributors = await _githubAPI.getContributors(
ghOrg,
managerRepo,
);
}
}

View File

@ -55,6 +55,7 @@ class HomeView extends StatelessWidget {
),
const SizedBox(height: 10),
LatestCommitCard(
onPressed: () => model.updateManager(context),
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 23),
@ -77,7 +78,7 @@ class HomeView extends StatelessWidget {
label: "homeView.updatesAvailable",
isSelected: model.showUpdatableApps,
onSelected: (value) {
model.toggleUpdatableApps(value);
model.toggleUpdatableApps(true);
},
),
const SizedBox(width: 10),

View File

@ -1,26 +1,41 @@
import 'dart:convert';
import 'dart:io';
import 'package:app_installer/app_installer.dart';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:injectable/injectable.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/constants.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/services/manager_api.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
@lazySingleton
class HomeViewModel extends BaseViewModel {
final _navigationService = locator<NavigationService>();
final GithubAPI githubAPI = GithubAPI();
final PatcherAPI patcherAPI = locator<PatcherAPI>();
final NavigationService _navigationService = locator<NavigationService>();
final ManagerAPI _managerAPI = ManagerAPI();
final GithubAPI _githubAPI = GithubAPI();
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
bool showUpdatableApps = true;
Future<void> initialize() async {
await patcherAPI.loadPatches();
await _patcherAPI.loadPatches();
await flutterLocalNotificationsPlugin.initialize(
const InitializationSettings(
android: AndroidInitializationSettings('ic_notification'),
),
onSelectNotification: (p) => DeviceApps.openApp('app.revanced.manager'),
);
}
void toggleUpdatableApps(bool value) {
@ -28,13 +43,10 @@ class HomeViewModel extends BaseViewModel {
notifyListeners();
}
Future downloadPatches() => locator<ManagerAPI>().downloadPatches();
Future downloadIntegrations() => locator<ManagerAPI>().downloadIntegrations();
void navigateToInstaller(PatchedApplication app) async {
locator<AppSelectorViewModel>().selectedApp = app;
locator<PatchesSelectorViewModel>().selectedPatches =
await patcherAPI.getAppliedPatches(app);
locator<PatcherViewModel>().selectedApp = app;
locator<PatcherViewModel>().selectedPatches =
await _patcherAPI.getAppliedPatches(app);
_navigationService.navigateTo(Routes.installerView);
}
@ -44,15 +56,37 @@ class HomeViewModel extends BaseViewModel {
List<String> patchedApps = prefs.getStringList('patchedApps') ?? [];
for (String str in patchedApps) {
PatchedApplication app = PatchedApplication.fromJson(json.decode(str));
bool hasUpdates = await githubAPI.hasUpdates(
app,
'revanced',
'revanced-patches',
);
bool hasUpdates = await _githubAPI.hasUpdates(app, ghOrg, patchesRepo);
if (hasUpdates == isUpdatable) {
list.add(app);
}
}
return list;
}
void updateManager(BuildContext context) async {
File? managerApk = await _managerAPI.downloadManager();
if (managerApk != null) {
flutterLocalNotificationsPlugin.show(
0,
// ignore: use_build_context_synchronously
FlutterI18n.translate(
context,
'homeView.notificationTitle',
),
// ignore: use_build_context_synchronously
FlutterI18n.translate(
context,
'homeView.notificationText',
),
const NotificationDetails(
android: AndroidNotificationDetails(
'revanced_manager_channel',
'ReVanced Manager Channel',
),
),
);
AppInstaller.installApk(managerApk.path);
}
}
}

View File

@ -1,7 +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';
@ -15,9 +14,8 @@ class InstallerView extends StatelessWidget {
(_) => _controller.jumpTo(_controller.position.maxScrollExtent),
);
return ViewModelBuilder<InstallerViewModel>.reactive(
disposeViewModel: false,
onModelReady: (model) => model.initialize(),
viewModelBuilder: () => locator<InstallerViewModel>(),
onModelReady: (model) => model.initialize(context),
viewModelBuilder: () => InstallerViewModel(),
builder: (context, model, child) => WillPopScope(
child: Scaffold(
floatingActionButton: Visibility(

View File

@ -1,30 +1,43 @@
import 'dart:convert';
import 'package:device_apps/device_apps.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter_background/flutter_background.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
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 {
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final PatchedApplication? _app = locator<PatcherViewModel>().selectedApp;
final List<Patch> _patches = locator<PatcherViewModel>().selectedPatches;
static const _installerChannel = MethodChannel(
'app.revanced.manager/installer',
);
double? progress = 0.0;
String logs = '';
bool isPatching = false;
bool isInstalled = false;
Future<void> initialize() async {
Future<void> initialize(BuildContext context) async {
try {
await FlutterBackground.initialize(
androidConfig: const FlutterBackgroundAndroidConfig(
notificationTitle: 'Patching',
notificationText: 'ReVanced Manager is patching',
androidConfig: FlutterBackgroundAndroidConfig(
notificationTitle: FlutterI18n.translate(
context,
'installerView.notificationTitle',
),
notificationText: FlutterI18n.translate(
context,
'installerView.notificationText',
),
notificationImportance: AndroidNotificationImportance.Default,
notificationIcon: AndroidResource(
notificationIcon: const AndroidResource(
name: 'ic_notification',
defType: 'drawable',
),
@ -32,11 +45,28 @@ class InstallerViewModel extends BaseViewModel {
);
await FlutterBackground.enableBackgroundExecution();
} finally {
await locator<PatcherAPI>().handlePlatformChannelMethods();
await handlePlatformChannelMethods();
await runPatcher();
}
}
Future<dynamic> handlePlatformChannelMethods() async {
_installerChannel.setMethodCallHandler((call) async {
switch (call.method) {
case 'updateProgress':
if (call.arguments != null) {
updateProgress(call.arguments);
}
break;
case 'updateLog':
if (call.arguments != null) {
updateLog(call.arguments);
}
break;
}
});
}
void updateProgress(double value) {
progress = value;
isInstalled = false;
@ -59,37 +89,32 @@ class InstallerViewModel extends BaseViewModel {
Future<void> runPatcher() async {
updateProgress(0.0);
PatchedApplication? selectedApp =
locator<AppSelectorViewModel>().selectedApp;
List<Patch> selectedPatches =
locator<PatchesSelectorViewModel>().selectedPatches;
if (selectedApp != null && selectedPatches.isNotEmpty) {
String apkFilePath = selectedApp.apkFilePath;
if (_app != null && _patches.isNotEmpty) {
String apkFilePath = _app!.apkFilePath;
try {
updateLog('Initializing installer');
if (selectedApp.isRooted && !selectedApp.isFromStorage) {
if (_app!.isRooted && !_app!.isFromStorage) {
updateLog('Checking if an old patched version exists');
bool oldExists =
await locator<PatcherAPI>().checkOldPatch(selectedApp);
bool oldExists = await _patcherAPI.checkOldPatch(_app!);
if (oldExists) {
updateLog('Deleting old patched version');
await locator<PatcherAPI>().deleteOldPatch(selectedApp);
await _patcherAPI.deleteOldPatch(_app!);
}
}
updateLog('Creating working directory');
bool mergeIntegrations = false;
bool resourcePatching = false;
if (selectedApp.packageName == 'com.google.android.youtube') {
if (_app!.packageName == 'com.google.android.youtube') {
mergeIntegrations = true;
resourcePatching = true;
} else if (selectedApp.packageName ==
} else if (_app!.packageName ==
'com.google.android.apps.youtube.music') {
resourcePatching = true;
}
await locator<PatcherAPI>().initPatcher(mergeIntegrations);
await locator<PatcherAPI>().runPatcher(
await _patcherAPI.initPatcher(mergeIntegrations);
await _patcherAPI.runPatcher(
apkFilePath,
selectedPatches,
_patches,
mergeIntegrations,
resourcePatching,
);
@ -107,21 +132,16 @@ class InstallerViewModel extends BaseViewModel {
}
void installResult() async {
PatchedApplication? selectedApp =
locator<AppSelectorViewModel>().selectedApp;
List<Patch> selectedPatches =
locator<PatchesSelectorViewModel>().selectedPatches;
if (selectedApp != null) {
updateLog(selectedApp.isRooted
if (_app != null) {
updateLog(_app!.isRooted
? 'Installing patched file using root method'
: 'Installing patched file using nonroot method');
isInstalled = await locator<PatcherAPI>().installPatchedFile(selectedApp);
isInstalled = await _patcherAPI.installPatchedFile(_app!);
if (isInstalled) {
updateLog('Done');
selectedApp.patchDate = DateTime.now();
selectedApp.appliedPatches
.addAll(selectedPatches.map((p) => p.name).toList());
await saveApp(selectedApp);
_app!.patchDate = DateTime.now();
_app!.appliedPatches.addAll(_patches.map((p) => p.name).toList());
await saveApp();
} else {
updateLog('An error occurred! Aborting');
}
@ -129,39 +149,33 @@ class InstallerViewModel extends BaseViewModel {
}
void shareResult() {
PatchedApplication? selectedApp =
locator<AppSelectorViewModel>().selectedApp;
if (selectedApp != null) {
locator<PatcherAPI>().sharePatchedFile(
selectedApp.name,
selectedApp.version,
);
if (_app != null) {
_patcherAPI.sharePatchedFile(_app!.name, _app!.version);
}
}
Future<void> cleanWorkplace() async {
locator<PatcherAPI>().cleanPatcher();
locator<AppSelectorViewModel>().selectedApp = null;
locator<PatchesSelectorViewModel>().selectedPatches.clear();
locator<PatchesSelectorViewModel>().notifyListeners();
_patcherAPI.cleanPatcher();
locator<PatcherViewModel>().selectedApp = null;
locator<PatcherViewModel>().selectedPatches.clear();
locator<PatcherViewModel>().notifyListeners();
}
void openApp() {
PatchedApplication? selectedApp =
locator<AppSelectorViewModel>().selectedApp;
if (selectedApp != null) {
DeviceApps.openApp(selectedApp.packageName);
if (_app != null) {
DeviceApps.openApp(_app!.packageName);
}
}
Future<void> saveApp(PatchedApplication selectedApp) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> patchedApps = prefs.getStringList('patchedApps') ?? [];
String app = json.encode(selectedApp.toJson());
patchedApps.removeWhere(
(a) => json.decode(a)['packageName'] == selectedApp.packageName);
patchedApps.add(app);
prefs.setStringList('patchedApps', patchedApps);
Future<void> saveApp() async {
if (_app != null) {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> patchedApps = prefs.getStringList('patchedApps') ?? [];
String appStr = json.encode(_app!.toJson());
patchedApps.removeWhere(
(a) => json.decode(a)['packageName'] == _app!.packageName);
patchedApps.add(appStr);
prefs.setStringList('patchedApps', patchedApps);
}
}
}

View File

@ -1,12 +1,17 @@
import 'package:injectable/injectable.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/app/app.router.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
@lazySingleton
class PatcherViewModel extends BaseViewModel {
final _navigationService = locator<NavigationService>();
PatchedApplication? selectedApp;
List<Patch> selectedPatches = [];
final NavigationService _navigationService = locator<NavigationService>();
void navigateToAppSelector() {
_navigationService.navigateTo(Routes.appSelectorView);
@ -21,10 +26,10 @@ class PatcherViewModel extends BaseViewModel {
}
bool showFabButton() {
return locator<PatchesSelectorViewModel>().selectedPatches.isNotEmpty;
return selectedPatches.isNotEmpty;
}
bool dimPatchesCard() {
return locator<AppSelectorViewModel>().selectedApp == null;
return selectedApp == null;
}
}

View File

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/theme.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/patch_item.dart';
@ -15,16 +14,14 @@ class PatchesSelectorView extends StatefulWidget {
}
class _PatchesSelectorViewState extends State<PatchesSelectorView> {
final List<PatchItem> patches = [];
final List<PatchItem> _items = [];
String query = '';
@override
Widget build(BuildContext context) {
return ViewModelBuilder<PatchesSelectorViewModel>.reactive(
disposeViewModel: false,
fireOnModelReadyOnce: true,
onModelReady: (model) => model.initialize(),
viewModelBuilder: () => locator<PatchesSelectorViewModel>(),
viewModelBuilder: () => PatchesSelectorViewModel(),
builder: (context, model, child) => Scaffold(
body: SafeArea(
child: Padding(
@ -62,7 +59,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
onPressed: () => Navigator.of(context).pop(),
onPressed: () {
model.selectPatches();
Navigator.of(context).pop();
},
child: I18nText('patchesSelectorView.fabButton'),
),
],
@ -79,7 +79,7 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
}
Widget _getAllResults(PatchesSelectorViewModel model) {
patches.clear();
_items.clear();
return Expanded(
child: ListView.builder(
itemCount: model.patches.length,
@ -93,8 +93,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
isSelected: model.selectedPatches.any(
(element) => element.name == model.patches[index].name,
),
onChanged: (value) =>
model.selectPatch(model.patches[index].name, value),
);
patches.add(item);
_items.add(item);
return item;
},
),
@ -102,7 +104,7 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
}
Widget _getFilteredResults(PatchesSelectorViewModel model) {
patches.clear();
_items.clear();
return Expanded(
child: ListView.builder(
itemCount: model.patches.length,
@ -119,8 +121,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
isSelected: model.selectedPatches.any(
(element) => element.name == model.patches[index].name,
),
onChanged: (value) =>
model.selectPatch(model.patches[index].name, value),
);
patches.add(item);
_items.add(item);
return item;
} else {
return const SizedBox();

View File

@ -1,38 +1,34 @@
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
import 'package:revanced_manager/ui/widgets/patch_item.dart';
import 'package:stacked/stacked.dart';
class PatchesSelectorViewModel extends BaseViewModel {
final PatcherAPI patcherAPI = locator<PatcherAPI>();
List<Patch> patches = [];
List<Patch> selectedPatches = [];
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
final List<Patch> patches = [];
final List<Patch> selectedPatches =
locator<PatcherViewModel>().selectedPatches;
Future<void> initialize() async {
await getPatches();
patches.addAll(await _patcherAPI.getFilteredPatches(
locator<PatcherViewModel>().selectedApp,
));
notifyListeners();
}
Future<void> getPatches() async {
PatchedApplication? app = locator<AppSelectorViewModel>().selectedApp;
patches = await patcherAPI.getFilteredPatches(app);
void selectPatch(String name, bool isSelected) {
Patch patch = patches.firstWhere((p) => p.name == name);
if (isSelected && !selectedPatches.contains(patch)) {
selectedPatches.add(patch);
} else {
selectedPatches.remove(patch);
}
notifyListeners();
}
void selectPatch(PatchItem item) {
Patch patch = locator<PatchesSelectorViewModel>()
.patches
.firstWhere((p) => p.name == item.name);
if (item.isSelected &&
!locator<PatchesSelectorViewModel>().selectedPatches.contains(patch)) {
locator<PatchesSelectorViewModel>().selectedPatches.add(patch);
} else {
locator<PatchesSelectorViewModel>().selectedPatches.remove(patch);
}
locator<PatchesSelectorViewModel>().notifyListeners();
void selectPatches() {
locator<PatcherViewModel>().selectedPatches = selectedPatches;
locator<PatcherViewModel>().notifyListeners();
}
}

View File

@ -11,7 +11,6 @@ class RootCheckerView extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ViewModelBuilder<RootCheckerViewModel>.reactive(
disposeViewModel: false,
viewModelBuilder: () => RootCheckerViewModel(),
builder: (context, model, child) => Scaffold(
floatingActionButton: Column(

View File

@ -6,12 +6,13 @@ import 'package:root/root.dart';
import 'package:stacked_services/stacked_services.dart';
class RootCheckerViewModel extends BaseViewModel {
final _navigationService = locator<NavigationService>();
bool? isRooted = false;
final NavigationService _navigationService = locator<NavigationService>();
bool isRooted = false;
Future<void> checkRoot() async {
isRooted = await Root.isRooted();
if (isRooted == true) {
bool? res = await Root.isRooted();
isRooted = res != null && res == true;
if (isRooted) {
navigateToHome();
}
notifyListeners();
@ -19,7 +20,7 @@ class RootCheckerViewModel extends BaseViewModel {
Future<void> navigateToHome() async {
final prefs = await SharedPreferences.getInstance();
prefs.setBool('isRooted', isRooted!);
prefs.setBool('isRooted', isRooted);
_navigationService.navigateTo(Routes.navigation);
notifyListeners();
}

View File

@ -4,7 +4,7 @@ import 'package:stacked/stacked.dart';
import 'package:stacked_services/stacked_services.dart';
class SettingsViewModel extends BaseViewModel {
final _navigationService = locator<NavigationService>();
final NavigationService _navigationService = locator<NavigationService>();
void setLanguage(String language) {
notifyListeners();

View File

@ -1,25 +1,21 @@
import 'dart:typed_data';
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/constants.dart';
import 'package:revanced_manager/services/patcher_api.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
class AppSelectorCard extends StatelessWidget {
final Function()? onPressed;
final Function() onPressed;
final Color? color;
AppSelectorCard({
const AppSelectorCard({
Key? key,
this.onPressed,
required this.onPressed,
this.color = const Color(0xff1B222B),
}) : super(key: key);
final PatcherAPI patcherAPI = locator<PatcherAPI>();
@override
Widget build(BuildContext context) {
return GestureDetector(
@ -36,7 +32,7 @@ class AppSelectorCard extends StatelessWidget {
children: [
const SizedBox(height: 10),
I18nText(
locator<AppSelectorViewModel>().selectedApp == null
locator<PatcherViewModel>().selectedApp == null
? 'appSelectorCard.widgetTitle'
: 'appSelectorCard.widgetTitleSelected',
child: Text(
@ -48,7 +44,7 @@ class AppSelectorCard extends StatelessWidget {
),
),
const SizedBox(height: 10),
locator<AppSelectorViewModel>().selectedApp == null
locator<PatcherViewModel>().selectedApp == null
? I18nText(
'appSelectorCard.widgetSubtitle',
child: Text(
@ -62,11 +58,9 @@ class AppSelectorCard extends StatelessWidget {
height: 16.0,
child: ClipOval(
child: Image.memory(
locator<AppSelectorViewModel>().selectedApp == null
locator<PatcherViewModel>().selectedApp == null
? Uint8List(0)
: locator<AppSelectorViewModel>()
.selectedApp!
.icon,
: locator<PatcherViewModel>().selectedApp!.icon,
fit: BoxFit.cover,
),
),
@ -85,8 +79,8 @@ class AppSelectorCard extends StatelessWidget {
}
String _getAppSelection() {
String name = locator<AppSelectorViewModel>().selectedApp!.name;
String version = locator<AppSelectorViewModel>().selectedApp!.version;
String name = locator<PatcherViewModel>().selectedApp!.name;
String version = locator<PatcherViewModel>().selectedApp!.version;
return '$name (v$version)';
}
}

View File

@ -14,7 +14,7 @@ class ApplicationItem extends StatelessWidget {
final DateTime patchDate;
final String changelog;
final bool isUpdatableApp;
final Function()? onPressed;
final Function() onPressed;
const ApplicationItem({
Key? key,

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/constants.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
@ -10,7 +11,7 @@ class AvailableUpdatesCard extends StatelessWidget {
Key? key,
}) : super(key: key);
final GithubAPI githubAPI = GithubAPI();
final GithubAPI _githubAPI = GithubAPI();
@override
Widget build(BuildContext context) {
@ -27,10 +28,10 @@ class AvailableUpdatesCard extends StatelessWidget {
physics: const NeverScrollableScrollPhysics(),
itemCount: snapshot.data!.length,
itemBuilder: (context, index) => FutureBuilder<String>(
future: githubAPI.getChangelog(
future: _githubAPI.getChangelog(
snapshot.data![index],
'revanced',
'revanced-patches',
ghOrg,
patchesRepo,
),
initialData: '',
builder: (context, snapshot2) => ApplicationItem(

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/constants.dart';
import 'package:revanced_manager/models/patched_application.dart';
import 'package:revanced_manager/services/github_api.dart';
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
@ -10,7 +11,7 @@ class InstalledAppsCard extends StatelessWidget {
Key? key,
}) : super(key: key);
final GithubAPI githubAPI = GithubAPI();
final GithubAPI _githubAPI = GithubAPI();
@override
Widget build(BuildContext context) {
@ -27,10 +28,10 @@ class InstalledAppsCard extends StatelessWidget {
physics: const NeverScrollableScrollPhysics(),
itemCount: snapshot.data!.length,
itemBuilder: (context, index) => FutureBuilder<String>(
future: githubAPI.getChangelog(
future: _githubAPI.getChangelog(
snapshot.data![index],
'revanced',
'revanced-patches',
ghOrg,
patchesRepo,
),
initialData: '',
builder: (context, snapshot2) => ApplicationItem(

View File

@ -6,9 +6,11 @@ import 'package:revanced_manager/constants.dart';
import 'package:revanced_manager/ui/widgets/patch_text_button.dart';
class LatestCommitCard extends StatefulWidget {
final Function() onPressed;
final Color? color;
const LatestCommitCard({
Key? key,
required this.onPressed,
this.color = const Color(0xff1B222B),
}) : super(key: key);
@ -17,7 +19,7 @@ class LatestCommitCard extends StatefulWidget {
}
class _LatestCommitCardState extends State<LatestCommitCard> {
final GithubAPI githubAPI = GithubAPI();
final GithubAPI _githubAPI = GithubAPI();
@override
Widget build(BuildContext context) {
@ -45,10 +47,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
),
),
FutureBuilder<String>(
future: githubAPI.latestCommitTime(
'revanced',
'revanced-patcher',
),
future: _githubAPI.latestCommitTime(ghOrg, patcherRepo),
builder: (context, snapshot) => Text(
snapshot.hasData && snapshot.data!.isNotEmpty
? FlutterI18n.translate(
@ -78,10 +77,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
),
),
FutureBuilder<String>(
future: githubAPI.latestCommitTime(
'revanced',
'revanced-manager',
),
future: _githubAPI.latestCommitTime(ghOrg, managerRepo),
builder: (context, snapshot) => Text(
snapshot.hasData && snapshot.data!.isNotEmpty
? FlutterI18n.translate(
@ -105,7 +101,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
context,
'latestCommitCard.updateButton',
),
onPressed: () => {},
onPressed: widget.onPressed,
backgroundColor: Theme.of(context).colorScheme.secondary,
borderColor: Theme.of(context).colorScheme.secondary,
),

View File

@ -5,10 +5,10 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:revanced_manager/theme.dart';
class MagiskButton extends StatelessWidget {
final Function()? onPressed;
final Function() onPressed;
const MagiskButton({
Key? key,
this.onPressed,
required this.onPressed,
}) : super(key: key);
@override

View File

@ -1,7 +1,5 @@
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
// ignore: must_be_immutable
class PatchItem extends StatefulWidget {
@ -10,6 +8,7 @@ class PatchItem extends StatefulWidget {
final String description;
final String version;
bool isSelected;
final Function(bool) onChanged;
PatchItem({
Key? key,
@ -18,6 +17,7 @@ class PatchItem extends StatefulWidget {
required this.description,
required this.version,
required this.isSelected,
required this.onChanged,
}) : super(key: key);
@override
@ -30,7 +30,7 @@ class _PatchItemState extends State<PatchItem> {
return InkWell(
onTap: () {
setState(() => widget.isSelected = !widget.isSelected);
locator<PatchesSelectorViewModel>().selectPatch(widget);
widget.onChanged(widget.isSelected);
},
child: Container(
decoration: BoxDecoration(
@ -82,7 +82,7 @@ class _PatchItemState extends State<PatchItem> {
activeColor: Colors.blueGrey[500],
onChanged: (newValue) {
setState(() => widget.isSelected = newValue!);
locator<PatchesSelectorViewModel>().selectPatch(widget);
widget.onChanged(widget.isSelected);
},
),
)

View File

@ -4,16 +4,15 @@ import 'package:google_fonts/google_fonts.dart';
import 'package:revanced_manager/app/app.locator.dart';
import 'package:revanced_manager/constants.dart';
import 'package:revanced_manager/models/patch.dart';
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
class PatchSelectorCard extends StatelessWidget {
final Function()? onPressed;
final Function() onPressed;
final Color? color;
const PatchSelectorCard({
Key? key,
this.onPressed,
required this.onPressed,
this.color = const Color(0xff1B222B),
}) : super(key: key);
@ -32,7 +31,7 @@ class PatchSelectorCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
I18nText(
locator<PatchesSelectorViewModel>().selectedPatches.isEmpty
locator<PatcherViewModel>().selectedPatches.isEmpty
? 'patchSelectorCard.widgetTitle'
: 'patchSelectorCard.widgetTitleSelected',
child: Text(
@ -44,7 +43,7 @@ class PatchSelectorCard extends StatelessWidget {
),
),
const SizedBox(height: 10),
locator<AppSelectorViewModel>().selectedApp == null
locator<PatcherViewModel>().selectedApp == null
? I18nText(
'patchSelectorCard.widgetSubtitle',
child: Text(
@ -52,7 +51,7 @@ class PatchSelectorCard extends StatelessWidget {
style: robotoTextStyle,
),
)
: locator<PatchesSelectorViewModel>().selectedPatches.isEmpty
: locator<PatcherViewModel>().selectedPatches.isEmpty
? I18nText(
'patchSelectorCard.widgetEmptySubtitle',
child: Text(
@ -72,7 +71,7 @@ class PatchSelectorCard extends StatelessWidget {
String _getPatchesSelection() {
String text = '';
for (Patch p in locator<PatchesSelectorViewModel>().selectedPatches) {
for (Patch p in locator<PatcherViewModel>().selectedPatches) {
text += '${p.simpleName} (v${p.version})\n';
}
return text.substring(0, text.length - 1);

View File

@ -5,13 +5,13 @@ import 'package:revanced_manager/theme.dart';
class PatchTextButton extends StatelessWidget {
final String text;
final Function()? onPressed;
final Function() onPressed;
final Color borderColor;
final Color backgroundColor;
const PatchTextButton({
Key? key,
required this.text,
this.onPressed,
required this.onPressed,
this.borderColor = const Color(0xff7792BA),
this.backgroundColor = Colors.transparent,
}) : super(key: key);

View File

@ -4,7 +4,7 @@ homepage: https://github.com/Aunali321/revanced-manager
publish_to: 'none'
version: 1.0.0+1
version: 0.0.1+1
environment:
sdk: ">=2.17.5 <3.0.0"
@ -16,7 +16,6 @@ dependencies:
git:
url: https://github.com/ponces/flutter_plugin_device_apps
ref: appinfo-from-storage
dio: ^4.0.6
expandable: ^5.0.1
file_picker: ^5.0.1
flutter:
@ -24,6 +23,7 @@ dependencies:
flutter_background: ^1.1.0
flutter_cache_manager: ^3.3.0
flutter_i18n: ^0.32.4
flutter_local_notifications: ^9.8.0+1
flutter_svg: ^1.1.1+1
fluttertoast: ^8.0.9
get_it: ^7.2.0