mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2025-05-17 22:46:27 +08:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
6ac901f1d6 | |||
587ba795bb | |||
6b66c7bbd0 | |||
f398b6863a | |||
4d82ff3011 | |||
2a0ea78d7f | |||
4722880647 | |||
32fabcfa3f | |||
e0c46e4268 | |||
d84230fa22 | |||
9561153bfb | |||
c8c35ca801 | |||
cf99069804 | |||
6abb761724 | |||
4609ed9eba | |||
c0f743df89 | |||
f00f910973 | |||
2bc486dcec | |||
148981da18 | |||
d8df377427 | |||
efe1306aac | |||
ead308740f | |||
fa0f250e27 | |||
185d883b65 | |||
9d8b2e1a35 |
22
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
22
.github/ISSUE_TEMPLATE/bug-issue.yml
vendored
@ -20,14 +20,6 @@ body:
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Manager Branch
|
||||
options:
|
||||
- Flutter
|
||||
- Compose
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Bug description
|
||||
@ -112,3 +104,17 @@ body:
|
||||
description: Add additional context here.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your issue will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I filled out all of the requested information in this issue properly.
|
||||
required: true
|
||||
- label: The issue is related solely to the ReVanced Manager
|
||||
required: true
|
||||
|
24
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
24
.github/ISSUE_TEMPLATE/feature-issue.yml
vendored
@ -12,14 +12,6 @@ body:
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: Manager Branch
|
||||
options:
|
||||
- Flutter
|
||||
- Compose
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Issue
|
||||
@ -43,4 +35,18 @@ body:
|
||||
label: Additional context
|
||||
description: Add additional context here.
|
||||
validations:
|
||||
required: false
|
||||
required: false
|
||||
- type: checkboxes
|
||||
id: acknowledgements
|
||||
attributes:
|
||||
label: Acknowledgements
|
||||
description: Your issue will be closed if you haven't done these steps.
|
||||
options:
|
||||
- label: I have searched the existing issues and this is a new and no duplicate or related to another open issue.
|
||||
required: true
|
||||
- label: I have written a short but informative title.
|
||||
required: true
|
||||
- label: I filled out all of the requested information in this issue properly.
|
||||
required: true
|
||||
- label: The issue is related solely to the ReVanced Manager
|
||||
required: true
|
||||
|
@ -71,7 +71,7 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
// ReVanced
|
||||
implementation "app.revanced:revanced-patcher:5.0.0"
|
||||
implementation "app.revanced:revanced-patcher:5.0.1"
|
||||
|
||||
// Signing & aligning
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
||||
|
Binary file not shown.
BIN
android/app/src/main/jniLibs/arm64-v8a/libaapt2.so
Normal file
BIN
android/app/src/main/jniLibs/arm64-v8a/libaapt2.so
Normal file
Binary file not shown.
Binary file not shown.
BIN
android/app/src/main/jniLibs/armeabi-v7a/libaapt2.so
Normal file
BIN
android/app/src/main/jniLibs/armeabi-v7a/libaapt2.so
Normal file
Binary file not shown.
Binary file not shown.
BIN
android/app/src/main/jniLibs/x86_64/libaapt2.so
Normal file
BIN
android/app/src/main/jniLibs/x86_64/libaapt2.so
Normal file
Binary file not shown.
Binary file not shown.
@ -75,7 +75,9 @@
|
||||
"viewTitle": "Select patches",
|
||||
"searchBarHint": "Search patches",
|
||||
"doneButton": "Done",
|
||||
"noPatchesFound": "No patches found for the selected app"
|
||||
"noPatchesFound": "No patches found for the selected app",
|
||||
"selectAllPatchesWarningTitle": "Warning",
|
||||
"selectAllPatchesWarningContent": "You are about to select all patches, that includes unrecommended patches and can cause unwanted behavior."
|
||||
},
|
||||
"patchItem": {
|
||||
"unsupportedWarningButton": "Unsupported version",
|
||||
|
@ -6,10 +6,18 @@ import 'package:dio_http_cache_lts/dio_http_cache_lts.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
||||
|
||||
@lazySingleton
|
||||
class GithubAPI {
|
||||
final Dio _dio = Dio(BaseOptions(baseUrl: 'https://api.github.com'));
|
||||
final Dio _dio = Dio(
|
||||
BaseOptions(baseUrl: 'https://api.github.com'),
|
||||
)..httpClientAdapter = Http2Adapter(
|
||||
ConnectionManager(
|
||||
idleTimeout: 10000,
|
||||
onClientCreate: (_, config) => config.onBadCertificate = (_) => true,
|
||||
),
|
||||
);
|
||||
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
|
||||
final Options _cacheOptions = buildCacheOptions(
|
||||
const Duration(days: 1),
|
||||
|
@ -319,4 +319,14 @@ class ManagerAPI {
|
||||
}
|
||||
return newCommits;
|
||||
}
|
||||
|
||||
Future<bool> isSplitApk(PatchedApplication patchedApp) async {
|
||||
Application? app;
|
||||
if (patchedApp.isFromStorage) {
|
||||
app = await DeviceApps.getAppFromStorage(patchedApp.apkFilePath);
|
||||
} else {
|
||||
app = await DeviceApps.getApp(patchedApp.packageName);
|
||||
}
|
||||
return app != null && app.isSplit;
|
||||
}
|
||||
}
|
||||
|
@ -87,10 +87,21 @@ class PatcherAPI {
|
||||
.toList();
|
||||
}
|
||||
|
||||
bool dependencyNeedsIntegrations(String name) {
|
||||
return name.contains('integrations') ||
|
||||
_patches.any(
|
||||
(patch) =>
|
||||
patch.name == name &&
|
||||
(patch.dependencies.any(
|
||||
(dep) => dependencyNeedsIntegrations(dep),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
Future<bool> needsIntegrations(List<Patch> selectedPatches) async {
|
||||
return selectedPatches.any(
|
||||
(patch) => patch.dependencies.any(
|
||||
(dep) => dep.contains('integrations'),
|
||||
(dep) => dependencyNeedsIntegrations(dep),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:timeago/timeago.dart';
|
||||
import 'package:dio_http2_adapter/dio_http2_adapter.dart';
|
||||
|
||||
@lazySingleton
|
||||
class RevancedAPI {
|
||||
@ -17,7 +18,15 @@ class RevancedAPI {
|
||||
);
|
||||
|
||||
Future<void> initialize(String apiUrl) async {
|
||||
_dio = Dio(BaseOptions(baseUrl: apiUrl));
|
||||
_dio = Dio(BaseOptions(
|
||||
baseUrl: apiUrl,
|
||||
))
|
||||
..httpClientAdapter = Http2Adapter(
|
||||
ConnectionManager(
|
||||
idleTimeout: 10000,
|
||||
onClientCreate: (_, config) => config.onBadCertificate = (_) => true,
|
||||
),
|
||||
);
|
||||
_dio.interceptors.add(_dioCacheManager.interceptor);
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,15 @@ class RootAPI {
|
||||
final String _postFsDataDirPath = '/data/adb/post-fs-data.d';
|
||||
final String _serviceDDirPath = '/data/adb/service.d';
|
||||
|
||||
Future<bool> isRooted() async {
|
||||
try {
|
||||
bool? isRooted = await Root.isRootAvailable();
|
||||
return isRooted != null && isRooted;
|
||||
} on Exception {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> hasRootPermissions() async {
|
||||
try {
|
||||
bool? isRooted = await Root.isRootAvailable();
|
||||
|
@ -15,7 +15,7 @@ import 'package:revanced_manager/services/patcher_api.dart';
|
||||
import 'package:revanced_manager/services/toast.dart';
|
||||
import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
import 'package:timezone/timezone.dart' as tz;
|
||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:revanced_manager/ui/views/installer/installer_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/gradient_progress_indicator.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_popup_menu.dart';
|
||||
@ -18,130 +18,134 @@ class InstallerView extends StatelessWidget {
|
||||
onModelReady: (model) => model.initialize(context),
|
||||
viewModelBuilder: () => InstallerViewModel(),
|
||||
builder: (context, model, child) => WillPopScope(
|
||||
child: Scaffold(
|
||||
body: CustomScrollView(
|
||||
controller: model.scrollController,
|
||||
slivers: <Widget>[
|
||||
CustomSliverAppBar(
|
||||
title: Text(
|
||||
model.headerLogs,
|
||||
style: GoogleFonts.inter(
|
||||
color: Theme.of(context).textTheme.headline6!.color,
|
||||
),
|
||||
),
|
||||
onBackButtonPressed: () => model.onWillPop(context),
|
||||
actions: <Widget>[
|
||||
Visibility(
|
||||
visible: !model.isPatching && !model.hasErrors,
|
||||
child: CustomPopupMenu(
|
||||
onSelected: (value) => model.onMenuSelection(value),
|
||||
children: {
|
||||
0: I18nText(
|
||||
'installerView.shareApkMenuOption',
|
||||
child: const Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
1: I18nText(
|
||||
'installerView.shareLogMenuOption',
|
||||
child: const Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: Scaffold(
|
||||
body: CustomScrollView(
|
||||
controller: model.scrollController,
|
||||
slivers: <Widget>[
|
||||
CustomSliverAppBar(
|
||||
title: Text(
|
||||
model.headerLogs,
|
||||
style: GoogleFonts.inter(
|
||||
color: Theme.of(context).textTheme.headline6!.color,
|
||||
),
|
||||
),
|
||||
],
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size(double.infinity, 1.0),
|
||||
child:
|
||||
GradientProgressIndicator(progress: model.progress!)),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildListDelegate.fixed(
|
||||
<Widget>[
|
||||
CustomCard(
|
||||
child: Text(
|
||||
model.logs,
|
||||
style: GoogleFonts.jetBrainsMono(
|
||||
fontSize: 13,
|
||||
height: 1.5,
|
||||
onBackButtonPressed: () => model.onWillPop(context),
|
||||
actions: <Widget>[
|
||||
Visibility(
|
||||
visible: !model.isPatching,
|
||||
child: CustomPopupMenu(
|
||||
onSelected: (value) => model.onMenuSelection(value),
|
||||
children: {
|
||||
if (!model.hasErrors)
|
||||
0: I18nText(
|
||||
'installerView.shareApkMenuOption',
|
||||
child: const Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
1: I18nText(
|
||||
'installerView.shareLogMenuOption',
|
||||
child: const Text(
|
||||
'',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
bottom: PreferredSize(
|
||||
preferredSize: const Size(double.infinity, 1.0),
|
||||
child:
|
||||
GradientProgressIndicator(progress: model.progress!)),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildListDelegate.fixed(
|
||||
<Widget>[
|
||||
CustomCard(
|
||||
child: Text(
|
||||
model.logs,
|
||||
style: GoogleFonts.jetBrainsMono(
|
||||
fontSize: 13,
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Visibility(
|
||||
visible: !model.isPatching && !model.hasErrors,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0).copyWith(top: 0.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Visibility(
|
||||
visible: model.isInstalled,
|
||||
child: CustomMaterialButton(
|
||||
label: I18nText('installerView.openButton'),
|
||||
isExpanded: true,
|
||||
onPressed: () {
|
||||
model.openApp();
|
||||
model.cleanPatcher();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: !model.isInstalled,
|
||||
child: CustomMaterialButton(
|
||||
isFilled: false,
|
||||
label:
|
||||
I18nText('installerView.installRootButton'),
|
||||
isExpanded: true,
|
||||
onPressed: () => model.installResult(
|
||||
context,
|
||||
true,
|
||||
SliverFillRemaining(
|
||||
hasScrollBody: false,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Visibility(
|
||||
visible: !model.isPatching && !model.hasErrors,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0).copyWith(top: 0.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Visibility(
|
||||
visible: model.isInstalled,
|
||||
child: CustomMaterialButton(
|
||||
label: I18nText('installerView.openButton'),
|
||||
isExpanded: true,
|
||||
onPressed: () {
|
||||
model.openApp();
|
||||
model.cleanPatcher();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: !model.isInstalled,
|
||||
child: const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: !model.isInstalled,
|
||||
child: CustomMaterialButton(
|
||||
label: I18nText('installerView.installButton'),
|
||||
isExpanded: true,
|
||||
onPressed: () => model.installResult(
|
||||
context,
|
||||
false,
|
||||
Visibility(
|
||||
visible: !model.isInstalled && model.isRooted,
|
||||
child: CustomMaterialButton(
|
||||
isFilled: false,
|
||||
label:
|
||||
I18nText('installerView.installRootButton'),
|
||||
isExpanded: true,
|
||||
onPressed: () => model.installResult(
|
||||
context,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Visibility(
|
||||
visible: !model.isInstalled,
|
||||
child: const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
),
|
||||
Visibility(
|
||||
visible: !model.isInstalled,
|
||||
child: CustomMaterialButton(
|
||||
label: I18nText('installerView.installButton'),
|
||||
isExpanded: true,
|
||||
onPressed: () => model.installResult(
|
||||
context,
|
||||
false,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
onWillPop: () => model.onWillPop(context),
|
||||
|
@ -1,3 +1,4 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@ -9,15 +10,17 @@ import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:revanced_manager/models/patched_application.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/services/toast.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:wakelock/wakelock.dart';
|
||||
|
||||
class InstallerViewModel extends BaseViewModel {
|
||||
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||
final RootAPI _rootAPI = RootAPI();
|
||||
final Toast _toast = locator<Toast>();
|
||||
final PatchedApplication _app = locator<PatcherViewModel>().selectedApp!;
|
||||
final List<Patch> _patches = locator<PatcherViewModel>().selectedPatches;
|
||||
@ -28,11 +31,13 @@ class InstallerViewModel extends BaseViewModel {
|
||||
double? progress = 0.0;
|
||||
String logs = '';
|
||||
String headerLogs = '';
|
||||
bool isRooted = false;
|
||||
bool isPatching = true;
|
||||
bool isInstalled = false;
|
||||
bool hasErrors = false;
|
||||
|
||||
Future<void> initialize(BuildContext context) async {
|
||||
isRooted = await _rootAPI.isRooted();
|
||||
if (await Permission.ignoreBatteryOptimizations.isGranted) {
|
||||
try {
|
||||
FlutterBackground.initialize(
|
||||
|
@ -1,4 +1,3 @@
|
||||
import 'package:device_apps/device_apps.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
@ -6,14 +5,16 @@ import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/app/app.router.dart';
|
||||
import 'package:revanced_manager/models/patch.dart';
|
||||
import 'package:revanced_manager/models/patched_application.dart';
|
||||
import 'package:revanced_manager/services/manager_api.dart';
|
||||
import 'package:revanced_manager/services/patcher_api.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:stacked_services/stacked_services.dart';
|
||||
|
||||
@lazySingleton
|
||||
class PatcherViewModel extends BaseViewModel {
|
||||
final NavigationService _navigationService = locator<NavigationService>();
|
||||
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||
PatchedApplication? selectedApp;
|
||||
List<Patch> selectedPatches = [];
|
||||
@ -39,13 +40,12 @@ class PatcherViewModel extends BaseViewModel {
|
||||
}
|
||||
|
||||
Future<bool> isValidPatchConfig() async {
|
||||
bool needsResourcePatching =
|
||||
await _patcherAPI.needsResourcePatching(selectedPatches);
|
||||
bool needsResourcePatching = await _patcherAPI.needsResourcePatching(
|
||||
selectedPatches,
|
||||
);
|
||||
if (needsResourcePatching && selectedApp != null) {
|
||||
Application? app = await DeviceApps.getApp(selectedApp!.packageName);
|
||||
if (app != null && app.isSplit) {
|
||||
return false;
|
||||
}
|
||||
bool isSplit = await _managerAPI.isSplitApk(selectedApp!);
|
||||
return !isSplit;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -73,7 +73,12 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
||||
_query = searchQuery;
|
||||
});
|
||||
},
|
||||
onSelectAll: (value) => model.selectAllPatches(value),
|
||||
onSelectAll: (value) {
|
||||
if (value) {
|
||||
model.selectAllPatcherWarning(context);
|
||||
}
|
||||
model.selectAllPatches(value);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -1,10 +1,13 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_i18n/widgets/I18nText.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/patcher/patcher_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PatchesSelectorViewModel extends BaseViewModel {
|
||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||
@ -35,6 +38,23 @@ class PatchesSelectorViewModel extends BaseViewModel {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> selectAllPatcherWarning(BuildContext context) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (context) => AlertDialog(
|
||||
title: I18nText('patchesSelectorView.selectAllPatchesWarningTitle'),
|
||||
backgroundColor: Theme.of(context).colorScheme.secondaryContainer,
|
||||
content: I18nText('patchesSelectorView.selectAllPatchesWarningContent'),
|
||||
actions: <Widget>[
|
||||
CustomMaterialButton(
|
||||
label: I18nText('okButton'),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void selectAllPatches(bool isSelected) {
|
||||
selectedPatches.clear();
|
||||
if (isSelected) {
|
||||
|
@ -10,7 +10,7 @@ import 'package:path_provider/path_provider.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/app/app.router.dart';
|
||||
import 'package:revanced_manager/services/manager_api.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart';
|
||||
import 'package:share_extend/share_extend.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
@ -11,7 +11,7 @@ import 'package:revanced_manager/services/root_api.dart';
|
||||
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:revanced_manager/utils/string.dart';
|
||||
import 'package:stacked/stacked.dart';
|
||||
|
||||
|
@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:revanced_manager/app/app.locator.dart';
|
||||
import 'package:revanced_manager/ui/views/home/home_viewmodel.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
|
||||
|
||||
class LatestCommitCard extends StatefulWidget {
|
||||
|
@ -1,49 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomMaterialButton extends StatelessWidget {
|
||||
final Widget label;
|
||||
final bool isFilled;
|
||||
final bool isExpanded;
|
||||
final Function()? onPressed;
|
||||
|
||||
const CustomMaterialButton({
|
||||
Key? key,
|
||||
required this.label,
|
||||
this.isFilled = true,
|
||||
this.isExpanded = false,
|
||||
required this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextButton(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(
|
||||
isExpanded
|
||||
? const EdgeInsets.symmetric(horizontal: 24, vertical: 12)
|
||||
: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
shape: MaterialStateProperty.all(
|
||||
StadiumBorder(
|
||||
side: isFilled
|
||||
? BorderSide.none
|
||||
: BorderSide(
|
||||
width: 1,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
isFilled ? Theme.of(context).colorScheme.primary : Colors.transparent,
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
isFilled
|
||||
? Theme.of(context).colorScheme.surface
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: label,
|
||||
);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
|
||||
|
||||
// ignore: must_be_immutable
|
||||
@ -58,13 +58,28 @@ class _PatchItemState extends State<PatchItem> {
|
||||
children: <Widget>[
|
||||
Text(
|
||||
widget.simpleName,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.visible,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(widget.version)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.background
|
||||
.withOpacity(0.5),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Text(widget.version),
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
@ -147,7 +162,7 @@ class _PatchItemState extends State<PatchItem> {
|
||||
translationParams: {
|
||||
'packageVersion': widget.packageVersion,
|
||||
'supportedVersions':
|
||||
'\u2022 ${widget.supportedPackageVersions.join('\n\u2022 ')}',
|
||||
'\u2022 ${widget.supportedPackageVersions.reversed.join('\n\u2022 ')}',
|
||||
},
|
||||
),
|
||||
actions: <Widget>[
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_i18n/flutter_i18n.dart';
|
||||
import 'package:revanced_manager/ui/widgets/installerView/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
|
||||
import 'package:expandable/expandable.dart';
|
||||
import 'package:timeago/timeago.dart';
|
||||
|
130
lib/ui/widgets/shared/custom_material_button.dart
Normal file
130
lib/ui/widgets/shared/custom_material_button.dart
Normal file
@ -0,0 +1,130 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CustomMaterialButton extends StatelessWidget {
|
||||
final Widget label;
|
||||
final bool isFilled;
|
||||
final bool isExpanded;
|
||||
final Function()? onPressed;
|
||||
|
||||
const CustomMaterialButton({
|
||||
Key? key,
|
||||
required this.label,
|
||||
this.isFilled = true,
|
||||
this.isExpanded = false,
|
||||
required this.onPressed,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextButton(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(
|
||||
isExpanded
|
||||
? const EdgeInsets.symmetric(horizontal: 24, vertical: 12)
|
||||
: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
||||
),
|
||||
shape: MaterialStateProperty.all(
|
||||
StadiumBorder(
|
||||
side: isFilled
|
||||
? BorderSide.none
|
||||
: BorderSide(
|
||||
width: 1,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
isFilled ? Theme.of(context).colorScheme.primary : Colors.transparent,
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
isFilled
|
||||
? Theme.of(context).colorScheme.surface
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
child: label,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class TimerButton extends StatefulWidget {
|
||||
Widget label;
|
||||
bool isFilled;
|
||||
int seconds;
|
||||
final bool isRunning;
|
||||
final Function()? onTimerEnd;
|
||||
|
||||
TimerButton({
|
||||
Key? key,
|
||||
required this.seconds,
|
||||
required this.isRunning,
|
||||
required this.onTimerEnd,
|
||||
this.label = const Text(''),
|
||||
this.isFilled = true,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<TimerButton> createState() => _TimerButtonState();
|
||||
}
|
||||
|
||||
class _TimerButtonState extends State<TimerButton> {
|
||||
void timer(int seconds) {
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
if (seconds > 0) {
|
||||
setState(() {
|
||||
seconds--;
|
||||
});
|
||||
timer(seconds);
|
||||
} else {
|
||||
widget.onTimerEnd!();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
//decrement seconds
|
||||
if (widget.isRunning) {
|
||||
timer(widget.seconds);
|
||||
}
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext build) {
|
||||
return TextButton(
|
||||
style: ButtonStyle(
|
||||
shape: MaterialStateProperty.all(
|
||||
StadiumBorder(
|
||||
side: widget.isFilled
|
||||
? BorderSide.none
|
||||
: BorderSide(
|
||||
width: 1,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
widget.isFilled
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Colors.transparent,
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
widget.isFilled
|
||||
? Theme.of(context).colorScheme.surface
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
onPressed: widget.isRunning ? null : widget.onTimerEnd,
|
||||
child: Text(
|
||||
widget.isRunning ? '${widget.seconds}' : 'Install',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ homepage: https://github.com/revanced/revanced-manager
|
||||
|
||||
publish_to: 'none'
|
||||
|
||||
version: 0.0.19+19
|
||||
version: 0.0.24+24
|
||||
|
||||
environment:
|
||||
sdk: ">=2.17.5 <3.0.0"
|
||||
@ -20,6 +20,7 @@ dependencies:
|
||||
ref: revanced-manager
|
||||
device_info_plus: ^4.1.2
|
||||
dio: ^4.0.6
|
||||
dio_http2_adapter: ^2.0.0
|
||||
dio_http_cache_lts: ^0.4.1
|
||||
dynamic_color: ^1.5.4
|
||||
dynamic_themes: ^1.1.0
|
||||
|
Reference in New Issue
Block a user