| /* |
| * Copyright (C) 2007 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.providers.media; |
| |
| import android.content.DialogInterface; |
| import android.content.Intent; |
| import android.database.Cursor; |
| import android.media.Ringtone; |
| import android.media.RingtoneManager; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.provider.MediaStore; |
| import android.provider.Settings; |
| import android.view.View; |
| import android.widget.AdapterView; |
| import android.widget.ListView; |
| import android.widget.TextView; |
| |
| import com.android.internal.app.AlertActivity; |
| import com.android.internal.app.AlertController; |
| |
| /** |
| * The {@link RingtonePickerActivity} allows the user to choose one from all of the |
| * available ringtones. The chosen ringtone's URI will be persisted as a string. |
| * |
| * @see RingtoneManager#ACTION_RINGTONE_PICKER |
| */ |
| public final class RingtonePickerActivity extends AlertActivity implements |
| AdapterView.OnItemSelectedListener, Runnable, DialogInterface.OnClickListener, |
| AlertController.AlertParams.OnPrepareListViewListener { |
| |
| private static final String TAG = "RingtonePickerActivity"; |
| |
| private static final int DELAY_MS_SELECTION_PLAYED = 300; |
| |
| private static final String SAVE_CLICKED_POS = "clicked_pos"; |
| |
| private RingtoneManager mRingtoneManager; |
| |
| private Cursor mCursor; |
| private Handler mHandler; |
| |
| /** The position in the list of the 'Silent' item. */ |
| private int mSilentPos = -1; |
| |
| /** The position in the list of the 'Default' item. */ |
| private int mDefaultRingtonePos = -1; |
| |
| /** The position in the list of the last clicked item. */ |
| private int mClickedPos = -1; |
| |
| /** The position in the list of the ringtone to sample. */ |
| private int mSampleRingtonePos = -1; |
| |
| /** Whether this list has the 'Silent' item. */ |
| private boolean mHasSilentItem; |
| |
| /** The Uri to place a checkmark next to. */ |
| private Uri mExistingUri; |
| |
| /** The number of static items in the list. */ |
| private int mStaticItemCount; |
| |
| /** Whether this list has the 'Default' item. */ |
| private boolean mHasDefaultItem; |
| |
| /** The Uri to play when the 'Default' item is clicked. */ |
| private Uri mUriForDefaultItem; |
| |
| /** |
| * A Ringtone for the default ringtone. In most cases, the RingtoneManager |
| * will stop the previous ringtone. However, the RingtoneManager doesn't |
| * manage the default ringtone for us, so we should stop this one manually. |
| */ |
| private Ringtone mDefaultRingtone; |
| |
| private DialogInterface.OnClickListener mRingtoneClickListener = |
| new DialogInterface.OnClickListener() { |
| |
| /* |
| * On item clicked |
| */ |
| public void onClick(DialogInterface dialog, int which) { |
| // Save the position of most recently clicked item |
| mClickedPos = which; |
| |
| // Play clip |
| playRingtone(which, 0); |
| } |
| |
| }; |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| mHandler = new Handler(); |
| |
| Intent intent = getIntent(); |
| |
| /* |
| * Get whether to show the 'Default' item, and the URI to play when the |
| * default is clicked |
| */ |
| mHasDefaultItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, true); |
| mUriForDefaultItem = intent.getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI); |
| if (mUriForDefaultItem == null) { |
| mUriForDefaultItem = Settings.System.DEFAULT_RINGTONE_URI; |
| } |
| |
| if (savedInstanceState != null) { |
| mClickedPos = savedInstanceState.getInt(SAVE_CLICKED_POS, -1); |
| } |
| // Get whether to show the 'Silent' item |
| mHasSilentItem = intent.getBooleanExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, true); |
| |
| // Give the Activity so it can do managed queries |
| mRingtoneManager = new RingtoneManager(this); |
| |
| // Get whether to include DRM ringtones |
| final boolean includeDrm = intent.getBooleanExtra( |
| RingtoneManager.EXTRA_RINGTONE_INCLUDE_DRM, true); |
| mRingtoneManager.setIncludeDrm(includeDrm); |
| |
| // Get the types of ringtones to show |
| int types = intent.getIntExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, -1); |
| if (types != -1) { |
| mRingtoneManager.setType(types); |
| } |
| |
| mCursor = mRingtoneManager.getCursor(); |
| |
| // The volume keys will control the stream that we are choosing a ringtone for |
| setVolumeControlStream(mRingtoneManager.inferStreamType()); |
| |
| // Get the URI whose list item should have a checkmark |
| mExistingUri = intent |
| .getParcelableExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI); |
| |
| final AlertController.AlertParams p = mAlertParams; |
| p.mCursor = mCursor; |
| p.mOnClickListener = mRingtoneClickListener; |
| p.mLabelColumn = MediaStore.Audio.Media.TITLE; |
| p.mIsSingleChoice = true; |
| p.mOnItemSelectedListener = this; |
| p.mPositiveButtonText = getString(com.android.internal.R.string.ok); |
| p.mPositiveButtonListener = this; |
| p.mNegativeButtonText = getString(com.android.internal.R.string.cancel); |
| p.mPositiveButtonListener = this; |
| p.mOnPrepareListViewListener = this; |
| |
| p.mTitle = intent.getCharSequenceExtra(RingtoneManager.EXTRA_RINGTONE_TITLE); |
| if (p.mTitle == null) { |
| p.mTitle = getString(com.android.internal.R.string.ringtone_picker_title); |
| } |
| |
| setupAlert(); |
| } |
| |
| @Override |
| public void onSaveInstanceState(Bundle outState) { |
| super.onSaveInstanceState(outState); |
| outState.putInt(SAVE_CLICKED_POS, mClickedPos); |
| } |
| |
| public void onPrepareListView(ListView listView) { |
| |
| if (mHasDefaultItem) { |
| mDefaultRingtonePos = addDefaultRingtoneItem(listView); |
| |
| if (RingtoneManager.isDefault(mExistingUri)) { |
| mClickedPos = mDefaultRingtonePos; |
| } |
| } |
| |
| if (mHasSilentItem) { |
| mSilentPos = addSilentItem(listView); |
| |
| // The 'Silent' item should use a null Uri |
| if (mExistingUri == null) { |
| mClickedPos = mSilentPos; |
| } |
| } |
| |
| if (mClickedPos == -1) { |
| mClickedPos = getListPosition(mRingtoneManager.getRingtonePosition(mExistingUri)); |
| } |
| |
| // Put a checkmark next to an item. |
| mAlertParams.mCheckedItem = mClickedPos; |
| } |
| |
| /** |
| * Adds a static item to the top of the list. A static item is one that is not from the |
| * RingtoneManager. |
| * |
| * @param listView The ListView to add to. |
| * @param textResId The resource ID of the text for the item. |
| * @return The position of the inserted item. |
| */ |
| private int addStaticItem(ListView listView, int textResId) { |
| TextView textView = (TextView) getLayoutInflater().inflate( |
| com.android.internal.R.layout.select_dialog_singlechoice_holo, listView, false); |
| textView.setText(textResId); |
| listView.addHeaderView(textView); |
| mStaticItemCount++; |
| return listView.getHeaderViewsCount() - 1; |
| } |
| |
| private int addDefaultRingtoneItem(ListView listView) { |
| return addStaticItem(listView, com.android.internal.R.string.ringtone_default); |
| } |
| |
| private int addSilentItem(ListView listView) { |
| return addStaticItem(listView, com.android.internal.R.string.ringtone_silent); |
| } |
| |
| /* |
| * On click of Ok/Cancel buttons |
| */ |
| public void onClick(DialogInterface dialog, int which) { |
| boolean positiveResult = which == DialogInterface.BUTTON_POSITIVE; |
| |
| // Stop playing the previous ringtone |
| mRingtoneManager.stopPreviousRingtone(); |
| |
| if (positiveResult) { |
| Intent resultIntent = new Intent(); |
| Uri uri = null; |
| |
| if (mClickedPos == mDefaultRingtonePos) { |
| // Set it to the default Uri that they originally gave us |
| uri = mUriForDefaultItem; |
| } else if (mClickedPos == mSilentPos) { |
| // A null Uri is for the 'Silent' item |
| uri = null; |
| } else { |
| uri = mRingtoneManager.getRingtoneUri(getRingtoneManagerPosition(mClickedPos)); |
| } |
| |
| resultIntent.putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, uri); |
| setResult(RESULT_OK, resultIntent); |
| } else { |
| setResult(RESULT_CANCELED); |
| } |
| |
| getWindow().getDecorView().post(new Runnable() { |
| public void run() { |
| mCursor.deactivate(); |
| } |
| }); |
| |
| finish(); |
| } |
| |
| /* |
| * On item selected via keys |
| */ |
| public void onItemSelected(AdapterView parent, View view, int position, long id) { |
| playRingtone(position, DELAY_MS_SELECTION_PLAYED); |
| } |
| |
| public void onNothingSelected(AdapterView parent) { |
| } |
| |
| private void playRingtone(int position, int delayMs) { |
| mHandler.removeCallbacks(this); |
| mSampleRingtonePos = position; |
| mHandler.postDelayed(this, delayMs); |
| } |
| |
| public void run() { |
| |
| if (mSampleRingtonePos == mSilentPos) { |
| mRingtoneManager.stopPreviousRingtone(); |
| return; |
| } |
| |
| /* |
| * Stop the default ringtone, if it's playing (other ringtones will be |
| * stopped by the RingtoneManager when we get another Ringtone from it. |
| */ |
| if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) { |
| mDefaultRingtone.stop(); |
| mDefaultRingtone = null; |
| } |
| |
| Ringtone ringtone; |
| if (mSampleRingtonePos == mDefaultRingtonePos) { |
| if (mDefaultRingtone == null) { |
| mDefaultRingtone = RingtoneManager.getRingtone(this, mUriForDefaultItem); |
| } |
| ringtone = mDefaultRingtone; |
| |
| /* |
| * Normally the non-static RingtoneManager.getRingtone stops the |
| * previous ringtone, but we're getting the default ringtone outside |
| * of the RingtoneManager instance, so let's stop the previous |
| * ringtone manually. |
| */ |
| mRingtoneManager.stopPreviousRingtone(); |
| |
| } else { |
| ringtone = mRingtoneManager.getRingtone(getRingtoneManagerPosition(mSampleRingtonePos)); |
| } |
| |
| if (ringtone != null) { |
| ringtone.play(); |
| } |
| } |
| |
| @Override |
| protected void onStop() { |
| super.onStop(); |
| stopAnyPlayingRingtone(); |
| } |
| |
| @Override |
| protected void onPause() { |
| super.onPause(); |
| stopAnyPlayingRingtone(); |
| } |
| |
| private void stopAnyPlayingRingtone() { |
| |
| if (mDefaultRingtone != null && mDefaultRingtone.isPlaying()) { |
| mDefaultRingtone.stop(); |
| } |
| |
| if (mRingtoneManager != null) { |
| mRingtoneManager.stopPreviousRingtone(); |
| } |
| } |
| |
| private int getRingtoneManagerPosition(int listPos) { |
| return listPos - mStaticItemCount; |
| } |
| |
| private int getListPosition(int ringtoneManagerPos) { |
| |
| // If the manager position is -1 (for not found), return that |
| if (ringtoneManagerPos < 0) return ringtoneManagerPos; |
| |
| return ringtoneManagerPos + mStaticItemCount; |
| } |
| |
| } |