VolumePreference.java revision 69f593ccb7414ee98991b1da1a4bfbd9951e3570
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 17package android.preference; 18 19import android.content.Context; 20import android.content.res.TypedArray; 21import android.database.ContentObserver; 22import android.media.Ringtone; 23import android.media.RingtoneManager; 24import android.media.AudioManager; 25import android.net.Uri; 26import android.os.Handler; 27import android.preference.PreferenceManager; 28import android.provider.Settings; 29import android.provider.Settings.System; 30import android.util.AttributeSet; 31import android.view.KeyEvent; 32import android.view.View; 33import android.widget.SeekBar; 34import android.widget.SeekBar.OnSeekBarChangeListener; 35 36/** 37 * @hide 38 */ 39public class VolumePreference extends SeekBarPreference implements 40 PreferenceManager.OnActivityStopListener, View.OnKeyListener { 41 42 private static final String TAG = "VolumePreference"; 43 44 private int mStreamType; 45 46 /** May be null if the dialog isn't visible. */ 47 private SeekBarVolumizer mSeekBarVolumizer; 48 49 public VolumePreference(Context context, AttributeSet attrs) { 50 super(context, attrs); 51 52 TypedArray a = context.obtainStyledAttributes(attrs, 53 com.android.internal.R.styleable.VolumePreference, 0, 0); 54 mStreamType = a.getInt(android.R.styleable.VolumePreference_streamType, 0); 55 a.recycle(); 56 } 57 58 public void setStreamType(int streamType) { 59 mStreamType = streamType; 60 } 61 62 @Override 63 protected void onBindDialogView(View view) { 64 super.onBindDialogView(view); 65 66 final SeekBar seekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar); 67 mSeekBarVolumizer = new SeekBarVolumizer(getContext(), seekBar, mStreamType); 68 69 getPreferenceManager().registerOnActivityStopListener(this); 70 71 // grab focus and key events so that pressing the volume buttons in the 72 // dialog doesn't also show the normal volume adjust toast. 73 view.setOnKeyListener(this); 74 view.setFocusableInTouchMode(true); 75 view.requestFocus(); 76 } 77 78 public boolean onKey(View v, int keyCode, KeyEvent event) { 79 boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN); 80 switch (keyCode) { 81 case KeyEvent.KEYCODE_VOLUME_DOWN: 82 if (isdown) { 83 mSeekBarVolumizer.changeVolumeBy(-1); 84 } 85 return true; 86 case KeyEvent.KEYCODE_VOLUME_UP: 87 if (isdown) { 88 mSeekBarVolumizer.changeVolumeBy(1); 89 } 90 return true; 91 default: 92 return false; 93 } 94 } 95 96 @Override 97 protected void onDialogClosed(boolean positiveResult) { 98 super.onDialogClosed(positiveResult); 99 100 if (!positiveResult && mSeekBarVolumizer != null) { 101 mSeekBarVolumizer.revertVolume(); 102 } 103 104 cleanup(); 105 } 106 107 public void onActivityStop() { 108 cleanup(); 109 } 110 111 /** 112 * Do clean up. This can be called multiple times! 113 */ 114 private void cleanup() { 115 getPreferenceManager().unregisterOnActivityStopListener(this); 116 117 if (mSeekBarVolumizer != null) { 118 mSeekBarVolumizer.stop(); 119 mSeekBarVolumizer = null; 120 } 121 } 122 123 protected void onSampleStarting(SeekBarVolumizer volumizer) { 124 if (mSeekBarVolumizer != null && volumizer != mSeekBarVolumizer) { 125 mSeekBarVolumizer.stopSample(); 126 } 127 } 128 129 /** 130 * Turns a {@link SeekBar} into a volume control. 131 */ 132 public class SeekBarVolumizer implements OnSeekBarChangeListener, Runnable { 133 134 private Context mContext; 135 private Handler mHandler = new Handler(); 136 137 private AudioManager mAudioManager; 138 private int mStreamType; 139 private int mOriginalStreamVolume; 140 private Ringtone mRingtone; 141 142 private int mLastProgress; 143 private SeekBar mSeekBar; 144 145 private ContentObserver mVolumeObserver = new ContentObserver(mHandler) { 146 @Override 147 public void onChange(boolean selfChange) { 148 super.onChange(selfChange); 149 150 if (mSeekBar != null) { 151 mSeekBar.setProgress(System.getInt(mContext.getContentResolver(), 152 System.VOLUME_SETTINGS[mStreamType], 0)); 153 } 154 } 155 }; 156 157 public SeekBarVolumizer(Context context, SeekBar seekBar, int streamType) { 158 mContext = context; 159 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 160 mStreamType = streamType; 161 mSeekBar = seekBar; 162 163 initSeekBar(seekBar); 164 } 165 166 private void initSeekBar(SeekBar seekBar) { 167 seekBar.setMax(mAudioManager.getStreamMaxVolume(mStreamType)); 168 mOriginalStreamVolume = mAudioManager.getStreamVolume(mStreamType); 169 seekBar.setProgress(mOriginalStreamVolume); 170 seekBar.setOnSeekBarChangeListener(this); 171 172 mContext.getContentResolver().registerContentObserver( 173 System.getUriFor(System.VOLUME_SETTINGS[mStreamType]), 174 false, mVolumeObserver); 175 176 Uri defaultUri = null; 177 if (mStreamType == AudioManager.STREAM_RING) { 178 defaultUri = Settings.System.DEFAULT_RINGTONE_URI; 179 } else if (mStreamType == AudioManager.STREAM_NOTIFICATION) { 180 defaultUri = Settings.System.DEFAULT_NOTIFICATION_URI; 181 } else { 182 defaultUri = Settings.System.DEFAULT_ALARM_ALERT_URI; 183 } 184 185 mRingtone = RingtoneManager.getRingtone(mContext, defaultUri); 186 if (mRingtone != null) { 187 mRingtone.setStreamType(mStreamType); 188 } 189 } 190 191 public void stop() { 192 stopSample(); 193 mContext.getContentResolver().unregisterContentObserver(mVolumeObserver); 194 mSeekBar.setOnSeekBarChangeListener(null); 195 } 196 197 public void revertVolume() { 198 mAudioManager.setStreamVolume(mStreamType, mOriginalStreamVolume, 0); 199 } 200 201 public void onProgressChanged(SeekBar seekBar, int progress, 202 boolean fromTouch) { 203 if (!fromTouch) { 204 return; 205 } 206 207 postSetVolume(progress); 208 } 209 210 private void postSetVolume(int progress) { 211 // Do the volume changing separately to give responsive UI 212 mLastProgress = progress; 213 mHandler.removeCallbacks(this); 214 mHandler.post(this); 215 } 216 217 public void onStartTrackingTouch(SeekBar seekBar) { 218 } 219 220 public void onStopTrackingTouch(SeekBar seekBar) { 221 if (mRingtone != null && !mRingtone.isPlaying()) { 222 sample(); 223 } 224 } 225 226 public void run() { 227 mAudioManager.setStreamVolume(mStreamType, mLastProgress, 0); 228 } 229 230 private void sample() { 231 onSampleStarting(this); 232 mRingtone.play(); 233 } 234 235 public void stopSample() { 236 if (mRingtone != null) { 237 mRingtone.stop(); 238 } 239 } 240 241 public SeekBar getSeekBar() { 242 return mSeekBar; 243 } 244 245 public void changeVolumeBy(int amount) { 246 mSeekBar.incrementProgressBy(amount); 247 if (mRingtone != null && !mRingtone.isPlaying()) { 248 sample(); 249 } 250 postSetVolume(mSeekBar.getProgress()); 251 } 252 } 253} 254