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