* #32381: added proper screenlock (based on CSipSimple mechanism)
diff --git a/src/org/sflphone/client/CallActivity.java b/src/org/sflphone/client/CallActivity.java
index b018bbf..03485d6 100644
--- a/src/org/sflphone/client/CallActivity.java
+++ b/src/org/sflphone/client/CallActivity.java
@@ -46,6 +46,8 @@
import org.sflphone.service.CallManagerCallBack;
import org.sflphone.service.ISipService;
import org.sflphone.service.SipService;
+import org.sflphone.utils.CallProximityManager;
+import org.sflphone.utils.CallProximityManager.ProximityDirector;
import org.sflphone.views.CallPaneLayout;
import android.app.Activity;
@@ -68,7 +70,7 @@
import android.view.Window;
import android.widget.Toast;
-public class CallActivity extends Activity implements CallInterface, CallFragment.Callbacks{
+public class CallActivity extends Activity implements CallInterface, CallFragment.Callbacks, ProximityDirector{
static final String TAG = "CallActivity";
private ISipService service;
@@ -82,6 +84,8 @@
/* result code sent in case of call failure */
public static int RESULT_FAILURE = -10;
+
+ private CallProximityManager proximityManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -90,9 +94,7 @@
receiver = new CallReceiver(this);
- // mCallsFragment = new CallListFragment();
-
- // getFragmentManager().beginTransaction().replace(R.id.calllist_pane, mCallsFragment).commit();
+ proximityManager = new CallProximityManager(this, this);
slidingPaneLayout = (CallPaneLayout) findViewById(R.id.slidingpanelayout);
@@ -121,6 +123,7 @@
}
});
+ proximityManager.startTracking();
Intent intent = new Intent(this, SipService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@@ -182,6 +185,8 @@
protected void onDestroy() {
unregisterReceiver(receiver);
unbindService(mConnection);
+ proximityManager.stopTracking();
+ proximityManager.release(0);
super.onDestroy();
}
@@ -268,9 +273,10 @@
if (mCurrentCallFragment != null) {
mCurrentCallFragment.changeCallState(callID, newState);
-
}
+ proximityManager.updateProximitySensorMode();
+
try {
HashMap<String, SipCall> callMap = (HashMap<String, SipCall>) service.getCallList();
HashMap<String, Conference> confMap = (HashMap<String, Conference>) service.getConferenceList();
@@ -399,4 +405,15 @@
public void slideChatScreen() {
slidingPaneLayout.openPane();
}
+
+ @Override
+ public boolean shouldActivateProximity() {
+ return true;
+ }
+
+ @Override
+ public void onProximityTrackingChanged(boolean acquired) {
+ // TODO Stub de la méthode généré automatiquement
+
+ }
}
diff --git a/src/org/sflphone/fragments/CallFragment.java b/src/org/sflphone/fragments/CallFragment.java
index 858be93..08e27b3 100644
--- a/src/org/sflphone/fragments/CallFragment.java
+++ b/src/org/sflphone/fragments/CallFragment.java
@@ -31,6 +31,8 @@
package org.sflphone.fragments;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Locale;
@@ -45,6 +47,7 @@
import org.sflphone.model.Conference;
import org.sflphone.model.SipCall;
import org.sflphone.service.ISipService;
+import org.sflphone.utils.CallProximityManager;
import android.app.Activity;
import android.app.Fragment;
@@ -59,6 +62,7 @@
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
+import android.os.PowerManager;
import android.os.RemoteException;
import android.util.Log;
import android.view.KeyEvent;
@@ -71,12 +75,11 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
-import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.ImageButton;
import android.widget.TextView;
-public class CallFragment extends Fragment implements Callback, SensorEventListener {
+public class CallFragment extends Fragment implements Callback{
static final String TAG = "CallFragment";
@@ -88,6 +91,8 @@
private Conference conf;
private TextView callStatusTxt;
+ private TextView codecNameTxt;
+
private BubblesView view;
private BubbleModel model;
@@ -98,10 +103,19 @@
private SensorManager mSensorManager;
private Sensor mSensor;
-
+
TransferDFragment editName;
- private TextView codecNameTxt;
+ private PowerManager.WakeLock fullLock;
+ private PowerManager.WakeLock partialLock;
+ private PowerManager.WakeLock proximityLock;
+
+ private static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = 32;
+ private static final int WAIT_FOR_PROXIMITY_NEGATIVE = 1;
+
+ private Method wakelockParameterizedRelease;
+
+
@Override
public void onCreate(Bundle savedBundle) {
@@ -115,6 +129,22 @@
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
this.setHasOptionsMenu(true);
+
+
+// PowerManager pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
+// fullLock = pm.newWakeLock(PowerManager.ON_AFTER_RELEASE | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
+// partialLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+// proximityLock = pm.newWakeLock(PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
+//
+// Method maybeRelease = null;
+// try {
+// maybeRelease = proximityLock.getClass().getDeclaredMethod("release", Integer.TYPE);
+// } catch (NoSuchMethodException e) {
+// Log.d("LockManager", "Parameterized WakeLock release not available on this device.");
+// }
+// wakelockParameterizedRelease = maybeRelease;
+
+
}
/**
@@ -122,7 +152,6 @@
*/
private static Callbacks sDummyCallbacks = new Callbacks() {
-
@Override
public ISipService getService() {
return null;
@@ -137,7 +166,7 @@
}
@Override
- public void slideChatScreen() {
+ public void slideChatScreen() {
}
};
@@ -152,7 +181,7 @@
public void startTimer();
public void slideChatScreen();
-
+
public void replaceCurrentCallDisplayed();
}
@@ -167,10 +196,10 @@
// rootView.requestDisallowInterceptTouchEvent(true);
mCallbacks = (Callbacks) activity;
-// myself = SipCall.SipCallBuilder.buildMyselfCall(activity.getContentResolver(), "Me");
+ // myself = SipCall.SipCallBuilder.buildMyselfCall(activity.getContentResolver(), "Me");
}
-
+
@Override
public void onCreateOptionsMenu(Menu m, MenuInflater inf) {
super.onCreateOptionsMenu(m, inf);
@@ -180,11 +209,11 @@
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
-// switch (item.getItemId()) {
-// case R.id.menuitem_chat:
-// mCallbacks.slideChatScreen();
-// break;
-// }
+ // switch (item.getItemId()) {
+ // case R.id.menuitem_chat:
+ // mCallbacks.slideChatScreen();
+ // break;
+ // }
return true;
}
@@ -193,7 +222,7 @@
public void onDetach() {
super.onDetach();
mCallbacks = sDummyCallbacks;
- mSensorManager.unregisterListener(this);
+// mSensorManager.unregisterListener(this);
}
@Override
@@ -204,13 +233,13 @@
@Override
public void onResume() {
super.onResume();
- mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
+// mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
public void onPause() {
super.onPause();
- mSensorManager.unregisterListener(this);
+// mSensorManager.unregisterListener(this);
}
@Override
@@ -361,7 +390,7 @@
private Bubble getBubbleFor(SipCall call, float x, float y) {
Bubble contact_bubble = model.getBubble(call.getCallId());
if (contact_bubble != null) {
- ((BubbleContact)contact_bubble).setCall(call);
+ ((BubbleContact) contact_bubble).setCall(call);
contact_bubble.attractor.set(x, y);
return contact_bubble;
}
@@ -371,16 +400,17 @@
model.addBubble(contact_bubble);
return contact_bubble;
}
-
+
private Bubble getBubbleForUser(Conference conf, float x, float y) {
Bubble contact_bubble = model.getBubble(conf.getId());
if (contact_bubble != null) {
contact_bubble.attractor.set(x, y);
- ((BubbleUser)contact_bubble).setConference(conf);
+ ((BubbleUser) contact_bubble).setConference(conf);
return contact_bubble;
}
- contact_bubble = new BubbleUser(getActivity(), CallContact.ContactBuilder.buildUserContact(getActivity().getContentResolver(), getResources().getString(R.string.me)), conf, x, y, BUBBLE_SIZE);
+ contact_bubble = new BubbleUser(getActivity(), CallContact.ContactBuilder.buildUserContact(getActivity().getContentResolver(), getResources()
+ .getString(R.string.me)), conf, x, y, BUBBLE_SIZE);
model.addBubble(contact_bubble);
return contact_bubble;
@@ -390,9 +420,9 @@
* Should be called when a bubble is removed from the model
*/
void bubbleRemoved(Bubble b) {
-// if (b.associated_call == null) {
-// return;
-// }
+ // if (b.associated_call == null) {
+ // return;
+ // }
}
public void changeCallState(String callID, String newState) {
@@ -468,7 +498,7 @@
editName = TransferDFragment.newInstance();
Bundle b = new Bundle();
try {
- b.putParcelableArrayList("calls", (ArrayList<Conference>)mCallbacks.getService().getConcurrentCalls());
+ b.putParcelableArrayList("calls", (ArrayList<Conference>) mCallbacks.getService().getConcurrentCalls());
b.putParcelable("call_selected", contact.associated_call);
editName.setArguments(b);
editName.setTargetFragment(this, REQUEST_TRANSFER);
@@ -489,7 +519,7 @@
// check that soft input is hidden
InputMethodManager lManager = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
lManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
- if(editName != null && editName.isVisible()){
+ if (editName != null && editName.isVisible()) {
editName.dismiss();
}
}
@@ -503,28 +533,46 @@
callStatusTxt.setText(String.format("%d:%02d:%02d", duration / 3600, duration % 3600 / 60, duration % 60));
}
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
+// @Override
+// public void onAccuracyChanged(Sensor sensor, int accuracy) {
+//
+// }
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (event.values[0] == 0) {
- WindowManager.LayoutParams params = getActivity().getWindow().getAttributes();
- params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
- params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
- params.screenBrightness = 0.004f;
- getActivity().getWindow().setAttributes(params);
-
- } else {
- WindowManager.LayoutParams params = getActivity().getWindow().getAttributes();
- getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
- params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
- params.screenBrightness = 1.0f;
- getActivity().getWindow().setAttributes(params);
- }
- }
+// @Override
+// public void onSensorChanged(SensorEvent event) {
+// if (event.values[0] == 0) {
+// // WindowManager.LayoutParams params = getActivity().getWindow().getAttributes();
+// // params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+// // params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+// // params.screenBrightness = -1f;
+// // getActivity().getWindow().setAttributes(params);
+//
+// } else {
+// // WindowManager.LayoutParams params = getActivity().getWindow().getAttributes();
+// // getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
+// // params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+// // params.screenBrightness = 1.0f;
+// // getActivity().getWindow().setAttributes(params);
+// }
+// }
+
+// private void releaseProximityLock() {
+// boolean released = false;
+// if (wakelockParameterizedRelease != null) {
+// try {
+// wakelockParameterizedRelease.invoke(proximityLock, new Integer(WAIT_FOR_PROXIMITY_NEGATIVE));
+// released = true;
+// } catch (IllegalAccessException e) {
+// Log.d("LockManager", "Failed to invoke release method", e);
+// } catch (InvocationTargetException e) {
+// Log.d("LockManager", "Failed to invoke release method", e);
+// }
+// }
+//
+// if(!released) {
+// proximityLock.release();
+// }
+// }
public Conference getConference() {
return conf;
@@ -532,11 +580,10 @@
public void onKeyUp(int keyCode, KeyEvent event) {
try {
-
- if(event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN)
+
+ if (event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP || event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_DOWN)
return;
-
-
+
String toSend = Character.toString(event.getDisplayLabel());
toSend.toUpperCase(Locale.getDefault());
Log.d(TAG, "toSend " + toSend);
diff --git a/src/org/sflphone/utils/AccelerometerListener.java b/src/org/sflphone/utils/AccelerometerListener.java
new file mode 100644
index 0000000..adc5c0f
--- /dev/null
+++ b/src/org/sflphone/utils/AccelerometerListener.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
+ * Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
+ *
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+package org.sflphone.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.os.Message;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This class is used to listen to the accelerometer to monitor the orientation of the phone. The client of this class is notified when the
+ * orientation changes between horizontal and vertical.
+ */
+public final class AccelerometerListener {
+ private static final String TAG = "AccelerometerListener";
+ private static final boolean DEBUG = true;
+ private static final boolean VDEBUG = false;
+
+ private SensorManager mSensorManager;
+ private Sensor mSensor;
+
+ // mOrientation is the orientation value most recently reported to the client.
+ private int mOrientation;
+
+ // mPendingOrientation is the latest orientation computed based on the sensor value.
+ // This is sent to the client after a rebounce delay, at which point it is copied to
+ // mOrientation.
+ private int mPendingOrientation;
+
+ private OrientationListener mListener;
+
+ // Device orientation
+ public static final int ORIENTATION_UNKNOWN = 0;
+ public static final int ORIENTATION_VERTICAL = 1;
+ public static final int ORIENTATION_HORIZONTAL = 2;
+
+ private static final int ORIENTATION_CHANGED = 1234;
+
+ private static final int VERTICAL_DEBOUNCE = 100;
+ private static final int HORIZONTAL_DEBOUNCE = 500;
+ private static final double VERTICAL_ANGLE = 50.0;
+
+ public interface OrientationListener {
+ public void orientationChanged(int orientation);
+ }
+
+ public AccelerometerListener(Context context, OrientationListener listener) {
+ mListener = listener;
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ }
+
+ public void enable(boolean enable) {
+// if (DEBUG)
+// Log.d(TAG, "enable(" + enable + ")");
+ synchronized (this) {
+ if (enable) {
+ mOrientation = ORIENTATION_UNKNOWN;
+ mPendingOrientation = ORIENTATION_UNKNOWN;
+ mSensorManager.registerListener(mSensorListener, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ } else {
+ mSensorManager.unregisterListener(mSensorListener);
+ mHandler.removeMessages(ORIENTATION_CHANGED);
+ }
+ }
+ }
+
+ private void setOrientation(int orientation) {
+ synchronized (this) {
+ if (mPendingOrientation == orientation) {
+ // Pending orientation has not changed, so do nothing.
+ return;
+ }
+
+ // Cancel any pending messages.
+ // We will either start a new timer or cancel alltogether
+ // if the orientation has not changed.
+ mHandler.removeMessages(ORIENTATION_CHANGED);
+
+ if (mOrientation != orientation) {
+ // Set timer to send an event if the orientation has changed since its
+ // previously reported value.
+ mPendingOrientation = orientation;
+ Message m = mHandler.obtainMessage(ORIENTATION_CHANGED);
+ // set delay to our debounce timeout
+ int delay = (orientation == ORIENTATION_VERTICAL ? VERTICAL_DEBOUNCE : HORIZONTAL_DEBOUNCE);
+ mHandler.sendMessageDelayed(m, delay);
+ } else {
+ // no message is pending
+ mPendingOrientation = ORIENTATION_UNKNOWN;
+ }
+ }
+ }
+
+ private void onSensorEvent(double x, double y, double z) {
+// if (VDEBUG)
+// Log.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");
+
+ // If some values are exactly zero, then likely the sensor is not powered up yet.
+ // ignore these events to avoid false horizontal positives.
+ if (x == 0.0 || y == 0.0 || z == 0.0)
+ return;
+
+ // magnitude of the acceleration vector projected onto XY plane
+ double xy = Math.sqrt(x * x + y * y);
+ // compute the vertical angle
+ double angle = Math.atan2(xy, z);
+ // convert to degrees
+ angle = angle * 180.0 / Math.PI;
+ int orientation = (angle > VERTICAL_ANGLE ? ORIENTATION_VERTICAL : ORIENTATION_HORIZONTAL);
+// if (VDEBUG)
+// Log.d(TAG, "angle: " + angle + " orientation: " + orientation);
+ setOrientation(orientation);
+ }
+
+ SensorEventListener mSensorListener = new SensorEventListener() {
+ public void onSensorChanged(SensorEvent event) {
+ onSensorEvent(event.values[0], event.values[1], event.values[2]);
+ }
+
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // ignore
+ }
+ };
+
+ Handler mHandler = new AccelerometerHandler(this);
+
+ private static class AccelerometerHandler extends Handler {
+ WeakReference<AccelerometerListener> l;
+
+ AccelerometerHandler(AccelerometerListener listener) {
+ l = new WeakReference<AccelerometerListener>(listener);
+ }
+
+ public void handleMessage(Message msg) {
+ AccelerometerListener listener = l.get();
+ if (listener == null) {
+ return;
+ }
+ switch (msg.what) {
+ case ORIENTATION_CHANGED:
+ synchronized (listener) {
+ listener.mOrientation = listener.mPendingOrientation;
+// if (DEBUG) {
+// Log.d(TAG, "orientation: "
+// + (listener.mOrientation == ORIENTATION_HORIZONTAL ? "horizontal"
+// : (listener.mOrientation == ORIENTATION_VERTICAL ? "vertical" : "unknown")));
+// }
+ listener.mListener.orientationChanged(listener.mOrientation);
+ }
+ break;
+ }
+ }
+ };
+}
\ No newline at end of file
diff --git a/src/org/sflphone/utils/CallProximityManager.java b/src/org/sflphone/utils/CallProximityManager.java
new file mode 100644
index 0000000..da9614e
--- /dev/null
+++ b/src/org/sflphone/utils/CallProximityManager.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
+ * Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
+ *
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+package org.sflphone.utils;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+import org.sflphone.utils.AccelerometerListener.OrientationListener;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.net.wifi.WifiManager;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.util.Log;
+
+/**
+ * Class to manage proximity detection while in call.
+ *
+ */
+public class CallProximityManager implements SensorEventListener, OrientationListener {
+ private static final String THIS_FILE = "CallProximityManager";
+
+ private Context mContext;
+
+ private SensorManager sensorManager;
+ private PowerManager powerManager;
+
+ // Timeout management of screen locker ui
+ // private ScreenLocker mScreenLocker;
+ private Boolean useTimeoutOverlay = null;
+
+ // Self management of proximity sensor
+ private Sensor proximitySensor;
+ private static final float PROXIMITY_THRESHOLD = 5.0f;
+ private boolean invertProximitySensor = false;
+ private boolean proximitySensorTracked = false;
+ private boolean isFirstRun = true;
+ private ProximityDirector mDirector = null;
+
+ // The hidden api that uses a wake lock
+ private WakeLock proximityWakeLock;
+
+ // The accelerometer
+ private AccelerometerListener accelerometerManager;
+ private int mOrientation;
+ private boolean accelerometerEnabled = false;
+
+ private int WAIT_FOR_PROXIMITY_NEGATIVE = 1;
+ // private final static int SCREEN_LOCKER_ACQUIRE_DELAY = "google_sdk".equals(Build.PRODUCT) ? ScreenLocker.WAIT_BEFORE_LOCK_LONG
+ // : ScreenLocker.WAIT_BEFORE_LOCK_SHORT;
+
+ private static Method powerLockReleaseIntMethod;
+
+ public interface ProximityDirector {
+ public boolean shouldActivateProximity();
+
+ public void onProximityTrackingChanged(boolean acquired);
+ }
+
+ public CallProximityManager(Context context, ProximityDirector director) {
+ mContext = context;
+ mDirector = director;
+ // mScreenLocker = screenLocker;
+
+ sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ accelerometerManager = new AccelerometerListener(context, this);
+
+ // Try to detect the hidden api
+ if (powerManager != null) {
+ WifiManager wman = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+
+ // Try to use powermanager proximity sensor
+ try {
+ boolean supportProximity = false;
+ Field f = PowerManager.class.getDeclaredField("PROXIMITY_SCREEN_OFF_WAKE_LOCK");
+ int proximityScreenOffWakeLock = (Integer) f.get(null);
+ if (Compatibility.isCompatible(17)) {
+ // Changes of the private API on android 4.2
+ Method method = powerManager.getClass().getDeclaredMethod("isWakeLockLevelSupported", int.class);
+ supportProximity = (Boolean) method.invoke(powerManager, proximityScreenOffWakeLock);
+ Log.d(THIS_FILE, "Use 4.2 detection way for proximity sensor detection. Result is " + supportProximity);
+ } else {
+ Method method = powerManager.getClass().getDeclaredMethod("getSupportedWakeLockFlags");
+ int supportedFlags = (Integer) method.invoke(powerManager);
+ Log.d(THIS_FILE, "Proxmity flags supported : " + supportedFlags);
+ supportProximity = ((supportedFlags & proximityScreenOffWakeLock) != 0x0);
+ }
+ if (supportProximity) {
+ Log.d(THIS_FILE, "We can use native screen locker !!");
+ proximityWakeLock = powerManager.newWakeLock(proximityScreenOffWakeLock, "com.csipsimple.CallProximity");
+ proximityWakeLock.setReferenceCounted(false);
+ }
+
+ } catch (Exception e) {
+ Log.d(THIS_FILE, "Impossible to get power manager supported wake lock flags ");
+ }
+ if (powerLockReleaseIntMethod == null) {
+ try {
+ powerLockReleaseIntMethod = proximityWakeLock.getClass().getDeclaredMethod("release", int.class);
+
+ } catch (Exception e) {
+ Log.d(THIS_FILE, "Impossible to get power manager release with it");
+ }
+
+ }
+ }
+
+ // Try to detect a proximity sensor as fallback
+ if (proximityWakeLock == null) {
+ proximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ // invertProximitySensor = SipConfigManager.getPreferenceBooleanValue(context, SipConfigManager.INVERT_PROXIMITY_SENSOR);
+ }
+
+ }
+
+ public synchronized void startTracking() {
+ // If we should manage it ourselves
+ if (proximitySensor != null && !proximitySensorTracked) {
+ // Fall back to manual mode
+ isFirstRun = true;
+ Log.d(THIS_FILE, "Register sensor");
+ sensorManager.registerListener(this, proximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+ proximitySensorTracked = true;
+ }
+ if (!accelerometerEnabled) {
+ accelerometerManager.enable(true);
+ accelerometerEnabled = true;
+ }
+ }
+
+ public synchronized void stopTracking() {
+ if (proximitySensor != null && proximitySensorTracked) {
+ proximitySensorTracked = false;
+ sensorManager.unregisterListener(this);
+ Log.d(THIS_FILE, "Unregister to sensor is done !!!");
+ }
+ if (accelerometerEnabled) {
+ accelerometerManager.enable(false);
+ accelerometerEnabled = false;
+ }
+ // mScreenLocker.tearDown();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ // Log.d(THIS_FILE, "Tracked : "+proximitySensorTracked);
+ if (proximitySensorTracked && !isFirstRun) {
+ float distance = event.values[0];
+ boolean active = (distance >= 0.0 && distance < PROXIMITY_THRESHOLD && distance < event.sensor.getMaximumRange());
+ if (invertProximitySensor) {
+ active = !active;
+ }
+ Log.d(THIS_FILE, "Distance is now " + distance);
+
+ boolean isValidCallState = false;
+ if (mDirector != null) {
+ isValidCallState = mDirector.shouldActivateProximity();
+ }
+
+ if (isValidCallState && active) {
+ // mScreenLocker.show();
+ if (mDirector != null) {
+ mDirector.onProximityTrackingChanged(true);
+ }
+ } else {
+ // mScreenLocker.hide();
+ if (mDirector != null) {
+ mDirector.onProximityTrackingChanged(false);
+ }
+ }
+
+ }
+ if (isFirstRun) {
+ isFirstRun = false;
+ }
+ }
+
+ private boolean isProximityWakeHeld = false;
+
+ /**
+ * Release any lock taken by the proximity sensor
+ */
+ public synchronized void release(int flag) {
+ if (proximityWakeLock != null && isProximityWakeHeld) {
+ boolean usedNewRelease = false;
+ if (powerLockReleaseIntMethod != null) {
+ try {
+ powerLockReleaseIntMethod.invoke(proximityWakeLock, flag);
+ usedNewRelease = true;
+ // Log.d(THIS_FILE, "CALL NEW RELEASE WITH FLAG " + flag);
+ } catch (Exception e) {
+ Log.d(THIS_FILE, "Error calling new release method ", e);
+ }
+ }
+ if (!usedNewRelease) {
+ proximityWakeLock.release();
+ }
+ isProximityWakeHeld = false;
+ }
+
+ if (shouldUseTimeoutOverlay()) {
+ // mScreenLocker.hide();
+ }
+ // Notify
+ if (mDirector != null) {
+ mDirector.onProximityTrackingChanged(false);
+ }
+ }
+
+ public synchronized void acquire() {
+ if (proximityWakeLock != null && !isProximityWakeHeld) {
+ proximityWakeLock.acquire();
+ isProximityWakeHeld = true;
+ }
+ if (shouldUseTimeoutOverlay()) {
+ // mScreenLocker.delayedLock(SCREEN_LOCKER_ACQUIRE_DELAY);
+ }
+ // Notify
+ if (mDirector != null) {
+ mDirector.onProximityTrackingChanged(true);
+ }
+ }
+
+ /**
+ * Update proximity lock mode depending on current state
+ */
+ public synchronized void updateProximitySensorMode() {
+
+ // We do not keep the screen off when the user is outside in-call screen and we are
+ // horizontal, but we do not force it on when we become horizontal until the
+ // proximity sensor goes negative.
+ boolean horizontal = (mOrientation == AccelerometerListener.ORIENTATION_HORIZONTAL);
+
+ boolean activeRegardingCalls = false;
+ if (mDirector != null) {
+ activeRegardingCalls = mDirector.shouldActivateProximity();
+ }
+
+ Log.d(THIS_FILE, "Horizontal : " + horizontal + " and activate for calls " + activeRegardingCalls);
+ if (activeRegardingCalls && !horizontal) {
+ // Phone is in use! Arrange for the screen to turn off
+ // automatically when the sensor detects a close object.
+ acquire();
+ } else {
+ // Phone is either idle, or ringing. We don't want any
+ // special proximity sensor behavior in either case.
+ int flags = (!horizontal ? 0 : WAIT_FOR_PROXIMITY_NEGATIVE);
+ release(flags);
+ }
+ }
+
+ /**
+ * Should the application display the overlay after a timeout.
+ *
+ * @return false if we are in table mode or if proximity sensor can be used
+ */
+ private boolean shouldUseTimeoutOverlay() {
+ if (useTimeoutOverlay == null) {
+ useTimeoutOverlay = proximitySensor == null && proximityWakeLock == null && !Compatibility.isTabletScreen(mContext);
+ }
+ return useTimeoutOverlay;
+ }
+
+ public void restartTimer() {
+ if (shouldUseTimeoutOverlay()) {
+ // mScreenLocker.delayedLock(ScreenLocker.WAIT_BEFORE_LOCK_LONG);
+ }
+ }
+
+ @Override
+ public void orientationChanged(int orientation) {
+ mOrientation = orientation;
+ updateProximitySensorMode();
+ }
+
+}
\ No newline at end of file
diff --git a/src/org/sflphone/utils/Compatibility.java b/src/org/sflphone/utils/Compatibility.java
new file mode 100644
index 0000000..7a4a800
--- /dev/null
+++ b/src/org/sflphone/utils/Compatibility.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2010-2012 Regis Montoya (aka r3gis - www.r3gis.fr)
+ * Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
+ *
+ * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Additional permission under GNU GPL version 3 section 7:
+ *
+ * If you modify this program, or any covered work, by linking or
+ * combining it with the OpenSSL project's OpenSSL library (or a
+ * modified version of that library), containing parts covered by the
+ * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
+ * grants you additional permission to convey the resulting work.
+ * Corresponding Source for a non-source form of such a combination
+ * shall include the source code for the parts of OpenSSL used as well
+ * as that of the covered work.
+ */
+
+package org.sflphone.utils;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.lang.reflect.Field;
+import java.util.regex.Pattern;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Configuration;
+import android.media.AudioManager;
+import android.media.MediaRecorder.AudioSource;
+import android.net.Uri;
+import android.os.Environment;
+import android.provider.Contacts;
+import android.util.Log;
+
+@SuppressWarnings("deprecation")
+public final class Compatibility {
+
+ private Compatibility() {
+ }
+
+ private static final String THIS_FILE = "Compat";
+
+ public static int getApiLevel() {
+ return android.os.Build.VERSION.SDK_INT;
+ }
+
+ public static boolean isCompatible(int apiLevel) {
+ return android.os.Build.VERSION.SDK_INT >= apiLevel;
+ }
+
+ /**
+ * Get the stream id for in call track. Can differ on some devices. Current
+ * device for which it's different :
+ *
+ * @return
+ */
+ public static int getInCallStream(boolean requestBluetooth) {
+ /* Archos 5IT */
+ if (android.os.Build.BRAND.equalsIgnoreCase("archos")
+ && android.os.Build.DEVICE.equalsIgnoreCase("g7a")) {
+ // Since archos has no voice call capabilities, voice call stream is
+ // not implemented
+ // So we have to choose the good stream tag, which is by default
+ // falled back to music
+ return AudioManager.STREAM_MUSIC;
+ }
+ if (requestBluetooth) {
+ return 6; /* STREAM_BLUETOOTH_SCO -- Thx @Stefan for the contrib */
+ }
+
+ // return AudioManager.STREAM_MUSIC;
+ return AudioManager.STREAM_VOICE_CALL;
+ }
+
+ public static boolean shouldUseRoutingApi() {
+ Log.d(THIS_FILE, "Current device " + android.os.Build.BRAND + " - "
+ + android.os.Build.DEVICE);
+
+ // HTC evo 4G
+ if (android.os.Build.PRODUCT.equalsIgnoreCase("htc_supersonic")) {
+ return true;
+ }
+
+ // ZTE joe
+ if (android.os.Build.DEVICE.equalsIgnoreCase("joe")) {
+ return true;
+ }
+
+ // Samsung GT-S5830
+ if (android.os.Build.DEVICE.toUpperCase().startsWith("GT-S")) {
+ return true;
+ }
+
+ if (!isCompatible(4)) {
+ // If android 1.5, force routing api use
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static boolean shouldUseModeApi() {
+
+ // ZTE blade et joe
+ if (android.os.Build.DEVICE.equalsIgnoreCase("blade")
+ || android.os.Build.DEVICE.equalsIgnoreCase("joe")) {
+ return true;
+ }
+ // Samsung GT-S5360 GT-S5830 GT-S6102 ... probably all..
+ if (android.os.Build.DEVICE.toUpperCase().startsWith("GT-") ||
+ android.os.Build.PRODUCT.toUpperCase().startsWith("GT-") ||
+ android.os.Build.DEVICE.toUpperCase().startsWith("YP-")) {
+ return true;
+ }
+
+ // HTC evo 4G
+ if (android.os.Build.PRODUCT.equalsIgnoreCase("htc_supersonic")) {
+ return true;
+ }
+ // LG P500, Optimus V
+ if (android.os.Build.DEVICE.toLowerCase().startsWith("thunder")) {
+ return true;
+ }
+ // LG-E720(b)
+ if (android.os.Build.MODEL.toUpperCase().startsWith("LG-E720")
+ && !Compatibility.isCompatible(9)) {
+ return true;
+ }
+ // LG-LS840
+ if (android.os.Build.DEVICE.toLowerCase().startsWith("cayman")) {
+ return true;
+ }
+
+ // Huawei
+ if (android.os.Build.DEVICE.equalsIgnoreCase("U8150") ||
+ android.os.Build.DEVICE.equalsIgnoreCase("U8110") ||
+ android.os.Build.DEVICE.equalsIgnoreCase("U8120") ||
+ android.os.Build.DEVICE.equalsIgnoreCase("U8100") ||
+ android.os.Build.PRODUCT.equalsIgnoreCase("U8655")) {
+ return true;
+ }
+
+ // Moto defy mini
+ if (android.os.Build.MODEL.equalsIgnoreCase("XT320")) {
+ return true;
+ }
+
+ // Alcatel
+ if (android.os.Build.DEVICE.toUpperCase().startsWith("ONE_TOUCH_993D")) {
+ return true;
+ }
+
+ // N4
+ if (android.os.Build.DEVICE.toUpperCase().startsWith("MAKO")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ public static String guessInCallMode() {
+ // New api for 2.3.3 is not available on galaxy S II :(
+ if (!isCompatible(11) && android.os.Build.DEVICE.toUpperCase().startsWith("GT-I9100")) {
+ return Integer.toString(AudioManager.MODE_NORMAL);
+ }
+
+ if (android.os.Build.BRAND.equalsIgnoreCase("sdg") || isCompatible(10)) {
+ // Note that in APIs this is only available from level 11.
+ return "3";
+ }
+ if (android.os.Build.DEVICE.equalsIgnoreCase("blade")) {
+ return Integer.toString(AudioManager.MODE_IN_CALL);
+ }
+
+ if (!isCompatible(5)) {
+ return Integer.toString(AudioManager.MODE_IN_CALL);
+ }
+
+ return Integer.toString(AudioManager.MODE_NORMAL);
+ }
+
+ public static String getDefaultMicroSource() {
+ // Except for galaxy S II :(
+ if (!isCompatible(11) && android.os.Build.DEVICE.toUpperCase().startsWith("GT-I9100")) {
+ return Integer.toString(AudioSource.MIC);
+ }
+
+ if (isCompatible(10)) {
+ // Note that in APIs this is only available from level 11.
+ // VOICE_COMMUNICATION
+ return Integer.toString(0x7);
+ }
+ /*
+ * Too risky in terms of regressions else if (isCompatible(4)) { //
+ * VOICE_CALL return 0x4; }
+ */
+ /*
+ * if(android.os.Build.MODEL.equalsIgnoreCase("X10i")) { // VOICE_CALL
+ * return Integer.toString(0x4); }
+ */
+ /*
+ * Not relevant anymore, atrix I tested sounds fine with that
+ * if(android.os.Build.DEVICE.equalsIgnoreCase("olympus")) { //Motorola
+ * atrix bug // CAMCORDER return Integer.toString(0x5); }
+ */
+
+ return Integer.toString(AudioSource.DEFAULT);
+ }
+
+ public static String getDefaultFrequency() {
+ if (android.os.Build.DEVICE.equalsIgnoreCase("olympus")) {
+ // Atrix bug
+ return "32000";
+ }
+ if (android.os.Build.DEVICE.toUpperCase().equals("GT-P1010")) {
+ // Galaxy tab see issue 932
+ return "32000";
+ }
+
+ return isCompatible(4) ? "16000" : "8000";
+ }
+
+ public static String getCpuAbi() {
+ if (isCompatible(4)) {
+ Field field;
+ try {
+ field = android.os.Build.class.getField("CPU_ABI");
+ return field.get(null).toString();
+ } catch (Exception e) {
+ Log.w(THIS_FILE, "Announce to be android 1.6 but no CPU ABI field", e);
+ }
+
+ }
+ return "armeabi";
+ }
+
+ public final static int getNumCores() {
+ // Private Class to display only CPU devices in the directory listing
+ class CpuFilter implements FileFilter {
+ @Override
+ public boolean accept(File pathname) {
+ // Check if filename is "cpu", followed by a single digit number
+ if (Pattern.matches("cpu[0-9]", pathname.getName())) {
+ return true;
+ }
+ return false;
+ }
+ }
+ try {
+ // Get directory containing CPU info
+ File dir = new File("/sys/devices/system/cpu/");
+ // Filter to only list the devices we care about
+ File[] files = dir.listFiles(new CpuFilter());
+ // Return the number of cores (virtual CPU devices)
+ return files.length;
+ } catch (Exception e) {
+ return Runtime.getRuntime().availableProcessors();
+ }
+ }
+
+ private static boolean needPspWorkaround() {
+ // New api for 2.3 does not work on Incredible S
+ if (android.os.Build.DEVICE.equalsIgnoreCase("vivo")) {
+ return true;
+ }
+
+ // New API for android 2.3 should be able to manage this but do only for
+ // honeycomb cause seems not correctly supported by all yet
+ if (isCompatible(11)) {
+ return false;
+ }
+
+ // All htc except....
+ if (android.os.Build.PRODUCT.toLowerCase().startsWith("htc")
+ || android.os.Build.BRAND.toLowerCase().startsWith("htc")
+ || android.os.Build.PRODUCT.toLowerCase().equalsIgnoreCase("inc") /*
+ * For
+ * Incredible
+ */
+ || android.os.Build.DEVICE.equalsIgnoreCase("passion") /* N1 */) {
+ if (android.os.Build.DEVICE.equalsIgnoreCase("hero") /* HTC HERO */
+ || android.os.Build.DEVICE.equalsIgnoreCase("magic") /*
+ * Magic
+ * Aka
+ * Dev
+ * G2
+ */
+ || android.os.Build.DEVICE.equalsIgnoreCase("tatoo") /* Tatoo */
+ || android.os.Build.DEVICE.equalsIgnoreCase("dream") /*
+ * Dream
+ * Aka
+ * Dev
+ * G1
+ */
+ || android.os.Build.DEVICE.equalsIgnoreCase("legend") /* Legend */
+
+ ) {
+ return false;
+ }
+
+ // Older than 2.3 has no chance to have the new full perf wifi mode
+ // working since does not exists
+ if (!isCompatible(9)) {
+ return true;
+ } else {
+ // N1 is fine with that
+ if (android.os.Build.DEVICE.equalsIgnoreCase("passion")) {
+ return false;
+ }
+ return true;
+ }
+
+ }
+ // Dell streak
+ if (android.os.Build.BRAND.toLowerCase().startsWith("dell") &&
+ android.os.Build.DEVICE.equalsIgnoreCase("streak")) {
+ return true;
+ }
+ // Motorola milestone 1 and 2 & motorola droid & defy not under 2.3
+ if ((android.os.Build.DEVICE.toLowerCase().contains("milestone2") ||
+ android.os.Build.BOARD.toLowerCase().contains("sholes") ||
+ android.os.Build.PRODUCT.toLowerCase().contains("sholes") ||
+ android.os.Build.DEVICE.equalsIgnoreCase("olympus") ||
+ android.os.Build.DEVICE.toLowerCase().contains("umts_jordan")) && !isCompatible(9)) {
+ return true;
+ }
+ // Moto defy mini
+ if (android.os.Build.MODEL.equalsIgnoreCase("XT320")) {
+ return true;
+ }
+
+ // Alcatel ONE touch
+ if (android.os.Build.DEVICE.startsWith("one_touch_990")) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private static boolean needToneWorkaround() {
+ if (android.os.Build.PRODUCT.toLowerCase().startsWith("gt-i5800") ||
+ android.os.Build.PRODUCT.toLowerCase().startsWith("gt-i5801") ||
+ android.os.Build.PRODUCT.toLowerCase().startsWith("gt-i9003")) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean needSGSWorkaround() {
+ if (isCompatible(9)) {
+ return false;
+ }
+ if (android.os.Build.DEVICE.toUpperCase().startsWith("GT-I9000") ||
+ android.os.Build.DEVICE.toUpperCase().startsWith("GT-P1000")) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean needWebRTCImplementation() {
+ if (android.os.Build.DEVICE.toLowerCase().contains("droid2")) {
+ return true;
+ }
+ if (android.os.Build.MODEL.toLowerCase().contains("droid bionic")) {
+ return true;
+ }
+ if (android.os.Build.DEVICE.toLowerCase().contains("sunfire")) {
+ return true;
+ }
+ // Huawei Y300
+ if (android.os.Build.DEVICE.equalsIgnoreCase("U8833")) {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean shouldSetupAudioBeforeInit() {
+ // Setup for GT / GS samsung devices.
+ if (android.os.Build.DEVICE.toLowerCase().startsWith("gt-")
+ || android.os.Build.PRODUCT.toLowerCase().startsWith("gt-")) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean shouldFocusAudio() {
+ /* HTC One X */
+ if (android.os.Build.DEVICE.toLowerCase().startsWith("endeavoru") ||
+ android.os.Build.DEVICE.toLowerCase().startsWith("evita")) {
+ return false;
+ }
+
+ if (android.os.Build.DEVICE.toUpperCase().startsWith("GT-P7510") && isCompatible(15)) {
+ return false;
+ }
+ return true;
+ }
+
+// private static int getDefaultAudioImplementation() {
+// // Acer A510
+// if (android.os.Build.DEVICE.toLowerCase().startsWith("picasso")) {
+// return SipConfigManager.AUDIO_IMPLEMENTATION_JAVA;
+// }
+// if (Compatibility.isCompatible(11)) {
+// return SipConfigManager.AUDIO_IMPLEMENTATION_OPENSLES;
+// }
+// if (android.os.Build.DEVICE.equalsIgnoreCase("ST25i") && Compatibility.isCompatible(10)) {
+// return SipConfigManager.AUDIO_IMPLEMENTATION_OPENSLES;
+// }
+// if (android.os.Build.DEVICE.equalsIgnoreCase("u8510") && Compatibility.isCompatible(10)) {
+// return SipConfigManager.AUDIO_IMPLEMENTATION_OPENSLES;
+// }
+// return SipConfigManager.AUDIO_IMPLEMENTATION_JAVA;
+// }
+
+
+
+ public static boolean useFlipAnimation() {
+ if (android.os.Build.BRAND.equalsIgnoreCase("archos")
+ && android.os.Build.DEVICE.equalsIgnoreCase("g7a")) {
+ return false;
+ }
+ return true;
+ }
+
+
+ public static Intent getContactPhoneIntent() {
+ Intent intent = new Intent(Intent.ACTION_PICK);
+ /*
+ * intent.setAction(Intent.ACTION_GET_CONTENT);
+ * intent.setType(Contacts.Phones.CONTENT_ITEM_TYPE);
+ */
+ if (isCompatible(5)) {
+ // Don't use constant to allow backward compat simply
+ intent.setData(Uri.parse("content://com.android.contacts/contacts"));
+ } else {
+ // Fallback for android 4
+ intent.setData(Contacts.People.CONTENT_URI);
+ }
+
+ return intent;
+
+ }
+
+
+ public static boolean isTabletScreen(Context ctxt) {
+ boolean isTablet = false;
+ if (!isCompatible(4)) {
+ return false;
+ }
+ Configuration cfg = ctxt.getResources().getConfiguration();
+ int screenLayoutVal = 0;
+ try {
+ Field f = Configuration.class.getDeclaredField("screenLayout");
+ screenLayoutVal = (Integer) f.get(cfg);
+ } catch (Exception e) {
+ return false;
+ }
+ int screenLayout = (screenLayoutVal & 0xF);
+ // 0xF = SCREENLAYOUT_SIZE_MASK but avoid 1.5 incompat doing that
+ if (screenLayout == 0x3 || screenLayout == 0x4) {
+ // 0x3 = SCREENLAYOUT_SIZE_LARGE but avoid 1.5 incompat doing that
+ // 0x4 = SCREENLAYOUT_SIZE_XLARGE but avoid 1.5 incompat doing that
+ isTablet = true;
+ }
+
+ return isTablet;
+ }
+
+ public static int getHomeMenuId() {
+ return 0x0102002c;
+ // return android.R.id.home;
+ }
+
+ public static boolean isInstalledOnSdCard(Context context) {
+ // check for API level 8 and higher
+ if (Compatibility.isCompatible(8)) {
+ PackageManager pm = context.getPackageManager();
+ try {
+ PackageInfo pi = pm.getPackageInfo(context.getPackageName(), 0);
+ ApplicationInfo ai = pi.applicationInfo;
+ return (ai.flags & 0x00040000 /*
+ * ApplicationInfo.
+ * FLAG_EXTERNAL_STORAGE
+ */) == 0x00040000 /*
+ * ApplicationInfo.
+ * FLAG_EXTERNAL_STORAGE
+ */;
+ } catch (NameNotFoundException e) {
+ // ignore
+ }
+ }
+
+ // check for API level 7 - check files dir
+ try {
+ String filesDir = context.getFilesDir().getAbsolutePath();
+ if (filesDir.startsWith("/data/")) {
+ return false;
+ } else if (filesDir.contains(Environment.getExternalStorageDirectory().getPath())) {
+ return true;
+ }
+ } catch (Throwable e) {
+ // ignore
+ }
+
+ return false;
+ }
+
+}
\ No newline at end of file