RingtoneManager.java revision 853ad6fbe34fa26e81e4b7325309a034d7a1b038
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.MediaStore;
31import android.provider.Settings;
32import android.provider.Settings.System;
33import android.util.Log;
34
35import java.util.ArrayList;
36import java.util.List;
37
38/**
39 * RingtoneManager provides access to ringtones, notification, and other types
40 * of sounds. It manages querying the different media providers and combines the
41 * results into a single cursor. It also provides a {@link Ringtone} for each
42 * ringtone. We generically call these sounds ringtones, however the
43 * {@link #TYPE_RINGTONE} refers to the type of sounds that are suitable for the
44 * phone ringer.
45 * <p>
46 * To show a ringtone picker to the user, use the
47 * {@link #ACTION_RINGTONE_PICKER} intent to launch the picker as a subactivity.
48 *
49 * @see Ringtone
50 */
51public class RingtoneManager {
52
53    private static final String TAG = "RingtoneManager";
54
55    // Make sure these are in sync with attrs.xml:
56    // <attr name="ringtoneType">
57
58    /**
59     * Type that refers to sounds that are used for the phone ringer.
60     */
61    public static final int TYPE_RINGTONE = 1;
62
63    /**
64     * Type that refers to sounds that are used for notifications.
65     */
66    public static final int TYPE_NOTIFICATION = 2;
67
68    /**
69     * Type that refers to sounds that are used for the alarm.
70     */
71    public static final int TYPE_ALARM = 4;
72
73    /**
74     * All types of sounds.
75     */
76    public static final int TYPE_ALL = TYPE_RINGTONE | TYPE_NOTIFICATION | TYPE_ALARM;
77
78    // </attr>
79
80    /**
81     * Activity Action: Shows a ringtone picker.
82     * <p>
83     * Input: {@link #EXTRA_RINGTONE_EXISTING_URI},
84     * {@link #EXTRA_RINGTONE_SHOW_DEFAULT},
85     * {@link #EXTRA_RINGTONE_SHOW_SILENT}, {@link #EXTRA_RINGTONE_TYPE},
86     * {@link #EXTRA_RINGTONE_DEFAULT_URI}, {@link #EXTRA_RINGTONE_TITLE},
87     * <p>
88     * Output: {@link #EXTRA_RINGTONE_PICKED_URI}.
89     */
90    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
91    public static final String ACTION_RINGTONE_PICKER = "android.intent.action.RINGTONE_PICKER";
92
93    /**
94     * Given to the ringtone picker as a boolean. Whether to show an item for
95     * "Default".
96     *
97     * @see #ACTION_RINGTONE_PICKER
98     */
99    public static final String EXTRA_RINGTONE_SHOW_DEFAULT =
100            "android.intent.extra.ringtone.SHOW_DEFAULT";
101
102    /**
103     * Given to the ringtone picker as a boolean. Whether to show an item for
104     * "Silent". If the "Silent" item is picked,
105     * {@link #EXTRA_RINGTONE_PICKED_URI} will be null.
106     *
107     * @see #ACTION_RINGTONE_PICKER
108     */
109    public static final String EXTRA_RINGTONE_SHOW_SILENT =
110            "android.intent.extra.ringtone.SHOW_SILENT";
111
112    /**
113     * Given to the ringtone picker as a boolean. Whether to include DRM ringtones.
114     * Obsolete - does nothing now
115     * @deprecated
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     * Returned from the ringtone picker as a {@link Uri}.
166     * <p>
167     * It will be one of:
168     * <li> the picked ringtone,
169     * <li> a {@link Uri} that equals {@link System#DEFAULT_RINGTONE_URI},
170     * {@link System#DEFAULT_NOTIFICATION_URI}, or
171     * {@link System#DEFAULT_ALARM_ALERT_URI} if the default was chosen,
172     * <li> null if the "Silent" item was picked.
173     *
174     * @see #ACTION_RINGTONE_PICKER
175     */
176    public static final String EXTRA_RINGTONE_PICKED_URI =
177            "android.intent.extra.ringtone.PICKED_URI";
178
179    // Make sure the column ordering and then ..._COLUMN_INDEX are in sync
180
181    private static final String[] INTERNAL_COLUMNS = new String[] {
182        MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
183        "\"" + MediaStore.Audio.Media.INTERNAL_CONTENT_URI + "\"",
184        MediaStore.Audio.Media.TITLE_KEY
185    };
186
187    private static final String[] MEDIA_COLUMNS = new String[] {
188        MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE,
189        "\"" + MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "\"",
190        MediaStore.Audio.Media.TITLE_KEY
191    };
192
193    /**
194     * The column index (in the cursor returned by {@link #getCursor()} for the
195     * row ID.
196     */
197    public static final int ID_COLUMN_INDEX = 0;
198
199    /**
200     * The column index (in the cursor returned by {@link #getCursor()} for the
201     * title.
202     */
203    public static final int TITLE_COLUMN_INDEX = 1;
204
205    /**
206     * The column index (in the cursor returned by {@link #getCursor()} for the
207     * media provider's URI.
208     */
209    public static final int URI_COLUMN_INDEX = 2;
210
211    private Activity mActivity;
212    private Context mContext;
213
214    private Cursor mCursor;
215
216    private int mType = TYPE_RINGTONE;
217
218    /**
219     * If a column (item from this list) exists in the Cursor, its value must
220     * be true (value of 1) for the row to be returned.
221     */
222    private final List<String> mFilterColumns = new ArrayList<String>();
223
224    private boolean mStopPreviousRingtone = true;
225    private Ringtone mPreviousRingtone;
226
227    /**
228     * Constructs a RingtoneManager. This constructor is recommended as its
229     * constructed instance manages cursor(s).
230     *
231     * @param activity The activity used to get a managed cursor.
232     */
233    public RingtoneManager(Activity activity) {
234        mContext = mActivity = activity;
235        setType(mType);
236    }
237
238    /**
239     * Constructs a RingtoneManager. The instance constructed by this
240     * constructor will not manage the cursor(s), so the client should handle
241     * this itself.
242     *
243     * @param context The context to used to get a cursor.
244     */
245    public RingtoneManager(Context context) {
246        mContext = context;
247        setType(mType);
248    }
249
250    /**
251     * Sets which type(s) of ringtones will be listed by this.
252     *
253     * @param type The type(s), one or more of {@link #TYPE_RINGTONE},
254     *            {@link #TYPE_NOTIFICATION}, {@link #TYPE_ALARM},
255     *            {@link #TYPE_ALL}.
256     * @see #EXTRA_RINGTONE_TYPE
257     */
258    public void setType(int type) {
259
260        if (mCursor != null) {
261            throw new IllegalStateException(
262                    "Setting filter columns should be done before querying for ringtones.");
263        }
264
265        mType = type;
266        setFilterColumnsList(type);
267    }
268
269    /**
270     * Infers the playback stream type based on what type of ringtones this
271     * manager is returning.
272     *
273     * @return The stream type.
274     */
275    public int inferStreamType() {
276        switch (mType) {
277
278            case TYPE_ALARM:
279                return AudioManager.STREAM_ALARM;
280
281            case TYPE_NOTIFICATION:
282                return AudioManager.STREAM_NOTIFICATION;
283
284            default:
285                return AudioManager.STREAM_RING;
286        }
287    }
288
289    /**
290     * Whether retrieving another {@link Ringtone} will stop playing the
291     * previously retrieved {@link Ringtone}.
292     * <p>
293     * If this is false, make sure to {@link Ringtone#stop()} any previous
294     * ringtones to free resources.
295     *
296     * @param stopPreviousRingtone If true, the previously retrieved
297     *            {@link Ringtone} will be stopped.
298     */
299    public void setStopPreviousRingtone(boolean stopPreviousRingtone) {
300        mStopPreviousRingtone = stopPreviousRingtone;
301    }
302
303    /**
304     * @see #setStopPreviousRingtone(boolean)
305     */
306    public boolean getStopPreviousRingtone() {
307        return mStopPreviousRingtone;
308    }
309
310    /**
311     * Stops playing the last {@link Ringtone} retrieved from this.
312     */
313    public void stopPreviousRingtone() {
314        if (mPreviousRingtone != null) {
315            mPreviousRingtone.stop();
316        }
317    }
318
319    /**
320     * Returns whether DRM ringtones will be included.
321     *
322     * @return Whether DRM ringtones will be included.
323     * @see #setIncludeDrm(boolean)
324     * Obsolete - always returns false
325     * @deprecated
326     */
327    @Deprecated
328    public boolean getIncludeDrm() {
329        return false;
330    }
331
332    /**
333     * Sets whether to include DRM ringtones.
334     *
335     * @param includeDrm Whether to include DRM ringtones.
336     * Obsolete - no longer does anything
337     * @deprecated
338     */
339    @Deprecated
340    public void setIncludeDrm(boolean includeDrm) {
341        if (includeDrm) {
342            Log.w(TAG, "setIncludeDrm no longer supported");
343        }
344    }
345
346    /**
347     * Returns a {@link Cursor} of all the ringtones available. The returned
348     * cursor will be the same cursor returned each time this method is called,
349     * so do not {@link Cursor#close()} the cursor. The cursor can be
350     * {@link Cursor#deactivate()} safely.
351     * <p>
352     * If {@link RingtoneManager#RingtoneManager(Activity)} was not used, the
353     * caller should manage the returned cursor through its activity's life
354     * cycle to prevent leaking the cursor.
355     *
356     * @return A {@link Cursor} of all the ringtones available.
357     * @see #ID_COLUMN_INDEX
358     * @see #TITLE_COLUMN_INDEX
359     * @see #URI_COLUMN_INDEX
360     */
361    public Cursor getCursor() {
362        if (mCursor != null && mCursor.requery()) {
363            return mCursor;
364        }
365
366        final Cursor internalCursor = getInternalRingtones();
367        final Cursor mediaCursor = getMediaRingtones();
368
369        return mCursor = new SortCursor(new Cursor[] { internalCursor, 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        return uri;
466    }
467
468    private static Uri getValidRingtoneUriFromCursorAndClose(Context context, Cursor cursor) {
469        if (cursor != null) {
470            Uri uri = null;
471
472            if (cursor.moveToFirst()) {
473                uri = getUriFromCursor(cursor);
474            }
475            cursor.close();
476
477            return uri;
478        } else {
479            return null;
480        }
481    }
482
483    private Cursor getInternalRingtones() {
484        return query(
485                MediaStore.Audio.Media.INTERNAL_CONTENT_URI, INTERNAL_COLUMNS,
486                constructBooleanTrueWhereClause(mFilterColumns),
487                null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
488    }
489
490    private Cursor getMediaRingtones() {
491         // Get the external media cursor. First check to see if it is mounted.
492        final String status = Environment.getExternalStorageState();
493
494        return (status.equals(Environment.MEDIA_MOUNTED) ||
495                    status.equals(Environment.MEDIA_MOUNTED_READ_ONLY))
496                ? query(
497                    MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, MEDIA_COLUMNS,
498                    constructBooleanTrueWhereClause(mFilterColumns), null,
499                    MediaStore.Audio.Media.DEFAULT_SORT_ORDER)
500                : null;
501    }
502
503    private void setFilterColumnsList(int type) {
504        List<String> columns = mFilterColumns;
505        columns.clear();
506
507        if ((type & TYPE_RINGTONE) != 0) {
508            columns.add(MediaStore.Audio.AudioColumns.IS_RINGTONE);
509        }
510
511        if ((type & TYPE_NOTIFICATION) != 0) {
512            columns.add(MediaStore.Audio.AudioColumns.IS_NOTIFICATION);
513        }
514
515        if ((type & TYPE_ALARM) != 0) {
516            columns.add(MediaStore.Audio.AudioColumns.IS_ALARM);
517        }
518    }
519
520    /**
521     * Constructs a where clause that consists of at least one column being 1
522     * (true). This is used to find all matching sounds for the given sound
523     * types (ringtone, notifications, etc.)
524     *
525     * @param columns The columns that must be true.
526     * @return The where clause.
527     */
528    private static String constructBooleanTrueWhereClause(List<String> columns) {
529
530        if (columns == null) return null;
531
532        StringBuilder sb = new StringBuilder();
533        sb.append("(");
534
535        for (int i = columns.size() - 1; i >= 0; i--) {
536            sb.append(columns.get(i)).append("=1 or ");
537        }
538
539        if (columns.size() > 0) {
540            // Remove last ' or '
541            sb.setLength(sb.length() - 4);
542        }
543
544        sb.append(")");
545
546        return sb.toString();
547    }
548
549    private Cursor query(Uri uri,
550            String[] projection,
551            String selection,
552            String[] selectionArgs,
553            String sortOrder) {
554        if (mActivity != null) {
555            return mActivity.managedQuery(uri, projection, selection, selectionArgs, sortOrder);
556        } else {
557            return mContext.getContentResolver().query(uri, projection, selection, selectionArgs,
558                    sortOrder);
559        }
560    }
561
562    /**
563     * Returns a {@link Ringtone} for a given sound URI.
564     * <p>
565     * If the given URI cannot be opened for any reason, this method will
566     * attempt to fallback on another sound. If it cannot find any, it will
567     * return null.
568     *
569     * @param context A context used to query.
570     * @param ringtoneUri The {@link Uri} of a sound or ringtone.
571     * @return A {@link Ringtone} for the given URI, or null.
572     */
573    public static Ringtone getRingtone(final Context context, Uri ringtoneUri) {
574        // Don't set the stream type
575        return getRingtone(context, ringtoneUri, -1);
576    }
577
578    /**
579     * Returns a {@link Ringtone} for a given sound URI on the given stream
580     * type. Normally, if you change the stream type on the returned
581     * {@link Ringtone}, it will re-create the {@link MediaPlayer}. This is just
582     * an optimized route to avoid that.
583     *
584     * @param streamType The stream type for the ringtone, or -1 if it should
585     *            not be set (and the default used instead).
586     * @see #getRingtone(Context, Uri)
587     */
588    private static Ringtone getRingtone(final Context context, Uri ringtoneUri, int streamType) {
589        try {
590            final Ringtone r = new Ringtone(context, true);
591            if (streamType >= 0) {
592                r.setStreamType(streamType);
593            }
594            r.setUri(ringtoneUri);
595            return r;
596        } catch (Exception ex) {
597            Log.e(TAG, "Failed to open ringtone " + ringtoneUri + ": " + ex);
598        }
599
600        return null;
601    }
602
603    /**
604     * Gets the current default sound's {@link Uri}. This will give the actual
605     * sound {@link Uri}, instead of using this, most clients can use
606     * {@link System#DEFAULT_RINGTONE_URI}.
607     *
608     * @param context A context used for querying.
609     * @param type The type whose default sound should be returned. One of
610     *            {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
611     *            {@link #TYPE_ALARM}.
612     * @return A {@link Uri} pointing to the default sound for the sound type.
613     * @see #setActualDefaultRingtoneUri(Context, int, Uri)
614     */
615    public static Uri getActualDefaultRingtoneUri(Context context, int type) {
616        String setting = getSettingForType(type);
617        if (setting == null) return null;
618        final String uriString = Settings.System.getString(context.getContentResolver(), setting);
619        return uriString != null ? Uri.parse(uriString) : null;
620    }
621
622    /**
623     * Sets the {@link Uri} of the default sound for a given sound type.
624     *
625     * @param context A context used for querying.
626     * @param type The type whose default sound should be set. One of
627     *            {@link #TYPE_RINGTONE}, {@link #TYPE_NOTIFICATION}, or
628     *            {@link #TYPE_ALARM}.
629     * @param ringtoneUri A {@link Uri} pointing to the default sound to set.
630     * @see #getActualDefaultRingtoneUri(Context, int)
631     */
632    public static void setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri) {
633        String setting = getSettingForType(type);
634        if (setting == null) return;
635        Settings.System.putString(context.getContentResolver(), setting,
636                ringtoneUri != null ? ringtoneUri.toString() : null);
637    }
638
639    private static String getSettingForType(int type) {
640        if ((type & TYPE_RINGTONE) != 0) {
641            return Settings.System.RINGTONE;
642        } else if ((type & TYPE_NOTIFICATION) != 0) {
643            return Settings.System.NOTIFICATION_SOUND;
644        } else if ((type & TYPE_ALARM) != 0) {
645            return Settings.System.ALARM_ALERT;
646        } else {
647            return null;
648        }
649    }
650
651    /**
652     * Returns whether the given {@link Uri} is one of the default ringtones.
653     *
654     * @param ringtoneUri The ringtone {@link Uri} to be checked.
655     * @return Whether the {@link Uri} is a default.
656     */
657    public static boolean isDefault(Uri ringtoneUri) {
658        return getDefaultType(ringtoneUri) != -1;
659    }
660
661    /**
662     * Returns the type of a default {@link Uri}.
663     *
664     * @param defaultRingtoneUri The default {@link Uri}. For example,
665     *            {@link System#DEFAULT_RINGTONE_URI},
666     *            {@link System#DEFAULT_NOTIFICATION_URI}, or
667     *            {@link System#DEFAULT_ALARM_ALERT_URI}.
668     * @return The type of the defaultRingtoneUri, or -1.
669     */
670    public static int getDefaultType(Uri defaultRingtoneUri) {
671        if (defaultRingtoneUri == null) {
672            return -1;
673        } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_RINGTONE_URI)) {
674            return TYPE_RINGTONE;
675        } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_NOTIFICATION_URI)) {
676            return TYPE_NOTIFICATION;
677        } else if (defaultRingtoneUri.equals(Settings.System.DEFAULT_ALARM_ALERT_URI)) {
678            return TYPE_ALARM;
679        } else {
680            return -1;
681        }
682    }
683
684    /**
685     * Returns the {@link Uri} for the default ringtone of a particular type.
686     * Rather than returning the actual ringtone's sound {@link Uri}, this will
687     * return the symbolic {@link Uri} which will resolved to the actual sound
688     * when played.
689     *
690     * @param type The ringtone type whose default should be returned.
691     * @return The {@link Uri} of the default ringtone for the given type.
692     */
693    public static Uri getDefaultUri(int type) {
694        if ((type & TYPE_RINGTONE) != 0) {
695            return Settings.System.DEFAULT_RINGTONE_URI;
696        } else if ((type & TYPE_NOTIFICATION) != 0) {
697            return Settings.System.DEFAULT_NOTIFICATION_URI;
698        } else if ((type & TYPE_ALARM) != 0) {
699            return Settings.System.DEFAULT_ALARM_ALERT_URI;
700        } else {
701            return null;
702        }
703    }
704
705}
706