blob: a96977a338a9c242804a27a62cf1c70f6af5e1bf [file] [log] [blame]
Dave Mankoff63a12822019-09-16 14:38:06 -04001/*
2 * Copyright (C) 2019 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
17package com.android.systemui.util.sensors;
18
Dave Mankoffd94c9692019-10-18 12:38:21 -040019import android.content.res.Resources;
Dave Mankoff63a12822019-09-16 14:38:06 -040020import android.hardware.Sensor;
21import android.hardware.SensorEvent;
22import android.hardware.SensorEventListener;
23import android.hardware.SensorManager;
24import android.os.Handler;
25import android.util.Log;
26
Dave Mankoffbf52f4b2019-09-20 14:34:28 -040027import com.android.internal.annotations.VisibleForTesting;
Dave Mankoff63a12822019-09-16 14:38:06 -040028import com.android.systemui.R;
Dave Mankofff4736812019-10-18 17:25:50 -040029import com.android.systemui.dagger.qualifiers.MainResources;
Dave Mankoff63a12822019-09-16 14:38:06 -040030
31import java.util.ArrayList;
32import java.util.List;
33import java.util.Locale;
34import java.util.function.Consumer;
35
36import javax.inject.Inject;
37
38/**
39 * Simple wrapper around SensorManager customized for the Proximity sensor.
40 */
Dave Mankoffbc3091c2019-09-18 11:44:29 -040041public class ProximitySensor {
Dave Mankoff63a12822019-09-16 14:38:06 -040042 private static final String TAG = "ProxSensor";
43 private static final boolean DEBUG = false;
44
45 private final Sensor mSensor;
46 private final AsyncSensorManager mSensorManager;
47 private final boolean mUsingBrightnessSensor;
48 private final float mMaxRange;
49 private List<ProximitySensorListener> mListeners = new ArrayList<>();
50 private String mTag = null;
Dave Mankoffbf52f4b2019-09-20 14:34:28 -040051 @VisibleForTesting ProximityEvent mLastEvent;
Dave Mankoff63a12822019-09-16 14:38:06 -040052 private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
53 private boolean mPaused;
54 private boolean mRegistered;
Dave Mankoff63a12822019-09-16 14:38:06 -040055
56 private SensorEventListener mSensorEventListener = new SensorEventListener() {
57 @Override
58 public synchronized void onSensorChanged(SensorEvent event) {
59 onSensorEvent(event);
60 }
61
62 @Override
63 public void onAccuracyChanged(Sensor sensor, int accuracy) {
64 }
65 };
66
67 @Inject
Dave Mankofff4736812019-10-18 17:25:50 -040068 public ProximitySensor(@MainResources Resources resources,
Dave Mankoffd94c9692019-10-18 12:38:21 -040069 AsyncSensorManager sensorManager) {
Dave Mankoff63a12822019-09-16 14:38:06 -040070 mSensorManager = sensorManager;
Dave Mankoffd94c9692019-10-18 12:38:21 -040071 Sensor sensor = findBrightnessSensor(resources);
Dave Mankoff63a12822019-09-16 14:38:06 -040072
73 if (sensor == null) {
74 mUsingBrightnessSensor = false;
75 sensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
76 } else {
77 mUsingBrightnessSensor = true;
78 }
79 mSensor = sensor;
80 if (mSensor != null) {
81 mMaxRange = mSensor.getMaximumRange();
82 } else {
83 mMaxRange = 0;
84 }
85 }
86
87 public void setTag(String tag) {
88 mTag = tag;
89 }
90
91 public void setSensorDelay(int sensorDelay) {
92 mSensorDelay = sensorDelay;
93 }
94
95 /**
96 * Unregister with the {@link SensorManager} without unsetting listeners on this object.
97 */
98 public void pause() {
99 mPaused = true;
100 unregisterInternal();
101 }
102
103 /**
104 * Register with the {@link SensorManager}. No-op if no listeners are registered on this object.
105 */
106 public void resume() {
107 mPaused = false;
108 registerInternal();
109 }
110
Dave Mankoffd94c9692019-10-18 12:38:21 -0400111 private Sensor findBrightnessSensor(Resources resources) {
112 String sensorType = resources.getString(R.string.doze_brightness_sensor_type);
Dave Mankoff63a12822019-09-16 14:38:06 -0400113 List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
114 Sensor sensor = null;
115 for (Sensor s : sensorList) {
116 if (sensorType.equals(s.getStringType())) {
117 sensor = s;
118 break;
119 }
120 }
121
122 return sensor;
123 }
124
125 /**
Dave Mankoff63a12822019-09-16 14:38:06 -0400126 * Returns true if we are registered with the SensorManager.
127 */
128 public boolean isRegistered() {
129 return mRegistered;
130 }
131
132 /**
133 * Returns {@code false} if a Proximity sensor is not available.
134 */
135 public boolean getSensorAvailable() {
136 return mSensor != null;
137 }
138
139 /**
140 * Add a listener.
141 *
142 * Registers itself with the {@link SensorManager} if this is the first listener
143 * added. If a cool down is currently running, the sensor will be registered when it is over.
144 */
145 public boolean register(ProximitySensorListener listener) {
146 if (!getSensorAvailable()) {
147 return false;
148 }
149
Dave Mankoff63a12822019-09-16 14:38:06 -0400150 mListeners.add(listener);
151 registerInternal();
152
153 return true;
154 }
155
Dave Mankoffbf52f4b2019-09-20 14:34:28 -0400156 protected void registerInternal() {
Dave Mankoffbc3091c2019-09-18 11:44:29 -0400157 if (mRegistered || mPaused || mListeners.isEmpty()) {
Dave Mankoff63a12822019-09-16 14:38:06 -0400158 return;
159 }
Dave Mankoffbf52f4b2019-09-20 14:34:28 -0400160 logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
Dave Mankoff63a12822019-09-16 14:38:06 -0400161 logDebug("Registering sensor listener");
162 mRegistered = true;
163 mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
164 }
165
166 /**
167 * Remove a listener.
168 *
169 * If all listeners are removed from an instance of this class,
170 * it will unregister itself with the SensorManager.
171 */
172 public void unregister(ProximitySensorListener listener) {
173 mListeners.remove(listener);
174 if (mListeners.size() == 0) {
175 unregisterInternal();
176 }
177 }
178
Dave Mankoffbf52f4b2019-09-20 14:34:28 -0400179 protected void unregisterInternal() {
Dave Mankoff63a12822019-09-16 14:38:06 -0400180 if (!mRegistered) {
181 return;
182 }
183 logDebug("unregistering sensor listener");
184 mSensorManager.unregisterListener(mSensorEventListener);
185 mRegistered = false;
186 }
187
188 public Boolean isNear() {
189 return getSensorAvailable() && mLastEvent != null ? mLastEvent.getNear() : null;
190 }
191
192 /** Update all listeners with the last value this class received from the sensor. */
193 public void alertListeners() {
194 mListeners.forEach(proximitySensorListener ->
195 proximitySensorListener.onSensorEvent(mLastEvent));
196 }
197
198 private void onSensorEvent(SensorEvent event) {
199 boolean near = event.values[0] < mMaxRange;
200 if (mUsingBrightnessSensor) {
201 near = event.values[0] == 0;
202 }
203 mLastEvent = new ProximityEvent(near, event.timestamp);
204 alertListeners();
Dave Mankoff63a12822019-09-16 14:38:06 -0400205 }
206
207 @Override
208 public String toString() {
Dave Mankoffbc3091c2019-09-18 11:44:29 -0400209 return String.format("{registered=%s, paused=%s, near=%s, sensor=%s}",
210 isRegistered(), mPaused, isNear(), mSensor);
Dave Mankoff63a12822019-09-16 14:38:06 -0400211 }
212
213 /**
214 * Convenience class allowing for briefly checking the proximity sensor.
215 */
216 public static class ProximityCheck implements Runnable {
217
218 private final ProximitySensor mSensor;
219 private final Handler mHandler;
220 private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
221
222 @Inject
223 public ProximityCheck(ProximitySensor sensor, Handler handler) {
224 mSensor = sensor;
225 mSensor.setTag("prox_check");
226 mHandler = handler;
227 mSensor.pause();
228 ProximitySensorListener listener = proximityEvent -> {
229 mCallbacks.forEach(
230 booleanConsumer ->
231 booleanConsumer.accept(
232 proximityEvent == null ? null : proximityEvent.getNear()));
233 mCallbacks.clear();
234 mSensor.pause();
235 };
236 mSensor.register(listener);
237 }
238
239 /** Set a descriptive tag for the sensors registration. */
240 public void setTag(String tag) {
241 mSensor.setTag(tag);
242 }
243
244 @Override
245 public void run() {
246 mSensor.pause();
247 mSensor.alertListeners();
248 }
249
250 /**
251 * Query the proximity sensor, timing out if no result.
252 */
253 public void check(long timeoutMs, Consumer<Boolean> callback) {
254 if (!mSensor.getSensorAvailable()) {
255 callback.accept(null);
256 }
257 mCallbacks.add(callback);
258 if (!mSensor.isRegistered()) {
259 mSensor.resume();
260 mHandler.postDelayed(this, timeoutMs);
261 }
262 }
263 }
264
265 /** Implement to be notified of ProximityEvents. */
266 public interface ProximitySensorListener {
267 /** Called when the ProximitySensor changes. */
268 void onSensorEvent(ProximityEvent proximityEvent);
269 }
270
271 /**
272 * Returned when the near/far state of a {@link ProximitySensor} changes.
273 */
274 public static class ProximityEvent {
275 private final boolean mNear;
276 private final long mTimestampNs;
277
278 public ProximityEvent(boolean near, long timestampNs) {
279 mNear = near;
280 mTimestampNs = timestampNs;
281 }
282
283 public boolean getNear() {
284 return mNear;
285 }
286
287 public long getTimestampNs() {
288 return mTimestampNs;
289 }
290
291 public long getTimestampMs() {
292 return mTimestampNs / 1000000;
293 }
294
295 @Override
296 public String toString() {
297 return String.format((Locale) null, "{near=%s, timestamp_ns=%d}", mNear, mTimestampNs);
298 }
299
300 }
301
302 private void logDebug(String msg) {
303 if (DEBUG) {
304 Log.d(TAG, (mTag != null ? "[" + mTag + "] " : "") + msg);
305 }
306 }
307}