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