blob: d20e89dcae1b378dab6b7c0d950a5cb80978caa1 [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
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.content.ContentResolver;
22import android.content.ContentValues;
23import android.content.ContentUris;
24import android.database.Cursor;
25import android.database.DatabaseUtils;
Ray Chen00c575a2009-08-28 14:12:15 -070026import android.database.sqlite.SQLiteException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.graphics.Bitmap;
28import android.graphics.BitmapFactory;
29import android.graphics.Matrix;
Ray Chen00c575a2009-08-28 14:12:15 -070030import android.media.MiniThumbFile;
Ray Chenbf124e72010-01-26 14:43:35 -080031import android.media.ThumbnailUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.net.Uri;
33import android.os.Environment;
Ray Chen00c575a2009-08-28 14:12:15 -070034import android.os.ParcelFileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.util.Log;
36
37import java.io.FileInputStream;
38import java.io.FileNotFoundException;
39import java.io.IOException;
40import java.io.InputStream;
41import java.io.OutputStream;
42import java.io.UnsupportedEncodingException;
43import java.text.Collator;
44
45/**
46 * The Media provider contains meta data for all available media on both internal
47 * and external storage devices.
48 */
Ray Chen00c575a2009-08-28 14:12:15 -070049public final class MediaStore {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 private final static String TAG = "MediaStore";
51
52 public static final String AUTHORITY = "media";
53
54 private static final String CONTENT_AUTHORITY_SLASH = "content://" + AUTHORITY + "/";
55
56 /**
Daniel Sandleredcdbb62010-02-18 16:00:43 -050057 * Activity Action: Launch a music player.
58 * The activity should be able to play, browse, or manipulate music files stored on the device.
59 */
60 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
61 public static final String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER";
62
63 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 * Activity Action: Perform a search for media.
65 * Contains at least the {@link android.app.SearchManager#QUERY} extra.
66 * May also contain any combination of the following extras:
67 * EXTRA_MEDIA_ARTIST, EXTRA_MEDIA_ALBUM, EXTRA_MEDIA_TITLE, EXTRA_MEDIA_FOCUS
68 *
69 * @see android.provider.MediaStore#EXTRA_MEDIA_ARTIST
70 * @see android.provider.MediaStore#EXTRA_MEDIA_ALBUM
71 * @see android.provider.MediaStore#EXTRA_MEDIA_TITLE
72 * @see android.provider.MediaStore#EXTRA_MEDIA_FOCUS
73 */
74 @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
75 public static final String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH";
76
77 /**
78 * The name of the Intent-extra used to define the artist
79 */
80 public static final String EXTRA_MEDIA_ARTIST = "android.intent.extra.artist";
81 /**
82 * The name of the Intent-extra used to define the album
83 */
84 public static final String EXTRA_MEDIA_ALBUM = "android.intent.extra.album";
85 /**
86 * The name of the Intent-extra used to define the song title
87 */
88 public static final String EXTRA_MEDIA_TITLE = "android.intent.extra.title";
89 /**
90 * The name of the Intent-extra used to define the search focus. The search focus
91 * indicates whether the search should be for things related to the artist, album
92 * or song that is identified by the other extras.
93 */
94 public static final String EXTRA_MEDIA_FOCUS = "android.intent.extra.focus";
95
96 /**
97 * The name of the Intent-extra used to control the orientation of a ViewImage or a MovieView.
98 * This is an int property that overrides the activity's requestedOrientation.
99 * @see android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
100 */
101 public static final String EXTRA_SCREEN_ORIENTATION = "android.intent.extra.screenOrientation";
102
103 /**
104 * The name of an Intent-extra used to control the UI of a ViewImage.
105 * This is a boolean property that overrides the activity's default fullscreen state.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 */
107 public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen";
108
109 /**
110 * The name of an Intent-extra used to control the UI of a ViewImage.
111 * This is a boolean property that specifies whether or not to show action icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 */
113 public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons";
114
115 /**
116 * The name of the Intent-extra used to control the onCompletion behavior of a MovieView.
117 * This is a boolean property that specifies whether or not to finish the MovieView activity
118 * when the movie completes playing. The default value is true, which means to automatically
119 * exit the movie player activity when the movie completes playing.
120 */
121 public static final String EXTRA_FINISH_ON_COMPLETION = "android.intent.extra.finishOnCompletion";
122
123 /**
124 * The name of the Intent action used to launch a camera in still image mode.
125 */
126 public static final String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA";
127
128 /**
129 * The name of the Intent action used to launch a camera in video mode.
130 */
131 public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
132
133 /**
134 * Standard Intent action that can be sent to have the camera application
135 * capture an image and return it.
136 * <p>
137 * The caller may pass an extra EXTRA_OUTPUT to control where this image will be written.
138 * If the EXTRA_OUTPUT is not present, then a small sized image is returned as a Bitmap
139 * object in the extra field. This is useful for applications that only need a small image.
140 * If the EXTRA_OUTPUT is present, then the full-sized image will be written to the Uri
141 * value of EXTRA_OUTPUT.
142 * @see #EXTRA_OUTPUT
143 * @see #EXTRA_VIDEO_QUALITY
144 */
145 public final static String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
146
147 /**
148 * Standard Intent action that can be sent to have the camera application
149 * capture an video and return it.
150 * <p>
151 * The caller may pass in an extra EXTRA_VIDEO_QUALITY to control the video quality.
152 * <p>
153 * The caller may pass in an extra EXTRA_OUTPUT to control
154 * where the video is written. If EXTRA_OUTPUT is not present the video will be
155 * written to the standard location for videos, and the Uri of that location will be
156 * returned in the data field of the Uri.
157 * @see #EXTRA_OUTPUT
158 */
159 public final static String ACTION_VIDEO_CAPTURE = "android.media.action.VIDEO_CAPTURE";
160
161 /**
162 * The name of the Intent-extra used to control the quality of a recorded video. This is an
163 * integer property. Currently value 0 means low quality, suitable for MMS messages, and
164 * value 1 means high quality. In the future other quality levels may be added.
165 */
166 public final static String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality";
167
168 /**
169 * Specify the maximum allowed size.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 */
171 public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit";
172
173 /**
Chih-Chung Changeb0098d2009-08-25 12:59:54 +0800174 * Specify the maximum allowed recording duration in seconds.
Chih-Chung Changeb0098d2009-08-25 12:59:54 +0800175 */
176 public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit";
177
178 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 * The name of the Intent-extra used to indicate a content resolver Uri to be used to
180 * store the requested image or video.
181 */
182 public final static String EXTRA_OUTPUT = "output";
183
184 /**
Marco Nelissened297a82010-01-04 13:24:31 -0800185 * The string that is used when a media attribute is not known. For example,
186 * if an audio file does not have any meta data, the artist and album columns
187 * will be set to this value.
Marco Nelissened297a82010-01-04 13:24:31 -0800188 */
189 public static final String UNKNOWN_STRING = "<unknown>";
190
191 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 * Common fields for most MediaProvider tables
193 */
194
Ray Chen00c575a2009-08-28 14:12:15 -0700195 public interface MediaColumns extends BaseColumns {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 /**
197 * The data stream for the file
198 * <P>Type: DATA STREAM</P>
199 */
200 public static final String DATA = "_data";
201
202 /**
203 * The size of the file in bytes
204 * <P>Type: INTEGER (long)</P>
205 */
206 public static final String SIZE = "_size";
207
208 /**
209 * The display name of the file
210 * <P>Type: TEXT</P>
211 */
212 public static final String DISPLAY_NAME = "_display_name";
213
214 /**
215 * The title of the content
216 * <P>Type: TEXT</P>
217 */
218 public static final String TITLE = "title";
219
220 /**
221 * The time the file was added to the media provider
222 * Units are seconds since 1970.
223 * <P>Type: INTEGER (long)</P>
224 */
225 public static final String DATE_ADDED = "date_added";
226
227 /**
228 * The time the file was last modified
229 * Units are seconds since 1970.
230 * NOTE: This is for internal use by the media scanner. Do not modify this field.
231 * <P>Type: INTEGER (long)</P>
232 */
233 public static final String DATE_MODIFIED = "date_modified";
234
235 /**
236 * The MIME type of the file
237 * <P>Type: TEXT</P>
238 */
239 public static final String MIME_TYPE = "mime_type";
240 }
241
242 /**
Ray Chen00c575a2009-08-28 14:12:15 -0700243 * This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
244 * to be accessed elsewhere.
245 */
246 private static class InternalThumbnails implements BaseColumns {
247 private static final int MINI_KIND = 1;
248 private static final int FULL_SCREEN_KIND = 2;
249 private static final int MICRO_KIND = 3;
250 private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
Ray Chen13ed5752009-10-05 12:21:24 -0700251 static final int DEFAULT_GROUP_ID = 0;
Chih-Chung Chang2ca36192010-08-24 19:25:56 +0800252 private static final Object sThumbBufLock = new Object();
253 private static byte[] sThumbBuf;
Ray Chen00c575a2009-08-28 14:12:15 -0700254
Ray Chenf9a243d2009-10-29 18:15:29 -0700255 private static Bitmap getMiniThumbFromFile(Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
256 Bitmap bitmap = null;
257 Uri thumbUri = null;
258 try {
259 long thumbId = c.getLong(0);
260 String filePath = c.getString(1);
261 thumbUri = ContentUris.withAppendedId(baseUri, thumbId);
262 ParcelFileDescriptor pfdInput = cr.openFileDescriptor(thumbUri, "r");
263 bitmap = BitmapFactory.decodeFileDescriptor(
264 pfdInput.getFileDescriptor(), null, options);
265 pfdInput.close();
266 } catch (FileNotFoundException ex) {
267 Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
268 } catch (IOException ex) {
269 Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
270 } catch (OutOfMemoryError ex) {
271 Log.e(TAG, "failed to allocate memory for thumbnail "
272 + thumbUri + "; " + ex);
273 }
274 return bitmap;
275 }
276
Ray Chen00c575a2009-08-28 14:12:15 -0700277 /**
Ray Chenb9944192009-09-29 20:24:01 -0700278 * This method cancels the thumbnail request so clients waiting for getThumbnail will be
279 * interrupted and return immediately. Only the original process which made the getThumbnail
280 * requests can cancel their own requests.
281 *
282 * @param cr ContentResolver
Ray Chenef093cd2009-09-29 19:38:58 -0700283 * @param origId original image or video id. use -1 to cancel all requests.
Ray Chen13ed5752009-10-05 12:21:24 -0700284 * @param groupId the same groupId used in getThumbnail
Ray Chenb9944192009-09-29 20:24:01 -0700285 * @param baseUri the base URI of requested thumbnails
286 */
Ray Chen13ed5752009-10-05 12:21:24 -0700287 static void cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri,
288 long groupId) {
Ray Chenb9944192009-09-29 20:24:01 -0700289 Uri cancelUri = baseUri.buildUpon().appendQueryParameter("cancel", "1")
Ray Chen13ed5752009-10-05 12:21:24 -0700290 .appendQueryParameter("orig_id", String.valueOf(origId))
291 .appendQueryParameter("group_id", String.valueOf(groupId)).build();
Ray Chenb9944192009-09-29 20:24:01 -0700292 Cursor c = null;
293 try {
294 c = cr.query(cancelUri, PROJECTION, null, null, null);
295 }
296 finally {
297 if (c != null) c.close();
298 }
299 }
300 /**
Ray Chen00c575a2009-08-28 14:12:15 -0700301 * This method ensure thumbnails associated with origId are generated and decode the byte
302 * stream from database (MICRO_KIND) or file (MINI_KIND).
303 *
304 * Special optimization has been done to avoid further IPC communication for MICRO_KIND
305 * thumbnails.
306 *
307 * @param cr ContentResolver
308 * @param origId original image or video id
309 * @param kind could be MINI_KIND or MICRO_KIND
310 * @param options this is only used for MINI_KIND when decoding the Bitmap
311 * @param baseUri the base URI of requested thumbnails
Ray Chen13ed5752009-10-05 12:21:24 -0700312 * @param groupId the id of group to which this request belongs
Ray Chen00c575a2009-08-28 14:12:15 -0700313 * @return Bitmap bitmap of specified thumbnail kind
314 */
Ray Chen13ed5752009-10-05 12:21:24 -0700315 static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, int kind,
Ray Chen00c575a2009-08-28 14:12:15 -0700316 BitmapFactory.Options options, Uri baseUri, boolean isVideo) {
317 Bitmap bitmap = null;
318 String filePath = null;
Ray Chenb9944192009-09-29 20:24:01 -0700319 // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo);
Ray Chenf9a243d2009-10-29 18:15:29 -0700320 // If the magic is non-zero, we simply return thumbnail if it does exist.
Ray Chen00c575a2009-08-28 14:12:15 -0700321 // querying MediaProvider and simply return thumbnail.
Ray Chenf9a243d2009-10-29 18:15:29 -0700322 MiniThumbFile thumbFile = MiniThumbFile.instance(baseUri);
323 long magic = thumbFile.getMagic(origId);
324 if (magic != 0) {
325 if (kind == MICRO_KIND) {
Chih-Chung Chang2ca36192010-08-24 19:25:56 +0800326 synchronized (sThumbBufLock) {
327 if (sThumbBuf == null) {
328 sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
329 }
330 if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
331 bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
332 if (bitmap == null) {
333 Log.w(TAG, "couldn't decode byte array.");
334 }
Ray Chen00c575a2009-08-28 14:12:15 -0700335 }
336 }
337 return bitmap;
Ray Chenf9a243d2009-10-29 18:15:29 -0700338 } else if (kind == MINI_KIND) {
339 String column = isVideo ? "video_id=" : "image_id=";
340 Cursor c = null;
341 try {
342 c = cr.query(baseUri, PROJECTION, column + origId, null, null);
343 if (c != null && c.moveToFirst()) {
344 bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
345 if (bitmap != null) {
346 return bitmap;
347 }
348 }
349 } finally {
350 if (c != null) c.close();
351 }
Ray Chen00c575a2009-08-28 14:12:15 -0700352 }
353 }
354
355 Cursor c = null;
356 try {
357 Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
Ray Chen13ed5752009-10-05 12:21:24 -0700358 .appendQueryParameter("orig_id", String.valueOf(origId))
359 .appendQueryParameter("group_id", String.valueOf(groupId)).build();
Ray Chen00c575a2009-08-28 14:12:15 -0700360 c = cr.query(blockingUri, PROJECTION, null, null, null);
361 // This happens when original image/video doesn't exist.
362 if (c == null) return null;
363
364 // Assuming thumbnail has been generated, at least original image exists.
365 if (kind == MICRO_KIND) {
Chih-Chung Chang2ca36192010-08-24 19:25:56 +0800366 synchronized (sThumbBufLock) {
367 if (sThumbBuf == null) {
368 sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
369 }
370 if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
371 bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
372 if (bitmap == null) {
373 Log.w(TAG, "couldn't decode byte array.");
374 }
Ray Chen00c575a2009-08-28 14:12:15 -0700375 }
376 }
377 } else if (kind == MINI_KIND) {
378 if (c.moveToFirst()) {
Ray Chenf9a243d2009-10-29 18:15:29 -0700379 bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
Ray Chen00c575a2009-08-28 14:12:15 -0700380 }
381 } else {
382 throw new IllegalArgumentException("Unsupported kind: " + kind);
383 }
384
385 // We probably run out of space, so create the thumbnail in memory.
386 if (bitmap == null) {
Ray Chen44dcf652010-04-05 11:26:40 -0700387 Log.v(TAG, "Create the thumbnail in memory: origId=" + origId
388 + ", kind=" + kind + ", isVideo="+isVideo);
Ray Chen00c575a2009-08-28 14:12:15 -0700389 Uri uri = Uri.parse(
390 baseUri.buildUpon().appendPath(String.valueOf(origId))
391 .toString().replaceFirst("thumbnails", "media"));
Ray Chenef093cd2009-09-29 19:38:58 -0700392 if (filePath == null) {
Marco Nelissen9b150b72009-09-30 16:39:15 -0700393 if (c != null) c.close();
Ray Chen00c575a2009-08-28 14:12:15 -0700394 c = cr.query(uri, PROJECTION, null, null, null);
Ray Chenef093cd2009-09-29 19:38:58 -0700395 if (c == null || !c.moveToFirst()) {
396 return null;
397 }
398 filePath = c.getString(1);
399 }
400 if (isVideo) {
Ray Chen44dcf652010-04-05 11:26:40 -0700401 bitmap = ThumbnailUtils.createVideoThumbnail(filePath, kind);
Ray Chen00c575a2009-08-28 14:12:15 -0700402 } else {
Ray Chen44dcf652010-04-05 11:26:40 -0700403 bitmap = ThumbnailUtils.createImageThumbnail(filePath, kind);
Ray Chen00c575a2009-08-28 14:12:15 -0700404 }
405 }
406 } catch (SQLiteException ex) {
407 Log.w(TAG, ex);
408 } finally {
409 if (c != null) c.close();
410 }
411 return bitmap;
412 }
413 }
414
415 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 * Contains meta data for all available images.
417 */
Ray Chen00c575a2009-08-28 14:12:15 -0700418 public static final class Images {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 public interface ImageColumns extends MediaColumns {
420 /**
421 * The description of the image
422 * <P>Type: TEXT</P>
423 */
424 public static final String DESCRIPTION = "description";
425
426 /**
427 * The picasa id of the image
428 * <P>Type: TEXT</P>
429 */
430 public static final String PICASA_ID = "picasa_id";
431
432 /**
433 * Whether the video should be published as public or private
434 * <P>Type: INTEGER</P>
435 */
436 public static final String IS_PRIVATE = "isprivate";
437
438 /**
439 * The latitude where the image was captured.
440 * <P>Type: DOUBLE</P>
441 */
442 public static final String LATITUDE = "latitude";
443
444 /**
445 * The longitude where the image was captured.
446 * <P>Type: DOUBLE</P>
447 */
448 public static final String LONGITUDE = "longitude";
449
450 /**
451 * The date & time that the image was taken in units
452 * of milliseconds since jan 1, 1970.
453 * <P>Type: INTEGER</P>
454 */
455 public static final String DATE_TAKEN = "datetaken";
456
457 /**
458 * The orientation for the image expressed as degrees.
459 * Only degrees 0, 90, 180, 270 will work.
460 * <P>Type: INTEGER</P>
461 */
462 public static final String ORIENTATION = "orientation";
463
464 /**
465 * The mini thumb id.
466 * <P>Type: INTEGER</P>
467 */
468 public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
469
470 /**
471 * The bucket id of the image. This is a read-only property that
472 * is automatically computed from the DATA column.
473 * <P>Type: TEXT</P>
474 */
475 public static final String BUCKET_ID = "bucket_id";
476
477 /**
478 * The bucket display name of the image. This is a read-only property that
479 * is automatically computed from the DATA column.
480 * <P>Type: TEXT</P>
481 */
482 public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
483 }
484
485 public static final class Media implements ImageColumns {
Ray Chen00c575a2009-08-28 14:12:15 -0700486 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
488 }
489
490 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
Ray Chen00c575a2009-08-28 14:12:15 -0700491 String where, String orderBy) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 return cr.query(uri, projection, where,
493 null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
494 }
495
496 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection,
Ray Chen00c575a2009-08-28 14:12:15 -0700497 String selection, String [] selectionArgs, String orderBy) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 return cr.query(uri, projection, selection,
499 selectionArgs, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
500 }
501
502 /**
503 * Retrieves an image for the given url as a {@link Bitmap}.
504 *
505 * @param cr The content resolver to use
506 * @param url The url of the image
507 * @throws FileNotFoundException
508 * @throws IOException
509 */
510 public static final Bitmap getBitmap(ContentResolver cr, Uri url)
Ray Chen00c575a2009-08-28 14:12:15 -0700511 throws FileNotFoundException, IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 InputStream input = cr.openInputStream(url);
513 Bitmap bitmap = BitmapFactory.decodeStream(input);
514 input.close();
515 return bitmap;
516 }
517
518 /**
519 * Insert an image and create a thumbnail for it.
520 *
521 * @param cr The content resolver to use
522 * @param imagePath The path to the image to insert
523 * @param name The name of the image
524 * @param description The description of the image
525 * @return The URL to the newly created image
526 * @throws FileNotFoundException
527 */
Ray Chen00c575a2009-08-28 14:12:15 -0700528 public static final String insertImage(ContentResolver cr, String imagePath,
529 String name, String description) throws FileNotFoundException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 // Check if file exists with a FileInputStream
531 FileInputStream stream = new FileInputStream(imagePath);
532 try {
Marco Nelissen2f189fa2009-06-30 10:32:00 -0700533 Bitmap bm = BitmapFactory.decodeFile(imagePath);
534 String ret = insertImage(cr, bm, name, description);
535 bm.recycle();
536 return ret;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 } finally {
538 try {
539 stream.close();
540 } catch (IOException e) {
541 }
542 }
543 }
544
545 private static final Bitmap StoreThumbnail(
546 ContentResolver cr,
547 Bitmap source,
548 long id,
549 float width, float height,
550 int kind) {
551 // create the matrix to scale it
552 Matrix matrix = new Matrix();
553
554 float scaleX = width / source.getWidth();
555 float scaleY = height / source.getHeight();
556
557 matrix.setScale(scaleX, scaleY);
558
559 Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
560 source.getWidth(),
561 source.getHeight(), matrix,
562 true);
563
564 ContentValues values = new ContentValues(4);
565 values.put(Images.Thumbnails.KIND, kind);
566 values.put(Images.Thumbnails.IMAGE_ID, (int)id);
567 values.put(Images.Thumbnails.HEIGHT, thumb.getHeight());
568 values.put(Images.Thumbnails.WIDTH, thumb.getWidth());
569
570 Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
571
572 try {
573 OutputStream thumbOut = cr.openOutputStream(url);
574
575 thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
576 thumbOut.close();
577 return thumb;
578 }
579 catch (FileNotFoundException ex) {
580 return null;
581 }
582 catch (IOException ex) {
583 return null;
584 }
585 }
586
587 /**
588 * Insert an image and create a thumbnail for it.
589 *
590 * @param cr The content resolver to use
591 * @param source The stream to use for the image
592 * @param title The name of the image
593 * @param description The description of the image
594 * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored
595 * for any reason.
596 */
597 public static final String insertImage(ContentResolver cr, Bitmap source,
Ray Chen00c575a2009-08-28 14:12:15 -0700598 String title, String description) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 ContentValues values = new ContentValues();
600 values.put(Images.Media.TITLE, title);
601 values.put(Images.Media.DESCRIPTION, description);
602 values.put(Images.Media.MIME_TYPE, "image/jpeg");
603
604 Uri url = null;
605 String stringUrl = null; /* value to be returned */
606
Ray Chen00c575a2009-08-28 14:12:15 -0700607 try {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 url = cr.insert(EXTERNAL_CONTENT_URI, values);
609
610 if (source != null) {
611 OutputStream imageOut = cr.openOutputStream(url);
612 try {
613 source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut);
614 } finally {
615 imageOut.close();
616 }
617
618 long id = ContentUris.parseId(url);
Ray Chen44dcf652010-04-05 11:26:40 -0700619 // Wait until MINI_KIND thumbnail is generated.
620 Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id,
621 Images.Thumbnails.MINI_KIND, null);
622 // This is for backward compatibility.
623 Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F,
624 Images.Thumbnails.MICRO_KIND);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 } else {
626 Log.e(TAG, "Failed to create thumbnail, removing original");
627 cr.delete(url, null, null);
628 url = null;
629 }
630 } catch (Exception e) {
631 Log.e(TAG, "Failed to insert image", e);
632 if (url != null) {
633 cr.delete(url, null, null);
634 url = null;
635 }
636 }
637
638 if (url != null) {
639 stringUrl = url.toString();
640 }
641
642 return stringUrl;
643 }
644
645 /**
646 * Get the content:// style URI for the image media table on the
647 * given volume.
648 *
649 * @param volumeName the name of the volume to get the URI for
650 * @return the URI to the image media table on the given volume
651 */
652 public static Uri getContentUri(String volumeName) {
653 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
654 "/images/media");
655 }
656
657 /**
658 * The content:// style URI for the internal storage.
659 */
660 public static final Uri INTERNAL_CONTENT_URI =
661 getContentUri("internal");
662
663 /**
664 * The content:// style URI for the "primary" external storage
665 * volume.
666 */
667 public static final Uri EXTERNAL_CONTENT_URI =
668 getContentUri("external");
669
670 /**
671 * The MIME type of of this directory of
672 * images. Note that each entry in this directory will have a standard
673 * image MIME type as appropriate -- for example, image/jpeg.
674 */
675 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/image";
676
677 /**
678 * The default sort order for this table
679 */
680 public static final String DEFAULT_SORT_ORDER = ImageColumns.BUCKET_DISPLAY_NAME;
Ray Chen00c575a2009-08-28 14:12:15 -0700681 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682
Ray Chen00c575a2009-08-28 14:12:15 -0700683 /**
684 * This class allows developers to query and get two kinds of thumbnails:
685 * MINI_KIND: 512 x 384 thumbnail
686 * MICRO_KIND: 96 x 96 thumbnail
687 */
688 public static class Thumbnails implements BaseColumns {
689 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
691 }
692
Ray Chen00c575a2009-08-28 14:12:15 -0700693 public static final Cursor queryMiniThumbnails(ContentResolver cr, Uri uri, int kind,
694 String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 return cr.query(uri, projection, "kind = " + kind, null, DEFAULT_SORT_ORDER);
696 }
697
Ray Chen00c575a2009-08-28 14:12:15 -0700698 public static final Cursor queryMiniThumbnail(ContentResolver cr, long origId, int kind,
699 String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 return cr.query(EXTERNAL_CONTENT_URI, projection,
701 IMAGE_ID + " = " + origId + " AND " + KIND + " = " +
702 kind, null, null);
703 }
704
705 /**
Ray Chenb9944192009-09-29 20:24:01 -0700706 * This method cancels the thumbnail request so clients waiting for getThumbnail will be
707 * interrupted and return immediately. Only the original process which made the getThumbnail
708 * requests can cancel their own requests.
709 *
710 * @param cr ContentResolver
711 * @param origId original image id
712 */
713 public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
Ray Chen13ed5752009-10-05 12:21:24 -0700714 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
715 InternalThumbnails.DEFAULT_GROUP_ID);
Ray Chenb9944192009-09-29 20:24:01 -0700716 }
717
718 /**
Ray Chen00c575a2009-08-28 14:12:15 -0700719 * This method checks if the thumbnails of the specified image (origId) has been created.
720 * It will be blocked until the thumbnails are generated.
721 *
722 * @param cr ContentResolver used to dispatch queries to MediaProvider.
723 * @param origId Original image id associated with thumbnail of interest.
724 * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
725 * @param options this is only used for MINI_KIND when decoding the Bitmap
726 * @return A Bitmap instance. It could be null if the original image
727 * associated with origId doesn't exist or memory is not enough.
728 */
729 public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
730 BitmapFactory.Options options) {
Ray Chen13ed5752009-10-05 12:21:24 -0700731 return InternalThumbnails.getThumbnail(cr, origId,
732 InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
733 EXTERNAL_CONTENT_URI, false);
734 }
735
736 /**
737 * This method cancels the thumbnail request so clients waiting for getThumbnail will be
738 * interrupted and return immediately. Only the original process which made the getThumbnail
739 * requests can cancel their own requests.
740 *
741 * @param cr ContentResolver
742 * @param origId original image id
743 * @param groupId the same groupId used in getThumbnail.
744 */
745 public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
746 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
747 }
748
749 /**
750 * This method checks if the thumbnails of the specified image (origId) has been created.
751 * It will be blocked until the thumbnails are generated.
752 *
753 * @param cr ContentResolver used to dispatch queries to MediaProvider.
754 * @param origId Original image id associated with thumbnail of interest.
755 * @param groupId the id of group to which this request belongs
756 * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
757 * @param options this is only used for MINI_KIND when decoding the Bitmap
758 * @return A Bitmap instance. It could be null if the original image
759 * associated with origId doesn't exist or memory is not enough.
760 */
761 public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
762 int kind, BitmapFactory.Options options) {
763 return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
Ray Chen00c575a2009-08-28 14:12:15 -0700764 EXTERNAL_CONTENT_URI, false);
765 }
766
767 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 * Get the content:// style URI for the image media table on the
769 * given volume.
770 *
771 * @param volumeName the name of the volume to get the URI for
772 * @return the URI to the image media table on the given volume
773 */
774 public static Uri getContentUri(String volumeName) {
775 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
776 "/images/thumbnails");
777 }
778
779 /**
780 * The content:// style URI for the internal storage.
781 */
782 public static final Uri INTERNAL_CONTENT_URI =
783 getContentUri("internal");
784
785 /**
786 * The content:// style URI for the "primary" external storage
787 * volume.
788 */
789 public static final Uri EXTERNAL_CONTENT_URI =
790 getContentUri("external");
791
792 /**
793 * The default sort order for this table
794 */
795 public static final String DEFAULT_SORT_ORDER = "image_id ASC";
796
797 /**
798 * The data stream for the thumbnail
799 * <P>Type: DATA STREAM</P>
800 */
801 public static final String DATA = "_data";
802
803 /**
804 * The original image for the thumbnal
805 * <P>Type: INTEGER (ID from Images table)</P>
806 */
807 public static final String IMAGE_ID = "image_id";
808
809 /**
810 * The kind of the thumbnail
811 * <P>Type: INTEGER (One of the values below)</P>
812 */
813 public static final String KIND = "kind";
814
815 public static final int MINI_KIND = 1;
816 public static final int FULL_SCREEN_KIND = 2;
817 public static final int MICRO_KIND = 3;
Ray Chen00c575a2009-08-28 14:12:15 -0700818 /**
819 * The blob raw data of thumbnail
820 * <P>Type: DATA STREAM</P>
821 */
822 public static final String THUMB_DATA = "thumb_data";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823
824 /**
825 * The width of the thumbnal
826 * <P>Type: INTEGER (long)</P>
827 */
828 public static final String WIDTH = "width";
829
830 /**
831 * The height of the thumbnail
832 * <P>Type: INTEGER (long)</P>
833 */
834 public static final String HEIGHT = "height";
835 }
836 }
837
838 /**
839 * Container for all audio content.
840 */
841 public static final class Audio {
842 /**
843 * Columns for audio file that show up in multiple tables.
844 */
845 public interface AudioColumns extends MediaColumns {
846
847 /**
848 * A non human readable key calculated from the TITLE, used for
849 * searching, sorting and grouping
850 * <P>Type: TEXT</P>
851 */
852 public static final String TITLE_KEY = "title_key";
853
854 /**
855 * The duration of the audio file, in ms
856 * <P>Type: INTEGER (long)</P>
857 */
858 public static final String DURATION = "duration";
859
860 /**
861 * The position, in ms, playback was at when playback for this file
862 * was last stopped.
863 * <P>Type: INTEGER (long)</P>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 */
865 public static final String BOOKMARK = "bookmark";
866
867 /**
868 * The id of the artist who created the audio file, if any
869 * <P>Type: INTEGER (long)</P>
870 */
871 public static final String ARTIST_ID = "artist_id";
872
873 /**
874 * The artist who created the audio file, if any
875 * <P>Type: TEXT</P>
876 */
877 public static final String ARTIST = "artist";
878
879 /**
Marco Nelissenabc28192010-03-18 17:10:38 -0700880 * The artist credited for the album that contains the audio file
881 * <P>Type: TEXT</P>
882 * @hide
883 */
884 public static final String ALBUM_ARTIST = "album_artist";
885
886 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 * A non human readable key calculated from the ARTIST, used for
888 * searching, sorting and grouping
889 * <P>Type: TEXT</P>
890 */
891 public static final String ARTIST_KEY = "artist_key";
892
893 /**
894 * The composer of the audio file, if any
895 * <P>Type: TEXT</P>
896 */
897 public static final String COMPOSER = "composer";
898
899 /**
900 * The id of the album the audio file is from, if any
901 * <P>Type: INTEGER (long)</P>
902 */
903 public static final String ALBUM_ID = "album_id";
904
905 /**
906 * The album the audio file is from, if any
907 * <P>Type: TEXT</P>
908 */
909 public static final String ALBUM = "album";
910
911 /**
912 * A non human readable key calculated from the ALBUM, used for
913 * searching, sorting and grouping
914 * <P>Type: TEXT</P>
915 */
916 public static final String ALBUM_KEY = "album_key";
917
918 /**
919 * A URI to the album art, if any
920 * <P>Type: TEXT</P>
921 */
922 public static final String ALBUM_ART = "album_art";
923
924 /**
925 * The track number of this song on the album, if any.
926 * This number encodes both the track number and the
927 * disc number. For multi-disc sets, this number will
928 * be 1xxx for tracks on the first disc, 2xxx for tracks
929 * on the second disc, etc.
930 * <P>Type: INTEGER</P>
931 */
932 public static final String TRACK = "track";
933
934 /**
935 * The year the audio file was recorded, if any
936 * <P>Type: INTEGER</P>
937 */
938 public static final String YEAR = "year";
939
940 /**
941 * Non-zero if the audio file is music
942 * <P>Type: INTEGER (boolean)</P>
943 */
944 public static final String IS_MUSIC = "is_music";
945
946 /**
947 * Non-zero if the audio file is a podcast
948 * <P>Type: INTEGER (boolean)</P>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 */
950 public static final String IS_PODCAST = "is_podcast";
951
952 /**
953 * Non-zero id the audio file may be a ringtone
954 * <P>Type: INTEGER (boolean)</P>
955 */
956 public static final String IS_RINGTONE = "is_ringtone";
957
958 /**
959 * Non-zero id the audio file may be an alarm
960 * <P>Type: INTEGER (boolean)</P>
961 */
962 public static final String IS_ALARM = "is_alarm";
963
964 /**
965 * Non-zero id the audio file may be a notification sound
966 * <P>Type: INTEGER (boolean)</P>
967 */
968 public static final String IS_NOTIFICATION = "is_notification";
969 }
970
971 /**
972 * Converts a name to a "key" that can be used for grouping, sorting
973 * and searching.
974 * The rules that govern this conversion are:
975 * - remove 'special' characters like ()[]'!?.,
976 * - remove leading/trailing spaces
977 * - convert everything to lowercase
978 * - remove leading "the ", "an " and "a "
979 * - remove trailing ", the|an|a"
980 * - remove accents. This step leaves us with CollationKey data,
981 * which is not human readable
982 *
983 * @param name The artist or album name to convert
984 * @return The "key" for the given name.
985 */
986 public static String keyFor(String name) {
987 if (name != null) {
Marco Nelissen816cf522009-07-06 09:19:10 -0700988 boolean sortfirst = false;
Marco Nelissen9a488b42010-01-04 15:09:03 -0800989 if (name.equals(UNKNOWN_STRING)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 return "\001";
991 }
Marco Nelissen816cf522009-07-06 09:19:10 -0700992 // Check if the first character is \001. We use this to
993 // force sorting of certain special files, like the silent ringtone.
994 if (name.startsWith("\001")) {
995 sortfirst = true;
996 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 name = name.trim().toLowerCase();
998 if (name.startsWith("the ")) {
999 name = name.substring(4);
1000 }
1001 if (name.startsWith("an ")) {
1002 name = name.substring(3);
1003 }
1004 if (name.startsWith("a ")) {
1005 name = name.substring(2);
1006 }
1007 if (name.endsWith(", the") || name.endsWith(",the") ||
1008 name.endsWith(", an") || name.endsWith(",an") ||
1009 name.endsWith(", a") || name.endsWith(",a")) {
1010 name = name.substring(0, name.lastIndexOf(','));
1011 }
Marco Nelissene754e122009-05-22 12:16:58 -07001012 name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 if (name.length() > 0) {
1014 // Insert a separator between the characters to avoid
1015 // matches on a partial character. If we ever change
1016 // to start-of-word-only matches, this can be removed.
1017 StringBuilder b = new StringBuilder();
1018 b.append('.');
1019 int nl = name.length();
1020 for (int i = 0; i < nl; i++) {
1021 b.append(name.charAt(i));
1022 b.append('.');
1023 }
1024 name = b.toString();
Marco Nelissen816cf522009-07-06 09:19:10 -07001025 String key = DatabaseUtils.getCollationKey(name);
1026 if (sortfirst) {
1027 key = "\001" + key;
1028 }
1029 return key;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 } else {
1031 return "";
1032 }
1033 }
1034 return null;
1035 }
1036
1037 public static final class Media implements AudioColumns {
1038 /**
1039 * Get the content:// style URI for the audio media table on the
1040 * given volume.
1041 *
1042 * @param volumeName the name of the volume to get the URI for
1043 * @return the URI to the audio media table on the given volume
1044 */
1045 public static Uri getContentUri(String volumeName) {
1046 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1047 "/audio/media");
1048 }
1049
1050 public static Uri getContentUriForPath(String path) {
1051 return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ?
1052 EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI);
1053 }
1054
1055 /**
1056 * The content:// style URI for the internal storage.
1057 */
1058 public static final Uri INTERNAL_CONTENT_URI =
1059 getContentUri("internal");
1060
1061 /**
1062 * The content:// style URI for the "primary" external storage
1063 * volume.
1064 */
1065 public static final Uri EXTERNAL_CONTENT_URI =
1066 getContentUri("external");
1067
1068 /**
1069 * The MIME type for this table.
1070 */
1071 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/audio";
1072
1073 /**
1074 * The default sort order for this table
1075 */
Marco Nelissen816cf522009-07-06 09:19:10 -07001076 public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077
1078 /**
1079 * Activity Action: Start SoundRecorder application.
1080 * <p>Input: nothing.
1081 * <p>Output: An uri to the recorded sound stored in the Media Library
1082 * if the recording was successful.
1083 * May also contain the extra EXTRA_MAX_BYTES.
1084 * @see #EXTRA_MAX_BYTES
1085 */
1086 public static final String RECORD_SOUND_ACTION =
1087 "android.provider.MediaStore.RECORD_SOUND";
1088
1089 /**
1090 * The name of the Intent-extra used to define a maximum file size for
1091 * a recording made by the SoundRecorder application.
1092 *
1093 * @see #RECORD_SOUND_ACTION
1094 */
1095 public static final String EXTRA_MAX_BYTES =
1096 "android.provider.MediaStore.extra.MAX_BYTES";
1097 }
1098
1099 /**
1100 * Columns representing an audio genre
1101 */
1102 public interface GenresColumns {
1103 /**
1104 * The name of the genre
1105 * <P>Type: TEXT</P>
1106 */
1107 public static final String NAME = "name";
1108 }
1109
1110 /**
1111 * Contains all genres for audio files
1112 */
1113 public static final class Genres implements BaseColumns, GenresColumns {
1114 /**
1115 * Get the content:// style URI for the audio genres table on the
1116 * given volume.
1117 *
1118 * @param volumeName the name of the volume to get the URI for
1119 * @return the URI to the audio genres table on the given volume
1120 */
1121 public static Uri getContentUri(String volumeName) {
1122 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1123 "/audio/genres");
1124 }
1125
1126 /**
1127 * The content:// style URI for the internal storage.
1128 */
1129 public static final Uri INTERNAL_CONTENT_URI =
1130 getContentUri("internal");
1131
1132 /**
1133 * The content:// style URI for the "primary" external storage
1134 * volume.
1135 */
1136 public static final Uri EXTERNAL_CONTENT_URI =
1137 getContentUri("external");
1138
1139 /**
1140 * The MIME type for this table.
1141 */
1142 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/genre";
1143
1144 /**
1145 * The MIME type for entries in this table.
1146 */
1147 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/genre";
1148
1149 /**
1150 * The default sort order for this table
1151 */
1152 public static final String DEFAULT_SORT_ORDER = NAME;
1153
1154 /**
1155 * Sub-directory of each genre containing all members.
1156 */
1157 public static final class Members implements AudioColumns {
1158
1159 public static final Uri getContentUri(String volumeName,
1160 long genreId) {
1161 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1162 + "/audio/genres/" + genreId + "/members");
1163 }
1164
1165 /**
1166 * A subdirectory of each genre containing all member audio files.
1167 */
1168 public static final String CONTENT_DIRECTORY = "members";
1169
1170 /**
1171 * The default sort order for this table
1172 */
Marco Nelissen816cf522009-07-06 09:19:10 -07001173 public static final String DEFAULT_SORT_ORDER = TITLE_KEY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174
1175 /**
1176 * The ID of the audio file
1177 * <P>Type: INTEGER (long)</P>
1178 */
1179 public static final String AUDIO_ID = "audio_id";
1180
1181 /**
1182 * The ID of the genre
1183 * <P>Type: INTEGER (long)</P>
1184 */
1185 public static final String GENRE_ID = "genre_id";
1186 }
1187 }
1188
1189 /**
1190 * Columns representing a playlist
1191 */
1192 public interface PlaylistsColumns {
1193 /**
1194 * The name of the playlist
1195 * <P>Type: TEXT</P>
1196 */
1197 public static final String NAME = "name";
1198
1199 /**
1200 * The data stream for the playlist file
1201 * <P>Type: DATA STREAM</P>
1202 */
1203 public static final String DATA = "_data";
1204
1205 /**
1206 * The time the file was added to the media provider
1207 * Units are seconds since 1970.
1208 * <P>Type: INTEGER (long)</P>
1209 */
1210 public static final String DATE_ADDED = "date_added";
1211
1212 /**
1213 * The time the file was last modified
1214 * Units are seconds since 1970.
1215 * NOTE: This is for internal use by the media scanner. Do not modify this field.
1216 * <P>Type: INTEGER (long)</P>
1217 */
1218 public static final String DATE_MODIFIED = "date_modified";
1219 }
1220
1221 /**
1222 * Contains playlists for audio files
1223 */
1224 public static final class Playlists implements BaseColumns,
1225 PlaylistsColumns {
1226 /**
1227 * Get the content:// style URI for the audio playlists table on the
1228 * given volume.
1229 *
1230 * @param volumeName the name of the volume to get the URI for
1231 * @return the URI to the audio playlists table on the given volume
1232 */
1233 public static Uri getContentUri(String volumeName) {
1234 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1235 "/audio/playlists");
1236 }
1237
1238 /**
1239 * The content:// style URI for the internal storage.
1240 */
1241 public static final Uri INTERNAL_CONTENT_URI =
1242 getContentUri("internal");
1243
1244 /**
1245 * The content:// style URI for the "primary" external storage
1246 * volume.
1247 */
1248 public static final Uri EXTERNAL_CONTENT_URI =
1249 getContentUri("external");
1250
1251 /**
1252 * The MIME type for this table.
1253 */
1254 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/playlist";
1255
1256 /**
1257 * The MIME type for entries in this table.
1258 */
1259 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/playlist";
1260
1261 /**
1262 * The default sort order for this table
1263 */
1264 public static final String DEFAULT_SORT_ORDER = NAME;
1265
1266 /**
1267 * Sub-directory of each playlist containing all members.
1268 */
1269 public static final class Members implements AudioColumns {
1270 public static final Uri getContentUri(String volumeName,
1271 long playlistId) {
1272 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1273 + "/audio/playlists/" + playlistId + "/members");
1274 }
1275
1276 /**
Marco Nelissene3d05fc2009-12-09 16:01:46 -08001277 * Convenience method to move a playlist item to a new location
1278 * @param res The content resolver to use
1279 * @param playlistId The numeric id of the playlist
1280 * @param from The position of the item to move
1281 * @param to The position to move the item to
1282 * @return true on success
Marco Nelissene3d05fc2009-12-09 16:01:46 -08001283 */
1284 public static final boolean moveItem(ContentResolver res,
1285 long playlistId, int from, int to) {
1286 Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external",
1287 playlistId)
1288 .buildUpon()
1289 .appendEncodedPath(String.valueOf(from))
1290 .appendQueryParameter("move", "true")
1291 .build();
1292 ContentValues values = new ContentValues();
1293 values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, to);
1294 return res.update(uri, values, null, null) != 0;
1295 }
1296
1297 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 * The ID within the playlist.
1299 */
1300 public static final String _ID = "_id";
1301
1302 /**
1303 * A subdirectory of each playlist containing all member audio
1304 * files.
1305 */
1306 public static final String CONTENT_DIRECTORY = "members";
1307
1308 /**
1309 * The ID of the audio file
1310 * <P>Type: INTEGER (long)</P>
1311 */
1312 public static final String AUDIO_ID = "audio_id";
1313
1314 /**
1315 * The ID of the playlist
1316 * <P>Type: INTEGER (long)</P>
1317 */
1318 public static final String PLAYLIST_ID = "playlist_id";
1319
1320 /**
1321 * The order of the songs in the playlist
1322 * <P>Type: INTEGER (long)></P>
1323 */
1324 public static final String PLAY_ORDER = "play_order";
1325
1326 /**
1327 * The default sort order for this table
1328 */
1329 public static final String DEFAULT_SORT_ORDER = PLAY_ORDER;
1330 }
1331 }
1332
1333 /**
1334 * Columns representing an artist
1335 */
1336 public interface ArtistColumns {
1337 /**
1338 * The artist who created the audio file, if any
1339 * <P>Type: TEXT</P>
1340 */
1341 public static final String ARTIST = "artist";
1342
1343 /**
1344 * A non human readable key calculated from the ARTIST, used for
1345 * searching, sorting and grouping
1346 * <P>Type: TEXT</P>
1347 */
1348 public static final String ARTIST_KEY = "artist_key";
1349
1350 /**
1351 * The number of albums in the database for this artist
1352 */
1353 public static final String NUMBER_OF_ALBUMS = "number_of_albums";
1354
1355 /**
1356 * The number of albums in the database for this artist
1357 */
1358 public static final String NUMBER_OF_TRACKS = "number_of_tracks";
1359 }
1360
1361 /**
1362 * Contains artists for audio files
1363 */
1364 public static final class Artists implements BaseColumns, ArtistColumns {
1365 /**
1366 * Get the content:// style URI for the artists table on the
1367 * given volume.
1368 *
1369 * @param volumeName the name of the volume to get the URI for
1370 * @return the URI to the audio artists table on the given volume
1371 */
1372 public static Uri getContentUri(String volumeName) {
1373 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1374 "/audio/artists");
1375 }
1376
1377 /**
1378 * The content:// style URI for the internal storage.
1379 */
1380 public static final Uri INTERNAL_CONTENT_URI =
1381 getContentUri("internal");
1382
1383 /**
1384 * The content:// style URI for the "primary" external storage
1385 * volume.
1386 */
1387 public static final Uri EXTERNAL_CONTENT_URI =
1388 getContentUri("external");
1389
1390 /**
1391 * The MIME type for this table.
1392 */
1393 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/artists";
1394
1395 /**
1396 * The MIME type for entries in this table.
1397 */
1398 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/artist";
1399
1400 /**
1401 * The default sort order for this table
1402 */
1403 public static final String DEFAULT_SORT_ORDER = ARTIST_KEY;
1404
1405 /**
1406 * Sub-directory of each artist containing all albums on which
1407 * a song by the artist appears.
1408 */
1409 public static final class Albums implements AlbumColumns {
1410 public static final Uri getContentUri(String volumeName,
1411 long artistId) {
1412 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName
1413 + "/audio/artists/" + artistId + "/albums");
1414 }
1415 }
1416 }
1417
1418 /**
1419 * Columns representing an album
1420 */
1421 public interface AlbumColumns {
1422
1423 /**
1424 * The id for the album
1425 * <P>Type: INTEGER</P>
1426 */
1427 public static final String ALBUM_ID = "album_id";
1428
1429 /**
1430 * The album on which the audio file appears, if any
1431 * <P>Type: TEXT</P>
1432 */
1433 public static final String ALBUM = "album";
1434
1435 /**
1436 * The artist whose songs appear on this album
1437 * <P>Type: TEXT</P>
1438 */
1439 public static final String ARTIST = "artist";
1440
1441 /**
1442 * The number of songs on this album
1443 * <P>Type: INTEGER</P>
1444 */
1445 public static final String NUMBER_OF_SONGS = "numsongs";
1446
1447 /**
1448 * This column is available when getting album info via artist,
1449 * and indicates the number of songs on the album by the given
1450 * artist.
1451 * <P>Type: INTEGER</P>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001452 */
1453 public static final String NUMBER_OF_SONGS_FOR_ARTIST = "numsongs_by_artist";
1454
1455 /**
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07001456 * The year in which the earliest songs
1457 * on this album were released. This will often
1458 * be the same as {@link #LAST_YEAR}, but for compilation albums
1459 * they might differ.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001460 * <P>Type: INTEGER</P>
1461 */
1462 public static final String FIRST_YEAR = "minyear";
Ray Chen00c575a2009-08-28 14:12:15 -07001463
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07001464 /**
1465 * The year in which the latest songs
1466 * on this album were released. This will often
1467 * be the same as {@link #FIRST_YEAR}, but for compilation albums
1468 * they might differ.
1469 * <P>Type: INTEGER</P>
1470 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001471 public static final String LAST_YEAR = "maxyear";
1472
1473 /**
1474 * A non human readable key calculated from the ALBUM, used for
1475 * searching, sorting and grouping
1476 * <P>Type: TEXT</P>
1477 */
1478 public static final String ALBUM_KEY = "album_key";
1479
1480 /**
1481 * Cached album art.
1482 * <P>Type: TEXT</P>
1483 */
1484 public static final String ALBUM_ART = "album_art";
1485 }
1486
1487 /**
1488 * Contains artists for audio files
1489 */
1490 public static final class Albums implements BaseColumns, AlbumColumns {
1491 /**
1492 * Get the content:// style URI for the albums table on the
1493 * given volume.
1494 *
1495 * @param volumeName the name of the volume to get the URI for
1496 * @return the URI to the audio albums table on the given volume
1497 */
1498 public static Uri getContentUri(String volumeName) {
1499 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1500 "/audio/albums");
1501 }
1502
1503 /**
1504 * The content:// style URI for the internal storage.
1505 */
1506 public static final Uri INTERNAL_CONTENT_URI =
1507 getContentUri("internal");
1508
1509 /**
1510 * The content:// style URI for the "primary" external storage
1511 * volume.
1512 */
1513 public static final Uri EXTERNAL_CONTENT_URI =
1514 getContentUri("external");
1515
1516 /**
1517 * The MIME type for this table.
1518 */
1519 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/albums";
1520
1521 /**
1522 * The MIME type for entries in this table.
1523 */
1524 public static final String ENTRY_CONTENT_TYPE = "vnd.android.cursor.item/album";
1525
1526 /**
1527 * The default sort order for this table
1528 */
1529 public static final String DEFAULT_SORT_ORDER = ALBUM_KEY;
1530 }
1531 }
1532
1533 public static final class Video {
1534
1535 /**
1536 * The default sort order for this table.
1537 */
1538 public static final String DEFAULT_SORT_ORDER = MediaColumns.DISPLAY_NAME;
1539
Ray Chen00c575a2009-08-28 14:12:15 -07001540 public static final Cursor query(ContentResolver cr, Uri uri, String[] projection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541 return cr.query(uri, projection, null, null, DEFAULT_SORT_ORDER);
1542 }
1543
1544 public interface VideoColumns extends MediaColumns {
1545
1546 /**
1547 * The duration of the video file, in ms
1548 * <P>Type: INTEGER (long)</P>
1549 */
1550 public static final String DURATION = "duration";
1551
1552 /**
1553 * The artist who created the video file, if any
1554 * <P>Type: TEXT</P>
1555 */
1556 public static final String ARTIST = "artist";
1557
1558 /**
1559 * The album the video file is from, if any
1560 * <P>Type: TEXT</P>
1561 */
1562 public static final String ALBUM = "album";
1563
1564 /**
1565 * The resolution of the video file, formatted as "XxY"
1566 * <P>Type: TEXT</P>
1567 */
1568 public static final String RESOLUTION = "resolution";
1569
1570 /**
1571 * The description of the video recording
1572 * <P>Type: TEXT</P>
1573 */
1574 public static final String DESCRIPTION = "description";
1575
1576 /**
1577 * Whether the video should be published as public or private
1578 * <P>Type: INTEGER</P>
1579 */
1580 public static final String IS_PRIVATE = "isprivate";
1581
1582 /**
1583 * The user-added tags associated with a video
1584 * <P>Type: TEXT</P>
1585 */
1586 public static final String TAGS = "tags";
1587
1588 /**
1589 * The YouTube category of the video
1590 * <P>Type: TEXT</P>
1591 */
1592 public static final String CATEGORY = "category";
1593
1594 /**
1595 * The language of the video
1596 * <P>Type: TEXT</P>
1597 */
1598 public static final String LANGUAGE = "language";
1599
1600 /**
1601 * The latitude where the image was captured.
1602 * <P>Type: DOUBLE</P>
1603 */
1604 public static final String LATITUDE = "latitude";
1605
1606 /**
1607 * The longitude where the image was captured.
1608 * <P>Type: DOUBLE</P>
1609 */
1610 public static final String LONGITUDE = "longitude";
1611
1612 /**
1613 * The date & time that the image was taken in units
1614 * of milliseconds since jan 1, 1970.
1615 * <P>Type: INTEGER</P>
1616 */
1617 public static final String DATE_TAKEN = "datetaken";
1618
1619 /**
1620 * The mini thumb id.
1621 * <P>Type: INTEGER</P>
1622 */
1623 public static final String MINI_THUMB_MAGIC = "mini_thumb_magic";
1624
1625 /**
1626 * The bucket id of the video. This is a read-only property that
1627 * is automatically computed from the DATA column.
1628 * <P>Type: TEXT</P>
1629 */
1630 public static final String BUCKET_ID = "bucket_id";
1631
1632 /**
1633 * The bucket display name of the video. This is a read-only property that
1634 * is automatically computed from the DATA column.
1635 * <P>Type: TEXT</P>
1636 */
1637 public static final String BUCKET_DISPLAY_NAME = "bucket_display_name";
1638
1639 /**
1640 * The bookmark for the video. Time in ms. Represents the location in the video that the
1641 * video should start playing at the next time it is opened. If the value is null or
1642 * out of the range 0..DURATION-1 then the video should start playing from the
1643 * beginning.
1644 * <P>Type: INTEGER</P>
1645 */
1646 public static final String BOOKMARK = "bookmark";
1647 }
1648
1649 public static final class Media implements VideoColumns {
1650 /**
1651 * Get the content:// style URI for the video media table on the
1652 * given volume.
1653 *
1654 * @param volumeName the name of the volume to get the URI for
1655 * @return the URI to the video media table on the given volume
1656 */
1657 public static Uri getContentUri(String volumeName) {
1658 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1659 "/video/media");
1660 }
1661
1662 /**
1663 * The content:// style URI for the internal storage.
1664 */
1665 public static final Uri INTERNAL_CONTENT_URI =
1666 getContentUri("internal");
1667
1668 /**
1669 * The content:// style URI for the "primary" external storage
1670 * volume.
1671 */
1672 public static final Uri EXTERNAL_CONTENT_URI =
1673 getContentUri("external");
1674
1675 /**
1676 * The MIME type for this table.
1677 */
1678 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/video";
1679
1680 /**
1681 * The default sort order for this table
1682 */
1683 public static final String DEFAULT_SORT_ORDER = TITLE;
1684 }
Ray Chen00c575a2009-08-28 14:12:15 -07001685
1686 /**
1687 * This class allows developers to query and get two kinds of thumbnails:
1688 * MINI_KIND: 512 x 384 thumbnail
1689 * MICRO_KIND: 96 x 96 thumbnail
1690 *
1691 */
1692 public static class Thumbnails implements BaseColumns {
1693 /**
Ray Chenb9944192009-09-29 20:24:01 -07001694 * This method cancels the thumbnail request so clients waiting for getThumbnail will be
1695 * interrupted and return immediately. Only the original process which made the getThumbnail
1696 * requests can cancel their own requests.
1697 *
1698 * @param cr ContentResolver
1699 * @param origId original video id
1700 */
1701 public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
Ray Chen13ed5752009-10-05 12:21:24 -07001702 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
1703 InternalThumbnails.DEFAULT_GROUP_ID);
Ray Chenb9944192009-09-29 20:24:01 -07001704 }
1705
1706 /**
Ray Chen00c575a2009-08-28 14:12:15 -07001707 * This method checks if the thumbnails of the specified image (origId) has been created.
1708 * It will be blocked until the thumbnails are generated.
1709 *
1710 * @param cr ContentResolver used to dispatch queries to MediaProvider.
1711 * @param origId Original image id associated with thumbnail of interest.
Ray Chen13ed5752009-10-05 12:21:24 -07001712 * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND.
1713 * @param options this is only used for MINI_KIND when decoding the Bitmap
1714 * @return A Bitmap instance. It could be null if the original image
1715 * associated with origId doesn't exist or memory is not enough.
1716 */
1717 public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
1718 BitmapFactory.Options options) {
1719 return InternalThumbnails.getThumbnail(cr, origId,
1720 InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
1721 EXTERNAL_CONTENT_URI, true);
1722 }
1723
1724 /**
1725 * This method checks if the thumbnails of the specified image (origId) has been created.
1726 * It will be blocked until the thumbnails are generated.
1727 *
1728 * @param cr ContentResolver used to dispatch queries to MediaProvider.
1729 * @param origId Original image id associated with thumbnail of interest.
1730 * @param groupId the id of group to which this request belongs
Ray Chen00c575a2009-08-28 14:12:15 -07001731 * @param kind The type of thumbnail to fetch. Should be either MINI_KIND or MICRO_KIND
1732 * @param options this is only used for MINI_KIND when decoding the Bitmap
1733 * @return A Bitmap instance. It could be null if the original image associated with
1734 * origId doesn't exist or memory is not enough.
1735 */
Ray Chen13ed5752009-10-05 12:21:24 -07001736 public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
1737 int kind, BitmapFactory.Options options) {
1738 return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
Ray Chen00c575a2009-08-28 14:12:15 -07001739 EXTERNAL_CONTENT_URI, true);
1740 }
1741
1742 /**
Ray Chen13ed5752009-10-05 12:21:24 -07001743 * This method cancels the thumbnail request so clients waiting for getThumbnail will be
1744 * interrupted and return immediately. Only the original process which made the getThumbnail
1745 * requests can cancel their own requests.
1746 *
1747 * @param cr ContentResolver
1748 * @param origId original video id
1749 * @param groupId the same groupId used in getThumbnail.
1750 */
1751 public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
1752 InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
1753 }
1754
1755 /**
Ray Chen00c575a2009-08-28 14:12:15 -07001756 * Get the content:// style URI for the image media table on the
1757 * given volume.
1758 *
1759 * @param volumeName the name of the volume to get the URI for
1760 * @return the URI to the image media table on the given volume
1761 */
1762 public static Uri getContentUri(String volumeName) {
1763 return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
1764 "/video/thumbnails");
1765 }
1766
1767 /**
1768 * The content:// style URI for the internal storage.
1769 */
1770 public static final Uri INTERNAL_CONTENT_URI =
1771 getContentUri("internal");
1772
1773 /**
1774 * The content:// style URI for the "primary" external storage
1775 * volume.
1776 */
1777 public static final Uri EXTERNAL_CONTENT_URI =
1778 getContentUri("external");
1779
1780 /**
1781 * The default sort order for this table
1782 */
1783 public static final String DEFAULT_SORT_ORDER = "video_id ASC";
1784
1785 /**
1786 * The data stream for the thumbnail
1787 * <P>Type: DATA STREAM</P>
1788 */
1789 public static final String DATA = "_data";
1790
1791 /**
1792 * The original image for the thumbnal
1793 * <P>Type: INTEGER (ID from Video table)</P>
1794 */
1795 public static final String VIDEO_ID = "video_id";
1796
1797 /**
1798 * The kind of the thumbnail
1799 * <P>Type: INTEGER (One of the values below)</P>
1800 */
1801 public static final String KIND = "kind";
1802
1803 public static final int MINI_KIND = 1;
1804 public static final int FULL_SCREEN_KIND = 2;
1805 public static final int MICRO_KIND = 3;
1806
1807 /**
1808 * The width of the thumbnal
1809 * <P>Type: INTEGER (long)</P>
1810 */
1811 public static final String WIDTH = "width";
1812
1813 /**
1814 * The height of the thumbnail
1815 * <P>Type: INTEGER (long)</P>
1816 */
1817 public static final String HEIGHT = "height";
1818 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001819 }
1820
1821 /**
1822 * Uri for querying the state of the media scanner.
1823 */
1824 public static Uri getMediaScannerUri() {
1825 return Uri.parse(CONTENT_AUTHORITY_SLASH + "none/media_scanner");
1826 }
1827
1828 /**
1829 * Name of current volume being scanned by the media scanner.
1830 */
1831 public static final String MEDIA_SCANNER_VOLUME = "volume";
Karl Ostmo8ce072d2010-01-30 15:15:39 -06001832
1833 /**
1834 * Name of the file signaling the media scanner to ignore media in the containing directory
1835 * and its subdirectories. Developers should use this to avoid application graphics showing
1836 * up in the Gallery and likewise prevent application sounds and music from showing up in
1837 * the Music app.
1838 */
1839 public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840}