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.dialer;
18 
19 import android.content.Context;
20 import android.hardware.Sensor;
21 import android.hardware.SensorEvent;
22 import android.hardware.SensorEventListener;
23 import android.hardware.SensorManager;
24 
25 import javax.annotation.concurrent.GuardedBy;
26 
27 /**
28  * Manages the proximity sensor and notifies a listener when enabled.
29  */
30 public class ProximitySensorManager {
31     /**
32      * Listener of the state of the proximity sensor.
33      * <p>
34      * This interface abstracts two possible states for the proximity sensor, near and far.
35      * <p>
36      * The actual meaning of these states depends on the actual sensor.
37      */
38     public interface Listener {
39         /** Called when the proximity sensor transitions from the far to the near state. */
onNear()40         public void onNear();
41         /** Called when the proximity sensor transitions from the near to the far state. */
onFar()42         public void onFar();
43     }
44 
45     public static enum State {
46         NEAR, FAR
47     }
48 
49     private final ProximitySensorEventListener mProximitySensorListener;
50 
51     /**
52      * The current state of the manager, i.e., whether it is currently tracking the state of the
53      * sensor.
54      */
55     private boolean mManagerEnabled;
56 
57     /**
58      * The listener to the state of the sensor.
59      * <p>
60      * Contains most of the logic concerning tracking of the sensor.
61      * <p>
62      * After creating an instance of this object, one should call {@link #register()} and
63      * {@link #unregister()} to enable and disable the notifications.
64      * <p>
65      * Instead of calling unregister, one can call {@link #unregisterWhenFar()} to unregister the
66      * listener the next time the sensor reaches the {@link State#FAR} state if currently in the
67      * {@link State#NEAR} state.
68      */
69     private static class ProximitySensorEventListener implements SensorEventListener {
70         private static final float FAR_THRESHOLD = 5.0f;
71 
72         private final SensorManager mSensorManager;
73         private final Sensor mProximitySensor;
74         private final float mMaxValue;
75         private final Listener mListener;
76 
77         /**
78          * The last state of the sensor.
79          * <p>
80          * Before registering and after unregistering we are always in the {@link State#FAR} state.
81          */
82         @GuardedBy("this") private State mLastState;
83         /**
84          * If this flag is set to true, we are waiting to reach the {@link State#FAR} state and
85          * should notify the listener and unregister when that happens.
86          */
87         @GuardedBy("this") private boolean mWaitingForFarState;
88 
ProximitySensorEventListener(SensorManager sensorManager, Sensor proximitySensor, Listener listener)89         public ProximitySensorEventListener(SensorManager sensorManager, Sensor proximitySensor,
90                 Listener listener) {
91             mSensorManager = sensorManager;
92             mProximitySensor = proximitySensor;
93             mMaxValue = proximitySensor.getMaximumRange();
94             mListener = listener;
95             // Initialize at far state.
96             mLastState = State.FAR;
97             mWaitingForFarState = false;
98         }
99 
100         @Override
onSensorChanged(SensorEvent event)101         public void onSensorChanged(SensorEvent event) {
102             // Make sure we have a valid value.
103             if (event.values == null) return;
104             if (event.values.length == 0) return;
105             float value = event.values[0];
106             // Convert the sensor into a NEAR/FAR state.
107             State state = getStateFromValue(value);
108             synchronized (this) {
109                 // No change in state, do nothing.
110                 if (state == mLastState) return;
111                 // Keep track of the current state.
112                 mLastState = state;
113                 // If we are waiting to reach the far state and we are now in it, unregister.
114                 if (mWaitingForFarState && mLastState == State.FAR) {
115                     unregisterWithoutNotification();
116                 }
117             }
118             // Notify the listener of the state change.
119             switch (state) {
120                 case NEAR:
121                     mListener.onNear();
122                     break;
123 
124                 case FAR:
125                     mListener.onFar();
126                     break;
127             }
128         }
129 
130         @Override
onAccuracyChanged(Sensor sensor, int accuracy)131         public void onAccuracyChanged(Sensor sensor, int accuracy) {
132             // Nothing to do here.
133         }
134 
135         /** Returns the state of the sensor given its current value. */
getStateFromValue(float value)136         private State getStateFromValue(float value) {
137             // Determine if the current value corresponds to the NEAR or FAR state.
138             // Take case of the case where the proximity sensor is binary: if the current value is
139             // equal to the maximum, we are always in the FAR state.
140             return (value > FAR_THRESHOLD || value == mMaxValue) ? State.FAR : State.NEAR;
141         }
142 
143         /**
144          * Unregister the next time the sensor reaches the {@link State#FAR} state.
145          */
unregisterWhenFar()146         public synchronized void unregisterWhenFar() {
147             if (mLastState == State.FAR) {
148                 // We are already in the far state, just unregister now.
149                 unregisterWithoutNotification();
150             } else {
151                 mWaitingForFarState = true;
152             }
153         }
154 
155         /** Register the listener and call the listener as necessary. */
register()156         public synchronized void register() {
157             // It is okay to register multiple times.
158             mSensorManager.registerListener(this, mProximitySensor, SensorManager.SENSOR_DELAY_UI);
159             // We should no longer be waiting for the far state if we are registering again.
160             mWaitingForFarState = false;
161         }
162 
unregister()163         public void unregister() {
164             State lastState;
165             synchronized (this) {
166                 unregisterWithoutNotification();
167                 lastState = mLastState;
168                 // Always go back to the FAR state. That way, when we register again we will get a
169                 // transition when the sensor gets into the NEAR state.
170                 mLastState = State.FAR;
171             }
172             // Notify the listener if we changed the state to FAR while unregistering.
173             if (lastState != State.FAR) {
174                 mListener.onFar();
175             }
176         }
177 
178         @GuardedBy("this")
unregisterWithoutNotification()179         private void unregisterWithoutNotification() {
180             mSensorManager.unregisterListener(this);
181             mWaitingForFarState = false;
182         }
183     }
184 
ProximitySensorManager(Context context, Listener listener)185     public ProximitySensorManager(Context context, Listener listener) {
186         SensorManager sensorManager =
187                 (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
188         Sensor proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
189         if (proximitySensor == null) {
190             // If there is no sensor, we should not do anything.
191             mProximitySensorListener = null;
192         } else {
193             mProximitySensorListener =
194                     new ProximitySensorEventListener(sensorManager, proximitySensor, listener);
195         }
196     }
197 
198     /**
199      * Enables the proximity manager.
200      * <p>
201      * The listener will start getting notifications of events.
202      * <p>
203      * This method is idempotent.
204      */
enable()205     public void enable() {
206         if (mProximitySensorListener != null && !mManagerEnabled) {
207             mProximitySensorListener.register();
208             mManagerEnabled = true;
209         }
210     }
211 
212     /**
213      * Disables the proximity manager.
214      * <p>
215      * The listener will stop receiving notifications of events, possibly after receiving a last
216      * {@link Listener#onFar()} callback.
217      * <p>
218      * If {@code waitForFarState} is true, if the sensor is not currently in the {@link State#FAR}
219      * state, the listener will receive a {@link Listener#onFar()} callback the next time the sensor
220      * actually reaches the {@link State#FAR} state.
221      * <p>
222      * If {@code waitForFarState} is false, the listener will receive a {@link Listener#onFar()}
223      * callback immediately if the sensor is currently not in the {@link State#FAR} state.
224      * <p>
225      * This method is idempotent.
226      */
disable(boolean waitForFarState)227     public void disable(boolean waitForFarState) {
228         if (mProximitySensorListener != null && mManagerEnabled) {
229             if (waitForFarState) {
230                 mProximitySensorListener.unregisterWhenFar();
231             } else {
232                 mProximitySensorListener.unregister();
233             }
234             mManagerEnabled = false;
235         }
236     }
237 }
238