mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
fix: code refactoring (#5)
This commit is contained in:
parent
6153e1f9e8
commit
389eae1447
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion flutter.compileSdkVersion
|
compileSdkVersion 33
|
||||||
ndkVersion flutter.ndkVersion
|
ndkVersion flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
@ -45,7 +45,7 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "app.revanced.manager"
|
applicationId "app.revanced.manager"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion flutter.targetSdkVersion
|
targetSdkVersion 33
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
"updatesSubtitle": "Updates",
|
"updatesSubtitle": "Updates",
|
||||||
"patchedSubtitle": "Patched Applications",
|
"patchedSubtitle": "Patched Applications",
|
||||||
"updatesAvailable": "Updates Available",
|
"updatesAvailable": "Updates Available",
|
||||||
"installed": "Installed"
|
"installed": "Installed",
|
||||||
|
"notificationTitle": "ReVanced Manager was updated!",
|
||||||
|
"notificationText": "Tap to open the app"
|
||||||
},
|
},
|
||||||
"applicationItem": {
|
"applicationItem": {
|
||||||
"patchButton": "Patch",
|
"patchButton": "Patch",
|
||||||
@ -50,7 +52,9 @@
|
|||||||
"installerView": {
|
"installerView": {
|
||||||
"widgetTitle": "Installer",
|
"widgetTitle": "Installer",
|
||||||
"fabInstallButton": "Install",
|
"fabInstallButton": "Install",
|
||||||
"fabOpenButton": "Open"
|
"fabOpenButton": "Open",
|
||||||
|
"notificationTitle": "ReVanced Manager is patching",
|
||||||
|
"notificationText": "Tap to return to the installer"
|
||||||
},
|
},
|
||||||
"settingsView": {
|
"settingsView": {
|
||||||
"widgetTitle": "Settings",
|
"widgetTitle": "Settings",
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import 'package:revanced_manager/main.dart';
|
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/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_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/contributors/contributors_view.dart';
|
||||||
import 'package:revanced_manager/ui/views/home/home_viewmodel.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_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/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_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/root_checker/root_checker_view.dart';
|
||||||
import 'package:revanced_manager/ui/views/settings/settings_view.dart';
|
import 'package:revanced_manager/ui/views/settings/settings_view.dart';
|
||||||
import 'package:stacked/stacked_annotations.dart';
|
import 'package:stacked/stacked_annotations.dart';
|
||||||
@ -30,13 +25,8 @@ import 'package:stacked_themes/stacked_themes.dart';
|
|||||||
dependencies: [
|
dependencies: [
|
||||||
LazySingleton(classType: NavigationService),
|
LazySingleton(classType: NavigationService),
|
||||||
LazySingleton(classType: PatcherAPI),
|
LazySingleton(classType: PatcherAPI),
|
||||||
LazySingleton(classType: ManagerAPI),
|
|
||||||
LazySingleton(classType: RootAPI),
|
|
||||||
LazySingleton(classType: HomeViewModel),
|
LazySingleton(classType: HomeViewModel),
|
||||||
LazySingleton(classType: PatcherViewModel),
|
LazySingleton(classType: PatcherViewModel),
|
||||||
LazySingleton(classType: AppSelectorViewModel),
|
|
||||||
LazySingleton(classType: PatchesSelectorViewModel),
|
|
||||||
LazySingleton(classType: InstallerViewModel),
|
|
||||||
LazySingleton(
|
LazySingleton(
|
||||||
classType: ThemeService,
|
classType: ThemeService,
|
||||||
resolveUsing: ThemeService.getInstance,
|
resolveUsing: ThemeService.getInstance,
|
||||||
|
@ -1,32 +1,36 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
import 'package:github/github.dart';
|
import 'package:github/github.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
|
||||||
import 'package:revanced_manager/models/patched_application.dart';
|
import 'package:revanced_manager/models/patched_application.dart';
|
||||||
import 'package:timeago/timeago.dart';
|
import 'package:timeago/timeago.dart';
|
||||||
|
|
||||||
@lazySingleton
|
|
||||||
class GithubAPI {
|
class GithubAPI {
|
||||||
var github = GitHub();
|
final GitHub _github = GitHub();
|
||||||
|
|
||||||
Future<String?> latestRelease(String org, repoName) async {
|
Future<File?> latestRelease(String org, repoName) async {
|
||||||
try {
|
try {
|
||||||
var latestRelease = await github.repositories.getLatestRelease(
|
var latestRelease = await _github.repositories.getLatestRelease(
|
||||||
RepositorySlug(org, repoName),
|
RepositorySlug(org, repoName),
|
||||||
);
|
);
|
||||||
return latestRelease.assets
|
String? url = latestRelease.assets
|
||||||
?.firstWhere((asset) =>
|
?.firstWhere((asset) =>
|
||||||
asset.name != null &&
|
asset.name != null &&
|
||||||
(asset.name!.endsWith('.dex') || asset.name!.endsWith('.apk')) &&
|
(asset.name!.endsWith('.dex') || asset.name!.endsWith('.apk')) &&
|
||||||
!asset.name!.contains('-sources') &&
|
!asset.name!.contains('-sources') &&
|
||||||
!asset.name!.contains('-javadoc'))
|
!asset.name!.contains('-javadoc'))
|
||||||
.browserDownloadUrl;
|
.browserDownloadUrl;
|
||||||
|
if (url != null) {
|
||||||
|
return await DefaultCacheManager().getSingleFile(url);
|
||||||
|
}
|
||||||
} on Exception {
|
} on Exception {
|
||||||
return '';
|
return null;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<String> latestCommitTime(String org, repoName) async {
|
Future<String> latestCommitTime(String org, repoName) async {
|
||||||
try {
|
try {
|
||||||
var repo = await github.repositories.getRepository(
|
var repo = await _github.repositories.getRepository(
|
||||||
RepositorySlug(org, repoName),
|
RepositorySlug(org, repoName),
|
||||||
);
|
);
|
||||||
return repo.pushedAt != null
|
return repo.pushedAt != null
|
||||||
@ -39,7 +43,7 @@ class GithubAPI {
|
|||||||
|
|
||||||
Future<List<Contributor>> getContributors(String org, repoName) async {
|
Future<List<Contributor>> getContributors(String org, repoName) async {
|
||||||
try {
|
try {
|
||||||
var contributors = github.repositories.listContributors(
|
var contributors = _github.repositories.listContributors(
|
||||||
RepositorySlug(org, repoName),
|
RepositorySlug(org, repoName),
|
||||||
);
|
);
|
||||||
return contributors.toList();
|
return contributors.toList();
|
||||||
|
@ -1,52 +1,19 @@
|
|||||||
import 'dart:io';
|
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/constants.dart';
|
||||||
import 'package:revanced_manager/services/github_api.dart';
|
import 'package:revanced_manager/services/github_api.dart';
|
||||||
|
|
||||||
@lazySingleton
|
|
||||||
class ManagerAPI {
|
class ManagerAPI {
|
||||||
final Dio dio = Dio();
|
final GithubAPI _githubAPI = GithubAPI();
|
||||||
final GithubAPI githubAPI = GithubAPI();
|
|
||||||
|
|
||||||
Future<String?> getPath() async {
|
Future<File?> downloadPatches() async {
|
||||||
final path = await p.getApplicationSupportDirectory();
|
return await _githubAPI.latestRelease(ghOrg, patchesRepo);
|
||||||
Directory('${path.path}/revanced').createSync();
|
|
||||||
final workDirPath = '${path.path}/revanced';
|
|
||||||
return workDirPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<File?> downloadAssets(String repo) async {
|
Future<File?> downloadIntegrations() async {
|
||||||
try {
|
return await _githubAPI.latestRelease(ghOrg, integrationsRepo);
|
||||||
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<void> downloadPatches() async {
|
Future<File?> downloadManager() async {
|
||||||
await downloadAssets(patchesRepo);
|
return await _githubAPI.latestRelease(ghOrg, managerRepo);
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> downloadIntegrations() async {
|
|
||||||
await downloadAssets(integrationsRepo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,12 @@ import 'dart:io';
|
|||||||
import 'package:app_installer/app_installer.dart';
|
import 'package:app_installer/app_installer.dart';
|
||||||
import 'package:device_apps/device_apps.dart';
|
import 'package:device_apps/device_apps.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
import 'package:path_provider/path_provider.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/patch.dart';
|
||||||
import 'package:revanced_manager/models/patched_application.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/services/root_api.dart';
|
||||||
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
|
|
||||||
import 'package:revanced_manager/utils/string.dart';
|
import 'package:revanced_manager/utils/string.dart';
|
||||||
import 'package:share_extend/share_extend.dart';
|
import 'package:share_extend/share_extend.dart';
|
||||||
|
|
||||||
@ -19,11 +16,8 @@ class PatcherAPI {
|
|||||||
static const patcherChannel = MethodChannel(
|
static const patcherChannel = MethodChannel(
|
||||||
'app.revanced.manager/patcher',
|
'app.revanced.manager/patcher',
|
||||||
);
|
);
|
||||||
static const installerChannel = MethodChannel(
|
final ManagerAPI _managerAPI = ManagerAPI();
|
||||||
'app.revanced.manager/installer',
|
final RootAPI _rootAPI = RootAPI();
|
||||||
);
|
|
||||||
final GithubAPI githubAPI = GithubAPI();
|
|
||||||
final RootAPI rootAPI = RootAPI();
|
|
||||||
Directory? _tmpDir;
|
Directory? _tmpDir;
|
||||||
Directory? _workDir;
|
Directory? _workDir;
|
||||||
Directory? _cacheDir;
|
Directory? _cacheDir;
|
||||||
@ -33,45 +27,18 @@ class PatcherAPI {
|
|||||||
File? _patchedFile;
|
File? _patchedFile;
|
||||||
File? _outFile;
|
File? _outFile;
|
||||||
|
|
||||||
Future<dynamic> handlePlatformChannelMethods() async {
|
Future<void> loadPatches() 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 {
|
|
||||||
if (_patchBundleFile == null) {
|
if (_patchBundleFile == null) {
|
||||||
String? dexFileUrl =
|
_patchBundleFile = await _managerAPI.downloadPatches();
|
||||||
await githubAPI.latestRelease('revanced', 'revanced-patches');
|
if (_patchBundleFile != null) {
|
||||||
if (dexFileUrl != null && dexFileUrl.isNotEmpty) {
|
await patcherChannel.invokeMethod<bool>(
|
||||||
try {
|
'loadPatches',
|
||||||
_patchBundleFile =
|
{
|
||||||
await DefaultCacheManager().getSingleFile(dexFileUrl);
|
'pathBundlesPaths': <String>[_patchBundleFile!.absolute.path],
|
||||||
return await patcherChannel.invokeMethod<bool>(
|
},
|
||||||
'loadPatches',
|
);
|
||||||
{
|
|
||||||
'pathBundlesPaths': <String>[_patchBundleFile!.absolute.path],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} on Exception {
|
|
||||||
_patchBundleFile = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<ApplicationWithIcon>> getFilteredInstalledApps() async {
|
Future<List<ApplicationWithIcon>> getFilteredInstalledApps() async {
|
||||||
@ -181,20 +148,9 @@ class PatcherAPI {
|
|||||||
return appliedPatches;
|
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 {
|
Future<void> initPatcher(bool mergeIntegrations) async {
|
||||||
if (mergeIntegrations) {
|
if (mergeIntegrations) {
|
||||||
_integrations = await downloadIntegrations();
|
_integrations = await _managerAPI.downloadIntegrations();
|
||||||
} else {
|
|
||||||
_integrations = File('');
|
|
||||||
}
|
}
|
||||||
_tmpDir = await getTemporaryDirectory();
|
_tmpDir = await getTemporaryDirectory();
|
||||||
_workDir = _tmpDir!.createTempSync('tmp-');
|
_workDir = _tmpDir!.createTempSync('tmp-');
|
||||||
@ -218,7 +174,7 @@ class PatcherAPI {
|
|||||||
'inputFilePath': _inputFile!.path,
|
'inputFilePath': _inputFile!.path,
|
||||||
'patchedFilePath': _patchedFile!.path,
|
'patchedFilePath': _patchedFile!.path,
|
||||||
'outFilePath': _outFile!.path,
|
'outFilePath': _outFile!.path,
|
||||||
'integrationsPath': _integrations!.path,
|
'integrationsPath': _integrations != null ? _integrations!.path : '',
|
||||||
'selectedPatches': selectedPatches.map((p) => p.name).toList(),
|
'selectedPatches': selectedPatches.map((p) => p.name).toList(),
|
||||||
'cacheDirPath': _cacheDir!.path,
|
'cacheDirPath': _cacheDir!.path,
|
||||||
'mergeIntegrations': mergeIntegrations,
|
'mergeIntegrations': mergeIntegrations,
|
||||||
@ -231,7 +187,7 @@ class PatcherAPI {
|
|||||||
if (_outFile != null) {
|
if (_outFile != null) {
|
||||||
try {
|
try {
|
||||||
if (patchedApp.isRooted && !patchedApp.isFromStorage) {
|
if (patchedApp.isRooted && !patchedApp.isFromStorage) {
|
||||||
return rootAPI.installApp(
|
return _rootAPI.installApp(
|
||||||
patchedApp.packageName,
|
patchedApp.packageName,
|
||||||
patchedApp.apkFilePath,
|
patchedApp.apkFilePath,
|
||||||
_outFile!.path,
|
_outFile!.path,
|
||||||
@ -268,14 +224,14 @@ class PatcherAPI {
|
|||||||
|
|
||||||
Future<bool> checkOldPatch(PatchedApplication patchedApp) async {
|
Future<bool> checkOldPatch(PatchedApplication patchedApp) async {
|
||||||
if (patchedApp.isRooted) {
|
if (patchedApp.isRooted) {
|
||||||
return await rootAPI.checkApp(patchedApp.packageName);
|
return await _rootAPI.checkApp(patchedApp.packageName);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> deleteOldPatch(PatchedApplication patchedApp) async {
|
Future<void> deleteOldPatch(PatchedApplication patchedApp) async {
|
||||||
if (patchedApp.isRooted) {
|
if (patchedApp.isRooted) {
|
||||||
await rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath);
|
await _rootAPI.deleteApp(patchedApp.packageName, patchedApp.apkFilePath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,14 @@
|
|||||||
import 'package:injectable/injectable.dart';
|
|
||||||
import 'package:root/root.dart';
|
import 'package:root/root.dart';
|
||||||
|
|
||||||
@lazySingleton
|
|
||||||
class RootAPI {
|
class RootAPI {
|
||||||
final String managerDirPath = "/data/adb/revanced_manager";
|
final String _managerDirPath = "/data/adb/revanced_manager";
|
||||||
final String postFsDataDirPath = "/data/adb/post-fs-data.d";
|
final String _postFsDataDirPath = "/data/adb/post-fs-data.d";
|
||||||
final String serviceDDirPath = "/data/adb/service.d";
|
final String _serviceDDirPath = "/data/adb/service.d";
|
||||||
|
|
||||||
Future<bool> checkApp(String packageName) async {
|
Future<bool> checkApp(String packageName) async {
|
||||||
try {
|
try {
|
||||||
String? res = await Root.exec(
|
String? res = await Root.exec(
|
||||||
cmd: 'ls -la "$managerDirPath/$packageName"',
|
cmd: 'ls -la "$_managerDirPath/$packageName"',
|
||||||
);
|
);
|
||||||
return res != null && res.isNotEmpty;
|
return res != null && res.isNotEmpty;
|
||||||
} on Exception {
|
} on Exception {
|
||||||
@ -26,13 +24,13 @@ class RootAPI {
|
|||||||
cmd: 'su -mm -c "umount -l $originalFilePath"',
|
cmd: 'su -mm -c "umount -l $originalFilePath"',
|
||||||
);
|
);
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'rm -rf "$managerDirPath/$packageName"',
|
cmd: 'rm -rf "$_managerDirPath/$packageName"',
|
||||||
);
|
);
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'rm -rf "$serviceDDirPath/$packageName.sh"',
|
cmd: 'rm -rf "$_serviceDDirPath/$packageName.sh"',
|
||||||
);
|
);
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'rm -rf "$postFsDataDirPath/$packageName.sh"',
|
cmd: 'rm -rf "$_postFsDataDirPath/$packageName.sh"',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +41,7 @@ class RootAPI {
|
|||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'mkdir -p "$managerDirPath/$packageName"',
|
cmd: 'mkdir -p "$_managerDirPath/$packageName"',
|
||||||
);
|
);
|
||||||
installServiceDScript(packageName);
|
installServiceDScript(packageName);
|
||||||
installPostFsDataScript(packageName);
|
installPostFsDataScript(packageName);
|
||||||
@ -58,10 +56,10 @@ class RootAPI {
|
|||||||
Future<void> installServiceDScript(String packageName) async {
|
Future<void> installServiceDScript(String packageName) async {
|
||||||
String content = '#!/system/bin/sh\n'
|
String content = '#!/system/bin/sh\n'
|
||||||
'while [ "\$(getprop sys.boot_completed | tr -d \'"\'"\'\\\\r\'"\'"\')" != "1" ]; do sleep 1; done\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'
|
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
|
||||||
'[ ! -z \$stock_path ] && mount -o bind \$base_path \$stock_path';
|
'[ ! -z \$stock_path ] && mount -o bind \$base_path \$stock_path';
|
||||||
String scriptFilePath = '$serviceDDirPath/$packageName.sh';
|
String scriptFilePath = '$_serviceDDirPath/$packageName.sh';
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
||||||
);
|
);
|
||||||
@ -74,7 +72,7 @@ class RootAPI {
|
|||||||
String content = '#!/system/bin/sh\n'
|
String content = '#!/system/bin/sh\n'
|
||||||
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
|
'stock_path=\$(pm path $packageName | grep base | sed \'"\'"\'s/package://g\'"\'"\')\n'
|
||||||
'[ ! -z \$stock_path ] && umount -l \$stock_path';
|
'[ ! -z \$stock_path ] && umount -l \$stock_path';
|
||||||
String scriptFilePath = '$postFsDataDirPath/$packageName.sh';
|
String scriptFilePath = '$_postFsDataDirPath/$packageName.sh';
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
cmd: 'echo \'$content\' > "$scriptFilePath"',
|
||||||
);
|
);
|
||||||
@ -84,7 +82,7 @@ class RootAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> installApk(String packageName, String patchedFilePath) async {
|
Future<void> installApk(String packageName, String patchedFilePath) async {
|
||||||
String newPatchedFilePath = '$managerDirPath/$packageName/base.apk';
|
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
|
cmd: 'cp "$patchedFilePath" "$newPatchedFilePath"',
|
||||||
);
|
);
|
||||||
@ -104,7 +102,7 @@ class RootAPI {
|
|||||||
String originalFilePath,
|
String originalFilePath,
|
||||||
String patchedFilePath,
|
String patchedFilePath,
|
||||||
) async {
|
) async {
|
||||||
String newPatchedFilePath = '$managerDirPath/$packageName/base.apk';
|
String newPatchedFilePath = '$_managerDirPath/$packageName/base.apk';
|
||||||
await Root.exec(
|
await Root.exec(
|
||||||
cmd: 'am force-stop "$packageName"',
|
cmd: 'am force-stop "$packageName"',
|
||||||
);
|
);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_i18n/flutter_i18n.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/theme.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/installed_app_item.dart';
|
import 'package:revanced_manager/ui/widgets/installed_app_item.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/search_bar.dart';
|
import 'package:revanced_manager/ui/widgets/search_bar.dart';
|
||||||
@ -15,14 +14,13 @@ class AppSelectorView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _AppSelectorViewState extends State<AppSelectorView> {
|
class _AppSelectorViewState extends State<AppSelectorView> {
|
||||||
String query = '';
|
String _query = '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ViewModelBuilder<AppSelectorViewModel>.reactive(
|
return ViewModelBuilder<AppSelectorViewModel>.reactive(
|
||||||
disposeViewModel: false,
|
|
||||||
onModelReady: (model) => model.initialize(),
|
onModelReady: (model) => model.initialize(),
|
||||||
viewModelBuilder: () => locator<AppSelectorViewModel>(),
|
viewModelBuilder: () => AppSelectorViewModel(),
|
||||||
builder: (context, model, child) => Scaffold(
|
builder: (context, model, child) => Scaffold(
|
||||||
floatingActionButton: FloatingActionButton.extended(
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@ -51,17 +49,17 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
|||||||
hintTextColor: Theme.of(context).colorScheme.tertiary,
|
hintTextColor: Theme.of(context).colorScheme.tertiary,
|
||||||
onQueryChanged: (searchQuery) {
|
onQueryChanged: (searchQuery) {
|
||||||
setState(() {
|
setState(() {
|
||||||
query = searchQuery;
|
_query = searchQuery;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 12),
|
||||||
query.isEmpty || query.length < 2
|
_query.isEmpty || _query.length < 2
|
||||||
? _getAllResults(model)
|
? _getAllResults(model)
|
||||||
: _getFilteredResults(model)
|
: _getFilteredResults(model)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: query.isEmpty || query.length < 2
|
: _query.isEmpty || _query.length < 2
|
||||||
? Center(
|
? Center(
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
@ -105,7 +103,7 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
|||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
model.apps.sort((a, b) => a.appName.compareTo(b.appName));
|
model.apps.sort((a, b) => a.appName.compareTo(b.appName));
|
||||||
if (model.apps[index].appName.toLowerCase().contains(
|
if (model.apps[index].appName.toLowerCase().contains(
|
||||||
query.toLowerCase(),
|
_query.toLowerCase(),
|
||||||
)) {
|
)) {
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
@ -8,30 +8,22 @@ import 'package:revanced_manager/app/app.locator.dart';
|
|||||||
import 'package:revanced_manager/models/patched_application.dart';
|
import 'package:revanced_manager/models/patched_application.dart';
|
||||||
import 'package:revanced_manager/services/patcher_api.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/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:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
class AppSelectorViewModel extends BaseViewModel {
|
class AppSelectorViewModel extends BaseViewModel {
|
||||||
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||||
bool isRooted = false;
|
final List<ApplicationWithIcon> apps = [];
|
||||||
bool isFromStorage = false;
|
bool _isRooted = false;
|
||||||
List<ApplicationWithIcon> apps = [];
|
|
||||||
PatchedApplication? selectedApp;
|
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
await getApps();
|
apps.addAll(await _patcherAPI.getFilteredInstalledApps());
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
isRooted = prefs.getBool('isRooted') ?? false;
|
_isRooted = prefs.getBool('isRooted') ?? false;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getApps() async {
|
|
||||||
apps = await patcherAPI.getFilteredInstalledApps();
|
|
||||||
}
|
|
||||||
|
|
||||||
void selectApp(ApplicationWithIcon application) async {
|
void selectApp(ApplicationWithIcon application) async {
|
||||||
isFromStorage = false;
|
|
||||||
PatchedApplication app = PatchedApplication(
|
PatchedApplication app = PatchedApplication(
|
||||||
name: application.appName,
|
name: application.appName,
|
||||||
packageName: application.packageName,
|
packageName: application.packageName,
|
||||||
@ -39,18 +31,16 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||||||
apkFilePath: application.apkFilePath,
|
apkFilePath: application.apkFilePath,
|
||||||
icon: application.icon,
|
icon: application.icon,
|
||||||
patchDate: DateTime.now(),
|
patchDate: DateTime.now(),
|
||||||
isRooted: isRooted,
|
isRooted: _isRooted,
|
||||||
isFromStorage: isFromStorage,
|
isFromStorage: false,
|
||||||
appliedPatches: [],
|
appliedPatches: [],
|
||||||
);
|
);
|
||||||
locator<AppSelectorViewModel>().selectedApp = app;
|
locator<PatcherViewModel>().selectedApp = app;
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
locator<PatcherViewModel>().selectedPatches.clear();
|
||||||
locator<PatchesSelectorViewModel>().notifyListeners();
|
|
||||||
locator<PatcherViewModel>().notifyListeners();
|
locator<PatcherViewModel>().notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> selectAppFromStorage(BuildContext context) async {
|
Future<void> selectAppFromStorage(BuildContext context) async {
|
||||||
isFromStorage = true;
|
|
||||||
try {
|
try {
|
||||||
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
FilePickerResult? result = await FilePicker.platform.pickFiles(
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
@ -69,13 +59,12 @@ class AppSelectorViewModel extends BaseViewModel {
|
|||||||
apkFilePath: result.files.single.path!,
|
apkFilePath: result.files.single.path!,
|
||||||
icon: application.icon,
|
icon: application.icon,
|
||||||
patchDate: DateTime.now(),
|
patchDate: DateTime.now(),
|
||||||
isRooted: isRooted,
|
isRooted: _isRooted,
|
||||||
isFromStorage: isFromStorage,
|
isFromStorage: true,
|
||||||
appliedPatches: [],
|
appliedPatches: [],
|
||||||
);
|
);
|
||||||
locator<AppSelectorViewModel>().selectedApp = app;
|
locator<PatcherViewModel>().selectedApp = app;
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
locator<PatcherViewModel>().selectedPatches.clear();
|
||||||
locator<PatchesSelectorViewModel>().notifyListeners();
|
|
||||||
locator<PatcherViewModel>().notifyListeners();
|
locator<PatcherViewModel>().notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ class ContributorsView extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ViewModelBuilder<ContributorsViewModel>.reactive(
|
return ViewModelBuilder<ContributorsViewModel>.reactive(
|
||||||
disposeViewModel: false,
|
|
||||||
viewModelBuilder: () => ContributorsViewModel(),
|
viewModelBuilder: () => ContributorsViewModel(),
|
||||||
onModelReady: (model) => model.getContributors(),
|
onModelReady: (model) => model.getContributors(),
|
||||||
builder: (context, model, child) => Scaffold(
|
builder: (context, model, child) => Scaffold(
|
||||||
|
@ -1,27 +1,36 @@
|
|||||||
import 'package:github/github.dart';
|
import 'package:github/github.dart';
|
||||||
|
import 'package:revanced_manager/constants.dart';
|
||||||
import 'package:revanced_manager/services/github_api.dart';
|
import 'package:revanced_manager/services/github_api.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
class ContributorsViewModel extends BaseViewModel {
|
class ContributorsViewModel extends BaseViewModel {
|
||||||
final GithubAPI githubAPI = GithubAPI();
|
final GithubAPI _githubAPI = GithubAPI();
|
||||||
List<Contributor> patchesContributors = [];
|
List<Contributor> patchesContributors = [];
|
||||||
List<Contributor> integrationsContributors = [];
|
List<Contributor> integrationsContributors = [];
|
||||||
List<Contributor> patcherContributors = [];
|
List<Contributor> patcherContributors = [];
|
||||||
List<Contributor> cliContributors = [];
|
List<Contributor> cliContributors = [];
|
||||||
List<Contributor> managerContributors = [];
|
List<Contributor> managerContributors = [];
|
||||||
|
|
||||||
Future<List<Contributor>> getContributors() async {
|
Future<void> getContributors() async {
|
||||||
patchesContributors =
|
patchesContributors = await _githubAPI.getContributors(
|
||||||
await githubAPI.getContributors('revanced', 'revanced-patches');
|
ghOrg,
|
||||||
integrationsContributors =
|
patchesRepo,
|
||||||
await githubAPI.getContributors('revanced', 'revanced-integrations');
|
);
|
||||||
patcherContributors =
|
integrationsContributors = await _githubAPI.getContributors(
|
||||||
await githubAPI.getContributors('revanced', 'revanced-patcher');
|
ghOrg,
|
||||||
cliContributors =
|
integrationsRepo,
|
||||||
await githubAPI.getContributors('revanced', 'revanced-cli');
|
);
|
||||||
managerContributors =
|
patcherContributors = await _githubAPI.getContributors(
|
||||||
await githubAPI.getContributors('revanced', 'revanced-manager');
|
ghOrg,
|
||||||
|
patcherRepo,
|
||||||
return [];
|
);
|
||||||
|
cliContributors = await _githubAPI.getContributors(
|
||||||
|
ghOrg,
|
||||||
|
cliRepo,
|
||||||
|
);
|
||||||
|
managerContributors = await _githubAPI.getContributors(
|
||||||
|
ghOrg,
|
||||||
|
managerRepo,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ class HomeView extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
LatestCommitCard(
|
LatestCommitCard(
|
||||||
|
onPressed: () => model.updateManager(context),
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 23),
|
const SizedBox(height: 23),
|
||||||
@ -77,7 +78,7 @@ class HomeView extends StatelessWidget {
|
|||||||
label: "homeView.updatesAvailable",
|
label: "homeView.updatesAvailable",
|
||||||
isSelected: model.showUpdatableApps,
|
isSelected: model.showUpdatableApps,
|
||||||
onSelected: (value) {
|
onSelected: (value) {
|
||||||
model.toggleUpdatableApps(value);
|
model.toggleUpdatableApps(true);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
|
@ -1,26 +1,41 @@
|
|||||||
import 'dart:convert';
|
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:injectable/injectable.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.dart';
|
import 'package:revanced_manager/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/app/app.router.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/models/patched_application.dart';
|
||||||
import 'package:revanced_manager/services/github_api.dart';
|
import 'package:revanced_manager/services/github_api.dart';
|
||||||
import 'package:revanced_manager/services/manager_api.dart';
|
import 'package:revanced_manager/services/manager_api.dart';
|
||||||
import 'package:revanced_manager/services/patcher_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/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:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
|
||||||
@lazySingleton
|
@lazySingleton
|
||||||
class HomeViewModel extends BaseViewModel {
|
class HomeViewModel extends BaseViewModel {
|
||||||
final _navigationService = locator<NavigationService>();
|
final NavigationService _navigationService = locator<NavigationService>();
|
||||||
final GithubAPI githubAPI = GithubAPI();
|
final ManagerAPI _managerAPI = ManagerAPI();
|
||||||
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
final GithubAPI _githubAPI = GithubAPI();
|
||||||
|
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||||
|
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
|
||||||
|
FlutterLocalNotificationsPlugin();
|
||||||
bool showUpdatableApps = true;
|
bool showUpdatableApps = true;
|
||||||
|
|
||||||
Future<void> initialize() async {
|
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) {
|
void toggleUpdatableApps(bool value) {
|
||||||
@ -28,13 +43,10 @@ class HomeViewModel extends BaseViewModel {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future downloadPatches() => locator<ManagerAPI>().downloadPatches();
|
|
||||||
Future downloadIntegrations() => locator<ManagerAPI>().downloadIntegrations();
|
|
||||||
|
|
||||||
void navigateToInstaller(PatchedApplication app) async {
|
void navigateToInstaller(PatchedApplication app) async {
|
||||||
locator<AppSelectorViewModel>().selectedApp = app;
|
locator<PatcherViewModel>().selectedApp = app;
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches =
|
locator<PatcherViewModel>().selectedPatches =
|
||||||
await patcherAPI.getAppliedPatches(app);
|
await _patcherAPI.getAppliedPatches(app);
|
||||||
_navigationService.navigateTo(Routes.installerView);
|
_navigationService.navigateTo(Routes.installerView);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,15 +56,37 @@ class HomeViewModel extends BaseViewModel {
|
|||||||
List<String> patchedApps = prefs.getStringList('patchedApps') ?? [];
|
List<String> patchedApps = prefs.getStringList('patchedApps') ?? [];
|
||||||
for (String str in patchedApps) {
|
for (String str in patchedApps) {
|
||||||
PatchedApplication app = PatchedApplication.fromJson(json.decode(str));
|
PatchedApplication app = PatchedApplication.fromJson(json.decode(str));
|
||||||
bool hasUpdates = await githubAPI.hasUpdates(
|
bool hasUpdates = await _githubAPI.hasUpdates(app, ghOrg, patchesRepo);
|
||||||
app,
|
|
||||||
'revanced',
|
|
||||||
'revanced-patches',
|
|
||||||
);
|
|
||||||
if (hasUpdates == isUpdatable) {
|
if (hasUpdates == isUpdatable) {
|
||||||
list.add(app);
|
list.add(app);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list;
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||||
import 'package:google_fonts/google_fonts.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:revanced_manager/ui/views/installer/installer_viewmodel.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
@ -15,9 +14,8 @@ class InstallerView extends StatelessWidget {
|
|||||||
(_) => _controller.jumpTo(_controller.position.maxScrollExtent),
|
(_) => _controller.jumpTo(_controller.position.maxScrollExtent),
|
||||||
);
|
);
|
||||||
return ViewModelBuilder<InstallerViewModel>.reactive(
|
return ViewModelBuilder<InstallerViewModel>.reactive(
|
||||||
disposeViewModel: false,
|
onModelReady: (model) => model.initialize(context),
|
||||||
onModelReady: (model) => model.initialize(),
|
viewModelBuilder: () => InstallerViewModel(),
|
||||||
viewModelBuilder: () => locator<InstallerViewModel>(),
|
|
||||||
builder: (context, model, child) => WillPopScope(
|
builder: (context, model, child) => WillPopScope(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
floatingActionButton: Visibility(
|
floatingActionButton: Visibility(
|
||||||
|
@ -1,30 +1,43 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:device_apps/device_apps.dart';
|
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_background/flutter_background.dart';
|
||||||
|
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.dart';
|
import 'package:revanced_manager/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/models/patch.dart';
|
import 'package:revanced_manager/models/patch.dart';
|
||||||
import 'package:revanced_manager/models/patched_application.dart';
|
import 'package:revanced_manager/models/patched_application.dart';
|
||||||
import 'package:revanced_manager/services/patcher_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/patcher/patcher_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:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
class InstallerViewModel extends BaseViewModel {
|
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;
|
double? progress = 0.0;
|
||||||
String logs = '';
|
String logs = '';
|
||||||
bool isPatching = false;
|
bool isPatching = false;
|
||||||
bool isInstalled = false;
|
bool isInstalled = false;
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize(BuildContext context) async {
|
||||||
try {
|
try {
|
||||||
await FlutterBackground.initialize(
|
await FlutterBackground.initialize(
|
||||||
androidConfig: const FlutterBackgroundAndroidConfig(
|
androidConfig: FlutterBackgroundAndroidConfig(
|
||||||
notificationTitle: 'Patching',
|
notificationTitle: FlutterI18n.translate(
|
||||||
notificationText: 'ReVanced Manager is patching',
|
context,
|
||||||
|
'installerView.notificationTitle',
|
||||||
|
),
|
||||||
|
notificationText: FlutterI18n.translate(
|
||||||
|
context,
|
||||||
|
'installerView.notificationText',
|
||||||
|
),
|
||||||
notificationImportance: AndroidNotificationImportance.Default,
|
notificationImportance: AndroidNotificationImportance.Default,
|
||||||
notificationIcon: AndroidResource(
|
notificationIcon: const AndroidResource(
|
||||||
name: 'ic_notification',
|
name: 'ic_notification',
|
||||||
defType: 'drawable',
|
defType: 'drawable',
|
||||||
),
|
),
|
||||||
@ -32,11 +45,28 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
);
|
);
|
||||||
await FlutterBackground.enableBackgroundExecution();
|
await FlutterBackground.enableBackgroundExecution();
|
||||||
} finally {
|
} finally {
|
||||||
await locator<PatcherAPI>().handlePlatformChannelMethods();
|
await handlePlatformChannelMethods();
|
||||||
await runPatcher();
|
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) {
|
void updateProgress(double value) {
|
||||||
progress = value;
|
progress = value;
|
||||||
isInstalled = false;
|
isInstalled = false;
|
||||||
@ -59,37 +89,32 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
|
|
||||||
Future<void> runPatcher() async {
|
Future<void> runPatcher() async {
|
||||||
updateProgress(0.0);
|
updateProgress(0.0);
|
||||||
PatchedApplication? selectedApp =
|
if (_app != null && _patches.isNotEmpty) {
|
||||||
locator<AppSelectorViewModel>().selectedApp;
|
String apkFilePath = _app!.apkFilePath;
|
||||||
List<Patch> selectedPatches =
|
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches;
|
|
||||||
if (selectedApp != null && selectedPatches.isNotEmpty) {
|
|
||||||
String apkFilePath = selectedApp.apkFilePath;
|
|
||||||
try {
|
try {
|
||||||
updateLog('Initializing installer');
|
updateLog('Initializing installer');
|
||||||
if (selectedApp.isRooted && !selectedApp.isFromStorage) {
|
if (_app!.isRooted && !_app!.isFromStorage) {
|
||||||
updateLog('Checking if an old patched version exists');
|
updateLog('Checking if an old patched version exists');
|
||||||
bool oldExists =
|
bool oldExists = await _patcherAPI.checkOldPatch(_app!);
|
||||||
await locator<PatcherAPI>().checkOldPatch(selectedApp);
|
|
||||||
if (oldExists) {
|
if (oldExists) {
|
||||||
updateLog('Deleting old patched version');
|
updateLog('Deleting old patched version');
|
||||||
await locator<PatcherAPI>().deleteOldPatch(selectedApp);
|
await _patcherAPI.deleteOldPatch(_app!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
updateLog('Creating working directory');
|
updateLog('Creating working directory');
|
||||||
bool mergeIntegrations = false;
|
bool mergeIntegrations = false;
|
||||||
bool resourcePatching = false;
|
bool resourcePatching = false;
|
||||||
if (selectedApp.packageName == 'com.google.android.youtube') {
|
if (_app!.packageName == 'com.google.android.youtube') {
|
||||||
mergeIntegrations = true;
|
mergeIntegrations = true;
|
||||||
resourcePatching = true;
|
resourcePatching = true;
|
||||||
} else if (selectedApp.packageName ==
|
} else if (_app!.packageName ==
|
||||||
'com.google.android.apps.youtube.music') {
|
'com.google.android.apps.youtube.music') {
|
||||||
resourcePatching = true;
|
resourcePatching = true;
|
||||||
}
|
}
|
||||||
await locator<PatcherAPI>().initPatcher(mergeIntegrations);
|
await _patcherAPI.initPatcher(mergeIntegrations);
|
||||||
await locator<PatcherAPI>().runPatcher(
|
await _patcherAPI.runPatcher(
|
||||||
apkFilePath,
|
apkFilePath,
|
||||||
selectedPatches,
|
_patches,
|
||||||
mergeIntegrations,
|
mergeIntegrations,
|
||||||
resourcePatching,
|
resourcePatching,
|
||||||
);
|
);
|
||||||
@ -107,21 +132,16 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void installResult() async {
|
void installResult() async {
|
||||||
PatchedApplication? selectedApp =
|
if (_app != null) {
|
||||||
locator<AppSelectorViewModel>().selectedApp;
|
updateLog(_app!.isRooted
|
||||||
List<Patch> selectedPatches =
|
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches;
|
|
||||||
if (selectedApp != null) {
|
|
||||||
updateLog(selectedApp.isRooted
|
|
||||||
? 'Installing patched file using root method'
|
? 'Installing patched file using root method'
|
||||||
: 'Installing patched file using nonroot method');
|
: 'Installing patched file using nonroot method');
|
||||||
isInstalled = await locator<PatcherAPI>().installPatchedFile(selectedApp);
|
isInstalled = await _patcherAPI.installPatchedFile(_app!);
|
||||||
if (isInstalled) {
|
if (isInstalled) {
|
||||||
updateLog('Done');
|
updateLog('Done');
|
||||||
selectedApp.patchDate = DateTime.now();
|
_app!.patchDate = DateTime.now();
|
||||||
selectedApp.appliedPatches
|
_app!.appliedPatches.addAll(_patches.map((p) => p.name).toList());
|
||||||
.addAll(selectedPatches.map((p) => p.name).toList());
|
await saveApp();
|
||||||
await saveApp(selectedApp);
|
|
||||||
} else {
|
} else {
|
||||||
updateLog('An error occurred! Aborting');
|
updateLog('An error occurred! Aborting');
|
||||||
}
|
}
|
||||||
@ -129,39 +149,33 @@ class InstallerViewModel extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void shareResult() {
|
void shareResult() {
|
||||||
PatchedApplication? selectedApp =
|
if (_app != null) {
|
||||||
locator<AppSelectorViewModel>().selectedApp;
|
_patcherAPI.sharePatchedFile(_app!.name, _app!.version);
|
||||||
if (selectedApp != null) {
|
|
||||||
locator<PatcherAPI>().sharePatchedFile(
|
|
||||||
selectedApp.name,
|
|
||||||
selectedApp.version,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> cleanWorkplace() async {
|
Future<void> cleanWorkplace() async {
|
||||||
locator<PatcherAPI>().cleanPatcher();
|
_patcherAPI.cleanPatcher();
|
||||||
locator<AppSelectorViewModel>().selectedApp = null;
|
locator<PatcherViewModel>().selectedApp = null;
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches.clear();
|
locator<PatcherViewModel>().selectedPatches.clear();
|
||||||
locator<PatchesSelectorViewModel>().notifyListeners();
|
|
||||||
locator<PatcherViewModel>().notifyListeners();
|
locator<PatcherViewModel>().notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void openApp() {
|
void openApp() {
|
||||||
PatchedApplication? selectedApp =
|
if (_app != null) {
|
||||||
locator<AppSelectorViewModel>().selectedApp;
|
DeviceApps.openApp(_app!.packageName);
|
||||||
if (selectedApp != null) {
|
|
||||||
DeviceApps.openApp(selectedApp.packageName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> saveApp(PatchedApplication selectedApp) async {
|
Future<void> saveApp() async {
|
||||||
SharedPreferences prefs = await SharedPreferences.getInstance();
|
if (_app != null) {
|
||||||
List<String> patchedApps = prefs.getStringList('patchedApps') ?? [];
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
String app = json.encode(selectedApp.toJson());
|
List<String> patchedApps = prefs.getStringList('patchedApps') ?? [];
|
||||||
patchedApps.removeWhere(
|
String appStr = json.encode(_app!.toJson());
|
||||||
(a) => json.decode(a)['packageName'] == selectedApp.packageName);
|
patchedApps.removeWhere(
|
||||||
patchedApps.add(app);
|
(a) => json.decode(a)['packageName'] == _app!.packageName);
|
||||||
prefs.setStringList('patchedApps', patchedApps);
|
patchedApps.add(appStr);
|
||||||
|
prefs.setStringList('patchedApps', patchedApps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
|
import 'package:injectable/injectable.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.dart';
|
import 'package:revanced_manager/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/app/app.router.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/models/patch.dart';
|
||||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
|
import 'package:revanced_manager/models/patched_application.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
|
||||||
|
@lazySingleton
|
||||||
class PatcherViewModel extends BaseViewModel {
|
class PatcherViewModel extends BaseViewModel {
|
||||||
final _navigationService = locator<NavigationService>();
|
PatchedApplication? selectedApp;
|
||||||
|
List<Patch> selectedPatches = [];
|
||||||
|
|
||||||
|
final NavigationService _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
void navigateToAppSelector() {
|
void navigateToAppSelector() {
|
||||||
_navigationService.navigateTo(Routes.appSelectorView);
|
_navigationService.navigateTo(Routes.appSelectorView);
|
||||||
@ -21,10 +26,10 @@ class PatcherViewModel extends BaseViewModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool showFabButton() {
|
bool showFabButton() {
|
||||||
return locator<PatchesSelectorViewModel>().selectedPatches.isNotEmpty;
|
return selectedPatches.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool dimPatchesCard() {
|
bool dimPatchesCard() {
|
||||||
return locator<AppSelectorViewModel>().selectedApp == null;
|
return selectedApp == null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_i18n/flutter_i18n.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/theme.dart';
|
||||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
|
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/patch_item.dart';
|
import 'package:revanced_manager/ui/widgets/patch_item.dart';
|
||||||
@ -15,16 +14,14 @@ class PatchesSelectorView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
||||||
final List<PatchItem> patches = [];
|
final List<PatchItem> _items = [];
|
||||||
String query = '';
|
String query = '';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ViewModelBuilder<PatchesSelectorViewModel>.reactive(
|
return ViewModelBuilder<PatchesSelectorViewModel>.reactive(
|
||||||
disposeViewModel: false,
|
|
||||||
fireOnModelReadyOnce: true,
|
|
||||||
onModelReady: (model) => model.initialize(),
|
onModelReady: (model) => model.initialize(),
|
||||||
viewModelBuilder: () => locator<PatchesSelectorViewModel>(),
|
viewModelBuilder: () => PatchesSelectorViewModel(),
|
||||||
builder: (context, model, child) => Scaffold(
|
builder: (context, model, child) => Scaffold(
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@ -62,7 +59,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
|||||||
shape: RoundedRectangleBorder(
|
shape: RoundedRectangleBorder(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
),
|
),
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () {
|
||||||
|
model.selectPatches();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
child: I18nText('patchesSelectorView.fabButton'),
|
child: I18nText('patchesSelectorView.fabButton'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -79,7 +79,7 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _getAllResults(PatchesSelectorViewModel model) {
|
Widget _getAllResults(PatchesSelectorViewModel model) {
|
||||||
patches.clear();
|
_items.clear();
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: model.patches.length,
|
itemCount: model.patches.length,
|
||||||
@ -93,8 +93,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
|||||||
isSelected: model.selectedPatches.any(
|
isSelected: model.selectedPatches.any(
|
||||||
(element) => element.name == model.patches[index].name,
|
(element) => element.name == model.patches[index].name,
|
||||||
),
|
),
|
||||||
|
onChanged: (value) =>
|
||||||
|
model.selectPatch(model.patches[index].name, value),
|
||||||
);
|
);
|
||||||
patches.add(item);
|
_items.add(item);
|
||||||
return item;
|
return item;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -102,7 +104,7 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _getFilteredResults(PatchesSelectorViewModel model) {
|
Widget _getFilteredResults(PatchesSelectorViewModel model) {
|
||||||
patches.clear();
|
_items.clear();
|
||||||
return Expanded(
|
return Expanded(
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
itemCount: model.patches.length,
|
itemCount: model.patches.length,
|
||||||
@ -119,8 +121,10 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
|||||||
isSelected: model.selectedPatches.any(
|
isSelected: model.selectedPatches.any(
|
||||||
(element) => element.name == model.patches[index].name,
|
(element) => element.name == model.patches[index].name,
|
||||||
),
|
),
|
||||||
|
onChanged: (value) =>
|
||||||
|
model.selectPatch(model.patches[index].name, value),
|
||||||
);
|
);
|
||||||
patches.add(item);
|
_items.add(item);
|
||||||
return item;
|
return item;
|
||||||
} else {
|
} else {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
|
@ -1,38 +1,34 @@
|
|||||||
import 'package:revanced_manager/app/app.locator.dart';
|
import 'package:revanced_manager/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/models/patch.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/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/patcher/patcher_viewmodel.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/patch_item.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
class PatchesSelectorViewModel extends BaseViewModel {
|
class PatchesSelectorViewModel extends BaseViewModel {
|
||||||
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||||
List<Patch> patches = [];
|
final List<Patch> patches = [];
|
||||||
List<Patch> selectedPatches = [];
|
final List<Patch> selectedPatches =
|
||||||
|
locator<PatcherViewModel>().selectedPatches;
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
await getPatches();
|
patches.addAll(await _patcherAPI.getFilteredPatches(
|
||||||
|
locator<PatcherViewModel>().selectedApp,
|
||||||
|
));
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> getPatches() async {
|
void selectPatch(String name, bool isSelected) {
|
||||||
PatchedApplication? app = locator<AppSelectorViewModel>().selectedApp;
|
Patch patch = patches.firstWhere((p) => p.name == name);
|
||||||
patches = await patcherAPI.getFilteredPatches(app);
|
if (isSelected && !selectedPatches.contains(patch)) {
|
||||||
|
selectedPatches.add(patch);
|
||||||
|
} else {
|
||||||
|
selectedPatches.remove(patch);
|
||||||
|
}
|
||||||
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectPatch(PatchItem item) {
|
void selectPatches() {
|
||||||
Patch patch = locator<PatchesSelectorViewModel>()
|
locator<PatcherViewModel>().selectedPatches = selectedPatches;
|
||||||
.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();
|
|
||||||
locator<PatcherViewModel>().notifyListeners();
|
locator<PatcherViewModel>().notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,6 @@ class RootCheckerView extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ViewModelBuilder<RootCheckerViewModel>.reactive(
|
return ViewModelBuilder<RootCheckerViewModel>.reactive(
|
||||||
disposeViewModel: false,
|
|
||||||
viewModelBuilder: () => RootCheckerViewModel(),
|
viewModelBuilder: () => RootCheckerViewModel(),
|
||||||
builder: (context, model, child) => Scaffold(
|
builder: (context, model, child) => Scaffold(
|
||||||
floatingActionButton: Column(
|
floatingActionButton: Column(
|
||||||
|
@ -6,12 +6,13 @@ import 'package:root/root.dart';
|
|||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
|
||||||
class RootCheckerViewModel extends BaseViewModel {
|
class RootCheckerViewModel extends BaseViewModel {
|
||||||
final _navigationService = locator<NavigationService>();
|
final NavigationService _navigationService = locator<NavigationService>();
|
||||||
bool? isRooted = false;
|
bool isRooted = false;
|
||||||
|
|
||||||
Future<void> checkRoot() async {
|
Future<void> checkRoot() async {
|
||||||
isRooted = await Root.isRooted();
|
bool? res = await Root.isRooted();
|
||||||
if (isRooted == true) {
|
isRooted = res != null && res == true;
|
||||||
|
if (isRooted) {
|
||||||
navigateToHome();
|
navigateToHome();
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
@ -19,7 +20,7 @@ class RootCheckerViewModel extends BaseViewModel {
|
|||||||
|
|
||||||
Future<void> navigateToHome() async {
|
Future<void> navigateToHome() async {
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
prefs.setBool('isRooted', isRooted!);
|
prefs.setBool('isRooted', isRooted);
|
||||||
_navigationService.navigateTo(Routes.navigation);
|
_navigationService.navigateTo(Routes.navigation);
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ import 'package:stacked/stacked.dart';
|
|||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
|
||||||
class SettingsViewModel extends BaseViewModel {
|
class SettingsViewModel extends BaseViewModel {
|
||||||
final _navigationService = locator<NavigationService>();
|
final NavigationService _navigationService = locator<NavigationService>();
|
||||||
|
|
||||||
void setLanguage(String language) {
|
void setLanguage(String language) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
@ -1,25 +1,21 @@
|
|||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.dart';
|
import 'package:revanced_manager/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/constants.dart';
|
import 'package:revanced_manager/constants.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/app_selector/app_selector_viewmodel.dart';
|
|
||||||
|
|
||||||
class AppSelectorCard extends StatelessWidget {
|
class AppSelectorCard extends StatelessWidget {
|
||||||
final Function()? onPressed;
|
final Function() onPressed;
|
||||||
final Color? color;
|
final Color? color;
|
||||||
|
|
||||||
AppSelectorCard({
|
const AppSelectorCard({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.onPressed,
|
required this.onPressed,
|
||||||
this.color = const Color(0xff1B222B),
|
this.color = const Color(0xff1B222B),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final PatcherAPI patcherAPI = locator<PatcherAPI>();
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
@ -36,7 +32,7 @@ class AppSelectorCard extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
I18nText(
|
I18nText(
|
||||||
locator<AppSelectorViewModel>().selectedApp == null
|
locator<PatcherViewModel>().selectedApp == null
|
||||||
? 'appSelectorCard.widgetTitle'
|
? 'appSelectorCard.widgetTitle'
|
||||||
: 'appSelectorCard.widgetTitleSelected',
|
: 'appSelectorCard.widgetTitleSelected',
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -48,7 +44,7 @@ class AppSelectorCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
locator<AppSelectorViewModel>().selectedApp == null
|
locator<PatcherViewModel>().selectedApp == null
|
||||||
? I18nText(
|
? I18nText(
|
||||||
'appSelectorCard.widgetSubtitle',
|
'appSelectorCard.widgetSubtitle',
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -62,11 +58,9 @@ class AppSelectorCard extends StatelessWidget {
|
|||||||
height: 16.0,
|
height: 16.0,
|
||||||
child: ClipOval(
|
child: ClipOval(
|
||||||
child: Image.memory(
|
child: Image.memory(
|
||||||
locator<AppSelectorViewModel>().selectedApp == null
|
locator<PatcherViewModel>().selectedApp == null
|
||||||
? Uint8List(0)
|
? Uint8List(0)
|
||||||
: locator<AppSelectorViewModel>()
|
: locator<PatcherViewModel>().selectedApp!.icon,
|
||||||
.selectedApp!
|
|
||||||
.icon,
|
|
||||||
fit: BoxFit.cover,
|
fit: BoxFit.cover,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -85,8 +79,8 @@ class AppSelectorCard extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _getAppSelection() {
|
String _getAppSelection() {
|
||||||
String name = locator<AppSelectorViewModel>().selectedApp!.name;
|
String name = locator<PatcherViewModel>().selectedApp!.name;
|
||||||
String version = locator<AppSelectorViewModel>().selectedApp!.version;
|
String version = locator<PatcherViewModel>().selectedApp!.version;
|
||||||
return '$name (v$version)';
|
return '$name (v$version)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class ApplicationItem extends StatelessWidget {
|
|||||||
final DateTime patchDate;
|
final DateTime patchDate;
|
||||||
final String changelog;
|
final String changelog;
|
||||||
final bool isUpdatableApp;
|
final bool isUpdatableApp;
|
||||||
final Function()? onPressed;
|
final Function() onPressed;
|
||||||
|
|
||||||
const ApplicationItem({
|
const ApplicationItem({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.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/models/patched_application.dart';
|
||||||
import 'package:revanced_manager/services/github_api.dart';
|
import 'package:revanced_manager/services/github_api.dart';
|
||||||
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
||||||
@ -10,7 +11,7 @@ class AvailableUpdatesCard extends StatelessWidget {
|
|||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final GithubAPI githubAPI = GithubAPI();
|
final GithubAPI _githubAPI = GithubAPI();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -27,10 +28,10 @@ class AvailableUpdatesCard extends StatelessWidget {
|
|||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemCount: snapshot.data!.length,
|
itemCount: snapshot.data!.length,
|
||||||
itemBuilder: (context, index) => FutureBuilder<String>(
|
itemBuilder: (context, index) => FutureBuilder<String>(
|
||||||
future: githubAPI.getChangelog(
|
future: _githubAPI.getChangelog(
|
||||||
snapshot.data![index],
|
snapshot.data![index],
|
||||||
'revanced',
|
ghOrg,
|
||||||
'revanced-patches',
|
patchesRepo,
|
||||||
),
|
),
|
||||||
initialData: '',
|
initialData: '',
|
||||||
builder: (context, snapshot2) => ApplicationItem(
|
builder: (context, snapshot2) => ApplicationItem(
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.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/models/patched_application.dart';
|
||||||
import 'package:revanced_manager/services/github_api.dart';
|
import 'package:revanced_manager/services/github_api.dart';
|
||||||
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
||||||
@ -10,7 +11,7 @@ class InstalledAppsCard extends StatelessWidget {
|
|||||||
Key? key,
|
Key? key,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
final GithubAPI githubAPI = GithubAPI();
|
final GithubAPI _githubAPI = GithubAPI();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -27,10 +28,10 @@ class InstalledAppsCard extends StatelessWidget {
|
|||||||
physics: const NeverScrollableScrollPhysics(),
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
itemCount: snapshot.data!.length,
|
itemCount: snapshot.data!.length,
|
||||||
itemBuilder: (context, index) => FutureBuilder<String>(
|
itemBuilder: (context, index) => FutureBuilder<String>(
|
||||||
future: githubAPI.getChangelog(
|
future: _githubAPI.getChangelog(
|
||||||
snapshot.data![index],
|
snapshot.data![index],
|
||||||
'revanced',
|
ghOrg,
|
||||||
'revanced-patches',
|
patchesRepo,
|
||||||
),
|
),
|
||||||
initialData: '',
|
initialData: '',
|
||||||
builder: (context, snapshot2) => ApplicationItem(
|
builder: (context, snapshot2) => ApplicationItem(
|
||||||
|
@ -6,9 +6,11 @@ import 'package:revanced_manager/constants.dart';
|
|||||||
import 'package:revanced_manager/ui/widgets/patch_text_button.dart';
|
import 'package:revanced_manager/ui/widgets/patch_text_button.dart';
|
||||||
|
|
||||||
class LatestCommitCard extends StatefulWidget {
|
class LatestCommitCard extends StatefulWidget {
|
||||||
|
final Function() onPressed;
|
||||||
final Color? color;
|
final Color? color;
|
||||||
const LatestCommitCard({
|
const LatestCommitCard({
|
||||||
Key? key,
|
Key? key,
|
||||||
|
required this.onPressed,
|
||||||
this.color = const Color(0xff1B222B),
|
this.color = const Color(0xff1B222B),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -17,7 +19,7 @@ class LatestCommitCard extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LatestCommitCardState extends State<LatestCommitCard> {
|
class _LatestCommitCardState extends State<LatestCommitCard> {
|
||||||
final GithubAPI githubAPI = GithubAPI();
|
final GithubAPI _githubAPI = GithubAPI();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -45,10 +47,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
FutureBuilder<String>(
|
FutureBuilder<String>(
|
||||||
future: githubAPI.latestCommitTime(
|
future: _githubAPI.latestCommitTime(ghOrg, patcherRepo),
|
||||||
'revanced',
|
|
||||||
'revanced-patcher',
|
|
||||||
),
|
|
||||||
builder: (context, snapshot) => Text(
|
builder: (context, snapshot) => Text(
|
||||||
snapshot.hasData && snapshot.data!.isNotEmpty
|
snapshot.hasData && snapshot.data!.isNotEmpty
|
||||||
? FlutterI18n.translate(
|
? FlutterI18n.translate(
|
||||||
@ -78,10 +77,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
FutureBuilder<String>(
|
FutureBuilder<String>(
|
||||||
future: githubAPI.latestCommitTime(
|
future: _githubAPI.latestCommitTime(ghOrg, managerRepo),
|
||||||
'revanced',
|
|
||||||
'revanced-manager',
|
|
||||||
),
|
|
||||||
builder: (context, snapshot) => Text(
|
builder: (context, snapshot) => Text(
|
||||||
snapshot.hasData && snapshot.data!.isNotEmpty
|
snapshot.hasData && snapshot.data!.isNotEmpty
|
||||||
? FlutterI18n.translate(
|
? FlutterI18n.translate(
|
||||||
@ -105,7 +101,7 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
|
|||||||
context,
|
context,
|
||||||
'latestCommitCard.updateButton',
|
'latestCommitCard.updateButton',
|
||||||
),
|
),
|
||||||
onPressed: () => {},
|
onPressed: widget.onPressed,
|
||||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||||
borderColor: Theme.of(context).colorScheme.secondary,
|
borderColor: Theme.of(context).colorScheme.secondary,
|
||||||
),
|
),
|
||||||
|
@ -5,10 +5,10 @@ import 'package:google_fonts/google_fonts.dart';
|
|||||||
import 'package:revanced_manager/theme.dart';
|
import 'package:revanced_manager/theme.dart';
|
||||||
|
|
||||||
class MagiskButton extends StatelessWidget {
|
class MagiskButton extends StatelessWidget {
|
||||||
final Function()? onPressed;
|
final Function() onPressed;
|
||||||
const MagiskButton({
|
const MagiskButton({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.onPressed,
|
required this.onPressed,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.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
|
// ignore: must_be_immutable
|
||||||
class PatchItem extends StatefulWidget {
|
class PatchItem extends StatefulWidget {
|
||||||
@ -10,6 +8,7 @@ class PatchItem extends StatefulWidget {
|
|||||||
final String description;
|
final String description;
|
||||||
final String version;
|
final String version;
|
||||||
bool isSelected;
|
bool isSelected;
|
||||||
|
final Function(bool) onChanged;
|
||||||
|
|
||||||
PatchItem({
|
PatchItem({
|
||||||
Key? key,
|
Key? key,
|
||||||
@ -18,6 +17,7 @@ class PatchItem extends StatefulWidget {
|
|||||||
required this.description,
|
required this.description,
|
||||||
required this.version,
|
required this.version,
|
||||||
required this.isSelected,
|
required this.isSelected,
|
||||||
|
required this.onChanged,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -30,7 +30,7 @@ class _PatchItemState extends State<PatchItem> {
|
|||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() => widget.isSelected = !widget.isSelected);
|
setState(() => widget.isSelected = !widget.isSelected);
|
||||||
locator<PatchesSelectorViewModel>().selectPatch(widget);
|
widget.onChanged(widget.isSelected);
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
@ -82,7 +82,7 @@ class _PatchItemState extends State<PatchItem> {
|
|||||||
activeColor: Colors.blueGrey[500],
|
activeColor: Colors.blueGrey[500],
|
||||||
onChanged: (newValue) {
|
onChanged: (newValue) {
|
||||||
setState(() => widget.isSelected = newValue!);
|
setState(() => widget.isSelected = newValue!);
|
||||||
locator<PatchesSelectorViewModel>().selectPatch(widget);
|
widget.onChanged(widget.isSelected);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -4,16 +4,15 @@ import 'package:google_fonts/google_fonts.dart';
|
|||||||
import 'package:revanced_manager/app/app.locator.dart';
|
import 'package:revanced_manager/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/constants.dart';
|
import 'package:revanced_manager/constants.dart';
|
||||||
import 'package:revanced_manager/models/patch.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/patcher/patcher_viewmodel.dart';
|
||||||
import 'package:revanced_manager/ui/views/patches_selector/patches_selector_viewmodel.dart';
|
|
||||||
|
|
||||||
class PatchSelectorCard extends StatelessWidget {
|
class PatchSelectorCard extends StatelessWidget {
|
||||||
final Function()? onPressed;
|
final Function() onPressed;
|
||||||
final Color? color;
|
final Color? color;
|
||||||
|
|
||||||
const PatchSelectorCard({
|
const PatchSelectorCard({
|
||||||
Key? key,
|
Key? key,
|
||||||
this.onPressed,
|
required this.onPressed,
|
||||||
this.color = const Color(0xff1B222B),
|
this.color = const Color(0xff1B222B),
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
@ -32,7 +31,7 @@ class PatchSelectorCard extends StatelessWidget {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
I18nText(
|
I18nText(
|
||||||
locator<PatchesSelectorViewModel>().selectedPatches.isEmpty
|
locator<PatcherViewModel>().selectedPatches.isEmpty
|
||||||
? 'patchSelectorCard.widgetTitle'
|
? 'patchSelectorCard.widgetTitle'
|
||||||
: 'patchSelectorCard.widgetTitleSelected',
|
: 'patchSelectorCard.widgetTitleSelected',
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -44,7 +43,7 @@ class PatchSelectorCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
locator<AppSelectorViewModel>().selectedApp == null
|
locator<PatcherViewModel>().selectedApp == null
|
||||||
? I18nText(
|
? I18nText(
|
||||||
'patchSelectorCard.widgetSubtitle',
|
'patchSelectorCard.widgetSubtitle',
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -52,7 +51,7 @@ class PatchSelectorCard extends StatelessWidget {
|
|||||||
style: robotoTextStyle,
|
style: robotoTextStyle,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: locator<PatchesSelectorViewModel>().selectedPatches.isEmpty
|
: locator<PatcherViewModel>().selectedPatches.isEmpty
|
||||||
? I18nText(
|
? I18nText(
|
||||||
'patchSelectorCard.widgetEmptySubtitle',
|
'patchSelectorCard.widgetEmptySubtitle',
|
||||||
child: Text(
|
child: Text(
|
||||||
@ -72,7 +71,7 @@ class PatchSelectorCard extends StatelessWidget {
|
|||||||
|
|
||||||
String _getPatchesSelection() {
|
String _getPatchesSelection() {
|
||||||
String text = '';
|
String text = '';
|
||||||
for (Patch p in locator<PatchesSelectorViewModel>().selectedPatches) {
|
for (Patch p in locator<PatcherViewModel>().selectedPatches) {
|
||||||
text += '${p.simpleName} (v${p.version})\n';
|
text += '${p.simpleName} (v${p.version})\n';
|
||||||
}
|
}
|
||||||
return text.substring(0, text.length - 1);
|
return text.substring(0, text.length - 1);
|
||||||
|
@ -5,13 +5,13 @@ import 'package:revanced_manager/theme.dart';
|
|||||||
|
|
||||||
class PatchTextButton extends StatelessWidget {
|
class PatchTextButton extends StatelessWidget {
|
||||||
final String text;
|
final String text;
|
||||||
final Function()? onPressed;
|
final Function() onPressed;
|
||||||
final Color borderColor;
|
final Color borderColor;
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
const PatchTextButton({
|
const PatchTextButton({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.text,
|
required this.text,
|
||||||
this.onPressed,
|
required this.onPressed,
|
||||||
this.borderColor = const Color(0xff7792BA),
|
this.borderColor = const Color(0xff7792BA),
|
||||||
this.backgroundColor = Colors.transparent,
|
this.backgroundColor = Colors.transparent,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
@ -4,7 +4,7 @@ homepage: https://github.com/Aunali321/revanced-manager
|
|||||||
|
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
|
|
||||||
version: 1.0.0+1
|
version: 0.0.1+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ">=2.17.5 <3.0.0"
|
sdk: ">=2.17.5 <3.0.0"
|
||||||
@ -16,7 +16,6 @@ dependencies:
|
|||||||
git:
|
git:
|
||||||
url: https://github.com/ponces/flutter_plugin_device_apps
|
url: https://github.com/ponces/flutter_plugin_device_apps
|
||||||
ref: appinfo-from-storage
|
ref: appinfo-from-storage
|
||||||
dio: ^4.0.6
|
|
||||||
expandable: ^5.0.1
|
expandable: ^5.0.1
|
||||||
file_picker: ^5.0.1
|
file_picker: ^5.0.1
|
||||||
flutter:
|
flutter:
|
||||||
@ -24,6 +23,7 @@ dependencies:
|
|||||||
flutter_background: ^1.1.0
|
flutter_background: ^1.1.0
|
||||||
flutter_cache_manager: ^3.3.0
|
flutter_cache_manager: ^3.3.0
|
||||||
flutter_i18n: ^0.32.4
|
flutter_i18n: ^0.32.4
|
||||||
|
flutter_local_notifications: ^9.8.0+1
|
||||||
flutter_svg: ^1.1.1+1
|
flutter_svg: ^1.1.1+1
|
||||||
fluttertoast: ^8.0.9
|
fluttertoast: ^8.0.9
|
||||||
get_it: ^7.2.0
|
get_it: ^7.2.0
|
||||||
|
Loading…
Reference in New Issue
Block a user