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 com.android.internal.app; 18 19 import com.android.internal.app.AlertActivity; 20 import com.android.internal.app.AlertController; 21 22 import android.content.DialogInterface; 23 import android.content.Intent; 24 import android.database.Cursor; 25 import android.media.Ringtone; 26 import android.media.RingtoneManager; 27 import android.net.Uri; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.provider.MediaStore; 31 import android.provider.Settings; 32 import android.view.View; 33 import android.widget.AdapterView; 34 import android.widget.ListView; 35 import 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 */ 43 public 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 == DialogInterface.BUTTON_POSITIVE; 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 mRingtoneManager.stopPreviousRingtone(); 280 return; 281 } 282 283 /* 284 * Stop the default ringtone, if it's playing (other ringtones will be 285 * stopped by the RingtoneManager when we get another Ringtone from it. 286 */ 287 if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) { 288 mDefaultRingtone.stop(); 289 mDefaultRingtone = null; 290 } 291 292 Ringtone ringtone; 293 if (mSampleRingtonePos == mDefaultRingtonePos) { 294 if (mDefaultRingtone == null) { 295 mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem); 296 } 297 ringtone = mDefaultRingtone; 298 299 /* 300 * Normally the non-static RingtoneManager.getRingtone stops the 301 * previous ringtone, but we're getting the default ringtone outside 302 * of the RingtoneManager instance, so let's stop the previous 303 * ringtone manually. 304 */ 305 mRingtoneManager.stopPreviousRingtone(); 306 307 } else { 308 ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos)); 309 } 310 311 if (ringtone != null) { 312 ringtone.play(); 313 } 314 } 315 316 @Override 317 protected void onStop() { 318 super.onStop(); 319 stopAnyPlayingRingtone(); 320 } 321 322 @Override 323 protected void onPause() { 324 super.onPause(); 325 stopAnyPlayingRingtone(); 326 } 327 328 private void stopAnyPlayingRingtone() { 329 330 if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) { 331 mDefaultRingtone.stop(); 332 } 333 334 if (mRingtoneManager != null) { 335 mRingtoneManager.stopPreviousRingtone(); 336 } 337 } 338 339 private int getRingtoneManagerPosition(int listPos) { 340 return listPos - mStaticItemCount; 341 } 342 343 private int getListPosition(int ringtoneManagerPos) { 344 345 // If the manager position is -1 (for not found), return that 346 if (ringtoneManagerPos < 0) return ringtoneManagerPos; 347 348 return ringtoneManagerPos + mStaticItemCount; 349 } 350 351 } 352