Merge "Batch ACTION_MOVE events to vsync boundries when resizing task"
diff --git a/core/java/android/view/BatchedInputEventReceiver.java b/core/java/android/view/BatchedInputEventReceiver.java
new file mode 100644
index 0000000..b1d28e0
--- /dev/null
+++ b/core/java/android/view/BatchedInputEventReceiver.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.view;
+
+import android.os.Looper;
+
+/**
+ * Similar to {@link InputEventReceiver}, but batches events to vsync boundaries when possible.
+ * @hide
+ */
+public class BatchedInputEventReceiver extends InputEventReceiver {
+    Choreographer mChoreographer;
+    private boolean mBatchedInputScheduled;
+
+    public BatchedInputEventReceiver(
+            InputChannel inputChannel, Looper looper, Choreographer choreographer) {
+        super(inputChannel, looper);
+        mChoreographer = choreographer;
+    }
+
+    @Override
+    public void onBatchedInputEventPending() {
+        scheduleBatchedInput();
+    }
+
+    @Override
+    public void dispose() {
+        unscheduleBatchedInput();
+        super.dispose();
+    }
+
+    void doConsumeBatchedInput(long frameTimeNanos) {
+        if (mBatchedInputScheduled) {
+            mBatchedInputScheduled = false;
+            if (consumeBatchedInputEvents(frameTimeNanos) && frameTimeNanos != -1) {
+                // If we consumed a batch here, we want to go ahead and schedule the
+                // consumption of batched input events on the next frame. Otherwise, we would
+                // wait until we have more input events pending and might get starved by other
+                // things occurring in the process. If the frame time is -1, however, then
+                // we're in a non-batching mode, so there's no need to schedule this.
+                scheduleBatchedInput();
+            }
+        }
+    }
+
+    private void scheduleBatchedInput() {
+        if (!mBatchedInputScheduled) {
+            mBatchedInputScheduled = true;
+            mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
+        }
+    }
+
+    private void unscheduleBatchedInput() {
+        if (mBatchedInputScheduled) {
+            mBatchedInputScheduled = false;
+            mChoreographer.removeCallbacks(
+                    Choreographer.CALLBACK_INPUT, mBatchedInputRunnable, null);
+        }
+    }
+
+    private final class BatchedInputRunnable implements Runnable {
+        @Override
+        public void run() {
+            doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
+        }
+    }
+    private final BatchedInputRunnable mBatchedInputRunnable = new BatchedInputRunnable();
+}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 9557d121..d1904d8 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -35,12 +35,13 @@
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Slog;
+import android.view.Choreographer;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.InputChannel;
 import android.view.InputDevice;
 import android.view.InputEvent;
-import android.view.InputEventReceiver;
+import android.view.BatchedInputEventReceiver;
 import android.view.MotionEvent;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -103,9 +104,10 @@
     InputApplicationHandle mDragApplicationHandle;
     InputWindowHandle mDragWindowHandle;
 
-    private final class WindowPositionerEventReceiver extends InputEventReceiver {
-        public WindowPositionerEventReceiver(InputChannel inputChannel, Looper looper) {
-            super(inputChannel, looper);
+    private final class WindowPositionerEventReceiver extends BatchedInputEventReceiver {
+        public WindowPositionerEventReceiver(
+                InputChannel inputChannel, Looper looper, Choreographer choreographer) {
+            super(inputChannel, looper, choreographer);
         }
 
         @Override
@@ -222,8 +224,8 @@
         mClientChannel = channels[1];
         mService.mInputManager.registerInputChannel(mServerChannel, null);
 
-        mInputEventReceiver = new WindowPositionerEventReceiver(mClientChannel,
-                mService.mH.getLooper());
+        mInputEventReceiver = new WindowPositionerEventReceiver(
+                mClientChannel, mService.mH.getLooper(), mService.mChoreographer);
 
         mDragApplicationHandle = new InputApplicationHandle(null);
         mDragApplicationHandle.name = TAG;