Add touch navigation input source

Bug: 8276741
Change-Id: I674b9804bf9ae76d694ae7073b54a7d43474a43c
diff --git a/api/current.txt b/api/current.txt
index 3f3819c..3132a91 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -24171,6 +24171,7 @@
     field public static final int SOURCE_CLASS_BUTTON = 1; // 0x1
     field public static final int SOURCE_CLASS_JOYSTICK = 16; // 0x10
     field public static final int SOURCE_CLASS_MASK = 255; // 0xff
+    field public static final int SOURCE_CLASS_NONE = 0; // 0x0
     field public static final int SOURCE_CLASS_POINTER = 2; // 0x2
     field public static final int SOURCE_CLASS_POSITION = 8; // 0x8
     field public static final int SOURCE_CLASS_TRACKBALL = 4; // 0x4
@@ -24182,6 +24183,7 @@
     field public static final int SOURCE_STYLUS = 16386; // 0x4002
     field public static final int SOURCE_TOUCHPAD = 1048584; // 0x100008
     field public static final int SOURCE_TOUCHSCREEN = 4098; // 0x1002
+    field public static final int SOURCE_TOUCH_NAVIGATION = 2097152; // 0x200000
     field public static final int SOURCE_TRACKBALL = 65540; // 0x10004
     field public static final int SOURCE_UNKNOWN = 0; // 0x0
   }
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index c3dc76d..dd523d2 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -62,7 +62,14 @@
      * specify the desired interpretation for its input events.
      */
     public static final int SOURCE_CLASS_MASK = 0x000000ff;
-    
+
+    /**
+     * The input source has no class.
+     *
+     * It is up to the application to determine how to handle the device based on the device type.
+     */
+    public static final int SOURCE_CLASS_NONE = 0x00000000;
+
     /**
      * The input source has buttons or keys.
      * Examples: {@link #SOURCE_KEYBOARD}, {@link #SOURCE_DPAD}.
@@ -202,6 +209,17 @@
     public static final int SOURCE_TOUCHPAD = 0x00100000 | SOURCE_CLASS_POSITION;
 
     /**
+     * The input source is a touch device whose motions should be interpreted as navigation events.
+     *
+     * For example, an upward swipe should be as an upward focus traversal in the same manner as
+     * pressing up on a D-Pad would be. Swipes to the left, right and down should be treated in a
+     * similar manner.
+     *
+     * @see #SOURCE_CLASS_NONE
+     */
+    public static final int SOURCE_TOUCH_NAVIGATION = 0x00200000 | SOURCE_CLASS_NONE;
+
+    /**
      * The input source is a joystick.
      * (It may also be a {@link #SOURCE_GAMEPAD}).
      *
diff --git a/core/java/android/view/SimulatedDpad.java b/core/java/android/view/SimulatedDpad.java
index 883fd49..c889328 100644
--- a/core/java/android/view/SimulatedDpad.java
+++ b/core/java/android/view/SimulatedDpad.java
@@ -28,7 +28,7 @@
 import android.util.Log;
 
 /**
- * This class creates DPAD events from touchpad events.
+ * This class creates DPAD events from TouchNavigation events.
  * 
  * @see ViewRootImpl
  */
@@ -47,18 +47,18 @@
     private static final int MSG_FLICK = 313;
     // TODO: Pass touch slop from the input device
     private static final int TOUCH_SLOP = 30;
-    // The position of the previous touchpad event
-    private float mLastTouchpadXPosition;
-    private float mLastTouchpadYPosition;
-    // Where the touchpad was initially pressed
-    private float mTouchpadEnterXPosition;
-    private float mTouchpadEnterYPosition;
+    // The position of the previous TouchNavigation event
+    private float mLastTouchNavigationXPosition;
+    private float mLastTouchNavigationYPosition;
+    // Where the Touch Navigation was initially pressed
+    private float mTouchNavigationEnterXPosition;
+    private float mTouchNavigationEnterYPosition;
     // When the most recent ACTION_HOVER_ENTER occurred
-    private long mLastTouchPadStartTimeMs = 0;
+    private long mLastTouchNavigationStartTimeMs = 0;
     // When the most recent direction key was sent
-    private long mLastTouchPadKeySendTimeMs = 0;
+    private long mLastTouchNavigationKeySendTimeMs = 0;
     // When the most recent touch event of any type occurred
