|
|
|
@ -61,7 +61,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
),
|
|
|
|
),
|
|
|
|
availableAlbums: const [],
|
|
|
|
availableAlbums: const [],
|
|
|
|
selectedBackupAlbums: const {},
|
|
|
|
selectedBackupAlbums: const {},
|
|
|
|
backupCandidates: const {},
|
|
|
|
excludedBackupAlbums: const {},
|
|
|
|
|
|
|
|
allUniqueAssets: const {},
|
|
|
|
selectedAlbumsBackupAssetsIds: const {},
|
|
|
|
selectedAlbumsBackupAssetsIds: const {},
|
|
|
|
currentUploadAsset: CurrentUploadAsset(
|
|
|
|
currentUploadAsset: CurrentUploadAsset(
|
|
|
|
id: '...',
|
|
|
|
id: '...',
|
|
|
|
@ -93,10 +94,22 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
/// The total unique assets will be used for backing mechanism
|
|
|
|
/// The total unique assets will be used for backing mechanism
|
|
|
|
///
|
|
|
|
///
|
|
|
|
void addAlbumForBackup(AvailableAlbum album) {
|
|
|
|
void addAlbumForBackup(AvailableAlbum album) {
|
|
|
|
|
|
|
|
if (state.excludedBackupAlbums.contains(album)) {
|
|
|
|
|
|
|
|
removeExcludedAlbumForBackup(album);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
state = state
|
|
|
|
state = state
|
|
|
|
.copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album});
|
|
|
|
.copyWith(selectedBackupAlbums: {...state.selectedBackupAlbums, album});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void addExcludedAlbumForBackup(AvailableAlbum album) {
|
|
|
|
|
|
|
|
if (state.selectedBackupAlbums.contains(album)) {
|
|
|
|
|
|
|
|
removeAlbumForBackup(album);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
state = state
|
|
|
|
|
|
|
|
.copyWith(excludedBackupAlbums: {...state.excludedBackupAlbums, album});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void removeAlbumForBackup(AvailableAlbum album) {
|
|
|
|
void removeAlbumForBackup(AvailableAlbum album) {
|
|
|
|
Set<AvailableAlbum> currentSelectedAlbums = state.selectedBackupAlbums;
|
|
|
|
Set<AvailableAlbum> currentSelectedAlbums = state.selectedBackupAlbums;
|
|
|
|
|
|
|
|
|
|
|
|
@ -105,6 +118,14 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
state = state.copyWith(selectedBackupAlbums: currentSelectedAlbums);
|
|
|
|
state = state.copyWith(selectedBackupAlbums: currentSelectedAlbums);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void removeExcludedAlbumForBackup(AvailableAlbum album) {
|
|
|
|
|
|
|
|
Set<AvailableAlbum> currentExcludedAlbums = state.excludedBackupAlbums;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
currentExcludedAlbums.removeWhere((a) => a == album);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
state = state.copyWith(excludedBackupAlbums: currentExcludedAlbums);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Future<void> backupAlbumSelectionDone() {
|
|
|
|
Future<void> backupAlbumSelectionDone() {
|
|
|
|
if (state.selectedBackupAlbums.isEmpty) {
|
|
|
|
if (state.selectedBackupAlbums.isEmpty) {
|
|
|
|
// disable any backup
|
|
|
|
// disable any backup
|
|
|
|
@ -219,6 +240,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
state = state.copyWith(availableAlbums: availableAlbums);
|
|
|
|
state = state.copyWith(availableAlbums: availableAlbums);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final List<BackupAlbum> excludedBackupAlbums =
|
|
|
|
|
|
|
|
await _backupService.excludedAlbumsQuery().findAll();
|
|
|
|
final List<BackupAlbum> selectedBackupAlbums =
|
|
|
|
final List<BackupAlbum> selectedBackupAlbums =
|
|
|
|
await _backupService.selectedAlbumsQuery().findAll();
|
|
|
|
await _backupService.selectedAlbumsQuery().findAll();
|
|
|
|
|
|
|
|
|
|
|
|
@ -236,8 +259,22 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final Set<AvailableAlbum> excludedAlbums = {};
|
|
|
|
|
|
|
|
for (final BackupAlbum ba in excludedBackupAlbums) {
|
|
|
|
|
|
|
|
final albumAsset = albumMap[ba.id];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (albumAsset != null) {
|
|
|
|
|
|
|
|
excludedAlbums.add(
|
|
|
|
|
|
|
|
AvailableAlbum(albumEntity: albumAsset, lastBackup: ba.lastBackup),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
log.severe('Excluded album not found');
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
state = state.copyWith(
|
|
|
|
state = state.copyWith(
|
|
|
|
selectedBackupAlbums: selectedAlbums,
|
|
|
|
selectedBackupAlbums: selectedAlbums,
|
|
|
|
|
|
|
|
excludedBackupAlbums: excludedAlbums,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
log.info(
|
|
|
|
log.info(
|
|
|
|
@ -253,7 +290,8 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
///
|
|
|
|
///
|
|
|
|
Future<void> _updateBackupAssetCount() async {
|
|
|
|
Future<void> _updateBackupAssetCount() async {
|
|
|
|
final duplicatedAssetIds = await _backupService.getDuplicatedAssetIds();
|
|
|
|
final duplicatedAssetIds = await _backupService.getDuplicatedAssetIds();
|
|
|
|
final Set<AssetEntity> backupCandidates = {};
|
|
|
|
final Set<AssetEntity> assetsFromSelectedAlbums = {};
|
|
|
|
|
|
|
|
final Set<AssetEntity> assetsFromExcludedAlbums = {};
|
|
|
|
|
|
|
|
|
|
|
|
for (final album in state.selectedBackupAlbums) {
|
|
|
|
for (final album in state.selectedBackupAlbums) {
|
|
|
|
final assetCount = await album.albumEntity.assetCountAsync;
|
|
|
|
final assetCount = await album.albumEntity.assetCountAsync;
|
|
|
|
@ -266,9 +304,25 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
start: 0,
|
|
|
|
start: 0,
|
|
|
|
end: assetCount,
|
|
|
|
end: assetCount,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
backupCandidates.addAll(assets);
|
|
|
|
assetsFromSelectedAlbums.addAll(assets);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (final album in state.excludedBackupAlbums) {
|
|
|
|
|
|
|
|
final assetCount = await album.albumEntity.assetCountAsync;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (assetCount == 0) {
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final assets = await album.albumEntity.getAssetListRange(
|
|
|
|
|
|
|
|
start: 0,
|
|
|
|
|
|
|
|
end: assetCount,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
assetsFromExcludedAlbums.addAll(assets);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
final Set<AssetEntity> allUniqueAssets =
|
|
|
|
|
|
|
|
assetsFromSelectedAlbums.difference(assetsFromExcludedAlbums);
|
|
|
|
final allAssetsInDatabase = await _backupService.getDeviceBackupAsset();
|
|
|
|
final allAssetsInDatabase = await _backupService.getDeviceBackupAsset();
|
|
|
|
|
|
|
|
|
|
|
|
if (allAssetsInDatabase == null) {
|
|
|
|
if (allAssetsInDatabase == null) {
|
|
|
|
@ -277,28 +331,28 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
|
|
|
|
|
|
|
|
// Find asset that were backup from selected albums
|
|
|
|
// Find asset that were backup from selected albums
|
|
|
|
final Set<String> selectedAlbumsBackupAssets =
|
|
|
|
final Set<String> selectedAlbumsBackupAssets =
|
|
|
|
Set.from(backupCandidates.map((e) => e.id));
|
|
|
|
Set.from(allUniqueAssets.map((e) => e.id));
|
|
|
|
|
|
|
|
|
|
|
|
selectedAlbumsBackupAssets
|
|
|
|
selectedAlbumsBackupAssets
|
|
|
|
.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId));
|
|
|
|
.removeWhere((assetId) => !allAssetsInDatabase.contains(assetId));
|
|
|
|
|
|
|
|
|
|
|
|
// Remove duplicated asset from all unique assets
|
|
|
|
// Remove duplicated asset from all unique assets
|
|
|
|
backupCandidates.removeWhere(
|
|
|
|
allUniqueAssets.removeWhere(
|
|
|
|
(asset) => duplicatedAssetIds.contains(asset.id),
|
|
|
|
(asset) => duplicatedAssetIds.contains(asset.id),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (backupCandidates.isEmpty) {
|
|
|
|
if (allUniqueAssets.isEmpty) {
|
|
|
|
log.info("No assets are selected for back up");
|
|
|
|
log.info("No assets are selected for back up");
|
|
|
|
state = state.copyWith(
|
|
|
|
state = state.copyWith(
|
|
|
|
backupProgress: BackUpProgressEnum.idle,
|
|
|
|
backupProgress: BackUpProgressEnum.idle,
|
|
|
|
allAssetsInDatabase: allAssetsInDatabase,
|
|
|
|
allAssetsInDatabase: allAssetsInDatabase,
|
|
|
|
backupCandidates: {},
|
|
|
|
allUniqueAssets: {},
|
|
|
|
selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
|
|
|
|
selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
state = state.copyWith(
|
|
|
|
state = state.copyWith(
|
|
|
|
allAssetsInDatabase: allAssetsInDatabase,
|
|
|
|
allAssetsInDatabase: allAssetsInDatabase,
|
|
|
|
backupCandidates: backupCandidates,
|
|
|
|
allUniqueAssets: allUniqueAssets,
|
|
|
|
selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
|
|
|
|
selectedAlbumsBackupAssetsIds: selectedAlbumsBackupAssets,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -333,8 +387,10 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
final selected = state.selectedBackupAlbums.map(
|
|
|
|
final selected = state.selectedBackupAlbums.map(
|
|
|
|
(e) => BackupAlbum(e.id, e.lastBackup ?? epoch, BackupSelection.select),
|
|
|
|
(e) => BackupAlbum(e.id, e.lastBackup ?? epoch, BackupSelection.select),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
|
|
|
|
final excluded = state.excludedBackupAlbums.map(
|
|
|
|
final backupAlbums = selected.toList();
|
|
|
|
(e) => BackupAlbum(e.id, e.lastBackup ?? epoch, BackupSelection.exclude),
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
final backupAlbums = selected.followedBy(excluded).toList();
|
|
|
|
backupAlbums.sortBy((e) => e.id);
|
|
|
|
backupAlbums.sortBy((e) => e.id);
|
|
|
|
return _db.writeTxn(() async {
|
|
|
|
return _db.writeTxn(() async {
|
|
|
|
final dbAlbums = await _db.backupAlbums.where().sortById().findAll();
|
|
|
|
final dbAlbums = await _db.backupAlbums.where().sortById().findAll();
|
|
|
|
@ -371,13 +427,13 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
if (hasPermission) {
|
|
|
|
if (hasPermission) {
|
|
|
|
await PhotoManager.clearFileCache();
|
|
|
|
await PhotoManager.clearFileCache();
|
|
|
|
|
|
|
|
|
|
|
|
if (state.backupCandidates.isEmpty) {
|
|
|
|
if (state.allUniqueAssets.isEmpty) {
|
|
|
|
log.info("No Asset On Device - Abort Backup Process");
|
|
|
|
log.info("No Asset On Device - Abort Backup Process");
|
|
|
|
state = state.copyWith(backupProgress: BackUpProgressEnum.idle);
|
|
|
|
state = state.copyWith(backupProgress: BackUpProgressEnum.idle);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Set<AssetEntity> assetsWillBeBackup = Set.from(state.backupCandidates);
|
|
|
|
Set<AssetEntity> assetsWillBeBackup = Set.from(state.allUniqueAssets);
|
|
|
|
// Remove item that has already been backed up
|
|
|
|
// Remove item that has already been backed up
|
|
|
|
for (final assetId in state.allAssetsInDatabase) {
|
|
|
|
for (final assetId in state.allAssetsInDatabase) {
|
|
|
|
assetsWillBeBackup.removeWhere((e) => e.id == assetId);
|
|
|
|
assetsWillBeBackup.removeWhere((e) => e.id == assetId);
|
|
|
|
@ -448,7 +504,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
) {
|
|
|
|
) {
|
|
|
|
if (isDuplicated) {
|
|
|
|
if (isDuplicated) {
|
|
|
|
state = state.copyWith(
|
|
|
|
state = state.copyWith(
|
|
|
|
backupCandidates: state.backupCandidates
|
|
|
|
allUniqueAssets: state.allUniqueAssets
|
|
|
|
.where((asset) => asset.id != deviceAssetId)
|
|
|
|
.where((asset) => asset.id != deviceAssetId)
|
|
|
|
.toSet(),
|
|
|
|
.toSet(),
|
|
|
|
);
|
|
|
|
);
|
|
|
|
@ -462,17 +518,20 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (state.backupCandidates.length -
|
|
|
|
if (state.allUniqueAssets.length -
|
|
|
|
state.selectedAlbumsBackupAssetsIds.length ==
|
|
|
|
state.selectedAlbumsBackupAssetsIds.length ==
|
|
|
|
0) {
|
|
|
|
0) {
|
|
|
|
final latestAssetBackup =
|
|
|
|
final latestAssetBackup =
|
|
|
|
state.backupCandidates.map((e) => e.modifiedDateTime).reduce(
|
|
|
|
state.allUniqueAssets.map((e) => e.modifiedDateTime).reduce(
|
|
|
|
(v, e) => e.isAfter(v) ? e : v,
|
|
|
|
(v, e) => e.isAfter(v) ? e : v,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
state = state.copyWith(
|
|
|
|
state = state.copyWith(
|
|
|
|
selectedBackupAlbums: state.selectedBackupAlbums
|
|
|
|
selectedBackupAlbums: state.selectedBackupAlbums
|
|
|
|
.map((e) => e.copyWith(lastBackup: latestAssetBackup))
|
|
|
|
.map((e) => e.copyWith(lastBackup: latestAssetBackup))
|
|
|
|
.toSet(),
|
|
|
|
.toSet(),
|
|
|
|
|
|
|
|
excludedBackupAlbums: state.excludedBackupAlbums
|
|
|
|
|
|
|
|
.map((e) => e.copyWith(lastBackup: latestAssetBackup))
|
|
|
|
|
|
|
|
.toSet(),
|
|
|
|
backupProgress: BackUpProgressEnum.done,
|
|
|
|
backupProgress: BackUpProgressEnum.done,
|
|
|
|
progressInPercentage: 0.0,
|
|
|
|
progressInPercentage: 0.0,
|
|
|
|
progressInFileSize: "0 B / 0 B",
|
|
|
|
progressInFileSize: "0 B / 0 B",
|
|
|
|
@ -571,8 +630,12 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
.filter()
|
|
|
|
.filter()
|
|
|
|
.selectionEqualTo(BackupSelection.select)
|
|
|
|
.selectionEqualTo(BackupSelection.select)
|
|
|
|
.findAll();
|
|
|
|
.findAll();
|
|
|
|
|
|
|
|
final List<BackupAlbum> excludedBackupAlbums = await _db.backupAlbums
|
|
|
|
|
|
|
|
.filter()
|
|
|
|
|
|
|
|
.selectionEqualTo(BackupSelection.exclude)
|
|
|
|
|
|
|
|
.findAll();
|
|
|
|
Set<AvailableAlbum> selectedAlbums = state.selectedBackupAlbums;
|
|
|
|
Set<AvailableAlbum> selectedAlbums = state.selectedBackupAlbums;
|
|
|
|
|
|
|
|
Set<AvailableAlbum> excludedAlbums = state.excludedBackupAlbums;
|
|
|
|
if (selectedAlbums.isNotEmpty) {
|
|
|
|
if (selectedAlbums.isNotEmpty) {
|
|
|
|
selectedAlbums = _updateAlbumsBackupTime(
|
|
|
|
selectedAlbums = _updateAlbumsBackupTime(
|
|
|
|
selectedAlbums,
|
|
|
|
selectedAlbums,
|
|
|
|
@ -580,10 +643,17 @@ class BackupNotifier extends StateNotifier<BackUpState> {
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (excludedAlbums.isNotEmpty) {
|
|
|
|
|
|
|
|
excludedAlbums = _updateAlbumsBackupTime(
|
|
|
|
|
|
|
|
excludedAlbums,
|
|
|
|
|
|
|
|
excludedBackupAlbums,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
final BackUpProgressEnum previous = state.backupProgress;
|
|
|
|
final BackUpProgressEnum previous = state.backupProgress;
|
|
|
|
state = state.copyWith(
|
|
|
|
state = state.copyWith(
|
|
|
|
backupProgress: BackUpProgressEnum.inBackground,
|
|
|
|
backupProgress: BackUpProgressEnum.inBackground,
|
|
|
|
selectedBackupAlbums: selectedAlbums,
|
|
|
|
selectedBackupAlbums: selectedAlbums,
|
|
|
|
|
|
|
|
excludedBackupAlbums: excludedAlbums,
|
|
|
|
);
|
|
|
|
);
|
|
|
|
// assumes the background service is currently running
|
|
|
|
// assumes the background service is currently running
|
|
|
|
// if true, waits until it has stopped to start the backup
|
|
|
|
// if true, waits until it has stopped to start the backup
|
|
|
|
|