| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.contacts; |
| |
| import android.content.Context; |
| import android.hardware.Sensor; |
| import android.hardware.SensorEvent; |
| import android.hardware.SensorEventListener; |
| import android.hardware.SensorManager; |
| |
| import javax.annotation.concurrent.GuardedBy; |
| |
| /** |
| * Manages the proximity sensor and notifies a listener when enabled. |
| */ |
| public class ProximitySensorManager { |
| /** |
| * Listener of the state of the proximity sensor. |
| * <p> |
| * This interface abstracts two possible states for the proximity sensor, near and far. |
| * <p> |
| * The actual meaning of these states depends on the actual sensor. |
| */ |
| public interface Listener { |
| /** Called when the proximity sensor transitions from the far to the near state. */ |
| public void onNear(); |
| /** Called when the proximity sensor transitions from the near to the far state. */ |
| public void onFar(); |
| } |
| |
| public static enum State { |
| NEAR, FAR |
| } |
| |
| private final ProximitySensorEventListener mProximitySensorListener; |
| |
| /** |
| * The current state of the manager, i.e., whether it is currently tracking the state of the |
| * sensor. |
| */ |
| private boolean mManagerEnabled; |
| |
| /** |
| * The listener to the state of the sensor. |
| * <p> |
| * Contains most of the logic concerning tracking of the sensor. |
| * <p> |
| * After creating an instance of this object, one should call {@link #register()} and |
| * {@link #unregister()} to enable and disable the notifications. |
| * <p> |
| * Instead of calling unregister, one can call {@link #unregisterWhenFar()} to unregister the |
| * listener the next time the sensor reaches the {@link State#FAR} state if currently in the |
| * {@link State#NEAR} state. |
| */ |
| private static class ProximitySensorEventListener implements SensorEventListener { |
| private static final float FAR_THRESHOLD = 5.0f; |
| |
| private final SensorManager mSensorManager; |
| private final Sensor mProximitySensor; |
| private final float mMaxValue; |
| private final Listener mListener; |
| |
| /** |
| * The last state of the sensor. |
| * <p> |
| * Before registering and after unregistering we are always in the {@link State#FAR} state. |
| */ |
| @GuardedBy("this") private State mLastState; |
| /** |
| * If this flag is set to true, we are waiting to reach the {@link State#FAR} state and |
| * should notify the listener and unregister when that happens. |
| */ |
| @GuardedBy("this") private boolean mWaitingForFarState; |
| |
| public ProximitySensorEventListener(SensorManager sensorManager, Sensor proximitySensor, |
| Listener listener) { |
| mSensorManager = sensorManager; |
| mProximitySensor = proximitySensor; |
| mMaxValue = proximitySensor.getMaximumRange(); |
| mListener = listener; |
| // Initialize at far state. |
| mLastState = State.FAR; |
| mWaitingForFarState = false; |
| } |
| |
| @Override |
| public void onSensorChanged(SensorEvent event) { |
| // Make sure we have a valid value. |
| if (event.values == null) return; |
| if (event.values.length == 0) return; |
| float value = event.values[0]; |
| // Convert the sensor into a NEAR/FAR state. |
| State state = getStateFromValue(value); |
| synchronized (this) { |
| // No change in state, do nothing. |
| if (state == mLastState) return; |
| // Keep track of the current state. |
| mLastState = state; |
| // If we are waiting to reach the far state and we are now in it, unregister. |
| if (mWaitingForFarState && mLastState == State.FAR) { |
| unregisterWithoutNotification(); |
| } |
| } |
| // Notify the listener of the state change. |
| switch (state) { |
| case NEAR: |
| mListener.onNear(); |
| break; |
| |
| case FAR: |
| mListener.onFar(); |
| break; |
| } |
| } |
| |
| @Override |
| public void onAccuracyChanged(Sensor sensor, int accuracy) { |
| // Nothing to do here. |
| } |
| |
| /** Returns the state of the sensor given its current value. */ |
| private State getStateFromValue(float value) { |
| // Determine if the current value corresponds to the NEAR or FAR state. |
| // Take case of the case where the proximity sensor is binary: if the current value is |
| // equal to the maximum, we are always in the FAR state. |
| return (value > FAR_THRESHOLD || value == mMaxValue) ? State.FAR : State.NEAR; |
| } |
| |
| /** |
| * Unregister the next time the sensor reaches the {@link State#FAR} state. |
| */ |
| public synchronized void unregisterWhenFar() { |
| if (mLastState == State.FAR) { |
| // We are already in the far state, just unregister now. |
| unregisterWithoutNotification(); |
| } else { |
| mWaitingForFarState = true; |
| } |
| } |
| |
| /** Register the listener and call the listener as necessary. */ |
| public synchronized void register() { |
| // It is okay to register multiple times. |
| mSensorManager.registerListener(this, mProximitySensor, SensorManager.SENSOR_DELAY_UI); |
| // We should no longer be waiting for the far state if we are registering again. |
| mWaitingForFarState = false; |
| } |
| |
| public void unregister() { |
| State lastState; |
| synchronized (this) { |
| unregisterWithoutNotification(); |
| lastState = mLastState; |
| // Always go back to the FAR state. That way, when we register again we will get a |
| // transition when the sensor gets into the NEAR state. |
| mLastState = State.FAR; |
| } |
| // Notify the listener if we changed the state to FAR while unregistering. |
| if (lastState != State.FAR) { |
| mListener.onFar(); |
| } |
| } |
| |
| @GuardedBy("this") |
| private void unregisterWithoutNotification() { |
| mSensorManager.unregisterListener(this); |
| mWaitingForFarState = false; |
| } |
| } |
| |
| public ProximitySensorManager(Context context, Listener listener) { |
| SensorManager sensorManager = |
| (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); |
| Sensor proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); |
| if (proximitySensor == null) { |
| // If there is no sensor, we should not do anything. |
| mProximitySensorListener = null; |
| } else { |
| mProximitySensorListener = |
| new ProximitySensorEventListener(sensorManager, proximitySensor, listener); |
| } |
| } |
| |
| /** |
| * Enables the proximity manager. |
| * <p> |
| * The listener will start getting notifications of events. |
| * <p> |
| * This method is idempotent. |
| */ |
| public void enable() { |
| if (mProximitySensorListener != null && !mManagerEnabled) { |
| mProximitySensorListener.register(); |
| mManagerEnabled = true; |
| } |
| } |
| |
| /** |
| * Disables the proximity manager. |
| * <p> |
| * The listener will stop receiving notifications of events, possibly after receiving a last |
| * {@link Listener#onFar()} callback. |
| * <p> |
| * If {@code waitForFarState} is true, if the sensor is not currently in the {@link State#FAR} |
| * state, the listener will receive a {@link Listener#onFar()} callback the next time the sensor |
| * actually reaches the {@link State#FAR} state. |
| * <p> |
| * If {@code waitForFarState} is false, the listener will receive a {@link Listener#onFar()} |
| * callback immediately if the sensor is currently not in the {@link State#FAR} state. |
| * <p> |
| * This method is idempotent. |
| */ |
| public void disable(boolean waitForFarState) { |
| if (mProximitySensorListener != null && mManagerEnabled) { |
| if (waitForFarState) { |
| mProximitySensorListener.unregisterWhenFar(); |
| } else { |
| mProximitySensorListener.unregister(); |
| } |
| mManagerEnabled = false; |
| } |
| } |
| } |