@ -9,14 +9,17 @@ import 'package:immich_mobile/modules/backup/models/current_upload_asset.model.d
import ' package:immich_mobile/modules/backup/models/error_upload_asset.model.dart ' ;
import ' package:immich_mobile/modules/backup/models/manual_upload_state.model.dart ' ;
import ' package:immich_mobile/modules/backup/providers/backup.provider.dart ' ;
import ' package:immich_mobile/modules/backup/providers/error_backup_list.provider.dart ' ;
import ' package:immich_mobile/modules/backup/services/backup.service.dart ' ;
import ' package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart ' ;
import ' package:immich_mobile/modules/settings/providers/app_settings.provider.dart ' ;
import ' package:immich_mobile/modules/settings/services/app_settings.service.dart ' ;
import ' package:immich_mobile/shared/models/asset.dart ' ;
import ' package:immich_mobile/shared/providers/app_state.provider.dart ' ;
import ' package:immich_mobile/shared/services/local_notification.service.dart ' ;
import ' package:immich_mobile/shared/ui/immich_toast.dart ' ;
import ' package:immich_mobile/utils/backup_progress.dart ' ;
import ' package:logging/logging.dart ' ;
import ' package:permission_handler/permission_handler.dart ' ;
import ' package:photo_manager/photo_manager.dart ' ;
@ -24,24 +27,19 @@ final manualUploadProvider =
StateNotifierProvider < ManualUploadNotifier , ManualUploadState > ( ( ref ) {
return ManualUploadNotifier (
ref . watch ( localNotificationService ) ,
ref . watch ( backgroundServiceProvider ) ,
ref . watch ( backupServiceProvider ) ,
ref . watch ( backupProvider . notifier ) ,
ref ,
) ;
} ) ;
class ManualUploadNotifier extends StateNotifier < ManualUploadState > {
final Logger _log = Logger ( " ManualUploadNotifier " ) ;
final LocalNotificationService _localNotificationService ;
final BackgroundService _backgroundService ;
final BackupService _backupService ;
final BackupNotifier _backupProvider ;
final Ref ref ;
ManualUploadNotifier (
this . _localNotificationService ,
this . _backgroundService ,
this . _backupService ,
this . _backupProvider ,
this . ref ,
) : super (
@ -54,15 +52,13 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
fileName: ' ... ' ,
fileType: ' ... ' ,
) ,
manualUploadsTotal: 0 ,
manualUploadSuccess: 0 ,
manualUploadFailures: 0 ,
totalAssetsToUpload: 0 ,
successfulUploads: 0 ,
currentAssetIndex: 0 ,
showDetailedNotification: false ,
) ,
) ;
int get _uploadedAssetsCount = >
state . manualUploadSuccess + state . manualUploadFailures ;
String _lastPrintedDetailContent = ' ' ;
String ? _lastPrintedDetailTitle ;
@ -78,11 +74,12 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
_localNotificationService . showOrUpdateManualUploadStatus (
" backup_background_service_in_progress_notification " . tr ( ) ,
formatAssetBackupProgress (
_uploadedAssetsCount ,
state . manualUploadsTotal ,
state. currentAssetIndex ,
state . totalAssetsToUpload ,
) ,
maxProgress: state . manualUploadsTotal ,
progress: _uploadedAssetsCount ,
maxProgress: state . totalAssetsToUpload ,
progress: state . currentAssetIndex ,
showActions: true ,
) ;
}
}
@ -103,44 +100,52 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
progress: total > 0 ? ( progress * 1000 ) ~ / total : 0 ,
maxProgress: 1000 ,
isDetailed: true ,
/ / Detailed noitifcation is displayed for Single asset uploads . Show actions for such case
showActions: state . totalAssetsToUpload = = 1 ,
) ;
}
}
}
void _on Manual AssetUploaded(
void _on AssetUploaded(
String deviceAssetId ,
String deviceId ,
bool isDuplicated ,
) {
state = state . copyWith ( manualUploadSuccess: state . manualUploadSucces s + 1 ) ;
state = state . copyWith ( successfulUploads: state . successfulUpload s + 1 ) ;
_backupProvider . updateServerInfo ( ) ;
if ( state . manualUploadsTotal > 1 ) {
_throttledNotifiy ( ) ;
}
}
void _onManualBackupError ( ErrorUploadAsset errorAssetInfo ) {
state =
state . copyWith ( manualUploadFailures: state . manualUploadFailures + 1 ) ;
if ( state . manualUploadsTotal > 1 ) {
_throttledNotifiy ( ) ;
}
void _onAssetUploadError ( ErrorUploadAsset errorAssetInfo ) {
ref . watch ( errorBackupListProvider . notifier ) . add ( errorAssetInfo ) ;
}
void _onProgress ( int sent , int total ) {
final title = " backup_background_service_current_upload_notification "
. tr ( args: [ state . currentUploadAsset . fileName ] ) ;
_throttledDetailNotify ( title: title , progress: sent , total: total ) ;
state = state . copyWith (
progressInPercentage: ( sent . toDouble ( ) / total . toDouble ( ) * 100 ) ,
) ;
if ( state . showDetailedNotification ) {
final title = " backup_background_service_current_upload_notification "
. tr ( args: [ state . currentUploadAsset . fileName ] ) ;
_throttledDetailNotify ( title: title , progress: sent , total: total ) ;
}
}
void _onSetCurrentBackupAsset ( CurrentUploadAsset currentUploadAsset ) {
state = state . copyWith ( currentUploadAsset: currentUploadAsset ) ;
_throttledDetailNotify . title =
" backup_background_service_current_upload_notification "
. tr ( args: [ currentUploadAsset . fileName ] ) ;
_throttledDetailNotify . progress = 0 ;
_throttledDetailNotify . total = 0 ;
state = state . copyWith (
currentUploadAsset: currentUploadAsset ,
currentAssetIndex: state . currentAssetIndex + 1 ,
) ;
if ( state . totalAssetsToUpload > 1 ) {
_throttledNotifiy ( ) ;
}
if ( state . showDetailedNotification ) {
_throttledDetailNotify . title =
" backup_background_service_current_upload_notification "
. tr ( args: [ currentUploadAsset . fileName ] ) ;
_throttledDetailNotify . progress = 0 ;
_throttledDetailNotify . total = 0 ;
}
}
Future < bool > _startUpload ( Iterable < Asset > allManualUploads ) async {
@ -161,11 +166,11 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
return false ;
}
/ / Reset state
state = state . copyWith (
manualUploadsTotal: allManualUploads . length ,
manualUploadSuccess: 0 ,
manualUploadFailures: 0 ,
progressInPercentage: 0 ,
totalAssetsToUpload: allUploadAssets . length ,
successfulUploads: 0 ,
currentAssetIndex: 0 ,
currentUploadAsset: CurrentUploadAsset (
id: ' ... ' ,
fileCreatedAt: DateTime . parse ( ' 2020-10-04 ' ) ,
@ -174,8 +179,10 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
) ,
cancelToken: CancellationToken ( ) ,
) ;
/ / Reset Error List
ref . watch ( errorBackupListProvider . notifier ) . empty ( ) ;
if ( state . manualUploadsTotal > 1 ) {
if ( state . totalAssetsToUpload > 1 ) {
_throttledNotifiy ( ) ;
}
@ -184,25 +191,38 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
ref . read ( appSettingsServiceProvider ) . getSetting < bool > (
AppSettingsEnum . backgroundBackupSingleProgress ,
) | |
state . manualUploadsTotal = = 1 ;
state . totalAssetsToUpload = = 1 ;
state =
state . copyWith ( showDetailedNotification: showDetailedNotification ) ;
final bool ok = await _backupService . backupAsset (
allUploadAssets ,
state . cancelToken ,
_on Manual AssetUploaded,
showDetailedNotification ? _onProgress : ( sent , total ) { } ,
showDetailedNotification ? _onSetCurrentBackupAsset : ( asset ) { } ,
_onManualBackup Error,
) ;
final bool ok = await ref. read ( backupServiceProvider ) . backupAsset (
allUploadAssets ,
state . cancelToken ,
_on AssetUploaded,
_onProgress ,
_onSetCurrentBackupAsset ,
_onAssetUpload Error,
) ;
/ / Close detailed notification
await _localNotificationService . closeNotification (
LocalNotificationService . manualUploadDetailedNotificationID ,
) ;
_log . info (
' [_startUpload] Manual Upload Completed - success: ${ state . successfulUploads } , '
' failed: ${ state . totalAssetsToUpload - state . successfulUploads } ' ,
) ;
bool hasErrors = false ;
if ( ( state . manualUploadFailures ! = 0 & &
state . manualUploadSuccess = = 0 ) | |
/ / User cancelled upload
if ( ! ok & & state . cancelToken . isCancelled ) {
await _localNotificationService . showOrUpdateManualUploadStatus (
" backup_manual_title " . tr ( ) ,
" backup_manual_cancelled " . tr ( ) ,
presentBanner: true ,
) ;
hasErrors = true ;
} else if ( state . successfulUploads = = 0 | |
( ! ok & & ! state . cancelToken . isCancelled ) ) {
await _localNotificationService . showOrUpdateManualUploadStatus (
" backup_manual_title " . tr ( ) ,
@ -210,7 +230,7 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
presentBanner: true ,
) ;
hasErrors = true ;
} else if ( state . manualUploadSuccess ! = 0 ) {
} else {
await _localNotificationService . showOrUpdateManualUploadStatus (
" backup_manual_title " . tr ( ) ,
" backup_manual_success " . tr ( ) ,
@ -219,6 +239,7 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
}
_backupProvider . updateBackupProgress ( BackUpProgressEnum . idle ) ;
_handleAppInActivity ( ) ;
await _backupProvider . notifyBackgroundServiceCanRun ( ) ;
return ! hasErrors ;
} else {
@ -228,20 +249,34 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
} catch ( e ) {
debugPrint ( " ERROR _startUpload: ${ e . toString ( ) } " ) ;
}
_backupProvider . updateBackupProgress ( BackUpProgressEnum . idle ) ;
_handleAppInActivity ( ) ;
await _localNotificationService . closeNotification (
LocalNotificationService . manualUploadDetailedNotificationID ,
) ;
_backupProvider . updateBackupProgress ( BackUpProgressEnum . idle ) ;
await _backupProvider . notifyBackgroundServiceCanRun ( ) ;
return false ;
}
void _handleAppInActivity ( ) {
final appState = ref . read ( appStateProvider . notifier ) . getAppState ( ) ;
/ / The app is currently in background . Perform the necessary cleanups which
/ / are on - hold for upload completion
if ( appState ! = AppStateEnum . active | | appState ! = AppStateEnum . resumed ) {
ref . read ( appStateProvider . notifier ) . handleAppInactivity ( ) ;
}
}
void cancelBackup ( ) {
if ( _backupProvider . backupProgress ! = BackUpProgressEnum . manualInProgress ) {
if ( _backupProvider . backupProgress ! = BackUpProgressEnum . inProgress & &
_backupProvider . backupProgress ! = BackUpProgressEnum . manualInProgress ) {
_backupProvider . notifyBackgroundServiceCanRun ( ) ;
}
state . cancelToken . cancel ( ) ;
_backupProvider . updateBackupProgress ( BackUpProgressEnum . idle ) ;
if ( _backupProvider . backupProgress ! = BackUpProgressEnum . manualInProgress ) {
_backupProvider . updateBackupProgress ( BackUpProgressEnum . idle ) ;
}
state = state . copyWith ( progressInPercentage: 0 ) ;
}
Future < bool > uploadAssets (
@ -250,7 +285,8 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
) async {
/ / assumes the background service is currently running and
/ / waits until it has stopped to start the backup .
final bool hasLock = await _backgroundService . acquireLock ( ) ;
final bool hasLock =
await ref . read ( backgroundServiceProvider ) . acquireLock ( ) ;
if ( ! hasLock ) {
debugPrint ( " [uploadAssets] could not acquire lock, exiting " ) ;
ImmichToast . show (