1 /*
2  * Copyright (C) 2011 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.view.HapticFeedbackConstants.CLOCK_TICK;
20 
21 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SETTINGS_SLIDER;
22 
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.database.ContentObserver;
26 import android.hardware.input.InputManager;
27 import android.hardware.input.InputSettings;
28 import android.os.Handler;
29 import android.os.Parcel;
30 import android.os.Parcelable;
31 import android.provider.Settings;
32 import android.util.AttributeSet;
33 import android.view.View;
34 import android.widget.SeekBar;
35 
36 import com.android.internal.jank.InteractionJankMonitor;
37 
38 public class PointerSpeedPreference extends SeekBarDialogPreference implements
39         SeekBar.OnSeekBarChangeListener {
40     private final InputManager mIm;
41     private final InteractionJankMonitor mJankMonitor = InteractionJankMonitor.getInstance();
42     private SeekBar mSeekBar;
43 
44     private int mOldSpeed;
45     private boolean mRestoredOldState;
46 
47     private boolean mTouchInProgress;
48     private int mLastProgress = -1;
49 
50     private ContentObserver mSpeedObserver = new ContentObserver(new Handler()) {
51         @Override
52         public void onChange(boolean selfChange) {
53             onSpeedChanged();
54         }
55     };
56 
PointerSpeedPreference(Context context, AttributeSet attrs)57     public PointerSpeedPreference(Context context, AttributeSet attrs) {
58         super(context, attrs);
59         mIm = (InputManager)getContext().getSystemService(Context.INPUT_SERVICE);
60     }
61 
62     @Override
onClick()63     protected void onClick() {
64         super.onClick();
65 
66         getContext().getContentResolver().registerContentObserver(
67                 Settings.System.getUriFor(Settings.System.POINTER_SPEED), true,
68                 mSpeedObserver);
69 
70         mRestoredOldState = false;
71     }
72 
73     @Override
onBindDialogView(View view)74     protected void onBindDialogView(View view) {
75         super.onBindDialogView(view);
76 
77         mSeekBar = getSeekBar(view);
78         mSeekBar.setMax(InputSettings.MAX_POINTER_SPEED - InputSettings.MIN_POINTER_SPEED);
79         mOldSpeed = InputSettings.getPointerSpeed(getContext());
80         mSeekBar.setProgress(mOldSpeed - InputSettings.MIN_POINTER_SPEED);
81         mSeekBar.setOnSeekBarChangeListener(this);
82         mSeekBar.setContentDescription(getTitle());
83     }
84 
onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch)85     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
86         if (!mTouchInProgress) {
87             mIm.tryPointerSpeed(progress + InputSettings.MIN_POINTER_SPEED);
88         }
89         if (progress != mLastProgress) {
90             seekBar.performHapticFeedback(CLOCK_TICK);
91             mLastProgress = progress;
92         }
93     }
94 
onStartTrackingTouch(SeekBar seekBar)95     public void onStartTrackingTouch(SeekBar seekBar) {
96         mTouchInProgress = true;
97         mJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
98                 .withView(CUJ_SETTINGS_SLIDER, seekBar)
99                 .setTag(getKey()));
100     }
101 
onStopTrackingTouch(SeekBar seekBar)102     public void onStopTrackingTouch(SeekBar seekBar) {
103         mTouchInProgress = false;
104         mIm.tryPointerSpeed(seekBar.getProgress() + InputSettings.MIN_POINTER_SPEED);
105         mJankMonitor.end(CUJ_SETTINGS_SLIDER);
106     }
107 
onSpeedChanged()108     private void onSpeedChanged() {
109         int speed = InputSettings.getPointerSpeed(getContext());
110         mSeekBar.setProgress(speed - InputSettings.MIN_POINTER_SPEED);
111     }
112 
113     @Override
onDialogClosed(boolean positiveResult)114     protected void onDialogClosed(boolean positiveResult) {
115         super.onDialogClosed(positiveResult);
116 
117         final ContentResolver resolver = getContext().getContentResolver();
118 
119         if (positiveResult) {
120             InputSettings.setPointerSpeed(getContext(),
121                     mSeekBar.getProgress() + InputSettings.MIN_POINTER_SPEED);
122         } else {
123             restoreOldState();
124         }
125 
126         resolver.unregisterContentObserver(mSpeedObserver);
127     }
128 
restoreOldState()129     private void restoreOldState() {
130         if (mRestoredOldState) return;
131 
132         mIm.tryPointerSpeed(mOldSpeed);
133         mRestoredOldState = true;
134     }
135 
136     @Override
onSaveInstanceState()137     protected Parcelable onSaveInstanceState() {
138         final Parcelable superState = super.onSaveInstanceState();
139         if (getDialog() == null || !getDialog().isShowing()) return superState;
140 
141         // Save the dialog state
142         final SavedState myState = new SavedState(superState);
143         myState.progress = mSeekBar.getProgress();
144         myState.oldSpeed = mOldSpeed;
145 
146         // Restore the old state when the activity or dialog is being paused
147         restoreOldState();
148         return myState;
149     }
150 
151     @Override
onRestoreInstanceState(Parcelable state)152     protected void onRestoreInstanceState(Parcelable state) {
153         if (state == null || !state.getClass().equals(SavedState.class)) {
154             // Didn't save state for us in onSaveInstanceState
155             super.onRestoreInstanceState(state);
156             return;
157         }
158 
159         SavedState myState = (SavedState) state;
160         super.onRestoreInstanceState(myState.getSuperState());
161         mOldSpeed = myState.oldSpeed;
162         mIm.tryPointerSpeed(myState.progress + InputSettings.MIN_POINTER_SPEED);
163     }
164 
165     private static class SavedState extends BaseSavedState {
166         int progress;
167         int oldSpeed;
168 
SavedState(Parcel source)169         public SavedState(Parcel source) {
170             super(source);
171             progress = source.readInt();
172             oldSpeed = source.readInt();
173         }
174 
175         @Override
writeToParcel(Parcel dest, int flags)176         public void writeToParcel(Parcel dest, int flags) {
177             super.writeToParcel(dest, flags);
178             dest.writeInt(progress);
179             dest.writeInt(oldSpeed);
180         }
181 
SavedState(Parcelable superState)182         public SavedState(Parcelable superState) {
183             super(superState);
184         }
185 
186         public static final Parcelable.Creator<SavedState> CREATOR =
187                 new Parcelable.Creator<SavedState>() {
188 
189             public SavedState createFromParcel(Parcel in) {
190                 return new SavedState(in);
191             }
192 
193             public SavedState[] newArray(int size) {
194                 return new SavedState[size];
195             }
196         };
197     }
198 }
199 
200