-    private long mLastTouchPadEventTimeMs = 0;
+    private long mLastTouchNavigationEventTimeMs = 0;
     // Did the swipe begin in a valid region
     private boolean mEdgeSwipePossible;
 
@@ -140,7 +140,7 @@
         }
     };
 
-    public void updateTouchPad(ViewRootImpl viewroot, MotionEvent event,
+    public void updateTouchNavigation(ViewRootImpl viewroot, MotionEvent event,
             boolean synthesizeNewKeys) {
         if (!synthesizeNewKeys) {
             mHandler.removeMessages(MSG_FLICK);
@@ -149,14 +149,14 @@
         if (device == null) {
             return;
         }
-        // Store what time the touchpad event occurred
+        // Store what time the TouchNavigation event occurred
         final long time = SystemClock.uptimeMillis();
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
-                mLastTouchPadStartTimeMs = time;
+                mLastTouchNavigationStartTimeMs = time;
                 mAlwaysInTapRegion = true;
-                mTouchpadEnterXPosition = event.getX();
-                mTouchpadEnterYPosition = event.getY();
+                mTouchNavigationEnterXPosition = event.getX();
+                mTouchNavigationEnterYPosition = event.getY();
                 mAccumulatedX = 0;
                 mAccumulatedY = 0;
                 mLastMoveX = 0;
@@ -173,8 +173,8 @@
                 break;
             case MotionEvent.ACTION_MOVE:
                 // Determine whether the move is slop or an intentional move
-                float deltaX = event.getX() - mTouchpadEnterXPosition;
-                float deltaY = event.getY() - mTouchpadEnterYPosition;
+                float deltaX = event.getX() - mTouchNavigationEnterXPosition;
+                float deltaY = event.getY() - mTouchNavigationEnterYPosition;
                 if (mTouchSlopSquared < deltaX * deltaX + deltaY * deltaY) {
                     mAlwaysInTapRegion = false;
                 }
@@ -199,9 +199,9 @@
                     }
                 }
                 // Find the difference in position between the two most recent
-                // touchpad events
-                mLastMoveX = event.getX() - mLastTouchpadXPosition;
-                mLastMoveY = event.getY() - mLastTouchpadYPosition;
+                // TouchNavigation events
+                mLastMoveX = event.getX() - mLastTouchNavigationXPosition;
+                mLastMoveY = event.getY() - mLastTouchNavigationYPosition;
                 mAccumulatedX += mLastMoveX;
                 mAccumulatedY += mLastMoveY;
                 float mAccumulatedXSquared = mAccumulatedX * mAccumulatedX;
@@ -251,28 +251,28 @@
                     mAccumulatedY = isXAxis ? 0 : dominantAxis;
 
                     mLastKeySent = key;
-                    mKeySendRateMs = (int) ((time - mLastTouchPadKeySendTimeMs) / repeatCount);
-                    mLastTouchPadKeySendTimeMs = time;
+                    mKeySendRateMs = (int) (time - mLastTouchNavigationKeySendTimeMs) / repeatCount;
+                    mLastTouchNavigationKeySendTimeMs = time;
                 }
                 break;
             case MotionEvent.ACTION_UP:
-                if (time - mLastTouchPadStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
+                if (time - mLastTouchNavigationStartTimeMs < MAX_TAP_TIME && mAlwaysInTapRegion) {
                     if (synthesizeNewKeys) {
-                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchPadStartTimeMs, time,
-                                KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
-                                event.getMetaState(), event.getDeviceId(), 0,
-                                KeyEvent.FLAG_FALLBACK, event.getSource()));
-                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchPadStartTimeMs, time,
-                                KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
-                                event.getMetaState(), event.getDeviceId(), 0,
-                                KeyEvent.FLAG_FALLBACK, event.getSource()));
+                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
+                                    time, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER, 0,
+                                    event.getMetaState(), event.getDeviceId(), 0,
+                                    KeyEvent.FLAG_FALLBACK, event.getSource()));
+                        viewroot.enqueueInputEvent(new KeyEvent(mLastTouchNavigationStartTimeMs,
+                                    time, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER, 0,
+                                    event.getMetaState(), event.getDeviceId(), 0,
+                                    KeyEvent.FLAG_FALLBACK, event.getSource()));
                     }
                 } else {
                     float xMoveSquared = mLastMoveX * mLastMoveX;
                     float yMoveSquared = mLastMoveY * mLastMoveY;
                     // Determine whether the last gesture was a fling.
                     if (mMinFlickDistanceSquared <= xMoveSquared + yMoveSquared &&
-                            time - mLastTouchPadEventTimeMs <= MAX_TAP_TIME &&
+                            time - mLastTouchNavigationEventTimeMs <= MAX_TAP_TIME &&
                             mKeySendRateMs <= mMaxRepeatDelay && mKeySendRateMs > 0) {
                         mLastDeviceId = event.getDeviceId();
                         mLastSource = event.getSource();
@@ -291,8 +291,8 @@
         }
 
         // Store touch event position and time
-        mLastTouchPadEventTimeMs = time;
-        mLastTouchpadXPosition = event.getX();
-        mLastTouchpadYPosition = event.getY();
+        mLastTouchNavigationEventTimeMs = time;
+        mLastTouchNavigationXPosition = event.getX();
+        mLastTouchNavigationYPosition = event.getY();
     }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b8fae865..a937882 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3559,15 +3559,15 @@
     private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
         final MotionEvent event = (MotionEvent) q.mEvent;
         final int source = event.getSource();
-        final boolean isJoystick = (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
-        final boolean isTouchPad = (source & InputDevice.SOURCE_CLASS_POSITION) != 0;
+        final boolean isJoystick = event.isFromSource(InputDevice.SOURCE_CLASS_JOYSTICK);
+        final boolean isTouchNavigation = event.isFromSource(InputDevice.SOURCE_TOUCH_NAVIGATION);
 
         // If there is no view, then the event will not be handled.
         if (mView == null || !mAdded) {
             if (isJoystick) {
                 updateJoystickDirection(event, false);
-            } else if (isTouchPad) {
-              mSimulatedDpad.updateTouchPad(this, event, false);
+            } else if (isTouchNavigation) {
+                mSimulatedDpad.updateTouchNavigation(this, event, false);
             }
             return EVENT_NOT_HANDLED;
         }
@@ -3576,8 +3576,8 @@
         if (mView.dispatchGenericMotionEvent(event)) {
             if (isJoystick) {
                 updateJoystickDirection(event, false);
-            } else if (isTouchPad) {
-              mSimulatedDpad.updateTouchPad(this, event, false);
+            } else if (isTouchNavigation) {
+                mSimulatedDpad.updateTouchNavigation(this, event, false);
             }
             return EVENT_HANDLED;
         }
@@ -3588,8 +3588,8 @@
             updateJoystickDirection(event, true);
             return EVENT_HANDLED;
         }
-        if (isTouchPad) {
-            mSimulatedDpad.updateTouchPad(this, event, true);
+        if (isTouchNavigation) {
+            mSimulatedDpad.updateTouchNavigation(this, event, true);
             return EVENT_HANDLED;
         }
         return EVENT_NOT_HANDLED;
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index bc8df18..43d76bb 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -2788,6 +2788,8 @@
             mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
         } else if (deviceTypeString == "touchPad") {
             mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+        } else if (deviceTypeString == "touchNavigation") {
+            mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_NAVIGATION;
         } else if (deviceTypeString == "pointer") {
             mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
         } else if (deviceTypeString != "default") {
@@ -2832,6 +2834,9 @@
     case Parameters::DEVICE_TYPE_TOUCH_PAD:
         dump.append(INDENT4 "DeviceType: touchPad\n");
         break;
+    case Parameters::DEVICE_TYPE_TOUCH_NAVIGATION:
+        dump.append(INDENT4 "DeviceType: touchNavigation\n");
+        break;
     case Parameters::DEVICE_TYPE_POINTER:
         dump.append(INDENT4 "DeviceType: pointer\n");
         break;
@@ -2885,6 +2890,9 @@
         if (hasStylus()) {
             mSource |= AINPUT_SOURCE_STYLUS;
         }
+    } else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
+        mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
+        mDeviceMode = DEVICE_MODE_UNSCALED;
     } else {
         mSource = AINPUT_SOURCE_TOUCHPAD;
         mDeviceMode = DEVICE_MODE_UNSCALED;
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 61b21e2..c596b37 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -1192,6 +1192,7 @@
         enum DeviceType {
             DEVICE_TYPE_TOUCH_SCREEN,
             DEVICE_TYPE_TOUCH_PAD,
+            DEVICE_TYPE_TOUCH_NAVIGATION,
             DEVICE_TYPE_POINTER,
         };