diff --git a/lib/services/revanced_api.dart b/lib/services/revanced_api.dart index bccca46f..720c4f65 100644 --- a/lib/services/revanced_api.dart +++ b/lib/services/revanced_api.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:developer'; import 'dart:io'; @@ -15,7 +16,7 @@ import 'package:timeago/timeago.dart'; @lazySingleton class RevancedAPI { late Dio _dio = Dio(); - + final _cacheOptions = CacheOptions( store: MemCacheStore(), maxStale: const Duration(days: 1), @@ -164,6 +165,43 @@ class RevancedAPI { return null; } + StreamController managerUpdateProgress = StreamController(); + + void updateManagerDownloadProgress(int progress) { + managerUpdateProgress.add(progress.toDouble()); + } + + Stream getManagerUpdateProgress() { + return managerUpdateProgress.stream; + } + + void disposeManagerUpdateProgress() { + managerUpdateProgress.close(); + } + + Future downloadManager() async { + final Map? release = await _getLatestRelease( + '.apk', + 'revanced/revanced-manager', + ); + File? outputFile; + await for (final result in DefaultCacheManager().getFileStream( + release!['browser_download_url'] as String, + withProgress: true, + )) { + if (result is DownloadProgress) { + final totalSize = result.totalSize ?? 10000000; + final progress = (result.downloaded / totalSize * 100).round(); + + updateManagerDownloadProgress(progress); + } else if (result is FileInfo) { + // The download is complete; convert the FileInfo object to a File object + outputFile = File(result.file.path); + } + } + return outputFile; + } + Future getLatestReleaseTime( String extension, String repoName, diff --git a/lib/ui/views/home/home_viewmodel.dart b/lib/ui/views/home/home_viewmodel.dart index c63e8b22..4275f205 100644 --- a/lib/ui/views/home/home_viewmodel.dart +++ b/lib/ui/views/home/home_viewmodel.dart @@ -1,4 +1,5 @@ // ignore_for_file: use_build_context_synchronously +import 'dart:async'; import 'dart:io'; import 'package:app_installer/app_installer.dart'; @@ -8,36 +9,43 @@ import 'package:flutter/material.dart'; import 'package:flutter_i18n/flutter_i18n.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:injectable/injectable.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:revanced_manager/app/app.locator.dart'; import 'package:revanced_manager/app/app.router.dart'; import 'package:revanced_manager/models/patched_application.dart'; import 'package:revanced_manager/services/github_api.dart'; import 'package:revanced_manager/services/manager_api.dart'; import 'package:revanced_manager/services/patcher_api.dart'; +import 'package:revanced_manager/services/revanced_api.dart'; import 'package:revanced_manager/services/toast.dart'; import 'package:revanced_manager/ui/views/navigation/navigation_viewmodel.dart'; import 'package:revanced_manager/ui/views/patcher/patcher_viewmodel.dart'; import 'package:revanced_manager/ui/widgets/homeView/update_confirmation_dialog.dart'; +import 'package:revanced_manager/ui/widgets/shared/custom_material_button.dart'; +import 'package:revanced_manager/utils/about_info.dart'; import 'package:stacked/stacked.dart'; import 'package:stacked_services/stacked_services.dart'; -import 'package:timezone/timezone.dart' as tz; @lazySingleton class HomeViewModel extends BaseViewModel { - final NavigationService _navigationService = - locator(); + final NavigationService _navigationService = locator(); final ManagerAPI _managerAPI = locator(); final PatcherAPI _patcherAPI = locator(); final GithubAPI _githubAPI = locator(); + final RevancedAPI _revancedAPI = locator(); final Toast _toast = locator(); - final flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); + final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); DateTime? _lastUpdate; bool showUpdatableApps = false; List patchedInstalledApps = []; List patchedUpdatableApps = []; + String _managerVersion = ''; Future initialize(BuildContext context) async { + _managerVersion = await AboutInfo.getInfo().then( + (value) => value.keys.contains('version') ? value['version']! : '', + ); + _managerVersion = await _managerAPI.getCurrentManagerVersion(); await flutterLocalNotificationsPlugin.initialize( const InitializationSettings( android: AndroidInitializationSettings('ic_notification'), @@ -63,10 +71,8 @@ class HomeViewModel extends BaseViewModel { _toast.showBottom('homeView.noConnection'); } final NotificationAppLaunchDetails? notificationAppLaunchDetails = - await flutterLocalNotificationsPlugin - .getNotificationAppLaunchDetails(); - if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? - false) { + await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails(); + if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) { _toast.showBottom('homeView.installingMessage'); final File? managerApk = await _managerAPI.downloadManager(); if (managerApk != null) { @@ -109,10 +115,8 @@ class HomeViewModel extends BaseViewModel { } Future hasManagerUpdates() async { - final String? latestVersion = - await _managerAPI.getLatestManagerVersion(); - final String currentVersion = - await _managerAPI.getCurrentManagerVersion(); + final String? latestVersion = await _managerAPI.getLatestManagerVersion(); + final String currentVersion = await _managerAPI.getCurrentManagerVersion(); if (latestVersion != null) { try { final int latestVersionInt = @@ -150,35 +154,115 @@ class HomeViewModel extends BaseViewModel { return false; } + Future downloadManager() async { + try { + final response = await _revancedAPI.downloadManager(); + final bytes = await response!.readAsBytes(); + final tempDir = await getTemporaryDirectory(); + final tempFile = File('${tempDir.path}/revanced-manager.apk'); + await tempFile.writeAsBytes(bytes); + return tempFile; + } on Exception catch (e) { + if (kDebugMode) { + print(e); + } + return null; + } + } + Future updateManager(BuildContext context) async { try { _toast.showBottom('homeView.downloadingMessage'); - final File? managerApk = await _managerAPI.downloadManager(); - if (managerApk != null) { - await flutterLocalNotificationsPlugin.zonedSchedule( - 0, - FlutterI18n.translate( - context, - 'homeView.notificationTitle', - ), - FlutterI18n.translate( - context, - 'homeView.notificationText', - ), - tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)), - const NotificationDetails( - android: AndroidNotificationDetails( - 'revanced_manager_channel', - 'ReVanced Manager Channel', - importance: Importance.max, - priority: Priority.high, - ticker: 'ticker', + showDialog( + context: context, + builder: (context) => SimpleDialog( + contentPadding: const EdgeInsets.all(16.0), + title: I18nText( + 'homeView.downloadingMessage', + child: Text( + '', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.secondary, + ), ), ), - androidAllowWhileIdle: true, - uiLocalNotificationDateInterpretation: - UILocalNotificationDateInterpretation.absoluteTime, - ); + children: [ + Column( + children: [ + Row( + children: [ + Icon( + Icons.new_releases_outlined, + color: Theme.of(context).colorScheme.secondary, + ), + const SizedBox(width: 8.0), + Text( + 'v$_managerVersion', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.secondary, + ), + ), + ], + ), + const SizedBox(height: 16.0), + StreamBuilder( + initialData: 0.0, + stream: _revancedAPI.managerUpdateProgress.stream, + builder: (context, snapshot) { + return LinearProgressIndicator( + value: snapshot.data! * 0.01, + valueColor: AlwaysStoppedAnimation( + Theme.of(context).colorScheme.secondary, + ), + ); + }, + ), + const SizedBox(height: 16.0), + Align( + alignment: Alignment.centerRight, + child: CustomMaterialButton( + label: I18nText('cancelButton'), + onPressed: () { + _revancedAPI.disposeManagerUpdateProgress(); + Navigator.of(context).pop(); + }, + ), + ), + ], + ), + ], + ), + ); + final File? managerApk = await downloadManager(); + if (managerApk != null) { + // await flutterLocalNotificationsPlugin.zonedSchedule( + // 0, + // FlutterI18n.translate( + // context, + // 'homeView.notificationTitle', + // ), + // FlutterI18n.translate( + // context, + // 'homeView.notificationText', + // ), + // tz.TZDateTime.now(tz.local).add(const Duration(seconds: 5)), + // const NotificationDetails( + // android: AndroidNotificationDetails( + // 'revanced_manager_channel', + // 'ReVanced Manager Channel', + // importance: Importance.max, + // priority: Priority.high, + // ticker: 'ticker', + // ), + // ), + // androidAllowWhileIdle: true, + // uiLocalNotificationDateInterpretation: + // UILocalNotificationDateInterpretation.absoluteTime, + // ); _toast.showBottom('homeView.installingMessage'); await AppInstaller.installApk(managerApk.path); } else {