Flip to Mute/Reject Call [3/3]

Ported over from SlimKat implementation (thanks Kufi)

Original message in 4.4:
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)

Change-Id: I744251f323f852bb47b9031bad52d261a1549f34
Signed-off-by: Pafcholini <nadyaivanova14@gmail.com>
diff --git a/src/com/android/incallui/AccelerometerListener.java b/src/com/android/incallui/AccelerometerListener.java
index b5ad296..40186bd 100644
--- a/src/com/android/incallui/AccelerometerListener.java
+++ b/src/com/android/incallui/AccelerometerListener.java
@@ -23,6 +23,9 @@
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Message;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.telecom.TelecomManager;
 import android.util.Log;
 
 /**
@@ -59,12 +62,24 @@
     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;
+
     public interface OrientationListener {
         public void orientationChanged(int orientation);
     }
 
+    private interface ResettableSensorEventListener extends SensorEventListener {
+        public void reset();
+    }
+
     public AccelerometerListener(Context context) {
-        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+        mContext = context;
+        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
         mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
     }
 
@@ -87,6 +102,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) {
@@ -145,6 +179,111 @@
         }
     };
 
+    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:
+                {
+                    TelecomManager tm =
+                            (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+                    if (tm != null) {
+                        tm.silenceRinger();
+                    }
+                }
+                break;
+            case DISMISS_CALL:
+                {
+                    TelecomManager tm =
+                            (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+                    if (tm != null) {
+                        tm.endCall();
+                    }
+                }
+                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);
+    }
+
     Handler mHandler = new Handler() {
         @Override
         public void handleMessage(Message msg) {
diff --git a/src/com/android/incallui/InCallPresenter.java b/src/com/android/incallui/InCallPresenter.java
index 07da827..da1e49a 100644
--- a/src/com/android/incallui/InCallPresenter.java
+++ b/src/com/android/incallui/InCallPresenter.java
@@ -97,6 +97,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 mAccountSelectionCancelled = false;
@@ -238,6 +239,8 @@
         mWakeLock = mPowerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK |
                 PowerManager.ACQUIRE_CAUSES_WAKEUP, "InCallPresenter");
 
+        mAccelerometerListener = new AccelerometerListener(context);
+
         mCallList = callList;
 
         // This only gets called by the service so this is okay.
@@ -472,6 +475,10 @@
         Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
         mInCallState = newState;
 
+        if (!newState.isIncoming() && mAccelerometerListener != null) {
+            mAccelerometerListener.enableSensor(false);
+        }
+
         // notify listeners of new state
         for (InCallStateListener listener : mListeners) {
             Log.d(this, "Notify " + listener + " of state " + mInCallState.toString());
@@ -501,6 +508,10 @@
         Log.i(this, "Phone switching state: " + oldState + " -> " + newState);
         mInCallState = newState;
 
+        if (newState.isIncoming() && mAccelerometerListener != null) {
+            mAccelerometerListener.enableSensor(true);
+        }
+
         for (IncomingCallListener listener : mIncomingCallListeners) {
             listener.onIncomingCall(oldState, mInCallState, call);
         }
@@ -720,6 +731,11 @@
      * Answers any incoming call.
      */
     public void answerIncomingCall(Context context, int videoState) {
+        // Just in case
+        if (mAccelerometerListener != null) {
+            mAccelerometerListener.enableSensor(false);
+        }
+
         // By the time we receive this intent, we could be shut down and call list
         // could be null.  Bail in those cases.
         if (mCallList == null) {
@@ -738,6 +754,11 @@
      * Declines any incoming call.
      */
     public void declineIncomingCall(Context context) {
+        // Just in case
+        if (mAccelerometerListener != null) {
+            mAccelerometerListener.enableSensor(false);
+        }
+
         // By the time we receive this intent, we could be shut down and call list
         // could be null.  Bail in those cases.
         if (mCallList == null) {
@@ -946,6 +967,9 @@
         if (incomingCall != null) {
             TelecomAdapter.getInstance().answerCall(
                     incomingCall.getId(), VideoProfile.STATE_AUDIO_ONLY);
+            if (mAccelerometerListener != null) {
+                mAccelerometerListener.enableSensor(false);
+            }
             return true;
         }
 
@@ -1367,6 +1391,8 @@
             mWakeLock = null;
             mPowerManager = null;
 
+            mAccelerometerListener = null;
+
             mAudioModeProvider = null;
 
             if (mStatusBarNotifier != null) {