Fix issue with entering '+' with switch access

Distinguish between a manual long press and system long
press (via accessibility) when modifying the current
dial string.

Bug: 23554996
Change-Id: I3fc610c8e24bdb39729b827715e08a3e7d73ba1f
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 0893b1c..140437e 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.dialer;
 
+import com.google.common.annotations.VisibleForTesting;
+
 import android.app.Fragment;
 import android.app.FragmentTransaction;
 import android.content.ActivityNotFoundException;
@@ -128,7 +130,8 @@
     private static final String KEY_FIRST_LAUNCH = "first_launch";
     private static final String KEY_IS_DIALPAD_SHOWN = "is_dialpad_shown";
 
-    private static final String TAG_DIALPAD_FRAGMENT = "dialpad";
+    @VisibleForTesting
+    public static final String TAG_DIALPAD_FRAGMENT = "dialpad";
     private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
     private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
     private static final String TAG_FAVORITES_FRAGMENT = "favorites";
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 9c77f30..5177110 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -414,6 +414,7 @@
         return mDigits != null;
     }
 
+    @VisibleForTesting
     public EditText getDigitsWidget() {
         return mDigits;
     }
@@ -970,7 +971,7 @@
                 // Just for safety we also check if the digits field is empty or not.
                 if (isDigitsEmpty() || TextUtils.equals(mDigits.getText(), "1")) {
                     // We'll try to initiate voicemail and thus we want to remove irrelevant string.
-                    removePreviousDigitIfPossible();
+                    removePreviousDigitIfPossible('1');
 
                     List<PhoneAccountHandle> subscriptionAccountHandles =
                             PhoneAccountUtils.getSubscriptionPhoneAccounts(getActivity());
@@ -1007,8 +1008,13 @@
                 return false;
             }
             case R.id.zero: {
-                // Remove tentative input ('0') done by onTouch().
-                removePreviousDigitIfPossible();
+                if (mPressedDialpadKeys.contains(view)) {
+                    // If the zero key is currently pressed, then the long press occurred by touch
+                    // (and not via other means like certain accessibility input methods).
+                    // Remove the '0' that was input when the key was first pressed.
+                    removePreviousDigitIfPossible('0');
+                }
+
                 keyPressed(KeyEvent.KEYCODE_PLUS);
 
                 // Stop tone immediately
@@ -1029,12 +1035,16 @@
     }
 
     /**
-     * Remove the digit just before the current position. This can be used if we want to replace
-     * the previous digit or cancel previously entered character.
+     * Remove the digit just before the current position of the cursor, iff the following conditions
+     *  are true:
+     * 1) The cursor is not positioned at index 0.
+     * 2) The digit before the current cursor position matches the current digit.
+     *
+     * @param digit to remove from the digits view.
      */
-    private void removePreviousDigitIfPossible() {
+    private void removePreviousDigitIfPossible(char digit) {
         final int currentPosition = mDigits.getSelectionStart();
-        if (currentPosition > 0) {
+        if (currentPosition > 0 && digit == mDigits.getText().charAt(currentPosition - 1)) {
             mDigits.setSelection(currentPosition);
             mDigits.getText().delete(currentPosition - 1, currentPosition);
         }
diff --git a/tests/src/com/android/dialer/dialpad/DialpadFragmentInstrumentationTest.java b/tests/src/com/android/dialer/dialpad/DialpadFragmentInstrumentationTest.java
new file mode 100644
index 0000000..876c13b
--- /dev/null
+++ b/tests/src/com/android/dialer/dialpad/DialpadFragmentInstrumentationTest.java
@@ -0,0 +1,121 @@
+package com.android.dialer.dialpad;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+
+import com.android.dialer.DialtactsActivity;
+import com.android.dialer.R;
+
+/**
+ * Tests that rely on instrumenting an actual instance of a {@link DialpadFragment}.
+ */
+public class DialpadFragmentInstrumentationTest extends
+        ActivityInstrumentationTestCase2<DialtactsActivity> {
+    private DialtactsActivity mActivity;
+
+    public DialpadFragmentInstrumentationTest() {
+        super(DialtactsActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mActivity = getActivity();
+    }
+
+    /**
+     * Simulates a long click event on the zero key with a prior onPressed callback.
+     *
+     */
+    public void testManualLongClickZero_DeletesPreviousCharacter() {
+        final DialpadFragment fragment = showDialpad();
+        pressAndReleaseKey(9, fragment);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                final View zeroKey = findViewByDigit(0, fragment);
+                fragment.onPressed(zeroKey, true);
+                fragment.onLongClick(zeroKey);
+            }
+        });
+
+        assertEquals("9+", fragment.getDigitsWidget().getText().toString());
+    }
+
+    /**
+     * Simulates a long click event on the zero key without a prior onPressed
+     * callback.
+     */
+    public void testSystemLongClickZero_PreservesPreviousCharacter() {
+        final DialpadFragment fragment = showDialpad();
+        pressAndReleaseKey(9, fragment);
+
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                final View zeroKey = findViewByDigit(0, fragment);
+                fragment.onLongClick(zeroKey);
+            }
+        });
+
+        assertEquals("9+", fragment.getDigitsWidget().getText().toString());
+    }
+
+    private DialpadFragment showDialpad() {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.showDialpad();
+            }
+        });
+        getInstrumentation().waitForIdleSync();
+        return (DialpadFragment) mActivity.getFragmentManager().findFragmentByTag(
+                DialtactsActivity.TAG_DIALPAD_FRAGMENT);
+    }
+
+    private void pressAndReleaseKey(int digit, final DialpadFragment fragment) {
+        final View dialpadKey = findViewByDigit(digit, fragment);
+        final String digitsBefore = fragment.getDigitsWidget().getText().toString();
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                fragment.onPressed(dialpadKey, true);
+                fragment.onPressed(dialpadKey, false);
+            }
+        });
+        assertEquals(digitsBefore + String.valueOf(digit),
+                fragment.getDigitsWidget().getText().toString());
+    }
+
+    private View findViewByDigit(int digit, DialpadFragment fragment) {
+        return fragment.getView().findViewById(getViewIdByDigit(digit));
+    }
+
+    private int getViewIdByDigit(int digit) {
+        switch (digit) {
+            case 0:
+                return R.id.zero;
+            case 1:
+                return R.id.one;
+            case 2:
+                return R.id.two;
+            case 3:
+                return R.id.three;
+            case 4:
+                return R.id.four;
+            case 5:
+                return R.id.five;
+            case 6:
+                return R.id.six;
+            case 7:
+                return R.id.seven;
+            case 8:
+                return R.id.eight;
+            case 9:
+                return R.id.nine;
+            default:
+                return 0;
+        }
+    }
+}