Add a "show touches" option for demos and presentations.
Bug: 4569045

Change-Id: I8726ea292dd7def790a5e40d7d7e58968974f896
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 15c57e6..c821c74 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1737,6 +1737,14 @@
         public static final String POINTER_LOCATION = "pointer_location";
 
         /**
+         * Show touch positions on screen?
+         * 0 = no
+         * 1 = yes
+         * @hide
+         */
+        public static final String SHOW_TOUCHES = "show_touches";
+
+        /**
          * Log raw orientation data from {@link WindowOrientationListener} for use with the
          * orientationplot.py tool.
          * 0 = no
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index bfcf8e0..f4e5b79 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -2491,7 +2491,8 @@
 
     bool resetNeeded = false;
     if (!changes || (changes & (InputReaderConfiguration::CHANGE_DISPLAY_INFO
-            | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT))) {
+            | InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT
+            | InputReaderConfiguration::CHANGE_SHOW_TOUCHES))) {
         // Configure device sources, surface dimensions, orientation and
         // scaling factors.
         configureSurface(when, &resetNeeded);
@@ -2681,18 +2682,19 @@
     bool deviceModeChanged;
     if (mDeviceMode != oldDeviceMode) {
         deviceModeChanged = true;
-
-        if (mDeviceMode == DEVICE_MODE_POINTER) {
-            if (mPointerController == NULL) {
-                mPointerController = getPolicy()->obtainPointerController(getDeviceId());
-            }
-        } else {
-            mPointerController.clear();
-        }
-
         mOrientedRanges.clear();
     }
 
+    // Create pointer controller if needed.
+    if (mDeviceMode == DEVICE_MODE_POINTER ||
+            (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches)) {
+        if (mPointerController == NULL) {
+            mPointerController = getPolicy()->obtainPointerController(getDeviceId());
+        }
+    } else {
+        mPointerController.clear();
+    }
+
     bool orientationChanged = mSurfaceOrientation != orientation;
     if (orientationChanged) {
         mSurfaceOrientation = orientation;
@@ -3380,7 +3382,7 @@
         cookPointerData();
 
         // Dispatch the touches either directly or by translation through a pointer on screen.
-        if (mPointerController != NULL) {
+        if (mDeviceMode == DEVICE_MODE_POINTER) {
             for (BitSet32 idBits(mCurrentRawPointerData.touchingIdBits); !idBits.isEmpty(); ) {
                 uint32_t id = idBits.clearFirstMarkedBit();
                 const RawPointerData::Pointer& pointer = mCurrentRawPointerData.pointerForId(id);
@@ -3418,6 +3420,17 @@
 
             dispatchPointerUsage(when, policyFlags, pointerUsage);
         } else {
+            if (mDeviceMode == DEVICE_MODE_DIRECT
+                    && mConfig.showTouches && mPointerController != NULL) {
+                mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
+                mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL);
+
+                mPointerController->setButtonState(mCurrentButtonState);
+                mPointerController->setSpots(mCurrentCookedPointerData.pointerCoords,
+                        mCurrentCookedPointerData.idToIndex,
+                        mCurrentCookedPointerData.touchingIdBits);
+            }
+
             dispatchHoverExit(when, policyFlags);
             dispatchTouches(when, policyFlags);
             dispatchHoverEnterAndMove(when, policyFlags);
@@ -3442,7 +3455,7 @@
 }
 
 void TouchInputMapper::timeoutExpired(nsecs_t when) {
-    if (mPointerController != NULL) {
+    if (mDeviceMode == DEVICE_MODE_POINTER) {
         if (mPointerUsage == POINTER_USAGE_GESTURES) {
             dispatchPointerGestures(when, 0 /*policyFlags*/, true /*isTimeout*/);
         }
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index bad96df..cd3ea37 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -56,6 +56,9 @@
         // The display size or orientation changed.
         CHANGE_DISPLAY_INFO = 1 << 2,
 
+        // The visible touches option changed.
+        CHANGE_SHOW_TOUCHES = 1 << 3,
+
         // All devices must be reopened.
         CHANGE_MUST_REOPEN = 1 << 31,
     };
@@ -140,6 +143,9 @@
     // will cover this portion of the display diagonal.
     float pointerGestureZoomSpeedRatio;
 
+    // True to show the location of touches on the touch screen as spots.
+    bool showTouches;
+
     InputReaderConfiguration() :
             virtualKeyQuietTime(0),
             pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
@@ -155,7 +161,8 @@
             pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
             pointerGestureSwipeMaxWidthRatio(0.25f),
             pointerGestureMovementSpeedRatio(0.8f),
