blob: da9614e69a5ed633e47b613584b499ac0444d2cf [file] [log] [blame]
Alexandre Lisionb8add812013-10-24 11:42:42 -04001/*
2 * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
3 * Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
4 *
5 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 *
21 * Additional permission under GNU GPL version 3 section 7:
22 *
23 * If you modify this program, or any covered work, by linking or
24 * combining it with the OpenSSL project's OpenSSL library (or a
25 * modified version of that library), containing parts covered by the
26 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
27 * grants you additional permission to convey the resulting work.
28 * Corresponding Source for a non-source form of such a combination
29 * shall include the source code for the parts of OpenSSL used as well
30 * as that of the covered work.
31 */
32package org.sflphone.utils;
33
34import java.lang.reflect.Field;
35import java.lang.reflect.Method;
36
37import org.sflphone.utils.AccelerometerListener.OrientationListener;
38
39import android.content.Context;
40import android.hardware.Sensor;
41import android.hardware.SensorEvent;
42import android.hardware.SensorEventListener;
43import android.hardware.SensorManager;
44import android.net.wifi.WifiManager;
45import android.os.PowerManager;
46import android.os.PowerManager.WakeLock;
47import android.util.Log;
48
49/**
50 * Class to manage proximity detection while in call.
51 *
52 */
53public class CallProximityManager implements SensorEventListener, OrientationListener {
54 private static final String THIS_FILE = "CallProximityManager";
55
56 private Context mContext;
57
58 private SensorManager sensorManager;
59 private PowerManager powerManager;
60
61 // Timeout management of screen locker ui
62 // private ScreenLocker mScreenLocker;
63 private Boolean useTimeoutOverlay = null;
64
65 // Self management of proximity sensor
66 private Sensor proximitySensor;
67 private static final float PROXIMITY_THRESHOLD = 5.0f;
68 private boolean invertProximitySensor = false;
69 private boolean proximitySensorTracked = false;
70 private boolean isFirstRun = true;
71 private ProximityDirector mDirector = null;
72
73 // The hidden api that uses a wake lock
74 private WakeLock proximityWakeLock;
75
76 // The accelerometer
77 private AccelerometerListener accelerometerManager;
78 private int mOrientation;
79 private boolean accelerometerEnabled = false;
80
81 private int WAIT_FOR_PROXIMITY_NEGATIVE = 1;
82 // private final static int SCREEN_LOCKER_ACQUIRE_DELAY = "google_sdk".equals(Build.PRODUCT) ? ScreenLocker.WAIT_BEFORE_LOCK_LONG
83 // : ScreenLocker.WAIT_BEFORE_LOCK_SHORT;
84
85 private static Method powerLockReleaseIntMethod;
86
87 public interface ProximityDirector {
88 public boolean shouldActivateProximity();
89
90 public void onProximityTrackingChanged(boolean acquired);
91 }
92
93 public CallProximityManager(Context context, ProximityDirector director) {
94 mContext = context;
95 mDirector = director;
96 // mScreenLocker = screenLocker;
97
98 sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
99 powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
100 accelerometerManager = new AccelerometerListener(context, this);
101
102 // Try to detect the hidden api
103 if (powerManager != null) {
104 WifiManager wman = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
105
106 // Try to use powermanager proximity sensor
107 try {
108 boolean supportProximity = false;
109 Field f = PowerManager.class.getDeclaredField("PROXIMITY_SCREEN_OFF_WAKE_LOCK");
110 int proximityScreenOffWakeLock = (Integer) f.get(null);
111 if (Compatibility.isCompatible(17)) {
112 // Changes of the private API on android 4.2
113 Method method = powerManager.getClass().getDeclaredMethod("isWakeLockLevelSupported", int.class);
114 supportProximity = (Boolean) method.invoke(powerManager, proximityScreenOffWakeLock);
115 Log.d(THIS_FILE, "Use 4.2 detection way for proximity sensor detection. Result is " + supportProximity);
116 } else {
117 Method method = powerManager.getClass().getDeclaredMethod("getSupportedWakeLockFlags");
118 int supportedFlags = (Integer) method.invoke(powerManager);
119 Log.d(THIS_FILE, "Proxmity flags supported : " + supportedFlags);
120 supportProximity = ((supportedFlags & proximityScreenOffWakeLock) != 0x0);
121 }
122 if (supportProximity) {
123 Log.d(THIS_FILE, "We can use native screen locker !!");
124 proximityWakeLock = powerManager.newWakeLock(proximityScreenOffWakeLock, "com.csipsimple.CallProximity");
125 proximityWakeLock.setReferenceCounted(false);
126 }
127
128 } catch (Exception e) {
129 Log.d(THIS_FILE, "Impossible to get power manager supported wake lock flags ");
130 }
131 if (powerLockReleaseIntMethod == null) {
132 try {
133 powerLockReleaseIntMethod = proximityWakeLock.getClass().getDeclaredMethod("release", int.class);
134
135 } catch (Exception e) {
136 Log.d(THIS_FILE, "Impossible to get power manager release with it");
137 }
138
139 }
140 }
141
142 // Try to detect a proximity sensor as fallback
143 if (proximityWakeLock == null) {
144 proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
145 // invertProximitySensor = SipConfigManager.getPreferenceBooleanValue(context, SipConfigManager.INVERT_PROXIMITY_SENSOR);
146 }
147
148 }
149
150 public synchronized void startTracking() {
151 // If we should manage it ourselves
152 if (proximitySensor != null && !proximitySensorTracked) {
153 // Fall back to manual mode
154 isFirstRun = true;
155 Log.d(THIS_FILE, "Register sensor");
156 sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
157 proximitySensorTracked = true;
158 }
159 if (!accelerometerEnabled) {
160 accelerometerManager.enable(true);
161 accelerometerEnabled = true;
162 }
163 }
164
165 public synchronized void stopTracking() {
166 if (proximitySensor != null && proximitySensorTracked) {
167 proximitySensorTracked = false;
168 sensorManager.unregisterListener(this);
169 Log.d(THIS_FILE, "Unregister to sensor is done !!!");
170 }
171 if (accelerometerEnabled) {
172 accelerometerManager.enable(false);
173 accelerometerEnabled = false;
174 }
175 // mScreenLocker.tearDown();
176 }
177
178 @Override
179 public void onAccuracyChanged(Sensor sensor, int accuracy) {
180 }
181
182 @Override
183 public void onSensorChanged(SensorEvent event) {
184 // Log.d(THIS_FILE, "Tracked : "+proximitySensorTracked);
185 if (proximitySensorTracked && !isFirstRun) {
186 float distance = event.values[0];
187 boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD && distance < event.sensor.getMaximumRange());
188 if (invertProximitySensor) {
189 active = !active;
190 }
191 Log.d(THIS_FILE, "Distance is now " + distance);
192
193 boolean isValidCallState = false;
194 if (mDirector != null) {
195 isValidCallState = mDirector.shouldActivateProximity();
196 }
197
198 if (isValidCallState && active) {
199 // mScreenLocker.show();
200 if (mDirector != null) {
201 mDirector.onProximityTrackingChanged(true);
202 }
203 } else {
204 // mScreenLocker.hide();
205 if (mDirector != null) {
206 mDirector.onProximityTrackingChanged(false);
207 }
208 }
209
210 }
211 if (isFirstRun) {
212 isFirstRun = false;
213 }
214 }
215
216 private boolean isProximityWakeHeld = false;
217
218 /**
219 * Release any lock taken by the proximity sensor
220 */
221 public synchronized void release(int flag) {
222 if (proximityWakeLock != null && isProximityWakeHeld) {
223 boolean usedNewRelease = false;
224 if (powerLockReleaseIntMethod != null) {
225 try {
226 powerLockReleaseIntMethod.invoke(proximityWakeLock, flag);
227 usedNewRelease = true;
228 // Log.d(THIS_FILE, "CALL NEW RELEASE WITH FLAG " + flag);
229 } catch (Exception e) {
230 Log.d(THIS_FILE, "Error calling new release method ", e);
231 }
232 }
233 if (!usedNewRelease) {
234 proximityWakeLock.release();
235 }
236 isProximityWakeHeld = false;
237 }
238
239 if (shouldUseTimeoutOverlay()) {
240 // mScreenLocker.hide();
241 }
242 // Notify
243 if (mDirector != null) {
244 mDirector.onProximityTrackingChanged(false);
245 }
246 }
247
248 public synchronized void acquire() {
249 if (proximityWakeLock != null && !isProximityWakeHeld) {
250 proximityWakeLock.acquire();
251 isProximityWakeHeld = true;
252 }
253 if (shouldUseTimeoutOverlay()) {
254 // mScreenLocker.delayedLock(SCREEN_LOCKER_ACQUIRE_DELAY);
255 }
256 // Notify
257 if (mDirector != null) {
258 mDirector.onProximityTrackingChanged(true);
259 }
260 }
261
262 /**
263 * Update proximity lock mode depending on current state
264 */
265 public synchronized void updateProximitySensorMode() {
266
267 // We do not keep the screen off when the user is outside in-call screen and we are
268 // horizontal, but we do not force it on when we become horizontal until the
269 // proximity sensor goes negative.
270 boolean horizontal = (mOrientation == AccelerometerListener.ORIENTATION_HORIZONTAL);
271
272 boolean activeRegardingCalls = false;
273 if (mDirector != null) {
274 activeRegardingCalls = mDirector.shouldActivateProximity();
275 }
276
277 Log.d(THIS_FILE, "Horizontal : " + horizontal + " and activate for calls " + activeRegardingCalls);
278 if (activeRegardingCalls && !horizontal) {
279 // Phone is in use! Arrange for the screen to turn off
280 // automatically when the sensor detects a close object.
281 acquire();
282 } else {
283 // Phone is either idle, or ringing. We don't want any
284 // special proximity sensor behavior in either case.
285 int flags = (!horizontal ? 0 : WAIT_FOR_PROXIMITY_NEGATIVE);
286 release(flags);
287 }
288 }
289
290 /**
291 * Should the application display the overlay after a timeout.
292 *
293 * @return false if we are in table mode or if proximity sensor can be used
294 */
295 private boolean shouldUseTimeoutOverlay() {
296 if (useTimeoutOverlay == null) {
297 useTimeoutOverlay = proximitySensor == null && proximityWakeLock == null && !Compatibility.isTabletScreen(mContext);
298 }
299 return useTimeoutOverlay;
300 }
301
302 public void restartTimer() {
303 if (shouldUseTimeoutOverlay()) {
304 // mScreenLocker.delayedLock(ScreenLocker.WAIT_BEFORE_LOCK_LONG);
305 }
306 }
307
308 @Override
309 public void orientationChanged(int orientation) {
310 mOrientation = orientation;
311 updateProximitySensorMode();
312 }
313
314}