Home | History | Annotate | Download | only in contacts
      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.contacts;
     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