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.res.AssetFileDescriptor;
27import android.database.Cursor;
28import android.net.Uri;
29import android.os.Environment;
30import android.provider.DrmStore;
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     * {@link #EXTRA_RINGTONE_INCLUDE_DRM}.
89     * <p>
90     * Output: {@link #EXTRA_RINGTONE_PICKED_URI}.
91     */
92    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
93    public static final String ACTION_RINGTONE_PICKER = "android.intent.action.RINGTONE_PICKER";
94
95    /**
96     * Given to the ringtone picker as a boolean. Whether to show an item for
97     * "Default".
98     *
99     * @see #ACTION_RINGTONE_PICKER
100     */
101    public static final String EXTRA_RINGTONE_SHOW_DEFAULT =
102            "android.intent.extra.ringtone.SHOW_DEFAULT";
103
104    /**
105     * Given to the ringtone picker as a boolean. Whether to show an item for
106     * "Silent". If the "Silent" item is picked,
107     * {@link #EXTRA_RINGTONE_PICKED_URI} will be null.
108     *
109     * @see #ACTION_RINGTONE_PICKER
110     */
111    public static final String EXTRA_RINGTONE_SHOW_SILENT =
112            "android.intent.extra.ringtone.SHOW_SILENT";
113
114    /**
115     * Given to the ringtone picker as a boolean. Whether to include DRM ringtones.
116     */
117    public static final String EXTRA_RINGTONE_INCLUDE_DRM =
118            "android.intent.extra.ringtone.INCLUDE_DRM";
119
120    /**
121     * Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the
122     * current ringtone, which will be used to show a checkmark next to the item
123     * for this {@link Uri}. If showing an item for "Default" (@see
124     * {@link #EXTRA_RINGTONE_SHOW_DEFAULT}), this can also be one of
125     * {@link System#DEFAULT_RINGTONE_URI},
126     * {@link System#DEFAULT_NOTIFICATION_URI}, or
127     * {@link System#DEFAULT_ALARM_ALERT_URI} to have the "Default" item
128     * checked.
129     *
130     * @see #ACTION_RINGTONE_PICKER
131     */
132    public static final String EXTRA_RINGTONE_EXISTING_URI =
133            "android.intent.extra.ringtone.EXISTING_URI";
134
135    /**
136     * Given to the ringtone picker as a {@link Uri}. The {@link Uri} of the
137     * ringtone to play when the user attempts to preview the "Default"
138     * ringtone. This can be one of {@link System#DEFAULT_RINGTONE_URI},
139     * {@link System#DEFAULT_NOTIFICATION_URI}, or
140     * {@link System#DEFAULT_ALARM_ALERT_URI} to have the "Default" point to
141     * the current sound for the given default sound type. If you are showing a
142     * ringtone picker for some other type of sound, you are free to provide any
143     * {@link Uri} here.
144     */
145    public static final String EXTRA_RINGTONE_DEFAULT_URI =
146            "android.intent.extra.ringtone.DEFAULT_URI";
147
148    /**
149     * Given to the ringtone picker as an int. Specifies which ringtone type(s) should be
150     * shown in the picker. One or more of {@link #TYPE_RINGTONE},
151     * {@link #TYPE_NOTIFICATION}, {@link #TYPE_ALARM}, or {@link #TYPE_ALL}
152     * (bitwise-ored together).
153     */
154    public static final String EXTRA_RINGTONE_TYPE = "android.intent.extra.ringtone.TYPE";
155
156    /**
157     * Given to the ringtone picker as a {@link CharSequence}. The title to
158     * show for the ringtone picker. This has a default value that is suitable
159     * in most cases.
160     */
161    public static final String EXTRA_RINGTONE_TITLE = "android.intent.extra.ringtone.TITLE";
162
163    /**
164     * Returned from the ringtone picker as a {@link Uri}.
165     * <p>
166     * It will be one of:
167     * <li> the picked ringtone,
168     * <li> a {@link Uri} that equals {@link System#DEFAULT_RINGTONE_URI},
169     * {@link System#DEFAULT_NOTIFICATION_URI}, or
170     * {@link System#DEFAULT_ALARM_ALERT_URI} if the default was chosen,
171     * <li> null if the "Silent" item was picked.
172     *
173     * @see #ACTION_RINGTONE_PICKER
174     */
175    public static final String EXTRA_RINGTONE_PICKED_URI =
176            "android.intent.extra.ringtone.PICKED_URI";
177
178    // Make sure the column ordering and then ..._COLUMN_INDEX are in sync
179
180    private static final String[] INTERNAL_COLUMNS = new String[] {
181        MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
182        "\"" + MediaStore.Audio.Media.INTERNAL_CONTENT_URI + "\"",
183        MediaStore.Audio.Media.TITLE_KEY
184    };
185
186    private static final String[] DRM_COLUMNS = new String[] {
187        DrmStore.Audio._ID, DrmStore.Audio.TITLE,
188        "\"" + DrmStore.Audio.CONTENT_URI + "\"",
189        DrmStore.Audio.TITLE + " AS " + MediaStore.Audio.Media.TITLE_KEY
190    };
191
192    private static final String[] MEDIA_COLUMNS = new String[] {
193        MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
194        "\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "\"",
195        MediaStore.Audio.Media.TITLE_KEY
196    };
197
198    /**
199     * The column index (in the cursor returned by {@link #getCursor()} for the
200     * row ID.
201     */
202    public static final int ID_COLUMN_INDEX = 0;
203
204    /**
205     * The column index (in the cursor returned by {@link #getCursor()} for the
206     * title.
207     */
208    public static final int TITLE_COLUMN_INDEX = 1;
209
210    /**
211     * The column index (in the cursor returned by {@link #getCursor()} for the
212     * media provider's URI.
213     */
214    public static final int URI_COLUMN_INDEX = 2;
215
216    private Activity mActivity;
217    private Context mContext;
218
219    private Cursor mCursor;
220
221    private int mType = TYPE_RINGTONE;
222
223    /**
224     * If a column (item from this list) exists in the Cursor, its value must
225     * be true (value of 1) for the row to be returned.
226     */
227    private final List<String> mFilterColumns = new ArrayList<String>();
228
229    private boolean mStopPreviousRingtone = true;
230    private Ringtone mPreviousRingtone;
231
232    private boolean mIncludeDrm;
233
234    /**
235     * Constructs a RingtoneManager. This constructor is recommended as its
236     * constructed instance manages cursor(s).
237     *
238     * @param activity The activity used to get a managed cursor.
239     */
240    public RingtoneManager(Activity activity) {
241        mContext = mActivity = activity;
242        setType(mType);
243    }
244
245    /**
246     * Constructs a RingtoneManager. The instance constructed by this
247     * constructor will not manage the cursor(s), so the client should handle
248     * this itself.
249     *
250     * @param context The context to used to get a cursor.
251     */
252    public RingtoneManager(Context context) {
253        mContext = context;
254        setType(mType);
255    }
256
257    /**
258     * Sets which type(s) of ringtones will be listed by this.
259     *
260     * @param type The type(s), one or more of {@link #TYPE_RINGTONE},
261     *            {@link #TYPE_NOTIFICATION}, {@link #TYPE_ALARM},
262     *            {@link #TYPE_ALL}.
263     * @see #EXTRA_RINGTONE_TYPE
264     */
265    public void setType(int type) {
266
267        if (mCursor != null) {
268            throw new IllegalStateException(
269                    "Setting filter columns should be done before querying for ringtones.");
270        }
271
272        mType = type;
273        setFilterColumnsList(type);
274    }
275
276    /**
277     * Infers the playback stream type based on what type of ringtones this
278     * manager is returning.
279     *
280     * @return The stream type.
281     */
282    public int inferStreamType() {
283        switch (mType) {
284
285            case TYPE_ALARM:
286                return AudioManager.STREAM_ALARM;
287
288            case TYPE_NOTIFICATION:
289                return AudioManager.STREAM_NOTIFICATION;
290
291            default:
292                return AudioManager.STREAM_RING;
293        }
294    }
295
296    /**
297     * Whether retrieving another {@link Ringtone} will stop playing the
298     * previously retrieved {@link Ringtone}.
299     * <p>
300     * If this is false, make sure to {@link Ringtone#stop()} any previous
301     * ringtones to free resources.
302     *
303     * @param stopPreviousRingtone If true, the previously retrieved
304     *            {@link Ringtone} will be stopped.
305     */
306    public void setStopPreviousRingtone(boolean stopPreviousRingtone) {
307        mStopPreviousRingtone = stopPreviousRingtone;
308    }
309
310    /**
311     * @see #setStopPreviousRingtone(boolean)
312     */
313    public boolean getStopPreviousRingtone() {
314        return mStopPreviousRingtone;
315    }
316
317    /**
318     * Stops playing the last {@link Ringtone} retrieved from this.
319     */
320    public void stopPreviousRingtone() {
321        if (mPreviousRingtone != null) {
322            mPreviousRingtone.stop();
323        }
324    }
325
326    /**
327     * Returns whether DRM ringtones will be included.
328     *
329     * @return Whether DRM ringtones will be included.
330     * @see #setIncludeDrm(boolean)
331     */
332    public boolean getIncludeDrm() {
333        return mIncludeDrm;
334    }
335
336    /**
337     * Sets whether to include DRM ringtones.
338     *
339     * @param includeDrm Whether to include DRM ringtones.
340     */
341    public void setIncludeDrm(boolean includeDrm) {
342        mIncludeDrm = includeDrm;
343    }
344
345    /**
346     * Returns a {@link Cursor} of all the ringtones available. The returned
347     * cursor will be the same cursor returned each time this method is called,
348     * so do not {@link Cursor#close()} the cursor. The cursor can be
349     * {@link Cursor#deactivate()} safely.
350     * <p>
351     * If {@link RingtoneManager#RingtoneManager(Activity)} was not used, the
352     * caller should manage the returned cursor through its activity's life
353     * cycle to prevent leaking the cursor.
354     *
355     * @return A {@link Cursor} of all the ringtones available.
356     * @see #ID_COLUMN_INDEX
357     * @see #TITLE_COLUMN_INDEX
358     * @see #URI_COLUMN_INDEX
359     */
360    public Cursor getCursor() {
361        if (mCursor != null && mCursor.requery()) {
362            return mCursor;
363        }
364
365        final Cursor internalCursor = getInternalRingtones();
366        final Cursor drmCursor = mIncludeDrm ? getDrmRingtones() : null;
367        final Cursor mediaCursor = getMediaRingtones();
368
369        return mCursor = new SortCursor(new Cursor[] { internalCursor, drmCursor, mediaCursor },
370                MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
371    }
372
373    /**
374     * Gets a {@link Ringtone} for the ringtone at the given position in the
375     * {@link Cursor}.
376     *
377     * @param position The position (in the {@link Cursor}) of the ringtone.
378     * @return A {@link Ringtone} pointing to the ringtone.
379     */
380    public Ringtone getRingtone(int position) {
381        if (mStopPreviousRingtone && mPreviousRingtone != null) {
382            mPreviousRingtone.stop();
383        }
384
385        mPreviousRingtone = getRingtone(mContext, getRingtoneUri(position), inferStreamType());
386        return mPreviousRingtone;
387    }
388
389    /**
390     * Gets a {@link Uri} for the ringtone at the given position in the {@link Cursor}.
391     *
392     * @param position The position (in the {@link Cursor}) of the ringtone.
393     * @return A {@link Uri} pointing to the ringtone.
394     */
395    public Uri getRingtoneUri(int position) {
396        // use cursor directly instead of requerying it, which could easily
397        // cause position to shuffle.
398        if (mCursor == null || !mCursor.moveToPosition(position)) {
399            return null;
400        }
401
402        return getUriFromCursor(mCursor);
403    }
404
405    private static Uri getUriFromCursor(Cursor cursor) {
406        return ContentUris.withAppendedId(Uri.parse(cursor.getString(URI_COLUMN_INDEX)), cursor
407                .getLong(ID_COLUMN_INDEX));
408    }
409
410    /**
411     * Gets the position of a {@link Uri} within this {@link RingtoneManager}.
412     *
413     * @param ringtoneUri The {@link Uri} to retreive the position of.
414     * @return The position of the {@link Uri}, or -1 if it cannot be found.
415     */
416    public int getRingtonePosition(Uri ringtoneUri) {
417
418        if (ringtoneUri == null) return -1;
419
420        final Cursor cursor = getCursor();
421        final int cursorCount = cursor.getCount();
422
423        if (!cursor.moveToFirst()) {
424            return -1;
425        }
426
427        // Only create Uri objects when the actual URI changes
428        Uri currentUri = null;
429        String previousUriString = null;
430        for (int i = 0; i < cursorCount; i++) {
431            String uriString = cursor.getString(URI_COLUMN_INDEX);
432            if (currentUri == null || !uriString.equals(previousUriString)) {
433                currentUri = Uri.parse(uriString);
434            }
435
436            if (ringtoneUri.equals(ContentUris.withAppendedId(currentUri, cursor
437                    .getLong(ID_COLUMN_INDEX)))) {
438                return i;
439            }
440
441            cursor.move(1);
442
443            previousUriString = uriString;
444        }
445
446        return -1;
447    }
448
449    /**
450     * Returns a valid ringtone URI. No guarantees on which it returns. If it
451     * cannot find one, returns null.
452     *
453     * @param context The context to use for querying.
454     * @return A ringtone URI, or null if one cannot be found.
455     */
456    public static Uri getValidRingtoneUri(Context context) {
457        final RingtoneManager rm = new RingtoneManager(context);
458
459        Uri uri = getValidRingtoneUriFromCursorAndClose(context, rm.getInternalRingtones());
460
461        if (uri == null) {
462            uri = getValidRingtoneUriFromCursorAndClose(context, rm.getMediaRingtones());
463        }
464
465        if (uri == null) {
466            uri = getValidRingtoneUriFromCursorAndClose(context, rm.getDrmRingtones());
467        }
468
469        return uri;
470    }
471
472    private static Uri getValidRingtoneUriFromCursorAndClose(Context context, Cursor cursor) {
473        if (cursor != null) {
474            Uri uri = null;
475
476            if (cursor.moveToFirst()) {
477                uri = getUriFromCursor(cursor);
478            }
479            cursor.close();
480
481            return uri;
482        } else {
483            return null;
484        }
485    }
486
487    private Cursor getInternalRingtones() {
488        return query(
489                MediaStore.Audio.Media.INTERNAL_CONTENT_URI, INTERNAL_COLUMNS,
490                constructBooleanTrueWhereClause(mFilterColumns, mIncludeDrm),
491                null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
492    }
493
494    private Cursor getDrmRingtones() {
495        // DRM store does not have any columns to use for filtering
496        return query(
497                DrmStore.Audio.CONTENT_URI, DRM_COLUMNS,
498                null, null, DrmStore.Audio.TITLE);
499    }
500
501    private Cursor getMediaRingtones() {
502         // Get the external media cursor. First check to see if it is mounted.
503        final String status = Environment.getExternalStorageState();
504
505        return (status.equals(Environment.MEDIA_MOUNTED) ||
506                    status.equals(Environment.MEDIA_MOUNTED_READ_ONLY))
507                ? query(
508                    MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
509                    constructBooleanTrueWhereClause(mFilterColumns, mIncludeDrm), null,
510                    MediaStore.Audio.Media.DEFAULT_SORT_ORDER)
511                : null;
512    }
513
514    private void setFilterColumnsList(int type) {
515        List<String> columns = mFilterColumns;
516        columns.clear();
517
518        if ((type & TYPE_RINGTONE) != 0) {
519            columns.add(MediaStore.Audio.AudioColumns.IS_RINGTONE);
520        }
521
522        if ((type & TYPE_NOTIFICATION) != 0) {
523            columns.add(MediaStore.Audio.AudioColumns.IS_NOTIFICATION);
524        }
525
526        if ((type & TYPE_ALARM) != 0) {
527            columns.add(MediaStore.Audio.AudioColumns.IS_ALARM);
528        }
529    }
530
531    /**
532     * Constructs a where clause that consists of at least one column being 1
533     * (true). This is used to find all matching sounds for the given sound
534     * types (ringtone, notifications, etc.)
535     *
536     * @param columns The columns that must be true.
537     * @return The where clause.
538     */
539    private static String constructBooleanTrueWhereClause(List<String> columns, boolean includeDrm) {
540
541        if (columns == null) return null;
542
543        StringBuilder sb = new StringBuilder();
544        sb.append("(");
545
546        for (int i = columns.size() - 1; i >= 0; i--) {
547            sb.append(columns.get(i)).append("=1 or ");
548        }
549
550        if (columns.size() > 0) {
551            // Remove last ' or '
552            sb.setLength(sb.length() - 4);
553        }
554
555        sb.append(")");
556
557        if (!includeDrm) {
558            // If not DRM files should be shown, the where clause
559            // will be something like "(is_notification=1) and is_drm=0"
560            sb.append(" and ");
561            sb.append(MediaStore.MediaColumns.IS_DRM);
562            sb.append("=0");
563        }
564
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