• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.media;
18 
19 import com.android.internal.database.SortCursor;
20 
21 import android.annotation.SdkConstant;
22 import android.annotation.SdkConstant.SdkConstantType;
23 import android.app.Activity;
24 import android.content.ContentUris;
25 import android.content.Context;
26 import android.content.res.AssetFileDescriptor;
27 import android.database.Cursor;
28 import android.net.Uri;
29 import android.os.Environment;
30 import android.provider.DrmStore;
31 import android.provider.MediaStore;
32 import android.provider.Settings;
33 import android.provider.Settings.System;
34 import android.util.Log;
35 
36 import java.util.ArrayList;
37 import 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  */
52 public 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      */
RingtoneManager(Activity activity)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      */
RingtoneManager(Context context)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      */
setType(int type)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      */
inferStreamType()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      */
setStopPreviousRingtone(boolean stopPreviousRingtone)306     public void setStopPreviousRingtone(boolean stopPreviousRingtone) {
307         mStopPreviousRingtone = stopPreviousRingtone;
308     }
309 
310     /**
311      * @see #setStopPreviousRingtone(boolean)
312      */
getStopPreviousRingtone()313     public boolean getStopPreviousRingtone() {
314         return mStopPreviousRingtone;
315     }
316 
317     /**
318      * Stops playing the last {@link Ringtone} retrieved from this.
319      */
stopPreviousRingtone()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      */
getIncludeDrm()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      */
setIncludeDrm(boolean includeDrm)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      */
getCursor()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      */
getRingtone(int position)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      */
getRingtoneUri(int position)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 
getUriFromCursor(Cursor cursor)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      */
getRingtonePosition(Uri ringtoneUri)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      */
getValidRingtoneUri(Context context)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 
getValidRingtoneUriFromCursorAndClose(Context context, Cursor cursor)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 
getInternalRingtones()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 
getDrmRingtones()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 
getMediaRingtones()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 
setFilterColumnsList(int type)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      */
constructBooleanTrueWhereClause(List<String> columns, boolean includeDrm)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 
query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)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      */
getRingtone(final Context context, Uri ringtoneUri)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      */
getRingtone(final Context context, Uri ringtoneUri, int streamType)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      */
getActualDefaultRingtoneUri(Context context, int type)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      */
setActualDefaultRingtoneUri(Context context, int type, Uri ringtoneUri)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 
getSettingForType(int type)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      */
isDefault(Uri ringtoneUri)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      */
getDefaultType(Uri defaultRingtoneUri)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      */
getDefaultUri(int type)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