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