diff --git a/mobile/lib/modules/backup/models/backup_state.model.dart b/mobile/lib/modules/backup/models/backup_state.model.dart index dd90251b88..3a9003731c 100644 --- a/mobile/lib/modules/backup/models/backup_state.model.dart +++ b/mobile/lib/modules/backup/models/backup_state.model.dart @@ -21,6 +21,11 @@ class BackUpState { final BackUpProgressEnum backupProgress; final List allAssetsInDatabase; final double progressInPercentage; + final String progressInFileSize; + final double progressInFileSpeed; + final List progressInFileSpeeds; + final DateTime progressInFileSpeedUpdateTime; + final int progressInFileSpeedUpdateSentBytes; final double iCloudDownloadProgress; final CancellationToken cancelToken; final ServerDiskInfo serverInfo; @@ -48,6 +53,11 @@ class BackUpState { required this.backupProgress, required this.allAssetsInDatabase, required this.progressInPercentage, + required this.progressInFileSize, + required this.progressInFileSpeed, + required this.progressInFileSpeeds, + required this.progressInFileSpeedUpdateTime, + required this.progressInFileSpeedUpdateSentBytes, required this.iCloudDownloadProgress, required this.cancelToken, required this.serverInfo, @@ -68,6 +78,11 @@ class BackUpState { BackUpProgressEnum? backupProgress, List? allAssetsInDatabase, double? progressInPercentage, + String? progressInFileSize, + double? progressInFileSpeed, + List? progressInFileSpeeds, + DateTime? progressInFileSpeedUpdateTime, + int? progressInFileSpeedUpdateSentBytes, double? iCloudDownloadProgress, CancellationToken? cancelToken, ServerDiskInfo? serverInfo, @@ -87,6 +102,13 @@ class BackUpState { backupProgress: backupProgress ?? this.backupProgress, allAssetsInDatabase: allAssetsInDatabase ?? this.allAssetsInDatabase, progressInPercentage: progressInPercentage ?? this.progressInPercentage, + progressInFileSize: progressInFileSize ?? this.progressInFileSize, + progressInFileSpeed: progressInFileSpeed ?? this.progressInFileSpeed, + progressInFileSpeeds: progressInFileSpeeds ?? this.progressInFileSpeeds, + progressInFileSpeedUpdateTime: + progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, + progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? + this.progressInFileSpeedUpdateSentBytes, iCloudDownloadProgress: iCloudDownloadProgress ?? this.iCloudDownloadProgress, cancelToken: cancelToken ?? this.cancelToken, @@ -109,7 +131,7 @@ class BackUpState { @override String toString() { - return 'BackUpState(backupProgress: $backupProgress, allAssetsInDatabase: $allAssetsInDatabase, progressInPercentage: $progressInPercentage, iCloudDownloadProgress: $iCloudDownloadProgress, cancelToken: $cancelToken, serverInfo: $serverInfo, autoBackup: $autoBackup, backgroundBackup: $backgroundBackup, backupRequireWifi: $backupRequireWifi, backupRequireCharging: $backupRequireCharging, backupTriggerDelay: $backupTriggerDelay, availableAlbums: $availableAlbums, selectedBackupAlbums: $selectedBackupAlbums, excludedBackupAlbums: $excludedBackupAlbums, allUniqueAssets: $allUniqueAssets, selectedAlbumsBackupAssetsIds: $selectedAlbumsBackupAssetsIds, currentUploadAsset: $currentUploadAsset)'; + return 'BackUpState(backupProgress: $backupProgress, allAssetsInDatabase: $allAssetsInDatabase, progressInPercentage: $progressInPercentage, progressInFileSize: $progressInFileSize, progressInFileSpeed: $progressInFileSpeed, progressInFileSpeeds: $progressInFileSpeeds, progressInFileSpeedUpdateTime: $progressInFileSpeedUpdateTime, progressInFileSpeedUpdateSentBytes: $progressInFileSpeedUpdateSentBytes, iCloudDownloadProgress: $iCloudDownloadProgress, cancelToken: $cancelToken, serverInfo: $serverInfo, autoBackup: $autoBackup, backgroundBackup: $backgroundBackup, backupRequireWifi: $backupRequireWifi, backupRequireCharging: $backupRequireCharging, backupTriggerDelay: $backupTriggerDelay, availableAlbums: $availableAlbums, selectedBackupAlbums: $selectedBackupAlbums, excludedBackupAlbums: $excludedBackupAlbums, allUniqueAssets: $allUniqueAssets, selectedAlbumsBackupAssetsIds: $selectedAlbumsBackupAssetsIds, currentUploadAsset: $currentUploadAsset)'; } @override @@ -120,6 +142,12 @@ class BackUpState { return other.backupProgress == backupProgress && collectionEquals(other.allAssetsInDatabase, allAssetsInDatabase) && other.progressInPercentage == progressInPercentage && + other.progressInFileSize == progressInFileSize && + other.progressInFileSpeed == progressInFileSpeed && + collectionEquals(other.progressInFileSpeeds, progressInFileSpeeds) && + other.progressInFileSpeedUpdateTime == progressInFileSpeedUpdateTime && + other.progressInFileSpeedUpdateSentBytes == + progressInFileSpeedUpdateSentBytes && other.iCloudDownloadProgress == iCloudDownloadProgress && other.cancelToken == cancelToken && other.serverInfo == serverInfo && @@ -144,6 +172,11 @@ class BackUpState { return backupProgress.hashCode ^ allAssetsInDatabase.hashCode ^ progressInPercentage.hashCode ^ + progressInFileSize.hashCode ^ + progressInFileSpeed.hashCode ^ + progressInFileSpeeds.hashCode ^ + progressInFileSpeedUpdateTime.hashCode ^ + progressInFileSpeedUpdateSentBytes.hashCode ^ iCloudDownloadProgress.hashCode ^ cancelToken.hashCode ^ serverInfo.hashCode ^ diff --git a/mobile/lib/modules/backup/models/current_upload_asset.model.dart b/mobile/lib/modules/backup/models/current_upload_asset.model.dart index ae75f68f88..9a761c9e4a 100644 --- a/mobile/lib/modules/backup/models/current_upload_asset.model.dart +++ b/mobile/lib/modules/backup/models/current_upload_asset.model.dart @@ -6,6 +6,7 @@ class CurrentUploadAsset { final DateTime fileCreatedAt; final String fileName; final String fileType; + final int? fileSize; final bool? iCloudAsset; CurrentUploadAsset({ @@ -13,6 +14,7 @@ class CurrentUploadAsset { required this.fileCreatedAt, required this.fileName, required this.fileType, + this.fileSize, this.iCloudAsset, }); @@ -21,6 +23,7 @@ class CurrentUploadAsset { DateTime? fileCreatedAt, String? fileName, String? fileType, + int? fileSize, bool? iCloudAsset, }) { return CurrentUploadAsset( @@ -28,6 +31,7 @@ class CurrentUploadAsset { fileCreatedAt: fileCreatedAt ?? this.fileCreatedAt, fileName: fileName ?? this.fileName, fileType: fileType ?? this.fileType, + fileSize: fileSize ?? this.fileSize, iCloudAsset: iCloudAsset ?? this.iCloudAsset, ); } @@ -38,6 +42,7 @@ class CurrentUploadAsset { 'fileCreatedAt': fileCreatedAt.millisecondsSinceEpoch, 'fileName': fileName, 'fileType': fileType, + 'fileSize': fileSize, 'iCloudAsset': iCloudAsset, }; } @@ -49,6 +54,7 @@ class CurrentUploadAsset { DateTime.fromMillisecondsSinceEpoch(map['fileCreatedAt'] as int), fileName: map['fileName'] as String, fileType: map['fileType'] as String, + fileSize: map['fileSize'] as int, iCloudAsset: map['iCloudAsset'] != null ? map['iCloudAsset'] as bool : null, ); @@ -61,7 +67,7 @@ class CurrentUploadAsset { @override String toString() { - return 'CurrentUploadAsset(id: $id, fileCreatedAt: $fileCreatedAt, fileName: $fileName, fileType: $fileType, iCloudAsset: $iCloudAsset)'; + return 'CurrentUploadAsset(id: $id, fileCreatedAt: $fileCreatedAt, fileName: $fileName, fileType: $fileType, fileSize: $fileSize, iCloudAsset: $iCloudAsset)'; } @override @@ -72,6 +78,7 @@ class CurrentUploadAsset { other.fileCreatedAt == fileCreatedAt && other.fileName == fileName && other.fileType == fileType && + other.fileSize == fileSize && other.iCloudAsset == iCloudAsset; } @@ -81,6 +88,7 @@ class CurrentUploadAsset { fileCreatedAt.hashCode ^ fileName.hashCode ^ fileType.hashCode ^ + fileSize.hashCode ^ iCloudAsset.hashCode; } } diff --git a/mobile/lib/modules/backup/models/manual_upload_state.model.dart b/mobile/lib/modules/backup/models/manual_upload_state.model.dart index 1a83609c2f..3b56672cf8 100644 --- a/mobile/lib/modules/backup/models/manual_upload_state.model.dart +++ b/mobile/lib/modules/backup/models/manual_upload_state.model.dart @@ -1,4 +1,6 @@ import 'package:cancellation_token_http/http.dart'; +import 'package:collection/collection.dart'; + import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.dart'; class ManualUploadState { @@ -14,9 +16,19 @@ class ManualUploadState { final int totalAssetsToUpload; final int successfulUploads; final double progressInPercentage; + final String progressInFileSize; + final double progressInFileSpeed; + final List progressInFileSpeeds; + final DateTime progressInFileSpeedUpdateTime; + final int progressInFileSpeedUpdateSentBytes; const ManualUploadState({ required this.progressInPercentage, + required this.progressInFileSize, + required this.progressInFileSpeed, + required this.progressInFileSpeeds, + required this.progressInFileSpeedUpdateTime, + required this.progressInFileSpeedUpdateSentBytes, required this.cancelToken, required this.currentUploadAsset, required this.totalAssetsToUpload, @@ -27,6 +39,11 @@ class ManualUploadState { ManualUploadState copyWith({ double? progressInPercentage, + String? progressInFileSize, + double? progressInFileSpeed, + List? progressInFileSpeeds, + DateTime? progressInFileSpeedUpdateTime, + int? progressInFileSpeedUpdateSentBytes, CancellationToken? cancelToken, CurrentUploadAsset? currentUploadAsset, int? totalAssetsToUpload, @@ -36,6 +53,13 @@ class ManualUploadState { }) { return ManualUploadState( progressInPercentage: progressInPercentage ?? this.progressInPercentage, + progressInFileSize: progressInFileSize ?? this.progressInFileSize, + progressInFileSpeed: progressInFileSpeed ?? this.progressInFileSpeed, + progressInFileSpeeds: progressInFileSpeeds ?? this.progressInFileSpeeds, + progressInFileSpeedUpdateTime: + progressInFileSpeedUpdateTime ?? this.progressInFileSpeedUpdateTime, + progressInFileSpeedUpdateSentBytes: progressInFileSpeedUpdateSentBytes ?? + this.progressInFileSpeedUpdateSentBytes, cancelToken: cancelToken ?? this.cancelToken, currentUploadAsset: currentUploadAsset ?? this.currentUploadAsset, totalAssetsToUpload: totalAssetsToUpload ?? this.totalAssetsToUpload, @@ -48,15 +72,22 @@ class ManualUploadState { @override String toString() { - return 'ManualUploadState(progressInPercentage: $progressInPercentage, cancelToken: $cancelToken, currentUploadAsset: $currentUploadAsset, totalAssetsToUpload: $totalAssetsToUpload, successfulUploads: $successfulUploads, currentAssetIndex: $currentAssetIndex, showDetailedNotification: $showDetailedNotification)'; + return 'ManualUploadState(progressInPercentage: $progressInPercentage, progressInFileSize: $progressInFileSize, progressInFileSpeed: $progressInFileSpeed, progressInFileSpeeds: $progressInFileSpeeds, progressInFileSpeedUpdateTime: $progressInFileSpeedUpdateTime, progressInFileSpeedUpdateSentBytes: $progressInFileSpeedUpdateSentBytes, cancelToken: $cancelToken, currentUploadAsset: $currentUploadAsset, totalAssetsToUpload: $totalAssetsToUpload, successfulUploads: $successfulUploads, currentAssetIndex: $currentAssetIndex, showDetailedNotification: $showDetailedNotification)'; } @override bool operator ==(Object other) { if (identical(this, other)) return true; + final collectionEquals = const DeepCollectionEquality().equals; return other is ManualUploadState && other.progressInPercentage == progressInPercentage && + other.progressInFileSize == progressInFileSize && + other.progressInFileSpeed == progressInFileSpeed && + collectionEquals(other.progressInFileSpeeds, progressInFileSpeeds) && + other.progressInFileSpeedUpdateTime == progressInFileSpeedUpdateTime && + other.progressInFileSpeedUpdateSentBytes == + progressInFileSpeedUpdateSentBytes && other.cancelToken == cancelToken && other.currentUploadAsset == currentUploadAsset && other.totalAssetsToUpload == totalAssetsToUpload && @@ -68,6 +99,11 @@ class ManualUploadState { @override int get hashCode { return progressInPercentage.hashCode ^ + progressInFileSize.hashCode ^ + progressInFileSpeed.hashCode ^ + progressInFileSpeeds.hashCode ^ + progressInFileSpeedUpdateTime.hashCode ^ + progressInFileSpeedUpdateSentBytes.hashCode ^ cancelToken.hashCode ^ currentUploadAsset.hashCode ^ totalAssetsToUpload.hashCode ^ diff --git a/mobile/lib/modules/backup/providers/backup.provider.dart b/mobile/lib/modules/backup/providers/backup.provider.dart index 68c6bf9e66..a02ddf4e35 100644 --- a/mobile/lib/modules/backup/providers/backup.provider.dart +++ b/mobile/lib/modules/backup/providers/backup.provider.dart @@ -20,6 +20,7 @@ import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/providers/app_state.provider.dart'; import 'package:immich_mobile/shared/providers/db.provider.dart'; import 'package:immich_mobile/shared/services/server_info.service.dart'; +import 'package:immich_mobile/utils/backup_progress.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; @@ -40,6 +41,11 @@ class BackupNotifier extends StateNotifier { backupProgress: BackUpProgressEnum.idle, allAssetsInDatabase: const [], progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, cancelToken: CancellationToken(), autoBackup: Store.get(StoreKey.autoBackup, false), backgroundBackup: Store.get(StoreKey.backgroundBackup, false), @@ -63,6 +69,7 @@ class BackupNotifier extends StateNotifier { fileCreatedAt: DateTime.parse('2020-10-04'), fileName: '...', fileType: '...', + fileSize: 0, iCloudAsset: false, ), iCloudDownloadProgress: 0.0, @@ -495,6 +502,10 @@ class BackupNotifier extends StateNotifier { state = state.copyWith( backupProgress: BackUpProgressEnum.idle, progressInPercentage: 0.0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, ); } @@ -535,6 +546,10 @@ class BackupNotifier extends StateNotifier { .toSet(), backupProgress: BackUpProgressEnum.done, progressInPercentage: 0.0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, ); _updatePersistentAlbumsSelection(); } @@ -543,8 +558,36 @@ class BackupNotifier extends StateNotifier { } void _onUploadProgress(int sent, int total) { + double lastUploadSpeed = state.progressInFileSpeed; + List lastUploadSpeeds = state.progressInFileSpeeds.toList(); + DateTime lastUpdateTime = state.progressInFileSpeedUpdateTime; + int lastSentBytes = state.progressInFileSpeedUpdateSentBytes; + + final now = DateTime.now(); + final duration = now.difference(lastUpdateTime); + + // Keep the upload speed average span limited, to keep it somewhat relevant + if (lastUploadSpeeds.length > 10) { + lastUploadSpeeds.removeAt(0); + } + + if (duration.inSeconds > 0) { + lastUploadSpeeds.add( + ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), + ); + + lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); + lastUpdateTime = now; + lastSentBytes = sent; + } + state = state.copyWith( progressInPercentage: (sent.toDouble() / total.toDouble() * 100), + progressInFileSize: humanReadableFileBytesProgress(sent, total), + progressInFileSpeed: lastUploadSpeed, + progressInFileSpeeds: lastUploadSpeeds, + progressInFileSpeedUpdateTime: lastUpdateTime, + progressInFileSpeedUpdateSentBytes: lastSentBytes, ); } diff --git a/mobile/lib/modules/backup/providers/manual_upload.provider.dart b/mobile/lib/modules/backup/providers/manual_upload.provider.dart index bd7756adf7..6d9ecbd206 100644 --- a/mobile/lib/modules/backup/providers/manual_upload.provider.dart +++ b/mobile/lib/modules/backup/providers/manual_upload.provider.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:cancellation_token_http/http.dart'; +import 'package:collection/collection.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/widgets.dart'; import 'package:fluttertoast/fluttertoast.dart'; @@ -47,6 +48,11 @@ class ManualUploadNotifier extends StateNotifier { ) : super( ManualUploadState( progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeeds: const [], + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, cancelToken: CancellationToken(), currentUploadAsset: CurrentUploadAsset( id: '...', @@ -123,9 +129,38 @@ class ManualUploadNotifier extends StateNotifier { } void _onProgress(int sent, int total) { + double lastUploadSpeed = state.progressInFileSpeed; + List lastUploadSpeeds = state.progressInFileSpeeds.toList(); + DateTime lastUpdateTime = state.progressInFileSpeedUpdateTime; + int lastSentBytes = state.progressInFileSpeedUpdateSentBytes; + + final now = DateTime.now(); + final duration = now.difference(lastUpdateTime); + + // Keep the upload speed average span limited, to keep it somewhat relevant + if (lastUploadSpeeds.length > 10) { + lastUploadSpeeds.removeAt(0); + } + + if (duration.inSeconds > 0) { + lastUploadSpeeds.add( + ((sent - lastSentBytes) / duration.inSeconds).abs().roundToDouble(), + ); + + lastUploadSpeed = lastUploadSpeeds.average.abs().roundToDouble(); + lastUpdateTime = now; + lastSentBytes = sent; + } + state = state.copyWith( progressInPercentage: (sent.toDouble() / total.toDouble() * 100), + progressInFileSize: humanReadableFileBytesProgress(sent, total), + progressInFileSpeed: lastUploadSpeed, + progressInFileSpeeds: lastUploadSpeeds, + progressInFileSpeedUpdateTime: lastUpdateTime, + progressInFileSpeedUpdateSentBytes: lastSentBytes, ); + if (state.showDetailedNotification) { final title = "backup_background_service_current_upload_notification" .tr(args: [state.currentUploadAsset.fileName]); @@ -184,6 +219,8 @@ class ManualUploadNotifier extends StateNotifier { state = state.copyWith( progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, totalAssetsToUpload: allUploadAssets.length, successfulUploads: 0, currentAssetIndex: 0, @@ -291,7 +328,13 @@ class ManualUploadNotifier extends StateNotifier { if (_backupProvider.backupProgress != BackUpProgressEnum.manualInProgress) { _backupProvider.updateBackupProgress(BackUpProgressEnum.idle); } - state = state.copyWith(progressInPercentage: 0); + state = state.copyWith( + progressInPercentage: 0, + progressInFileSize: "0 B / 0 B", + progressInFileSpeed: 0, + progressInFileSpeedUpdateTime: DateTime.now(), + progressInFileSpeedUpdateSentBytes: 0, + ); } Future uploadAssets( diff --git a/mobile/lib/modules/backup/services/backup.service.dart b/mobile/lib/modules/backup/services/backup.service.dart index 48d6f71cf9..d4277a822e 100644 --- a/mobile/lib/modules/backup/services/backup.service.dart +++ b/mobile/lib/modules/backup/services/backup.service.dart @@ -316,6 +316,8 @@ class BackupService { req.files.add(assetRawUploadData); + var fileSize = file.lengthSync(); + if (entity.isLivePhoto) { if (livePhotoFile != null) { final livePhotoTitle = p.setExtension( @@ -330,6 +332,7 @@ class BackupService { filename: livePhotoTitle, ); req.files.add(livePhotoRawUploadData); + fileSize += livePhotoFile.lengthSync(); } else { _log.warning( "Failed to obtain motion part of the livePhoto - $originalFileName", @@ -345,6 +348,7 @@ class BackupService { : entity.createDateTime, fileName: originalFileName, fileType: _getAssetType(entity.type), + fileSize: fileSize, iCloudAsset: false, ), ); diff --git a/mobile/lib/modules/backup/ui/current_backup_asset_info_box.dart b/mobile/lib/modules/backup/ui/current_backup_asset_info_box.dart index 417fd3be59..35bee2f8d1 100644 --- a/mobile/lib/modules/backup/ui/current_backup_asset_info_box.dart +++ b/mobile/lib/modules/backup/ui/current_backup_asset_info_box.dart @@ -26,10 +26,28 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget { var uploadProgress = !isManualUpload ? ref.watch(backupProvider).progressInPercentage : ref.watch(manualUploadProvider).progressInPercentage; + var uploadFileProgress = !isManualUpload + ? ref.watch(backupProvider).progressInFileSize + : ref.watch(manualUploadProvider).progressInFileSize; + var uploadFileSpeed = !isManualUpload + ? ref.watch(backupProvider).progressInFileSpeed + : ref.watch(manualUploadProvider).progressInFileSpeed; var iCloudDownloadProgress = ref.watch(backupProvider).iCloudDownloadProgress; final isShowThumbnail = useState(false); + String formatUploadFileSpeed(double uploadFileSpeed) { + if (uploadFileSpeed < 1024) { + return '${uploadFileSpeed.toStringAsFixed(2)} B/s'; + } else if (uploadFileSpeed < 1024 * 1024) { + return '${(uploadFileSpeed / 1024).toStringAsFixed(2)} KB/s'; + } else if (uploadFileSpeed < 1024 * 1024 * 1024) { + return '${(uploadFileSpeed / (1024 * 1024)).toStringAsFixed(2)} MB/s'; + } else { + return '${(uploadFileSpeed / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB/s'; + } + } + String getAssetCreationDate() { return DateFormat.yMMMMd().format( DateTime.parse( @@ -202,7 +220,26 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget { ), Text( " ${uploadProgress.toStringAsFixed(0)}%", - style: const TextStyle(fontSize: 12), + style: const TextStyle(fontSize: 12, fontFamily: "OverpassMono"), + ), + ], + ), + ); + } + + buildUploadStats() { + return Padding( + padding: const EdgeInsets.only(top: 2.0, bottom: 2.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + uploadFileProgress, + style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), + ), + Text( + formatUploadFileSpeed(uploadFileSpeed), + style: const TextStyle(fontSize: 10, fontFamily: "OverpassMono"), ), ], ), @@ -265,6 +302,7 @@ class CurrentUploadingAssetInfoBox extends HookConsumerWidget { children: [ if (Platform.isIOS) buildiCloudDownloadProgerssBar(), buildUploadProgressBar(), + buildUploadStats(), Padding( padding: const EdgeInsets.only(top: 8.0), child: buildAssetInfoTable(), diff --git a/mobile/lib/utils/backup_progress.dart b/mobile/lib/utils/backup_progress.dart index f24e8c6cf9..38cdeacdb2 100644 --- a/mobile/lib/utils/backup_progress.dart +++ b/mobile/lib/utils/backup_progress.dart @@ -10,6 +10,25 @@ String formatAssetBackupProgress(int uploadedAssets, int assetsToUpload) { return "$percent% ($uploadedAssets/$assetsToUpload)"; } +/// prints progress in useful (kilo/mega/giga)bytes +String humanReadableFileBytesProgress(int bytes, int bytesTotal) { + String unit = "KB"; + + if (bytesTotal >= 0x40000000) { + unit = "GB"; + bytes >>= 20; + bytesTotal >>= 20; + } else if (bytesTotal >= 0x100000) { + unit = "MB"; + bytes >>= 10; + bytesTotal >>= 10; + } else if (bytesTotal < 0x400) { + return "${(bytes).toStringAsFixed(2)} B / ${(bytesTotal).toStringAsFixed(2)} B"; + } + + return "${(bytes / 1024.0).toStringAsFixed(2)} $unit / ${(bytesTotal / 1024.0).toStringAsFixed(2)} $unit"; +} + /// prints percentage and absolute progress in useful (kilo/mega/giga)bytes String humanReadableBytesProgress(int bytes, int bytesTotal) { String unit = "KB"; // Kilobyte