1 /* 2 * Copyright (C) 2019 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.provider.cts.media; 18 19 import android.content.ContentValues; 20 import android.content.Context; 21 import android.net.Uri; 22 import android.os.Build; 23 import android.os.ParcelFileDescriptor; 24 import android.provider.MediaStore; 25 import android.provider.MediaStore.DownloadColumns; 26 import android.provider.MediaStore.Downloads; 27 import android.provider.MediaStore.MediaColumns; 28 import android.text.format.DateUtils; 29 30 import androidx.annotation.NonNull; 31 import androidx.annotation.Nullable; 32 import androidx.test.filters.SdkSuppress; 33 34 import org.junit.Test; 35 36 import java.io.FileNotFoundException; 37 import java.io.OutputStream; 38 import java.util.Objects; 39 40 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.R) 41 public class MediaStoreUtils { 42 @Test testStub()43 public void testStub() { 44 } 45 46 /** 47 * Create a new pending media item using the given parameters. Pending items 48 * are expected to have a short lifetime, and owners should either 49 * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a 50 * pending item within a few hours after first creating it. 51 * 52 * @return token which can be passed to {@link #openPending(Context, Uri)} 53 * to work with this pending item. 54 * @see MediaColumns#IS_PENDING 55 * @see MediaStore#setIncludePending(Uri) 56 * @see MediaStore#createPending(Context, PendingParams) 57 * @removed 58 * @deprecated 59 */ 60 @Deprecated createPending(@onNull Context context, @NonNull PendingParams params)61 public static @NonNull Uri createPending(@NonNull Context context, 62 @NonNull PendingParams params) { 63 return context.getContentResolver().insert(params.insertUri, params.insertValues); 64 } 65 66 /** 67 * Open a pending media item to make progress on it. You can open a pending 68 * item multiple times before finally calling either 69 * {@link PendingSession#publish()} or {@link PendingSession#abandon()}. 70 * 71 * @param uri token which was previously returned from 72 * {@link #createPending(Context, PendingParams)}. 73 * @removed 74 * @deprecated 75 */ 76 @Deprecated openPending(@onNull Context context, @NonNull Uri uri)77 public static @NonNull PendingSession openPending(@NonNull Context context, @NonNull Uri uri) { 78 return new PendingSession(context, uri); 79 } 80 81 /** 82 * Parameters that describe a pending media item. 83 * 84 * @removed 85 * @deprecated 86 */ 87 @Deprecated 88 public static class PendingParams { 89 /** {@hide} */ 90 public final Uri insertUri; 91 /** {@hide} */ 92 public final ContentValues insertValues; 93 94 /** 95 * Create parameters that describe a pending media item. 96 * 97 * @param insertUri the {@code content://} Uri where this pending item 98 * should be inserted when finally published. For example, to 99 * publish an image, use 100 * {@link MediaStore.Images.Media#getContentUri(String)}. 101 */ PendingParams(@onNull Uri insertUri, @NonNull String displayName, @NonNull String mimeType)102 public PendingParams(@NonNull Uri insertUri, @NonNull String displayName, 103 @NonNull String mimeType) { 104 this.insertUri = Objects.requireNonNull(insertUri); 105 final long now = System.currentTimeMillis() / 1000; 106 this.insertValues = new ContentValues(); 107 this.insertValues.put(MediaColumns.DISPLAY_NAME, Objects.requireNonNull(displayName)); 108 this.insertValues.put(MediaColumns.MIME_TYPE, Objects.requireNonNull(mimeType)); 109 this.insertValues.put(MediaColumns.DATE_ADDED, now); 110 this.insertValues.put(MediaColumns.DATE_MODIFIED, now); 111 this.insertValues.put(MediaColumns.IS_PENDING, 1); 112 this.insertValues.put(MediaColumns.DATE_EXPIRES, 113 (System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS) / 1000); 114 } 115 setPath(@ullable String path)116 public void setPath(@Nullable String path) { 117 if (path == null) { 118 this.insertValues.remove(MediaColumns.RELATIVE_PATH); 119 } else { 120 this.insertValues.put(MediaColumns.RELATIVE_PATH, path); 121 } 122 } 123 setIsFavorite(@ullable Boolean isFavorite)124 public void setIsFavorite(@Nullable Boolean isFavorite) { 125 this.insertValues.put(MediaColumns.IS_FAVORITE, isFavorite); 126 } 127 128 /** 129 * Optionally set the Uri from where the file has been downloaded. This is used 130 * for files being added to {@link Downloads} table. 131 * 132 * @see DownloadColumns#DOWNLOAD_URI 133 */ setDownloadUri(@ullable Uri downloadUri)134 public void setDownloadUri(@Nullable Uri downloadUri) { 135 if (downloadUri == null) { 136 this.insertValues.remove(DownloadColumns.DOWNLOAD_URI); 137 } else { 138 this.insertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString()); 139 } 140 } 141 142 /** 143 * Optionally set the Uri indicating HTTP referer of the file. This is used for 144 * files being added to {@link Downloads} table. 145 * 146 * @see DownloadColumns#REFERER_URI 147 */ setRefererUri(@ullable Uri refererUri)148 public void setRefererUri(@Nullable Uri refererUri) { 149 if (refererUri == null) { 150 this.insertValues.remove(DownloadColumns.REFERER_URI); 151 } else { 152 this.insertValues.put(DownloadColumns.REFERER_URI, refererUri.toString()); 153 } 154 } 155 } 156 157 /** 158 * Session actively working on a pending media item. Pending items are 159 * expected to have a short lifetime, and owners should either 160 * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a 161 * pending item within a few hours after first creating it. 162 * 163 * @removed 164 * @deprecated 165 */ 166 @Deprecated 167 public static class PendingSession implements AutoCloseable { 168 /** {@hide} */ 169 private final Context mContext; 170 /** {@hide} */ 171 private final Uri mUri; 172 173 /** {@hide} */ PendingSession(Context context, Uri uri)174 public PendingSession(Context context, Uri uri) { 175 mContext = Objects.requireNonNull(context); 176 mUri = Objects.requireNonNull(uri); 177 } 178 179 /** 180 * Open the underlying file representing this media item. When a media 181 * item is successfully completed, you should 182 * {@link ParcelFileDescriptor#close()} and then {@link #publish()} it. 183 * 184 * @see #notifyProgress(int) 185 */ open()186 public @NonNull ParcelFileDescriptor open() throws FileNotFoundException { 187 return mContext.getContentResolver().openFileDescriptor(mUri, "rw"); 188 } 189 190 /** 191 * Open the underlying file representing this media item. When a media 192 * item is successfully completed, you should 193 * {@link OutputStream#close()} and then {@link #publish()} it. 194 * 195 * @see #notifyProgress(int) 196 */ openOutputStream()197 public @NonNull OutputStream openOutputStream() throws FileNotFoundException { 198 return mContext.getContentResolver().openOutputStream(mUri); 199 } 200 201 /** 202 * When this media item is successfully completed, call this method to 203 * publish and make the final item visible to the user. 204 * 205 * @return the final {@code content://} Uri representing the newly 206 * published media. 207 */ publish()208 public @NonNull Uri publish() { 209 final ContentValues values = new ContentValues(); 210 values.put(MediaColumns.IS_PENDING, 0); 211 values.putNull(MediaColumns.DATE_EXPIRES); 212 mContext.getContentResolver().update(mUri, values, null, null); 213 return mUri; 214 } 215 216 /** 217 * When this media item has failed to be completed, call this method to 218 * destroy the pending item record and any data related to it. 219 */ abandon()220 public void abandon() { 221 mContext.getContentResolver().delete(mUri, null, null); 222 } 223 224 @Override close()225 public void close() { 226 // No resources to close, but at least we can inform people that no 227 // progress is being actively made. 228 } 229 } 230 } 231