copy: fix for non primary volumes, update collection
This commit is contained in:
parent
1cd333d419
commit
487ac5c677
7 changed files with 100 additions and 45 deletions
|
@ -35,7 +35,7 @@ android {
|
||||||
applicationId "deckers.thibault.aves"
|
applicationId "deckers.thibault.aves"
|
||||||
// some Java 8 APIs (java.util.stream, etc.) require minSdkVersion 24
|
// some Java 8 APIs (java.util.stream, etc.) require minSdkVersion 24
|
||||||
// but Android Studio 4.0 desugaring features allow targeting older SDKs
|
// but Android Studio 4.0 desugaring features allow targeting older SDKs
|
||||||
minSdkVersion 23
|
minSdkVersion 24
|
||||||
targetSdkVersion 29 // same as compileSdkVersion
|
targetSdkVersion 29 // same as compileSdkVersion
|
||||||
versionCode flutterVersionCode.toInteger()
|
versionCode flutterVersionCode.toInteger()
|
||||||
versionName flutterVersionName
|
versionName flutterVersionName
|
||||||
|
|
|
@ -40,8 +40,6 @@ public class ImageEntry {
|
||||||
@Nullable
|
@Nullable
|
||||||
public String title;
|
public String title;
|
||||||
@Nullable
|
@Nullable
|
||||||
private String bucketDisplayName;
|
|
||||||
@Nullable
|
|
||||||
public Integer width, height, orientationDegrees;
|
public Integer width, height, orientationDegrees;
|
||||||
@Nullable
|
@Nullable
|
||||||
public Long sizeBytes;
|
public Long sizeBytes;
|
||||||
|
@ -66,7 +64,6 @@ public class ImageEntry {
|
||||||
this.title = (String) map.get("title");
|
this.title = (String) map.get("title");
|
||||||
this.dateModifiedSecs = toLong(map.get("dateModifiedSecs"));
|
this.dateModifiedSecs = toLong(map.get("dateModifiedSecs"));
|
||||||
this.sourceDateTakenMillis = toLong(map.get("sourceDateTakenMillis"));
|
this.sourceDateTakenMillis = toLong(map.get("sourceDateTakenMillis"));
|
||||||
this.bucketDisplayName = (String) map.get("bucketDisplayName");
|
|
||||||
this.durationMillis = toLong(map.get("durationMillis"));
|
this.durationMillis = toLong(map.get("durationMillis"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +79,6 @@ public class ImageEntry {
|
||||||
put("title", title);
|
put("title", title);
|
||||||
put("dateModifiedSecs", dateModifiedSecs);
|
put("dateModifiedSecs", dateModifiedSecs);
|
||||||
put("sourceDateTakenMillis", sourceDateTakenMillis);
|
put("sourceDateTakenMillis", sourceDateTakenMillis);
|
||||||
put("bucketDisplayName", bucketDisplayName);
|
|
||||||
put("durationMillis", durationMillis);
|
put("durationMillis", durationMillis);
|
||||||
// only for map export
|
// only for map export
|
||||||
put("contentId", getContentId());
|
put("contentId", getContentId());
|
||||||
|
|
|
@ -10,6 +10,8 @@ import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.storage.StorageManager;
|
||||||
|
import android.os.storage.StorageVolume;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
@ -51,7 +53,6 @@ public class MediaStoreImageProvider extends ImageProvider {
|
||||||
private static final String[] IMAGE_PROJECTION = Stream.of(BASE_PROJECTION, new String[]{
|
private static final String[] IMAGE_PROJECTION = Stream.of(BASE_PROJECTION, new String[]{
|
||||||
// uses MediaStore.Images.Media instead of MediaStore.MediaColumns for APIs < Q
|
// uses MediaStore.Images.Media instead of MediaStore.MediaColumns for APIs < Q
|
||||||
MediaStore.Images.Media.DATE_TAKEN,
|
MediaStore.Images.Media.DATE_TAKEN,
|
||||||
MediaStore.Images.Media.BUCKET_DISPLAY_NAME,
|
|
||||||
MediaStore.Images.Media.ORIENTATION,
|
MediaStore.Images.Media.ORIENTATION,
|
||||||
}).flatMap(Stream::of).toArray(String[]::new);
|
}).flatMap(Stream::of).toArray(String[]::new);
|
||||||
|
|
||||||
|
@ -59,7 +60,6 @@ public class MediaStoreImageProvider extends ImageProvider {
|
||||||
private static final String[] VIDEO_PROJECTION = Stream.of(BASE_PROJECTION, new String[]{
|
private static final String[] VIDEO_PROJECTION = Stream.of(BASE_PROJECTION, new String[]{
|
||||||
// uses MediaStore.Video.Media instead of MediaStore.MediaColumns for APIs < Q
|
// uses MediaStore.Video.Media instead of MediaStore.MediaColumns for APIs < Q
|
||||||
MediaStore.Video.Media.DATE_TAKEN,
|
MediaStore.Video.Media.DATE_TAKEN,
|
||||||
MediaStore.Video.Media.BUCKET_DISPLAY_NAME,
|
|
||||||
MediaStore.Video.Media.DURATION,
|
MediaStore.Video.Media.DURATION,
|
||||||
}, (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ?
|
}, (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ?
|
||||||
new String[]{
|
new String[]{
|
||||||
|
@ -111,7 +111,6 @@ public class MediaStoreImageProvider extends ImageProvider {
|
||||||
int heightColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.HEIGHT);
|
int heightColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.HEIGHT);
|
||||||
int dateModifiedColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED);
|
int dateModifiedColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED);
|
||||||
int dateTakenColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_TAKEN);
|
int dateTakenColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_TAKEN);
|
||||||
int bucketDisplayNameColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.BUCKET_DISPLAY_NAME);
|
|
||||||
|
|
||||||
// image & video for API >= Q, only for images for API < Q
|
// image & video for API >= Q, only for images for API < Q
|
||||||
int orientationColumn = cursor.getColumnIndex(MediaStore.MediaColumns.ORIENTATION);
|
int orientationColumn = cursor.getColumnIndex(MediaStore.MediaColumns.ORIENTATION);
|
||||||
|
@ -138,7 +137,6 @@ public class MediaStoreImageProvider extends ImageProvider {
|
||||||
put("title", cursor.getString(titleColumn));
|
put("title", cursor.getString(titleColumn));
|
||||||
put("dateModifiedSecs", cursor.getLong(dateModifiedColumn));
|
put("dateModifiedSecs", cursor.getLong(dateModifiedColumn));
|
||||||
put("sourceDateTakenMillis", cursor.getLong(dateTakenColumn));
|
put("sourceDateTakenMillis", cursor.getLong(dateTakenColumn));
|
||||||
put("bucketDisplayName", cursor.getString(bucketDisplayNameColumn));
|
|
||||||
// only for map export
|
// only for map export
|
||||||
put("contentId", contentId);
|
put("contentId", contentId);
|
||||||
}};
|
}};
|
||||||
|
@ -228,21 +226,19 @@ public class MediaStoreImageProvider extends ImageProvider {
|
||||||
public ListenableFuture<Map<String, Object>> move(final Activity activity, final String sourcePath, final Uri sourceUri, String destinationDir, String mimeType, boolean copy) {
|
public ListenableFuture<Map<String, Object>> move(final Activity activity, final String sourcePath, final Uri sourceUri, String destinationDir, String mimeType, boolean copy) {
|
||||||
SettableFuture<Map<String, Object>> future = SettableFuture.create();
|
SettableFuture<Map<String, Object>> future = SettableFuture.create();
|
||||||
|
|
||||||
// if (Env.isOnSdCard(activity, path)) {
|
String volumeName = "external";
|
||||||
// Uri sdCardTreeUri = PermissionManager.getSdCardTreeUri(activity);
|
StorageManager sm = activity.getSystemService(StorageManager.class);
|
||||||
// if (sdCardTreeUri == null) {
|
if (sm != null) {
|
||||||
// Runnable runnable = () -> move(activity, path, uri, copy, destinationPath, callback);
|
StorageVolume volume = sm.getStorageVolume(new File(destinationDir));
|
||||||
// new Handler(Looper.getMainLooper()).post(() -> PermissionManager.showSdCardAccessDialog(activity, runnable));
|
if (volume != null && !volume.isPrimary()) {
|
||||||
// return;
|
String uuid = volume.getUuid();
|
||||||
// }
|
if (uuid != null) {
|
||||||
//
|
// the UUID returned may be uppercase
|
||||||
// // if the file is on SD card, calling the content resolver delete() removes the entry from the Media Store
|
// but it should be lowercase to work with the MediaStore
|
||||||
// // but it doesn't delete the file, even if the app has the permission
|
volumeName = volume.getUuid().toLowerCase();
|
||||||
// StorageUtils.deleteFromSdCard(activity, sdCardTreeUri, Env.getStorageVolumes(activity), path);
|
}
|
||||||
// Log.d(LOG_TAG, "deleted from SD card at path=" + uri);
|
}
|
||||||
// callback.onSuccess(null);
|
}
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// from API 29, changing MediaColumns.RELATIVE_PATH can move files on disk (same storage device)
|
// from API 29, changing MediaColumns.RELATIVE_PATH can move files on disk (same storage device)
|
||||||
|
@ -251,9 +247,6 @@ public class MediaStoreImageProvider extends ImageProvider {
|
||||||
// DocumentFile.getParentFile() is null without picking a tree first
|
// DocumentFile.getParentFile() is null without picking a tree first
|
||||||
// DocumentsContract.copyDocument() and moveDocument() need parent doc uri
|
// DocumentsContract.copyDocument() and moveDocument() need parent doc uri
|
||||||
|
|
||||||
// TODO TLAD copy/move
|
|
||||||
// TODO TLAD cannot copy to SD card, even with the permission to the volume root, by inserting to MediaStore
|
|
||||||
|
|
||||||
PathComponents sourcePathComponents = new PathComponents(sourcePath, Env.getStorageVolumes(activity));
|
PathComponents sourcePathComponents = new PathComponents(sourcePath, Env.getStorageVolumes(activity));
|
||||||
String destinationPath = destinationDir + File.separator + sourcePathComponents.getFilename();
|
String destinationPath = destinationDir + File.separator + sourcePathComponents.getFilename();
|
||||||
|
|
||||||
|
@ -262,9 +255,8 @@ public class MediaStoreImageProvider extends ImageProvider {
|
||||||
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
|
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
|
||||||
// contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "");
|
// contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, "");
|
||||||
// contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "");
|
// contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "");
|
||||||
Uri tableUrl = mimeType.startsWith(MimeTypes.VIDEO) ? MediaStore.Video.Media.EXTERNAL_CONTENT_URI : MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
Uri tableUrl = mimeType.startsWith(MimeTypes.VIDEO) ? MediaStore.Video.Media.getContentUri(volumeName) : MediaStore.Images.Media.getContentUri(volumeName);
|
||||||
Uri destinationUri = activity.getContentResolver().insert(tableUrl, contentValues);
|
Uri destinationUri = activity.getContentResolver().insert(tableUrl, contentValues);
|
||||||
// Log.d("TLAD", "move copy from=" + sourcePath + " to=" + destinationPath + " (destinationUri=" + destinationUri + ")");
|
|
||||||
if (destinationUri == null) {
|
if (destinationUri == null) {
|
||||||
future.setException(new Exception("failed to insert row to content resolver"));
|
future.setException(new Exception("failed to insert row to content resolver"));
|
||||||
} else {
|
} else {
|
||||||
|
@ -272,6 +264,8 @@ public class MediaStoreImageProvider extends ImageProvider {
|
||||||
DocumentFileCompat destination = DocumentFileCompat.fromSingleUri(activity, destinationUri);
|
DocumentFileCompat destination = DocumentFileCompat.fromSingleUri(activity, destinationUri);
|
||||||
source.copyTo(destination);
|
source.copyTo(destination);
|
||||||
|
|
||||||
|
// TODO TLAD delete source when it is a `move`
|
||||||
|
|
||||||
Map<String, Object> newFields = new HashMap<>();
|
Map<String, Object> newFields = new HashMap<>();
|
||||||
newFields.put("uri", destinationUri.toString());
|
newFields.put("uri", destinationUri.toString());
|
||||||
newFields.put("contentId", ContentUris.parseId(destinationUri));
|
newFields.put("contentId", ContentUris.parseId(destinationUri));
|
||||||
|
|
|
@ -28,7 +28,6 @@ class ImageEntry {
|
||||||
String sourceTitle;
|
String sourceTitle;
|
||||||
final int dateModifiedSecs;
|
final int dateModifiedSecs;
|
||||||
final int sourceDateTakenMillis;
|
final int sourceDateTakenMillis;
|
||||||
final String bucketDisplayName;
|
|
||||||
final int durationMillis;
|
final int durationMillis;
|
||||||
int _catalogDateMillis;
|
int _catalogDateMillis;
|
||||||
CatalogMetadata _catalogMetadata;
|
CatalogMetadata _catalogMetadata;
|
||||||
|
@ -49,12 +48,38 @@ class ImageEntry {
|
||||||
this.sourceTitle,
|
this.sourceTitle,
|
||||||
this.dateModifiedSecs,
|
this.dateModifiedSecs,
|
||||||
this.sourceDateTakenMillis,
|
this.sourceDateTakenMillis,
|
||||||
this.bucketDisplayName,
|
|
||||||
this.durationMillis,
|
this.durationMillis,
|
||||||
}) : directory = path != null ? dirname(path) : null {
|
}) : directory = path != null ? dirname(path) : null {
|
||||||
isFavouriteNotifier.value = isFavourite;
|
isFavouriteNotifier.value = isFavourite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageEntry copyWith({
|
||||||
|
@required String uri,
|
||||||
|
@required String path,
|
||||||
|
@required int contentId,
|
||||||
|
}) {
|
||||||
|
final copyContentId = contentId ?? this.contentId;
|
||||||
|
final copied = ImageEntry(
|
||||||
|
uri: uri ?? uri,
|
||||||
|
path: path ?? this.path,
|
||||||
|
contentId: copyContentId,
|
||||||
|
mimeType: mimeType,
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
orientationDegrees: orientationDegrees,
|
||||||
|
sizeBytes: sizeBytes,
|
||||||
|
sourceTitle: sourceTitle,
|
||||||
|
dateModifiedSecs: dateModifiedSecs,
|
||||||
|
sourceDateTakenMillis: sourceDateTakenMillis,
|
||||||
|
durationMillis: durationMillis,
|
||||||
|
)
|
||||||
|
.._catalogDateMillis = _catalogDateMillis
|
||||||
|
.._catalogMetadata = _catalogMetadata?.copyWith(contentId: copyContentId)
|
||||||
|
.._addressDetails = _addressDetails?.copyWith(contentId: copyContentId);
|
||||||
|
|
||||||
|
return copied;
|
||||||
|
}
|
||||||
|
|
||||||
factory ImageEntry.fromMap(Map map) {
|
factory ImageEntry.fromMap(Map map) {
|
||||||
return ImageEntry(
|
return ImageEntry(
|
||||||
uri: map['uri'] as String,
|
uri: map['uri'] as String,
|
||||||
|
@ -68,7 +93,6 @@ class ImageEntry {
|
||||||
sourceTitle: map['title'] as String,
|
sourceTitle: map['title'] as String,
|
||||||
dateModifiedSecs: map['dateModifiedSecs'] as int,
|
dateModifiedSecs: map['dateModifiedSecs'] as int,
|
||||||
sourceDateTakenMillis: map['sourceDateTakenMillis'] as int,
|
sourceDateTakenMillis: map['sourceDateTakenMillis'] as int,
|
||||||
bucketDisplayName: map['bucketDisplayName'] as String,
|
|
||||||
durationMillis: map['durationMillis'] as int,
|
durationMillis: map['durationMillis'] as int,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +110,6 @@ class ImageEntry {
|
||||||
'title': sourceTitle,
|
'title': sourceTitle,
|
||||||
'dateModifiedSecs': dateModifiedSecs,
|
'dateModifiedSecs': dateModifiedSecs,
|
||||||
'sourceDateTakenMillis': sourceDateTakenMillis,
|
'sourceDateTakenMillis': sourceDateTakenMillis,
|
||||||
'bucketDisplayName': bucketDisplayName,
|
|
||||||
'durationMillis': durationMillis,
|
'durationMillis': durationMillis,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,6 +48,21 @@ class CatalogMetadata {
|
||||||
: latitude = latitude == null || latitude < -90.0 || latitude > 90.0 ? null : latitude,
|
: latitude = latitude == null || latitude < -90.0 || latitude > 90.0 ? null : latitude,
|
||||||
longitude = longitude == null || longitude < -180.0 || longitude > 180.0 ? null : longitude;
|
longitude = longitude == null || longitude < -180.0 || longitude > 180.0 ? null : longitude;
|
||||||
|
|
||||||
|
CatalogMetadata copyWith({
|
||||||
|
@required int contentId,
|
||||||
|
}) {
|
||||||
|
return CatalogMetadata(
|
||||||
|
contentId: contentId ?? this.contentId,
|
||||||
|
dateMillis: dateMillis,
|
||||||
|
isAnimated: isAnimated,
|
||||||
|
videoRotation: videoRotation,
|
||||||
|
xmpSubjects: xmpSubjects,
|
||||||
|
xmpTitleDescription: xmpTitleDescription,
|
||||||
|
latitude: latitude,
|
||||||
|
longitude: longitude,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
factory CatalogMetadata.fromMap(Map map, {bool boolAsInteger = false}) {
|
factory CatalogMetadata.fromMap(Map map, {bool boolAsInteger = false}) {
|
||||||
final isAnimated = map['isAnimated'] ?? (boolAsInteger ? 0 : false);
|
final isAnimated = map['isAnimated'] ?? (boolAsInteger ? 0 : false);
|
||||||
return CatalogMetadata(
|
return CatalogMetadata(
|
||||||
|
@ -121,6 +136,19 @@ class AddressDetails {
|
||||||
this.locality,
|
this.locality,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddressDetails copyWith({
|
||||||
|
@required int contentId,
|
||||||
|
}) {
|
||||||
|
return AddressDetails(
|
||||||
|
contentId: contentId ?? this.contentId,
|
||||||
|
addressLine: addressLine,
|
||||||
|
countryCode: countryCode,
|
||||||
|
countryName: countryName,
|
||||||
|
adminArea: adminArea,
|
||||||
|
locality: locality,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
factory AddressDetails.fromMap(Map map) {
|
factory AddressDetails.fromMap(Map map) {
|
||||||
return AddressDetails(
|
return AddressDetails(
|
||||||
contentId: map['contentId'],
|
contentId: map['contentId'],
|
||||||
|
|
|
@ -188,6 +188,8 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
),
|
),
|
||||||
const PopupMenuItem(
|
const PopupMenuItem(
|
||||||
value: CollectionAction.move,
|
value: CollectionAction.move,
|
||||||
|
// TODO TLAD enable when handled on native side
|
||||||
|
enabled: false,
|
||||||
child: MenuRow(text: 'Move to album'),
|
child: MenuRow(text: 'Move to album'),
|
||||||
),
|
),
|
||||||
const PopupMenuItem(
|
const PopupMenuItem(
|
||||||
|
|
|
@ -62,11 +62,10 @@ class SelectionActionDelegate with PermissionAwareMixin {
|
||||||
leading: const BackButton(),
|
leading: const BackButton(),
|
||||||
title: Text(copy ? 'Copy to Album' : 'Move to Album'),
|
title: Text(copy ? 'Copy to Album' : 'Move to Album'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
const IconButton(
|
||||||
icon: const Icon(AIcons.createAlbum),
|
icon: Icon(AIcons.createAlbum),
|
||||||
onPressed: () {
|
// TODO TLAD album creation
|
||||||
// TODO TLAD album creation
|
onPressed: null,
|
||||||
},
|
|
||||||
tooltip: 'Create album',
|
tooltip: 'Create album',
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -90,19 +89,31 @@ class SelectionActionDelegate with PermissionAwareMixin {
|
||||||
opStream: ImageFileService.move(selection, copy: copy, destinationPath: filter.album),
|
opStream: ImageFileService.move(selection, copy: copy, destinationPath: filter.album),
|
||||||
onDone: (Set<MoveOpEvent> processed) {
|
onDone: (Set<MoveOpEvent> processed) {
|
||||||
debugPrint('$runtimeType _moveSelection onDone');
|
debugPrint('$runtimeType _moveSelection onDone');
|
||||||
final movedUris = processed.where((e) => e.success).map((e) => e.uri);
|
final movedOps = processed.where((e) => e.success);
|
||||||
final movedCount = movedUris.length;
|
final movedCount = movedOps.length;
|
||||||
final selectionCount = selection.length;
|
final selectionCount = selection.length;
|
||||||
if (movedCount < selectionCount) {
|
if (movedCount < selectionCount) {
|
||||||
final count = selectionCount - movedCount;
|
final count = selectionCount - movedCount;
|
||||||
_showFeedback(context, 'Failed to move ${Intl.plural(count, one: '${count} item', other: '${count} items')}');
|
_showFeedback(context, 'Failed to move ${Intl.plural(count, one: '${count} item', other: '${count} items')}');
|
||||||
}
|
}
|
||||||
if (movedCount > 0) {
|
if (movedCount > 0) {
|
||||||
processed.forEach((event) {
|
if (copy) {
|
||||||
debugPrint('$runtimeType _moveSelection moved entry uri=${event.uri} newFields=${event.newFields}');
|
collection.source.addAll(movedOps.map((movedOp) {
|
||||||
// TODO TLAD update source
|
final sourceUri = movedOp.uri;
|
||||||
});
|
final newFields = movedOp.newFields;
|
||||||
|
final sourceEntry = selection.firstWhere((entry) => entry.uri == sourceUri, orElse: () => null);
|
||||||
|
return sourceEntry?.copyWith(
|
||||||
|
uri: newFields['uri'] as String,
|
||||||
|
path: newFields['path'] as String,
|
||||||
|
contentId: newFields['contentId'] as int,
|
||||||
|
);
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// TODO TLAD update old entries path/dir/ID
|
||||||
|
}
|
||||||
|
// TODO TLAD update DB for catalog/address/fav
|
||||||
}
|
}
|
||||||
|
collection.clearSelection();
|
||||||
collection.browse();
|
collection.browse();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -149,6 +160,7 @@ class SelectionActionDelegate with PermissionAwareMixin {
|
||||||
if (deletedCount > 0) {
|
if (deletedCount > 0) {
|
||||||
collection.source.removeEntries(selection.where((e) => deletedUris.contains(e.uri)));
|
collection.source.removeEntries(selection.where((e) => deletedUris.contains(e.uri)));
|
||||||
}
|
}
|
||||||
|
collection.clearSelection();
|
||||||
collection.browse();
|
collection.browse();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue