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