[3/3] Flip to Mute/Reject Call
Ported over from 4.2 implementation (thanks sasikumardr for that)
Original author of this patch was bugadani
We did just a cleanup
Provide easy mute and dismiss functionality for the phone application.
Available actions:
0: mute ringer
1: dismiss call
2: no action (default)
PS:
- fix NPE
Change-Id: Ia10df0045e9919c6b7efc97857abaeba246c365d
diff --git a/src/com/android/incallui/AccelerometerListener.java b/src/com/android/incallui/AccelerometerListener.java
index 1a70778..0384ad2 100644
--- a/src/com/android/incallui/AccelerometerListener.java
+++ b/src/com/android/incallui/AccelerometerListener.java
@@ -23,8 +23,14 @@
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Message;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
import android.util.Log;
+import com.android.internal.telephony.ITelephony;
+
+import java.lang.reflect.Method;
+
/**
* This class is used to listen to the accelerometer to monitor the
* orientation of the phone. The client of this class is notified when
@@ -59,16 +65,34 @@
private static final int HORIZONTAL_DEBOUNCE = 500;
private static final double VERTICAL_ANGLE = 50.0;
+ // Flip action IDs
+ private static final int MUTE_RINGER = 0;
+ private static final int DISMISS_CALL = 1;
+ private static final int RINGING_NO_ACTION = 2;
+
+ private Context mContext;
+ private ITelephony mTelephonyService;
+
public interface OrientationListener {
public void orientationChanged(int orientation);
}
+ private interface ResettableSensorEventListener extends SensorEventListener {
+ public void reset();
+ }
+
public AccelerometerListener(Context context, OrientationListener listener) {
mListener = listener;
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}
+ public AccelerometerListener(Context context) {
+ mContext = context;
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mTelephonyService = getTeleService();
+ }
+
public void enable(boolean enable) {
if (DEBUG) Log.d(TAG, "enable(" + enable + ")");
synchronized (this) {
@@ -84,6 +108,25 @@
}
}
+ public void enableSensor(boolean enable) {
+ if (DEBUG) Log.d(TAG, "enableSensor(" + enable + ")");
+ int action = getFlipAction();
+ synchronized (this) {
+ if (enable) {
+ if (action != RINGING_NO_ACTION) {
+ mFlipListener.reset();
+ mSensorManager.registerListener(mFlipListener,
+ mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
+ SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ } else {
+ if (action != RINGING_NO_ACTION) {
+ mSensorManager.unregisterListener(mFlipListener);
+ }
+ }
+ }
+ }
+
private void setOrientation(int orientation) {
synchronized (this) {
if (mPendingOrientation == orientation) {
@@ -140,6 +183,123 @@
}
};
+ private final ResettableSensorEventListener
+ mFlipListener = new ResettableSensorEventListener() {
+ // Our accelerometers are not quite accurate.
+ private static final int FACE_UP_GRAVITY_THRESHOLD = 7;
+ private static final int FACE_DOWN_GRAVITY_THRESHOLD = -7;
+ private static final int TILT_THRESHOLD = 3;
+ private static final int SENSOR_SAMPLES = 3;
+ private static final int MIN_ACCEPT_COUNT = SENSOR_SAMPLES - 1;
+
+ private boolean mStopped;
+ private boolean mWasFaceUp;
+ private boolean[] mSamples = new boolean[SENSOR_SAMPLES];
+ private int mSampleIndex;
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int acc) {
+ }
+
+ @Override
+ public void reset() {
+ mWasFaceUp = false;
+ mStopped = false;
+ for (int i = 0; i < SENSOR_SAMPLES; i++) {
+ mSamples[i] = false;
+ }
+ }
+
+ private boolean filterSamples() {
+ int trues = 0;
+ for (boolean sample : mSamples) {
+ if(sample) {
+ ++trues;
+ }
+ }
+ return trues >= MIN_ACCEPT_COUNT;
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mStopped) {
+ return;
+ }
+ // Add a sample overwriting the oldest one. Several samples
+ // are used to avoid the erroneous values the sensor sometimes
+ // returns.
+ float z = event.values[2];
+
+ if (!mWasFaceUp) {
+ // Check if its face up enough.
+ mSamples[mSampleIndex] = z > FACE_UP_GRAVITY_THRESHOLD;
+
+ // face up
+ if (filterSamples()) {
+ mWasFaceUp = true;
+ for (int i = 0; i < SENSOR_SAMPLES; i++) {
+ mSamples[i] = false;
+ }
+ }
+ } else {
+ // Check if its face down enough.
+ mSamples[mSampleIndex] = z < FACE_DOWN_GRAVITY_THRESHOLD;
+
+ // face down
+ if (filterSamples()) {
+ mStopped = true;
+ handleAction();
+ }
+ }
+
+ mSampleIndex = ((mSampleIndex + 1) % SENSOR_SAMPLES);
+ }
+ };
+
+ public void handleAction() {
+ switch(getFlipAction()) {
+ case MUTE_RINGER:
+ if (mTelephonyService!= null) {
+ try{
+ mTelephonyService.silenceRinger();
+ }catch(android.os.RemoteException e){
+ Log.d(TAG, e.toString());
+ }
+ }
+ break;
+ case DISMISS_CALL:
+ CallCommandClient.getInstance().rejectCall(
+ CallList.getInstance().getIncomingCall(), false, null);
+ break;
+ case RINGING_NO_ACTION:
+ default:
+ //no action
+ break;
+ }
+ }
+
+ private int getFlipAction(){
+ return Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.CALL_FLIP_ACTION_KEY, RINGING_NO_ACTION);
+ }
+
+ private ITelephony getTeleService() {
+ TelephonyManager tm =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ try {
+ // Java reflection to gain access to TelephonyManager's
+ // ITelephony getter
+ Class c = Class.forName(tm.getClass().getName());
+ Method m = c.getDeclaredMethod("getITelephony");
+ m.setAccessible(true);
+ return (ITelephony) m.invoke(tm);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "- Could not connect to telephony subsystem");
+ return null;
+ }
+ }
+
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
diff --git a/src/com/android/incallui/InCallPresenter.java b/src/com/android/incallui/InCallPresenter.java
index a3ea80d..cedda0b 100644
--- a/src/com/android/incallui/InCallPresenter.java
+++ b/src/com/android/incallui/InCallPresenter.java
@@ -58,6 +58,7 @@
private CallList mCallList;
private InCallActivity mInCallActivity;
private InCallState mInCallState = InCallState.NO_CALLS;
+ private AccelerometerListener mAccelerometerListener;
private ProximitySensor mProximitySensor;
private boolean mServiceConnected = false;
private boolean mCallUiInBackground = false;
@@ -108,6 +109,8 @@
mProximitySensor = new ProximitySensor(context, mAudioModeProvider);
addListener(mProximitySensor);
+ mAccelerometerListener = new AccelerometerListener(context);
+
mCallList = callList;
// This only gets called by the service so this is okay.
@@ -235,6 +238,9 @@
// Renable notification shade and soft navigation buttons, if we are no longer in the
// incoming call screen
if (!newState.isIncoming()) {
+ if (mAccelerometerListener != null) {
+ mAccelerometerListener.enableSensor(false);
+ }
CallCommandClient.getInstance().setSystemBarNavigationEnabled(true);
}
@@ -271,6 +277,9 @@
// on new incoming call as long it is no background call
if (newState.isIncoming() && !mCallUiInBackground) {
CallCommandClient.getInstance().setSystemBarNavigationEnabled(false);
+ if (mAccelerometerListener != null) {
+ mAccelerometerListener.enableSensor(true);
+ }
}
for (IncomingCallListener listener : mIncomingCallListeners) {
@@ -456,6 +465,9 @@
// (1) Attempt to answer a call
if (incomingCall != null) {
CallCommandClient.getInstance().answerCall(incomingCall.getCallId());
+ if (mAccelerometerListener != null) {
+ mAccelerometerListener.enableSensor(false);
+ }
return true;
}
@@ -694,6 +706,8 @@
}
mProximitySensor = null;
+ mAccelerometerListener = null;
+
mAudioModeProvider = null;
if (mStatusBarNotifier != null) {