1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.test.mediahelper; 18 19 import android.content.ContentResolver; 20 import android.content.ContentUris; 21 import android.content.Context; 22 import android.database.Cursor; 23 import android.net.Uri; 24 import android.provider.MediaStore; 25 26 import com.google.common.collect.ImmutableList; 27 28 import java.io.FileDescriptor; 29 import java.io.FileNotFoundException; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.List; 33 34 /** Helper functions for interacting with android {@link MediaStore}. */ 35 public class MediaStoreHelper { 36 private static MediaStoreHelper sInstance; 37 private ContentResolver mContentResolver; 38 MediaStoreHelper(Context context)39 private MediaStoreHelper(Context context) { 40 mContentResolver = context.getContentResolver(); 41 } 42 getInstance(Context context)43 public static MediaStoreHelper getInstance(Context context) { 44 if (sInstance == null) { 45 sInstance = new MediaStoreHelper(context); 46 } 47 return sInstance; 48 } 49 50 /** 51 * Returns the maximum value of {@link MediaStore.MediaColumns.GENERATION_ADDED} at given URI. 52 * 53 * @param uri The URI, the content:// style uri, for the content to retrieve from {@link 54 * MediaStore}. For example, to retrieve from image media table, use 55 * "content://media/external/images/media". 56 * @param selection A filter declaring which rows to return, formatted as an SQL WHERE clause 57 * (excluding the WHERE itself). Passing null will return all rows for the given URI. 58 * @param selectionArgs You may include ?s in selection, which will be replaced by the values 59 * from selectionArgs, in the order that they appear in the selection. The values will be 60 * bound as Strings. 61 * @return A long value of the maximum generation. Returns -1L if no records find. 62 */ getMaxGeneration(Uri uri, String selection, String[] selectionArgs)63 public long getMaxGeneration(Uri uri, String selection, String[] selectionArgs) { 64 long maxGen = -1L; 65 Cursor cursor = 66 mContentResolver.query( 67 uri, 68 new String[] {MediaStore.MediaColumns.GENERATION_ADDED}, 69 selection, 70 selectionArgs, 71 MediaStore.MediaColumns.GENERATION_ADDED + " DESC"); 72 if (cursor != null) { 73 if (cursor.moveToFirst()) { 74 maxGen = 75 cursor.getLong( 76 cursor.getColumnIndex(MediaStore.MediaColumns.GENERATION_ADDED)); 77 } 78 cursor.close(); 79 } 80 return maxGen; 81 } 82 83 /** 84 * Returns a value list of {@link MediaStore.MediaColumns#_ID}. 85 * 86 * @param uri The URI, the content:// style uri, for the content to retrieve from {@link 87 * MediaStore}. For example, to retrieve from image media table, use 88 * "content://media/external/images/media". 89 * @param selection A filter declaring which rows to return, formatted as an SQL WHERE clause 90 * (excluding the WHERE itself). Passing null will return all rows for the given URI. 91 * @param selectionArgs You may include ?s in selection, which will be replaced by the values 92 * from selectionArgs, in the order that they appear in the selection. The values will be 93 * bound as Strings. 94 * @param fromGen Generation added. The id of the records returned all have generation bigger 95 * than this value. It is also formatted as a WHERE clause together with what specified in 96 * selection. 97 * @return A list of record ids. 98 */ getListOfIdsFromMediaStore( Uri uri, String selection, String[] selectionArgs, long fromGen)99 public List<Long> getListOfIdsFromMediaStore( 100 Uri uri, String selection, String[] selectionArgs, long fromGen) { 101 ImmutableList.Builder builder = new ImmutableList.Builder(); 102 String selectionStr = 103 MediaStore.MediaColumns.GENERATION_ADDED + ">" + String.valueOf(fromGen); 104 if (selection != null && !selection.isEmpty()) { 105 selectionStr = selectionStr + " AND " + selection; 106 } 107 Cursor cursor = 108 mContentResolver.query( 109 uri, 110 new String[] { 111 MediaStore.MediaColumns._ID, MediaStore.MediaColumns.GENERATION_ADDED 112 }, 113 selectionStr, 114 selectionArgs, 115 null); 116 try { 117 while (cursor != null && cursor.moveToNext()) { 118 builder.add(cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns._ID))); 119 } 120 } finally { 121 if (cursor != null) { 122 cursor.close(); 123 } 124 } 125 return builder.build(); 126 } 127 128 /** 129 * Returns a list of media {@link FileDescriptor} for specific MIME type. 130 * 131 * @param uri The URI, the content:// style uri, for the content to retrieve from {@link 132 * MediaStore}. For example, to retrieve from image media table, use 133 * "content://media/external/images/media". 134 * @param mimeType The MIME type of the media item, eg. "image/jpeg". {@see 135 * MediaStore.MediaColumns#MIME_TYPE} 136 * @param fromGen Generation added. The returned records all have generation bigger than this. 137 * @param selection A filter declaring which rows to return, formatted as an SQL WHERE clause 138 * (excluding the WHERE itself). Passing null will return all rows for the given URI. 139 * @param selectionArgs You may include ?s in selection, which will be replaced by the values 140 * from selectionArgs, in the order that they appear in the selection. The values will be 141 * bound as Strings. 142 * @return A list of media {@link FileDescriptor} for specified type. 143 */ getMediaFileDescriptors( Uri uri, String mimeType, String selection, String[] selectionArgs, long fromGen)144 public List<FileDescriptor> getMediaFileDescriptors( 145 Uri uri, String mimeType, String selection, String[] selectionArgs, long fromGen) { 146 ImmutableList.Builder fileListBuilder = new ImmutableList.Builder(); 147 StringBuilder selectionBuilder = new StringBuilder(); 148 List<String> argList = new ArrayList<>(); 149 selectionBuilder.append(MediaStore.MediaColumns.IS_PENDING + "=?"); 150 argList.add("0"); 151 if (mimeType != null) { 152 selectionBuilder.append(" AND "); 153 selectionBuilder.append(MediaStore.MediaColumns.MIME_TYPE + "=?"); 154 argList.add(mimeType); 155 } 156 if (selection != null && !selection.isEmpty()) { 157 selectionBuilder.append(" AND "); 158 selectionBuilder.append(selection); 159 } 160 if (selectionArgs != null) { 161 argList.addAll(Arrays.asList(selectionArgs)); 162 } 163 List<Long> ids = 164 getListOfIdsFromMediaStore( 165 uri, 166 selectionBuilder.toString(), 167 argList.toArray(new String[argList.size()]), 168 fromGen); 169 for (long id : ids) { 170 Uri fileUri = ContentUris.withAppendedId(uri, id); 171 try { 172 FileDescriptor fileDescriptor = 173 mContentResolver.openFileDescriptor(fileUri, "r").getFileDescriptor(); 174 fileListBuilder.add(fileDescriptor); 175 } catch (FileNotFoundException e) { 176 throw new RuntimeException("Unable to open file descriptor at " + uri, e); 177 } 178 } 179 return fileListBuilder.build(); 180 } 181 } 182