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 com.android.internal.app; 18 19import com.android.internal.app.AlertActivity; 20import com.android.internal.app.AlertController; 21 22import android.content.DialogInterface; 23import android.content.Intent; 24import android.database.Cursor; 25import android.media.Ringtone; 26import android.media.RingtoneManager; 27import android.net.Uri; 28import android.os.Bundle; 29import android.os.Handler; 30import android.provider.MediaStore; 31import android.provider.Settings; 32import android.view.View; 33import android.widget.AdapterView; 34import android.widget.ListView; 35import android.widget.TextView; 36 37/** 38 * The {@link RingtonePickerActivity} allows the user to choose one from all of the 39 * available ringtones. The chosen ringtone's URI will be persisted as a string. 40 * 41 * @see RingtoneManager#ACTION_RINGTONE_PICKER 42 */ 43public final class RingtonePickerActivity extends AlertActivity implements 44 AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener, 45 AlertController.AlertParams.OnPrepareListViewListener { 46 47 private static final String TAG = "RingtonePickerActivity"; 48 49 private static final int DELAY_MS_SELECTION_PLAYED = 300; 50 51 private static final String SAVE_CLICKED_POS = "clicked_pos"; 52 53 private RingtoneManager mRingtoneManager; 54 55 private Cursor mCursor; 56 private Handler mHandler; 57 58 /** The position in the list of the 'Silent' item. */ 59 private int mSilentPos = -1; 60 61 /** The position in the list of the 'Default' item. */ 62 private int mDefaultRingtonePos = -1; 63 64 /** The position in the list of the last clicked item. */ 65 private int mClickedPos = -1; 66 67 /** The position in the list of the ringtone to sample. */ 68 private int mSampleRingtonePos = -1; 69 70 /** Whether this list has the 'Silent' item. */ 71 private boolean mHasSilentItem; 72 73 /** The Uri to place a checkmark next to. */ 74 private Uri mExistingUri; 75 76 /** The number of static items in the list. */ 77 private int mStaticItemCount; 78 79 /** Whether this list has the 'Default' item. */ 80 private boolean mHasDefaultItem; 81 82 /** The Uri to play when the 'Default' item is clicked. */ 83 private Uri mUriForDefaultItem; 84 85 /** 86 * A Ringtone for the default ringtone. In most cases, the RingtoneManager 87 * will stop the previous ringtone. However, the RingtoneManager doesn't 88 * manage the default ringtone for us, so we should stop this one manually. 89 */ 90 private Ringtone mDefaultRingtone; 91 92 private DialogInterface.OnClickListener mRingtoneClickListener = 93 new DialogInterface.OnClickListener() { 94 95 /* 96 * On item clicked 97 */ 98 public void onClick(DialogInterface dialog, int which) { 99 // Save the position of most recently clicked item 100 mClickedPos = which; 101 102 // Play clip 103 playRingtone(which, 0); 104 } 105 106 }; 107 108 @Override 109 protected void onCreate(Bundle savedInstanceState) { 110 super.onCreate(savedInstanceState); 111 112 mHandler = new Handler(); 113 114 Intent intent = getIntent(); 115 116 /* 117 * Get whether to show the 'Default' item, and the URI to play when the 118 * default is clicked 119 */ 120 mHasDefaultItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); 121 mUriForDefaultItem = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI); 122 if (mUriForDefaultItem == null) { 123 mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI; 124 } 125 126 if (savedInstanceState != null) { 127 mClickedPos = savedInstanceState.getInt(SAVE_CLICKED_POS, -1); 128 } 129 // Get whether to show the 'Silent' item 130 mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); 131 132 // Give the Activity so it can do managed queries 133 mRingtoneManager = new RingtoneManager(this); 134 135 // Get whether to include DRM ringtones 136 boolean includeDrm = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_INCLUDE_DRM, 137 true); 138 mRingtoneManager.setIncludeDrm(includeDrm); 139 140 // Get the types of ringtones to show 141 int types = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1); 142 if (types != -1) { 143 mRingtoneManager.setType(types); 144 } 145 146 mCursor = mRingtoneManager.getCursor(); 147 148 // The volume keys will control the stream that we are choosing a ringtone for 149 setVolumeControlStream(mRingtoneManager.inferStreamType()); 150 151 // Get the URI whose list item should have a checkmark 152 mExistingUri = intent 153 .getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI); 154 155 final AlertController.AlertParams p = mAlertParams; 156 p.mCursor = mCursor; 157 p.mOnClickListener = mRingtoneClickListener; 158 p.mLabelColumn = MediaStore.Audio.Media.TITLE; 159 p.mIsSingleChoice = true; 160 p.mOnItemSelectedListener = this; 161 p.mPositiveButtonText = getString(com.android.internal.R.string.ok); 162 p.mPositiveButtonListener = this; 163 p.mNegativeButtonText = getString(com.android.internal.R.string.cancel); 164 p.mPositiveButtonListener = this; 165 p.mOnPrepareListViewListener = this; 166 167 p.mTitle = intent.getCharSequenceExtra(RingtoneManager.EXTRA_RINGTONE_TITLE); 168 if (p.mTitle == null) { 169 p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title); 170 } 171 172 setupAlert(); 173 } 174 175 @Override 176 public void onSaveInstanceState(Bundle outState) { 177 super.onSaveInstanceState(outState); 178 outState.putInt(SAVE_CLICKED_POS, mClickedPos); 179 } 180 181 public void onPrepareListView(ListView listView) { 182 183 if (mHasDefaultItem) { 184 mDefaultRingtonePos = addDefaultRingtoneItem(listView); 185 186 if (RingtoneManager.isDefault(mExistingUri)) { 187 mClickedPos = mDefaultRingtonePos; 188 } 189 } 190 191 if (mHasSilentItem) { 192 mSilentPos = addSilentItem(listView); 193 194 // The 'Silent' item should use a null Uri 195 if (mExistingUri == null) { 196 mClickedPos = mSilentPos; 197 } 198 } 199 200 if (mClickedPos == -1) { 201 mClickedPos = getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri)); 202 } 203 204 // Put a checkmark next to an item. 205 mAlertParams.mCheckedItem = mClickedPos; 206 } 207 208 /** 209 * Adds a static item to the top of the list. A static item is one that is not from the 210 * RingtoneManager. 211 * 212 * @param listView The ListView to add to. 213 * @param textResId The resource ID of the text for the item. 214 * @return The position of the inserted item. 215 */ 216 private int addStaticItem(ListView listView, int textResId) { 217 TextView textView = (TextView) getLayoutInflater().inflate( 218 com.android.internal.R.layout.select_dialog_singlechoice_holo, listView, false); 219 textView.setText(textResId); 220 listView.addHeaderView(textView); 221 mStaticItemCount++; 222 return listView.getHeaderViewsCount() - 1; 223 } 224 225 private int addDefaultRingtoneItem(ListView listView) { 226 return addStaticItem(listView, com.android.internal.R.string.ringtone_default); 227 } 228 229 private int addSilentItem(ListView listView) { 230 return addStaticItem(listView, com.android.internal.R.string.ringtone_silent); 231 } 232 233 /* 234 * On click of Ok/Cancel buttons 235 */ 236 public void onClick(DialogInterface dialog, int which) { 237 boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE; 238 239 // Stop playing the previous ringtone 240 mRingtoneManager.stopPreviousRingtone(); 241 242 if (positiveResult) { 243 Intent resultIntent = new Intent(); 244 Uri uri = null; 245 246 if (mClickedPos == mDefaultRingtonePos) { 247 // Set it to the default Uri that they originally gave us 248 uri = mUriForDefaultItem; 249 } else if (mClickedPos == mSilentPos) { 250 // A null Uri is for the 'Silent' item 251 uri = null; 252 } else { 253 uri = mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(mClickedPos)); 254 } 255 256 resultIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, uri); 257 setResult(RESULT_OK, resultIntent); 258 } else { 259 setResult(RESULT_CANCELED); 260 } 261 262 getWindow().getDecorView().post(new Runnable() { 263 public void run() { 264 mCursor.deactivate(); 265 } 266 }); 267 268 finish(); 269 } 270 271 /* 272 * On item selected via keys 273 */ 274 public void onItemSelected(AdapterView parent, View view, int position, long id) { 275 playRingtone(position, DELAY_MS_SELECTION_PLAYED); 276 } 277 278 public void onNothingSelected(AdapterView parent) { 279 } 280 281 private void playRingtone(int position, int delayMs) { 282 mHandler.removeCallbacks(this); 283 mSampleRingtonePos = position; 284 mHandler.postDelayed(this, delayMs); 285 } 286 287 public void run() { 288 289 if (mSampleRingtonePos == mSilentPos) { 290 mRingtoneManager.stopPreviousRingtone(); 291 return; 292 } 293 294 /* 295 * Stop the default ringtone, if it's playing (other ringtones will be 296 * stopped by the RingtoneManager when we get another Ringtone from it. 297 */ 298 if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) { 299 mDefaultRingtone.stop(); 300 mDefaultRingtone = null; 301 } 302 303 Ringtone ringtone; 304 if (mSampleRingtonePos == mDefaultRingtonePos) { 305 if (mDefaultRingtone == null) { 306 mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem); 307 } 308 ringtone = mDefaultRingtone; 309 310 /* 311 * Normally the non-static RingtoneManager.getRingtone stops the 312 * previous ringtone, but we're getting the default ringtone outside 313 * of the RingtoneManager instance, so let's stop the previous 314 * ringtone manually. 315 */ 316 mRingtoneManager.stopPreviousRingtone(); 317 318 } else { 319 ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos)); 320 } 321 322 if (ringtone != null) { 323 ringtone.play(); 324 } 325 } 326 327 @Override 328 protected void onStop() { 329 super.onStop(); 330 stopAnyPlayingRingtone(); 331 } 332 333 @Override 334 protected void onPause() { 335 super.onPause(); 336 stopAnyPlayingRingtone(); 337 } 338 339 private void stopAnyPlayingRingtone() { 340 341 if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) { 342 mDefaultRingtone.stop(); 343 } 344 345 if (mRingtoneManager != null) { 346 mRingtoneManager.stopPreviousRingtone(); 347 } 348 } 349 350 private int getRingtoneManagerPosition(int listPos) { 351 return listPos - mStaticItemCount; 352 } 353 354 private int getListPosition(int ringtoneManagerPos) { 355 356 // If the manager position is -1 (for not found), return that 357 if (ringtoneManagerPos < 0) return ringtoneManagerPos; 358 359 return ringtoneManagerPos + mStaticItemCount; 360 } 361 362} 363