Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2008 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.settings;
     18 
     19 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
     20 
     21 import com.android.internal.telephony.TelephonyIntents;
     22 
     23 import android.app.Dialog;
     24 import android.content.BroadcastReceiver;
     25 import android.content.ContentResolver;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.IntentFilter;
     29 import android.media.AudioManager;
     30 import android.media.AudioSystem;
     31 import android.net.Uri;
     32 import android.os.Handler;
     33 import android.os.Looper;
     34 import android.os.Message;
     35 import android.os.Parcel;
     36 import android.os.Parcelable;
     37 import android.preference.VolumePreference;
     38 import android.provider.Settings;
     39 import android.provider.Settings.System;
     40 import android.util.AttributeSet;
     41 import android.util.Log;
     42 import android.view.KeyEvent;
     43 import android.view.View;
     44 import android.view.View.OnClickListener;
     45 import android.widget.CheckBox;
     46 import android.widget.CompoundButton;
     47 import android.widget.ImageView;
     48 import android.widget.SeekBar;
     49 import android.widget.TextView;
     50 
     51 /**
     52  * Special preference type that allows configuration of both the ring volume and
     53  * notification volume.
     54  */
     55 public class RingerVolumePreference extends VolumePreference {
     56     private static final String TAG = "RingerVolumePreference";
     57     private static final int MSG_RINGER_MODE_CHANGED = 101;
     58 
     59     private SeekBarVolumizer [] mSeekBarVolumizer;
     60 
     61     // These arrays must all match in length and order
     62     private static final int[] SEEKBAR_ID = new int[] {
     63         R.id.media_volume_seekbar,
     64         R.id.ringer_volume_seekbar,
     65         R.id.notification_volume_seekbar,
     66         R.id.alarm_volume_seekbar
     67     };
     68 
     69     private static final int[] SEEKBAR_TYPE = new int[] {
     70         AudioManager.STREAM_MUSIC,
     71         AudioManager.STREAM_RING,
     72         AudioManager.STREAM_NOTIFICATION,
     73         AudioManager.STREAM_ALARM
     74     };
     75 
     76     private static final int[] CHECKBOX_VIEW_ID = new int[] {
     77         R.id.media_mute_button,
     78         R.id.ringer_mute_button,
     79         R.id.notification_mute_button,
     80         R.id.alarm_mute_button
     81     };
     82 
     83     private static final int[] SEEKBAR_MUTED_RES_ID = new int[] {
     84         com.android.internal.R.drawable.ic_audio_vol_mute,
     85         com.android.internal.R.drawable.ic_audio_ring_notif_mute,
     86         com.android.internal.R.drawable.ic_audio_notification_mute,
     87         com.android.internal.R.drawable.ic_audio_alarm_mute
     88     };
     89 
     90     private static final int[] SEEKBAR_UNMUTED_RES_ID = new int[] {
     91         com.android.internal.R.drawable.ic_audio_vol,
     92         com.android.internal.R.drawable.ic_audio_ring_notif,
     93         com.android.internal.R.drawable.ic_audio_notification,
     94         com.android.internal.R.drawable.ic_audio_alarm
     95     };
     96 
     97     private ImageView[] mCheckBoxes = new ImageView[SEEKBAR_MUTED_RES_ID.length];
     98     private SeekBar[] mSeekBars = new SeekBar[SEEKBAR_ID.length];
     99 
    100     private Handler mHandler = new Handler() {
    101         public void handleMessage(Message msg) {
    102             updateSlidersAndMutedStates();
    103         }
    104     };
    105 
    106     @Override
    107     public void createActionButtons() {
    108         setPositiveButtonText(android.R.string.ok);
    109         setNegativeButtonText(null);
    110     }
    111 
    112     private void updateSlidersAndMutedStates() {
    113         for (int i = 0; i < SEEKBAR_TYPE.length; i++) {
    114             int streamType = SEEKBAR_TYPE[i];
    115             boolean muted = mAudioManager.isStreamMute(streamType);
    116 
    117             if (mCheckBoxes[i] != null) {
    118                 if ((streamType == AudioManager.STREAM_RING) &&
    119                         (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE)) {
    120                     mCheckBoxes[i].setImageResource(
    121                             com.android.internal.R.drawable.ic_audio_ring_notif_vibrate);
    122                 } else {
    123                     mCheckBoxes[i].setImageResource(
    124                             muted ? SEEKBAR_MUTED_RES_ID[i] : SEEKBAR_UNMUTED_RES_ID[i]);
    125                 }
    126             }
    127             if (mSeekBars[i] != null) {
    128                 final int volume = mAudioManager.getStreamVolume(streamType);
    129                 mSeekBars[i].setProgress(volume);
    130                 if (streamType != mAudioManager.getMasterStreamType() && muted) {
    131                     mSeekBars[i].setEnabled(false);
    132                 } else {
    133                     mSeekBars[i].setEnabled(true);
    134                 }
    135             }
    136         }
    137     }
    138 
    139     private BroadcastReceiver mRingModeChangedReceiver;
    140     private AudioManager mAudioManager;
    141 
    142     //private SeekBarVolumizer mNotificationSeekBarVolumizer;
    143     //private TextView mNotificationVolumeTitle;
    144 
    145     public RingerVolumePreference(Context context, AttributeSet attrs) {
    146         super(context, attrs);
    147 
    148         // The always visible seekbar is for ring volume
    149         setStreamType(AudioManager.STREAM_RING);
    150 
    151         setDialogLayoutResource(R.layout.preference_dialog_ringervolume);
    152         //setDialogIcon(R.drawable.ic_settings_sound);
    153 
    154         mSeekBarVolumizer = new SeekBarVolumizer[SEEKBAR_ID.length];
    155 
    156         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    157     }
    158 
    159     @Override
    160     protected void onBindDialogView(View view) {
    161         super.onBindDialogView(view);
    162 
    163         for (int i = 0; i < SEEKBAR_ID.length; i++) {
    164             SeekBar seekBar = (SeekBar) view.findViewById(SEEKBAR_ID[i]);
    165             mSeekBars[i] = seekBar;
    166             if (SEEKBAR_TYPE[i] == AudioManager.STREAM_MUSIC) {
    167                 mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
    168                         SEEKBAR_TYPE[i], getMediaVolumeUri(getContext()));
    169             } else {
    170                 mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
    171                         SEEKBAR_TYPE[i]);
    172             }
    173         }
    174 
    175         // Register callbacks for mute/unmute buttons
    176         for (int i = 0; i < mCheckBoxes.length; i++) {
    177             ImageView checkbox = (ImageView) view.findViewById(CHECKBOX_VIEW_ID[i]);
    178             mCheckBoxes[i] = checkbox;
    179         }
    180 
    181         // Load initial states from AudioManager
    182         updateSlidersAndMutedStates();
    183 
    184         // Listen for updates from AudioManager
    185         if (mRingModeChangedReceiver == null) {
    186             final IntentFilter filter = new IntentFilter();
    187             filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
    188             mRingModeChangedReceiver = new BroadcastReceiver() {
    189                 public void onReceive(Context context, Intent intent) {
    190                     final String action = intent.getAction();
    191                     if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
    192                         mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, intent
    193                                 .getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
    194                     }
    195                 }
    196             };
    197             getContext().registerReceiver(mRingModeChangedReceiver, filter);
    198         }
    199 
    200         // Disable either ringer+notifications or notifications
    201         int id;
    202         if (!Utils.isVoiceCapable(getContext())) {
    203             id = R.id.ringer_section;
    204         } else {
    205             id = R.id.notification_section;
    206         }
    207         View hideSection = view.findViewById(id);
    208         hideSection.setVisibility(View.GONE);
    209     }
    210 
    211     private Uri getMediaVolumeUri(Context context) {
    212         return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
    213                 + context.getPackageName()
    214                 + "/" + R.raw.media_volume);
    215     }
    216 
    217     @Override
    218     protected void onDialogClosed(boolean positiveResult) {
    219         super.onDialogClosed(positiveResult);
    220 
    221         if (!positiveResult) {
    222             for (SeekBarVolumizer vol : mSeekBarVolumizer) {
    223                 if (vol != null) vol.revertVolume();
    224             }
    225         }
    226         cleanup();
    227     }
    228 
    229     @Override
    230     public void onActivityStop() {
    231         super.onActivityStop();
    232 
    233         for (SeekBarVolumizer vol : mSeekBarVolumizer) {
    234             if (vol != null) vol.stopSample();
    235         }
    236     }
    237 
    238     @Override
    239     public boolean onKey(View v, int keyCode, KeyEvent event) {
    240         boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
    241         switch (keyCode) {
    242             case KeyEvent.KEYCODE_VOLUME_DOWN:
    243             case KeyEvent.KEYCODE_VOLUME_UP:
    244             case KeyEvent.KEYCODE_VOLUME_MUTE:
    245                 return true;
    246             default:
    247                 return false;
    248         }
    249     }
    250 
    251     @Override
    252     protected void onSampleStarting(SeekBarVolumizer volumizer) {
    253         super.onSampleStarting(volumizer);
    254         for (SeekBarVolumizer vol : mSeekBarVolumizer) {
    255             if (vol != null && vol != volumizer) vol.stopSample();
    256         }
    257     }
    258 
    259     private void cleanup() {
    260         for (int i = 0; i < SEEKBAR_ID.length; i++) {
    261             if (mSeekBarVolumizer[i] != null) {
    262                 Dialog dialog = getDialog();
    263                 if (dialog != null && dialog.isShowing()) {
    264                     // Stopped while dialog was showing, revert changes
    265                     mSeekBarVolumizer[i].revertVolume();
    266                 }
    267                 mSeekBarVolumizer[i].stop();
    268                 mSeekBarVolumizer[i] = null;
    269             }
    270         }
    271         if (mRingModeChangedReceiver != null) {
    272             getContext().unregisterReceiver(mRingModeChangedReceiver);
    273             mRingModeChangedReceiver = null;
    274         }
    275     }
    276 
    277     @Override
    278     protected Parcelable onSaveInstanceState() {
    279         final Parcelable superState = super.onSaveInstanceState();
    280         if (isPersistent()) {
    281             // No need to save instance state since it's persistent
    282             return superState;
    283         }
    284 
    285         final SavedState myState = new SavedState(superState);
    286         VolumeStore[] volumeStore = myState.getVolumeStore(SEEKBAR_ID.length);
    287         for (int i = 0; i < SEEKBAR_ID.length; i++) {
    288             SeekBarVolumizer vol = mSeekBarVolumizer[i];
    289             if (vol != null) {
    290                 vol.onSaveInstanceState(volumeStore[i]);
    291             }
    292         }
    293         return myState;
    294     }
    295 
    296     @Override
    297     protected void onRestoreInstanceState(Parcelable state) {
    298         if (state == null || !state.getClass().equals(SavedState.class)) {
    299             // Didn't save state for us in onSaveInstanceState
    300             super.onRestoreInstanceState(state);
    301             return;
    302         }
    303 
    304         SavedState myState = (SavedState) state;
    305         super.onRestoreInstanceState(myState.getSuperState());
    306         VolumeStore[] volumeStore = myState.getVolumeStore(SEEKBAR_ID.length);
    307         for (int i = 0; i < SEEKBAR_ID.length; i++) {
    308             SeekBarVolumizer vol = mSeekBarVolumizer[i];
    309             if (vol != null) {
    310                 vol.onRestoreInstanceState(volumeStore[i]);
    311             }
    312         }
    313     }
    314 
    315     private static class SavedState extends BaseSavedState {
    316         VolumeStore [] mVolumeStore;
    317 
    318         public SavedState(Parcel source) {
    319             super(source);
    320             mVolumeStore = new VolumeStore[SEEKBAR_ID.length];
    321             for (int i = 0; i < SEEKBAR_ID.length; i++) {
    322                 mVolumeStore[i] = new VolumeStore();
    323                 mVolumeStore[i].volume = source.readInt();
    324                 mVolumeStore[i].originalVolume = source.readInt();
    325             }
    326         }
    327 
    328         @Override
    329         public void writeToParcel(Parcel dest, int flags) {
    330             super.writeToParcel(dest, flags);
    331             for (int i = 0; i < SEEKBAR_ID.length; i++) {
    332                 dest.writeInt(mVolumeStore[i].volume);
    333                 dest.writeInt(mVolumeStore[i].originalVolume);
    334             }
    335         }
    336 
    337         VolumeStore[] getVolumeStore(int count) {
    338             if (mVolumeStore == null || mVolumeStore.length != count) {
    339                 mVolumeStore = new VolumeStore[count];
    340                 for (int i = 0; i < count; i++) {
    341                     mVolumeStore[i] = new VolumeStore();
    342                 }
    343             }
    344             return mVolumeStore;
    345         }
    346 
    347         public SavedState(Parcelable superState) {
    348             super(superState);
    349         }
    350 
    351         public static final Parcelable.Creator<SavedState> CREATOR =
    352                 new Parcelable.Creator<SavedState>() {
    353             public SavedState createFromParcel(Parcel in) {
    354                 return new SavedState(in);
    355             }
    356 
    357             public SavedState[] newArray(int size) {
    358                 return new SavedState[size];
    359             }
    360         };
    361     }
    362 }
    363