• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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
onCreate(Bundle savedInstanceState)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
onSaveInstanceState(Bundle outState)176     public void onSaveInstanceState(Bundle outState) {
177         super.onSaveInstanceState(outState);
178         outState.putInt(SAVE_CLICKED_POS, mClickedPos);
179     }
180 
onPrepareListView(ListView listView)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      */
addStaticItem(ListView listView, int textResId)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 
addDefaultRingtoneItem(ListView listView)225     private int addDefaultRingtoneItem(ListView listView) {
226         return addStaticItem(listView, com.android.internal.R.string.ringtone_default);
227     }
228 
addSilentItem(ListView listView)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      */
onClick(DialogInterface dialog, int which)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      */
onItemSelected(AdapterView parent, View view, int position, long id)274     public void onItemSelected(AdapterView parent, View view, int position, long id) {
275         playRingtone(position, DELAY_MS_SELECTION_PLAYED);
276     }
277 
onNothingSelected(AdapterView parent)278     public void onNothingSelected(AdapterView parent) {
279     }
280 
playRingtone(int position, int delayMs)281     private void playRingtone(int position, int delayMs) {
282         mHandler.removeCallbacks(this);
283         mSampleRingtonePos = position;
284         mHandler.postDelayed(this, delayMs);
285     }
286 
run()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
onStop()328     protected void onStop() {
329         super.onStop();
330         stopAnyPlayingRingtone();
331     }
332 
333     @Override
onPause()334     protected void onPause() {
335         super.onPause();
336         stopAnyPlayingRingtone();
337     }
338 
stopAnyPlayingRingtone()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 
getRingtoneManagerPosition(int listPos)350     private int getRingtoneManagerPosition(int listPos) {
351         return listPos - mStaticItemCount;
352     }
353 
getListPosition(int ringtoneManagerPos)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