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