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