blob: 14863338f182d80ae5d3e105aa3d342c64247656 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
17package android.provider;
18
Jeff Sharkey9efa7b82018-12-07 17:15:36 -070019import android.annotation.BytesLong;
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -070020import android.annotation.CurrentTimeMillisLong;
21import android.annotation.CurrentTimeSecondsLong;
Jeff Sharkey5cc407f2019-01-04 16:09:18 -070022import android.annotation.DurationMillisLong;
Jeff Sharkeyc8e49242018-11-02 14:34:44 -060023import android.annotation.IntRange;
Jeff Sharkey30b77bc2018-07-27 20:56:34 -060024import android.annotation.NonNull;
25import android.annotation.Nullable;
Jeff Sharkey9efa7b82018-12-07 17:15:36 -070026import android.annotation.RequiresPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.annotation.SdkConstant;
28import android.annotation.SdkConstant.SdkConstantType;
Jeff Sharkey9efa7b82018-12-07 17:15:36 -070029import android.annotation.TestApi;
Mathew Inwood6750f2e2018-08-10 09:29:25 +010030import android.annotation.UnsupportedAppUsage;
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -060031import android.app.Activity;
32import android.app.AppGlobals;
Jeff Sharkey30b77bc2018-07-27 20:56:34 -060033import android.content.ClipData;
Garfield Tan92b96ba2016-11-01 14:33:48 -070034import android.content.ContentProviderClient;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.ContentResolver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.content.ContentUris;
Jeff Sharkey9d0843d2013-05-07 12:41:33 -070037import android.content.ContentValues;
Marco Nelissen3822f732011-02-03 10:59:30 -080038import android.content.Context;
Jorim Jaggid9449862015-05-29 14:49:08 -070039import android.content.Intent;
Garfield Tan92b96ba2016-11-01 14:33:48 -070040import android.content.UriPermission;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.database.Cursor;
42import android.database.DatabaseUtils;
43import android.graphics.Bitmap;
44import android.graphics.BitmapFactory;
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -070045import android.graphics.ImageDecoder;
Jeff Sharkey30b77bc2018-07-27 20:56:34 -060046import android.graphics.Point;
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -070047import android.graphics.PostProcessor;
Jeff Sharkey33e70bd2018-12-04 11:28:11 -070048import android.media.ExifInterface;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.net.Uri;
Garfield Tan92b96ba2016-11-01 14:33:48 -070050import android.os.Bundle;
Jeff Sharkey30b77bc2018-07-27 20:56:34 -060051import android.os.CancellationSignal;
Jeff Sharkeyc8e49242018-11-02 14:34:44 -060052import android.os.Environment;
Jeff Sharkey8559e652019-01-20 11:21:59 -070053import android.os.FileUtils;
Jeff Sharkey52827962018-10-18 14:44:41 -060054import android.os.OperationCanceledException;
Jeff Sharkeyc8e49242018-11-02 14:34:44 -060055import android.os.ParcelFileDescriptor;
Garfield Tan92b96ba2016-11-01 14:33:48 -070056import android.os.RemoteException;
Jeff Sharkeyc8e49242018-11-02 14:34:44 -060057import android.os.UserHandle;
Jeff Sharkeydc50d4c2018-12-10 18:28:56 -070058import android.os.UserManager;
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -060059import android.os.storage.StorageManager;
60import android.os.storage.StorageVolume;
61import android.os.storage.VolumeInfo;
Jorim Jaggid9449862015-05-29 14:49:08 -070062import android.service.media.CameraPrewarmService;
Jeff Sharkeyc8e49242018-11-02 14:34:44 -060063import android.text.TextUtils;
Jeff Sharkey5cc407f2019-01-04 16:09:18 -070064import android.text.format.DateUtils;
Jeff Sharkey30b77bc2018-07-27 20:56:34 -060065import android.util.ArrayMap;
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -060066import android.util.ArraySet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.util.Log;
68
Jeff Sharkey30b77bc2018-07-27 20:56:34 -060069import com.android.internal.annotations.GuardedBy;
70
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -060071import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import java.io.FileInputStream;
73import java.io.FileNotFoundException;
74import java.io.IOException;
75import java.io.InputStream;
76import java.io.OutputStream;
Jeff Sharkey3f64ec52019-01-22 13:19:40 -070077import java.util.ArrayList;
78import java.util.Collection;
Garfield Tan92b96ba2016-11-01 14:33:48 -070079import java.util.List;
Jeff Sharkeyc8e49242018-11-02 14:34:44 -060080import java.util.Objects;
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -060081import java.util.Set;
Sudheer Shankaa50f1a32018-12-07 01:15:46 -080082import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083
84/**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -070085 * The contract between the media provider and applications. Contains
86 * definitions for the supported URIs and columns.
87 * <p>
88 * The media provider provides an indexed collection of common media types, such
89 * as {@link Audio}, {@link Video}, and {@link Images}, from any attached
90 * storage devices. Each collection is organized based on the primary MIME type
91 * of the underlying content; for example, {@code image/*} content is indexed
92 * under {@link Images}. The {@link Files} collection provides a broad view
93 * across all collections, and does not filter by MIME type.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 */
Ray Chen00c575a2009-08-28 14:12:15 -070095public final class MediaStore {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 private final static String TAG = "MediaStore";
97
Jeff Sharkeybc2ae002018-07-31 10:45:37 -060098 /** The authority for the media provider */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 public static final String AUTHORITY = "media";
Jeff Sharkeybc2ae002018-07-31 10:45:37 -0600100 /** A content:// style uri to the authority for the media provider */
Jeff Sharkeyba4acd52019-03-05 19:46:54 -0700101 public static final @NonNull Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
Jeff Sharkey36ee3622019-01-23 10:55:29 -0700103 /**
104 * Volume name used for content on "internal" storage of device. This
105 * volume contains media distributed with the device, such as built-in
106 * ringtones and wallpapers.
107 */
Jeff Sharkey8559e652019-01-20 11:21:59 -0700108 public static final String VOLUME_INTERNAL = "internal";
Jeff Sharkey36ee3622019-01-23 10:55:29 -0700109
110 /**
111 * Volume name used for content on "external" storage of device. This only
112 * includes media on the primary shared storage device; the contents of any
113 * secondary storage devices can be obtained using
114 * {@link #getAllVolumeNames(Context)}.
115 */
Jeff Sharkey8559e652019-01-20 11:21:59 -0700116 public static final String VOLUME_EXTERNAL = "external";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117
Jeff Sharkeycb269aac2019-01-25 11:15:38 -0700118 /** {@hide} */ @TestApi
119 public static final String SCAN_FILE_CALL = "scan_file";
120 /** {@hide} */ @TestApi
121 public static final String SCAN_VOLUME_CALL = "scan_volume";
122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 /**
Sudheer Shankacae41c42019-02-04 18:16:15 -0800124 * Extra used with {@link #SCAN_FILE_CALL} or {@link #SCAN_VOLUME_CALL} to indicate that
125 * the file path originated from shell.
126 *
127 * {@hide}
128 */
129 @TestApi
130 public static final String EXTRA_ORIGINATED_FROM_SHELL =
131 "android.intent.extra.originated_from_shell";
132
133 /**
Marco Nelissenca78f3d2012-01-27 09:43:20 -0800134 * The method name used by the media scanner and mtp to tell the media provider to
135 * rescan and reclassify that have become unhidden because of renaming folders or
136 * removing nomedia files
137 * @hide
138 */
139 public static final String UNHIDE_CALL = "unhide";
140
141 /**
Sean Stout87313542017-09-08 11:21:16 -0700142 * The method name used by the media scanner service to reload all localized ringtone titles due
143 * to a locale change.
144 * @hide
145 */
146 public static final String RETRANSLATE_CALL = "update_titles";
147
Jeff Sharkey643e99e2018-10-22 18:01:27 -0600148 /** {@hide} */
Jeff Sharkey1b404be2019-03-01 11:31:30 -0700149 public static final String GET_VERSION_CALL = "get_version";
150 /** {@hide} */
Jeff Sharkey643e99e2018-10-22 18:01:27 -0600151 public static final String GET_DOCUMENT_URI_CALL = "get_document_uri";
152 /** {@hide} */
153 public static final String GET_MEDIA_URI_CALL = "get_media_uri";
154
Jeff Sharkey9efa7b82018-12-07 17:15:36 -0700155 /** {@hide} */
156 public static final String GET_CONTRIBUTED_MEDIA_CALL = "get_contributed_media";
157 /** {@hide} */
158 public static final String DELETE_CONTRIBUTED_MEDIA_CALL = "delete_contributed_media";
159
Sean Stout87313542017-09-08 11:21:16 -0700160 /**
Marco Nelissenac259f12012-02-07 07:54:39 -0800161 * This is for internal use by the media scanner only.
162 * Name of the (optional) Uri parameter that determines whether to skip deleting
163 * the file pointed to by the _data column, when deleting the database entry.
164 * The only appropriate value for this parameter is "false", in which case the
165 * delete will be skipped. Note especially that setting this to true, or omitting
166 * the parameter altogether, will perform the default action, which is different
167 * for different types of media.
168 * @hide
169 */
170 public static final String PARAM_DELETE_DATA = "deletedata";
171
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600172 /** {@hide} */
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600173 public static final String PARAM_INCLUDE_PENDING = "includePending";
174 /** {@hide} */
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700175 public static final String PARAM_INCLUDE_TRASHED = "includeTrashed";
176 /** {@hide} */
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600177 public static final String PARAM_PROGRESS = "progress";
Jeff Sharkeycb394992018-12-01 18:26:43 -0700178 /** {@hide} */
179 public static final String PARAM_REQUIRE_ORIGINAL = "requireOriginal";
Sudheer Shankae93db512019-01-11 16:05:28 -0800180 /** {@hide} */
181 public static final String PARAM_LIMIT = "limit";
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600182
Marco Nelissenac259f12012-02-07 07:54:39 -0800183 /**
Daniel Sandleredcdbb62010-02-18 16:00:43 -0500184 * Activity Action: Launch a music player.
185 * The activity should be able to play, browse, or manipulate music files stored on the device.
Jeff Brown6651a632011-11-28 12:59:11 -0800186 *
187 * @deprecated Use {@link android.content.Intent#CATEGORY_APP_MUSIC} instead.
Daniel Sandleredcdbb62010-02-18 16:00:43 -0500188 */
Jeff Brown6651a632011-11-28 12:59:11 -0800189 @Deprecated
Daniel Sandleredcdbb62010-02-18 16:00:43 -0500190 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
191 public static final String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER";
192
193 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 * Activity Action: Perform a search for media.
195 * Contains at least the {@link android.app.SearchManager#QUERY} extra.
196 * May also contain any combination of the following extras:
197 * EXTRA_MEDIA_ARTIST, EXTRA_MEDIA_ALBUM, EXTRA_MEDIA_TITLE, EXTRA_MEDIA_FOCUS
198 *
199 * @see android.provider.MediaStore#EXTRA_MEDIA_ARTIST
200 * @see android.provider.MediaStore#EXTRA_MEDIA_ALBUM
201 * @see android.provider.MediaStore#EXTRA_MEDIA_TITLE
202 * @see android.provider.MediaStore#EXTRA_MEDIA_FOCUS
203 */
204 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
205 public static final String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH";
206
207 /**
Mike LeBeau2fe6fd02010-09-08 19:10:17 -0400208 * An intent to perform a search for music media and automatically play content from the
209 * result when possible. This can be fired, for example, by the result of a voice recognition
210 * command to listen to music.
Ricardo Cerveraa3b13842014-04-01 17:38:08 -0700211 * <p>This intent always includes the {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS}
212 * and {@link android.app.SearchManager#QUERY} extras. The
213 * {@link android.provider.MediaStore#EXTRA_MEDIA_FOCUS} extra determines the search mode, and
214 * the value of the {@link android.app.SearchManager#QUERY} extra depends on the search mode.
215 * For more information about the search modes for this intent, see
216 * <a href="{@docRoot}guide/components/intents-common.html#PlaySearch">Play music based
217 * on a search query</a> in <a href="{@docRoot}guide/components/intents-common.html">Common
218 * Intents</a>.</p>
219 *
220 * <p>This intent makes the most sense for apps that can support large-scale search of music,
221 * such as services connected to an online database of music which can be streamed and played
222 * on the device.</p>
Mike LeBeau2fe6fd02010-09-08 19:10:17 -0400223 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700224 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
Mike LeBeau2fe6fd02010-09-08 19:10:17 -0400225 public static final String INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH =
226 "android.media.action.MEDIA_PLAY_FROM_SEARCH";
Wu-cheng Lif8832052012-08-20 18:30:00 +0800227
Mike LeBeau2fe6fd02010-09-08 19:10:17 -0400228 /**
Florian Uunkb1ac72b2012-09-21 12:16:16 +0100229 * An intent to perform a search for readable media and automatically play content from the
230 * result when possible. This can be fired, for example, by the result of a voice recognition
231 * command to read a book or magazine.
232 * <p>
233 * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string that can
234 * contain any type of unstructured text search, like the name of a book or magazine, an author
235 * a genre, a publisher, or any combination of these.
236 * <p>
237 * Because this intent includes an open-ended unstructured search string, it makes the most
238 * sense for apps that can support large-scale search of text media, such as services connected
239 * to an online database of books and/or magazines which can be read on the device.
240 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700241 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
Florian Uunkb1ac72b2012-09-21 12:16:16 +0100242 public static final String INTENT_ACTION_TEXT_OPEN_FROM_SEARCH =
243 "android.media.action.TEXT_OPEN_FROM_SEARCH";
244
245 /**
246 * An intent to perform a search for video media and automatically play content from the
247 * result when possible. This can be fired, for example, by the result of a voice recognition
248 * command to play movies.
249 * <p>
250 * Contains the {@link android.app.SearchManager#QUERY} extra, which is a string that can
251 * contain any type of unstructured video search, like the name of a movie, one or more actors,
252 * a genre, or any combination of these.
253 * <p>
254 * Because this intent includes an open-ended unstructured search string, it makes the most
255 * sense for apps that can support large-scale search of video, such as services connected to an
256 * online database of videos which can be streamed and played on the device.
257 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700258 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
Florian Uunkb1ac72b2012-09-21 12:16:16 +0100259 public static final String INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH =
260 "android.media.action.VIDEO_PLAY_FROM_SEARCH";
261
262 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 * The name of the Intent-extra used to define the artist
264 */
265 public static final String EXTRA_MEDIA_ARTIST = "android.intent.extra.artist";
266 /**
267 * The name of the Intent-extra used to define the album
268 */
269 public static final String EXTRA_MEDIA_ALBUM = "android.intent.extra.album";
270 /**
271 * The name of the Intent-extra used to define the song title
272 */
273 public static final String EXTRA_MEDIA_TITLE = "android.intent.extra.title";
274 /**
Matt Caseybd7bcf02014-02-05 15:51:39 -0800275 * The name of the Intent-extra used to define the genre.
276 */
277 public static final String EXTRA_MEDIA_GENRE = "android.intent.extra.genre";
278 /**
Matt Casey1dbf1f82014-03-26 17:09:56 -0700279 * The name of the Intent-extra used to define the playlist.
280 */
281 public static final String EXTRA_MEDIA_PLAYLIST = "android.intent.extra.playlist";
282 /**
Matt Caseybd7bcf02014-02-05 15:51:39 -0800283 * The name of the Intent-extra used to define the radio channel.
284 */
285 public static final String EXTRA_MEDIA_RADIO_CHANNEL = "android.intent.extra.radio_channel";
286 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 * The name of the Intent-extra used to define the search focus. The search focus
288 * indicates whether the search should be for things related to the artist, album
289 * or song that is identified by the other extras.
290 */
291 public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus";
292
293 /**
294 * The name of the Intent-extra used to control the orientation of a ViewImage or a MovieView.
295 * This is an int property that overrides the activity's requestedOrientation.
Aurimas Liutikase701dc12018-06-01 16:04:37 -0700296 * @see android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 */
298 public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation";
299
300 /**
301 * The name of an Intent-extra used to control the UI of a ViewImage.
302 * This is a boolean property that overrides the activity's default fullscreen state.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 */
304 public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
305
306 /**
307 * The name of an Intent-extra used to control the UI of a ViewImage.
308 * This is a boolean property that specifies whether or not to show action icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 */
310 public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons";
311
312 /**
313 * The name of the Intent-extra used to control the onCompletion behavior of a MovieView.
314 * This is a boolean property that specifies whether or not to finish the MovieView activity
315 * when the movie completes playing. The default value is true, which means to automatically
316 * exit the movie player activity when the movie completes playing.
317 */
318 public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
319
320 /**
321 * The name of the Intent action used to launch a camera in still image mode.
322 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700323 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
325
326 /**
Jorim Jaggid9449862015-05-29 14:49:08 -0700327 * Name under which an activity handling {@link #INTENT_ACTION_STILL_IMAGE_CAMERA} or
328 * {@link #INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE} publishes the service name for its prewarm
329 * service.
Jorim Jaggia86790b2015-04-02 16:32:29 -0700330 * <p>
Jorim Jaggid9449862015-05-29 14:49:08 -0700331 * This meta-data should reference the fully qualified class name of the prewarm service
332 * extending {@link CameraPrewarmService}.
Jorim Jaggia86790b2015-04-02 16:32:29 -0700333 * <p>
Jorim Jaggid9449862015-05-29 14:49:08 -0700334 * The prewarm service will get bound and receive a prewarm signal
335 * {@link CameraPrewarmService#onPrewarm()} when a camera launch intent fire might be imminent.
336 * An application implementing a prewarm service should do the absolute minimum amount of work
337 * to initialize the camera in order to reduce startup time in likely case that shortly after a
338 * camera launch intent would be sent.
Jorim Jaggia86790b2015-04-02 16:32:29 -0700339 */
Jorim Jaggid9449862015-05-29 14:49:08 -0700340 public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE =
341 "android.media.still_image_camera_preview_service";
Jorim Jaggia86790b2015-04-02 16:32:29 -0700342
343 /**
Wu-cheng Lif8832052012-08-20 18:30:00 +0800344 * The name of the Intent action used to launch a camera in still image mode
345 * for use when the device is secured (e.g. with a pin, password, pattern,
346 * or face unlock). Applications responding to this intent must not expose
347 * any personal content like existing photos or videos on the device. The
348 * applications should be careful not to share any photo or video with other
349 * applications or internet. The activity should use {@link
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -0600350 * Activity#setShowWhenLocked} to display
Wu-cheng Lif8832052012-08-20 18:30:00 +0800351 * on top of the lock screen while secured. There is no activity stack when
352 * this flag is used, so launching more than one activity is strongly
353 * discouraged.
Wu-cheng Lif8832052012-08-20 18:30:00 +0800354 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700355 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
Wu-cheng Lif8832052012-08-20 18:30:00 +0800356 public static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
357 "android.media.action.STILL_IMAGE_CAMERA_SECURE";
358
359 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 * The name of the Intent action used to launch a camera in video mode.
361 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700362 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
364
365 /**
366 * Standard Intent action that can be sent to have the camera application
367 * capture an image and return it.
368 * <p>
369 * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
370 * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
371 * object in the extra field. This is useful for applications that only need a small image.
372 * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
373 * value of EXTRA_OUTPUT.
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700374 * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
Nicolas Prevotd1c99b12014-07-04 16:56:17 +0100375 * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
376 * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
377 * If you don't set a ClipData, it will be copied there for you when calling
378 * {@link Context#startActivity(Intent)}.
Svetoslav7008b512015-06-24 18:47:07 -0700379 *
Dianne Hackborn0e3de6c2015-07-29 15:20:21 -0700380 * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
Svetoslav7008b512015-06-24 18:47:07 -0700381 * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
Philip P. Moltmannf8173ca2016-04-12 15:11:23 -0700382 * is not granted, then attempting to use this action will result in a {@link
Svetoslav7008b512015-06-24 18:47:07 -0700383 * java.lang.SecurityException}.
384 *
385 * @see #EXTRA_OUTPUT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700387 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
389
390 /**
Wu-cheng Li37959ef2012-09-21 14:40:26 +0800391 * Intent action that can be sent to have the camera application capture an image and return
392 * it when the device is secured (e.g. with a pin, password, pattern, or face unlock).
393 * Applications responding to this intent must not expose any personal content like existing
394 * photos or videos on the device. The applications should be careful not to share any photo
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -0600395 * or video with other applications or Internet. The activity should use {@link
396 * Activity#setShowWhenLocked} to display on top of the
Wu-cheng Li37959ef2012-09-21 14:40:26 +0800397 * lock screen while secured. There is no activity stack when this flag is used, so
398 * launching more than one activity is strongly discouraged.
399 * <p>
400 * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
401 * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
402 * object in the extra field. This is useful for applications that only need a small image.
403 * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
404 * value of EXTRA_OUTPUT.
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700405 * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
Nicolas Prevotd1c99b12014-07-04 16:56:17 +0100406 * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
407 * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
408 * If you don't set a ClipData, it will be copied there for you when calling
409 * {@link Context#startActivity(Intent)}.
Wu-cheng Li37959ef2012-09-21 14:40:26 +0800410 *
411 * @see #ACTION_IMAGE_CAPTURE
412 * @see #EXTRA_OUTPUT
Wu-cheng Li37959ef2012-09-21 14:40:26 +0800413 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700414 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
Wu-cheng Li37959ef2012-09-21 14:40:26 +0800415 public static final String ACTION_IMAGE_CAPTURE_SECURE =
416 "android.media.action.IMAGE_CAPTURE_SECURE";
417
418 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 * Standard Intent action that can be sent to have the camera application
Ken Wakasaf76a50c2012-03-09 19:56:35 +0900420 * capture a video and return it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800421 * <p>
422 * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality.
423 * <p>
424 * The caller may pass in an extra EXTRA_OUTPUT to control
425 * where the video is written. If EXTRA_OUTPUT is not present the video will be
426 * written to the standard location for videos, and the Uri of that location will be
427 * returned in the data field of the Uri.
Dianne Hackborn955d8d62014-10-07 20:17:19 -0700428 * As of {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this uri can also be supplied through
Nicolas Prevotd1c99b12014-07-04 16:56:17 +0100429 * {@link android.content.Intent#setClipData(ClipData)}. If using this approach, you still must
430 * supply the uri through the EXTRA_OUTPUT field for compatibility with old applications.
431 * If you don't set a ClipData, it will be copied there for you when calling
432 * {@link Context#startActivity(Intent)}.
Svetoslav7008b512015-06-24 18:47:07 -0700433 *
Dianne Hackborn0e3de6c2015-07-29 15:20:21 -0700434 * <p>Note: if you app targets {@link android.os.Build.VERSION_CODES#M M} and above
Svetoslav7008b512015-06-24 18:47:07 -0700435 * and declares as using the {@link android.Manifest.permission#CAMERA} permission which
436 * is not granted, then atempting to use this action will result in a {@link
437 * java.lang.SecurityException}.
438 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800439 * @see #EXTRA_OUTPUT
Wu-cheng Lie7bc7462011-03-16 17:18:58 +0800440 * @see #EXTRA_VIDEO_QUALITY
441 * @see #EXTRA_SIZE_LIMIT
442 * @see #EXTRA_DURATION_LIMIT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 */
Jeff Sharkey32cd2fb2013-10-02 10:11:22 -0700444 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
446
447 /**
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -0600448 * Standard action that can be sent to review the given media file.
449 * <p>
450 * The launched application is expected to provide a large-scale view of the
451 * given media file, while allowing the user to quickly access other
452 * recently captured media files.
453 * <p>
454 * Input: {@link Intent#getData} is URI of the primary media item to
455 * initially display.
456 *
457 * @see #ACTION_REVIEW_SECURE
458 * @see #EXTRA_BRIGHTNESS
459 */
460 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
461 public final static String ACTION_REVIEW = "android.provider.action.REVIEW";
462
463 /**
464 * Standard action that can be sent to review the given media file when the
465 * device is secured (e.g. with a pin, password, pattern, or face unlock).
466 * The applications should be careful not to share any media with other
467 * applications or Internet. The activity should use
468 * {@link Activity#setShowWhenLocked} to display on top of the lock screen
469 * while secured. There is no activity stack when this flag is used, so
470 * launching more than one activity is strongly discouraged.
471 * <p>
472 * The launched application is expected to provide a large-scale view of the
473 * given primary media file, while only allowing the user to quickly access
474 * other media from an explicit secondary list.
475 * <p>
476 * Input: {@link Intent#getData} is URI of the primary media item to
477 * initially display. {@link Intent#getClipData} is the limited list of
478 * secondary media items that the user is allowed to review. If
479 * {@link Intent#getClipData} is undefined, then no other media access
480 * should be allowed.
481 *
482 * @see #EXTRA_BRIGHTNESS
483 */
484 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
485 public final static String ACTION_REVIEW_SECURE = "android.provider.action.REVIEW_SECURE";
486
487 /**
488 * When defined, the launched application is requested to set the given
489 * brightness value via
490 * {@link android.view.WindowManager.LayoutParams#screenBrightness} to help
491 * ensure a smooth transition when launching {@link #ACTION_REVIEW} or
492 * {@link #ACTION_REVIEW_SECURE} intents.
493 */
494 public final static String EXTRA_BRIGHTNESS = "android.provider.extra.BRIGHTNESS";
495
496 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800497 * The name of the Intent-extra used to control the quality of a recorded video. This is an
498 * integer property. Currently value 0 means low quality, suitable for MMS messages, and
499 * value 1 means high quality. In the future other quality levels may be added.
500 */
501 public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality";
502
503 /**
504 * Specify the maximum allowed size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 */
506 public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit";
507
508 /**
Chih-Chung Changeb0098d2009-08-25 12:59:54 +0800509 * Specify the maximum allowed recording duration in seconds.
Chih-Chung Changeb0098d2009-08-25 12:59:54 +0800510 */
511 public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
512
513 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800514 * The name of the Intent-extra used to indicate a content resolver Uri to be used to
515 * store the requested image or video.
516 */
517 public final static String EXTRA_OUTPUT = "output";
518
519 /**
Marco Nelissened297a82010-01-04 13:24:31 -0800520 * The string that is used when a media attribute is not known. For example,
521 * if an audio file does not have any meta data, the artist and album columns
522 * will be set to this value.
Marco Nelissened297a82010-01-04 13:24:31 -0800523 */
524 public static final String UNKNOWN_STRING = "<unknown>";
525
526 /**
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600527 * Update the given {@link Uri} to also include any pending media items from
528 * calls such as
529 * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
530 * By default no pending items are returned.
531 *
532 * @see MediaColumns#IS_PENDING
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700533 * @see MediaStore#setIncludePending(Uri)
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600534 */
535 public static @NonNull Uri setIncludePending(@NonNull Uri uri) {
Sudheer Shankae93db512019-01-11 16:05:28 -0800536 return setIncludePending(uri.buildUpon()).build();
537 }
538
539 /** @hide */
540 public static @NonNull Uri.Builder setIncludePending(@NonNull Uri.Builder uriBuilder) {
541 return uriBuilder.appendQueryParameter(PARAM_INCLUDE_PENDING, "1");
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600542 }
543
544 /**
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700545 * Update the given {@link Uri} to also include any trashed media items from
546 * calls such as
547 * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
548 * By default no trashed items are returned.
549 *
550 * @see MediaColumns#IS_TRASHED
551 * @see MediaStore#setIncludeTrashed(Uri)
552 * @see MediaStore#trash(Context, Uri)
553 * @see MediaStore#untrash(Context, Uri)
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700554 * @removed
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700555 */
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700556 @Deprecated
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700557 public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) {
558 return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build();
559 }
560
561 /**
Jeff Sharkeycb394992018-12-01 18:26:43 -0700562 * Update the given {@link Uri} to indicate that the caller requires the
563 * original file contents when calling
564 * {@link ContentResolver#openFileDescriptor(Uri, String)}.
565 * <p>
566 * This can be useful when the caller wants to ensure they're backing up the
567 * exact bytes of the underlying media, without any Exif redaction being
568 * performed.
569 * <p>
570 * If the original file contents cannot be provided, a
571 * {@link UnsupportedOperationException} will be thrown when the returned
572 * {@link Uri} is used, such as when the caller doesn't hold
573 * {@link android.Manifest.permission#ACCESS_MEDIA_LOCATION}.
574 */
575 public static @NonNull Uri setRequireOriginal(@NonNull Uri uri) {
576 return uri.buildUpon().appendQueryParameter(PARAM_REQUIRE_ORIGINAL, "1").build();
577 }
578
579 /**
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600580 * Create a new pending media item using the given parameters. Pending items
581 * are expected to have a short lifetime, and owners should either
582 * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
583 * pending item within a few hours after first creating it.
584 *
585 * @return token which can be passed to {@link #openPending(Context, Uri)}
586 * to work with this pending item.
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700587 * @see MediaColumns#IS_PENDING
588 * @see MediaStore#setIncludePending(Uri)
589 * @see MediaStore#createPending(Context, PendingParams)
Jeff Sharkeyd0d26952019-02-27 22:49:21 -0700590 * @removed
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600591 */
Jeff Sharkeyd0d26952019-02-27 22:49:21 -0700592 @Deprecated
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600593 public static @NonNull Uri createPending(@NonNull Context context,
594 @NonNull PendingParams params) {
Jeff Sharkeyd8dc4bc2019-02-08 14:03:35 -0700595 return context.getContentResolver().insert(params.insertUri, params.insertValues);
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600596 }
597
598 /**
599 * Open a pending media item to make progress on it. You can open a pending
600 * item multiple times before finally calling either
601 * {@link PendingSession#publish()} or {@link PendingSession#abandon()}.
602 *
603 * @param uri token which was previously returned from
604 * {@link #createPending(Context, PendingParams)}.
Jeff Sharkeyd0d26952019-02-27 22:49:21 -0700605 * @removed
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600606 */
Jeff Sharkeyd0d26952019-02-27 22:49:21 -0700607 @Deprecated
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600608 public static @NonNull PendingSession openPending(@NonNull Context context, @NonNull Uri uri) {
609 return new PendingSession(context, uri);
610 }
611
612 /**
613 * Parameters that describe a pending media item.
Jeff Sharkeyd0d26952019-02-27 22:49:21 -0700614 *
615 * @removed
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600616 */
Jeff Sharkeyd0d26952019-02-27 22:49:21 -0700617 @Deprecated
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600618 public static class PendingParams {
619 /** {@hide} */
620 public final Uri insertUri;
621 /** {@hide} */
622 public final ContentValues insertValues;
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600623
624 /**
625 * Create parameters that describe a pending media item.
626 *
627 * @param insertUri the {@code content://} Uri where this pending item
628 * should be inserted when finally published. For example, to
629 * publish an image, use
630 * {@link MediaStore.Images.Media#getContentUri(String)}.
631 */
632 public PendingParams(@NonNull Uri insertUri, @NonNull String displayName,
633 @NonNull String mimeType) {
634 this.insertUri = Objects.requireNonNull(insertUri);
635 final long now = System.currentTimeMillis() / 1000;
636 this.insertValues = new ContentValues();
637 this.insertValues.put(MediaColumns.DISPLAY_NAME, Objects.requireNonNull(displayName));
638 this.insertValues.put(MediaColumns.MIME_TYPE, Objects.requireNonNull(mimeType));
639 this.insertValues.put(MediaColumns.DATE_ADDED, now);
640 this.insertValues.put(MediaColumns.DATE_MODIFIED, now);
641 this.insertValues.put(MediaColumns.IS_PENDING, 1);
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700642 this.insertValues.put(MediaColumns.DATE_EXPIRES,
643 (System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS) / 1000);
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600644 }
645
646 /**
647 * Optionally set the primary directory under which this pending item
648 * should be persisted. Only specific well-defined directories from
649 * {@link Environment} are allowed based on the media type being
650 * inserted.
651 * <p>
652 * For example, when creating pending {@link MediaStore.Images.Media}
653 * items, only {@link Environment#DIRECTORY_PICTURES} or
654 * {@link Environment#DIRECTORY_DCIM} are allowed.
655 * <p>
656 * You may leave this value undefined to store the media in a default
657 * location. For example, when this value is left undefined, pending
658 * {@link MediaStore.Audio.Media} items are stored under
659 * {@link Environment#DIRECTORY_MUSIC}.
Jeff Sharkeyc4597792019-01-30 15:14:19 -0700660 *
661 * @see MediaColumns#PRIMARY_DIRECTORY
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600662 */
663 public void setPrimaryDirectory(@Nullable String primaryDirectory) {
Jeff Sharkeyd8dc4bc2019-02-08 14:03:35 -0700664 if (primaryDirectory == null) {
665 this.insertValues.remove(MediaColumns.PRIMARY_DIRECTORY);
666 } else {
667 this.insertValues.put(MediaColumns.PRIMARY_DIRECTORY, primaryDirectory);
668 }
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600669 }
670
671 /**
672 * Optionally set the secondary directory under which this pending item
673 * should be persisted. Any valid directory name is allowed.
674 * <p>
675 * You may leave this value undefined to store the media as a direct
676 * descendant of the {@link #setPrimaryDirectory(String)} location.
Jeff Sharkeyc4597792019-01-30 15:14:19 -0700677 *
678 * @see MediaColumns#SECONDARY_DIRECTORY
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600679 */
680 public void setSecondaryDirectory(@Nullable String secondaryDirectory) {
Jeff Sharkeyd8dc4bc2019-02-08 14:03:35 -0700681 if (secondaryDirectory == null) {
682 this.insertValues.remove(MediaColumns.SECONDARY_DIRECTORY);
683 } else {
684 this.insertValues.put(MediaColumns.SECONDARY_DIRECTORY, secondaryDirectory);
685 }
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600686 }
Sudheer Shanka9b98edb2018-11-15 20:29:03 -0800687
688 /**
689 * Optionally set the Uri from where the file has been downloaded. This is used
690 * for files being added to {@link Downloads} table.
691 *
692 * @see DownloadColumns#DOWNLOAD_URI
693 */
694 public void setDownloadUri(@Nullable Uri downloadUri) {
695 if (downloadUri == null) {
696 this.insertValues.remove(DownloadColumns.DOWNLOAD_URI);
697 } else {
698 this.insertValues.put(DownloadColumns.DOWNLOAD_URI, downloadUri.toString());
699 }
700 }
701
702 /**
703 * Optionally set the Uri indicating HTTP referer of the file. This is used for
704 * files being added to {@link Downloads} table.
705 *
706 * @see DownloadColumns#REFERER_URI
707 */
708 public void setRefererUri(@Nullable Uri refererUri) {
709 if (refererUri == null) {
710 this.insertValues.remove(DownloadColumns.REFERER_URI);
711 } else {
712 this.insertValues.put(DownloadColumns.REFERER_URI, refererUri.toString());
713 }
714 }
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600715 }
716
717 /**
718 * Session actively working on a pending media item. Pending items are
719 * expected to have a short lifetime, and owners should either
720 * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a
721 * pending item within a few hours after first creating it.
Jeff Sharkeyd0d26952019-02-27 22:49:21 -0700722 *
723 * @removed
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600724 */
Jeff Sharkeyd0d26952019-02-27 22:49:21 -0700725 @Deprecated
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600726 public static class PendingSession implements AutoCloseable {
727 /** {@hide} */
728 private final Context mContext;
729 /** {@hide} */
730 private final Uri mUri;
731
732 /** {@hide} */
733 public PendingSession(Context context, Uri uri) {
734 mContext = Objects.requireNonNull(context);
735 mUri = Objects.requireNonNull(uri);
736 }
737
738 /**
739 * Open the underlying file representing this media item. When a media
740 * item is successfully completed, you should
741 * {@link ParcelFileDescriptor#close()} and then {@link #publish()} it.
742 *
743 * @see #notifyProgress(int)
744 */
745 public @NonNull ParcelFileDescriptor open() throws FileNotFoundException {
746 return mContext.getContentResolver().openFileDescriptor(mUri, "rw");
747 }
748
749 /**
750 * Open the underlying file representing this media item. When a media
751 * item is successfully completed, you should
752 * {@link OutputStream#close()} and then {@link #publish()} it.
753 *
754 * @see #notifyProgress(int)
755 */
756 public @NonNull OutputStream openOutputStream() throws FileNotFoundException {
757 return mContext.getContentResolver().openOutputStream(mUri);
758 }
759
760 /**
761 * Notify of current progress on this pending media item. Gallery
762 * applications may choose to surface progress information of this
763 * pending item.
764 *
765 * @param progress a percentage between 0 and 100.
766 */
767 public void notifyProgress(@IntRange(from = 0, to = 100) int progress) {
768 final Uri withProgress = mUri.buildUpon()
769 .appendQueryParameter(PARAM_PROGRESS, Integer.toString(progress)).build();
770 mContext.getContentResolver().notifyChange(withProgress, null, 0);
771 }
772
773 /**
774 * When this media item is successfully completed, call this method to
775 * publish and make the final item visible to the user.
776 *
777 * @return the final {@code content://} Uri representing the newly
778 * published media.
779 */
780 public @NonNull Uri publish() {
781 final ContentValues values = new ContentValues();
782 values.put(MediaColumns.IS_PENDING, 0);
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700783 values.putNull(MediaColumns.DATE_EXPIRES);
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600784 mContext.getContentResolver().update(mUri, values, null, null);
785 return mUri;
786 }
787
788 /**
789 * When this media item has failed to be completed, call this method to
790 * destroy the pending item record and any data related to it.
791 */
792 public void abandon() {
793 mContext.getContentResolver().delete(mUri, null, null);
794 }
795
796 @Override
797 public void close() {
798 // No resources to close, but at least we can inform people that no
799 // progress is being actively made.
800 notifyProgress(-1);
801 }
802 }
803
804 /**
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700805 * Mark the given item as being "trashed", meaning it should be deleted at
806 * some point in the future. This is a more gentle operation than simply
807 * calling {@link ContentResolver#delete(Uri, String, String[])}, which
808 * would take effect immediately.
809 * <p>
810 * This method preserves trashed items for at least 48 hours before erasing
811 * them, giving the user a chance to untrash the item.
812 *
813 * @see MediaColumns#IS_TRASHED
814 * @see MediaStore#setIncludeTrashed(Uri)
815 * @see MediaStore#trash(Context, Uri)
816 * @see MediaStore#untrash(Context, Uri)
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700817 * @removed
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700818 */
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700819 @Deprecated
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700820 public static void trash(@NonNull Context context, @NonNull Uri uri) {
821 trash(context, uri, 48 * DateUtils.HOUR_IN_MILLIS);
822 }
823
824 /**
825 * Mark the given item as being "trashed", meaning it should be deleted at
826 * some point in the future. This is a more gentle operation than simply
827 * calling {@link ContentResolver#delete(Uri, String, String[])}, which
828 * would take effect immediately.
829 * <p>
830 * This method preserves trashed items for at least the given timeout before
831 * erasing them, giving the user a chance to untrash the item.
832 *
833 * @see MediaColumns#IS_TRASHED
834 * @see MediaStore#setIncludeTrashed(Uri)
835 * @see MediaStore#trash(Context, Uri)
836 * @see MediaStore#untrash(Context, Uri)
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700837 * @removed
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700838 */
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700839 @Deprecated
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700840 public static void trash(@NonNull Context context, @NonNull Uri uri,
841 @DurationMillisLong long timeoutMillis) {
842 if (timeoutMillis < 0) {
843 throw new IllegalArgumentException();
844 }
845
846 final ContentValues values = new ContentValues();
847 values.put(MediaColumns.IS_TRASHED, 1);
848 values.put(MediaColumns.DATE_EXPIRES,
849 (System.currentTimeMillis() + timeoutMillis) / 1000);
850 context.getContentResolver().update(uri, values, null, null);
851 }
852
853 /**
854 * Mark the given item as being "untrashed", meaning it should no longer be
855 * deleted as previously requested through {@link #trash(Context, Uri)}.
856 *
857 * @see MediaColumns#IS_TRASHED
858 * @see MediaStore#setIncludeTrashed(Uri)
859 * @see MediaStore#trash(Context, Uri)
860 * @see MediaStore#untrash(Context, Uri)
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700861 * @removed
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700862 */
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700863 @Deprecated
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700864 public static void untrash(@NonNull Context context, @NonNull Uri uri) {
865 final ContentValues values = new ContentValues();
866 values.put(MediaColumns.IS_TRASHED, 0);
867 values.putNull(MediaColumns.DATE_EXPIRES);
868 context.getContentResolver().update(uri, values, null, null);
869 }
870
871 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700872 * Common media metadata columns.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800873 */
Ray Chen00c575a2009-08-28 14:12:15 -0700874 public interface MediaColumns extends BaseColumns {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700876 * Path to the media item on disk.
Jeff Sharkey60cfad82016-01-05 17:30:57 -0700877 * <p>
878 * Note that apps may not have filesystem permissions to directly access
879 * this path. Instead of trying to open this path directly, apps should
880 * use {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
881 * access.
Jeff Sharkey52827962018-10-18 14:44:41 -0600882 *
883 * @deprecated Apps may not have filesystem permissions to directly
884 * access this path. Instead of trying to open this path
885 * directly, apps should use
886 * {@link ContentResolver#openFileDescriptor(Uri, String)}
887 * to gain access. This value will always be {@code NULL}
888 * for apps targeting
889 * {@link android.os.Build.VERSION_CODES#Q} or higher.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 */
Jeff Sharkey52827962018-10-18 14:44:41 -0600891 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700892 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 public static final String DATA = "_data";
894
895 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700896 * Hash of the media item on disk.
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -0600897 * <p>
898 * Contains a 20-byte binary blob which is the SHA-1 hash of the file as
899 * persisted on disk. For performance reasons, the hash may not be
900 * immediately available, in which case a {@code NULL} value will be
901 * returned. If the underlying file is modified, this value will be
902 * cleared and recalculated.
903 * <p>
904 * If you require the hash of a specific item, you can call
905 * {@link ContentResolver#canonicalize(Uri)}, which will block until the
906 * hash is calculated.
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700907 *
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700908 * @removed
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -0600909 */
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700910 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700911 @Column(value = Cursor.FIELD_TYPE_BLOB, readOnly = true)
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -0600912 public static final String HASH = "_hash";
913
914 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700915 * The size of the media item.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700917 @BytesLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700918 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 public static final String SIZE = "_size";
920
921 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700922 * The display name of the media item.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700924 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 public static final String DISPLAY_NAME = "_display_name";
926
927 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700928 * The title of the media item.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700930 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 public static final String TITLE = "title";
932
933 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700934 * The time the media item was first added.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700936 @CurrentTimeSecondsLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700937 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 public static final String DATE_ADDED = "date_added";
939
940 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700941 * The time the media item was last modified.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -0700943 @CurrentTimeSecondsLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700944 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 public static final String DATE_MODIFIED = "date_modified";
946
947 /**
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -0700948 * The MIME type of the media item.
949 * <p>
950 * This is typically defined based on the file extension of the media
951 * item. However, it may be the value of the {@code format} attribute
952 * defined by the <em>Dublin Core Media Initiative</em> standard,
953 * extracted from any XMP metadata contained within this media item.
954 * <p class="note">
955 * Note: the {@code format} attribute may be ignored if the top-level
956 * MIME type disagrees with the file extension. For example, it's
957 * reasonable for an {@code image/jpeg} file to declare a {@code format}
958 * of {@code image/vnd.google.panorama360+jpg}, but declaring a
959 * {@code format} of {@code audio/ogg} would be ignored.
960 * <p>
961 * This is a read-only column that is automatically computed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700963 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 public static final String MIME_TYPE = "mime_type";
Mike Lockwoodd815f792010-07-12 08:49:01 -0400965
966 /**
Mike Lockwoodcbaea352010-07-17 08:34:17 -0400967 * The MTP object handle of a newly transfered file.
968 * Used to pass the new file's object handle through the media scanner
969 * from MTP to the media provider
970 * For internal use only by MTP, media scanner and media provider.
Mike Lockwoodcbaea352010-07-17 08:34:17 -0400971 * @hide
972 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700973 @Deprecated
974 // @Column(Cursor.FIELD_TYPE_INTEGER)
Mike Lockwoodcbaea352010-07-17 08:34:17 -0400975 public static final String MEDIA_SCANNER_NEW_OBJECT_ID = "media_scanner_new_object_id";
Gloria Wang82428a82011-06-27 11:09:00 -0700976
977 /**
978 * Non-zero if the media file is drm-protected
Gloria Wang82428a82011-06-27 11:09:00 -0700979 * @hide
980 */
Mathew Inwood6750f2e2018-08-10 09:29:25 +0100981 @UnsupportedAppUsage
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700982 @Deprecated
983 @Column(Cursor.FIELD_TYPE_INTEGER)
Gloria Wang82428a82011-06-27 11:09:00 -0700984 public static final String IS_DRM = "is_drm";
985
Chih-Chung Change1bf8ef2011-09-20 20:37:21 +0800986 /**
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600987 * Flag indicating if a media item is pending, and still being inserted
988 * by its owner.
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600989 *
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700990 * @see MediaStore#setIncludePending(Uri)
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600991 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -0700992 @Column(Cursor.FIELD_TYPE_INTEGER)
Jeff Sharkeyc8e49242018-11-02 14:34:44 -0600993 public static final String IS_PENDING = "is_pending";
994
995 /**
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700996 * Flag indicating if a media item is trashed.
Jeff Sharkey5cc407f2019-01-04 16:09:18 -0700997 *
998 * @see MediaColumns#IS_TRASHED
999 * @see MediaStore#setIncludeTrashed(Uri)
1000 * @see MediaStore#trash(Context, Uri)
1001 * @see MediaStore#untrash(Context, Uri)
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -07001002 * @removed
Jeff Sharkey5cc407f2019-01-04 16:09:18 -07001003 */
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -07001004 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001005 @Column(Cursor.FIELD_TYPE_INTEGER)
Jeff Sharkey5cc407f2019-01-04 16:09:18 -07001006 public static final String IS_TRASHED = "is_trashed";
1007
1008 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001009 * The time the media item should be considered expired. Typically only
Jeff Sharkeyd0d26952019-02-27 22:49:21 -07001010 * meaningful in the context of {@link #IS_PENDING}.
Jeff Sharkey5cc407f2019-01-04 16:09:18 -07001011 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001012 @CurrentTimeSecondsLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001013 @Column(Cursor.FIELD_TYPE_INTEGER)
Jeff Sharkey5cc407f2019-01-04 16:09:18 -07001014 public static final String DATE_EXPIRES = "date_expires";
1015
1016 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001017 * The width of the media item, in pixels.
Chih-Chung Change1bf8ef2011-09-20 20:37:21 +08001018 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001019 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Chih-Chung Change1bf8ef2011-09-20 20:37:21 +08001020 public static final String WIDTH = "width";
1021
1022 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001023 * The height of the media item, in pixels.
Chih-Chung Change1bf8ef2011-09-20 20:37:21 +08001024 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001025 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Chih-Chung Change1bf8ef2011-09-20 20:37:21 +08001026 public static final String HEIGHT = "height";
Jeff Sharkey0ce83ae2018-08-06 18:55:59 -06001027
1028 /**
Jeff Sharkeyc8e49242018-11-02 14:34:44 -06001029 * Package name that contributed this media. The value may be
1030 * {@code NULL} if ownership cannot be reliably determined.
Jeff Sharkey0ce83ae2018-08-06 18:55:59 -06001031 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001032 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
Jeff Sharkey0ce83ae2018-08-06 18:55:59 -06001033 public static final String OWNER_PACKAGE_NAME = "owner_package_name";
Jeff Sharkeyc4597792019-01-30 15:14:19 -07001034
1035 /**
1036 * The primary directory name this media exists under. The value may be
1037 * {@code NULL} if the media doesn't have a primary directory name.
Jeff Sharkeyc4597792019-01-30 15:14:19 -07001038 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001039 @Column(Cursor.FIELD_TYPE_STRING)
Jeff Sharkeyc4597792019-01-30 15:14:19 -07001040 public static final String PRIMARY_DIRECTORY = "primary_directory";
1041
1042 /**
1043 * The secondary directory name this media exists under. The value may
1044 * be {@code NULL} if the media doesn't have a secondary directory name.
Jeff Sharkeyc4597792019-01-30 15:14:19 -07001045 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001046 @Column(Cursor.FIELD_TYPE_STRING)
Jeff Sharkeyc4597792019-01-30 15:14:19 -07001047 public static final String SECONDARY_DIRECTORY = "secondary_directory";
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -07001048
1049 /**
1050 * The "document ID" GUID as defined by the <em>XMP Media
1051 * Management</em> standard, extracted from any XMP metadata contained
1052 * within this media item. The value is {@code null} when no metadata
1053 * was found.
1054 * <p>
1055 * Each "document ID" is created once for each new resource. Different
1056 * renditions of that resource are expected to have different IDs.
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -07001057 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001058 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -07001059 public static final String DOCUMENT_ID = "document_id";
1060
1061 /**
1062 * The "instance ID" GUID as defined by the <em>XMP Media
1063 * Management</em> standard, extracted from any XMP metadata contained
1064 * within this media item. The value is {@code null} when no metadata
1065 * was found.
1066 * <p>
1067 * This "instance ID" changes with each save operation of a specific
1068 * "document ID".
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -07001069 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001070 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -07001071 public static final String INSTANCE_ID = "instance_id";
1072
1073 /**
1074 * The "original document ID" GUID as defined by the <em>XMP Media
1075 * Management</em> standard, extracted from any XMP metadata contained
1076 * within this media item.
1077 * <p>
1078 * This "original document ID" links a resource to its original source.
1079 * For example, when you save a PSD document as a JPEG, then convert the
1080 * JPEG to GIF format, the "original document ID" of both the JPEG and
1081 * GIF files is the "document ID" of the original PSD file.
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -07001082 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001083 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
Jeff Sharkeyc62b0ac2019-02-07 16:17:13 -07001084 public static final String ORIGINAL_DOCUMENT_ID = "original_document_id";
Jeff Sharkeyc8e49242018-11-02 14:34:44 -06001085 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001087 /**
Mike Lockwood0b20b772010-11-04 13:16:48 -04001088 * Media provider table containing an index of all files in the media storage,
1089 * including non-media files. This should be used by applications that work with
1090 * non-media file types (text, HTML, PDF, etc) as well as applications that need to
1091 * work with multiple media file types in a single query.
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001092 */
Mike Lockwood3b2a62e2010-09-08 12:47:57 -04001093 public static final class Files {
Sudheer Shankae93db512019-01-11 16:05:28 -08001094 /** @hide */
1095 public static final String TABLE = "files";
1096
1097 /** @hide */
1098 public static final Uri EXTERNAL_CONTENT_URI = getContentUri(VOLUME_EXTERNAL);
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001099
Mike Lockwood0b20b772010-11-04 13:16:48 -04001100 /**
1101 * Get the content:// style URI for the files table on the
1102 * given volume.
1103 *
1104 * @param volumeName the name of the volume to get the URI for
1105 * @return the URI to the files table on the given volume
1106 */
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001107 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06001108 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("file").build();
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001109 }
1110
Mike Lockwood0b20b772010-11-04 13:16:48 -04001111 /**
1112 * Get the content:// style URI for a single row in the files table on the
1113 * given volume.
1114 *
1115 * @param volumeName the name of the volume to get the URI for
1116 * @param rowId the file to get the URI for
1117 * @return the URI to the files table on the given volume
1118 */
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001119 public static final Uri getContentUri(String volumeName,
Mike Lockwood0b20b772010-11-04 13:16:48 -04001120 long rowId) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06001121 return ContentUris.withAppendedId(getContentUri(volumeName), rowId);
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001122 }
1123
Mike Lockwood0b20b772010-11-04 13:16:48 -04001124 /**
1125 * For use only by the MTP implementation.
1126 * @hide
1127 */
Mathew Inwood6750f2e2018-08-10 09:29:25 +01001128 @UnsupportedAppUsage
Mike Lockwood8490e662010-09-09 14:16:22 -04001129 public static Uri getMtpObjectsUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06001130 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("object").build();
Mike Lockwood8490e662010-09-09 14:16:22 -04001131 }
1132
Mike Lockwood0b20b772010-11-04 13:16:48 -04001133 /**
1134 * For use only by the MTP implementation.
1135 * @hide
1136 */
Mathew Inwood6750f2e2018-08-10 09:29:25 +01001137 @UnsupportedAppUsage
Mike Lockwood8490e662010-09-09 14:16:22 -04001138 public static final Uri getMtpObjectsUri(String volumeName,
Mike Lockwood3b2a62e2010-09-08 12:47:57 -04001139 long fileId) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06001140 return ContentUris.withAppendedId(getMtpObjectsUri(volumeName), fileId);
Mike Lockwood8490e662010-09-09 14:16:22 -04001141 }
1142
Mike Lockwood0b20b772010-11-04 13:16:48 -04001143 /**
1144 * Used to implement the MTP GetObjectReferences and SetObjectReferences commands.
1145 * @hide
1146 */
Mathew Inwood6750f2e2018-08-10 09:29:25 +01001147 @UnsupportedAppUsage
Mike Lockwood8490e662010-09-09 14:16:22 -04001148 public static final Uri getMtpReferencesUri(String volumeName,
1149 long fileId) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06001150 return getMtpObjectsUri(volumeName, fileId).buildUpon().appendPath("references")
1151 .build();
Mike Lockwood9a2046f2010-08-03 15:30:09 -04001152 }
1153
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001154 /**
Garfield Tan93615412017-03-20 17:21:55 -07001155 * Used to trigger special logic for directories.
1156 * @hide
1157 */
1158 public static final Uri getDirectoryUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06001159 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("dir").build();
Garfield Tan93615412017-03-20 17:21:55 -07001160 }
1161
Sudheer Shankafe7668a2018-12-16 15:52:33 -08001162 /** @hide */
1163 public static final Uri getContentUriForPath(String path) {
Jeff Sharkey1eda2ca2019-01-19 17:27:09 -07001164 return getContentUri(getVolumeName(new File(path)));
Sudheer Shankafe7668a2018-12-16 15:52:33 -08001165 }
1166
Garfield Tan93615412017-03-20 17:21:55 -07001167 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001168 * File metadata columns.
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001169 */
Mike Lockwood3b2a62e2010-09-08 12:47:57 -04001170 public interface FileColumns extends MediaColumns {
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001171 /**
Mike Lockwoodb239b6832011-04-05 10:21:27 -04001172 * The MTP storage ID of the file
Mike Lockwoodb239b6832011-04-05 10:21:27 -04001173 * @hide
1174 */
Mathew Inwood6750f2e2018-08-10 09:29:25 +01001175 @UnsupportedAppUsage
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001176 @Deprecated
1177 // @Column(Cursor.FIELD_TYPE_INTEGER)
Mike Lockwoodb239b6832011-04-05 10:21:27 -04001178 public static final String STORAGE_ID = "storage_id";
1179
1180 /**
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001181 * The MTP format code of the file
Mike Lockwood0b20b772010-11-04 13:16:48 -04001182 * @hide
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001183 */
Mathew Inwood6750f2e2018-08-10 09:29:25 +01001184 @UnsupportedAppUsage
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001185 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001186 public static final String FORMAT = "format";
1187
1188 /**
1189 * The index of the parent directory of the file
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001190 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001191 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001192 public static final String PARENT = "parent";
Mike Lockwoodfed16172010-07-09 10:46:02 -04001193
1194 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001195 * The MIME type of the media item.
1196 * <p>
1197 * This is typically defined based on the file extension of the media
1198 * item. However, it may be the value of the {@code format} attribute
1199 * defined by the <em>Dublin Core Media Initiative</em> standard,
1200 * extracted from any XMP metadata contained within this media item.
1201 * <p class="note">
1202 * Note: the {@code format} attribute may be ignored if the top-level
1203 * MIME type disagrees with the file extension. For example, it's
1204 * reasonable for an {@code image/jpeg} file to declare a {@code format}
1205 * of {@code image/vnd.google.panorama360+jpg}, but declaring a
1206 * {@code format} of {@code audio/ogg} would be ignored.
1207 * <p>
1208 * This is a read-only column that is automatically computed.
Mike Lockwoodfed16172010-07-09 10:46:02 -04001209 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001210 @Column(Cursor.FIELD_TYPE_STRING)
Mike Lockwoodc2206a02010-09-16 06:21:24 -04001211 public static final String MIME_TYPE = "mime_type";
Mike Lockwoodfed16172010-07-09 10:46:02 -04001212
1213 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001214 * The title of the media item.
Mike Lockwoodfed16172010-07-09 10:46:02 -04001215 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001216 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
Mike Lockwoodc2206a02010-09-16 06:21:24 -04001217 public static final String TITLE = "title";
1218
1219 /**
1220 * The media type (audio, video, image or playlist)
1221 * of the file, or 0 for not a media file
Mike Lockwoodc2206a02010-09-16 06:21:24 -04001222 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001223 @Column(Cursor.FIELD_TYPE_INTEGER)
Mike Lockwoodc2206a02010-09-16 06:21:24 -04001224 public static final String MEDIA_TYPE = "media_type";
1225
1226 /**
Mike Lockwooded723b42011-01-26 15:57:07 -08001227 * Constant for the {@link #MEDIA_TYPE} column indicating that file
1228 * is not an audio, image, video or playlist file.
Mike Lockwoodc2206a02010-09-16 06:21:24 -04001229 */
1230 public static final int MEDIA_TYPE_NONE = 0;
Mike Lockwooded723b42011-01-26 15:57:07 -08001231
1232 /**
1233 * Constant for the {@link #MEDIA_TYPE} column indicating that file is an image file.
1234 */
Mike Lockwoodc2206a02010-09-16 06:21:24 -04001235 public static final int MEDIA_TYPE_IMAGE = 1;
Mike Lockwooded723b42011-01-26 15:57:07 -08001236
1237 /**
1238 * Constant for the {@link #MEDIA_TYPE} column indicating that file is an audio file.
1239 */
Mike Lockwoodc2206a02010-09-16 06:21:24 -04001240 public static final int MEDIA_TYPE_AUDIO = 2;
Mike Lockwooded723b42011-01-26 15:57:07 -08001241
1242 /**
Ken Wakasaf76a50c2012-03-09 19:56:35 +09001243 * Constant for the {@link #MEDIA_TYPE} column indicating that file is a video file.
Mike Lockwooded723b42011-01-26 15:57:07 -08001244 */
Mike Lockwoodc2206a02010-09-16 06:21:24 -04001245 public static final int MEDIA_TYPE_VIDEO = 3;
Mike Lockwooded723b42011-01-26 15:57:07 -08001246
1247 /**
Ken Wakasaf76a50c2012-03-09 19:56:35 +09001248 * Constant for the {@link #MEDIA_TYPE} column indicating that file is a playlist file.
Mike Lockwooded723b42011-01-26 15:57:07 -08001249 */
Mike Lockwoodc2206a02010-09-16 06:21:24 -04001250 public static final int MEDIA_TYPE_PLAYLIST = 4;
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001251
1252 /**
1253 * Column indicating if the file is part of Downloads collection.
1254 * @hide
1255 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001256 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001257 public static final String IS_DOWNLOAD = "is_download";
Mike Lockwoodd21eac92010-07-03 00:44:05 -04001258 }
1259 }
1260
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001261 /** @hide */
1262 public static class ThumbnailConstants {
1263 public static final int MINI_KIND = 1;
1264 public static final int FULL_SCREEN_KIND = 2;
1265 public static final int MICRO_KIND = 3;
1266
1267 public static final Point MINI_SIZE = new Point(512, 384);
Jeff Sharkey32f6c7c2018-12-18 09:51:45 -07001268 public static final Point FULL_SCREEN_SIZE = new Point(1024, 786);
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001269 public static final Point MICRO_SIZE = new Point(96, 96);
1270 }
1271
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001272 /**
1273 * Download metadata columns.
1274 */
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001275 public interface DownloadColumns extends MediaColumns {
1276 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001277 * Uri indicating where the item has been downloaded from.
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001278 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001279 @Column(Cursor.FIELD_TYPE_STRING)
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001280 String DOWNLOAD_URI = "download_uri";
1281
1282 /**
1283 * Uri indicating HTTP referer of {@link #DOWNLOAD_URI}.
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001284 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001285 @Column(Cursor.FIELD_TYPE_STRING)
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001286 String REFERER_URI = "referer_uri";
Sudheer Shankafe7668a2018-12-16 15:52:33 -08001287
1288 /**
1289 * The description of the download.
Sudheer Shankafe7668a2018-12-16 15:52:33 -08001290 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001291 @Column(Cursor.FIELD_TYPE_STRING)
Sudheer Shankafe7668a2018-12-16 15:52:33 -08001292 String DESCRIPTION = "description";
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001293 }
1294
1295 /**
Jeff Sharkeyd0d26952019-02-27 22:49:21 -07001296 * Collection of downloaded items.
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001297 */
1298 public static final class Downloads implements DownloadColumns {
1299 private Downloads() {}
1300
1301 /**
1302 * The content:// style URI for the internal storage.
1303 */
Sudheer Shanka0f6069e2019-02-28 11:42:27 -08001304 @NonNull
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001305 public static final Uri INTERNAL_CONTENT_URI =
1306 getContentUri("internal");
1307
1308 /**
1309 * The content:// style URI for the "primary" external storage
1310 * volume.
1311 */
Sudheer Shanka0f6069e2019-02-28 11:42:27 -08001312 @NonNull
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001313 public static final Uri EXTERNAL_CONTENT_URI =
1314 getContentUri("external");
1315
1316 /**
Sudheer Shankaa50f1a32018-12-07 01:15:46 -08001317 * The MIME type for this table.
1318 */
1319 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/download";
1320
1321 /**
1322 * Regex that matches paths that needs to be considered part of downloads collection.
1323 * @hide
1324 */
1325 public static final Pattern PATTERN_DOWNLOADS_FILE = Pattern.compile(
1326 "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/.+");
1327 private static final Pattern PATTERN_DOWNLOADS_DIRECTORY = Pattern.compile(
1328 "(?i)^/storage/[^/]+/(?:[0-9]+/)?(?:Android/sandbox/[^/]+/)?Download/?");
1329
1330 /**
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001331 * Get the content:// style URI for the downloads table on the
1332 * given volume.
1333 *
1334 * @param volumeName the name of the volume to get the URI for
1335 * @return the URI to the image media table on the given volume
1336 */
Sudheer Shanka0f6069e2019-02-28 11:42:27 -08001337 public static @NonNull Uri getContentUri(@NonNull String volumeName) {
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001338 return AUTHORITY_URI.buildUpon().appendPath(volumeName)
1339 .appendPath("downloads").build();
1340 }
1341
1342 /** @hide */
Sudheer Shanka0f6069e2019-02-28 11:42:27 -08001343 public static @NonNull Uri getContentUriForPath(@NonNull String path) {
Jeff Sharkey1eda2ca2019-01-19 17:27:09 -07001344 return getContentUri(getVolumeName(new File(path)));
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001345 }
Sudheer Shankaa50f1a32018-12-07 01:15:46 -08001346
1347 /** @hide */
1348 public static boolean isDownload(@NonNull String path) {
1349 return PATTERN_DOWNLOADS_FILE.matcher(path).matches();
1350 }
1351
1352 /** @hide */
1353 public static boolean isDownloadDir(@NonNull String path) {
1354 return PATTERN_DOWNLOADS_DIRECTORY.matcher(path).matches();
1355 }
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001356 }
1357
Jeff Sharkey1eda2ca2019-01-19 17:27:09 -07001358 /** {@hide} */
1359 public static @NonNull String getVolumeName(@NonNull File path) {
Jeff Sharkey8559e652019-01-20 11:21:59 -07001360 if (FileUtils.contains(Environment.getStorageDirectory(), path)) {
1361 final StorageManager sm = AppGlobals.getInitialApplication()
1362 .getSystemService(StorageManager.class);
1363 final StorageVolume sv = sm.getStorageVolume(path);
1364 if (sv != null) {
1365 if (sv.isPrimary()) {
1366 return VOLUME_EXTERNAL;
1367 } else {
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07001368 return checkArgumentVolumeName(sv.getNormalizedUuid());
Jeff Sharkey8559e652019-01-20 11:21:59 -07001369 }
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001370 }
Jeff Sharkey8559e652019-01-20 11:21:59 -07001371 throw new IllegalStateException("Unknown volume at " + path);
Sudheer Shanka9b98edb2018-11-15 20:29:03 -08001372 } else {
1373 return VOLUME_INTERNAL;
1374 }
1375 }
1376
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 /**
Ray Chen00c575a2009-08-28 14:12:15 -07001378 * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
1379 * to be accessed elsewhere.
1380 */
Jeff Sharkey52827962018-10-18 14:44:41 -06001381 @Deprecated
Ray Chen00c575a2009-08-28 14:12:15 -07001382 private static class InternalThumbnails implements BaseColumns {
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001383 /**
1384 * Currently outstanding thumbnail requests that can be cancelled.
1385 */
1386 @GuardedBy("sPending")
1387 private static ArrayMap<Uri, CancellationSignal> sPending = new ArrayMap<>();
Ray Chenf9a243d2009-10-29 18:15:29 -07001388
Ray Chen00c575a2009-08-28 14:12:15 -07001389 /**
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001390 * Make a blocking request to obtain the given thumbnail, generating it
1391 * if needed.
Ray Chenb9944192009-09-29 20:24:01 -07001392 *
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001393 * @see #cancelThumbnail(ContentResolver, Uri)
Ray Chenb9944192009-09-29 20:24:01 -07001394 */
Jeff Sharkey52827962018-10-18 14:44:41 -06001395 @Deprecated
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001396 static @Nullable Bitmap getThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri,
1397 int kind, @Nullable BitmapFactory.Options opts) {
Jeff Sharkey52827962018-10-18 14:44:41 -06001398 final Point size;
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001399 if (kind == ThumbnailConstants.MICRO_KIND) {
Jeff Sharkey52827962018-10-18 14:44:41 -06001400 size = ThumbnailConstants.MICRO_SIZE;
Jeff Sharkey32f6c7c2018-12-18 09:51:45 -07001401 } else if (kind == ThumbnailConstants.FULL_SCREEN_KIND) {
1402 size = ThumbnailConstants.FULL_SCREEN_SIZE;
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001403 } else if (kind == ThumbnailConstants.MINI_KIND) {
Jeff Sharkey52827962018-10-18 14:44:41 -06001404 size = ThumbnailConstants.MINI_SIZE;
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001405 } else {
1406 throw new IllegalArgumentException("Unsupported kind: " + kind);
Ray Chenb9944192009-09-29 20:24:01 -07001407 }
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001408
1409 CancellationSignal signal = null;
1410 synchronized (sPending) {
1411 signal = sPending.get(uri);
1412 if (signal == null) {
1413 signal = new CancellationSignal();
1414 sPending.put(uri, signal);
1415 }
Ray Chenb9944192009-09-29 20:24:01 -07001416 }
Jeff Sharkey9d0843d2013-05-07 12:41:33 -07001417
Jeff Sharkey52827962018-10-18 14:44:41 -06001418 try {
1419 return cr.loadThumbnail(uri, Point.convert(size), signal);
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001420 } catch (IOException e) {
1421 Log.w(TAG, "Failed to obtain thumbnail for " + uri, e);
1422 return null;
Ray Chen00c575a2009-08-28 14:12:15 -07001423 } finally {
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001424 synchronized (sPending) {
1425 sPending.remove(uri);
1426 }
Ray Chen00c575a2009-08-28 14:12:15 -07001427 }
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001428 }
1429
1430 /**
1431 * This method cancels the thumbnail request so clients waiting for
1432 * {@link #getThumbnail} will be interrupted and return immediately.
1433 * Only the original process which made the request can cancel their own
1434 * requests.
1435 */
Jeff Sharkey52827962018-10-18 14:44:41 -06001436 @Deprecated
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001437 static void cancelThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri) {
1438 synchronized (sPending) {
1439 final CancellationSignal signal = sPending.get(uri);
1440 if (signal != null) {
1441 signal.cancel();
1442 }
1443 }
Ray Chen00c575a2009-08-28 14:12:15 -07001444 }
1445 }
1446
1447 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001448 * Collection of all media with MIME type of {@code image/*}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 */
Ray Chen00c575a2009-08-28 14:12:15 -07001450 public static final class Images {
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001451 /**
1452 * Image metadata columns.
1453 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001454 public interface ImageColumns extends MediaColumns {
1455 /**
1456 * The description of the image
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001458 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001459 public static final String DESCRIPTION = "description";
1460
1461 /**
1462 * The picasa id of the image
Jeff Sharkey7049e652018-09-13 17:05:07 -06001463 *
1464 * @deprecated this value was only relevant for images hosted on
1465 * Picasa, which are no longer supported.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 */
Jeff Sharkey7049e652018-09-13 17:05:07 -06001467 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001468 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001469 public static final String PICASA_ID = "picasa_id";
1470
1471 /**
1472 * Whether the video should be published as public or private
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001473 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001474 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 public static final String IS_PRIVATE = "isprivate";
1476
1477 /**
1478 * The latitude where the image was captured.
Jeff Sharkey33e70bd2018-12-04 11:28:11 -07001479 *
1480 * @deprecated location details are no longer indexed for privacy
1481 * reasons, and this value is now always {@code null}.
1482 * You can still manually obtain location metadata using
1483 * {@link ExifInterface#getLatLong(float[])}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001484 */
Jeff Sharkey33e70bd2018-12-04 11:28:11 -07001485 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001486 @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 public static final String LATITUDE = "latitude";
1488
1489 /**
1490 * The longitude where the image was captured.
Jeff Sharkey33e70bd2018-12-04 11:28:11 -07001491 *
1492 * @deprecated location details are no longer indexed for privacy
1493 * reasons, and this value is now always {@code null}.
1494 * You can still manually obtain location metadata using
1495 * {@link ExifInterface#getLatLong(float[])}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 */
Jeff Sharkey33e70bd2018-12-04 11:28:11 -07001497 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001498 @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 public static final String LONGITUDE = "longitude";
1500
1501 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001502 * The time the media item was taken.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001504 @CurrentTimeMillisLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001505 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001506 public static final String DATE_TAKEN = "datetaken";
1507
1508 /**
1509 * The orientation for the image expressed as degrees.
1510 * Only degrees 0, 90, 180, 270 will work.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001512 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 public static final String ORIENTATION = "orientation";
1514
1515 /**
1516 * The mini thumb id.
Jeff Sharkey7049e652018-09-13 17:05:07 -06001517 *
1518 * @deprecated all thumbnails should be obtained via
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06001519 * {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
Jeff Sharkey7049e652018-09-13 17:05:07 -06001520 * value is no longer supported.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001521 */
Jeff Sharkey7049e652018-09-13 17:05:07 -06001522 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001523 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524 public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
1525
1526 /**
Jeff Sharkey7b148d72019-01-04 14:17:48 -07001527 * The primary bucket ID of this media item. This can be useful to
1528 * present the user a first-level clustering of related media items.
1529 * This is a read-only column that is automatically computed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001531 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 public static final String BUCKET_ID = "bucket_id";
1533
1534 /**
Jeff Sharkey7b148d72019-01-04 14:17:48 -07001535 * The primary bucket display name of this media item. This can be
1536 * useful to present the user a first-level clustering of related
1537 * media items. This is a read-only column that is automatically
1538 * computed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001540 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541 public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
Jeff Sharkey7b148d72019-01-04 14:17:48 -07001542
1543 /**
Jeff Sharkeyc4597792019-01-30 15:14:19 -07001544 * The group ID of this media item. This can be useful to present
1545 * the user a grouping of related media items, such a burst of
1546 * images, or a {@code JPG} and {@code DNG} version of the same
1547 * image.
1548 * <p>
1549 * This is a read-only column that is automatically computed based
1550 * on the first portion of the filename. For example,
1551 * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
1552 * will have the same {@link #GROUP_ID} because the first portion of
1553 * their filenames is identical.
Jeff Sharkey7b148d72019-01-04 14:17:48 -07001554 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001555 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Jeff Sharkeyc4597792019-01-30 15:14:19 -07001556 public static final String GROUP_ID = "group_id";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 }
1558
1559 public static final class Media implements ImageColumns {
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001560 /**
1561 * @deprecated all queries should be performed through
1562 * {@link ContentResolver} directly, which offers modern
1563 * features like {@link CancellationSignal}.
1564 */
1565 @Deprecated
Ray Chen00c575a2009-08-28 14:12:15 -07001566 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
1568 }
1569
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001570 /**
1571 * @deprecated all queries should be performed through
1572 * {@link ContentResolver} directly, which offers modern
1573 * features like {@link CancellationSignal}.
1574 */
1575 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001576 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
Ray Chen00c575a2009-08-28 14:12:15 -07001577 String where, String orderBy) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001578 return cr.query(uri, projection, where,
1579 null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
1580 }
1581
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001582 /**
1583 * @deprecated all queries should be performed through
1584 * {@link ContentResolver} directly, which offers modern
1585 * features like {@link CancellationSignal}.
1586 */
1587 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001588 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
Ray Chen00c575a2009-08-28 14:12:15 -07001589 String selection, String [] selectionArgs, String orderBy) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001590 return cr.query(uri, projection, selection,
1591 selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
1592 }
1593
1594 /**
1595 * Retrieves an image for the given url as a {@link Bitmap}.
1596 *
1597 * @param cr The content resolver to use
1598 * @param url The url of the image
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001599 * @deprecated loading of images should be performed through
1600 * {@link ImageDecoder#createSource(ContentResolver, Uri)},
1601 * which offers modern features like
1602 * {@link PostProcessor}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001604 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 public static final Bitmap getBitmap(ContentResolver cr, Uri url)
Ray Chen00c575a2009-08-28 14:12:15 -07001606 throws FileNotFoundException, IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607 InputStream input = cr.openInputStream(url);
1608 Bitmap bitmap = BitmapFactory.decodeStream(input);
1609 input.close();
1610 return bitmap;
1611 }
1612
1613 /**
1614 * Insert an image and create a thumbnail for it.
1615 *
1616 * @param cr The content resolver to use
1617 * @param imagePath The path to the image to insert
1618 * @param name The name of the image
1619 * @param description The description of the image
1620 * @return The URL to the newly created image
Jeff Sharkeyd0d26952019-02-27 22:49:21 -07001621 * @deprecated inserting of images should be performed using
1622 * {@link MediaColumns#IS_PENDING}, which offers richer
1623 * control over lifecycle.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001625 @Deprecated
Ray Chen00c575a2009-08-28 14:12:15 -07001626 public static final String insertImage(ContentResolver cr, String imagePath,
1627 String name, String description) throws FileNotFoundException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001628 // Check if file exists with a FileInputStream
1629 FileInputStream stream = new FileInputStream(imagePath);
1630 try {
Marco Nelissen2f189fa2009-06-30 10:32:00 -07001631 Bitmap bm = BitmapFactory.decodeFile(imagePath);
1632 String ret = insertImage(cr, bm, name, description);
1633 bm.recycle();
1634 return ret;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001635 } finally {
1636 try {
1637 stream.close();
1638 } catch (IOException e) {
1639 }
1640 }
1641 }
1642
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 /**
1644 * Insert an image and create a thumbnail for it.
1645 *
1646 * @param cr The content resolver to use
1647 * @param source The stream to use for the image
1648 * @param title The name of the image
1649 * @param description The description of the image
1650 * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
1651 * for any reason.
Jeff Sharkeyd0d26952019-02-27 22:49:21 -07001652 * @deprecated inserting of images should be performed using
1653 * {@link MediaColumns#IS_PENDING}, which offers richer
1654 * control over lifecycle.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001655 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001656 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 public static final String insertImage(ContentResolver cr, Bitmap source,
Ray Chen00c575a2009-08-28 14:12:15 -07001658 String title, String description) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 ContentValues values = new ContentValues();
Jeff Sharkey96afa162019-01-02 11:46:48 -07001660 values.put(Images.Media.DISPLAY_NAME, title);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001661 values.put(Images.Media.DESCRIPTION, description);
1662 values.put(Images.Media.MIME_TYPE, "image/jpeg");
1663
1664 Uri url = null;
1665 String stringUrl = null; /* value to be returned */
1666
Ray Chen00c575a2009-08-28 14:12:15 -07001667 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 url = cr.insert(EXTERNAL_CONTENT_URI, values);
1669
1670 if (source != null) {
1671 OutputStream imageOut = cr.openOutputStream(url);
1672 try {
1673 source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
1674 } finally {
1675 imageOut.close();
1676 }
1677
1678 long id = ContentUris.parseId(url);
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001679 // Block until we've generated common thumbnails
1680 Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);
1681 Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MICRO_KIND, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001682 } else {
1683 Log.e(TAG, "Failed to create thumbnail, removing original");
1684 cr.delete(url, null, null);
1685 url = null;
1686 }
1687 } catch (Exception e) {
1688 Log.e(TAG, "Failed to insert image", e);
1689 if (url != null) {
1690 cr.delete(url, null, null);
1691 url = null;
1692 }
1693 }
1694
1695 if (url != null) {
1696 stringUrl = url.toString();
1697 }
1698
1699 return stringUrl;
1700 }
1701
1702 /**
1703 * Get the content:// style URI for the image media table on the
1704 * given volume.
1705 *
1706 * @param volumeName the name of the volume to get the URI for
1707 * @return the URI to the image media table on the given volume
1708 */
1709 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06001710 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("images")
1711 .appendPath("media").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001712 }
1713
1714 /**
1715 * The content:// style URI for the internal storage.
1716 */
1717 public static final Uri INTERNAL_CONTENT_URI =
1718 getContentUri("internal");
1719
1720 /**
1721 * The content:// style URI for the "primary" external storage
1722 * volume.
1723 */
1724 public static final Uri EXTERNAL_CONTENT_URI =
1725 getContentUri("external");
1726
1727 /**
1728 * The MIME type of of this directory of
1729 * images. Note that each entry in this directory will have a standard
1730 * image MIME type as appropriate -- for example, image/jpeg.
1731 */
1732 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image";
1733
1734 /**
1735 * The default sort order for this table
1736 */
1737 public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME;
Ray Chen00c575a2009-08-28 14:12:15 -07001738 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001739
Ray Chen00c575a2009-08-28 14:12:15 -07001740 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06001741 * This class provides utility methods to obtain thumbnails for various
1742 * {@link Images} items.
Jeff Sharkey36ee3622019-01-23 10:55:29 -07001743 *
1744 * @deprecated Callers should migrate to using
1745 * {@link ContentResolver#loadThumbnail}, since it offers
1746 * richer control over requested thumbnail sizes and
1747 * cancellation behavior.
Ray Chen00c575a2009-08-28 14:12:15 -07001748 */
Jeff Sharkey36ee3622019-01-23 10:55:29 -07001749 @Deprecated
Ray Chen00c575a2009-08-28 14:12:15 -07001750 public static class Thumbnails implements BaseColumns {
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001751 /**
1752 * @deprecated all queries should be performed through
1753 * {@link ContentResolver} directly, which offers modern
1754 * features like {@link CancellationSignal}.
1755 */
1756 @Deprecated
Ray Chen00c575a2009-08-28 14:12:15 -07001757 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001758 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
1759 }
1760
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001761 /**
1762 * @deprecated all queries should be performed through
1763 * {@link ContentResolver} directly, which offers modern
1764 * features like {@link CancellationSignal}.
1765 */
1766 @Deprecated
Ray Chen00c575a2009-08-28 14:12:15 -07001767 public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
1768 String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
1770 }
1771
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001772 /**
1773 * @deprecated all queries should be performed through
1774 * {@link ContentResolver} directly, which offers modern
1775 * features like {@link CancellationSignal}.
1776 */
1777 @Deprecated
Ray Chen00c575a2009-08-28 14:12:15 -07001778 public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
1779 String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001780 return cr.query(EXTERNAL_CONTENT_URI, projection,
1781 IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
1782 kind, null, null);
1783 }
1784
1785 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06001786 * Cancel any outstanding {@link #getThumbnail} requests, causing
1787 * them to return by throwing a {@link OperationCanceledException}.
1788 * <p>
1789 * This method has no effect on
1790 * {@link ContentResolver#loadThumbnail} calls, since they provide
1791 * their own {@link CancellationSignal}.
Ray Chenb9944192009-09-29 20:24:01 -07001792 *
Jeff Sharkey52827962018-10-18 14:44:41 -06001793 * @deprecated Callers should migrate to using
1794 * {@link ContentResolver#loadThumbnail}, since it
1795 * offers richer control over requested thumbnail sizes
1796 * and cancellation behavior.
Ray Chenb9944192009-09-29 20:24:01 -07001797 */
Jeff Sharkey52827962018-10-18 14:44:41 -06001798 @Deprecated
Ray Chenb9944192009-09-29 20:24:01 -07001799 public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001800 final Uri uri = ContentUris.withAppendedId(
1801 Images.Media.EXTERNAL_CONTENT_URI, origId);
1802 InternalThumbnails.cancelThumbnail(cr, uri);
Ray Chenb9944192009-09-29 20:24:01 -07001803 }
1804
1805 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06001806 * Return thumbnail representing a specific image item. If a
1807 * thumbnail doesn't exist, this method will block until it's
1808 * generated. Callers are responsible for their own in-memory
1809 * caching of returned values.
Ray Chen00c575a2009-08-28 14:12:15 -07001810 *
Jeff Sharkey52827962018-10-18 14:44:41 -06001811 * @param imageId the image item to obtain a thumbnail for.
1812 * @param kind optimal thumbnail size desired.
1813 * @return decoded thumbnail, or {@code null} if problem was
1814 * encountered.
1815 * @deprecated Callers should migrate to using
1816 * {@link ContentResolver#loadThumbnail}, since it
1817 * offers richer control over requested thumbnail sizes
1818 * and cancellation behavior.
Ray Chen00c575a2009-08-28 14:12:15 -07001819 */
Jeff Sharkey52827962018-10-18 14:44:41 -06001820 @Deprecated
1821 public static Bitmap getThumbnail(ContentResolver cr, long imageId, int kind,
Ray Chen00c575a2009-08-28 14:12:15 -07001822 BitmapFactory.Options options) {
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001823 final Uri uri = ContentUris.withAppendedId(
Jeff Sharkey52827962018-10-18 14:44:41 -06001824 Images.Media.EXTERNAL_CONTENT_URI, imageId);
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001825 return InternalThumbnails.getThumbnail(cr, uri, kind, options);
Ray Chen13ed5752009-10-05 12:21:24 -07001826 }
1827
1828 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06001829 * Cancel any outstanding {@link #getThumbnail} requests, causing
1830 * them to return by throwing a {@link OperationCanceledException}.
1831 * <p>
1832 * This method has no effect on
1833 * {@link ContentResolver#loadThumbnail} calls, since they provide
1834 * their own {@link CancellationSignal}.
Ray Chen13ed5752009-10-05 12:21:24 -07001835 *
Jeff Sharkey52827962018-10-18 14:44:41 -06001836 * @deprecated Callers should migrate to using
1837 * {@link ContentResolver#loadThumbnail}, since it
1838 * offers richer control over requested thumbnail sizes
1839 * and cancellation behavior.
Ray Chen13ed5752009-10-05 12:21:24 -07001840 */
Jeff Sharkey52827962018-10-18 14:44:41 -06001841 @Deprecated
1842 public static void cancelThumbnailRequest(ContentResolver cr, long origId,
1843 long groupId) {
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001844 cancelThumbnailRequest(cr, origId);
Ray Chen13ed5752009-10-05 12:21:24 -07001845 }
1846
1847 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06001848 * Return thumbnail representing a specific image item. If a
1849 * thumbnail doesn't exist, this method will block until it's
1850 * generated. Callers are responsible for their own in-memory
1851 * caching of returned values.
Ray Chen13ed5752009-10-05 12:21:24 -07001852 *
Jeff Sharkey52827962018-10-18 14:44:41 -06001853 * @param imageId the image item to obtain a thumbnail for.
1854 * @param kind optimal thumbnail size desired.
1855 * @return decoded thumbnail, or {@code null} if problem was
1856 * encountered.
1857 * @deprecated Callers should migrate to using
1858 * {@link ContentResolver#loadThumbnail}, since it
1859 * offers richer control over requested thumbnail sizes
1860 * and cancellation behavior.
Ray Chen13ed5752009-10-05 12:21:24 -07001861 */
Jeff Sharkey52827962018-10-18 14:44:41 -06001862 @Deprecated
1863 public static Bitmap getThumbnail(ContentResolver cr, long imageId, long groupId,
Ray Chen13ed5752009-10-05 12:21:24 -07001864 int kind, BitmapFactory.Options options) {
Jeff Sharkey52827962018-10-18 14:44:41 -06001865 return getThumbnail(cr, imageId, kind, options);
Ray Chen00c575a2009-08-28 14:12:15 -07001866 }
1867
1868 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001869 * Get the content:// style URI for the image media table on the
1870 * given volume.
1871 *
1872 * @param volumeName the name of the volume to get the URI for
1873 * @return the URI to the image media table on the given volume
1874 */
1875 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06001876 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("images")
1877 .appendPath("thumbnails").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001878 }
1879
1880 /**
1881 * The content:// style URI for the internal storage.
1882 */
1883 public static final Uri INTERNAL_CONTENT_URI =
1884 getContentUri("internal");
1885
1886 /**
1887 * The content:// style URI for the "primary" external storage
1888 * volume.
1889 */
1890 public static final Uri EXTERNAL_CONTENT_URI =
1891 getContentUri("external");
1892
1893 /**
1894 * The default sort order for this table
1895 */
1896 public static final String DEFAULT_SORT_ORDER = "image_id ASC";
1897
1898 /**
Jeff Sharkey60cfad82016-01-05 17:30:57 -07001899 * Path to the thumbnail file on disk.
1900 * <p>
1901 * Note that apps may not have filesystem permissions to directly
1902 * access this path. Instead of trying to open this path directly,
1903 * apps should use
1904 * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
1905 * access.
Jeff Sharkey52827962018-10-18 14:44:41 -06001906 *
1907 * @deprecated Apps may not have filesystem permissions to directly
1908 * access this path. Instead of trying to open this path
1909 * directly, apps should use
1910 * {@link ContentResolver#loadThumbnail}
1911 * to gain access. This value will always be
1912 * {@code NULL} for apps targeting
1913 * {@link android.os.Build.VERSION_CODES#Q} or higher.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001914 */
Jeff Sharkey52827962018-10-18 14:44:41 -06001915 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001916 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001917 public static final String DATA = "_data";
1918
1919 /**
1920 * The original image for the thumbnal
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001921 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001922 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001923 public static final String IMAGE_ID = "image_id";
1924
1925 /**
1926 * The kind of the thumbnail
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001928 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001929 public static final String KIND = "kind";
1930
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06001931 public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
1932 public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
1933 public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001934
Ray Chen00c575a2009-08-28 14:12:15 -07001935 /**
1936 * The blob raw data of thumbnail
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001937 *
1938 * @deprecated this column never existed internally, and could never
1939 * have returned valid data.
Ray Chen00c575a2009-08-28 14:12:15 -07001940 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001941 @Deprecated
1942 @Column(Cursor.FIELD_TYPE_BLOB)
Ray Chen00c575a2009-08-28 14:12:15 -07001943 public static final String THUMB_DATA = "thumb_data";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001944
1945 /**
1946 * The width of the thumbnal
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001947 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001948 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001949 public static final String WIDTH = "width";
1950
1951 /**
1952 * The height of the thumbnail
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001953 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001954 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 public static final String HEIGHT = "height";
1956 }
1957 }
1958
1959 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001960 * Collection of all media with MIME type of {@code audio/*}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001961 */
1962 public static final class Audio {
1963 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001964 * Audio metadata columns.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001965 */
1966 public interface AudioColumns extends MediaColumns {
1967
1968 /**
1969 * A non human readable key calculated from the TITLE, used for
1970 * searching, sorting and grouping
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001971 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001972 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 public static final String TITLE_KEY = "title_key";
1974
1975 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001976 * The duration of the audio item.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001977 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001978 @DurationMillisLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001979 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001980 public static final String DURATION = "duration";
1981
1982 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001983 * The position within the audio item at which playback should be
1984 * resumed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001985 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07001986 @DurationMillisLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001987 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001988 public static final String BOOKMARK = "bookmark";
1989
1990 /**
1991 * The id of the artist who created the audio file, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001992 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001993 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001994 public static final String ARTIST_ID = "artist_id";
1995
1996 /**
1997 * The artist who created the audio file, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001998 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07001999 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002000 public static final String ARTIST = "artist";
2001
2002 /**
Marco Nelissenabc28192010-03-18 17:10:38 -07002003 * The artist credited for the album that contains the audio file
Marco Nelissenabc28192010-03-18 17:10:38 -07002004 * @hide
2005 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002006 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
Marco Nelissenabc28192010-03-18 17:10:38 -07002007 public static final String ALBUM_ARTIST = "album_artist";
2008
2009 /**
Marco Nelissenee35aff2011-01-06 11:12:17 -08002010 * Whether the song is part of a compilation
Marco Nelissenee35aff2011-01-06 11:12:17 -08002011 * @hide
2012 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002013 @Deprecated
2014 // @Column(Cursor.FIELD_TYPE_STRING)
Marco Nelissenee35aff2011-01-06 11:12:17 -08002015 public static final String COMPILATION = "compilation";
2016
2017 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002018 * A non human readable key calculated from the ARTIST, used for
2019 * searching, sorting and grouping
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002020 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002021 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002022 public static final String ARTIST_KEY = "artist_key";
2023
2024 /**
2025 * The composer of the audio file, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002026 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002027 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 public static final String COMPOSER = "composer";
2029
2030 /**
2031 * The id of the album the audio file is from, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002032 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002033 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002034 public static final String ALBUM_ID = "album_id";
2035
2036 /**
2037 * The album the audio file is from, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002038 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002039 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002040 public static final String ALBUM = "album";
2041
2042 /**
2043 * A non human readable key calculated from the ALBUM, used for
2044 * searching, sorting and grouping
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002045 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002046 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002047 public static final String ALBUM_KEY = "album_key";
2048
2049 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002050 * The track number of this song on the album, if any.
2051 * This number encodes both the track number and the
2052 * disc number. For multi-disc sets, this number will
2053 * be 1xxx for tracks on the first disc, 2xxx for tracks
2054 * on the second disc, etc.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002055 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002056 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002057 public static final String TRACK = "track";
2058
2059 /**
2060 * The year the audio file was recorded, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002061 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002062 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002063 public static final String YEAR = "year";
2064
2065 /**
2066 * Non-zero if the audio file is music
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002067 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002068 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002069 public static final String IS_MUSIC = "is_music";
2070
2071 /**
2072 * Non-zero if the audio file is a podcast
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002073 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002074 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002075 public static final String IS_PODCAST = "is_podcast";
2076
2077 /**
Gloria Wang82428a82011-06-27 11:09:00 -07002078 * Non-zero if the audio file may be a ringtone
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002079 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002080 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002081 public static final String IS_RINGTONE = "is_ringtone";
2082
2083 /**
Gloria Wang82428a82011-06-27 11:09:00 -07002084 * Non-zero if the audio file may be an alarm
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002085 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002086 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002087 public static final String IS_ALARM = "is_alarm";
2088
2089 /**
Gloria Wang82428a82011-06-27 11:09:00 -07002090 * Non-zero if the audio file may be a notification sound
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002091 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002092 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 public static final String IS_NOTIFICATION = "is_notification";
Mike Lockwoode5d04952011-07-30 09:58:35 -04002094
2095 /**
Jeff Sharkey10887d52018-11-30 13:44:41 -07002096 * Non-zero if the audio file is an audiobook
Jeff Sharkey10887d52018-11-30 13:44:41 -07002097 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002098 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Jeff Sharkey10887d52018-11-30 13:44:41 -07002099 public static final String IS_AUDIOBOOK = "is_audiobook";
2100
2101 /**
Mike Lockwoode5d04952011-07-30 09:58:35 -04002102 * The genre of the audio file, if any
Mike Lockwoode5d04952011-07-30 09:58:35 -04002103 * Does not exist in the database - only used by the media scanner for inserts.
2104 * @hide
2105 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002106 @Deprecated
2107 // @Column(Cursor.FIELD_TYPE_STRING)
Mike Lockwoode5d04952011-07-30 09:58:35 -04002108 public static final String GENRE = "genre";
Sean Stout87313542017-09-08 11:21:16 -07002109
2110 /**
2111 * The resource URI of a localized title, if any
Sean Stout87313542017-09-08 11:21:16 -07002112 * Conforms to this pattern:
2113 * Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
2114 * Authority: Package Name of ringtone title provider
2115 * First Path Segment: Type of resource (must be "string")
2116 * Second Path Segment: Resource ID of title
2117 * @hide
2118 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002119 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
Sean Stout87313542017-09-08 11:21:16 -07002120 public static final String TITLE_RESOURCE_URI = "title_resource_uri";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002121 }
2122
2123 /**
2124 * Converts a name to a "key" that can be used for grouping, sorting
2125 * and searching.
2126 * The rules that govern this conversion are:
2127 * - remove 'special' characters like ()[]'!?.,
2128 * - remove leading/trailing spaces
2129 * - convert everything to lowercase
2130 * - remove leading "the ", "an " and "a "
2131 * - remove trailing ", the|an|a"
2132 * - remove accents. This step leaves us with CollationKey data,
2133 * which is not human readable
2134 *
2135 * @param name The artist or album name to convert
2136 * @return The "key" for the given name.
2137 */
2138 public static String keyFor(String name) {
2139 if (name != null) {
Marco Nelissen816cf522009-07-06 09:19:10 -07002140 boolean sortfirst = false;
Marco Nelissen9a488b42010-01-04 15:09:03 -08002141 if (name.equals(UNKNOWN_STRING)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002142 return "\001";
2143 }
Marco Nelissen816cf522009-07-06 09:19:10 -07002144 // Check if the first character is \001. We use this to
2145 // force sorting of certain special files, like the silent ringtone.
2146 if (name.startsWith("\001")) {
2147 sortfirst = true;
2148 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002149 name = name.trim().toLowerCase();
2150 if (name.startsWith("the ")) {
2151 name = name.substring(4);
2152 }
2153 if (name.startsWith("an ")) {
2154 name = name.substring(3);
2155 }
2156 if (name.startsWith("a ")) {
2157 name = name.substring(2);
2158 }
2159 if (name.endsWith(", the") || name.endsWith(",the") ||
2160 name.endsWith(", an") || name.endsWith(",an") ||
2161 name.endsWith(", a") || name.endsWith(",a")) {
2162 name = name.substring(0, name.lastIndexOf(','));
2163 }
Marco Nelissene754e122009-05-22 12:16:58 -07002164 name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002165 if (name.length() > 0) {
2166 // Insert a separator between the characters to avoid
2167 // matches on a partial character. If we ever change
2168 // to start-of-word-only matches, this can be removed.
2169 StringBuilder b = new StringBuilder();
2170 b.append('.');
2171 int nl = name.length();
2172 for (int i = 0; i < nl; i++) {
2173 b.append(name.charAt(i));
2174 b.append('.');
2175 }
2176 name = b.toString();
Marco Nelissen816cf522009-07-06 09:19:10 -07002177 String key = DatabaseUtils.getCollationKey(name);
2178 if (sortfirst) {
2179 key = "\001" + key;
2180 }
2181 return key;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002182 } else {
2183 return "";
2184 }
2185 }
2186 return null;
2187 }
2188
2189 public static final class Media implements AudioColumns {
2190 /**
2191 * Get the content:// style URI for the audio media table on the
2192 * given volume.
2193 *
2194 * @param volumeName the name of the volume to get the URI for
2195 * @return the URI to the audio media table on the given volume
2196 */
2197 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002198 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
2199 .appendPath("media").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002200 }
2201
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002202 /**
2203 * Get the content:// style URI for the given audio media file.
2204 *
2205 * @deprecated Apps may not have filesystem permissions to directly
2206 * access this path.
2207 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002208 @Deprecated
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002209 public static @Nullable Uri getContentUriForPath(@NonNull String path) {
Jeff Sharkey1eda2ca2019-01-19 17:27:09 -07002210 return getContentUri(getVolumeName(new File(path)));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002211 }
2212
2213 /**
2214 * The content:// style URI for the internal storage.
2215 */
2216 public static final Uri INTERNAL_CONTENT_URI =
2217 getContentUri("internal");
2218
2219 /**
2220 * The content:// style URI for the "primary" external storage
2221 * volume.
2222 */
2223 public static final Uri EXTERNAL_CONTENT_URI =
2224 getContentUri("external");
2225
2226 /**
2227 * The MIME type for this table.
2228 */
2229 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio";
2230
2231 /**
Matt Caseybd7bcf02014-02-05 15:51:39 -08002232 * The MIME type for an audio track.
2233 */
2234 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/audio";
2235
2236 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002237 * The default sort order for this table
2238 */
Marco Nelissen816cf522009-07-06 09:19:10 -07002239 public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002240
2241 /**
2242 * Activity Action: Start SoundRecorder application.
2243 * <p>Input: nothing.
2244 * <p>Output: An uri to the recorded sound stored in the Media Library
2245 * if the recording was successful.
2246 * May also contain the extra EXTRA_MAX_BYTES.
2247 * @see #EXTRA_MAX_BYTES
2248 */
Jeff Sharkey0f3f60b2017-04-24 18:06:20 -06002249 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002250 public static final String RECORD_SOUND_ACTION =
2251 "android.provider.MediaStore.RECORD_SOUND";
2252
2253 /**
2254 * The name of the Intent-extra used to define a maximum file size for
2255 * a recording made by the SoundRecorder application.
2256 *
2257 * @see #RECORD_SOUND_ACTION
2258 */
2259 public static final String EXTRA_MAX_BYTES =
2260 "android.provider.MediaStore.extra.MAX_BYTES";
2261 }
2262
2263 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002264 * Audio genre metadata columns.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002265 */
2266 public interface GenresColumns {
2267 /**
2268 * The name of the genre
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002269 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002270 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002271 public static final String NAME = "name";
2272 }
2273
2274 /**
2275 * Contains all genres for audio files
2276 */
2277 public static final class Genres implements BaseColumns, GenresColumns {
2278 /**
2279 * Get the content:// style URI for the audio genres table on the
2280 * given volume.
2281 *
2282 * @param volumeName the name of the volume to get the URI for
2283 * @return the URI to the audio genres table on the given volume
2284 */
2285 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002286 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
2287 .appendPath("genres").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002288 }
2289
2290 /**
Mike Lockwoodbdb05df2010-09-26 12:33:25 -04002291 * Get the content:// style URI for querying the genres of an audio file.
2292 *
2293 * @param volumeName the name of the volume to get the URI for
2294 * @param audioId the ID of the audio file for which to retrieve the genres
2295 * @return the URI to for querying the genres for the audio file
2296 * with the given the volume and audioID
2297 */
2298 public static Uri getContentUriForAudioId(String volumeName, int audioId) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002299 return ContentUris.withAppendedId(Audio.Media.getContentUri(volumeName), audioId)
2300 .buildUpon().appendPath("genres").build();
Mike Lockwoodbdb05df2010-09-26 12:33:25 -04002301 }
2302
2303 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002304 * The content:// style URI for the internal storage.
2305 */
2306 public static final Uri INTERNAL_CONTENT_URI =
2307 getContentUri("internal");
2308
2309 /**
2310 * The content:// style URI for the "primary" external storage
2311 * volume.
2312 */
2313 public static final Uri EXTERNAL_CONTENT_URI =
2314 getContentUri("external");
2315
2316 /**
2317 * The MIME type for this table.
2318 */
2319 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/genre";
2320
2321 /**
2322 * The MIME type for entries in this table.
2323 */
2324 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/genre";
2325
2326 /**
2327 * The default sort order for this table
2328 */
2329 public static final String DEFAULT_SORT_ORDER = NAME;
2330
2331 /**
2332 * Sub-directory of each genre containing all members.
2333 */
2334 public static final class Members implements AudioColumns {
2335
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002336 public static final Uri getContentUri(String volumeName, long genreId) {
2337 return ContentUris
2338 .withAppendedId(Audio.Genres.getContentUri(volumeName), genreId)
2339 .buildUpon().appendPath("members").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002340 }
2341
2342 /**
2343 * A subdirectory of each genre containing all member audio files.
2344 */
2345 public static final String CONTENT_DIRECTORY = "members";
2346
2347 /**
2348 * The default sort order for this table
2349 */
Marco Nelissen816cf522009-07-06 09:19:10 -07002350 public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002351
2352 /**
2353 * The ID of the audio file
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002354 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002355 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002356 public static final String AUDIO_ID = "audio_id";
2357
2358 /**
2359 * The ID of the genre
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002360 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002361 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002362 public static final String GENRE_ID = "genre_id";
2363 }
2364 }
2365
2366 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002367 * Audio playlist metadata columns.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002368 */
2369 public interface PlaylistsColumns {
2370 /**
2371 * The name of the playlist
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002372 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002373 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002374 public static final String NAME = "name";
2375
2376 /**
Jeff Sharkey60cfad82016-01-05 17:30:57 -07002377 * Path to the playlist file on disk.
2378 * <p>
2379 * Note that apps may not have filesystem permissions to directly
2380 * access this path. Instead of trying to open this path directly,
2381 * apps should use
2382 * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
2383 * access.
Jeff Sharkey52827962018-10-18 14:44:41 -06002384 *
2385 * @deprecated Apps may not have filesystem permissions to directly
2386 * access this path. Instead of trying to open this path
2387 * directly, apps should use
2388 * {@link ContentResolver#openFileDescriptor(Uri, String)}
2389 * to gain access. This value will always be
2390 * {@code NULL} for apps targeting
2391 * {@link android.os.Build.VERSION_CODES#Q} or higher.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002392 */
Jeff Sharkey52827962018-10-18 14:44:41 -06002393 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002394 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002395 public static final String DATA = "_data";
2396
2397 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002398 * The time the media item was first added.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002399 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002400 @CurrentTimeSecondsLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002401 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002402 public static final String DATE_ADDED = "date_added";
2403
2404 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002405 * The time the media item was last modified.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002406 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002407 @CurrentTimeSecondsLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002408 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002409 public static final String DATE_MODIFIED = "date_modified";
2410 }
2411
2412 /**
2413 * Contains playlists for audio files
2414 */
2415 public static final class Playlists implements BaseColumns,
2416 PlaylistsColumns {
2417 /**
2418 * Get the content:// style URI for the audio playlists table on the
2419 * given volume.
2420 *
2421 * @param volumeName the name of the volume to get the URI for
2422 * @return the URI to the audio playlists table on the given volume
2423 */
2424 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002425 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
2426 .appendPath("playlists").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002427 }
2428
2429 /**
2430 * The content:// style URI for the internal storage.
2431 */
2432 public static final Uri INTERNAL_CONTENT_URI =
2433 getContentUri("internal");
2434
2435 /**
2436 * The content:// style URI for the "primary" external storage
2437 * volume.
2438 */
2439 public static final Uri EXTERNAL_CONTENT_URI =
2440 getContentUri("external");
2441
2442 /**
2443 * The MIME type for this table.
2444 */
2445 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist";
2446
2447 /**
2448 * The MIME type for entries in this table.
2449 */
2450 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist";
2451
2452 /**
2453 * The default sort order for this table
2454 */
2455 public static final String DEFAULT_SORT_ORDER = NAME;
2456
2457 /**
2458 * Sub-directory of each playlist containing all members.
2459 */
2460 public static final class Members implements AudioColumns {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002461 public static final Uri getContentUri(String volumeName, long playlistId) {
2462 return ContentUris
2463 .withAppendedId(Audio.Playlists.getContentUri(volumeName), playlistId)
2464 .buildUpon().appendPath("members").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002465 }
2466
2467 /**
Marco Nelissene3d05fc2009-12-09 16:01:46 -08002468 * Convenience method to move a playlist item to a new location
2469 * @param res The content resolver to use
2470 * @param playlistId The numeric id of the playlist
2471 * @param from The position of the item to move
2472 * @param to The position to move the item to
2473 * @return true on success
Marco Nelissene3d05fc2009-12-09 16:01:46 -08002474 */
2475 public static final boolean moveItem(ContentResolver res,
2476 long playlistId, int from, int to) {
2477 Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external",
2478 playlistId)
2479 .buildUpon()
2480 .appendEncodedPath(String.valueOf(from))
2481 .appendQueryParameter("move", "true")
2482 .build();
2483 ContentValues values = new ContentValues();
2484 values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, to);
2485 return res.update(uri, values, null, null) != 0;
2486 }
2487
2488 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002489 * The ID within the playlist.
2490 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002491 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002492 public static final String _ID = "_id";
2493
2494 /**
2495 * A subdirectory of each playlist containing all member audio
2496 * files.
2497 */
2498 public static final String CONTENT_DIRECTORY = "members";
2499
2500 /**
2501 * The ID of the audio file
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002502 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002503 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002504 public static final String AUDIO_ID = "audio_id";
2505
2506 /**
2507 * The ID of the playlist
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002508 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002509 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002510 public static final String PLAYLIST_ID = "playlist_id";
2511
2512 /**
2513 * The order of the songs in the playlist
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002514 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002515 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002516 public static final String PLAY_ORDER = "play_order";
2517
2518 /**
2519 * The default sort order for this table
2520 */
2521 public static final String DEFAULT_SORT_ORDER = PLAY_ORDER;
2522 }
2523 }
2524
2525 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002526 * Audio artist metadata columns.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002527 */
2528 public interface ArtistColumns {
2529 /**
2530 * The artist who created the audio file, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002531 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002532 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002533 public static final String ARTIST = "artist";
2534
2535 /**
2536 * A non human readable key calculated from the ARTIST, used for
2537 * searching, sorting and grouping
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002538 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002539 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002540 public static final String ARTIST_KEY = "artist_key";
2541
2542 /**
2543 * The number of albums in the database for this artist
2544 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002545 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002546 public static final String NUMBER_OF_ALBUMS = "number_of_albums";
2547
2548 /**
2549 * The number of albums in the database for this artist
2550 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002551 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002552 public static final String NUMBER_OF_TRACKS = "number_of_tracks";
2553 }
2554
2555 /**
2556 * Contains artists for audio files
2557 */
2558 public static final class Artists implements BaseColumns, ArtistColumns {
2559 /**
2560 * Get the content:// style URI for the artists table on the
2561 * given volume.
2562 *
2563 * @param volumeName the name of the volume to get the URI for
2564 * @return the URI to the audio artists table on the given volume
2565 */
2566 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002567 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
2568 .appendPath("artists").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002569 }
2570
2571 /**
2572 * The content:// style URI for the internal storage.
2573 */
2574 public static final Uri INTERNAL_CONTENT_URI =
2575 getContentUri("internal");
2576
2577 /**
2578 * The content:// style URI for the "primary" external storage
2579 * volume.
2580 */
2581 public static final Uri EXTERNAL_CONTENT_URI =
2582 getContentUri("external");
2583
2584 /**
2585 * The MIME type for this table.
2586 */
2587 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/artists";
2588
2589 /**
2590 * The MIME type for entries in this table.
2591 */
2592 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/artist";
2593
2594 /**
2595 * The default sort order for this table
2596 */
2597 public static final String DEFAULT_SORT_ORDER = ARTIST_KEY;
2598
2599 /**
2600 * Sub-directory of each artist containing all albums on which
2601 * a song by the artist appears.
2602 */
2603 public static final class Albums implements AlbumColumns {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002604 public static final Uri getContentUri(String volumeName,long artistId) {
2605 return ContentUris
2606 .withAppendedId(Audio.Artists.getContentUri(volumeName), artistId)
2607 .buildUpon().appendPath("albums").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002608 }
2609 }
2610 }
2611
2612 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002613 * Audio album metadata columns.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002614 */
2615 public interface AlbumColumns {
2616
2617 /**
2618 * The id for the album
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002619 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002620 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002621 public static final String ALBUM_ID = "album_id";
2622
2623 /**
2624 * The album on which the audio file appears, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002625 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002626 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002627 public static final String ALBUM = "album";
2628
2629 /**
2630 * The artist whose songs appear on this album
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002631 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002632 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002633 public static final String ARTIST = "artist";
2634
2635 /**
2636 * The number of songs on this album
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002637 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002638 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002639 public static final String NUMBER_OF_SONGS = "numsongs";
2640
2641 /**
2642 * This column is available when getting album info via artist,
2643 * and indicates the number of songs on the album by the given
2644 * artist.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002645 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002646 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002647 public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
2648
2649 /**
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07002650 * The year in which the earliest songs
2651 * on this album were released. This will often
2652 * be the same as {@link #LAST_YEAR}, but for compilation albums
2653 * they might differ.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002654 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002655 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002656 public static final String FIRST_YEAR = "minyear";
Ray Chen00c575a2009-08-28 14:12:15 -07002657
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07002658 /**
2659 * The year in which the latest songs
2660 * on this album were released. This will often
2661 * be the same as {@link #FIRST_YEAR}, but for compilation albums
2662 * they might differ.
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07002663 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002664 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002665 public static final String LAST_YEAR = "maxyear";
2666
2667 /**
2668 * A non human readable key calculated from the ALBUM, used for
2669 * searching, sorting and grouping
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002670 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002671 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002672 public static final String ALBUM_KEY = "album_key";
2673
2674 /**
2675 * Cached album art.
Jeff Sharkey52827962018-10-18 14:44:41 -06002676 *
2677 * @deprecated Apps may not have filesystem permissions to directly
2678 * access this path. Instead of trying to open this path
2679 * directly, apps should use
2680 * {@link ContentResolver#loadThumbnail}
2681 * to gain access. This value will always be
2682 * {@code NULL} for apps targeting
2683 * {@link android.os.Build.VERSION_CODES#Q} or higher.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002684 */
Jeff Sharkey52827962018-10-18 14:44:41 -06002685 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002686 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002687 public static final String ALBUM_ART = "album_art";
2688 }
2689
2690 /**
2691 * Contains artists for audio files
2692 */
2693 public static final class Albums implements BaseColumns, AlbumColumns {
2694 /**
2695 * Get the content:// style URI for the albums table on the
2696 * given volume.
2697 *
2698 * @param volumeName the name of the volume to get the URI for
2699 * @return the URI to the audio albums table on the given volume
2700 */
2701 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002702 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("audio")
2703 .appendPath("albums").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002704 }
2705
2706 /**
2707 * The content:// style URI for the internal storage.
2708 */
2709 public static final Uri INTERNAL_CONTENT_URI =
2710 getContentUri("internal");
2711
2712 /**
2713 * The content:// style URI for the "primary" external storage
2714 * volume.
2715 */
2716 public static final Uri EXTERNAL_CONTENT_URI =
2717 getContentUri("external");
2718
2719 /**
2720 * The MIME type for this table.
2721 */
2722 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/albums";
2723
2724 /**
2725 * The MIME type for entries in this table.
2726 */
2727 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/album";
2728
2729 /**
2730 * The default sort order for this table
2731 */
2732 public static final String DEFAULT_SORT_ORDER = ALBUM_KEY;
2733 }
Matt Caseybd7bcf02014-02-05 15:51:39 -08002734
2735 public static final class Radio {
2736 /**
2737 * The MIME type for entries in this table.
2738 */
2739 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/radio";
Matt Caseyde6f4702014-06-06 10:16:15 -07002740
2741 // Not instantiable.
2742 private Radio() { }
Matt Caseybd7bcf02014-02-05 15:51:39 -08002743 }
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002744
2745 /**
2746 * This class provides utility methods to obtain thumbnails for various
2747 * {@link Audio} items.
2748 *
2749 * @deprecated Callers should migrate to using
2750 * {@link ContentResolver#loadThumbnail}, since it offers
2751 * richer control over requested thumbnail sizes and
2752 * cancellation behavior.
2753 * @hide
2754 */
2755 @Deprecated
2756 public static class Thumbnails implements BaseColumns {
2757 /**
2758 * Path to the thumbnail file on disk.
2759 * <p>
2760 * Note that apps may not have filesystem permissions to directly
2761 * access this path. Instead of trying to open this path directly,
2762 * apps should use
2763 * {@link ContentResolver#openFileDescriptor(Uri, String)} to gain
2764 * access.
2765 *
2766 * @deprecated Apps may not have filesystem permissions to directly
2767 * access this path. Instead of trying to open this path
2768 * directly, apps should use
2769 * {@link ContentResolver#loadThumbnail}
2770 * to gain access. This value will always be
2771 * {@code NULL} for apps targeting
2772 * {@link android.os.Build.VERSION_CODES#Q} or higher.
2773 */
2774 @Deprecated
2775 @Column(Cursor.FIELD_TYPE_STRING)
2776 public static final String DATA = "_data";
2777
2778 @Column(Cursor.FIELD_TYPE_INTEGER)
2779 public static final String ALBUM_ID = "album_id";
2780 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002781 }
2782
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002783 /**
2784 * Collection of all media with MIME type of {@code video/*}.
2785 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002786 public static final class Video {
2787
2788 /**
2789 * The default sort order for this table.
2790 */
2791 public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;
2792
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002793 /**
2794 * @deprecated all queries should be performed through
2795 * {@link ContentResolver} directly, which offers modern
2796 * features like {@link CancellationSignal}.
2797 */
2798 @Deprecated
Ray Chen00c575a2009-08-28 14:12:15 -07002799 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002800 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
2801 }
2802
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002803 /**
2804 * Video metadata columns.
2805 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002806 public interface VideoColumns extends MediaColumns {
2807
2808 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002809 * The duration of the video item.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002810 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002811 @DurationMillisLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002812 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002813 public static final String DURATION = "duration";
2814
2815 /**
2816 * The artist who created the video file, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002817 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002818 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002819 public static final String ARTIST = "artist";
2820
2821 /**
2822 * The album the video file is from, if any
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002823 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002824 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002825 public static final String ALBUM = "album";
2826
2827 /**
2828 * The resolution of the video file, formatted as "XxY"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002829 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002830 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002831 public static final String RESOLUTION = "resolution";
2832
2833 /**
2834 * The description of the video recording
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002835 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002836 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002837 public static final String DESCRIPTION = "description";
2838
2839 /**
2840 * Whether the video should be published as public or private
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002841 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002842 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002843 public static final String IS_PRIVATE = "isprivate";
2844
2845 /**
2846 * The user-added tags associated with a video
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002847 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002848 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002849 public static final String TAGS = "tags";
2850
2851 /**
2852 * The YouTube category of the video
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002853 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002854 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002855 public static final String CATEGORY = "category";
2856
2857 /**
2858 * The language of the video
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002859 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002860 @Column(Cursor.FIELD_TYPE_STRING)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002861 public static final String LANGUAGE = "language";
2862
2863 /**
Casey Hoc3898822012-03-02 16:32:09 -08002864 * The latitude where the video was captured.
Jeff Sharkey33e70bd2018-12-04 11:28:11 -07002865 *
2866 * @deprecated location details are no longer indexed for privacy
2867 * reasons, and this value is now always {@code null}.
2868 * You can still manually obtain location metadata using
2869 * {@link ExifInterface#getLatLong(float[])}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002870 */
Jeff Sharkey33e70bd2018-12-04 11:28:11 -07002871 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002872 @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002873 public static final String LATITUDE = "latitude";
2874
2875 /**
Casey Hoc3898822012-03-02 16:32:09 -08002876 * The longitude where the video was captured.
Jeff Sharkey33e70bd2018-12-04 11:28:11 -07002877 *
2878 * @deprecated location details are no longer indexed for privacy
2879 * reasons, and this value is now always {@code null}.
2880 * You can still manually obtain location metadata using
2881 * {@link ExifInterface#getLatLong(float[])}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002882 */
Jeff Sharkey33e70bd2018-12-04 11:28:11 -07002883 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002884 @Column(value = Cursor.FIELD_TYPE_FLOAT, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002885 public static final String LONGITUDE = "longitude";
2886
2887 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002888 * The time the media item was taken.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002889 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002890 @CurrentTimeMillisLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002891 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002892 public static final String DATE_TAKEN = "datetaken";
2893
2894 /**
2895 * The mini thumb id.
Jeff Sharkey7049e652018-09-13 17:05:07 -06002896 *
2897 * @deprecated all thumbnails should be obtained via
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002898 * {@link MediaStore.Images.Thumbnails#getThumbnail}, as this
Jeff Sharkey7049e652018-09-13 17:05:07 -06002899 * value is no longer supported.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002900 */
Jeff Sharkey7049e652018-09-13 17:05:07 -06002901 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002902 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002903 public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
2904
2905 /**
Jeff Sharkey7b148d72019-01-04 14:17:48 -07002906 * The primary bucket ID of this media item. This can be useful to
2907 * present the user a first-level clustering of related media items.
2908 * This is a read-only column that is automatically computed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002909 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002910 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002911 public static final String BUCKET_ID = "bucket_id";
2912
2913 /**
Jeff Sharkey7b148d72019-01-04 14:17:48 -07002914 * The primary bucket display name of this media item. This can be
2915 * useful to present the user a first-level clustering of related
2916 * media items. This is a read-only column that is automatically
2917 * computed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002918 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002919 @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002920 public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
2921
2922 /**
Jeff Sharkeyc4597792019-01-30 15:14:19 -07002923 * The group ID of this media item. This can be useful to present
2924 * the user a grouping of related media items, such a burst of
2925 * images, or a {@code JPG} and {@code DNG} version of the same
2926 * image.
2927 * <p>
2928 * This is a read-only column that is automatically computed based
2929 * on the first portion of the filename. For example,
2930 * {@code IMG1024.BURST001.JPG} and {@code IMG1024.BURST002.JPG}
2931 * will have the same {@link #GROUP_ID} because the first portion of
2932 * their filenames is identical.
Jeff Sharkey7b148d72019-01-04 14:17:48 -07002933 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002934 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Jeff Sharkeyc4597792019-01-30 15:14:19 -07002935 public static final String GROUP_ID = "group_id";
Jeff Sharkey7b148d72019-01-04 14:17:48 -07002936
2937 /**
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002938 * The position within the video item at which playback should be
2939 * resumed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002940 */
Jeff Sharkey9f05e0e2019-02-18 12:47:25 -07002941 @DurationMillisLong
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002942 @Column(Cursor.FIELD_TYPE_INTEGER)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002943 public static final String BOOKMARK = "bookmark";
nobuhiko saitou839cc002018-09-03 17:26:54 +09002944
2945 /**
2946 * The standard of color aspects
nobuhiko saitou839cc002018-09-03 17:26:54 +09002947 * @hide
2948 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002949 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
nobuhiko saitou839cc002018-09-03 17:26:54 +09002950 public static final String COLOR_STANDARD = "color_standard";
2951
2952 /**
2953 * The transfer of color aspects
nobuhiko saitou839cc002018-09-03 17:26:54 +09002954 * @hide
2955 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002956 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
nobuhiko saitou839cc002018-09-03 17:26:54 +09002957 public static final String COLOR_TRANSFER = "color_transfer";
2958
2959 /**
2960 * The range of color aspects
nobuhiko saitou839cc002018-09-03 17:26:54 +09002961 * @hide
2962 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07002963 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
nobuhiko saitou839cc002018-09-03 17:26:54 +09002964 public static final String COLOR_RANGE = "color_range";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002965 }
2966
2967 public static final class Media implements VideoColumns {
2968 /**
2969 * Get the content:// style URI for the video media table on the
2970 * given volume.
2971 *
2972 * @param volumeName the name of the volume to get the URI for
2973 * @return the URI to the video media table on the given volume
2974 */
2975 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06002976 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("video")
2977 .appendPath("media").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002978 }
2979
2980 /**
2981 * The content:// style URI for the internal storage.
2982 */
2983 public static final Uri INTERNAL_CONTENT_URI =
2984 getContentUri("internal");
2985
2986 /**
2987 * The content:// style URI for the "primary" external storage
2988 * volume.
2989 */
2990 public static final Uri EXTERNAL_CONTENT_URI =
2991 getContentUri("external");
2992
2993 /**
2994 * The MIME type for this table.
2995 */
2996 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video";
2997
2998 /**
2999 * The default sort order for this table
3000 */
3001 public static final String DEFAULT_SORT_ORDER = TITLE;
3002 }
Ray Chen00c575a2009-08-28 14:12:15 -07003003
3004 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06003005 * This class provides utility methods to obtain thumbnails for various
3006 * {@link Video} items.
Jeff Sharkey36ee3622019-01-23 10:55:29 -07003007 *
3008 * @deprecated Callers should migrate to using
3009 * {@link ContentResolver#loadThumbnail}, since it offers
3010 * richer control over requested thumbnail sizes and
3011 * cancellation behavior.
Ray Chen00c575a2009-08-28 14:12:15 -07003012 */
Jeff Sharkey36ee3622019-01-23 10:55:29 -07003013 @Deprecated
Ray Chen00c575a2009-08-28 14:12:15 -07003014 public static class Thumbnails implements BaseColumns {
3015 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06003016 * Cancel any outstanding {@link #getThumbnail} requests, causing
3017 * them to return by throwing a {@link OperationCanceledException}.
3018 * <p>
3019 * This method has no effect on
3020 * {@link ContentResolver#loadThumbnail} calls, since they provide
3021 * their own {@link CancellationSignal}.
Ray Chenb9944192009-09-29 20:24:01 -07003022 *
Jeff Sharkey52827962018-10-18 14:44:41 -06003023 * @deprecated Callers should migrate to using
3024 * {@link ContentResolver#loadThumbnail}, since it
3025 * offers richer control over requested thumbnail sizes
3026 * and cancellation behavior.
Ray Chenb9944192009-09-29 20:24:01 -07003027 */
Jeff Sharkey52827962018-10-18 14:44:41 -06003028 @Deprecated
Ray Chenb9944192009-09-29 20:24:01 -07003029 public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06003030 final Uri uri = ContentUris.withAppendedId(
3031 Video.Media.EXTERNAL_CONTENT_URI, origId);
3032 InternalThumbnails.cancelThumbnail(cr, uri);
Ray Chenb9944192009-09-29 20:24:01 -07003033 }
3034
3035 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06003036 * Return thumbnail representing a specific video item. If a
3037 * thumbnail doesn't exist, this method will block until it's
3038 * generated. Callers are responsible for their own in-memory
3039 * caching of returned values.
Ray Chen00c575a2009-08-28 14:12:15 -07003040 *
Jeff Sharkey52827962018-10-18 14:44:41 -06003041 * @param videoId the video item to obtain a thumbnail for.
3042 * @param kind optimal thumbnail size desired.
3043 * @return decoded thumbnail, or {@code null} if problem was
3044 * encountered.
3045 * @deprecated Callers should migrate to using
3046 * {@link ContentResolver#loadThumbnail}, since it
3047 * offers richer control over requested thumbnail sizes
3048 * and cancellation behavior.
Ray Chen13ed5752009-10-05 12:21:24 -07003049 */
Jeff Sharkey52827962018-10-18 14:44:41 -06003050 @Deprecated
3051 public static Bitmap getThumbnail(ContentResolver cr, long videoId, int kind,
Ray Chen13ed5752009-10-05 12:21:24 -07003052 BitmapFactory.Options options) {
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06003053 final Uri uri = ContentUris.withAppendedId(
Jeff Sharkey52827962018-10-18 14:44:41 -06003054 Video.Media.EXTERNAL_CONTENT_URI, videoId);
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06003055 return InternalThumbnails.getThumbnail(cr, uri, kind, options);
Ray Chen13ed5752009-10-05 12:21:24 -07003056 }
3057
3058 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06003059 * Cancel any outstanding {@link #getThumbnail} requests, causing
3060 * them to return by throwing a {@link OperationCanceledException}.
3061 * <p>
3062 * This method has no effect on
3063 * {@link ContentResolver#loadThumbnail} calls, since they provide
3064 * their own {@link CancellationSignal}.
Ray Chen13ed5752009-10-05 12:21:24 -07003065 *
Jeff Sharkey52827962018-10-18 14:44:41 -06003066 * @deprecated Callers should migrate to using
3067 * {@link ContentResolver#loadThumbnail}, since it
3068 * offers richer control over requested thumbnail sizes
3069 * and cancellation behavior.
Ray Chen00c575a2009-08-28 14:12:15 -07003070 */
Jeff Sharkey52827962018-10-18 14:44:41 -06003071 @Deprecated
3072 public static void cancelThumbnailRequest(ContentResolver cr, long videoId,
3073 long groupId) {
3074 cancelThumbnailRequest(cr, videoId);
Ray Chen00c575a2009-08-28 14:12:15 -07003075 }
3076
3077 /**
Jeff Sharkey52827962018-10-18 14:44:41 -06003078 * Return thumbnail representing a specific video item. If a
3079 * thumbnail doesn't exist, this method will block until it's
3080 * generated. Callers are responsible for their own in-memory
3081 * caching of returned values.
Ray Chen13ed5752009-10-05 12:21:24 -07003082 *
Jeff Sharkey52827962018-10-18 14:44:41 -06003083 * @param videoId the video item to obtain a thumbnail for.
3084 * @param kind optimal thumbnail size desired.
3085 * @return decoded thumbnail, or {@code null} if problem was
3086 * encountered.
3087 * @deprecated Callers should migrate to using
3088 * {@link ContentResolver#loadThumbnail}, since it
3089 * offers richer control over requested thumbnail sizes
3090 * and cancellation behavior.
Ray Chen13ed5752009-10-05 12:21:24 -07003091 */
Jeff Sharkey52827962018-10-18 14:44:41 -06003092 @Deprecated
3093 public static Bitmap getThumbnail(ContentResolver cr, long videoId, long groupId,
3094 int kind, BitmapFactory.Options options) {
3095 return getThumbnail(cr, videoId, kind, options);
Ray Chen13ed5752009-10-05 12:21:24 -07003096 }
3097
3098 /**
Ray Chen00c575a2009-08-28 14:12:15 -07003099 * Get the content:// style URI for the image media table on the
3100 * given volume.
3101 *
3102 * @param volumeName the name of the volume to get the URI for
3103 * @return the URI to the image media table on the given volume
3104 */
3105 public static Uri getContentUri(String volumeName) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06003106 return AUTHORITY_URI.buildUpon().appendPath(volumeName).appendPath("video")
3107 .appendPath("thumbnails").build();
Ray Chen00c575a2009-08-28 14:12:15 -07003108 }
3109
3110 /**
3111 * The content:// style URI for the internal storage.
3112 */
3113 public static final Uri INTERNAL_CONTENT_URI =
3114 getContentUri("internal");
3115
3116 /**
3117 * The content:// style URI for the "primary" external storage
3118 * volume.
3119 */
3120 public static final Uri EXTERNAL_CONTENT_URI =
3121 getContentUri("external");
3122
3123 /**
3124 * The default sort order for this table
3125 */
3126 public static final String DEFAULT_SORT_ORDER = "video_id ASC";
3127
3128 /**
Jeff Sharkey60cfad82016-01-05 17:30:57 -07003129 * Path to the thumbnail file on disk.
Jeff Sharkey52827962018-10-18 14:44:41 -06003130 *
3131 * @deprecated Apps may not have filesystem permissions to directly
3132 * access this path. Instead of trying to open this path
3133 * directly, apps should use
3134 * {@link ContentResolver#openFileDescriptor(Uri, String)}
3135 * to gain access. This value will always be
3136 * {@code NULL} for apps targeting
3137 * {@link android.os.Build.VERSION_CODES#Q} or higher.
Ray Chen00c575a2009-08-28 14:12:15 -07003138 */
Jeff Sharkey52827962018-10-18 14:44:41 -06003139 @Deprecated
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07003140 @Column(Cursor.FIELD_TYPE_STRING)
Ray Chen00c575a2009-08-28 14:12:15 -07003141 public static final String DATA = "_data";
3142
3143 /**
3144 * The original image for the thumbnal
Ray Chen00c575a2009-08-28 14:12:15 -07003145 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07003146 @Column(Cursor.FIELD_TYPE_INTEGER)
Ray Chen00c575a2009-08-28 14:12:15 -07003147 public static final String VIDEO_ID = "video_id";
3148
3149 /**
3150 * The kind of the thumbnail
Ray Chen00c575a2009-08-28 14:12:15 -07003151 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07003152 @Column(Cursor.FIELD_TYPE_INTEGER)
Ray Chen00c575a2009-08-28 14:12:15 -07003153 public static final String KIND = "kind";
3154
Jeff Sharkey30b77bc2018-07-27 20:56:34 -06003155 public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
3156 public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
3157 public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
Ray Chen00c575a2009-08-28 14:12:15 -07003158
3159 /**
3160 * The width of the thumbnal
Ray Chen00c575a2009-08-28 14:12:15 -07003161 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07003162 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Ray Chen00c575a2009-08-28 14:12:15 -07003163 public static final String WIDTH = "width";
3164
3165 /**
3166 * The height of the thumbnail
Ray Chen00c575a2009-08-28 14:12:15 -07003167 */
Jeff Sharkey9c41bda2019-02-14 15:29:37 -07003168 @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
Ray Chen00c575a2009-08-28 14:12:15 -07003169 public static final String HEIGHT = "height";
3170 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003171 }
3172
3173 /**
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06003174 * Return list of all volume names currently available. This includes a
3175 * unique name for each shared storage device that is currently mounted.
3176 * <p>
3177 * Each name can be passed to APIs like
3178 * {@link MediaStore.Images.Media#getContentUri(String)} to query media at
3179 * that location.
3180 */
Jeff Sharkeya30e5c32019-02-28 12:02:10 -07003181 public static @NonNull Set<String> getAllVolumeNames(@NonNull Context context) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06003182 final StorageManager sm = context.getSystemService(StorageManager.class);
3183 final Set<String> volumeNames = new ArraySet<>();
3184 volumeNames.add(VOLUME_INTERNAL);
3185 for (VolumeInfo vi : sm.getVolumes()) {
Jeff Sharkey30360892019-01-20 13:14:21 -07003186 if (vi.isVisibleForUser(UserHandle.myUserId()) && vi.isMountedReadable()) {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06003187 if (vi.isPrimary()) {
3188 volumeNames.add(VOLUME_EXTERNAL);
3189 } else {
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003190 volumeNames.add(vi.getNormalizedFsUuid());
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06003191 }
3192 }
3193 }
3194 return volumeNames;
3195 }
3196
3197 /**
Jeff Sharkeybc2ae002018-07-31 10:45:37 -06003198 * Return the volume name that the given {@link Uri} references.
3199 */
3200 public static @NonNull String getVolumeName(@NonNull Uri uri) {
3201 final List<String> segments = uri.getPathSegments();
3202 if (uri.getAuthority().equals(AUTHORITY) && segments != null && segments.size() > 0) {
3203 return segments.get(0);
3204 } else {
Jeff Sharkey8559e652019-01-20 11:21:59 -07003205 throw new IllegalArgumentException("Missing volume name: " + uri);
Jeff Sharkeybc2ae002018-07-31 10:45:37 -06003206 }
3207 }
3208
Jeff Sharkeyc8e49242018-11-02 14:34:44 -06003209 /** {@hide} */
Jeff Sharkey8559e652019-01-20 11:21:59 -07003210 public static @NonNull String checkArgumentVolumeName(@NonNull String volumeName) {
3211 if (TextUtils.isEmpty(volumeName)) {
3212 throw new IllegalArgumentException();
3213 }
3214
3215 if (VOLUME_INTERNAL.equals(volumeName)) {
3216 return volumeName;
3217 } else if (VOLUME_EXTERNAL.equals(volumeName)) {
3218 return volumeName;
3219 }
3220
3221 // When not one of the well-known values above, it must be a hex UUID
3222 for (int i = 0; i < volumeName.length(); i++) {
3223 final char c = volumeName.charAt(i);
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003224 if (('a' <= c && c <= 'f') || ('0' <= c && c <= '9') || (c == '-')) {
Jeff Sharkey8559e652019-01-20 11:21:59 -07003225 continue;
3226 } else {
3227 throw new IllegalArgumentException("Invalid volume name: " + volumeName);
3228 }
3229 }
3230 return volumeName;
3231 }
3232
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003233 /**
3234 * Return path where the given volume is mounted. Not valid for
3235 * {@link #VOLUME_INTERNAL}.
3236 *
3237 * @hide
3238 */
Jeff Sharkeycb269aac2019-01-25 11:15:38 -07003239 @TestApi
Jeff Sharkeyc8e49242018-11-02 14:34:44 -06003240 public static @NonNull File getVolumePath(@NonNull String volumeName)
3241 throws FileNotFoundException {
Jeff Sharkey8559e652019-01-20 11:21:59 -07003242 if (TextUtils.isEmpty(volumeName)) {
3243 throw new IllegalArgumentException();
3244 }
Jeff Sharkeyc8e49242018-11-02 14:34:44 -06003245
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003246 if (VOLUME_EXTERNAL.equals(volumeName)) {
Jeff Sharkeyc8e49242018-11-02 14:34:44 -06003247 return Environment.getExternalStorageDirectory();
3248 }
3249
3250 final StorageManager sm = AppGlobals.getInitialApplication()
3251 .getSystemService(StorageManager.class);
3252 for (VolumeInfo vi : sm.getVolumes()) {
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003253 if (Objects.equals(vi.getNormalizedFsUuid(), volumeName)) {
Jeff Sharkeyc8e49242018-11-02 14:34:44 -06003254 final File path = vi.getPathForUser(UserHandle.myUserId());
Jeff Sharkeyf81494a2019-01-19 13:26:07 -07003255 if (path != null) {
3256 return path;
3257 } else {
Jeff Sharkeyc8e49242018-11-02 14:34:44 -06003258 throw new FileNotFoundException("Failed to find path for " + vi);
3259 }
3260 }
3261 }
3262 throw new FileNotFoundException("Failed to find path for " + volumeName);
3263 }
3264
Jeff Sharkeybc2ae002018-07-31 10:45:37 -06003265 /**
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003266 * Return paths that should be scanned for the given volume.
3267 *
3268 * @hide
3269 */
Jeff Sharkeycb269aac2019-01-25 11:15:38 -07003270 @TestApi
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003271 public static @NonNull Collection<File> getVolumeScanPaths(@NonNull String volumeName)
3272 throws FileNotFoundException {
3273 if (TextUtils.isEmpty(volumeName)) {
3274 throw new IllegalArgumentException();
3275 }
3276
3277 final ArrayList<File> res = new ArrayList<>();
3278 if (VOLUME_INTERNAL.equals(volumeName)) {
Jeff Sharkey858b3112019-02-08 14:38:59 -07003279 addCanoncialFile(res, new File(Environment.getRootDirectory(), "media"));
3280 addCanoncialFile(res, new File(Environment.getOemDirectory(), "media"));
3281 addCanoncialFile(res, new File(Environment.getProductDirectory(), "media"));
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003282 } else {
Jeff Sharkey858b3112019-02-08 14:38:59 -07003283 addCanoncialFile(res, getVolumePath(volumeName));
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003284 final UserManager um = AppGlobals.getInitialApplication()
3285 .getSystemService(UserManager.class);
3286 if (VOLUME_EXTERNAL.equals(volumeName) && um.isDemoUser()) {
Jeff Sharkey858b3112019-02-08 14:38:59 -07003287 addCanoncialFile(res, Environment.getDataPreloadsMediaDirectory());
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003288 }
3289 }
3290 return res;
3291 }
3292
Jeff Sharkey858b3112019-02-08 14:38:59 -07003293 private static void addCanoncialFile(List<File> list, File file) {
3294 try {
3295 list.add(file.getCanonicalFile());
3296 } catch (IOException e) {
3297 Log.w(TAG, "Failed to resolve " + file + ": " + e);
3298 list.add(file);
3299 }
3300 }
3301
Jeff Sharkey3f64ec52019-01-22 13:19:40 -07003302 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003303 * Uri for querying the state of the media scanner.
3304 */
3305 public static Uri getMediaScannerUri() {
Jeff Sharkey0fc74ab2018-10-30 19:16:29 -06003306 return AUTHORITY_URI.buildUpon().appendPath("none").appendPath("media_scanner").build();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003307 }
3308
3309 /**
3310 * Name of current volume being scanned by the media scanner.
3311 */
3312 public static final String MEDIA_SCANNER_VOLUME = "volume";
Karl Ostmo8ce072d2010-01-30 15:15:39 -06003313
3314 /**
3315 * Name of the file signaling the media scanner to ignore media in the containing directory
3316 * and its subdirectories. Developers should use this to avoid application graphics showing
3317 * up in the Gallery and likewise prevent application sounds and music from showing up in
3318 * the Music app.
3319 */
3320 public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
Marco Nelissen3822f732011-02-03 10:59:30 -08003321
3322 /**
Jeff Sharkey1b404be2019-03-01 11:31:30 -07003323 * Return an opaque version string describing the {@link MediaStore} state.
3324 * <p>
3325 * Applications that import data from {@link MediaStore} into their own
3326 * caches can use this to detect that {@link MediaStore} has undergone
3327 * substantial changes, and that data should be rescanned.
3328 * <p>
3329 * No other assumptions should be made about the meaning of the version.
3330 * <p>
3331 * This method returns the version for {@link MediaStore#VOLUME_EXTERNAL};
3332 * to obtain a version for a different volume, use
3333 * {@link #getVersion(Context, String)}.
Marco Nelissen3822f732011-02-03 10:59:30 -08003334 */
Jeff Sharkey1b404be2019-03-01 11:31:30 -07003335 public static @NonNull String getVersion(@NonNull Context context) {
3336 return getVersion(context, VOLUME_EXTERNAL);
3337 }
3338
3339 /**
3340 * Return an opaque version string describing the {@link MediaStore} state.
3341 * <p>
3342 * Applications that import data from {@link MediaStore} into their own
3343 * caches can use this to detect that {@link MediaStore} has undergone
3344 * substantial changes, and that data should be rescanned.
3345 * <p>
3346 * No other assumptions should be made about the meaning of the version.
3347 */
3348 public static @NonNull String getVersion(@NonNull Context context, @NonNull String volumeName) {
3349 final ContentResolver resolver = context.getContentResolver();
3350 try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
3351 final Bundle in = new Bundle();
3352 in.putString(Intent.EXTRA_TEXT, volumeName);
3353 final Bundle out = client.call(GET_VERSION_CALL, null, in);
3354 return out.getString(Intent.EXTRA_TEXT);
3355 } catch (RemoteException e) {
3356 throw e.rethrowAsRuntimeException();
Marco Nelissen3822f732011-02-03 10:59:30 -08003357 }
Marco Nelissen3822f732011-02-03 10:59:30 -08003358 }
Garfield Tan92b96ba2016-11-01 14:33:48 -07003359
3360 /**
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003361 * Return a {@link DocumentsProvider} Uri that is an equivalent to the given
3362 * {@link MediaStore} Uri.
Garfield Tan92b96ba2016-11-01 14:33:48 -07003363 * <p>
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003364 * This allows apps with Storage Access Framework permissions to convert
3365 * between {@link MediaStore} and {@link DocumentsProvider} Uris that refer
3366 * to the same underlying item. Note that this method doesn't grant any new
3367 * permissions; callers must already hold permissions obtained with
3368 * {@link Intent#ACTION_OPEN_DOCUMENT} or related APIs.
Garfield Tan92b96ba2016-11-01 14:33:48 -07003369 *
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003370 * @param mediaUri The {@link MediaStore} Uri to convert.
3371 * @return An equivalent {@link DocumentsProvider} Uri. Returns {@code null}
3372 * if no equivalent was found.
3373 * @see #getMediaUri(Context, Uri)
Garfield Tan92b96ba2016-11-01 14:33:48 -07003374 */
Jeff Sharkeya30e5c32019-02-28 12:02:10 -07003375 public static @Nullable Uri getDocumentUri(@NonNull Context context, @NonNull Uri mediaUri) {
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003376 final ContentResolver resolver = context.getContentResolver();
3377 final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions();
Garfield Tan92b96ba2016-11-01 14:33:48 -07003378
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003379 try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
3380 final Bundle in = new Bundle();
3381 in.putParcelable(DocumentsContract.EXTRA_URI, mediaUri);
3382 in.putParcelableList(DocumentsContract.EXTRA_URI_PERMISSIONS, uriPermissions);
3383 final Bundle out = client.call(GET_DOCUMENT_URI_CALL, null, in);
3384 return out.getParcelable(DocumentsContract.EXTRA_URI);
Garfield Tan92b96ba2016-11-01 14:33:48 -07003385 } catch (RemoteException e) {
3386 throw e.rethrowAsRuntimeException();
3387 }
3388 }
3389
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003390 /**
3391 * Return a {@link MediaStore} Uri that is an equivalent to the given
3392 * {@link DocumentsProvider} Uri.
3393 * <p>
3394 * This allows apps with Storage Access Framework permissions to convert
3395 * between {@link MediaStore} and {@link DocumentsProvider} Uris that refer
3396 * to the same underlying item. Note that this method doesn't grant any new
3397 * permissions; callers must already hold permissions obtained with
3398 * {@link Intent#ACTION_OPEN_DOCUMENT} or related APIs.
3399 *
3400 * @param documentUri The {@link DocumentsProvider} Uri to convert.
3401 * @return An equivalent {@link MediaStore} Uri. Returns {@code null} if no
3402 * equivalent was found.
3403 * @see #getDocumentUri(Context, Uri)
3404 */
Jeff Sharkeya30e5c32019-02-28 12:02:10 -07003405 public static @Nullable Uri getMediaUri(@NonNull Context context, @NonNull Uri documentUri) {
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003406 final ContentResolver resolver = context.getContentResolver();
3407 final List<UriPermission> uriPermissions = resolver.getPersistedUriPermissions();
Garfield Tan92b96ba2016-11-01 14:33:48 -07003408
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003409 try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
Garfield Tan92b96ba2016-11-01 14:33:48 -07003410 final Bundle in = new Bundle();
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003411 in.putParcelable(DocumentsContract.EXTRA_URI, documentUri);
3412 in.putParcelableList(DocumentsContract.EXTRA_URI_PERMISSIONS, uriPermissions);
3413 final Bundle out = client.call(GET_MEDIA_URI_CALL, null, in);
Garfield Tan92b96ba2016-11-01 14:33:48 -07003414 return out.getParcelable(DocumentsContract.EXTRA_URI);
Jeff Sharkey643e99e2018-10-22 18:01:27 -06003415 } catch (RemoteException e) {
3416 throw e.rethrowAsRuntimeException();
Garfield Tan92b96ba2016-11-01 14:33:48 -07003417 }
3418 }
Jeff Sharkey9efa7b82018-12-07 17:15:36 -07003419
3420 /**
3421 * Calculate size of media contributed by given package under the calling
3422 * user. The meaning of "contributed" means it won't automatically be
3423 * deleted when the app is uninstalled.
3424 *
3425 * @hide
3426 */
Jeff Sharkey9efa7b82018-12-07 17:15:36 -07003427 @TestApi
3428 @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
Jeff Sharkeydc50d4c2018-12-10 18:28:56 -07003429 public static @BytesLong long getContributedMediaSize(Context context, String packageName,
3430 UserHandle user) throws IOException {
3431 final UserManager um = context.getSystemService(UserManager.class);
3432 if (um.isUserUnlocked(user) && um.isUserRunning(user)) {
3433 try {
3434 final ContentResolver resolver = context
3435 .createPackageContextAsUser(packageName, 0, user).getContentResolver();
3436 final Bundle in = new Bundle();
3437 in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
3438 final Bundle out = resolver.call(AUTHORITY, GET_CONTRIBUTED_MEDIA_CALL, null, in);
3439 return out.getLong(Intent.EXTRA_INDEX);
3440 } catch (Exception e) {
3441 throw new IOException(e);
3442 }
3443 } else {
3444 throw new IOException("User " + user + " must be unlocked and running");
Jeff Sharkey9efa7b82018-12-07 17:15:36 -07003445 }
3446 }
3447
3448 /**
3449 * Delete all media contributed by given package under the calling user. The
3450 * meaning of "contributed" means it won't automatically be deleted when the
3451 * app is uninstalled.
3452 *
3453 * @hide
3454 */
Jeff Sharkey9efa7b82018-12-07 17:15:36 -07003455 @TestApi
3456 @RequiresPermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
Jeff Sharkeydc50d4c2018-12-10 18:28:56 -07003457 public static void deleteContributedMedia(Context context, String packageName,
3458 UserHandle user) throws IOException {
3459 final UserManager um = context.getSystemService(UserManager.class);
3460 if (um.isUserUnlocked(user) && um.isUserRunning(user)) {
3461 try {
3462 final ContentResolver resolver = context
3463 .createPackageContextAsUser(packageName, 0, user).getContentResolver();
3464 final Bundle in = new Bundle();
3465 in.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
3466 resolver.call(AUTHORITY, DELETE_CONTRIBUTED_MEDIA_CALL, null, in);
3467 } catch (Exception e) {
3468 throw new IOException(e);
3469 }
3470 } else {
3471 throw new IOException("User " + user + " must be unlocked and running");
Jeff Sharkey9efa7b82018-12-07 17:15:36 -07003472 }
3473 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003474}