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 
17 package android.preference;
18 
19 import android.app.Dialog;
20 import android.content.Context;
21 import android.content.res.TypedArray;
22 import android.os.Parcel;
23 import android.os.Parcelable;
24 import android.util.AttributeSet;
25 import android.view.KeyEvent;
26 import android.view.View;
27 import android.widget.SeekBar;
28 
29 import com.android.internal.R;
30 
31 /**
32  * @hide
33  */
34 public class VolumePreference extends SeekBarDialogPreference implements
35         PreferenceManager.OnActivityStopListener, View.OnKeyListener, SeekBarVolumizer.Callback {
36     private int mStreamType;
37 
38     /** May be null if the dialog isn't visible. */
39     private SeekBarVolumizer mSeekBarVolumizer;
40 
VolumePreference( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)41     public VolumePreference(
42             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
43         super(context, attrs, defStyleAttr, defStyleRes);
44 
45         final TypedArray a = context.obtainStyledAttributes(attrs,
46                 R.styleable.VolumePreference, defStyleAttr, defStyleRes);
47         mStreamType = a.getInt(android.R.styleable.VolumePreference_streamType, 0);
48         a.recycle();
49     }
50 
VolumePreference(Context context, AttributeSet attrs, int defStyleAttr)51     public VolumePreference(Context context, AttributeSet attrs, int defStyleAttr) {
52         this(context, attrs, defStyleAttr, 0);
53     }
54 
VolumePreference(Context context, AttributeSet attrs)55     public VolumePreference(Context context, AttributeSet attrs) {
56         this(context, attrs, R.attr.seekBarDialogPreferenceStyle);
57     }
58 
VolumePreference(Context context)59     public VolumePreference(Context context) {
60         this(context, null);
61     }
62 
setStreamType(int streamType)63     public void setStreamType(int streamType) {
64         mStreamType = streamType;
65     }
66 
67     @Override
onBindDialogView(View view)68     protected void onBindDialogView(View view) {
69         super.onBindDialogView(view);
70 
71         final SeekBar seekBar = (SeekBar) view.findViewById(R.id.seekbar);
72         mSeekBarVolumizer = new SeekBarVolumizer(getContext(), mStreamType, null, this);
73         mSeekBarVolumizer.start();
74         mSeekBarVolumizer.setSeekBar(seekBar);
75 
76         getPreferenceManager().registerOnActivityStopListener(this);
77 
78         // grab focus and key events so that pressing the volume buttons in the
79         // dialog doesn't also show the normal volume adjust toast.
80         view.setOnKeyListener(this);
81         view.setFocusableInTouchMode(true);
82         view.requestFocus();
83     }
84 
onKey(View v, int keyCode, KeyEvent event)85     public boolean onKey(View v, int keyCode, KeyEvent event) {
86         // If key arrives immediately after the activity has been cleaned up.
87         if (mSeekBarVolumizer == null) return true;
88         boolean isdown = (event.getAction() == KeyEvent.ACTION_DOWN);
89         switch (keyCode) {
90             case KeyEvent.KEYCODE_VOLUME_DOWN:
91                 if (isdown) {
92                     mSeekBarVolumizer.changeVolumeBy(-1);
93                 }
94                 return true;
95             case KeyEvent.KEYCODE_VOLUME_UP:
96                 if (isdown) {
97                     mSeekBarVolumizer.changeVolumeBy(1);
98                 }
99                 return true;
100             case KeyEvent.KEYCODE_VOLUME_MUTE:
101                 if (isdown) {
102                     mSeekBarVolumizer.muteVolume();
103                 }
104                 return true;
105             default:
106                 return false;
107         }
108     }
109 
110     @Override
onDialogClosed(boolean positiveResult)111     protected void onDialogClosed(boolean positiveResult) {
112         super.onDialogClosed(positiveResult);
113 
114         if (!positiveResult && mSeekBarVolumizer != null) {
115             mSeekBarVolumizer.revertVolume();
116         }
117 
118         cleanup();
119     }
120 
onActivityStop()121     public void onActivityStop() {
122         if (mSeekBarVolumizer != null) {
123             mSeekBarVolumizer.stopSample();
124         }
125     }
126 
127     /**
128      * Do clean up.  This can be called multiple times!
129      */
cleanup()130     private void cleanup() {
131        getPreferenceManager().unregisterOnActivityStopListener(this);
132 
133        if (mSeekBarVolumizer != null) {
134            final Dialog dialog = getDialog();
135            if (dialog != null && dialog.isShowing()) {
136                final View view = dialog.getWindow().getDecorView().findViewById(R.id.seekbar);
137                if (view != null) {
138                    view.setOnKeyListener(null);
139                }
140 
141                // Stopped while dialog was showing, revert changes
142                mSeekBarVolumizer.revertVolume();
143            }
144 
145            mSeekBarVolumizer.stop();
146            mSeekBarVolumizer = null;
147        }
148 
149     }
150 
151     @Override
onSampleStarting(SeekBarVolumizer volumizer)152     public void onSampleStarting(SeekBarVolumizer volumizer) {
153         if (mSeekBarVolumizer != null && volumizer != mSeekBarVolumizer) {
154             mSeekBarVolumizer.stopSample();
155         }
156     }
157 
158     @Override
onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch)159     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
160         // noop
161     }
162 
163     @Override
onMuted(boolean muted, boolean zenMuted)164     public void onMuted(boolean muted, boolean zenMuted) {
165         // noop
166     }
167 
168     @Override
onSaveInstanceState()169     protected Parcelable onSaveInstanceState() {
170         final Parcelable superState = super.onSaveInstanceState();
171         if (isPersistent()) {
172             // No need to save instance state since it's persistent
173             return superState;
174         }
175 
176         final SavedState myState = new SavedState(superState);
177         if (mSeekBarVolumizer != null) {
178             mSeekBarVolumizer.onSaveInstanceState(myState.getVolumeStore());
179         }
180         return myState;
181     }
182 
183     @Override
onRestoreInstanceState(Parcelable state)184     protected void onRestoreInstanceState(Parcelable state) {
185         if (state == null || !state.getClass().equals(SavedState.class)) {
186             // Didn't save state for us in onSaveInstanceState
187             super.onRestoreInstanceState(state);
188             return;
189         }
190 
191         SavedState myState = (SavedState) state;
192         super.onRestoreInstanceState(myState.getSuperState());
193         if (mSeekBarVolumizer != null) {
194             mSeekBarVolumizer.onRestoreInstanceState(myState.getVolumeStore());
195         }
196     }
197 
198     public static class VolumeStore {
199         public int volume = -1;
200         public int originalVolume = -1;
201     }
202 
203     private static class SavedState extends BaseSavedState {
204         VolumeStore mVolumeStore = new VolumeStore();
205 
SavedState(Parcel source)206         public SavedState(Parcel source) {
207             super(source);
208             mVolumeStore.volume = source.readInt();
209             mVolumeStore.originalVolume = source.readInt();
210         }
211 
212         @Override
writeToParcel(Parcel dest, int flags)213         public void writeToParcel(Parcel dest, int flags) {
214             super.writeToParcel(dest, flags);
215             dest.writeInt(mVolumeStore.volume);
216             dest.writeInt(mVolumeStore.originalVolume);
217         }
218 
getVolumeStore()219         VolumeStore getVolumeStore() {
220             return mVolumeStore;
221         }
222 
SavedState(Parcelable superState)223         public SavedState(Parcelable superState) {
224             super(superState);
225         }
226 
227         public static final Parcelable.Creator<SavedState> CREATOR =
228                 new Parcelable.Creator<SavedState>() {
229             public SavedState createFromParcel(Parcel in) {
230                 return new SavedState(in);
231             }
232 
233             public SavedState[] newArray(int size) {
234                 return new SavedState[size];
235             }
236         };
237     }
238 }
239