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_SECTION_ID = new int[] {
     84         R.id.media_section,
     85         R.id.ringer_section,
     86         R.id.notification_section,
     87         R.id.alarm_section
     88     };
     89 
     90     private static final int[] SEEKBAR_MUTED_RES_ID = new int[] {
     91         com.android.internal.R.drawable.ic_audio_vol_mute,
     92         com.android.internal.R.drawable.ic_audio_ring_notif_mute,
     93         com.android.internal.R.drawable.ic_audio_notification_mute,
     94         com.android.internal.R.drawable.ic_audio_alarm_mute
     95     };
     96 
     97     private static final int[] SEEKBAR_UNMUTED_RES_ID = new int[] {
     98         com.android.internal.R.drawable.ic_audio_vol,
     99         com.android.internal.R.drawable.ic_audio_ring_notif,
    100         com.android.internal.R.drawable.ic_audio_notification,
    101         com.android.internal.R.drawable.ic_audio_alarm
    102     };
    103 
    104     private ImageView[] mCheckBoxes = new ImageView[SEEKBAR_MUTED_RES_ID.length];
    105     private SeekBar[] mSeekBars = new SeekBar[SEEKBAR_ID.length];
    106 
    107     private Handler mHandler = new Handler() {
    108         public void handleMessage(Message msg) {
    109             updateSlidersAndMutedStates();
    110         }
    111     };
    112 
    113     @Override
    114     public void createActionButtons() {
    115         setPositiveButtonText(android.R.string.ok);
    116         setNegativeButtonText(null);
    117     }
    118 
    119     private void updateSlidersAndMutedStates() {
    120         for (int i = 0; i < SEEKBAR_TYPE.length; i++) {
    121             int streamType = SEEKBAR_TYPE[i];
    122             boolean muted = mAudioManager.isStreamMute(streamType);
    123 
    124             if (mCheckBoxes[i] != null) {
    125                 if (((streamType == AudioManager.STREAM_RING) ||
    126                         (streamType == AudioManager.STREAM_NOTIFICATION)) &&
    127                         (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE)) {
    128                     mCheckBoxes[i].setImageResource(
    129                             com.android.internal.R.drawable.ic_audio_ring_notif_vibrate);
    130                 } else {
    131                     mCheckBoxes[i].setImageResource(
    132                             muted ? SEEKBAR_MUTED_RES_ID[i] : SEEKBAR_UNMUTED_RES_ID[i]);
    133                 }
    134             }
    135             if (mSeekBars[i] != null) {
    136                 final int volume = mAudioManager.getStreamVolume(streamType);
    137                 mSeekBars[i].setProgress(volume);
    138                 if (streamType != mAudioManager.getMasterStreamType() && muted) {
    139                     mSeekBars[i].setEnabled(false);
    140                 } else {
    141                     mSeekBars[i].setEnabled(true);
    142                 }
    143             }
    144         }
    145     }
    146 
    147     private BroadcastReceiver mRingModeChangedReceiver;
    148     private AudioManager mAudioManager;
    149 
    150     //private SeekBarVolumizer mNotificationSeekBarVolumizer;
    151     //private TextView mNotificationVolumeTitle;
    152 
    153     public RingerVolumePreference(Context context, AttributeSet attrs) {
    154         super(context, attrs);
    155 
    156         // The always visible seekbar is for ring volume
    157         setStreamType(AudioManager.STREAM_RING);
    158 
    159         setDialogLayoutResource(R.layout.preference_dialog_ringervolume);
    160         //setDialogIcon(R.drawable.ic_settings_sound);
    161 
    162         mSeekBarVolumizer = new SeekBarVolumizer[SEEKBAR_ID.length];
    163 
    164         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    165     }
    166 
    167     @Override
    168     protected void onBindDialogView(View view) {
    169         super.onBindDialogView(view);
    170 
    171         for (int i = 0; i < SEEKBAR_ID.length; i++) {
    172             SeekBar seekBar = (SeekBar) view.findViewById(SEEKBAR_ID[i]);
    173             mSeekBars[i] = seekBar;
    174             if (SEEKBAR_TYPE[i] == AudioManager.STREAM_MUSIC) {
    175                 mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
    176                         SEEKBAR_TYPE[i], getMediaVolumeUri(getContext()));
    177             } else {
    178                 mSeekBarVolumizer[i] = new SeekBarVolumizer(getContext(), seekBar,
    179                         SEEKBAR_TYPE[i]);
    180             }
    181         }
    182 
    183         // Register callbacks for mute/unmute buttons
    184         for (int i = 0; i < mCheckBoxes.length; i++) {
    185             ImageView checkbox = (ImageView) view.findViewById(CHECKBOX_VIEW_ID[i]);
    186             mCheckBoxes[i] = checkbox;
    187         }
    188 
    189         // Load initial states from AudioManager
    190         updateSlidersAndMutedStates();
    191 
    192         // Listen for updates from AudioManager
    193         if (mRingModeChangedReceiver == null) {
    194             final IntentFilter filter = new IntentFilter();
    195             filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
    196             mRingModeChangedReceiver = new BroadcastReceiver() {
    197                 public void onReceive(Context context, Intent intent) {
    198                     final String action = intent.getAction();
    199                     if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
    200                         mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, intent
    201                                 .getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0));
    202                     }
    203                 }
    204             };
    205             getContext().registerReceiver(mRingModeChangedReceiver, filter);
    206         }
    207 
    208         boolean useMasterVolume = getContext().getResources().
    209                 getBoolean(com.android.internal.R.bool.config_useMasterVolume);
    210         if (useMasterVolume) {
    211             // If config_useMasterVolume is true, all streams are treated as STREAM_MASTER.
    212             // So hide all except a stream.
    213             int id;
    214             if (Utils.isVoiceCapable(getContext())) {
    215                 id = R.id.ringer_section;
    216             } else {
    217                 id = R.id.media_section;
    218             }
    219             for (int i = 0; i < SEEKBAR_SECTION_ID.length; i++) {
    220                 if (SEEKBAR_SECTION_ID[i] != id) {
    221                     view.findViewById(SEEKBAR_SECTION_ID[i]).setVisibility(View.GONE);
    222                 }
    223             }
    224         } else {
    225             // Disable either ringer+notifications or notifications
    226             int id;
    227             if (!Utils.isVoiceCapable(getContext())) {
    228                 id = R.id.ringer_section;
    229             } else {
    230                 id = R.id.notification_section;
    231             }
    232             View hideSection = view.findViewById(id);
    233             hideSection.setVisibility(View.GONE);
    234         }
    235     }
    236 
    237     private Uri getMediaVolumeUri(Context context) {
    238         return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://"
    239                 + context.getPackageName()
    240                 + "/" + R.raw.media_volume);
    241     }
    242 
    243     @Override
    244     protected void onDialogClosed(boolean positiveResult) {
    245         super.onDialogClosed(positiveResult);
    246 
    247         if (!positiveResult) {
    248             for (SeekBarVolumizer vol : mSeekBarVolumizer) {
    249                 if (vol != null) vol.revertVolume();
    250             }
    251         }
    252         cleanup();
    253     }
    254 
    255     @Override
    256     public void onActivityStop() {
    257         super.onActivityStop();
    258 
    259         for (SeekBarVolumizer vol : mSeekBarVolumizer) {
    260             if (vol != null) vol.stopSample();
    261         }
    262     }
    263 
    264     @Override
    265     public boolean onKey(View v, int keyCode, KeyEvent event) {
    266         boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
    267         switch (keyCode) {
    268             case KeyEvent.KEYCODE_VOLUME_DOWN:
    269             case KeyEvent.KEYCODE_VOLUME_UP:
    270             case KeyEvent.KEYCODE_VOLUME_MUTE:
    271                 return true;
    272             default:
    273                 return false;
    274         }
    275     }
    276 
    277     @Override
    278     protected void onSampleStarting(SeekBarVolumizer volumizer) {
    279         super.onSampleStarting(volumizer);
    280         for (SeekBarVolumizer vol : mSeekBarVolumizer) {
    281             if (vol != null && vol != volumizer) vol.stopSample();
    282         }
    283     }
    284 
    285     private void cleanup() {
    286         for (int i = 0; i < SEEKBAR_ID.length; i++) {
    287             if (mSeekBarVolumizer[i] != null) {
    288                 Dialog dialog = getDialog();
    289                 if (dialog != null && dialog.isShowing()) {
    290                     // Stopped while dialog was showing, revert changes
    291                     mSeekBarVolumizer[i].revertVolume();
    292                 }
    293                 mSeekBarVolumizer[i].stop();
    294                 mSeekBarVolumizer[i] = null;
    295             }
    296         }
    297         if (mRingModeChangedReceiver != null) {
    298             getContext().unregisterReceiver(mRingModeChangedReceiver);
    299             mRingModeChangedReceiver = null;
    300         }
    301     }
    302 
    303     @Override
    304     protected Parcelable onSaveInstanceState() {
    305         final Parcelable superState = super.onSaveInstanceState();
    306         if (isPersistent()) {
    307             // No need to save instance state since it's persistent
    308             return superState;
    309         }
    310 
    311         final SavedState myState = new SavedState(superState);
    312         VolumeStore[] volumeStore = myState.getVolumeStore(SEEKBAR_ID.length);
    313         for (int i = 0; i < SEEKBAR_ID.length; i++) {
    314             SeekBarVolumizer vol = mSeekBarVolumizer[i];
    315             if (vol != null) {
    316                 vol.onSaveInstanceState(volumeStore[i]);
    317             }
    318         }
    319         return myState;
    320     }
    321 
    322     @Override
    323     protected void onRestoreInstanceState(Parcelable state) {
    324         if (state == null || !state.getClass().equals(SavedState.class)) {
    325             // Didn't save state for us in onSaveInstanceState
    326             super.onRestoreInstanceState(state);
    327             return;
    328         }
    329 
    330         SavedState myState = (SavedState) state;
    331         super.onRestoreInstanceState(myState.getSuperState());
    332         VolumeStore[] volumeStore = myState.getVolumeStore(SEEKBAR_ID.length);
    333         for (int i = 0; i < SEEKBAR_ID.length; i++) {
    334             SeekBarVolumizer vol = mSeekBarVolumizer[i];
    335             if (vol != null) {
    336                 vol.onRestoreInstanceState(volumeStore[i]);
    337             }
    338         }
    339     }
    340 
    341     private static class SavedState extends BaseSavedState {
    342         VolumeStore [] mVolumeStore;
    343 
    344         public SavedState(Parcel source) {
    345             super(source);
    346             mVolumeStore = new VolumeStore[SEEKBAR_ID.length];
    347             for (int i = 0; i < SEEKBAR_ID.length; i++) {
    348                 mVolumeStore[i] = new VolumeStore();
    349                 mVolumeStore[i].volume = source.readInt();
    350                 mVolumeStore[i].originalVolume = source.readInt();
    351             }
    352         }
    353 
    354         @Override
    355         public void writeToParcel(Parcel dest, int flags) {
    356             super.writeToParcel(dest, flags);
    357             for (int i = 0; i < SEEKBAR_ID.length; i++) {
    358                 dest.writeInt(mVolumeStore[i].volume);
    359                 dest.writeInt(mVolumeStore[i].originalVolume);
    360             }
    361         }
    362 
    363         VolumeStore[] getVolumeStore(int count) {
    364             if (mVolumeStore == null || mVolumeStore.length != count) {
    365                 mVolumeStore = new VolumeStore[count];
    366                 for (int i = 0; i < count; i++) {
    367                     mVolumeStore[i] = new VolumeStore();
    368                 }
    369             }
    370             return mVolumeStore;
    371         }
    372 
    373         public SavedState(Parcelable superState) {
    374             super(superState);
    375         }
    376 
    377         public static final Parcelable.Creator<SavedState> CREATOR =
    378                 new Parcelable.Creator<SavedState>() {
    379             public SavedState createFromParcel(Parcel in) {
    380                 return new SavedState(in);
    381             }
    382 
    383             public SavedState[] newArray(int size) {
    384                 return new SavedState[size];
    385             }
    386         };
    387     }
    388 }
    389