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