InCallUI: SmartMute

Mute the incoming call by flipping the phone

AICP: Brought to N by combining into one commit
3a4c1f7b7b319a3bfd560423ae19e4da3f52eda1
9063092dfc3eac9bc30f73fdca40ec6fe1aa1f44

AICP: brought up to O

@eyosen: brought up to P

Change-Id: I3ff27450eecbf501fafb2d75317a0d7dd844e7b2
Signed-off-by: linuxxxxx <joey@cyanogenmoditalia.it>
Signed-off-by: blinoff82 <blinov@gmail.com>
Signed-off-by: DennySPB <dennyspb@gmail.com>
diff --git a/java/com/android/dialer/app/res/values/bliss_strings.xml b/java/com/android/dialer/app/res/values/bliss_strings.xml
index 2425df2..5532bce 100644
--- a/java/com/android/dialer/app/res/values/bliss_strings.xml
+++ b/java/com/android/dialer/app/res/values/bliss_strings.xml
@@ -41,4 +41,9 @@
     <string name="auto_answer_delay_title">Autoanswer delay</string>
     <string name="auto_answer_dialog_title">Autoanswer delay in (ms)</string>
     <string name="auto_answer_delay_summary">Autoanswer will be activated in <xliff:g id="number">%d</xliff:g> ms</string>
+
+    <!-- Smart Options -->
+    <string name="smart_category_title">Gestures</string>
+    <string name="smart_mute_title">Smart mute</string>
+    <string name="smart_mute_summary">Mute the incoming call by flipping the device</string>
 </resources>
diff --git a/java/com/android/dialer/app/res/xml/sound_settings.xml b/java/com/android/dialer/app/res/xml/sound_settings.xml
index 673ec56..3062d40 100644
--- a/java/com/android/dialer/app/res/xml/sound_settings.xml
+++ b/java/com/android/dialer/app/res/xml/sound_settings.xml
@@ -90,4 +90,17 @@
 
   </PreferenceCategory>
 
+  <PreferenceCategory
+      android:key="dialer_general_smart_category_key"
+      android:title="@string/smart_category_title"
+      android:persistent="false">
+
+      <SwitchPreference
+          android:key="button_smart_mute"
+          android:title="@string/smart_mute_title"
+          android:summary="@string/smart_mute_summary"
+          android:defaultValue="false" />
+
+  </PreferenceCategory>
+
 </PreferenceScreen>
diff --git a/java/com/android/dialer/app/settings/SoundSettingsFragment.java b/java/com/android/dialer/app/settings/SoundSettingsFragment.java
index ce03322..c8e5a60 100644
--- a/java/com/android/dialer/app/settings/SoundSettingsFragment.java
+++ b/java/com/android/dialer/app/settings/SoundSettingsFragment.java
@@ -53,6 +53,8 @@
 
   private static final int MSG_UPDATE_RINGTONE_SUMMARY = 1;
 
+  public static final String BUTTON_SMART_MUTE_KEY = "button_smart_mute";
+
   private Preference ringtonePreference;
   private final Handler ringtoneLookupComplete =
       new Handler() {
diff --git a/java/com/android/incallui/AccelerometerListener.java b/java/com/android/incallui/AccelerometerListener.java
index 92e62b0..35a05b4 100644
--- a/java/com/android/incallui/AccelerometerListener.java
+++ b/java/com/android/incallui/AccelerometerListener.java
@@ -39,6 +39,7 @@
   private static final boolean DEBUG = true;
   private static final boolean VDEBUG = false;
   private static final int ORIENTATION_CHANGED = 1234;
+  private static final int FACE_UP_CHANGED = 1235;
   private static final int VERTICAL_DEBOUNCE = 100;
   private static final int HORIZONTAL_DEBOUNCE = 500;
   private static final double VERTICAL_ANGLE = 50.0;
@@ -50,7 +51,7 @@
   // This is sent to the client after a rebounce delay, at which point it is copied to
   // mOrientation.
   private int pendingOrientation;
-  private OrientationListener listener;
+  private ChangeListener listener;
   Handler handler =
       new Handler() {
         @Override
@@ -68,10 +69,13 @@
                               : (orientation == ORIENTATION_VERTICAL ? "vertical" : "unknown")));
                 }
                 if (listener != null) {
-                  listener.orientationChanged(orientation);
+                  listener.onOrientationChanged(orientation);
                 }
               }
               break;
+            case FACE_UP_CHANGED:
+                listener.onDeviceFlipped(msg.arg1 == 0);
+              break;
           }
         }
       };
