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. */ 40 public void onNear(); 41 /** Called when the proximity sensor transitions from the near to the far state. */ 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 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 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 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. */ 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 */ 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. */ 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 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") 179 private void unregisterWithoutNotification() { 180 mSensorManager.unregisterListener(this); 181 mWaitingForFarState = false; 182 } 183 } 184 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 */ 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 */ 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