mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-12 18:04:28 +01:00
feat: improve ux (#752)
* feat: restart app toast when changing sources, api url * fix: potentially fix manager stuck on black screen * feat: remove select all patches chip * feat: show all apps and recommended versions * chore(i18n): remove unused strings remove unused strings left out in7e05bca
* feat: select install type before patching * feat: update patches button (#782) * feat: update patches button * feat: show toast when force refresh * chore: don't translate "ReVanced Manager" and "ReVanced Patches" * Revert "feat: select install type before patching" This reverts commit74e0c09b54
. * feat: rename recommended patches to default patches * feat: add missing localization * feat: display restart app toast for resetting source --------- Co-authored-by: EvadeMaster <93124920+EvadeMaster@users.noreply.github.com>
This commit is contained in:
parent
0b952578d1
commit
3b677f8ae3
16 changed files with 397 additions and 167 deletions
|
@ -13,6 +13,7 @@
|
||||||
"settingsTab": "Settings"
|
"settingsTab": "Settings"
|
||||||
},
|
},
|
||||||
"homeView": {
|
"homeView": {
|
||||||
|
"refreshSuccess": "Refresh successfully",
|
||||||
"widgetTitle": "Dashboard",
|
"widgetTitle": "Dashboard",
|
||||||
"updatesSubtitle": "Updates",
|
"updatesSubtitle": "Updates",
|
||||||
"patchedSubtitle": "Patched applications",
|
"patchedSubtitle": "Patched applications",
|
||||||
|
@ -72,16 +73,15 @@
|
||||||
"viewTitle": "Select an application",
|
"viewTitle": "Select an application",
|
||||||
"searchBarHint": "Search applications",
|
"searchBarHint": "Search applications",
|
||||||
"storageButton": "Storage",
|
"storageButton": "Storage",
|
||||||
"errorMessage": "Unable to use selected application"
|
"errorMessage": "Unable to use selected application",
|
||||||
|
"downloadToast": "Download function is not available yet"
|
||||||
},
|
},
|
||||||
"patchesSelectorView": {
|
"patchesSelectorView": {
|
||||||
"viewTitle": "Select patches",
|
"viewTitle": "Select patches",
|
||||||
"searchBarHint": "Search patches",
|
"searchBarHint": "Search patches",
|
||||||
"doneButton": "Done",
|
"doneButton": "Done",
|
||||||
"recommended": "Recommended",
|
"default": "Default",
|
||||||
"recommendedTooltip": "Select all recommended patches",
|
"defaultTooltip": "Select all default patches",
|
||||||
"all": "All",
|
|
||||||
"allTooltip": "Select all patches",
|
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"noneTooltip": "Deselect all patches",
|
"noneTooltip": "Deselect all patches",
|
||||||
"loadPatchesSelection": "Load patches selection",
|
"loadPatchesSelection": "Load patches selection",
|
||||||
|
|
|
@ -3,20 +3,23 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:dio_http_cache_lts/dio_http_cache_lts.dart';
|
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
|
import 'package:native_dio_adapter/native_dio_adapter.dart';
|
||||||
import 'package:revanced_manager/models/patch.dart';
|
import 'package:revanced_manager/models/patch.dart';
|
||||||
|
|
||||||
@lazySingleton
|
@lazySingleton
|
||||||
class GithubAPI {
|
class GithubAPI {
|
||||||
late Dio _dio = Dio();
|
late Dio _dio = Dio();
|
||||||
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
|
|
||||||
final Options _cacheOptions = buildCacheOptions(
|
final _cacheOptions = CacheOptions(
|
||||||
const Duration(hours: 6),
|
store: MemCacheStore(),
|
||||||
maxStale: const Duration(days: 1),
|
maxStale: const Duration(days: 1),
|
||||||
|
priority: CachePriority.high,
|
||||||
);
|
);
|
||||||
|
|
||||||
final Map<String, String> repoAppPath = {
|
final Map<String, String> repoAppPath = {
|
||||||
'com.google.android.youtube': 'youtube',
|
'com.google.android.youtube': 'youtube',
|
||||||
'com.google.android.apps.youtube.music': 'music',
|
'com.google.android.apps.youtube.music': 'music',
|
||||||
|
@ -30,13 +33,29 @@ class GithubAPI {
|
||||||
|
|
||||||
Future<void> initialize(String repoUrl) async {
|
Future<void> initialize(String repoUrl) async {
|
||||||
try {
|
try {
|
||||||
|
if (Platform.isIOS || Platform.isMacOS || Platform.isAndroid) {
|
||||||
|
final CronetEngine androidCronetEngine = await CronetEngine.build(
|
||||||
|
userAgent: 'ReVanced Manager',
|
||||||
|
enableBrotli: true,
|
||||||
|
enableQuic: true,
|
||||||
|
);
|
||||||
|
_dio.httpClientAdapter =
|
||||||
|
NativeAdapter(androidCronetEngine: androidCronetEngine);
|
||||||
|
|
||||||
|
_dio = Dio(
|
||||||
|
BaseOptions(
|
||||||
|
baseUrl: repoUrl,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
_dio = Dio(
|
_dio = Dio(
|
||||||
BaseOptions(
|
BaseOptions(
|
||||||
baseUrl: repoUrl,
|
baseUrl: repoUrl,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
_dio.interceptors.add(_dioCacheManager.interceptor);
|
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -46,7 +65,7 @@ class GithubAPI {
|
||||||
|
|
||||||
Future<void> clearAllCache() async {
|
Future<void> clearAllCache() async {
|
||||||
try {
|
try {
|
||||||
await _dioCacheManager.clearAll();
|
await _cacheOptions.store!.clean();
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -58,7 +77,6 @@ class GithubAPI {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get(
|
final response = await _dio.get(
|
||||||
'/repos/$repoName/releases',
|
'/repos/$repoName/releases',
|
||||||
options: _cacheOptions,
|
|
||||||
);
|
);
|
||||||
return response.data[0];
|
return response.data[0];
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
|
@ -83,7 +101,6 @@ class GithubAPI {
|
||||||
'path': path,
|
'path': path,
|
||||||
'since': since.toIso8601String(),
|
'since': since.toIso8601String(),
|
||||||
},
|
},
|
||||||
options: _cacheOptions,
|
|
||||||
);
|
);
|
||||||
final List<dynamic> commits = response.data;
|
final List<dynamic> commits = response.data;
|
||||||
return commits
|
return commits
|
||||||
|
|
|
@ -29,6 +29,10 @@ class ManagerAPI {
|
||||||
String defaultIntegrationsRepo = 'revanced/revanced-integrations';
|
String defaultIntegrationsRepo = 'revanced/revanced-integrations';
|
||||||
String defaultCliRepo = 'revanced/revanced-cli';
|
String defaultCliRepo = 'revanced/revanced-cli';
|
||||||
String defaultManagerRepo = 'revanced/revanced-manager';
|
String defaultManagerRepo = 'revanced/revanced-manager';
|
||||||
|
String? patchesVersion = '';
|
||||||
|
bool isDefaultPatchesRepo() {
|
||||||
|
return getPatchesRepo() == 'revanced/revanced-patches';
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
_prefs = await SharedPreferences.getInstance();
|
_prefs = await SharedPreferences.getInstance();
|
||||||
|
@ -267,6 +271,19 @@ class ManagerAPI {
|
||||||
return packageInfo.version;
|
return packageInfo.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String?> getCurrentPatchesVersion() async {
|
||||||
|
if (isDefaultPatchesRepo()) {
|
||||||
|
patchesVersion = await getLatestPatchesVersion();
|
||||||
|
// print('Patches version: $patchesVersion');
|
||||||
|
return patchesVersion ?? '0.0.0';
|
||||||
|
} else {
|
||||||
|
// fetch from github
|
||||||
|
patchesVersion =
|
||||||
|
await _githubAPI.getLastestReleaseVersion(getPatchesRepo());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
Future<List<PatchedApplication>> getAppsToRemove(
|
Future<List<PatchedApplication>> getAppsToRemove(
|
||||||
List<PatchedApplication> patchedApps,
|
List<PatchedApplication> patchedApps,
|
||||||
) async {
|
) async {
|
||||||
|
|
|
@ -3,11 +3,11 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:dio/dio.dart';
|
import 'package:dio/dio.dart';
|
||||||
import 'package:dio_http_cache_lts/dio_http_cache_lts.dart';
|
import 'package:dio_cache_interceptor/dio_cache_interceptor.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||||
import 'package:injectable/injectable.dart';
|
import 'package:injectable/injectable.dart';
|
||||||
import 'package:native_dio_client/native_dio_client.dart';
|
import 'package:native_dio_adapter/native_dio_adapter.dart';
|
||||||
import 'package:revanced_manager/models/patch.dart';
|
import 'package:revanced_manager/models/patch.dart';
|
||||||
import 'package:revanced_manager/utils/check_for_gms.dart';
|
import 'package:revanced_manager/utils/check_for_gms.dart';
|
||||||
import 'package:timeago/timeago.dart';
|
import 'package:timeago/timeago.dart';
|
||||||
|
@ -15,10 +15,11 @@ import 'package:timeago/timeago.dart';
|
||||||
@lazySingleton
|
@lazySingleton
|
||||||
class RevancedAPI {
|
class RevancedAPI {
|
||||||
late Dio _dio = Dio();
|
late Dio _dio = Dio();
|
||||||
final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig());
|
|
||||||
final Options _cacheOptions = buildCacheOptions(
|
final _cacheOptions = CacheOptions(
|
||||||
const Duration(hours: 6),
|
store: MemCacheStore(),
|
||||||
maxStale: const Duration(days: 1),
|
maxStale: const Duration(days: 1),
|
||||||
|
priority: CachePriority.high,
|
||||||
);
|
);
|
||||||
|
|
||||||
Future<void> initialize(String apiUrl) async {
|
Future<void> initialize(String apiUrl) async {
|
||||||
|
@ -33,14 +34,25 @@ class RevancedAPI {
|
||||||
);
|
);
|
||||||
log('ReVanced API: Using default engine + $isGMSInstalled');
|
log('ReVanced API: Using default engine + $isGMSInstalled');
|
||||||
} else {
|
} else {
|
||||||
_dio = Dio(
|
if (Platform.isIOS || Platform.isMacOS || Platform.isAndroid) {
|
||||||
BaseOptions(
|
final CronetEngine androidCronetEngine = await CronetEngine.build(
|
||||||
baseUrl: apiUrl,
|
userAgent: 'ReVanced Manager',
|
||||||
),
|
enableBrotli: true,
|
||||||
)..httpClientAdapter = NativeAdapter();
|
enableQuic: true,
|
||||||
|
);
|
||||||
|
_dio.httpClientAdapter =
|
||||||
|
NativeAdapter(androidCronetEngine: androidCronetEngine);
|
||||||
|
|
||||||
|
_dio = Dio(
|
||||||
|
BaseOptions(
|
||||||
|
baseUrl: apiUrl,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
log('ReVanced API: Using CronetEngine + $isGMSInstalled');
|
log('ReVanced API: Using CronetEngine + $isGMSInstalled');
|
||||||
}
|
}
|
||||||
_dio.interceptors.add(_dioCacheManager.interceptor);
|
_dio.interceptors.add(DioCacheInterceptor(options: _cacheOptions));
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -50,7 +62,7 @@ class RevancedAPI {
|
||||||
|
|
||||||
Future<void> clearAllCache() async {
|
Future<void> clearAllCache() async {
|
||||||
try {
|
try {
|
||||||
await _dioCacheManager.clearAll();
|
await _cacheOptions.store!.clean();
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
if (kDebugMode) {
|
if (kDebugMode) {
|
||||||
print(e);
|
print(e);
|
||||||
|
@ -61,7 +73,7 @@ class RevancedAPI {
|
||||||
Future<Map<String, List<dynamic>>> getContributors() async {
|
Future<Map<String, List<dynamic>>> getContributors() async {
|
||||||
final Map<String, List<dynamic>> contributors = {};
|
final Map<String, List<dynamic>> contributors = {};
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get('/contributors', options: _cacheOptions);
|
final response = await _dio.get('/contributors');
|
||||||
final List<dynamic> repositories = response.data['repositories'];
|
final List<dynamic> repositories = response.data['repositories'];
|
||||||
for (final Map<String, dynamic> repo in repositories) {
|
for (final Map<String, dynamic> repo in repositories) {
|
||||||
final String name = repo['name'];
|
final String name = repo['name'];
|
||||||
|
@ -78,7 +90,7 @@ class RevancedAPI {
|
||||||
|
|
||||||
Future<List<Patch>> getPatches() async {
|
Future<List<Patch>> getPatches() async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get('/patches', options: _cacheOptions);
|
final response = await _dio.get('/patches');
|
||||||
final List<dynamic> patches = response.data;
|
final List<dynamic> patches = response.data;
|
||||||
return patches.map((patch) => Patch.fromJson(patch)).toList();
|
return patches.map((patch) => Patch.fromJson(patch)).toList();
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
|
@ -94,7 +106,7 @@ class RevancedAPI {
|
||||||
String repoName,
|
String repoName,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
final response = await _dio.get('/tools', options: _cacheOptions);
|
final response = await _dio.get('/tools');
|
||||||
final List<dynamic> tools = response.data['tools'];
|
final List<dynamic> tools = response.data['tools'];
|
||||||
return tools.firstWhereOrNull(
|
return tools.firstWhereOrNull(
|
||||||
(t) =>
|
(t) =>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter_i18n/flutter_i18n.dart';
|
||||||
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
import 'package:revanced_manager/ui/views/app_selector/app_selector_viewmodel.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart';
|
import 'package:revanced_manager/ui/widgets/appSelectorView/app_skeleton_loader.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart';
|
import 'package:revanced_manager/ui/widgets/appSelectorView/installed_app_item.dart';
|
||||||
|
import 'package:revanced_manager/ui/widgets/appSelectorView/not_installed_app_item.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/shared/search_bar.dart';
|
import 'package:revanced_manager/ui/widgets/shared/search_bar.dart';
|
||||||
import 'package:stacked/stacked.dart' hide SkeletonLoader;
|
import 'package:stacked/stacked.dart' hide SkeletonLoader;
|
||||||
|
|
||||||
|
@ -76,7 +77,16 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
||||||
SliverToBoxAdapter(
|
SliverToBoxAdapter(
|
||||||
child: model.noApps
|
child: model.noApps
|
||||||
? Center(
|
? Center(
|
||||||
child: I18nText('appSelectorCard.noAppsLabel'),
|
child: I18nText(
|
||||||
|
'appSelectorView.noApps',
|
||||||
|
child: Text(
|
||||||
|
'',
|
||||||
|
style: TextStyle(
|
||||||
|
color:
|
||||||
|
Theme.of(context).textTheme.titleLarge!.color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
: model.apps.isEmpty
|
: model.apps.isEmpty
|
||||||
? const AppSkeletonLoader()
|
? const AppSkeletonLoader()
|
||||||
|
@ -84,22 +94,42 @@ class _AppSelectorViewState extends State<AppSelectorView> {
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12.0)
|
padding: const EdgeInsets.symmetric(horizontal: 12.0)
|
||||||
.copyWith(bottom: 80),
|
.copyWith(bottom: 80),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: model
|
children: [
|
||||||
.getFilteredApps(_query)
|
...model
|
||||||
.map(
|
.getFilteredApps(_query)
|
||||||
(app) => InstalledAppItem(
|
.map(
|
||||||
name: app.appName,
|
(app) => InstalledAppItem(
|
||||||
pkgName: app.packageName,
|
name: app.appName,
|
||||||
icon: app.icon,
|
pkgName: app.packageName,
|
||||||
patchesCount:
|
icon: app.icon,
|
||||||
model.patchesCount(app.packageName),
|
patchesCount:
|
||||||
onTap: () {
|
model.patchesCount(app.packageName),
|
||||||
model.selectApp(app);
|
recommendedVersion:
|
||||||
Navigator.of(context).pop();
|
model.getRecommendedVersion(
|
||||||
},
|
app.packageName,
|
||||||
),
|
),
|
||||||
)
|
onTap: () {
|
||||||
.toList(),
|
model.selectApp(app);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
...model
|
||||||
|
.getFilteredAppsNames(_query)
|
||||||
|
.map(
|
||||||
|
(app) => NotInstalledAppItem(
|
||||||
|
name: app,
|
||||||
|
patchesCount: model.patchesCount(app),
|
||||||
|
recommendedVersion:
|
||||||
|
model.getRecommendedVersion(app),
|
||||||
|
onTap: () {
|
||||||
|
model.showDownloadToast();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -5,9 +5,11 @@ import 'package:file_picker/file_picker.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
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/models/patch.dart';
|
||||||
import 'package:revanced_manager/models/patched_application.dart';
|
import 'package:revanced_manager/models/patched_application.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/services/revanced_api.dart';
|
||||||
import 'package:revanced_manager/services/toast.dart';
|
import 'package:revanced_manager/services/toast.dart';
|
||||||
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart';
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
@ -15,14 +17,20 @@ import 'package:stacked/stacked.dart';
|
||||||
class AppSelectorViewModel extends BaseViewModel {
|
class AppSelectorViewModel extends BaseViewModel {
|
||||||
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
final PatcherAPI _patcherAPI = locator<PatcherAPI>();
|
||||||
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||||
|
final RevancedAPI _revancedAPI = locator<RevancedAPI>();
|
||||||
final Toast _toast = locator<Toast>();
|
final Toast _toast = locator<Toast>();
|
||||||
final List<ApplicationWithIcon> apps = [];
|
final List<ApplicationWithIcon> apps = [];
|
||||||
|
List<String> allApps = [];
|
||||||
bool noApps = false;
|
bool noApps = false;
|
||||||
int patchesCount(String packageName) {
|
int patchesCount(String packageName) {
|
||||||
return _patcherAPI.getFilteredPatches(packageName).length;
|
return _patcherAPI.getFilteredPatches(packageName).length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Patch> patches = [];
|
||||||
|
|
||||||
Future<void> initialize() async {
|
Future<void> initialize() async {
|
||||||
|
patches = await _revancedAPI.getPatches();
|
||||||
|
|
||||||
apps.addAll(
|
apps.addAll(
|
||||||
await _patcherAPI
|
await _patcherAPI
|
||||||
.getFilteredInstalledApps(_managerAPI.areUniversalPatchesEnabled()),
|
.getFilteredInstalledApps(_managerAPI.areUniversalPatchesEnabled()),
|
||||||
|
@ -34,9 +42,25 @@ class AppSelectorViewModel extends BaseViewModel {
|
||||||
.compareTo(_patcherAPI.getFilteredPatches(a.packageName).length),
|
.compareTo(_patcherAPI.getFilteredPatches(a.packageName).length),
|
||||||
);
|
);
|
||||||
noApps = apps.isEmpty;
|
noApps = apps.isEmpty;
|
||||||
|
getAllApps();
|
||||||
|
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> getAllApps() {
|
||||||
|
allApps = patches
|
||||||
|
.expand((e) => e.compatiblePackages.map((p) => p.name))
|
||||||
|
.toSet()
|
||||||
|
.where((name) => !apps.any((app) => app.packageName == name))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return allApps;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getRecommendedVersion(String packageName) {
|
||||||
|
return _patcherAPI.getRecommendedVersion(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> selectApp(ApplicationWithIcon application) async {
|
Future<void> selectApp(ApplicationWithIcon application) async {
|
||||||
locator<PatcherViewModel>().selectedApp = PatchedApplication(
|
locator<PatcherViewModel>().selectedApp = PatchedApplication(
|
||||||
name: application.appName,
|
name: application.appName,
|
||||||
|
@ -105,4 +129,18 @@ class AppSelectorViewModel extends BaseViewModel {
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> getFilteredAppsNames(String query) {
|
||||||
|
return allApps
|
||||||
|
.where(
|
||||||
|
(app) =>
|
||||||
|
query.isEmpty ||
|
||||||
|
query.length < 2 ||
|
||||||
|
app.toLowerCase().contains(query.toLowerCase()),
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void showDownloadToast() =>
|
||||||
|
_toast.showBottom('appSelectorView.downloadToast');
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,9 @@ class HomeView extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
LatestCommitCard(
|
LatestCommitCard(
|
||||||
onPressed: () =>
|
onPressedManager: () =>
|
||||||
model.showUpdateConfirmationDialog(context),
|
model.showUpdateConfirmationDialog(context),
|
||||||
|
onPressedPatches: () => model.forceRefresh(context),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 23),
|
const SizedBox(height: 23),
|
||||||
I18nText(
|
I18nText(
|
||||||
|
|
|
@ -105,6 +105,26 @@ class HomeViewModel extends BaseViewModel {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> hasPatchesUpdates() async {
|
||||||
|
final String? latestVersion = await _managerAPI.getLatestPatchesVersion();
|
||||||
|
final String? currentVersion = await _managerAPI.getCurrentPatchesVersion();
|
||||||
|
if (latestVersion != null) {
|
||||||
|
try {
|
||||||
|
final int latestVersionInt =
|
||||||
|
int.parse(latestVersion.replaceAll(RegExp('[^0-9]'), ''));
|
||||||
|
final int currentVersionInt =
|
||||||
|
int.parse(currentVersion!.replaceAll(RegExp('[^0-9]'), ''));
|
||||||
|
return latestVersionInt > currentVersionInt;
|
||||||
|
} on Exception catch (e) {
|
||||||
|
if (kDebugMode) {
|
||||||
|
print(e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> updateManager(BuildContext context) async {
|
Future<void> updateManager(BuildContext context) async {
|
||||||
try {
|
try {
|
||||||
_toast.showBottom('homeView.downloadingMessage');
|
_toast.showBottom('homeView.downloadingMessage');
|
||||||
|
@ -180,6 +200,7 @@ class HomeViewModel extends BaseViewModel {
|
||||||
_lastUpdate!.difference(DateTime.now()).inSeconds > 2) {
|
_lastUpdate!.difference(DateTime.now()).inSeconds > 2) {
|
||||||
_managerAPI.clearAllData();
|
_managerAPI.clearAllData();
|
||||||
}
|
}
|
||||||
|
_toast.showBottom('homeView.refreshSuccess');
|
||||||
initialize(context);
|
initialize(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,25 +135,13 @@ class _PatchesSelectorViewState extends State<PatchesSelectorView> {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
ActionChip(
|
ActionChip(
|
||||||
label: I18nText('patchesSelectorView.recommended'),
|
label: I18nText('patchesSelectorView.default'),
|
||||||
tooltip: FlutterI18n.translate(
|
tooltip: FlutterI18n.translate(
|
||||||
context,
|
context,
|
||||||
'patchesSelectorView.recommendedTooltip',
|
'patchesSelectorView.defaultTooltip',
|
||||||
),
|
),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
model.selectRecommendedPatches();
|
model.selectDefaultPatches();
|
||||||
},
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
ActionChip(
|
|
||||||
label: I18nText('patchesSelectorView.all'),
|
|
||||||
tooltip: FlutterI18n.translate(
|
|
||||||
context,
|
|
||||||
'patchesSelectorView.allTooltip',
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
model.selectAllPatcherWarning(context);
|
|
||||||
model.selectAllPatches(true);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_i18n/widgets/I18nText.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';
|
||||||
|
@ -9,7 +7,6 @@ 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/toast.dart';
|
import 'package:revanced_manager/services/toast.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/shared/custom_material_button.dart';
|
|
||||||
import 'package:stacked/stacked.dart';
|
import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
class PatchesSelectorViewModel extends BaseViewModel {
|
class PatchesSelectorViewModel extends BaseViewModel {
|
||||||
|
@ -50,39 +47,7 @@ class PatchesSelectorViewModel extends BaseViewModel {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> selectAllPatcherWarning(BuildContext context) {
|
void selectDefaultPatches() {
|
||||||
return showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => AlertDialog(
|
|
||||||
title: I18nText('warning'),
|
|
||||||
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 && _managerAPI.areExperimentalPatchesEnabled() == false) {
|
|
||||||
selectedPatches
|
|
||||||
.addAll(patches.where((element) => isPatchSupported(element)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSelected && _managerAPI.areExperimentalPatchesEnabled()) {
|
|
||||||
selectedPatches.addAll(patches);
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
void selectRecommendedPatches() {
|
|
||||||
selectedPatches.clear();
|
selectedPatches.clear();
|
||||||
|
|
||||||
if (_managerAPI.areExperimentalPatchesEnabled() == false) {
|
if (_managerAPI.areExperimentalPatchesEnabled() == false) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ 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/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/services/manager_api.dart';
|
import 'package:revanced_manager/services/manager_api.dart';
|
||||||
|
import 'package:revanced_manager/services/toast.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart';
|
import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart';
|
import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||||
|
@ -11,6 +12,7 @@ import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
class SManageApiUrl extends BaseViewModel {
|
class SManageApiUrl extends BaseViewModel {
|
||||||
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||||
|
final Toast _toast = locator<Toast>();
|
||||||
|
|
||||||
final TextEditingController _apiUrlController = TextEditingController();
|
final TextEditingController _apiUrlController = TextEditingController();
|
||||||
|
|
||||||
|
@ -90,7 +92,7 @@ class SManageApiUrl extends BaseViewModel {
|
||||||
label: I18nText('yesButton'),
|
label: I18nText('yesButton'),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_managerAPI.setApiUrl('');
|
_managerAPI.setApiUrl('');
|
||||||
Navigator.of(context).pop();
|
_toast.showBottom('settingsView.restartAppForChanges');
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -4,6 +4,7 @@ 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/app/app.locator.dart';
|
||||||
import 'package:revanced_manager/services/manager_api.dart';
|
import 'package:revanced_manager/services/manager_api.dart';
|
||||||
|
import 'package:revanced_manager/services/toast.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart';
|
import 'package:revanced_manager/ui/widgets/settingsView/custom_text_field.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart';
|
import 'package:revanced_manager/ui/widgets/settingsView/settings_tile_dialog.dart';
|
||||||
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||||
|
@ -11,6 +12,7 @@ import 'package:stacked/stacked.dart';
|
||||||
|
|
||||||
class SManageSources extends BaseViewModel {
|
class SManageSources extends BaseViewModel {
|
||||||
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
final ManagerAPI _managerAPI = locator<ManagerAPI>();
|
||||||
|
final Toast _toast = locator<Toast>();
|
||||||
|
|
||||||
final TextEditingController _hostSourceController = TextEditingController();
|
final TextEditingController _hostSourceController = TextEditingController();
|
||||||
final TextEditingController _orgPatSourceController = TextEditingController();
|
final TextEditingController _orgPatSourceController = TextEditingController();
|
||||||
|
@ -124,6 +126,7 @@ class SManageSources extends BaseViewModel {
|
||||||
_managerAPI.setIntegrationsRepo(
|
_managerAPI.setIntegrationsRepo(
|
||||||
'${_orgIntSourceController.text}/${_intSourceController.text}',
|
'${_orgIntSourceController.text}/${_intSourceController.text}',
|
||||||
);
|
);
|
||||||
|
_toast.showBottom('settingsView.restartAppForChanges');
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -151,6 +154,7 @@ class SManageSources extends BaseViewModel {
|
||||||
_managerAPI.setRepoUrl('');
|
_managerAPI.setRepoUrl('');
|
||||||
_managerAPI.setPatchesRepo('');
|
_managerAPI.setPatchesRepo('');
|
||||||
_managerAPI.setIntegrationsRepo('');
|
_managerAPI.setIntegrationsRepo('');
|
||||||
|
_toast.showBottom('settingsView.restartAppForChanges');
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,12 +9,14 @@ class InstalledAppItem extends StatefulWidget {
|
||||||
required this.pkgName,
|
required this.pkgName,
|
||||||
required this.icon,
|
required this.icon,
|
||||||
required this.patchesCount,
|
required this.patchesCount,
|
||||||
|
required this.recommendedVersion,
|
||||||
this.onTap,
|
this.onTap,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
final String name;
|
final String name;
|
||||||
final String pkgName;
|
final String pkgName;
|
||||||
final Uint8List icon;
|
final Uint8List icon;
|
||||||
final int patchesCount;
|
final int patchesCount;
|
||||||
|
final String recommendedVersion;
|
||||||
final Function()? onTap;
|
final Function()? onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -46,31 +48,35 @@ class _InstalledAppItemState extends State<InstalledAppItem> {
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
widget.name,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.visible,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(widget.pkgName),
|
||||||
Row(
|
Row(
|
||||||
children: <Widget>[
|
children: [
|
||||||
Text(
|
Text(
|
||||||
widget.name,
|
widget.recommendedVersion.isEmpty
|
||||||
maxLines: 2,
|
? 'All versions'
|
||||||
overflow: TextOverflow.visible,
|
: widget.recommendedVersion,
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 4),
|
||||||
Text(
|
Text(
|
||||||
widget.patchesCount == 1
|
widget.patchesCount == 1
|
||||||
? '${widget.patchesCount} patch'
|
? '• ${widget.patchesCount} patch'
|
||||||
: '${widget.patchesCount} patches',
|
: '• ${widget.patchesCount} patches',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 8,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
|
||||||
Text(widget.pkgName),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
85
lib/ui/widgets/appSelectorView/not_installed_app_item.dart
Normal file
85
lib/ui/widgets/appSelectorView/not_installed_app_item.dart
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:revanced_manager/ui/widgets/shared/custom_card.dart';
|
||||||
|
|
||||||
|
class NotInstalledAppItem extends StatefulWidget {
|
||||||
|
const NotInstalledAppItem({
|
||||||
|
Key? key,
|
||||||
|
required this.name,
|
||||||
|
required this.patchesCount,
|
||||||
|
required this.recommendedVersion,
|
||||||
|
this.onTap,
|
||||||
|
}) : super(key: key);
|
||||||
|
final String name;
|
||||||
|
final int patchesCount;
|
||||||
|
final String recommendedVersion;
|
||||||
|
final Function()? onTap;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<NotInstalledAppItem> createState() => _NotInstalledAppItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NotInstalledAppItem extends State<NotInstalledAppItem> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||||
|
child: CustomCard(
|
||||||
|
onTap: widget.onTap,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: <Widget>[
|
||||||
|
Container(
|
||||||
|
height: 48,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: const CircleAvatar(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
child: Icon(
|
||||||
|
Icons.square_rounded,
|
||||||
|
color: Colors.grey,
|
||||||
|
size: 44,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
widget.name,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
const Text('App not installed.'),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.recommendedVersion.isEmpty
|
||||||
|
? 'All versions'
|
||||||
|
: widget.recommendedVersion,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
widget.patchesCount == 1
|
||||||
|
? '• ${widget.patchesCount} patch'
|
||||||
|
: '• ${widget.patchesCount} patches',
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,9 +8,11 @@ import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart';
|
||||||
class LatestCommitCard extends StatefulWidget {
|
class LatestCommitCard extends StatefulWidget {
|
||||||
const LatestCommitCard({
|
const LatestCommitCard({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.onPressed,
|
required this.onPressedManager,
|
||||||
|
required this.onPressedPatches,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
final Function() onPressed;
|
final Function() onPressedManager;
|
||||||
|
final Function() onPressedPatches;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<LatestCommitCard> createState() => _LatestCommitCardState();
|
State<LatestCommitCard> createState() => _LatestCommitCardState();
|
||||||
|
@ -21,66 +23,109 @@ class _LatestCommitCardState extends State<LatestCommitCard> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CustomCard(
|
return Column(
|
||||||
child: Row(
|
children: [
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
// ReVanced Manager
|
||||||
children: <Widget>[
|
CustomCard(
|
||||||
Column(
|
child: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Row(
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
I18nText('latestCommitCard.patcherLabel'),
|
Row(
|
||||||
FutureBuilder<String?>(
|
children: const <Widget>[
|
||||||
future: model.getLatestPatcherReleaseTime(),
|
Text('ReVanced Manager'),
|
||||||
builder: (context, snapshot) => Text(
|
],
|
||||||
snapshot.hasData && snapshot.data!.isNotEmpty
|
),
|
||||||
? FlutterI18n.translate(
|
const SizedBox(height: 4),
|
||||||
context,
|
Row(
|
||||||
'latestCommitCard.timeagoLabel',
|
children: <Widget>[
|
||||||
translationParams: {'time': snapshot.data!},
|
FutureBuilder<String?>(
|
||||||
)
|
future: model.getLatestManagerReleaseTime(),
|
||||||
: FlutterI18n.translate(
|
builder: (context, snapshot) =>
|
||||||
context,
|
snapshot.hasData && snapshot.data!.isNotEmpty
|
||||||
'latestCommitCard.loadingLabel',
|
? I18nText(
|
||||||
),
|
'latestCommitCard.timeagoLabel',
|
||||||
),
|
translationParams: {'time': snapshot.data!},
|
||||||
|
)
|
||||||
|
: I18nText('latestCommitCard.loadingLabel'),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
FutureBuilder<bool>(
|
||||||
Row(
|
future: locator<HomeViewModel>().hasManagerUpdates(),
|
||||||
children: <Widget>[
|
initialData: false,
|
||||||
I18nText('latestCommitCard.managerLabel'),
|
builder: (context, snapshot) => Opacity(
|
||||||
FutureBuilder<String?>(
|
opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.25,
|
||||||
future: model.getLatestManagerReleaseTime(),
|
child: CustomMaterialButton(
|
||||||
builder: (context, snapshot) =>
|
label: I18nText('updateButton'),
|
||||||
snapshot.hasData && snapshot.data!.isNotEmpty
|
onPressed: snapshot.hasData && snapshot.data!
|
||||||
? I18nText(
|
? widget.onPressedManager
|
||||||
'latestCommitCard.timeagoLabel',
|
: () => {},
|
||||||
translationParams: {'time': snapshot.data!},
|
|
||||||
)
|
|
||||||
: I18nText('latestCommitCard.loadingLabel'),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
FutureBuilder<bool>(
|
),
|
||||||
future: locator<HomeViewModel>().hasManagerUpdates(),
|
|
||||||
initialData: false,
|
const SizedBox(height: 16),
|
||||||
builder: (context, snapshot) => Opacity(
|
|
||||||
opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.25,
|
// ReVanced Patches
|
||||||
child: CustomMaterialButton(
|
CustomCard(
|
||||||
label: I18nText('latestCommitCard.updateButton'),
|
child: Row(
|
||||||
onPressed: snapshot.hasData && snapshot.data!
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
? widget.onPressed
|
children: <Widget>[
|
||||||
: () => {},
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Row(
|
||||||
|
children: const <Widget>[
|
||||||
|
Text('ReVanced Patches'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
FutureBuilder<String?>(
|
||||||
|
future: model.getLatestPatcherReleaseTime(),
|
||||||
|
builder: (context, snapshot) => Text(
|
||||||
|
snapshot.hasData && snapshot.data!.isNotEmpty
|
||||||
|
? FlutterI18n.translate(
|
||||||
|
context,
|
||||||
|
'latestCommitCard.timeagoLabel',
|
||||||
|
translationParams: {'time': snapshot.data!},
|
||||||
|
)
|
||||||
|
: FlutterI18n.translate(
|
||||||
|
context,
|
||||||
|
'latestCommitCard.loadingLabel',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
FutureBuilder<bool>(
|
||||||
|
future: locator<HomeViewModel>().hasPatchesUpdates(),
|
||||||
|
initialData: false,
|
||||||
|
builder: (context, snapshot) => Opacity(
|
||||||
|
opacity: snapshot.hasData && snapshot.data! ? 1.0 : 0.25,
|
||||||
|
child: CustomMaterialButton(
|
||||||
|
label: I18nText('updateButton'),
|
||||||
|
onPressed: snapshot.hasData && snapshot.data!
|
||||||
|
? widget.onPressedPatches
|
||||||
|
: () => {},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,7 @@ dependencies:
|
||||||
url: https://github.com/ponces/flutter_plugin_device_apps
|
url: https://github.com/ponces/flutter_plugin_device_apps
|
||||||
ref: revanced-manager
|
ref: revanced-manager
|
||||||
device_info_plus: ^4.1.2
|
device_info_plus: ^4.1.2
|
||||||
dio: ^4.0.6
|
dio: ^5.0.0
|
||||||
dio_brotli_transformer: ^1.0.1
|
|
||||||
dio_http_cache_lts: ^0.4.1
|
|
||||||
dynamic_color: ^1.5.4
|
dynamic_color: ^1.5.4
|
||||||
dynamic_themes: ^1.1.0
|
dynamic_themes: ^1.1.0
|
||||||
expandable: ^5.0.1
|
expandable: ^5.0.1
|
||||||
|
@ -52,7 +50,7 @@ dependencies:
|
||||||
git:
|
git:
|
||||||
url: https://github.com/SuaMusica/logcat
|
url: https://github.com/SuaMusica/logcat
|
||||||
ref: feature/nullSafe
|
ref: feature/nullSafe
|
||||||
native_dio_client: ^0.0.1-dev+1
|
native_dio_adapter: ^0.1.0
|
||||||
package_info_plus: ^1.4.3+1
|
package_info_plus: ^1.4.3+1
|
||||||
path_provider: ^2.0.11
|
path_provider: ^2.0.11
|
||||||
permission_handler: ^10.0.0
|
permission_handler: ^10.0.0
|
||||||
|
@ -75,6 +73,7 @@ dependencies:
|
||||||
flutter_dotenv: ^5.0.2
|
flutter_dotenv: ^5.0.2
|
||||||
pub_release: ^8.0.3
|
pub_release: ^8.0.3
|
||||||
flutter_markdown: ^0.6.13
|
flutter_markdown: ^0.6.13
|
||||||
|
dio_cache_interceptor: ^3.4.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
json_serializable: ^6.3.1
|
json_serializable: ^6.3.1
|
||||||
|
|
Loading…
Reference in a new issue