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