-            pointerGestureZoomSpeedRatio(0.3f) { }
+            pointerGestureZoomSpeedRatio(0.3f),
+            showTouches(false) { }
 
     bool getDisplayInfo(int32_t displayId, bool external,
             int32_t* width, int32_t* height, int32_t* orientation) const;
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index c8b18c8..60333a3 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -94,6 +94,7 @@
     private static native boolean nativeTransferTouchFocus(InputChannel fromChannel,
             InputChannel toChannel);
     private static native void nativeSetPointerSpeed(int speed);
+    private static native void nativeSetShowTouches(boolean enabled);
     private static native String nativeDump();
     private static native void nativeMonitor();
     
@@ -147,7 +148,10 @@
         nativeStart();
 
         registerPointerSpeedSettingObserver();
+        registerShowTouchesSettingObserver();
+
         updatePointerSpeedFromSettings();
+        updateShowTouchesFromSettings();
     }
     
     public void setDisplaySize(int displayId, int width, int height,
@@ -454,6 +458,32 @@
         return speed;
     }
 
+    public void updateShowTouchesFromSettings() {
+        int setting = getShowTouchesSetting(0);
+        nativeSetShowTouches(setting != 0);
+    }
+
+    private void registerShowTouchesSettingObserver() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.getUriFor(Settings.System.SHOW_TOUCHES), true,
+                new ContentObserver(mWindowManagerService.mH) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        updateShowTouchesFromSettings();
+                    }
+                });
+    }
+
+    private int getShowTouchesSetting(int defaultValue) {
+        int result = defaultValue;
+        try {
+            result = Settings.System.getInt(mContext.getContentResolver(),
+                    Settings.System.SHOW_TOUCHES);
+        } catch (SettingNotFoundException snfe) {
+        }
+        return result;
+    }
+
     public void dump(PrintWriter pw) {
         String dumpStr = nativeDump();
         if (dumpStr != null) {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index f2a0a71..0a723e8 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -179,6 +179,7 @@
     void setInputDispatchMode(bool enabled, bool frozen);
     void setSystemUiVisibility(int32_t visibility);
     void setPointerSpeed(int32_t speed);
+    void setShowTouches(bool enabled);
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -233,6 +234,9 @@
         // True if pointer gestures are enabled.
         bool pointerGesturesEnabled;
 
+        // Show touches feature enable/disable.
+        bool showTouches;
+
         // Sprite controller singleton, created on first use.
         sp<SpriteController> spriteController;
 
@@ -276,6 +280,7 @@
         mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
         mLocked.pointerSpeed = 0;
         mLocked.pointerGesturesEnabled = true;
+        mLocked.showTouches = false;
     }
 
     sp<EventHub> eventHub = new EventHub();
@@ -431,6 +436,8 @@
                 * POINTER_SPEED_EXPONENT);
         outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled;
 
+        outConfig->showTouches = mLocked.showTouches;
+
         outConfig->setDisplayInfo(0, false /*external*/,
                 mLocked.displayWidth, mLocked.displayHeight, mLocked.displayOrientation);
         outConfig->setDisplayInfo(0, true /*external*/,
@@ -678,6 +685,22 @@
             InputReaderConfiguration::CHANGE_POINTER_SPEED);
 }
 
+void NativeInputManager::setShowTouches(bool enabled) {
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        if (mLocked.showTouches == enabled) {
+            return;
+        }
+
+        LOGI("Setting show touches feature to %s.", enabled ? "enabled" : "disabled");
+        mLocked.showTouches = enabled;
+    } // release lock
+
+    mInputManager->getReader()->requestRefreshConfiguration(
+            InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
+}
+
 bool NativeInputManager::isScreenOn() {
     return android_server_PowerManagerService_isScreenOn();
 }
@@ -1276,6 +1299,15 @@
     gNativeInputManager->setPointerSpeed(speed);
 }
 
+static void android_server_InputManager_nativeSetShowTouches(JNIEnv* env,
+        jclass clazz, jboolean enabled) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->setShowTouches(enabled);
+}
+
 static jstring android_server_InputManager_nativeDump(JNIEnv* env, jclass clazz) {
     if (checkInputManagerUnitialized(env)) {
         return NULL;
@@ -1343,6 +1375,8 @@
             (void*) android_server_InputManager_nativeTransferTouchFocus },
     { "nativeSetPointerSpeed", "(I)V",
             (void*) android_server_InputManager_nativeSetPointerSpeed },
+    { "nativeSetShowTouches", "(Z)V",
+            (void*) android_server_InputManager_nativeSetShowTouches },
     { "nativeDump", "()Ljava/lang/String;",
             (void*) android_server_InputManager_nativeDump },
     { "nativeMonitor", "()V",