album renaming performance review
This commit is contained in:
parent
ac67f4e080
commit
e137ee6e57
2 changed files with 40 additions and 77 deletions
|
@ -29,10 +29,8 @@ import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import deckers.thibault.aves.model.AvesImageEntry;
|
import deckers.thibault.aves.model.AvesImageEntry;
|
||||||
import deckers.thibault.aves.utils.MetadataHelper;
|
import deckers.thibault.aves.utils.MetadataHelper;
|
||||||
|
@ -128,33 +126,53 @@ public abstract class ImageProvider {
|
||||||
entriesByBaseContentUri.forEach((baseContentUri, entries) -> {
|
entriesByBaseContentUri.forEach((baseContentUri, entries) -> {
|
||||||
int count = entries.size();
|
int count = entries.size();
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
|
String[] mimeTypes = new String[count];
|
||||||
String[] oldPaths = new String[count];
|
String[] oldPaths = new String[count];
|
||||||
String[] newPaths = new String[count];
|
String[] newPaths = new String[count];
|
||||||
String[] mimeTypes = new String[count];
|
Map<String, Integer> oldContentIdByPath = new HashMap<>();
|
||||||
|
Map<String, SettableFuture<Map<String, Object>>> scanFutureByPath = new HashMap<>();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
Map<String, Object> entry = entries.get(i);
|
Map<String, Object> entry = entries.get(i);
|
||||||
String displayName = (String) entry.get("displayName");
|
String displayName = (String) entry.get("displayName");
|
||||||
oldPaths[i] = oldDirPath + displayName;
|
String newPath = newDirPath + displayName;
|
||||||
newPaths[i] = newDirPath + displayName;
|
|
||||||
mimeTypes[i] = (String) entry.get("mimeType");
|
mimeTypes[i] = (String) entry.get("mimeType");
|
||||||
|
oldPaths[i] = oldDirPath + displayName;
|
||||||
|
newPaths[i] = newPath;
|
||||||
|
oldContentIdByPath.put(newPath, (Integer) entry.get("oldContentId"));
|
||||||
|
scanFutureByPath.put(newPath, SettableFuture.create());
|
||||||
}
|
}
|
||||||
MediaScannerConnection.scanFile(context, oldPaths, mimeTypes, null);
|
MediaScannerConnection.scanFile(context, oldPaths, mimeTypes, null);
|
||||||
scanFutures.addAll(scanNewPaths(context, newPaths, mimeTypes, baseContentUri));
|
MediaScannerConnection.scanFile(context, newPaths, mimeTypes, (path, rawUri) -> {
|
||||||
|
SettableFuture<Map<String, Object>> future = scanFutureByPath.get(path);
|
||||||
|
if (future == null) {
|
||||||
|
Log.e(LOG_TAG, "no future for path=" + path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rawUri == null) {
|
||||||
|
future.setException(new Exception("failed to get URI of item at path=" + path));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// newURI is possibly a file media URI (e.g. "content://media/12a9-8b42/file/62872")
|
||||||
|
// but we need an image/video media URI (e.g. "content://media/external/images/media/62872")
|
||||||
|
long contentId = ContentUris.parseId(rawUri);
|
||||||
|
Uri contentUri = ContentUris.withAppendedId(baseContentUri, contentId);
|
||||||
|
Map<String, Object> newFields = new HashMap<String, Object>() {{
|
||||||
|
put("path", path);
|
||||||
|
put("uri", contentUri.toString());
|
||||||
|
put("contentId", contentId);
|
||||||
|
put("oldContentId", oldContentIdByPath.get(path));
|
||||||
|
}};
|
||||||
|
future.set(newFields);
|
||||||
|
});
|
||||||
|
|
||||||
|
scanFutures.addAll(scanFutureByPath.values());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<Map<String, Object>> scanResults = Futures.allAsList(scanFutures).get();
|
callback.onSuccess(Futures.allAsList(scanFutures).get());
|
||||||
Stream<Map<String, Object>> allEntries = entriesByBaseContentUri.values().stream().flatMap(Collection::stream);
|
|
||||||
Map<String, Integer> oldContentIdByDisplayName = allEntries.collect(Collectors.toMap(
|
|
||||||
fields -> (String) fields.get("displayName"),
|
|
||||||
fields -> (Integer) Objects.requireNonNull(fields.get("oldContentId"))
|
|
||||||
));
|
|
||||||
scanResults.forEach(newFields -> {
|
|
||||||
String displayName = (String) newFields.get("displayName");
|
|
||||||
newFields.put("oldContentId", oldContentIdByDisplayName.get(displayName));
|
|
||||||
});
|
|
||||||
callback.onSuccess(scanResults);
|
|
||||||
} catch (ExecutionException | InterruptedException e) {
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
callback.onFailure(e);
|
callback.onFailure(e);
|
||||||
}
|
}
|
||||||
|
@ -341,64 +359,6 @@ public abstract class ImageProvider {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Collection<SettableFuture<Map<String, Object>>> scanNewPaths(final Context context, final String[] paths, final String[] mimeTypes, final Uri baseContentUri) {
|
|
||||||
Map<String, SettableFuture<Map<String, Object>>> scanFutures = new HashMap<>();
|
|
||||||
for (String path : paths) {
|
|
||||||
scanFutures.put(path, SettableFuture.create());
|
|
||||||
}
|
|
||||||
|
|
||||||
MediaScannerConnection.scanFile(context, paths, mimeTypes, (path, rawUri) -> {
|
|
||||||
SettableFuture<Map<String, Object>> future = scanFutures.get(path);
|
|
||||||
if (future == null) {
|
|
||||||
Log.e(LOG_TAG, "no future for path=" + path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rawUri == null) {
|
|
||||||
future.setException(new Exception("failed to get URI of item at path=" + path));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// newURI is possibly a file media URI (e.g. "content://media/12a9-8b42/file/62872")
|
|
||||||
// but we need an image/video media URI (e.g. "content://media/external/images/media/62872")
|
|
||||||
long contentId = ContentUris.parseId(rawUri);
|
|
||||||
Uri contentUri = ContentUris.withAppendedId(baseContentUri, contentId);
|
|
||||||
|
|
||||||
Map<String, Object> newFields = new HashMap<>();
|
|
||||||
// we retrieve updated fields as the renamed/moved file became a new entry in the Media Store
|
|
||||||
String[] projection = {
|
|
||||||
MediaStore.MediaColumns.DISPLAY_NAME,
|
|
||||||
MediaStore.MediaColumns.TITLE,
|
|
||||||
MediaStore.MediaColumns.DATE_MODIFIED,
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
Cursor cursor = context.getContentResolver().query(contentUri, projection, null, null, null);
|
|
||||||
if (cursor != null) {
|
|
||||||
if (cursor.moveToNext()) {
|
|
||||||
newFields.put("uri", contentUri.toString());
|
|
||||||
newFields.put("contentId", contentId);
|
|
||||||
newFields.put("path", path);
|
|
||||||
newFields.put("displayName", cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)));
|
|
||||||
newFields.put("title", cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.TITLE)));
|
|
||||||
newFields.put("dateModifiedSecs", cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED)));
|
|
||||||
}
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
future.setException(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newFields.isEmpty()) {
|
|
||||||
future.setException(new Exception("failed to get item details from provider at contentUri=" + contentUri));
|
|
||||||
} else {
|
|
||||||
future.set(newFields);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return scanFutures.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void scanNewPath(final Context context, final String path, final String mimeType, final ImageOpCallback callback) {
|
protected void scanNewPath(final Context context, final String path, final String mimeType, final ImageOpCallback callback) {
|
||||||
MediaScannerConnection.scanFile(context, new String[]{path}, new String[]{mimeType}, (newPath, newUri) -> {
|
MediaScannerConnection.scanFile(context, new String[]{path}, new String[]{mimeType}, (newPath, newUri) -> {
|
||||||
long contentId = 0;
|
long contentId = 0;
|
||||||
|
|
|
@ -88,12 +88,15 @@ class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagMixin {
|
||||||
invalidateFilterEntryCounts();
|
invalidateFilterEntryCounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `dateModifiedSecs` changes when moving entries to another directory,
|
||||||
|
// but it does not change when renaming the containing directory
|
||||||
Future<void> moveEntry(ImageEntry entry, Map newFields) async {
|
Future<void> moveEntry(ImageEntry entry, Map newFields) async {
|
||||||
final oldContentId = entry.contentId;
|
final oldContentId = entry.contentId;
|
||||||
final newContentId = newFields['contentId'] as int;
|
final newContentId = newFields['contentId'] as int;
|
||||||
entry.uri = newFields['uri'] as String;
|
final newDateModifiedSecs = newFields['dateModifiedSecs'] as int;
|
||||||
|
if (newDateModifiedSecs != null) entry.dateModifiedSecs = newDateModifiedSecs;
|
||||||
entry.path = newFields['path'] as String;
|
entry.path = newFields['path'] as String;
|
||||||
entry.dateModifiedSecs = newFields['dateModifiedSecs'] as int;
|
entry.uri = newFields['uri'] as String;
|
||||||
entry.contentId = newContentId;
|
entry.contentId = newContentId;
|
||||||
entry.catalogMetadata = entry.catalogMetadata?.copyWith(contentId: newContentId);
|
entry.catalogMetadata = entry.catalogMetadata?.copyWith(contentId: newContentId);
|
||||||
entry.addressDetails = entry.addressDetails?.copyWith(contentId: newContentId);
|
entry.addressDetails = entry.addressDetails?.copyWith(contentId: newContentId);
|
||||||
|
|
Loading…
Reference in a new issue