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