Skip to content

Commit

Permalink
Support Android Q.
Browse files Browse the repository at this point in the history
1) "COUNT(*) AS " + COLUMN_COUNT is not working in Android Q anymore (Not sure why)
2) There are no official document specific how GROUP BY should work in CursorLoader. The unofficial workaround seem no longer work in Android Q - https://stackoverflow.com/a/33367564/72437

Due to the above 2 limitations, we need to perform manual calculation on "count per directory".
  • Loading branch information
yccheok committed Sep 11, 2019
1 parent c74fa78 commit 2c8f6f8
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 27 deletions.
6 changes: 3 additions & 3 deletions matisse/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ apply plugin: 'com.novoda.bintray-release'
apply plugin: 'checkstyle'

android {
compileSdkVersion 28
buildToolsVersion '28.0.3'
compileSdkVersion 29
buildToolsVersion '29.0.2'

defaultConfig {
minSdkVersion 14
targetSdkVersion 28
targetSdkVersion 29
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,14 @@ public Album[] newArray(int size) {
private final String mDisplayName;
private long mCount;

Album(String id, String coverPath, String albumName, long count) {
public Album(String id, String coverPath, String albumName, long count) {
mId = id;
mCoverPath = coverPath;
mDisplayName = albumName;
mCount = count;
}

Album(Parcel source) {
private Album(Parcel source) {
mId = source.readString();
mCoverPath = source.readString();
mDisplayName = source.readString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.os.Build;
import android.provider.MediaStore;
import android.util.SparseArray;

import androidx.loader.content.CursorLoader;

import com.zhihu.matisse.internal.entity.Album;
Expand All @@ -31,30 +34,44 @@
* Load all albums (grouped by bucket_id) into a single cursor.
*/
public class AlbumLoader extends CursorLoader {
private static final String COLUMN_BUCKET_ID = "bucket_id";
private static final String COLUMN_BUCKET_DISPLAY_NAME = "bucket_display_name";
public static final String COLUMN_COUNT = "count";
private static final Uri QUERY_URI = MediaStore.Files.getContentUri("external");
private static final String[] COLUMNS = {
MediaStore.Files.FileColumns._ID,
"bucket_id",
"bucket_display_name",
COLUMN_BUCKET_ID,
COLUMN_BUCKET_DISPLAY_NAME,
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.MIME_TYPE,
COLUMN_COUNT};
private static final String[] PROJECTION = {
MediaStore.Files.FileColumns._ID,
"bucket_id",
"bucket_display_name",
COLUMN_BUCKET_ID,
COLUMN_BUCKET_DISPLAY_NAME,
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.MIME_TYPE,
"COUNT(*) AS " + COLUMN_COUNT};

private static final String[] PROJECTION_29 = {
MediaStore.Files.FileColumns._ID,
COLUMN_BUCKET_ID,
COLUMN_BUCKET_DISPLAY_NAME,
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.MIME_TYPE};

// === params for showSingleMediaType: false ===
private static final String SELECTION =
"(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ ") GROUP BY (bucket_id";
private static final String SELECTION_29 =
"(" + MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " OR "
+ MediaStore.Files.FileColumns.MEDIA_TYPE + "=?)"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";
private static final String[] SELECTION_ARGS = {
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE),
String.valueOf(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO),
Expand All @@ -66,6 +83,9 @@ public class AlbumLoader extends CursorLoader {
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ ") GROUP BY (bucket_id";
private static final String SELECTION_FOR_SINGLE_MEDIA_TYPE_29 =
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0";

private static String[] getSelectionArgsForSingleMediaType(int mediaType) {
return new String[]{String.valueOf(mediaType)};
Expand All @@ -78,6 +98,10 @@ private static String[] getSelectionArgsForSingleMediaType(int mediaType) {
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ " AND " + MediaStore.MediaColumns.MIME_TYPE + "=?"
+ ") GROUP BY (bucket_id";
private static final String SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE_29 =
MediaStore.Files.FileColumns.MEDIA_TYPE + "=?"
+ " AND " + MediaStore.MediaColumns.SIZE + ">0"
+ " AND " + MediaStore.MediaColumns.MIME_TYPE + "=?";

private static String[] getSelectionArgsForSingleMediaGifType(int mediaType) {
return new String[]{String.valueOf(mediaType), "image/gif"};
Expand All @@ -87,23 +111,30 @@ private static String[] getSelectionArgsForSingleMediaGifType(int mediaType) {
private static final String BUCKET_ORDER_BY = "datetaken DESC";

private AlbumLoader(Context context, String selection, String[] selectionArgs) {
super(context, QUERY_URI, PROJECTION, selection, selectionArgs, BUCKET_ORDER_BY);
super(
context,
QUERY_URI,
android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ? PROJECTION : PROJECTION_29,
selection,
selectionArgs,
BUCKET_ORDER_BY
);
}

public static CursorLoader newInstance(Context context) {
String selection;
String[] selectionArgs;
if (SelectionSpec.getInstance().onlyShowGif()) {
selection = SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE;
selection = android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ? SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE : SELECTION_FOR_SINGLE_MEDIA_GIF_TYPE_29;
selectionArgs = getSelectionArgsForSingleMediaGifType(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
}else if (SelectionSpec.getInstance().onlyShowImages()) {
selection = SELECTION_FOR_SINGLE_MEDIA_TYPE;
selection = android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ? SELECTION_FOR_SINGLE_MEDIA_TYPE : SELECTION_FOR_SINGLE_MEDIA_TYPE_29;
selectionArgs = getSelectionArgsForSingleMediaType(MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE);
} else if (SelectionSpec.getInstance().onlyShowVideos()) {
selection = SELECTION_FOR_SINGLE_MEDIA_TYPE;
selection = android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ? SELECTION_FOR_SINGLE_MEDIA_TYPE : SELECTION_FOR_SINGLE_MEDIA_TYPE_29;
selectionArgs = getSelectionArgsForSingleMediaType(MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO);
} else {
selection = SELECTION;
selection = android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ? SELECTION : SELECTION_29;
selectionArgs = SELECTION_ARGS;
}
return new AlbumLoader(context, selection, selectionArgs);
Expand All @@ -113,20 +144,58 @@ public static CursorLoader newInstance(Context context) {
public Cursor loadInBackground() {
Cursor albums = super.loadInBackground();
MatrixCursor allAlbum = new MatrixCursor(COLUMNS);
int totalCount = 0;
String allAlbumCoverPath = "";
if (albums != null) {
while (albums.moveToNext()) {
totalCount += albums.getInt(albums.getColumnIndex(COLUMN_COUNT));

if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
int totalCount = 0;
String allAlbumCoverPath = "";
if (albums != null) {
while (albums.moveToNext()) {
totalCount += albums.getInt(albums.getColumnIndex(COLUMN_COUNT));
}
if (albums.moveToFirst()) {
allAlbumCoverPath = albums.getString(albums.getColumnIndex(MediaStore.MediaColumns.DATA));
}
}
if (albums.moveToFirst()) {
allAlbumCoverPath = albums.getString(albums.getColumnIndex(MediaStore.MediaColumns.DATA));

allAlbum.addRow(new String[]{Album.ALBUM_ID_ALL, Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, allAlbumCoverPath, "",
String.valueOf(totalCount)});

return new MergeCursor(new Cursor[]{allAlbum, albums});
} else {
int totalCount = 0;
String allAlbumCoverPath = "";
SparseArray<Album> albumList = new SparseArray<>();
if (albums != null) {
while (albums.moveToNext()) {
String albumCoverPath = albums.getString(albums.getColumnIndex(MediaStore.MediaColumns.DATA));
if ("".equals(allAlbumCoverPath)) {
allAlbumCoverPath = albumCoverPath;
}
int bucketId = albums.getInt(albums.getColumnIndex(COLUMN_BUCKET_ID));
String bucketDisplayName = albums.getString(albums.getColumnIndex(COLUMN_BUCKET_DISPLAY_NAME));
Album album = albumList.get(bucketId);
if (album == null) {
album = new Album(String.valueOf(bucketId), albumCoverPath, bucketDisplayName, 0);
albumList.append(bucketId, album);
}
album.addCaptureCount();
totalCount++;
}
}
}
allAlbum.addRow(new String[]{Album.ALBUM_ID_ALL, Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, allAlbumCoverPath, "",
String.valueOf(totalCount)});

return new MergeCursor(new Cursor[]{allAlbum, albums});
allAlbum.addRow(new String[]{Album.ALBUM_ID_ALL, Album.ALBUM_ID_ALL, Album.ALBUM_NAME_ALL, allAlbumCoverPath, null,
String.valueOf(totalCount)});

for (int i = 0, size = albumList.size(); i < size; i++) {
Album album = albumList.valueAt(i);
allAlbum.addRow(new String[]{album.getId(), album.getId(), album.getDisplayName(null), album.getCoverPath(), null,
String.valueOf(album.getCount())});
}

MergeCursor mergeCursor = new MergeCursor(new Cursor[]{allAlbum});

return mergeCursor;
}
}

@Override
Expand Down
4 changes: 2 additions & 2 deletions sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 28
compileSdkVersion 29

defaultConfig {
applicationId 'com.zhihu.matisse.sample'
minSdkVersion 14
targetSdkVersion 28
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
Expand Down

0 comments on commit 2c8f6f8

Please sign in to comment.