Home | History | Annotate | Download | only in app
      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