Move RINGTONE_PICKER to handle external storage.
To give RINGTONE_PICKER external storage access, move it from system
to MediaProvider.
Bug: 6346701
Change-Id: Ib5f8e8fa8a962be211bc60c6e09778c7e2b85f2a
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4b73de0..afffe51 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -64,5 +64,14 @@
<service android:name="MtpService" />
+ <activity android:name="RingtonePickerActivity"
+ android:theme="@*android:style/Theme.Holo.Dialog.Alert"
+ android:excludeFromRecents="true">
+ <intent-filter>
+ <action android:name="android.intent.action.RINGTONE_PICKER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
</application>
</manifest>
diff --git a/src/com/android/providers/media/RingtonePickerActivity.java b/src/com/android/providers/media/RingtonePickerActivity.java
new file mode 100644
index 0000000..2669fb7
--- /dev/null
+++ b/src/com/android/providers/media/RingtonePickerActivity.java
@@ -0,0 +1,362 @@
+/*
+ * 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;
+ }
+
+}