RingtoneManager.java revision 13ceb54a1986d6889892246069fe23b574036f2f
1/*
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.media;
18
19import com.android.internal.database.SortCursor;
20
21import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
23import android.app.Activity;
24import android.content.ContentUris;
25import android.content.Context;
26import android.content.pm.PackageManager;
27import android.database.Cursor;
28import android.net.Uri;
29import android.os.Environment;
30import android.os.Process;
31import android.provider.MediaStore;
32import android.provider.Settings;
33import android.provider.Settings.System;
34import android.util.Log;
35
36import java.util.ArrayList;
37import java.util.List;
38
39/**
40 * RingtoneManager provides access to ringtones, notification, and other types
41 * of sounds. It manages querying the different media providers and combines the
42 * results into a single cursor. It also provides a {@link Ringtone} for each
43 * ringtone. We generically call these sounds ringtones, however the
44 * {@link #TYPE_RINGTONE} refers to the type of sounds that are suitable for the
45 * phone ringer.
46 * <p>
47 * To show a ringtone picker to the user, use the
48 * {@link #ACTION_RINGTONE_PICKER} intent to launch the picker as a subactivity.
49 *
50 * @see Ringtone
51 */
52public class RingtoneManager {
53
54    private static final String TAG = "RingtoneManager";
55
56    // Make sure these are in sync with attrs.xml:
57    // <attr name="ringtoneType">
58
59    /**
60     * Type that refers to sounds that are used for the phone ringer.
61     */
62    public static final int TYPE_RINGTONE = 1;
63
64    /**
65     * Type that refers to sounds that are used for notifications.
66     */
67    public static final int TYPE_NOTIFICATION = 2;
68
69    /**
70     * Type that refers to sounds that are used for the alarm.
71     */
72    public static final int TYPE_ALARM = 4;
73
74    /**
75     * All types of sounds.
76     */
77    public static final int TYPE_ALL = TYPE_RINGTONE | TYPE_NOTIFICATION | TYPE_ALARM;
78
79    // </attr>
80
81    /**
82     * Activity Action: Shows a ringtone picker.
83     * <p>
84     * Input: {@link #EXTRA_RINGTONE_EXISTING_URI},
85     * {@link #EXTRA_RINGTONE_SHOW_DEFAULT},
86     * {@link #EXTRA_RINGTONE_SHOW_SILENT}, {@link #EXTRA_RINGTONE_TYPE},
87     * {@link #EXTRA_RINGTONE_DEFAULT_URI}, {@link #EXTRA_RINGTONE_TITLE},
88     * <p>
89     * Output: {@link #EXTRA_RINGTONE_PICKED_URI}.
90     */
91    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
92    public static final String ACTION_RINGTONE_PICKER = "android.intent.action.RINGTONE_PICKER";
93
94    /**
95     * Given to the ringtone picker as a boolean. Whether to show an item for
96     * "Default".
97     *
98     * @see #ACTION_RINGTONE_PICKER
99     */
100    public static final String EXTRA_RINGTONE_SHOW_DEFAULT =
101            "android.intent.extra.ringtone.SHOW_DEFAULT";
102
103    /**
104     * Given to the ringtone picker as a boolean. Whether to show an item for
105     * "Silent". If the "Silent" item is picked,
106     * {@link #EXTRA_RINGTONE_PICKED_URI} will be null.
107     *
108     * @see #ACTION_RINGTONE_PICKER
109     */
110    public static final String EXTRA_RINGTONE_SHOW_SILENT =
111            "android.intent.extra.ringtone.SHOW_SILENT";
112
113    /**
114     * Given to the ringtone picker as a boolean. Whether to include DRM ringtones.
115     * @deprecated DRM ringtones are no longer supported
116     */
117    @Deprecated
118    public static final String EXTRA_RINGTONE_INCLUDE_DRM =
119            "android.intent.extra.ringtone.INCLUDE_DRM";
120
121    /**
122     * Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the
123     * current ringtone, which will be used to show a checkmark next to the item
124     * for this {@link Uri}. If showing an item for "Default" (@see
125     * {@link #EXTRA_RINGTONE_SHOW_DEFAULT}), this can also be one of
126     * {@link System#DEFAULT_RINGTONE_URI},
127     * {@link System#DEFAULT_NOTIFICATION_URI}, or
128     * {@link System#DEFAULT_ALARM_ALERT_URI} to have the "Default" item
129     * checked.
130     *
131     * @see #ACTION_RINGTONE_PICKER
132     */
133    public static final String EXTRA_RINGTONE_EXISTING_URI =
134            "android.intent.extra.ringtone.EXISTING_URI";
135
136    /**
137     * Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the
138     * ringtone to play when the user attempts to preview the "Default"
139     * ringtone. This can be one of {@link System#DEFAULT_RINGTONE_URI},
140     * {@link System#DEFAULT_NOTIFICATION_URI}, or
141     * {@link System#DEFAULT_ALARM_ALERT_URI} to have the "Default" point to
142     * the current sound for the given default sound type. If you are showing a
143     * ringtone picker for some other type of sound, you are free to provide any
144     * {@link Uri} here.
145     */
146    public static final String EXTRA_RINGTONE_DEFAULT_URI =
147            "android.intent.extra.ringtone.DEFAULT_URI";
148
149    /**
150     * Given to the ringtone picker as an int. Specifies which ringtone type(s) should be
151     * shown in the picker. One or more of {@link #TYPE_RINGTONE},
152     * {@link #TYPE_NOTIFICATION}, {@link #TYPE_ALARM}, or {@link #TYPE_ALL}
153     * (bitwise-ored together).
154     */
155    public static final String EXTRA_RINGTONE_TYPE = "android.intent.extra.ringtone.TYPE";
156
157    /**
158     * Given to the ringtone picker as a {@link CharSequence}. The title to
159     * show for the ringtone picker. This has a default value that is suitable
160     * in most cases.
161     */
162    public static final String EXTRA_RINGTONE_TITLE = "android.intent.extra.ringtone.TITLE";
163
164    /**
165     * @hide
166     * Given to the ringtone picker as an int. Additional AudioAttributes flags to use
167     * when playing the ringtone in the picker.
168     * @see #ACTION_RINGTONE_PICKER
169     */
170    public static final String EXTRA_RINGTONE_AUDIO_ATTRIBUTES_FLAGS =
171            "android.intent.extra.ringtone.AUDIO_ATTRIBUTES_FLAGS";
172
173    /**
174     * Returned from the ringtone picker as a {@link Uri}.
175     * <p>
176     * It will be one of:
177     * <li> the picked ringtone,
178     * <li> a {@link Uri} that equals {@link System#DEFAULT_RINGTONE_URI},
179     * {@link System#DEFAULT_NOTIFICATION_URI}, or
180     * {@link System#DEFAULT_ALARM_ALERT_URI} if the default was chosen,
181     * <li> null if the "Silent" item was picked.
182     *
183     * @see #ACTION_RINGTONE_PICKER
184     */
185    public static final String EXTRA_RINGTONE_PICKED_URI =
186            "android.intent.extra.ringtone.PICKED_URI";
187
188    // Make sure the column ordering and then ..._COLUMN_INDEX are in sync
189
190    private static final String[] INTERNAL_COLUMNS = new String[] {
191        MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
192        "\"" + MediaStore.Audio.Media.INTERNAL_CONTENT_URI + "\"",
193        MediaStore.Audio.Media.TITLE_KEY
194    };
195
196    private static final String[] MEDIA_COLUMNS = new String[] {
197        MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
198        "\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "\"",
199        MediaStore.Audio.Media.TITLE_KEY
200    };
201
202    /**
203     * The column index (in the cursor returned by {@link #getCursor()} for the
204     * row ID.
205     */
206    public static final int ID_COLUMN_INDEX = 0;
207
208    /**
209     * The column index (in the cursor returned by {@link #getCursor()} for the
210     * title.
211     */
212    public static final int TITLE_COLUMN_INDEX = 1;
213
214    /**
215     * The column index (in the cursor returned by {@link #getCursor()} for the
216     * media provider's URI.
217     */
218    public static final int URI_COLUMN_INDEX = 2;
219
220    private Activity mActivity;
221    private Context mContext;
222
223    private Cursor mCursor;
224
225    private int mType = TYPE_RINGTONE;
226
227    /**
228     * If a column (item from this list) exists in the Cursor, its value must
229     * be true (value of 1) for the row to be returned.
230     */
231    private final List<String> mFilterColumns = new ArrayList<String>();
232
233    private boolean mStopPreviousRingtone = true;
234    private Ringtone mPreviousRingtone;
235
236    /**
237     * Constructs a RingtoneManager. This constructor is recommended as its
238     * constructed instance manages cursor(s).
239     *
240     * @param activity The activity used to get a managed cursor.
241     */
242    public RingtoneManager(Activity activity) {
243        mContext = mActivity = activity;
244        setType(mType);
245    }
246
247    /**
248     * Constructs a RingtoneManager. The instance constructed by this
249     * constructor will not manage the cursor(s), so the client should handle
250     * this itself.
251     *
252     * @param context The context to used to get a cursor.
253     */
254    public RingtoneManager(Context context) {
255        mContext = context;
256        setType(mType);
257    }
258
259    /**
260     * Sets which type(s) of ringtones will be listed by this.
261     *
262     * @param type The type(s), one or more of {@link #TYPE_RINGTONE},
263     *            {@link #TYPE_NOTIFICATION}, {@link #TYPE_ALARM},
264     *            {@link #TYPE_ALL}.
265     * @see #EXTRA_RINGTONE_TYPE
266     */
267    public void setType(int type) {
268
269        if (mCursor != null) {
270            throw new IllegalStateException(
271                    "Setting filter columns should be done before querying for ringtones.");
272        }
273
274        mType = type;
275        setFilterColumnsList(type);
276    }
277
278    /**
279     * Infers the playback stream type based on what type of ringtones this
280     * manager is returning.
281     *
282     * @return The stream type.
283     */
284    public int inferStreamType() {
285        switch (mType) {
286
287            case TYPE_ALARM:
288                return AudioManager.STREAM_ALARM;
289
290            case TYPE_NOTIFICATION:
291                return AudioManager.STREAM_NOTIFICATION;
292
293            default:
294                return AudioManager.STREAM_RING;
295        }
296    }
297
298    /**
299     * Whether retrieving another {@link Ringtone} will stop playing the
300     * previously retrieved {@link Ringtone}.
301     * <p>
302     * If this is false, make sure to {@link Ringtone#stop()} any previous
303     * ringtones to free resources.
304     *
305     * @param stopPreviousRingtone If true, the previously retrieved
306     *            {@link Ringtone} will be stopped.
307     */
308    public void setStopPreviousRingtone(boolean stopPreviousRingtone) {
309        mStopPreviousRingtone = stopPreviousRingtone;
310    }
311
312    /**
313     * @see #setStopPreviousRingtone(boolean)
314     */
315    public boolean getStopPreviousRingtone() {
316        return mStopPreviousRingtone;
317    }
318
319    /**
320     * Stops playing the last {@link Ringtone} retrieved from this.
321     */
322    public void stopPreviousRingtone() {
323        if (mPreviousRingtone != null) {
324            mPreviousRingtone.stop();
325        }
326    }
327
328    /**
329     * Returns whether DRM ringtones will be included.
330     *
331     * @return Whether DRM ringtones will be included.
332     * @see #setIncludeDrm(boolean)
333     * Obsolete - always returns false
334     * @deprecated DRM ringtones are no longer supported
335     */
336    @Deprecated
337    public boolean getIncludeDrm() {
338        return false;
339    }
340
341    /**
342     * Sets whether to include DRM ringtones.
343     *
344     * @param includeDrm Whether to include DRM ringtones.
345     * Obsolete - no longer has any effect
346     * @deprecated DRM ringtones are no longer supported
347     */
348    @Deprecated
349    public void setIncludeDrm(boolean includeDrm) {
350        if (includeDrm) {
351            Log.w(TAG, "setIncludeDrm no longer supported");
352        }
353    }
354
355    /**
356     * Returns a {@link Cursor} of all the ringtones available. The returned
357     * cursor will be the same cursor returned each time this method is called,
358     * so do not {@link Cursor#close()} the cursor. The cursor can be
359     * {@link Cursor#deactivate()} safely.
360     * <p>
361     * If {@link RingtoneManager#RingtoneManager(Activity)} was not used, the
362     * caller should manage the returned cursor through its activity's life
363     * cycle to prevent leaking the cursor.
364     * <p>
365     * Note that the list of ringtones available will differ depending on whether the caller
366     * has the {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission.
367     *
368     * @return A {@link Cursor} of all the ringtones available.
369     * @see #ID_COLUMN_INDEX
370     * @see #TITLE_COLUMN_INDEX
371     * @see #URI_COLUMN_INDEX
372     */
373    public Cursor getCursor() {
374        if (mCursor != null && mCursor.requery()) {
375            return mCursor;
376        }
377
378        final Cursor internalCursor = getInternalRingtones();
379        final Cursor mediaCursor = getMediaRingtones();
380
381        return mCursor = new SortCursor(new Cursor[] { internalCursor, mediaCursor },
382                MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
383    }
384
385    /**
386     * Gets a {@link Ringtone} for the ringtone at the given position in the
387     * {@link Cursor}.
388     *
389     * @param position The position (in the {@link Cursor}) of the ringtone.
390     * @return A {@link Ringtone} pointing to the ringtone.
391     */
392    public Ringtone getRingtone(int position) {
393        if (mStopPreviousRingtone && mPreviousRingtone != null) {
394            mPreviousRingtone.stop();
395        }
396
397        mPreviousRingtone = getRingtone(mContext, getRingtoneUri(position), inferStreamType());
398        return mPreviousRingtone;
399    }
400
401    /**
402     * Gets a {@link Uri} for the ringtone at the given position in the {@link Cursor}.
403     *
404     * @param position The position (in the {@link Cursor}) of the ringtone.
405     * @return A {@link Uri} pointing to the ringtone.
406     */
407    public Uri getRingtoneUri(int position) {
408        // use cursor directly instead of requerying it, which could easily
409        // cause position to shuffle.
410        if (mCursor == null || !mCursor.moveToPosition(position)) {
411            return null;
412        }
413
414        return getUriFromCursor(mCursor);
415    }
416
417    private static Uri getUriFromCursor(Cursor cursor) {
418        return ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), cursor
419                .getLong(ID_COLUMN_INDEX));
420    }
421
422    /**
423     * Gets the position of a {@link Uri} within this {@link RingtoneManager}.
424     *
425     * @param ringtoneUri The {@link Uri} to retreive the position of.
426     * @return The position of the {@link Uri}, or -1 if it cannot be found.
427     */
428    public int getRingtonePosition(Uri ringtoneUri) {
429
430        if (ringtoneUri == null) return -1;
431
432        final Cursor cursor = getCursor();
433        final int cursorCount = cursor.getCount();
434
435        if (!cursor.moveToFirst()) {
436            return -1;
437        }
438
439        // Only create Uri objects when the actual URI changes
440        Uri currentUri = null;
441        String previousUriString = null;
442        for (int i = 0; i < cursorCount; i++) {
443            String uriString = cursor.getString(URI_COLUMN_INDEX);
444            if (currentUri == null || !uriString.equals(previousUriString)) {
445                currentUri = Uri.parse(uriString);
446            }
447
448            if (ringtoneUri.equals(ContentUris.withAppendedId(currentUri, cursor
449                    .getLong(ID_COLUMN_INDEX)))) {
450                return i;
451            }
452
453            cursor.move(1);
454
455            previousUriString = uriString;
456        }
457
458        return -1;
459    }
460
461    /**
462     * Returns a valid ringtone URI. No guarantees on which it returns. If it
463     * cannot find one, returns null. If it can only find one on external storage and the caller
464     * doesn't have the {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} permission,
465     * returns null.
466     *
467     * @param context The context to use for querying.
468     * @return A ringtone URI, or null if one cannot be found.
469     */
470    public static Uri getValidRingtoneUri(Context context) {
471        final RingtoneManager rm = new RingtoneManager(context);
472
473        Uri uri = getValidRingtoneUriFromCursorAndClose(context, rm.getInternalRingtones());
474
475        if (uri == null) {
476            uri = getValidRingtoneUriFromCursorAndClose(context, rm.getMediaRingtones());
477        }
478
479        return uri;
480    }
481
482    private static Uri getValidRingtoneUriFromCursorAndClose(Context context, Cursor cursor) {
483        if (cursor != null) {
484            Uri uri = null;
485
486            if (cursor.moveToFirst()) {
487                uri = getUriFromCursor(cursor);
488            }
489            cursor.close();
490
491            return uri;
492        } else {
493            return null;
494        }
495    }
496
497    private Cursor getInternalRingtones() {
498        return query(
499                MediaStore.Audio.Media.INTERNAL_CONTENT_URI, INTERNAL_COLUMNS,
500                constructBooleanTrueWhereClause(mFilterColumns),
501                null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
502    }
503
504    private Cursor getMediaRingtones() {
505        if (PackageManager.PERMISSION_GRANTED != mContext.checkPermission(
506                android.Manifest.permission.READ_EXTERNAL_STORAGE,
507                Process.myPid(), Process.myUid())) {
508            Log.w(TAG, "No READ_EXTERNAL_STORAGE permission, ignoring ringtones on ext storage");
509            return null;
510        }
511         // Get the external media cursor. First check to see if it is mounted.
512        final String status = Environment.getExternalStorageState();
513
514        return (status.equals(Environment.MEDIA_MOUNTED) ||
515                    status.equals(Environment.MEDIA_MOUNTED_READ_ONLY))
516                ? query(
517                    MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
518                    constructBooleanTrueWhereClause(mFilterColumns), null,
519                    MediaStore.Audio.Media.DEFAULT_SORT_ORDER)
520                : null;
521    }
522
523    private void setFilterColumnsList(int type) {
524        List<String> columns = mFilterColumns;
525        columns.clear();
526
527        if ((type & TYPE_RINGTONE) != 0) {
528            columns.add(MediaStore.Audio.AudioColumns.IS_RINGTONE);
529        }
530
531        if ((type & TYPE_NOTIFICATION) != 0) {
532            columns.add(MediaStore.Audio.AudioColumns.IS_NOTIFICATION);
533        }
534
535        if ((type & TYPE_ALARM) != 0) {
536            columns.add(MediaStore.Audio.AudioColumns.IS_ALARM);
537        }
538    }
539
540    /**
541     * Constructs a where clause that consists of at least one column being 1
542     * (true). This is used to find all matching sounds for the given sound
543     * types (ringtone, notifications, etc.)
544     *
545     * @param columns The columns that must be true.
546     * @return The where clause.
547     */
548    private static String constructBooleanTrueWhereClause(List<String> columns) {
549
550        if (columns == null) return null;
551
552        StringBuilder sb = new StringBuilder();
553        sb.append("(");
554
555        for (int i = columns.size() - 1; i >= 0; i--) {
556            sb.append(columns.get(i)).append("=1 or ");
557        }
558
559        if (columns.size() > 0) {
560            // Remove last ' or '
561            sb.setLength(sb.length() - 4);
562        }
563
564        sb.append(")");
565
566        return sb.toString();
567    }
568
569    private Cursor query(Uri uri,
570            String[] projection,
571            String selection,
572            String[] selectionArgs,
573            String sortOrder) {
574        if (mActivity != null) {
575            return mActivity.managedQuery(uri, projection, selection, selectionArgs, sortOrder);
576        } else {
577            return mContext.getContentResolver().query(uri, projection, selection, selectionArgs,
578                    sortOrder);
579        }
580    }
581
582    /**
583     * Returns a {@link Ringtone} for a given sound URI.
584     * <p>
585     * If the given URI cannot be opened for any reason, this method will
586     * attempt to fallback on another sound. If it cannot find any, it will
587     * return null.
588     *
589     * @param context A context used to query.
590     * @param ringtoneUri The {@link Uri} of a sound or ringtone.
591     * @return A {@link Ringtone} for the given URI, or null.
592     */
593    public static Ringtone getRingtone(final Context context, Uri ringtoneUri) {
594        // Don't set the stream type
595        return getRingtone(context, ringtoneUri, -1);
596    }
597
598    /**
599     * Returns a {@link Ringtone} for a given sound URI on the given stream
600     * type. Normally, if you change the stream type on the returned
601     * {@link Ringtone}, it will re-create the {@link MediaPlayer}. This is just
602     * an optimized route to avoid that.
603     *
604     * @param streamType The stream type for the ringtone, or -1 if it should
605     *            not be set (and the default used instead).
606     * @see #getRingtone(Context, Uri)
607     */
608    private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) {
609        try {
610            final Ringtone r = new Ringtone(context, true);
611            if (streamType >= 0) {
612                r.setStreamType(streamType);
613            }
614            r.setUri(ringtoneUri);
615            return r;
616        } catch (Exception ex) {
617            Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
618        }
619
620        return null;
621    }
622
623    /**
624     * Gets the current default sound's {@link Uri}. This will give the actual
625     * sound {@link Uri}, instead of using this, most clients can use
626     * {@link System#DEFAULT_RINGTONE_URI}.
627     *
628     * @param context A context used for querying.
629     * @param type The type whose default sound should be returned. One of
630     *            {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
631     *            {@link #TYPE_ALARM}.
632     * @return A {@link Uri} pointing to the default sound for the sound type.
633     * @see #setActualDefaultRingtoneUri(Context, int, Uri)
634     */
635    public static Uri getActualDefaultRingtoneUri(Context context, int type) {
636        String setting = getSettingForType(type);
637        if (setting == null) return null;
638        final String uriString = Settings.System.getString(context.getContentResolver(), setting);
639        return uriString != null ? Uri.parse(uriString) : null;
640    }
641
642    /**
643     * Sets the {@link Uri} of the default sound for a given sound type.
644     *
645     * @param context A context used for querying.
646     * @param type The type whose default sound should be set. One of
647     *            {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
648     *            {@link #TYPE_ALARM}.
649     * @param ringtoneUri A {@link Uri} pointing to the default sound to set.
650     * @see #getActualDefaultRingtoneUri(Context, int)
651     */
652    public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
653        String setting = getSettingForType(type);
654        if (setting == null) return;
655        Settings.System.putString(context.getContentResolver(), setting,
656                ringtoneUri != null ? ringtoneUri.toString() : null);
657    }
658
659    private static String getSettingForType(int type) {
660        if ((type & TYPE_RINGTONE) != 0) {
661            return Settings.System.RINGTONE;
662        } else if ((type & TYPE_NOTIFICATION) != 0) {
663            return Settings.System.NOTIFICATION_SOUND;
664        } else if ((type & TYPE_ALARM) != 0) {
665            return Settings.System.ALARM_ALERT;
666        } else {
667            return null;
668        }
669    }
670
671    /**
672     * Returns whether the given {@link Uri} is one of the default ringtones.
673     *
674     * @param ringtoneUri The ringtone {@link Uri} to be checked.
675     * @return Whether the {@link Uri} is a default.
676     */
677    public static boolean isDefault(Uri ringtoneUri) {
678        return getDefaultType(ringtoneUri) != -1;
679    }
680
681    /**
682     * Returns the type of a default {@link Uri}.
683     *
684     * @param defaultRingtoneUri The default {@link Uri}. For example,
685     *            {@link System#DEFAULT_RINGTONE_URI},
686     *            {@link System#DEFAULT_NOTIFICATION_URI}, or
687     *            {@link System#DEFAULT_ALARM_ALERT_URI}.
688     * @return The type of the defaultRingtoneUri, or -1.
689     */
690    public static int getDefaultType(Uri defaultRingtoneUri) {
691        if (defaultRingtoneUri == null) {
692            return -1;
693        } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI)) {
694            return TYPE_RINGTONE;
695        } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_NOTIFICATION_URI)) {
696            return TYPE_NOTIFICATION;
697        } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_ALARM_ALERT_URI)) {
698            return TYPE_ALARM;
699        } else {
700            return -1;
701        }
702    }
703
704    /**
705     * Returns the {@link Uri} for the default ringtone of a particular type.
706     * Rather than returning the actual ringtone's sound {@link Uri}, this will
707     * return the symbolic {@link Uri} which will resolved to the actual sound
708     * when played.
709     *
710     * @param type The ringtone type whose default should be returned.
711     * @return The {@link Uri} of the default ringtone for the given type.
712     */
713    public static Uri getDefaultUri(int type) {
714        if ((type & TYPE_RINGTONE) != 0) {
715            return Settings.System.DEFAULT_RINGTONE_URI;
716        } else if ((type & TYPE_NOTIFICATION) != 0) {
717            return Settings.System.DEFAULT_NOTIFICATION_URI;
718        } else if ((type & TYPE_ALARM) != 0) {
719            return Settings.System.DEFAULT_ALARM_ALERT_URI;
720        } else {
721            return null;
722        }
723    }
724
725}
726