From 66dde4415dc6ed6385494494b1413a6223c3416d Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 15 Jan 2019 13:38:06 -0800 Subject: [PATCH] Added an 'All media' folder in the gallery. --- res/layout/mediapicker_media_item.xml | 2 +- res/values/strings.xml | 3 + .../securesms/mediasend/MediaRepository.java | 99 +++++++++++++++---- 3 files changed, 85 insertions(+), 19 deletions(-) diff --git a/res/layout/mediapicker_media_item.xml b/res/layout/mediapicker_media_item.xml index 993f4b9bef..bc9c28bd19 100644 --- a/res/layout/mediapicker_media_item.xml +++ b/res/layout/mediapicker_media_item.xml @@ -46,7 +46,7 @@ android:id="@+id/mediapicker_selected" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/transparent_black_70" + android:background="@color/transparent_black_90" android:visibility="gone"> Add a caption... + + All media + Received a message encrypted using an old version of Signal that is no longer supported. Please ask the sender to update to the most recent version and resend the message. You have left the group. diff --git a/src/org/thoughtcrime/securesms/mediasend/MediaRepository.java b/src/org/thoughtcrime/securesms/mediasend/MediaRepository.java index 5ed56ce9d0..ee7aca8148 100644 --- a/src/org/thoughtcrime/securesms/mediasend/MediaRepository.java +++ b/src/org/thoughtcrime/securesms/mediasend/MediaRepository.java @@ -9,12 +9,13 @@ import android.os.Environment; import android.provider.MediaStore.Images; import android.provider.MediaStore.Video; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; import com.annimon.stream.Stream; +import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.util.Util; -import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; import java.io.File; @@ -30,6 +31,8 @@ import java.util.Map; */ class MediaRepository { + private static final String ALL_MEDIA_BUCKET_ID = "org.thoughtcrime.securesms.ALL_MEDIA"; + /** * Retrieves a list of folders that contain media. */ @@ -46,11 +49,11 @@ class MediaRepository { @WorkerThread private @NonNull List getFolders(@NonNull Context context) { - Pair> imageFolders = getFolders(context, Images.Media.EXTERNAL_CONTENT_URI); - Pair> videoFolders = getFolders(context, Video.Media.EXTERNAL_CONTENT_URI); - Map folders = new HashMap<>(imageFolders.second()); + FolderResult imageFolders = getFolders(context, Images.Media.EXTERNAL_CONTENT_URI); + FolderResult videoFolders = getFolders(context, Video.Media.EXTERNAL_CONTENT_URI); + Map folders = new HashMap<>(imageFolders.getFolderData()); - for (Map.Entry entry : videoFolders.second().entrySet()) { + for (Map.Entry entry : videoFolders.getFolderData().entrySet()) { if (folders.containsKey(entry.getKey())) { folders.get(entry.getKey()).incrementCount(entry.getValue().getCount()); } else { @@ -58,7 +61,7 @@ class MediaRepository { } } - String cameraBucketId = imageFolders.first() != null ? imageFolders.first() : videoFolders.first(); + String cameraBucketId = imageFolders.getCameraBucketId() != null ? imageFolders.getCameraBucketId() : videoFolders.getCameraBucketId(); FolderData cameraFolder = cameraBucketId != null ? folders.remove(cameraBucketId) : null; List mediaFolders = Stream.of(folders.values()).map(folder -> new MediaFolder(folder.getThumbnail(), folder.getTitle(), @@ -68,6 +71,18 @@ class MediaRepository { .sorted((o1, o2) -> o1.getTitle().toLowerCase().compareTo(o2.getTitle().toLowerCase())) .toList(); + Uri allMediaThumbnail = imageFolders.getThumbnailTimestamp() > videoFolders.getThumbnailTimestamp() ? imageFolders.getThumbnail() : videoFolders.getThumbnail(); + + if (allMediaThumbnail != null) { + int allMediaCount = Stream.of(mediaFolders).reduce(0, (count, folder) -> count + folder.getItemCount()); + + if (cameraFolder != null) { + allMediaCount += cameraFolder.getCount(); + } + + mediaFolders.add(0, new MediaFolder(allMediaThumbnail, context.getString(R.string.MediaRepository_all_media), allMediaCount, ALL_MEDIA_BUCKET_ID, MediaFolder.FolderType.NORMAL)); + } + if (cameraFolder != null) { mediaFolders.add(0, new MediaFolder(cameraFolder.getThumbnail(), cameraFolder.getTitle(), cameraFolder.getCount(), cameraFolder.getBucketId(), MediaFolder.FolderType.CAMERA)); } @@ -76,12 +91,14 @@ class MediaRepository { } @WorkerThread - private @NonNull Pair> getFolders(@NonNull Context context, @NonNull Uri contentUri) { - String cameraPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + File.separator + "Camera"; - String cameraBucketId = null; - Map folders = new HashMap<>(); + private @NonNull FolderResult getFolders(@NonNull Context context, @NonNull Uri contentUri) { + String cameraPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getAbsolutePath() + File.separator + "Camera"; + String cameraBucketId = null; + Uri globalThumbnail = null; + long thumbnailTimestamp = 0; + Map folders = new HashMap<>(); - String[] projection = new String[] { Images.Media.DATA, Images.Media.BUCKET_ID, Images.Media.BUCKET_DISPLAY_NAME }; + String[] projection = new String[] { Images.Media.DATA, Images.Media.BUCKET_ID, Images.Media.BUCKET_DISPLAY_NAME, Images.Media.DATE_TAKEN }; String selection = Images.Media.DATA + " NOT NULL"; String sortBy = Images.Media.BUCKET_DISPLAY_NAME + " COLLATE NOCASE ASC, " + Images.Media.DATE_TAKEN + " DESC"; @@ -91,6 +108,7 @@ class MediaRepository { Uri thumbnail = Uri.fromFile(new File(path)); String bucketId = cursor.getString(cursor.getColumnIndexOrThrow(projection[1])); String title = cursor.getString(cursor.getColumnIndexOrThrow(projection[2])); + long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(projection[3])); FolderData folder = Util.getOrDefault(folders, bucketId, new FolderData(thumbnail, title, bucketId)); folder.incrementCount(); @@ -99,10 +117,15 @@ class MediaRepository { if (cameraBucketId == null && path.startsWith(cameraPath)) { cameraBucketId = bucketId; } + + if (timestamp > thumbnailTimestamp) { + globalThumbnail = thumbnail; + thumbnailTimestamp = timestamp; + } } } - return new Pair<>(cameraBucketId, folders); + return new FolderResult(cameraBucketId, globalThumbnail, thumbnailTimestamp, folders); } @WorkerThread @@ -120,13 +143,19 @@ class MediaRepository { @WorkerThread private @NonNull List getMediaInBucket(@NonNull Context context, @NonNull String bucketId, @NonNull Uri contentUri) { - List media = new LinkedList<>(); - String selection = Images.Media.BUCKET_ID + " = ? AND " + Images.Media.DATA + " NOT NULL"; - String sortBy = Images.Media.DATE_TAKEN + " DESC"; - String[] projection = Build.VERSION.SDK_INT >= 16 ? new String[] { Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN, Images.Media.WIDTH, Images.Media.HEIGHT } - : new String[] { Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN }; + List media = new LinkedList<>(); + String selection = Images.Media.BUCKET_ID + " = ? AND " + Images.Media.DATA + " NOT NULL"; + String[] selectionArgs = new String[] { bucketId }; + String sortBy = Images.Media.DATE_TAKEN + " DESC"; + String[] projection = Build.VERSION.SDK_INT >= 16 ? new String[] { Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN, Images.Media.WIDTH, Images.Media.HEIGHT } + : new String[] { Images.Media._ID, Images.Media.MIME_TYPE, Images.Media.DATE_TAKEN }; - try (Cursor cursor = context.getContentResolver().query(contentUri, projection, selection, new String[] { bucketId }, sortBy)) { + if (ALL_MEDIA_BUCKET_ID.equals(bucketId)) { + selection = Images.Media.DATA + " NOT NULL"; + selectionArgs = null; + } + + try (Cursor cursor = context.getContentResolver().query(contentUri, projection, selection, selectionArgs, sortBy)) { while (cursor != null && cursor.moveToNext()) { Uri uri = Uri.withAppendedPath(contentUri, cursor.getString(cursor.getColumnIndexOrThrow(projection[0]))); String mimetype = cursor.getString(cursor.getColumnIndexOrThrow(projection[1])); @@ -146,6 +175,40 @@ class MediaRepository { return media; } + private static class FolderResult { + private final String cameraBucketId; + private final Uri thumbnail; + private final long thumbnailTimestamp; + private final Map folderData; + + private FolderResult(@Nullable String cameraBucketId, + @Nullable Uri thumbnail, + long thumbnailTimestamp, + @NonNull Map folderData) + { + this.cameraBucketId = cameraBucketId; + this.thumbnail = thumbnail; + this.thumbnailTimestamp = thumbnailTimestamp; + this.folderData = folderData; + } + + @Nullable String getCameraBucketId() { + return cameraBucketId; + } + + @Nullable Uri getThumbnail() { + return thumbnail; + } + + long getThumbnailTimestamp() { + return thumbnailTimestamp; + } + + @NonNull Map getFolderData() { + return folderData; + } + } + private static class FolderData { private final Uri thumbnail; private final String title;