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.preference;
18 
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.res.TypedArray;
22 import android.media.RingtoneManager;
23 import android.net.Uri;
24 import android.provider.Settings.System;
25 import android.text.TextUtils;
26 import android.util.AttributeSet;
27 
28 /**
29  * A {@link Preference} that allows the user to choose a ringtone from those on the device.
30  * The chosen ringtone's URI will be persisted as a string.
31  * <p>
32  * If the user chooses the "Default" item, the saved string will be one of
33  * {@link System#DEFAULT_RINGTONE_URI},
34  * {@link System#DEFAULT_NOTIFICATION_URI}, or
35  * {@link System#DEFAULT_ALARM_ALERT_URI}. If the user chooses the "Silent"
36  * item, the saved string will be an empty string.
37  *
38  * @attr ref android.R.styleable#RingtonePreference_ringtoneType
39  * @attr ref android.R.styleable#RingtonePreference_showDefault
40  * @attr ref android.R.styleable#RingtonePreference_showSilent
41  */
42 public class RingtonePreference extends Preference implements
43         PreferenceManager.OnActivityResultListener {
44 
45     private static final String TAG = "RingtonePreference";
46 
47     private int mRingtoneType;
48     private boolean mShowDefault;
49     private boolean mShowSilent;
50 
51     private int mRequestCode;
52 
RingtonePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)53     public RingtonePreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
54         super(context, attrs, defStyleAttr, defStyleRes);
55 
56         final TypedArray a = context.obtainStyledAttributes(attrs,
57                 com.android.internal.R.styleable.RingtonePreference, defStyleAttr, defStyleRes);
58         mRingtoneType = a.getInt(com.android.internal.R.styleable.RingtonePreference_ringtoneType,
59                 RingtoneManager.TYPE_RINGTONE);
60         mShowDefault = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showDefault,
61                 true);
62         mShowSilent = a.getBoolean(com.android.internal.R.styleable.RingtonePreference_showSilent,
63                 true);
64         a.recycle();
65     }
66 
RingtonePreference(Context context, AttributeSet attrs, int defStyleAttr)67     public RingtonePreference(Context context, AttributeSet attrs, int defStyleAttr) {
68         this(context, attrs, defStyleAttr, 0);
69     }
70 
RingtonePreference(Context context, AttributeSet attrs)71     public RingtonePreference(Context context, AttributeSet attrs) {
72         this(context, attrs, com.android.internal.R.attr.ringtonePreferenceStyle);
73     }
74 
RingtonePreference(Context context)75     public RingtonePreference(Context context) {
76         this(context, null);
77     }
78 
79     /**
80      * Returns the sound type(s) that are shown in the picker.
81      *
82      * @return The sound type(s) that are shown in the picker.
83      * @see #setRingtoneType(int)
84      */
getRingtoneType()85     public int getRingtoneType() {
86         return mRingtoneType;
87     }
88 
89     /**
90      * Sets the sound type(s) that are shown in the picker.
91      *
92      * @param type The sound type(s) that are shown in the picker.
93      * @see RingtoneManager#EXTRA_RINGTONE_TYPE
94      */
setRingtoneType(int type)95     public void setRingtoneType(int type) {
96         mRingtoneType = type;
97     }
98 
99     /**
100      * Returns whether to a show an item for the default sound/ringtone.
101      *
102      * @return Whether to show an item for the default sound/ringtone.
103      */
getShowDefault()104     public boolean getShowDefault() {
105         return mShowDefault;
106     }
107 
108     /**
109      * Sets whether to show an item for the default sound/ringtone. The default
110      * to use will be deduced from the sound type(s) being shown.
111      *
112      * @param showDefault Whether to show the default or not.
113      * @see RingtoneManager#EXTRA_RINGTONE_SHOW_DEFAULT
114      */
setShowDefault(boolean showDefault)115     public void setShowDefault(boolean showDefault) {
116         mShowDefault = showDefault;
117     }
118 
119     /**
120      * Returns whether to a show an item for 'Silent'.
121      *
122      * @return Whether to show an item for 'Silent'.
123      */
getShowSilent()124     public boolean getShowSilent() {
125         return mShowSilent;
126     }
127 
128     /**
129      * Sets whether to show an item for 'Silent'.
130      *
131      * @param showSilent Whether to show 'Silent'.
132      * @see RingtoneManager#EXTRA_RINGTONE_SHOW_SILENT
133      */
setShowSilent(boolean showSilent)134     public void setShowSilent(boolean showSilent) {
135         mShowSilent = showSilent;
136     }
137 
138     @Override
onClick()139     protected void onClick() {
140         // Launch the ringtone picker
141         Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
142         onPrepareRingtonePickerIntent(intent);
143         PreferenceFragment owningFragment = getPreferenceManager().getFragment();
144         if (owningFragment != null) {
145             owningFragment.startActivityForResult(intent, mRequestCode);
146         } else {
147             getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode);
148         }
149     }
150 
151     /**
152      * Prepares the intent to launch the ringtone picker. This can be modified
153      * to adjust the parameters of the ringtone picker.
154      *
155      * @param ringtonePickerIntent The ringtone picker intent that can be
156      *            modified by putting extras.
157      */
onPrepareRingtonePickerIntent(Intent ringtonePickerIntent)158     protected void onPrepareRingtonePickerIntent(Intent ringtonePickerIntent) {
159 
160         ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI,
161                 onRestoreRingtone());
162 
163         ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, mShowDefault);
164         if (mShowDefault) {
165             ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI,
166                     RingtoneManager.getDefaultUri(getRingtoneType()));
167         }
168 
169         ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, mShowSilent);
170         ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, mRingtoneType);
171         ringtonePickerIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, getTitle());
172     }
173 
174     /**
175      * Called when a ringtone is chosen.
176      * <p>
177      * By default, this saves the ringtone URI to the persistent storage as a
178      * string.
179      *
180      * @param ringtoneUri The chosen ringtone's {@link Uri}. Can be null.
181      */
onSaveRingtone(Uri ringtoneUri)182     protected void onSaveRingtone(Uri ringtoneUri) {
183         persistString(ringtoneUri != null ? ringtoneUri.toString() : "");
184     }
185 
186     /**
187      * Called when the chooser is about to be shown and the current ringtone
188      * should be marked. Can return null to not mark any ringtone.
189      * <p>
190      * By default, this restores the previous ringtone URI from the persistent
191      * storage.
192      *
193      * @return The ringtone to be marked as the current ringtone.
194      */
onRestoreRingtone()195     protected Uri onRestoreRingtone() {
196         final String uriString = getPersistedString(null);
197         return !TextUtils.isEmpty(uriString) ? Uri.parse(uriString) : null;
198     }
199 
200     @Override
onGetDefaultValue(TypedArray a, int index)201     protected Object onGetDefaultValue(TypedArray a, int index) {
202         return a.getString(index);
203     }
204 
205     @Override
onSetInitialValue(boolean restorePersistedValue, Object defaultValueObj)206     protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValueObj) {
207         String defaultValue = (String) defaultValueObj;
208 
209         /*
210          * This method is normally to make sure the internal state and UI
211          * matches either the persisted value or the default value. Since we
212          * don't show the current value in the UI (until the dialog is opened)
213          * and we don't keep local state, if we are restoring the persisted
214          * value we don't need to do anything.
215          */
216         if (restorePersistedValue) {
217             return;
218         }
219 
220         // If we are setting to the default value, we should persist it.
221         if (!TextUtils.isEmpty(defaultValue)) {
222             onSaveRingtone(Uri.parse(defaultValue));
223         }
224     }
225 
226     @Override
onAttachedToHierarchy(PreferenceManager preferenceManager)227     protected void onAttachedToHierarchy(PreferenceManager preferenceManager) {
228         super.onAttachedToHierarchy(preferenceManager);
229 
230         preferenceManager.registerOnActivityResultListener(this);
231         mRequestCode = preferenceManager.getNextRequestCode();
232     }
233 
onActivityResult(int requestCode, int resultCode, Intent data)234     public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
235 
236         if (requestCode == mRequestCode) {
237 
238             if (data != null) {
239                 Uri uri = data.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI);
240 
241                 if (callChangeListener(uri != null ? uri.toString() : "")) {
242                     onSaveRingtone(uri);
243                 }
244             }
245 
246             return true;
247         }
248 
249         return false;
250     }
251 
252 }
253