/*
* Copyright (C) 2010 Google Inc.
*
* 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.utils;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.util.Log;
/**
* Convenience class for working with the ProximitySensor. Also uses the ambient
* light sensor in its place, when available.
*/
public class ProximitySensor {
/**
* Number of milliseconds to wait before reporting onSensorChanged events to
* the listener. Used to compensate for platform inconsistencies surrounding
* reporting the sensor state after listener registration or an accuracy
* change.
*/
private static final long REGISTRATION_EVENT_FILTER_TIMEOUT = 120;
// Trigger proximity if distance is less than 5 cm.
private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
private final SensorManager mSensorManager;
private final Sensor mProxSensor;
private final Handler mHandler = new Handler();
private final float mFarValue;
private ProximityChangeListener mCallback;
/**
* Whether this class should be dropping onSensorChanged events from
* reaching the client.
*/
private boolean mShouldDropEvents;
/** Whether the user is close to the proximity sensor. */
private boolean mIsClose;
/** Whether the sensor is currently active. */
private boolean mIsActive;
/**
* Constructor for ProximitySensor
*
* @param context The parent context.
*/
public ProximitySensor(Context context) {
mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
mProxSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
if (mProxSensor != null) {
mFarValue = Math.min(mProxSensor.getMaximumRange(), TYPICAL_PROXIMITY_THRESHOLD);
} else {
mFarValue = 0;
}
}
public void setProximityChangeListener(ProximityChangeListener listener) {
mCallback = listener;
}
/**
* Checks if something is close to the proximity sensor
*
* @return {@code true} if there is something close to the proximity sensor
*/
public boolean isClose() {
return mIsClose;
}
/**
* Stops listening for sensor events.
*/
public void stop() {
if ((mProxSensor == null) || !mIsActive) {
return;
}
LogUtils.log(
this, Log.VERBOSE, "Proximity sensor stopped at %d.", System.currentTimeMillis());
mIsActive = false;
mSensorManager.unregisterListener(mListener);
}
/**
* Starts listening for sensor events.
*/
public void start() {
if ((mProxSensor == null) || mIsActive) {
return;
}
mIsActive = true;
mShouldDropEvents = true;
mSensorManager.registerListener(mListener, mProxSensor, SensorManager.SENSOR_DELAY_UI);
LogUtils.log(this, Log.VERBOSE,
"Proximity sensor registered at %d.", System.currentTimeMillis());
mHandler.postDelayed(mFilterRunnable, REGISTRATION_EVENT_FILTER_TIMEOUT);
}
private final SensorEventListener mListener = new SensorEventListener() {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
LogUtils.log(this, Log.VERBOSE,
"Processing onAccuracyChanged event at %d.", System.currentTimeMillis());
mShouldDropEvents = true;
mHandler.removeCallbacks(mFilterRunnable);
mHandler.postDelayed(mFilterRunnable, REGISTRATION_EVENT_FILTER_TIMEOUT);
}
@Override
public void onSensorChanged(SensorEvent event) {
if (mShouldDropEvents) {
LogUtils.log(this, Log.VERBOSE,
"Dropping onSensorChanged event at %d.", System.currentTimeMillis());
return;
}
LogUtils.log(this, Log.VERBOSE,
"Processing onSensorChanged event at %d.", System.currentTimeMillis());
mIsClose = (event.values[0] < mFarValue);
mCallback.onProximityChanged(mIsClose);
}
};
/**
* Runnable used to enforce the {@link #REGISTRATION_EVENT_FILTER_TIMEOUT}
*/
private final Runnable mFilterRunnable = new Runnable() {
@Override
public void run() {
mShouldDropEvents = false;
LogUtils.log(this, Log.VERBOSE,
"Stopped filtering proximity events at %d.", System.currentTimeMillis());
}
};
/**
* Callback for when the proximity sensor detects a change
*/
public interface ProximityChangeListener {
public void onProximityChanged(boolean isClose);
}
}