Home | History | Annotate | Download | only in apprtc
      1 /*
      2  *  Copyright 2014 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 package org.appspot.apprtc;
     12 
     13 import org.appspot.apprtc.util.AppRTCUtils;
     14 import org.appspot.apprtc.util.AppRTCUtils.NonThreadSafe;
     15 
     16 import android.content.Context;
     17 import android.hardware.Sensor;
     18 import android.hardware.SensorEvent;
     19 import android.hardware.SensorEventListener;
     20 import android.hardware.SensorManager;
     21 import android.os.Build;
     22 import android.util.Log;
     23 
     24 /**
     25  * AppRTCProximitySensor manages functions related to the proximity sensor in
     26  * the AppRTC demo.
     27  * On most device, the proximity sensor is implemented as a boolean-sensor.
     28  * It returns just two values "NEAR" or "FAR". Thresholding is done on the LUX
     29  * value i.e. the LUX value of the light sensor is compared with a threshold.
     30  * A LUX-value more than the threshold means the proximity sensor returns "FAR".
     31  * Anything less than the threshold value and the sensor  returns "NEAR".
     32  */
     33 public class AppRTCProximitySensor implements SensorEventListener {
     34   private static final String TAG = "AppRTCProximitySensor";
     35 
     36   // This class should be created, started and stopped on one thread
     37   // (e.g. the main thread). We use |nonThreadSafe| to ensure that this is
     38   // the case. Only active when |DEBUG| is set to true.
     39   private final NonThreadSafe nonThreadSafe = new AppRTCUtils.NonThreadSafe();
     40 
     41   private final Runnable onSensorStateListener;
     42   private final SensorManager sensorManager;
     43   private Sensor proximitySensor = null;
     44   private boolean lastStateReportIsNear = false;
     45 
     46   /** Construction */
     47   static AppRTCProximitySensor create(Context context,
     48       Runnable sensorStateListener) {
     49     return new AppRTCProximitySensor(context, sensorStateListener);
     50   }
     51 
     52   private AppRTCProximitySensor(Context context, Runnable sensorStateListener) {
     53     Log.d(TAG, "AppRTCProximitySensor" + AppRTCUtils.getThreadInfo());
     54     onSensorStateListener = sensorStateListener;
     55     sensorManager = ((SensorManager) context.getSystemService(
     56         Context.SENSOR_SERVICE));
     57   }
     58 
     59   /**
     60    * Activate the proximity sensor. Also do initializtion if called for the
     61    * first time.
     62    */
     63   public boolean start() {
     64     checkIfCalledOnValidThread();
     65     Log.d(TAG, "start" + AppRTCUtils.getThreadInfo());
     66     if (!initDefaultSensor()) {
     67       // Proximity sensor is not supported on this device.
     68       return false;
     69     }
     70     sensorManager.registerListener(
     71         this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
     72     return true;
     73   }
     74 
     75   /** Deactivate the proximity sensor. */
     76   public void stop() {
     77     checkIfCalledOnValidThread();
     78     Log.d(TAG, "stop" + AppRTCUtils.getThreadInfo());
     79     if (proximitySensor == null) {
     80       return;
     81     }
     82     sensorManager.unregisterListener(this, proximitySensor);
     83   }
     84 
     85   /** Getter for last reported state. Set to true if "near" is reported. */
     86   public boolean sensorReportsNearState() {
     87     checkIfCalledOnValidThread();
     88     return lastStateReportIsNear;
     89   }
     90 
     91   @Override
     92   public final void onAccuracyChanged(Sensor sensor, int accuracy) {
     93     checkIfCalledOnValidThread();
     94     AppRTCUtils.assertIsTrue(sensor.getType() == Sensor.TYPE_PROXIMITY);
     95     if (accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE) {
     96       Log.e(TAG, "The values returned by this sensor cannot be trusted");
     97     }
     98   }
     99 
    100   @Override
    101   public final void onSensorChanged(SensorEvent event) {
    102     checkIfCalledOnValidThread();
    103     AppRTCUtils.assertIsTrue(event.sensor.getType() == Sensor.TYPE_PROXIMITY);
    104     // As a best practice; do as little as possible within this method and
    105     // avoid blocking.
    106     float distanceInCentimeters = event.values[0];
    107     if (distanceInCentimeters < proximitySensor.getMaximumRange()) {
    108       Log.d(TAG, "Proximity sensor => NEAR state");
    109       lastStateReportIsNear = true;
    110     } else {
    111       Log.d(TAG, "Proximity sensor => FAR state");
    112       lastStateReportIsNear = false;
    113     }
    114 
    115     // Report about new state to listening client. Client can then call
    116     // sensorReportsNearState() to query the current state (NEAR or FAR).
    117     if (onSensorStateListener != null) {
    118       onSensorStateListener.run();
    119     }
    120 
    121     Log.d(TAG, "onSensorChanged" + AppRTCUtils.getThreadInfo() + ": "
    122         + "accuracy=" + event.accuracy
    123         + ", timestamp=" + event.timestamp + ", distance=" + event.values[0]);
    124   }
    125 
    126   /**
    127    * Get default proximity sensor if it exists. Tablet devices (e.g. Nexus 7)
    128    * does not support this type of sensor and false will be retured in such
    129    * cases.
    130    */
    131   private boolean initDefaultSensor() {
    132     if (proximitySensor != null) {
    133       return true;
    134     }
    135     proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
    136     if (proximitySensor == null) {
    137       return false;
    138     }
    139     logProximitySensorInfo();
    140     return true;
    141   }
    142 
    143   /** Helper method for logging information about the proximity sensor. */
    144   private void logProximitySensorInfo() {
    145     if (proximitySensor == null) {
    146       return;
    147     }
    148     StringBuilder info = new StringBuilder("Proximity sensor: ");
    149     info.append("name=" + proximitySensor.getName());
    150     info.append(", vendor: " + proximitySensor.getVendor());
    151     info.append(", power: " + proximitySensor.getPower());
    152     info.append(", resolution: " + proximitySensor.getResolution());
    153     info.append(", max range: " + proximitySensor.getMaximumRange());
    154     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
    155       // Added in API level 9.
    156       info.append(", min delay: " + proximitySensor.getMinDelay());
    157     }
    158     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
    159       // Added in API level 20.
    160       info.append(", type: " + proximitySensor.getStringType());
    161     }
    162     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    163       // Added in API level 21.
    164       info.append(", max delay: " + proximitySensor.getMaxDelay());
    165       info.append(", reporting mode: " + proximitySensor.getReportingMode());
    166       info.append(", isWakeUpSensor: " + proximitySensor.isWakeUpSensor());
    167     }
    168     Log.d(TAG, info.toString());
    169   }
    170 
    171   /**
    172    * Helper method for debugging purposes. Ensures that method is
    173    * called on same thread as this object was created on.
    174    */
    175   private void checkIfCalledOnValidThread() {
    176     if (!nonThreadSafe.calledOnValidThread()) {
    177       throw new IllegalStateException("Method is not called on valid thread");
    178     }
    179   }
    180 }
    181