@@ -88,12 +92,33 @@
         }
       };
 
+  // Flip detection
+  private static final int FACE_UP_GRAVITY_THRESHOLD = 7;
+  private static final int FACE_DOWN_GRAVITY_THRESHOLD = -7;
+  private static final int SENSOR_SAMPLES = 3;
+  private static final int MIN_ACCEPT_COUNT = SENSOR_SAMPLES - 1;
+
+  private boolean mWasFaceUp;
+  private boolean[] mSamples = new boolean[SENSOR_SAMPLES];
+  private int mSampleIndex;
+
+  public interface ChangeListener {
+    void onOrientationChanged(int orientation);
+    void onDeviceFlipped(boolean faceDown);
+  }
+
   public AccelerometerListener(Context context) {
     sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
     sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
   }
 
-  public void setListener(OrientationListener listener) {
+  public AccelerometerListener(Context context, ChangeListener listener) {
+      setListener(listener);
+      sensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+      sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+  }
+
+  public void setListener(ChangeListener listener) {
     this.listener = listener;
   }
 
@@ -105,6 +130,8 @@
       if (enable) {
         orientation = ORIENTATION_UNKNOWN;
         pendingOrientation = ORIENTATION_UNKNOWN;
+        mWasFaceUp = false;
+        resetFlipSamples();
         sensorManager.registerListener(sensorListener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
       } else {
         sensorManager.unregisterListener(sensorListener);
@@ -113,6 +140,22 @@
     }
   }
 
+  private void resetFlipSamples() {
+      for (int i = 0; i < SENSOR_SAMPLES; i++) {
+          mSamples[i] = false;
+      }
+  }
+
+  private boolean filterFlipSamples() {
+      int trues = 0;
+      for (int i = 0; i < mSamples.length; i++) {
+          if (mSamples[i]) {
+              ++trues;
+          }
+      }
+      return trues >= MIN_ACCEPT_COUNT;
+  }
+
   private void setOrientation(int orientation) {
     synchronized (this) {
       if (pendingOrientation == orientation) {
@@ -140,6 +183,17 @@
     }
   }
 
+  private void setIsFaceUp(boolean faceUp) {
+      synchronized (this) {
+          if (mWasFaceUp != faceUp) {
+              handler.removeMessages(FACE_UP_CHANGED);
+              handler.obtainMessage(FACE_UP_CHANGED, faceUp ? 1 : 0, 0).sendToTarget();
+              mWasFaceUp = faceUp;
+              resetFlipSamples();
+          }
+      }
+  }
+
   private void onSensorEvent(double x, double y, double z) {
     if (VDEBUG) {
       LogUtil.d(TAG, "onSensorEvent(" + x + ", " + y + ", " + z + ")");
@@ -163,10 +217,24 @@
       LogUtil.d(TAG, "angle: " + angle + " orientation: " + orientation);
     }
     setOrientation(orientation);
-  }
 
-  public interface OrientationListener {
+    boolean nowFaceUp, wasFaceUp;
+    synchronized (this) {
+        nowFaceUp = wasFaceUp = mWasFaceUp;
+    }
 
-    void orientationChanged(int orientation);
+    if (!wasFaceUp) {
+        // Check if its face up enough.
+        mSamples[mSampleIndex] = z > FACE_UP_GRAVITY_THRESHOLD;
+    } else {
+        // Check if its face down enough.
+        mSamples[mSampleIndex] = z < FACE_DOWN_GRAVITY_THRESHOLD;
+    }
+    if (filterFlipSamples()) {
+        nowFaceUp = !wasFaceUp;
+    }
+
+    mSampleIndex = ((mSampleIndex + 1) % SENSOR_SAMPLES);
+    setIsFaceUp(nowFaceUp);
   }
 }
diff --git a/java/com/android/incallui/InCallPresenter.java b/java/com/android/incallui/InCallPresenter.java
index 8881029..ab749ed 100644
--- a/java/com/android/incallui/InCallPresenter.java
+++ b/java/com/android/incallui/InCallPresenter.java
@@ -18,10 +18,12 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.SharedPreferences;
 import android.graphics.Point;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Trace;
+import android.preference.PreferenceManager;
 import android.support.annotation.MainThread;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
@@ -94,7 +96,8 @@
  * presenters that want to listen in on the in-call state changes. TODO: This class has become more
  * of a state machine at this point. Consider renaming.
  */
-public class InCallPresenter implements CallList.Listener, AudioModeProvider.AudioModeListener {
+public class InCallPresenter implements CallList.Listener, AudioModeProvider.AudioModeListener,
+        AccelerometerListener.ChangeListener {
   private static final String PIXEL2017_SYSTEM_FEATURE =
       "com.google.android.feature.PIXEL_2017_EXPERIENCE";
   private static final String CALL_CONFIGURATION_EXTRA = "call_configuration";
@@ -198,6 +201,7 @@
       };
   private InCallState inCallState = InCallState.NO_CALLS;
   private ProximitySensor proximitySensor;
+  private AccelerometerListener mAccelerometerListener;
   private final PseudoScreenState pseudoScreenState = new PseudoScreenState();
   private boolean serviceConnected;
   private InCallCameraManager inCallCameraManager;
@@ -374,6 +378,7 @@
 
     this.proximitySensor = proximitySensor;
     addListener(this.proximitySensor);
+    mAccelerometerListener = new AccelerometerListener(context, this);
 
     if (themeColorManager == null) {
       themeColorManager = new ThemeColorManager(new InCallUIMaterialColorMapUtils(this.context));
@@ -867,6 +872,10 @@
     LogUtil.d(
         "InCallPresenter.onCallListChange", "onCallListChange newState changed to " + newState);
 
+    if (!newState.isIncoming() && mAccelerometerListener != null) {
+        mAccelerometerListener.enable(false);
+    }
+
     // Set the new state before announcing it to the world
     LogUtil.i(
         "InCallPresenter.onCallListChange",
@@ -965,6 +974,10 @@
         "InCallPresenter.onIncomingCall", "Phone switching state: " + oldState + " -> " + newState);
     inCallState = newState;
 
+    if (newState.isIncoming() && mAccelerometerListener != null) {
+        mAccelerometerListener.enable(true);
+    }
+
     Trace.beginSection("listener.onIncomingCall");
     for (IncomingCallListener listener : incomingCallListeners) {
       listener.onIncomingCall(oldState, inCallState, call);
@@ -1058,6 +1071,22 @@
         && (number.length() <= 8 || number.startsWith("*#*#") || number.endsWith("#*#*"));
   }
 
+  public void onOrientationChanged(int orientation) {
+      // ignored
+  }
+
+  @Override
+  public void onDeviceFlipped(boolean faceDown) {
+      if (!faceDown) {
+          return;
+      }
+
+      SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+      if (prefs.getBoolean("button_smart_mute", false)) {
+          TelecomUtil.silenceRinger(context);
+      }
+  }
+
   /** Given the call list, return the state in which the in-call screen should be. */
   public InCallState getPotentialStateFromCallList(CallList callList) {
 
@@ -1344,6 +1373,9 @@
     // (1) Attempt to answer a call
     if (incomingCall != null) {
       incomingCall.answer(VideoProfile.STATE_AUDIO_ONLY);
+      if (mAccelerometerListener != null) {
+          mAccelerometerListener.enable(false);
+      }
       return true;
     }
 
@@ -1649,6 +1681,11 @@
       }
       proximitySensor = null;
 
+      if (mAccelerometerListener != null) {
+          mAccelerometerListener.enable(false);
+          mAccelerometerListener = null;
+      }
+
       if (statusBarNotifier != null) {
         removeListener(statusBarNotifier);
         EnrichedCallComponent.get(context)
diff --git a/java/com/android/incallui/ProximitySensor.java b/java/com/android/incallui/ProximitySensor.java
index 941dc0b..580e8c7 100644
--- a/java/com/android/incallui/ProximitySensor.java
+++ b/java/com/android/incallui/ProximitySensor.java
@@ -50,7 +50,7 @@
  * and disabled. Most of that state is fed into this class through public methods.
  */
 public class ProximitySensor
-    implements AccelerometerListener.OrientationListener, InCallStateListener, AudioModeListener, SensorEventListener  {
+    implements AccelerometerListener.ChangeListener, InCallStateListener, AudioModeListener, SensorEventListener {
 
   private static final String TAG = ProximitySensor.class.getSimpleName();
   private static final String PREF_KEY_DISABLE_PROXI_SENSOR = "disable_proximity_sensor_key";
@@ -160,11 +160,16 @@
 
   /** Called to identify when the device is laid down flat. */
   @Override
-  public void orientationChanged(int orientation) {
+  public void onOrientationChanged(int orientation) {
     this.orientation = orientation;
     updateProximitySensorMode();
   }
 
+  @Override
+  public void onDeviceFlipped(boolean faceDown) {
+      // ignored
+  }
+
   /** Called to keep track of the overall UI state. */
   @Override
   public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {