From 595ecb7660d5eba695bf145eefb5f2ec8a44b54d Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Sun, 11 Sep 2022 02:01:06 +0100 Subject: [PATCH 01/12] feat: Add ReVanced API and implement cache on it and on Github API --- lib/services/github_api.dart | 145 ++++++++++------- lib/services/manager_api.dart | 152 +++++++++--------- lib/services/patcher_api.dart | 22 ++- lib/services/revanced_api.dart | 138 ++++++++++++++++ .../contributors/contributors_viewmodel.dart | 36 ++--- lib/ui/views/home/home_view.dart | 127 ++++++++------- lib/ui/views/home/home_viewmodel.dart | 28 +++- .../views/installer/installer_viewmodel.dart | 16 +- .../patches_selector_viewmodel.dart | 13 +- .../appInfoView/app_info_viewmodel.dart | 3 +- .../appSelectorView/app_skeleton_loader.dart | 4 +- .../contributorsView/contributors_card.dart | 7 +- .../widgets/homeView/latest_commit_card.dart | 17 +- lib/ui/widgets/shared/application_item.dart | 2 +- pubspec.yaml | 4 +- 15 files changed, 454 insertions(+), 260 deletions(-) create mode 100644 lib/services/revanced_api.dart diff --git a/lib/services/github_api.dart b/lib/services/github_api.dart index 81cfc66e..5ede1729 100644 --- a/lib/services/github_api.dart +++ b/lib/services/github_api.dart @@ -1,11 +1,21 @@ +import 'dart:convert'; import 'dart:io'; +// ignore: depend_on_referenced_packages +import 'package:collection/collection.dart'; +import 'package:dio/dio.dart'; +import 'package:dio_http_cache_lts/dio_http_cache_lts.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; -import 'package:github/github.dart'; -import 'package:timeago/timeago.dart'; +import 'package:revanced_manager/models/patch.dart'; class GithubAPI { - final GitHub _github = GitHub(); - + final String apiUrl = 'https://api.github.com'; + final Dio _dio = Dio(); + final DioCacheManager _dioCacheManager = DioCacheManager( + CacheConfig( + defaultMaxAge: const Duration(hours: 1), + defaultMaxStale: const Duration(days: 7), + ), + ); final Map repoAppPath = { 'com.google.android.youtube': 'youtube', 'com.google.android.apps.youtube.music': 'music', @@ -16,31 +26,78 @@ class GithubAPI { 'com.garzotto.pflotsh.ecmwf_a': 'ecmwf', }; - Future latestReleaseVersion(String repoName) async { - try { - var latestRelease = await _github.repositories.getLatestRelease( - RepositorySlug.full(repoName), - ); - return latestRelease.tagName; - } on Exception { - return null; - } + void initialize() { + _dio.interceptors.add(_dioCacheManager.interceptor); } - Future latestReleaseFile(String extension, String repoName) async { + Future clearAllCache() async { + await _dioCacheManager.clearAll(); + } + + Future?> _getLatestRelease(String repoName) async { try { - var latestRelease = await _github.repositories.getLatestRelease( - RepositorySlug.full(repoName), + var response = await _dio.get( + '$apiUrl/repos/$repoName/releases/latest', + options: buildCacheOptions(const Duration(hours: 1)), ); - String? url = latestRelease.assets - ?.firstWhere((asset) => - asset.name != null && - asset.name!.endsWith(extension) && - !asset.name!.contains('-sources') && - !asset.name!.contains('-javadoc')) - .browserDownloadUrl; - if (url != null) { - return await DefaultCacheManager().getSingleFile(url); + if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { + print('1 - From cache'); + } else { + print('1 - From net'); + } + return response.data; + } on Exception { + // ignore + } + return null; + } + + Future> getCommits( + String packageName, + String repoName, + DateTime since, + ) async { + String path = + 'src/main/kotlin/app/revanced/patches/${repoAppPath[packageName]}'; + try { + var response = await _dio.get( + '$apiUrl/repos/$repoName/commits', + queryParameters: { + 'path': path, + 'per_page': 3, + 'since': since.toIso8601String(), + }, + options: buildCacheOptions(const Duration(hours: 1)), + ); + if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { + print('2 - From cache'); + } else { + print('2 - From net'); + } + List commits = response.data; + return commits + .map((commit) => + (commit['commit']['message'] as String).split('\n')[0]) + .toList(); + } on Exception { + // ignore + } + return List.empty(); + } + + Future getLatestReleaseFile(String extension, String repoName) async { + try { + Map? release = await _getLatestRelease(repoName); + if (release != null) { + Map? asset = + (release['assets'] as List).firstWhereOrNull( + (asset) => (asset['name'] as String).endsWith(extension), + ); + if (asset != null) { + return await DefaultCacheManager().getSingleFile( + asset['browser_download_url'], + ); + } } } on Exception { return null; @@ -48,37 +105,17 @@ class GithubAPI { return null; } - Future latestCommitTime(String repoName) async { + Future> getPatches(String repoName) async { + List patches = []; try { - var repo = await _github.repositories.getRepository( - RepositorySlug.full(repoName), - ); - return repo.pushedAt != null - ? format(repo.pushedAt!, locale: 'en_short') - : ''; + File? f = await getLatestReleaseFile('.json', repoName); + if (f != null) { + List list = jsonDecode(f.readAsStringSync()); + patches = list.map((patch) => Patch.fromJson(patch)).toList(); + } } on Exception { - return ''; + // ignore } - } - - Future> getContributors(String repoName) async { - return await (_github.repositories.listContributors( - RepositorySlug.full(repoName), - )).toList(); - } - - Future> getCommits( - String packageName, - String repoName, - ) async { - String path = - 'src/main/kotlin/app/revanced/patches/${repoAppPath[packageName]}'; - return await (PaginationHelper(_github) - .objects, RepositoryCommit>( - 'GET', - '/repos/$repoName/commits', - (i) => RepositoryCommit.fromJson(i), - params: {'path': path}, - )).toList(); + return patches; } } diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index ae7ede71..c429f18c 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -1,16 +1,18 @@ import 'dart:convert'; import 'dart:io'; import 'package:device_apps/device_apps.dart'; -import 'package:github/github.dart'; import 'package:injectable/injectable.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/github_api.dart'; +import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/services/root_api.dart'; import 'package:shared_preferences/shared_preferences.dart'; @lazySingleton class ManagerAPI { + final RevancedAPI _revancedAPI = RevancedAPI(); final GithubAPI _githubAPI = GithubAPI(); final RootAPI _rootAPI = RootAPI(); final String patcherRepo = 'revanced-patcher'; @@ -26,10 +28,6 @@ class ManagerAPI { _prefs = await SharedPreferences.getInstance(); } - String getPatcherRepo() { - return defaultPatcherRepo; - } - String getPatchesRepo() { return _prefs.getString('patchesRepo') ?? defaultPatchesRepo; } @@ -52,46 +50,6 @@ class ManagerAPI { await _prefs.setString('integrationsRepo', value); } - String getCliRepo() { - return defaultCliRepo; - } - - String getManagerRepo() { - return _prefs.getString('managerRepo') ?? defaultManagerRepo; - } - - Future setManagerRepo(String value) async { - if (value.isEmpty || value.startsWith('/') || value.endsWith('/')) { - value = defaultManagerRepo; - } - await _prefs.setString('managerRepo', value); - } - - Future downloadPatches(String extension) async { - return await _githubAPI.latestReleaseFile(extension, getPatchesRepo()); - } - - Future downloadIntegrations(String extension) async { - return await _githubAPI.latestReleaseFile(extension, getIntegrationsRepo()); - } - - Future downloadManager(String extension) async { - return await _githubAPI.latestReleaseFile(extension, getManagerRepo()); - } - - Future getLatestPatchesVersion() async { - return await _githubAPI.latestReleaseVersion(getPatchesRepo()); - } - - Future getLatestManagerVersion() async { - return await _githubAPI.latestReleaseVersion(getManagerRepo()); - } - - Future getCurrentManagerVersion() async { - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - return packageInfo.version; - } - bool getUseDynamicTheme() { return _prefs.getBool('useDynamicTheme') ?? false; } @@ -110,9 +68,7 @@ class ManagerAPI { List getPatchedApps() { List apps = _prefs.getStringList('patchedApps') ?? []; - return apps - .map((a) => PatchedApplication.fromJson(json.decode(a))) - .toList(); + return apps.map((a) => PatchedApplication.fromJson(jsonDecode(a))).toList(); } Future setPatchedApps(List patchedApps) async { @@ -143,6 +99,71 @@ class ManagerAPI { await setPatchedApps(patchedApps); } + void clearAllData() { + _revancedAPI.clearAllCache(); + _githubAPI.clearAllCache(); + } + + Future>> getContributors() async { + return await _revancedAPI.getContributors(); + } + + Future> getPatches() async { + if (getPatchesRepo() == defaultPatchesRepo) { + return await _revancedAPI.getPatches(); + } else { + return await _githubAPI.getPatches(getPatchesRepo()); + } + } + + Future downloadPatches() async { + String repoName = getPatchesRepo(); + if (repoName == defaultPatchesRepo) { + return await _revancedAPI.getLatestReleaseFile( + '.jar', + defaultPatchesRepo, + ); + } else { + return await _githubAPI.getLatestReleaseFile('.jar', repoName); + } + } + + Future downloadIntegrations() async { + String repoName = getIntegrationsRepo(); + if (repoName == defaultIntegrationsRepo) { + return await _revancedAPI.getLatestReleaseFile( + '.apk', + defaultIntegrationsRepo, + ); + } else { + return await _githubAPI.getLatestReleaseFile('.apk', repoName); + } + } + + Future downloadManager() async { + return await _revancedAPI.getLatestReleaseFile('.apk', defaultManagerRepo); + } + + Future getLatestPatcherReleaseTime() async { + return await _revancedAPI.getLatestReleaseTime('.gz', defaultPatcherRepo); + } + + Future getLatestManagerReleaseTime() async { + return await _revancedAPI.getLatestReleaseTime('.apk', defaultManagerRepo); + } + + Future getLatestManagerVersion() async { + return await _revancedAPI.getLatestReleaseVersion( + '.apk', + defaultManagerRepo, + ); + } + + Future getCurrentManagerVersion() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + return packageInfo.version; + } + Future reAssessSavedApps() async { List patchedApps = getPatchedApps(); List toRemove = []; @@ -183,40 +204,27 @@ class ManagerAPI { } Future hasAppUpdates(String packageName, DateTime patchDate) async { - List commits = await _githubAPI.getCommits( + List commits = await _githubAPI.getCommits( packageName, getPatchesRepo(), + patchDate, ); - return commits.any((c) => - c.commit != null && - c.commit!.author != null && - c.commit!.author!.date != null && - c.commit!.author!.date!.isAfter(patchDate)); + return commits.isNotEmpty; } Future> getAppChangelog( - String packageName, - DateTime patchDate, - ) async { - List commits = await _githubAPI.getCommits( + String packageName, DateTime patchDate) async { + List newCommits = await _githubAPI.getCommits( packageName, getPatchesRepo(), + patchDate, ); - List newCommits = commits - .where((c) => - c.commit != null && - c.commit!.author != null && - c.commit!.author!.date != null && - c.commit!.author!.date!.isAfter(patchDate) && - c.commit!.message != null) - .map((c) => c.commit!.message!) - .toList(); if (newCommits.isEmpty) { - newCommits = commits - .where((c) => c.commit != null && c.commit!.message != null) - .take(3) - .map((c) => c.commit!.message!) - .toList(); + newCommits = await _githubAPI.getCommits( + packageName, + getPatchesRepo(), + DateTime(2022, 3, 20, 21, 06, 01), + ); } return newCommits; } diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index 1bed078c..9ff64d48 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -1,6 +1,7 @@ -import 'dart:convert'; import 'dart:io'; import 'package:app_installer/app_installer.dart'; +// ignore: depend_on_referenced_packages +import 'package:collection/collection.dart'; import 'package:device_apps/device_apps.dart'; import 'package:flutter/services.dart'; import 'package:injectable/injectable.dart'; @@ -39,11 +40,7 @@ class PatcherAPI { Future _loadPatches() async { try { if (_patches.isEmpty) { - File? patchJsonFile = await _managerAPI.downloadPatches('.json'); - if (patchJsonFile != null) { - List list = json.decode(patchJsonFile.readAsStringSync()); - _patches = list.map((patch) => Patch.fromJson(patch)).toList(); - } + _patches = await _managerAPI.getPatches(); } } on Exception { _patches = List.empty(); @@ -52,7 +49,6 @@ class PatcherAPI { Future> getFilteredInstalledApps() async { List filteredApps = []; - await _loadPatches(); for (Patch patch in _patches) { for (Package package in patch.compatiblePackages) { try { @@ -73,7 +69,6 @@ class PatcherAPI { } Future> getFilteredPatches(String packageName) async { - await _loadPatches(); return _patches .where((patch) => !patch.name.contains('settings') && @@ -82,7 +77,6 @@ class PatcherAPI { } Future> getAppliedPatches(List appliedPatches) async { - await _loadPatches(); return _patches .where((patch) => appliedPatches.contains(patch.name)) .toList(); @@ -104,20 +98,22 @@ class PatcherAPI { ); if (includeSettings) { try { - Patch settingsPatch = _patches.firstWhere( + Patch? settingsPatch = _patches.firstWhereOrNull( (patch) => patch.name.contains('settings') && patch.compatiblePackages.any((pack) => pack.name == packageName), ); - selectedPatches.add(settingsPatch); + if (settingsPatch != null) { + selectedPatches.add(settingsPatch); + } } catch (e) { // ignore } } - File? patchBundleFile = await _managerAPI.downloadPatches('.jar'); + File? patchBundleFile = await _managerAPI.downloadPatches(); File? integrationsFile; if (mergeIntegrations) { - integrationsFile = await _managerAPI.downloadIntegrations('.apk'); + integrationsFile = await _managerAPI.downloadIntegrations(); } if (patchBundleFile != null) { _tmpDir.createSync(); diff --git a/lib/services/revanced_api.dart b/lib/services/revanced_api.dart new file mode 100644 index 00000000..0fbfcccb --- /dev/null +++ b/lib/services/revanced_api.dart @@ -0,0 +1,138 @@ +import 'dart:io'; +// ignore: depend_on_referenced_packages +import 'package:collection/collection.dart'; +import 'package:dio/dio.dart'; +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:timeago/timeago.dart'; + +@lazySingleton +class RevancedAPI { + final String apiUrl = 'https://revanced-releases-api.afterst0rm.xyz'; + final Dio _dio = Dio(); + final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig()); + final Options _cacheOptions = buildCacheOptions( + const Duration(minutes: 10), + maxStale: const Duration(days: 7), + ); + + void initialize() { + _dio.interceptors.add(_dioCacheManager.interceptor); + } + + Future clearAllCache() async { + await _dioCacheManager.clearAll(); + } + + Future>> getContributors() async { + Map> contributors = {}; + try { + var response = await _dio.get( + '$apiUrl/contributors', + options: _cacheOptions, + ); + if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { + print('3 - From cache'); + } else { + print('3 - From net'); + } + List repositories = response.data['repositories']; + for (Map repo in repositories) { + String name = repo['name']; + contributors[name] = repo['contributors']; + } + } on Exception { + // ignore + } + return contributors; + } + + Future> getPatches() async { + try { + var response = await _dio.get('$apiUrl/patches', options: _cacheOptions); + if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { + print('4 - From cache'); + } else { + print('4 - From net'); + } + List patches = response.data; + return patches.map((patch) => Patch.fromJson(patch)).toList(); + } on Exception { + // ignore + } + return List.empty(); + } + + Future?> _getLatestRelease( + String extension, + String repoName, + ) async { + try { + var response = await _dio.get('$apiUrl/tools', options: _cacheOptions); + if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { + print('5 - From cache'); + } else { + print('5 - From net'); + } + List tools = response.data['tools']; + return tools.firstWhereOrNull( + (t) => + t['repository'] == repoName && + (t['name'] as String).endsWith(extension), + ); + } on Exception { + return null; + } + } + + Future getLatestReleaseVersion( + String extension, String repoName) async { + try { + Map? release = + await _getLatestRelease(extension, repoName); + if (release != null) { + return release['version']; + } + } on Exception { + return null; + } + return null; + } + + Future getLatestReleaseFile(String extension, String repoName) async { + try { + Map? release = await _getLatestRelease( + extension, + repoName, + ); + if (release != null) { + String url = release['browser_download_url']; + return await DefaultCacheManager().getSingleFile(url); + } + } on Exception { + return null; + } + return null; + } + + Future getLatestReleaseTime( + String extension, + String repoName, + ) async { + try { + Map? release = await _getLatestRelease( + extension, + repoName, + ); + if (release != null) { + DateTime timestamp = DateTime.parse(release['timestamp'] as String); + return format(timestamp, locale: 'en_short'); + } + } on Exception { + return null; + } + return null; + } +} diff --git a/lib/ui/views/contributors/contributors_viewmodel.dart b/lib/ui/views/contributors/contributors_viewmodel.dart index d3c4b4f4..92793070 100644 --- a/lib/ui/views/contributors/contributors_viewmodel.dart +++ b/lib/ui/views/contributors/contributors_viewmodel.dart @@ -1,34 +1,24 @@ -import 'package:github/github.dart'; import 'package:revanced_manager/app/app.locator.dart'; -import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:stacked/stacked.dart'; class ContributorsViewModel extends BaseViewModel { final ManagerAPI _managerAPI = locator(); - final GithubAPI _githubAPI = GithubAPI(); - List patcherContributors = []; - List patchesContributors = []; - List integrationsContributors = []; - List cliContributors = []; - List managerContributors = []; + List patcherContributors = []; + List patchesContributors = []; + List integrationsContributors = []; + List cliContributors = []; + List managerContributors = []; Future getContributors() async { - patcherContributors = await _githubAPI.getContributors( - _managerAPI.getPatcherRepo(), - ); - patchesContributors = await _githubAPI.getContributors( - _managerAPI.getPatchesRepo(), - ); - integrationsContributors = await _githubAPI.getContributors( - _managerAPI.getIntegrationsRepo(), - ); - cliContributors = await _githubAPI.getContributors( - _managerAPI.getCliRepo(), - ); - managerContributors = await _githubAPI.getContributors( - _managerAPI.getManagerRepo(), - ); + Map> contributors = + await _managerAPI.getContributors(); + patcherContributors = contributors[_managerAPI.defaultPatcherRepo] ?? []; + patchesContributors = contributors[_managerAPI.getPatchesRepo()] ?? []; + integrationsContributors = + contributors[_managerAPI.getIntegrationsRepo()] ?? []; + cliContributors = contributors[_managerAPI.defaultCliRepo] ?? []; + managerContributors = contributors[_managerAPI.defaultManagerRepo] ?? []; notifyListeners(); } } diff --git a/lib/ui/views/home/home_view.dart b/lib/ui/views/home/home_view.dart index 972c469f..495aaf80 100644 --- a/lib/ui/views/home/home_view.dart +++ b/lib/ui/views/home/home_view.dart @@ -20,73 +20,78 @@ class HomeView extends StatelessWidget { onModelReady: (model) => model.initialize(), viewModelBuilder: () => locator(), builder: (context, model, child) => Scaffold( - body: CustomScrollView( - slivers: [ - CustomSliverAppBar( - title: I18nText( - 'homeView.widgetTitle', - child: Text( - '', - style: GoogleFonts.inter( - color: Theme.of(context).textTheme.headline6!.color, + body: RefreshIndicator( + color: Theme.of(context).colorScheme.secondary, + backgroundColor: Theme.of(context).colorScheme.secondaryContainer, + onRefresh: () => model.forceRefresh(), + child: CustomScrollView( + slivers: [ + CustomSliverAppBar( + title: I18nText( + 'homeView.widgetTitle', + child: Text( + '', + style: GoogleFonts.inter( + color: Theme.of(context).textTheme.headline6!.color, + ), ), ), ), - ), - SliverPadding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - sliver: SliverList( - delegate: SliverChildListDelegate.fixed( - [ - I18nText( - 'homeView.updatesSubtitle', - child: Text( - '', - style: Theme.of(context).textTheme.headline6!, - ), - ), - const SizedBox(height: 10), - LatestCommitCard( - onPressed: () => - model.showUpdateConfirmationDialog(context), - ), - const SizedBox(height: 23), - I18nText( - 'homeView.patchedSubtitle', - child: Text( - '', - style: Theme.of(context).textTheme.headline6!, - ), - ), - const SizedBox(height: 8), - Row( - children: [ - DashboardChip( - label: I18nText('homeView.updatesAvailable'), - isSelected: model.showUpdatableApps, - onSelected: (value) { - model.toggleUpdatableApps(true); - }, + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + sliver: SliverList( + delegate: SliverChildListDelegate.fixed( + [ + I18nText( + 'homeView.updatesSubtitle', + child: Text( + '', + style: Theme.of(context).textTheme.headline6!, ), - const SizedBox(width: 10), - DashboardChip( - label: I18nText('homeView.installed'), - isSelected: !model.showUpdatableApps, - onSelected: (value) { - model.toggleUpdatableApps(false); - }, - ) - ], - ), - const SizedBox(height: 14), - model.showUpdatableApps - ? AvailableUpdatesCard() - : InstalledAppsCard(), - ], + ), + const SizedBox(height: 10), + LatestCommitCard( + onPressed: () => + model.showUpdateConfirmationDialog(context), + ), + const SizedBox(height: 23), + I18nText( + 'homeView.patchedSubtitle', + child: Text( + '', + style: Theme.of(context).textTheme.headline6!, + ), + ), + const SizedBox(height: 8), + Row( + children: [ + DashboardChip( + label: I18nText('homeView.updatesAvailable'), + isSelected: model.showUpdatableApps, + onSelected: (value) { + model.toggleUpdatableApps(true); + }, + ), + const SizedBox(width: 10), + DashboardChip( + label: I18nText('homeView.installed'), + isSelected: !model.showUpdatableApps, + onSelected: (value) { + model.toggleUpdatableApps(false); + }, + ) + ], + ), + const SizedBox(height: 14), + model.showUpdatableApps + ? AvailableUpdatesCard() + : InstalledAppsCard(), + ], + ), ), ), - ), - ], + ], + ), ), ), ); diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart index 737fa446..67ceb469 100644 --- a/lib/ui/views/home/home_viewmodel.dart +++ b/lib/ui/views/home/home_viewmodel.dart @@ -23,8 +23,8 @@ class HomeViewModel extends BaseViewModel { final NavigationService _navigationService = locator(); final ManagerAPI _managerAPI = locator(); final PatcherAPI _patcherAPI = locator(); - final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); + final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); + DateTime? _lastUpdate; bool showUpdatableApps = true; List patchedInstalledApps = []; List patchedUpdatableApps = []; @@ -61,10 +61,7 @@ class HomeViewModel extends BaseViewModel { } void _getPatchedApps() { - patchedInstalledApps = _managerAPI - .getPatchedApps() - .where((app) => app.hasUpdates == false) - .toList(); + patchedInstalledApps = _managerAPI.getPatchedApps().toList(); patchedUpdatableApps = _managerAPI .getPatchedApps() .where((app) => app.hasUpdates == true) @@ -98,7 +95,7 @@ class HomeViewModel extends BaseViewModel { toastLength: Toast.LENGTH_LONG, gravity: ToastGravity.CENTER, ); - File? managerApk = await _managerAPI.downloadManager('.apk'); + File? managerApk = await _managerAPI.downloadManager(); if (managerApk != null) { flutterLocalNotificationsPlugin.show( 0, @@ -170,4 +167,21 @@ class HomeViewModel extends BaseViewModel { ), ); } + + Future getLatestPatcherReleaseTime() async { + return _managerAPI.getLatestPatcherReleaseTime(); + } + + Future getLatestManagerReleaseTime() async { + return _managerAPI.getLatestManagerReleaseTime(); + } + + Future forceRefresh() async { + await Future.delayed(const Duration(seconds: 1)); + if (_lastUpdate == null || + _lastUpdate!.difference(DateTime.now()).inSeconds > 60) { + _managerAPI.clearAllData(); + } + initialize(); + } } diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index a75f3c22..9c3859c9 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -46,10 +46,11 @@ class InstallerViewModel extends BaseViewModel { ), ); await FlutterBackground.enableBackgroundExecution(); - } finally { - await handlePlatformChannelMethods(); - await runPatcher(); + } on Exception { + // ignore } + await handlePlatformChannelMethods(); + await runPatcher(); } Future handlePlatformChannelMethods() async { @@ -118,10 +119,13 @@ class InstallerViewModel extends BaseViewModel { update(1.0, 'Aborting...', 'No app or patches selected! Aborting'); } try { - await FlutterBackground.disableBackgroundExecution(); - } finally { - isPatching = false; + if (FlutterBackground.isBackgroundExecutionEnabled) { + await FlutterBackground.disableBackgroundExecution(); + } + } on Exception { + // ignore } + isPatching = false; } void installResult(bool installAsRoot) async { diff --git a/lib/ui/views/patches_selector/patches_selector_viewmodel.dart b/lib/ui/views/patches_selector/patches_selector_viewmodel.dart index 5d59022a..7d7e454a 100644 --- a/lib/ui/views/patches_selector/patches_selector_viewmodel.dart +++ b/lib/ui/views/patches_selector/patches_selector_viewmodel.dart @@ -1,3 +1,5 @@ +// ignore: depend_on_referenced_packages +import 'package:collection/collection.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patched_application.dart'; @@ -71,9 +73,14 @@ class PatchesSelectorViewModel extends BaseViewModel { List getSupportedVersions(Patch patch) { PatchedApplication app = locator().selectedApp!; - return patch.compatiblePackages - .firstWhere((pack) => pack.name == app.packageName) - .versions; + Package? package = patch.compatiblePackages.firstWhereOrNull( + (pack) => pack.name == app.packageName, + ); + if (package != null) { + return package.versions; + } else { + return List.empty(); + } } bool isPatchSupported(Patch patch) { diff --git a/lib/ui/widgets/appInfoView/app_info_viewmodel.dart b/lib/ui/widgets/appInfoView/app_info_viewmodel.dart index 1b49cefa..5a7328bc 100644 --- a/lib/ui/widgets/appInfoView/app_info_viewmodel.dart +++ b/lib/ui/widgets/appInfoView/app_info_viewmodel.dart @@ -7,6 +7,7 @@ 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/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'; @@ -73,7 +74,7 @@ class AppInfoViewModel extends BaseViewModel { label: I18nText('okButton'), onPressed: () { uninstallApp(app); - locator().notifyListeners(); + locator().notifyListeners(); Navigator.of(context).pop(); Navigator.of(context).pop(); }, diff --git a/lib/ui/widgets/appSelectorView/app_skeleton_loader.dart b/lib/ui/widgets/appSelectorView/app_skeleton_loader.dart index 1496a2e2..cd2dbcce 100644 --- a/lib/ui/widgets/appSelectorView/app_skeleton_loader.dart +++ b/lib/ui/widgets/appSelectorView/app_skeleton_loader.dart @@ -30,7 +30,7 @@ class AppSkeletonLoader extends StatelessWidget { children: [ Container( color: Colors.white, - height: 25, + height: 34, width: screenWidth * 0.4, child: SkeletonParagraph( style: const SkeletonParagraphStyle( @@ -42,7 +42,7 @@ class AppSkeletonLoader extends StatelessWidget { Container( margin: const EdgeInsets.only(bottom: 4), color: Colors.white, - height: 25, + height: 34, width: screenWidth * 0.6, child: SkeletonParagraph( style: const SkeletonParagraphStyle( diff --git a/lib/ui/widgets/contributorsView/contributors_card.dart b/lib/ui/widgets/contributorsView/contributors_card.dart index 621650cf..70b6beeb 100644 --- a/lib/ui/widgets/contributorsView/contributors_card.dart +++ b/lib/ui/widgets/contributorsView/contributors_card.dart @@ -1,11 +1,10 @@ import 'package:flutter/material.dart'; -import 'package:github/github.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:url_launcher/url_launcher.dart'; class ContributorsCard extends StatefulWidget { final String title; - final List contributors; + final List contributors; final double height; const ContributorsCard({ @@ -52,9 +51,9 @@ class _ContributorsCardState extends State { borderRadius: BorderRadius.circular(100), child: GestureDetector( onTap: () => launchUrl( - Uri.parse(widget.contributors[index].htmlUrl!)), + Uri.parse(widget.contributors[index]['html_url'])), child: Image.network( - widget.contributors[index].avatarUrl!, + widget.contributors[index]['avatar_url'], height: 40, width: 40, ), diff --git a/lib/ui/widgets/homeView/latest_commit_card.dart b/lib/ui/widgets/homeView/latest_commit_card.dart index 6593ef03..7603e3a7 100644 --- a/lib/ui/widgets/homeView/latest_commit_card.dart +++ b/lib/ui/widgets/homeView/latest_commit_card.dart @@ -1,8 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:revanced_manager/app/app.locator.dart'; -import 'package:revanced_manager/services/github_api.dart'; -import 'package:revanced_manager/services/manager_api.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_card.dart'; @@ -20,8 +18,7 @@ class LatestCommitCard extends StatefulWidget { } class _LatestCommitCardState extends State { - final ManagerAPI _managerAPI = locator(); - final GithubAPI _githubAPI = GithubAPI(); + final HomeViewModel model = locator(); @override Widget build(BuildContext context) { @@ -35,10 +32,8 @@ class _LatestCommitCardState extends State { Row( children: [ I18nText('latestCommitCard.patcherLabel'), - FutureBuilder( - future: _githubAPI.latestCommitTime( - _managerAPI.getPatcherRepo(), - ), + FutureBuilder( + future: model.getLatestPatcherReleaseTime(), builder: (context, snapshot) => Text( snapshot.hasData && snapshot.data!.isNotEmpty ? FlutterI18n.translate( @@ -58,10 +53,8 @@ class _LatestCommitCardState extends State { Row( children: [ I18nText('latestCommitCard.managerLabel'), - FutureBuilder( - future: _githubAPI.latestCommitTime( - _managerAPI.getManagerRepo(), - ), + FutureBuilder( + future: model.getLatestManagerReleaseTime(), builder: (context, snapshot) => snapshot.hasData && snapshot.data!.isNotEmpty ? I18nText( diff --git a/lib/ui/widgets/shared/application_item.dart b/lib/ui/widgets/shared/application_item.dart index 0aa741f4..9250a7cf 100644 --- a/lib/ui/widgets/shared/application_item.dart +++ b/lib/ui/widgets/shared/application_item.dart @@ -49,7 +49,7 @@ class ApplicationItem extends StatelessWidget { fontWeight: FontWeight.w500, ), ), - Text(format(patchDate, locale: 'en_short')), + Text(format(patchDate)), ], ), const Spacer(), diff --git a/pubspec.yaml b/pubspec.yaml index 2dc97fc4..7bed0459 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,6 +17,8 @@ dependencies: url: https://github.com/ponces/flutter_plugin_device_apps ref: appinfo-from-storage device_info_plus: ^4.1.2 + dio: ^4.0.6 + dio_http_cache_lts: ^0.4.1 dynamic_color: ^1.5.4 dynamic_themes: ^1.1.0 expandable: ^5.0.1 @@ -32,7 +34,6 @@ dependencies: fluttertoast: ^8.0.9 font_awesome_flutter: ^10.1.0 get_it: ^7.2.0 - github: ^9.4.0 google_fonts: ^3.0.1 http: ^0.13.4 injectable: ^1.5.3 @@ -40,6 +41,7 @@ dependencies: json_annotation: ^4.6.0 package_info_plus: ^1.4.3+1 path_provider: ^2.0.11 + pull_to_refresh: ^2.0.0 root: ^2.0.2 share_extend: ^2.0.0 shared_preferences: ^2.0.15 From 04bcb1ec24163c9e292ddf731581ae26a268caab Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 00:33:16 +0100 Subject: [PATCH 02/12] fix: Properly reload Home View on App uninstallation --- lib/ui/widgets/appInfoView/app_info_viewmodel.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ui/widgets/appInfoView/app_info_viewmodel.dart b/lib/ui/widgets/appInfoView/app_info_viewmodel.dart index 5a7328bc..97da896f 100644 --- a/lib/ui/widgets/appInfoView/app_info_viewmodel.dart +++ b/lib/ui/widgets/appInfoView/app_info_viewmodel.dart @@ -74,7 +74,7 @@ class AppInfoViewModel extends BaseViewModel { label: I18nText('okButton'), onPressed: () { uninstallApp(app); - locator().notifyListeners(); + locator().initialize(); Navigator.of(context).pop(); Navigator.of(context).pop(); }, From 92a84c3bfb057cf1f95f6c19d4ad05fa4a43f463 Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 00:34:36 +0100 Subject: [PATCH 03/12] feat: Keep screen awake during patching --- lib/ui/views/installer/installer_viewmodel.dart | 7 ++++--- pubspec.yaml | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ui/views/installer/installer_viewmodel.dart b/lib/ui/views/installer/installer_viewmodel.dart index 9c3859c9..e725dde1 100644 --- a/lib/ui/views/installer/installer_viewmodel.dart +++ b/lib/ui/views/installer/installer_viewmodel.dart @@ -10,6 +10,7 @@ import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/patcher_api.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:stacked/stacked.dart'; +import 'package:wakelock/wakelock.dart'; class InstallerViewModel extends BaseViewModel { final ManagerAPI _managerAPI = locator(); @@ -46,6 +47,7 @@ class InstallerViewModel extends BaseViewModel { ), ); await FlutterBackground.enableBackgroundExecution(); + await Wakelock.enable(); } on Exception { // ignore } @@ -119,9 +121,8 @@ class InstallerViewModel extends BaseViewModel { update(1.0, 'Aborting...', 'No app or patches selected! Aborting'); } try { - if (FlutterBackground.isBackgroundExecutionEnabled) { - await FlutterBackground.disableBackgroundExecution(); - } + await FlutterBackground.disableBackgroundExecution(); + await Wakelock.disable(); } on Exception { // ignore } diff --git a/pubspec.yaml b/pubspec.yaml index 7bed0459..0fb0768e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,6 +52,7 @@ dependencies: stacked_themes: ^0.3.9 timeago: ^3.2.2 url_launcher: ^6.1.5 + wakelock: ^0.6.2 dev_dependencies: build_runner: any From 6665095b2ef40af3c00a44681b813ac3f59e90de Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 00:35:41 +0100 Subject: [PATCH 04/12] feat: Add connectivity status toast message on Home View --- assets/i18n/en.json | 3 ++- lib/ui/views/home/home_view.dart | 4 ++-- lib/ui/views/home/home_viewmodel.dart | 18 +++++++++++++++--- .../appInfoView/app_info_viewmodel.dart | 2 +- pubspec.yaml | 1 + 5 files changed, 21 insertions(+), 7 deletions(-) diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 9ec665ad..3c49988e 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -25,7 +25,8 @@ "downloadingMessage": "Downloading update!", "installingMessage": "Installing update!", "errorDownloadMessage": "Unable to download update!", - "errorInstallMessage": "Unable to download update!" + "errorInstallMessage": "Unable to download update!", + "noConnection": "No internet connection" }, "applicationItem": { "patchButton": "Patch", diff --git a/lib/ui/views/home/home_view.dart b/lib/ui/views/home/home_view.dart index 495aaf80..46c2b038 100644 --- a/lib/ui/views/home/home_view.dart +++ b/lib/ui/views/home/home_view.dart @@ -17,13 +17,13 @@ class HomeView extends StatelessWidget { Widget build(BuildContext context) { return ViewModelBuilder.reactive( disposeViewModel: false, - onModelReady: (model) => model.initialize(), + onModelReady: (model) => model.initialize(context), viewModelBuilder: () => locator(), builder: (context, model, child) => Scaffold( body: RefreshIndicator( color: Theme.of(context).colorScheme.secondary, backgroundColor: Theme.of(context).colorScheme.secondaryContainer, - onRefresh: () => model.forceRefresh(), + onRefresh: () => model.forceRefresh(context), child: CustomScrollView( slivers: [ CustomSliverAppBar( diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart index 67ceb469..77fd2341 100644 --- a/lib/ui/views/home/home_viewmodel.dart +++ b/lib/ui/views/home/home_viewmodel.dart @@ -1,6 +1,7 @@ // ignore_for_file: use_build_context_synchronously import 'dart:io'; import 'package:app_installer/app_installer.dart'; +import 'package:cross_connectivity/cross_connectivity.dart'; import 'package:device_apps/device_apps.dart'; import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; @@ -29,13 +30,24 @@ class HomeViewModel extends BaseViewModel { List patchedInstalledApps = []; List patchedUpdatableApps = []; - Future initialize() async { + Future initialize(BuildContext context) async { await flutterLocalNotificationsPlugin.initialize( const InitializationSettings( android: AndroidInitializationSettings('ic_notification'), ), onSelectNotification: (p) => DeviceApps.openApp('app.revanced.manager'), ); + bool isConnected = await Connectivity().checkConnection(); + if (!isConnected) { + Fluttertoast.showToast( + msg: FlutterI18n.translate( + context, + 'homeView.noConnection', + ), + toastLength: Toast.LENGTH_LONG, + gravity: ToastGravity.CENTER, + ); + } _getPatchedApps(); _managerAPI.reAssessSavedApps().then((_) => _getPatchedApps()); } @@ -176,12 +188,12 @@ class HomeViewModel extends BaseViewModel { return _managerAPI.getLatestManagerReleaseTime(); } - Future forceRefresh() async { + Future forceRefresh(BuildContext context) async { await Future.delayed(const Duration(seconds: 1)); if (_lastUpdate == null || _lastUpdate!.difference(DateTime.now()).inSeconds > 60) { _managerAPI.clearAllData(); } - initialize(); + initialize(context); } } diff --git a/lib/ui/widgets/appInfoView/app_info_viewmodel.dart b/lib/ui/widgets/appInfoView/app_info_viewmodel.dart index 97da896f..3059aa4c 100644 --- a/lib/ui/widgets/appInfoView/app_info_viewmodel.dart +++ b/lib/ui/widgets/appInfoView/app_info_viewmodel.dart @@ -74,7 +74,7 @@ class AppInfoViewModel extends BaseViewModel { label: I18nText('okButton'), onPressed: () { uninstallApp(app); - locator().initialize(); + locator().initialize(context); Navigator.of(context).pop(); Navigator.of(context).pop(); }, diff --git a/pubspec.yaml b/pubspec.yaml index 0fb0768e..f9572626 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,6 +12,7 @@ environment: dependencies: animations: ^2.0.4 app_installer: ^1.1.0 + cross_connectivity: ^3.0.5 device_apps: git: url: https://github.com/ponces/flutter_plugin_device_apps From d01e7fdcc284a57655a99865976052704f38237a Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 00:36:18 +0100 Subject: [PATCH 05/12] fix: Show search bar during loading of selector views --- .../views/app_selector/app_selector_view.dart | 52 +++++++++--------- .../patches_selector_view.dart | 54 +++++++++---------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/lib/ui/views/app_selector/app_selector_view.dart b/lib/ui/views/app_selector/app_selector_view.dart index e1ea3acb..44cb9150 100644 --- a/lib/ui/views/app_selector/app_selector_view.dart +++ b/lib/ui/views/app_selector/app_selector_view.dart @@ -35,29 +35,29 @@ class _AppSelectorViewState extends State { child: Padding( padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 12.0), - child: model.noApps - ? Center( - child: I18nText('appSelectorCard.noAppsLabel'), - ) - : model.apps.isEmpty - ? const AppSkeletonLoader() - : Column( - children: [ - SearchBar( - showSelectIcon: false, - hintText: FlutterI18n.translate( - context, - 'appSelectorView.searchBarHint', - ), - onQueryChanged: (searchQuery) { - setState(() { - _query = searchQuery; - }); - }, - ), - const SizedBox(height: 12), - Expanded( - child: ListView( + child: Column( + children: [ + SearchBar( + showSelectIcon: false, + hintText: FlutterI18n.translate( + context, + 'appSelectorView.searchBarHint', + ), + onQueryChanged: (searchQuery) { + setState(() { + _query = searchQuery; + }); + }, + ), + const SizedBox(height: 12), + Expanded( + child: model.noApps + ? Center( + child: I18nText('appSelectorCard.noAppsLabel'), + ) + : model.apps.isEmpty + ? const AppSkeletonLoader() + : ListView( padding: const EdgeInsets.only(bottom: 80), children: model .getFilteredApps(_query) @@ -74,9 +74,9 @@ class _AppSelectorViewState extends State { )) .toList(), ), - ), - ], - ), + ), + ], + ), ), ), ), diff --git a/lib/ui/views/patches_selector/patches_selector_view.dart b/lib/ui/views/patches_selector/patches_selector_view.dart index e99de0f6..b68cb707 100644 --- a/lib/ui/views/patches_selector/patches_selector_view.dart +++ b/lib/ui/views/patches_selector/patches_selector_view.dart @@ -37,30 +37,30 @@ class _PatchesSelectorViewState extends State { child: Padding( padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 12.0), - child: model.patches.isEmpty - ? Center( - child: CircularProgressIndicator( - color: Theme.of(context).colorScheme.primary, - ), - ) - : Column( - children: [ - SearchBar( - showSelectIcon: true, - hintText: FlutterI18n.translate( - context, - 'patchesSelectorView.searchBarHint', - ), - onQueryChanged: (searchQuery) { - setState(() { - _query = searchQuery; - }); - }, - onSelectAll: (value) => model.selectAllPatches(value), - ), - const SizedBox(height: 12), - Expanded( - child: ListView( + child: Column( + children: [ + SearchBar( + showSelectIcon: true, + hintText: FlutterI18n.translate( + context, + 'patchesSelectorView.searchBarHint', + ), + onQueryChanged: (searchQuery) { + setState(() { + _query = searchQuery; + }); + }, + onSelectAll: (value) => model.selectAllPatches(value), + ), + const SizedBox(height: 12), + Expanded( + child: model.patches.isEmpty + ? Center( + child: CircularProgressIndicator( + color: Theme.of(context).colorScheme.primary, + ), + ) + : ListView( padding: const EdgeInsets.only(bottom: 80), children: model .getQueriedPatches(_query) @@ -160,9 +160,9 @@ class _PatchesSelectorViewState extends State { ) .toList(), ), - ), - ], - ), + ), + ], + ), ), ), ), From 6ca47d7ec30f417b8144fa5750ab15d039c4ce05 Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 00:37:41 +0100 Subject: [PATCH 06/12] fix: We are already official for quite some time :) --- pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pubspec.yaml b/pubspec.yaml index f9572626..2990edd9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: revanced_manager -description: An unofficial ReVanced Manager based on Flutter. -homepage: https://github.com/Aunali321/revanced-manager +description: The official ReVanced Manager. +homepage: https://github.com/revanced/revanced-manager publish_to: 'none' From 0e4c19060e6d0146a9cefdaf9299a2002d0f9dfa Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 01:41:53 +0100 Subject: [PATCH 07/12] fix: use singletons to enable cache and remove debug messages --- lib/app/app.dart | 4 ++++ lib/main.dart | 4 ++++ lib/services/github_api.dart | 25 ++++++++----------------- lib/services/manager_api.dart | 5 +++-- lib/services/revanced_api.dart | 17 +---------------- 5 files changed, 20 insertions(+), 35 deletions(-) diff --git a/lib/app/app.dart b/lib/app/app.dart index 653162dd..845800ba 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -1,5 +1,7 @@ +import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/patcher_api.dart'; +import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/ui/views/app_selector/app_selector_view.dart'; import 'package:revanced_manager/ui/views/contributors/contributors_view.dart'; import 'package:revanced_manager/ui/views/home/home_viewmodel.dart'; @@ -32,6 +34,8 @@ import 'package:stacked_services/stacked_services.dart'; LazySingleton(classType: NavigationService), LazySingleton(classType: ManagerAPI), LazySingleton(classType: PatcherAPI), + LazySingleton(classType: RevancedAPI), + LazySingleton(classType: GithubAPI), ], ) class AppSetup {} diff --git a/lib/main.dart b/lib/main.dart index 925d9415..77f89813 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,8 +3,10 @@ import 'package:flutter_i18n/flutter_i18n.dart'; // ignore: depend_on_referenced_packages import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:revanced_manager/app/app.locator.dart'; +import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/patcher_api.dart'; +import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/ui/theme/dynamic_theme_builder.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_view.dart'; import 'package:stacked_themes/stacked_themes.dart'; @@ -15,6 +17,8 @@ Future main() async { WidgetsFlutterBinding.ensureInitialized(); await locator().initialize(); await locator().initialize(); + locator().initialize(); + locator().initialize(); runApp(const MyApp()); } diff --git a/lib/services/github_api.dart b/lib/services/github_api.dart index 5ede1729..be6d28e8 100644 --- a/lib/services/github_api.dart +++ b/lib/services/github_api.dart @@ -5,16 +5,17 @@ import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; 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'; +@lazySingleton class GithubAPI { final String apiUrl = 'https://api.github.com'; final Dio _dio = Dio(); - final DioCacheManager _dioCacheManager = DioCacheManager( - CacheConfig( - defaultMaxAge: const Duration(hours: 1), - defaultMaxStale: const Duration(days: 7), - ), + final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig()); + final Options _cacheOptions = buildCacheOptions( + const Duration(hours: 1), + maxStale: const Duration(days: 7), ); final Map repoAppPath = { 'com.google.android.youtube': 'youtube', @@ -38,13 +39,8 @@ class GithubAPI { try { var response = await _dio.get( '$apiUrl/repos/$repoName/releases/latest', - options: buildCacheOptions(const Duration(hours: 1)), + options: _cacheOptions, ); - if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { - print('1 - From cache'); - } else { - print('1 - From net'); - } return response.data; } on Exception { // ignore @@ -67,13 +63,8 @@ class GithubAPI { 'per_page': 3, 'since': since.toIso8601String(), }, - options: buildCacheOptions(const Duration(hours: 1)), + options: _cacheOptions, ); - if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { - print('2 - From cache'); - } else { - print('2 - From net'); - } List commits = response.data; return commits .map((commit) => diff --git a/lib/services/manager_api.dart b/lib/services/manager_api.dart index c429f18c..d621cb0c 100644 --- a/lib/services/manager_api.dart +++ b/lib/services/manager_api.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:device_apps/device_apps.dart'; import 'package:injectable/injectable.dart'; import 'package:package_info_plus/package_info_plus.dart'; +import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/models/patch.dart'; import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/github_api.dart'; @@ -12,8 +13,8 @@ import 'package:shared_preferences/shared_preferences.dart'; @lazySingleton class ManagerAPI { - final RevancedAPI _revancedAPI = RevancedAPI(); - final GithubAPI _githubAPI = GithubAPI(); + final RevancedAPI _revancedAPI = locator(); + final GithubAPI _githubAPI = locator(); final RootAPI _rootAPI = RootAPI(); final String patcherRepo = 'revanced-patcher'; final String cliRepo = 'revanced-cli'; diff --git a/lib/services/revanced_api.dart b/lib/services/revanced_api.dart index 0fbfcccb..8e19f986 100644 --- a/lib/services/revanced_api.dart +++ b/lib/services/revanced_api.dart @@ -14,7 +14,7 @@ class RevancedAPI { final Dio _dio = Dio(); final DioCacheManager _dioCacheManager = DioCacheManager(CacheConfig()); final Options _cacheOptions = buildCacheOptions( - const Duration(minutes: 10), + const Duration(hours: 1), maxStale: const Duration(days: 7), ); @@ -33,11 +33,6 @@ class RevancedAPI { '$apiUrl/contributors', options: _cacheOptions, ); - if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { - print('3 - From cache'); - } else { - print('3 - From net'); - } List repositories = response.data['repositories']; for (Map repo in repositories) { String name = repo['name']; @@ -52,11 +47,6 @@ class RevancedAPI { Future> getPatches() async { try { var response = await _dio.get('$apiUrl/patches', options: _cacheOptions); - if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { - print('4 - From cache'); - } else { - print('4 - From net'); - } List patches = response.data; return patches.map((patch) => Patch.fromJson(patch)).toList(); } on Exception { @@ -71,11 +61,6 @@ class RevancedAPI { ) async { try { var response = await _dio.get('$apiUrl/tools', options: _cacheOptions); - if (response.headers.value(DIO_CACHE_HEADER_KEY_DATA_SOURCE) != null) { - print('5 - From cache'); - } else { - print('5 - From net'); - } List tools = response.data['tools']; return tools.firstWhereOrNull( (t) => From 0c5c3b32f1dd8c050958194659b686c8b8eab125 Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 02:04:07 +0100 Subject: [PATCH 08/12] fix: Add missing referenced packages instead of ignoring warning --- lib/main.dart | 1 - lib/services/github_api.dart | 1 - lib/services/patcher_api.dart | 1 - lib/services/revanced_api.dart | 1 - lib/ui/views/patches_selector/patches_selector_viewmodel.dart | 1 - lib/ui/views/settings/settings_viewmodel.dart | 1 - pubspec.yaml | 3 +++ 7 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 77f89813..3fcf32bb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; -// ignore: depend_on_referenced_packages import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/services/github_api.dart'; diff --git a/lib/services/github_api.dart b/lib/services/github_api.dart index be6d28e8..fed256e9 100644 --- a/lib/services/github_api.dart +++ b/lib/services/github_api.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'dart:io'; -// ignore: depend_on_referenced_packages import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:dio_http_cache_lts/dio_http_cache_lts.dart'; diff --git a/lib/services/patcher_api.dart b/lib/services/patcher_api.dart index 9ff64d48..5d9e765b 100644 --- a/lib/services/patcher_api.dart +++ b/lib/services/patcher_api.dart @@ -1,6 +1,5 @@ import 'dart:io'; import 'package:app_installer/app_installer.dart'; -// ignore: depend_on_referenced_packages import 'package:collection/collection.dart'; import 'package:device_apps/device_apps.dart'; import 'package:flutter/services.dart'; diff --git a/lib/services/revanced_api.dart b/lib/services/revanced_api.dart index 8e19f986..bc672ca3 100644 --- a/lib/services/revanced_api.dart +++ b/lib/services/revanced_api.dart @@ -1,5 +1,4 @@ import 'dart:io'; -// ignore: depend_on_referenced_packages import 'package:collection/collection.dart'; import 'package:dio/dio.dart'; import 'package:dio_http_cache_lts/dio_http_cache_lts.dart'; diff --git a/lib/ui/views/patches_selector/patches_selector_viewmodel.dart b/lib/ui/views/patches_selector/patches_selector_viewmodel.dart index 7d7e454a..b7c37221 100644 --- a/lib/ui/views/patches_selector/patches_selector_viewmodel.dart +++ b/lib/ui/views/patches_selector/patches_selector_viewmodel.dart @@ -1,4 +1,3 @@ -// ignore: depend_on_referenced_packages import 'package:collection/collection.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/models/patch.dart'; diff --git a/lib/ui/views/settings/settings_viewmodel.dart b/lib/ui/views/settings/settings_viewmodel.dart index 454df33b..f682a8cf 100644 --- a/lib/ui/views/settings/settings_viewmodel.dart +++ b/lib/ui/views/settings/settings_viewmodel.dart @@ -1,5 +1,4 @@ // ignore_for_file: use_build_context_synchronously - import 'package:device_info_plus/device_info_plus.dart'; import 'package:dynamic_themes/dynamic_themes.dart'; import 'package:flutter/material.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index 2990edd9..826ebc00 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,6 +12,7 @@ environment: dependencies: animations: ^2.0.4 app_installer: ^1.1.0 + collection: ^1.16.0 cross_connectivity: ^3.0.5 device_apps: git: @@ -31,6 +32,8 @@ dependencies: flutter_cache_manager: ^3.3.0 flutter_i18n: ^0.32.4 flutter_local_notifications: ^9.8.0+1 + flutter_localizations: + sdk: flutter flutter_svg: ^1.1.1+1 fluttertoast: ^8.0.9 font_awesome_flutter: ^10.1.0 From e22b6433757d517a10a154431d3a2b14aedff91c Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 09:12:34 +0100 Subject: [PATCH 09/12] fix: Improve display of contributors avatars using cache --- .../contributorsView/contributors_card.dart | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/ui/widgets/contributorsView/contributors_card.dart b/lib/ui/widgets/contributorsView/contributors_card.dart index 70b6beeb..45bc38b6 100644 --- a/lib/ui/widgets/contributorsView/contributors_card.dart +++ b/lib/ui/widgets/contributorsView/contributors_card.dart @@ -1,4 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:flutter_cache_manager/file.dart'; +import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:revanced_manager/ui/widgets/shared/custom_card.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -51,11 +53,25 @@ class _ContributorsCardState extends State { borderRadius: BorderRadius.circular(100), child: GestureDetector( onTap: () => launchUrl( - Uri.parse(widget.contributors[index]['html_url'])), - child: Image.network( - widget.contributors[index]['avatar_url'], - height: 40, - width: 40, + Uri.parse( + widget.contributors[index]['html_url'], + ), + ), + child: FutureBuilder( + future: DefaultCacheManager().getSingleFile( + widget.contributors[index]['avatar_url'], + ), + builder: (context, snapshot) => snapshot.hasData + ? Image.file( + snapshot.data!, + height: 40, + width: 40, + ) + : Image.network( + widget.contributors[index]['avatar_url'], + height: 40, + width: 40, + ), ), ), ); From 7e8ccdff430059336927d9be1d6129e9ad7bc3a3 Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 09:18:03 +0100 Subject: [PATCH 10/12] fix: Handle exceptions on APIs in a more legible way --- lib/services/github_api.dart | 8 +++----- lib/services/revanced_api.dart | 5 ++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/services/github_api.dart b/lib/services/github_api.dart index fed256e9..1b18d9a8 100644 --- a/lib/services/github_api.dart +++ b/lib/services/github_api.dart @@ -42,9 +42,8 @@ class GithubAPI { ); return response.data; } on Exception { - // ignore + return null; } - return null; } Future> getCommits( @@ -70,9 +69,8 @@ class GithubAPI { (commit['commit']['message'] as String).split('\n')[0]) .toList(); } on Exception { - // ignore + return List.empty(); } - return List.empty(); } Future getLatestReleaseFile(String extension, String repoName) async { @@ -104,7 +102,7 @@ class GithubAPI { patches = list.map((patch) => Patch.fromJson(patch)).toList(); } } on Exception { - // ignore + return List.empty(); } return patches; } diff --git a/lib/services/revanced_api.dart b/lib/services/revanced_api.dart index bc672ca3..09d1e4c8 100644 --- a/lib/services/revanced_api.dart +++ b/lib/services/revanced_api.dart @@ -38,7 +38,7 @@ class RevancedAPI { contributors[name] = repo['contributors']; } } on Exception { - // ignore + return {}; } return contributors; } @@ -49,9 +49,8 @@ class RevancedAPI { List patches = response.data; return patches.map((patch) => Patch.fromJson(patch)).toList(); } on Exception { - // ignore + return List.empty(); } - return List.empty(); } Future?> _getLatestRelease( From 38088d2f6a458e1cc38653f69a8af8b0fc01374f Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 11:14:54 +0100 Subject: [PATCH 11/12] fix: Installer buttons should be fixed to bottom --- lib/ui/views/installer/installer_view.dart | 107 +++++++++++---------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/lib/ui/views/installer/installer_view.dart b/lib/ui/views/installer/installer_view.dart index fbeec134..32933723 100644 --- a/lib/ui/views/installer/installer_view.dart +++ b/lib/ui/views/installer/installer_view.dart @@ -66,7 +66,7 @@ class InstallerView extends StatelessWidget { ), ), SliverPadding( - padding: const EdgeInsets.all(20.0), + padding: const EdgeInsets.all(20.0).copyWith(bottom: 20.0), sliver: SliverList( delegate: SliverChildListDelegate.fixed( [ @@ -79,61 +79,64 @@ class InstallerView extends StatelessWidget { ), ), ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 16, - horizontal: 0, - ), - child: Visibility( - visible: !model.isPatching, - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - 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(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(false), - ), - ), - ], - ), - ), - ), ], ), ), ), + SliverFillRemaining( + hasScrollBody: false, + child: Align( + alignment: Alignment.bottomCenter, + child: Visibility( + visible: !model.isPatching, + child: Padding( + padding: const EdgeInsets.all(20.0).copyWith(top: 0.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 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(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(false), + ), + ), + ], + ), + ), + ), + ), + ), ], ), ), From 0db6426e91584be973bf17fbdf6367aedfc20292 Mon Sep 17 00:00:00 2001 From: Alberto Ponces Date: Mon, 12 Sep 2022 11:20:02 +0100 Subject: [PATCH 12/12] fix: Improve progress indicator background --- lib/ui/views/installer/installer_view.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/ui/views/installer/installer_view.dart b/lib/ui/views/installer/installer_view.dart index 32933723..f29dba69 100644 --- a/lib/ui/views/installer/installer_view.dart +++ b/lib/ui/views/installer/installer_view.dart @@ -60,7 +60,8 @@ class InstallerView extends StatelessWidget { preferredSize: const Size(double.infinity, 1.0), child: LinearProgressIndicator( color: Theme.of(context).colorScheme.primary, - backgroundColor: Theme.of(context).colorScheme.secondary, + backgroundColor: + Theme.of(context).colorScheme.primaryContainer, value: model.progress, ), ),