summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--core/java/android/view/InputDevice.java16
-rw-r--r--core/java/android/view/View.java41
-rw-r--r--core/java/android/view/ViewGroup.java26
-rw-r--r--core/java/android/view/ViewParent.java14
-rw-r--r--core/java/android/view/ViewRootImpl.java30
6 files changed, 125 insertions, 3 deletions
diff --git a/api/current.txt b/api/current.txt
index f08ddd063b95..a0b38cd52ced 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -53892,6 +53892,7 @@ package android.view {
method public boolean requestRectangleOnScreen(android.graphics.Rect);
method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
method public final void requestUnbufferedDispatch(android.view.MotionEvent);
+ method public final void requestUnbufferedDispatch(int);
method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
method public void resetPivot();
method public static int resolveSize(int, int);
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 360deddf544d..58e5b2dfaa37 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.IntDef;
import android.annotation.RequiresPermission;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -28,6 +29,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.Vibrator;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -138,6 +141,19 @@ public final class InputDevice implements Parcelable {
*/
public static final int SOURCE_CLASS_JOYSTICK = 0x00000010;
+ /** @hide */
+ @IntDef(flag = true, prefix = { "SOURCE_CLASS_" }, value = {
+ SOURCE_CLASS_NONE,
+ SOURCE_CLASS_BUTTON,
+ SOURCE_CLASS_POINTER,
+ SOURCE_CLASS_POINTER,
+ SOURCE_CLASS_TRACKBALL,
+ SOURCE_CLASS_POSITION,
+ SOURCE_CLASS_JOYSTICK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface InputSourceClass {}
+
/**
* The input source is unknown.
*/
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b9be33ca0f59..4c7307ee5b8c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -110,6 +110,7 @@ import android.view.AccessibilityIterators.ParagraphTextSegmentIterator;
import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.AccessibilityIterators.WordTextSegmentIterator;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.InputDevice.InputSourceClass;
import android.view.Window.OnContentApplyWindowInsetsListener;
import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation.Bounds;
@@ -5205,6 +5206,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private int mExplicitStyle;
/**
+ * Specifies which input source classes should provide unbuffered input events to this view
+ *
+ * @see View#requestUnbufferedDispatch(int)
+ */
+ @InputSourceClass
+ int mUnbufferedInputSource = InputDevice.SOURCE_CLASS_NONE;
+
+ /**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
@@ -8013,6 +8022,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mAttachInfo.mKeyDispatchState.reset(this);
}
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+
notifyEnterOrExitForAutoFillIfNeeded(gainFocus);
}
@@ -15820,12 +15833,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* system not batch {@link MotionEvent}s but instead deliver them as soon as they're
* available. This method should only be called for touch events.
*
- * <p class="note">This api is not intended for most applications. Buffered dispatch
+ * <p class="note">This API is not intended for most applications. Buffered dispatch
* provides many of benefits, and just requesting unbuffered dispatch on most MotionEvent
* streams will not improve your input latency. Side effects include: increased latency,
* jittery scrolls and inability to take advantage of system resampling. Talk to your input
* professional to see if {@link #requestUnbufferedDispatch(MotionEvent)} is right for
* you.</p>
+ *
+ * To receive unbuffered events for arbitrary input device source classes, use
+ * {@link #requestUnbufferedDispatch(int)},
+ *
+ * @see View#requestUnbufferedDispatch(int)
*/
public final void requestUnbufferedDispatch(MotionEvent event) {
final int action = event.getAction();
@@ -15837,6 +15855,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mAttachInfo.mUnbufferedDispatchRequested = true;
}
+ /**
+ * Request unbuffered dispatch of the given event source class to this view.
+ * This is similar to {@link View#requestUnbufferedDispatch(MotionEvent)}, but does not
+ * automatically terminate, and allows the specification of arbitrary input source classes.
+ *
+ * @param source The combined input source class to request unbuffered dispatch for. All
+ * events coming from these source classes will not be buffered. Set to
+ * {@link InputDevice#SOURCE_CLASS_NONE} in order to return to default behaviour.
+ *
+ * @see View#requestUnbufferedDispatch(MotionEvent)
+ */
+ public final void requestUnbufferedDispatch(@InputSourceClass int source) {
+ if (mUnbufferedInputSource == source) {
+ return;
+ }
+ mUnbufferedInputSource = source;
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+ }
+
private boolean hasSize() {
return (mBottom > mTop) && (mRight > mLeft);
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 56f933c8a221..b6c46be66761 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -9029,4 +9029,30 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
getChildAt(i).encode(encoder);
}
}
+
+ /** @hide */
+ @Override
+ public final void onDescendantUnbufferedRequested() {
+ // First look at the focused child for focused events
+ int focusedChildNonPointerSource = InputDevice.SOURCE_CLASS_NONE;
+ if (mFocused != null) {
+ focusedChildNonPointerSource = mFocused.mUnbufferedInputSource
+ & (~InputDevice.SOURCE_CLASS_POINTER);
+ }
+ mUnbufferedInputSource = focusedChildNonPointerSource;
+
+ // Request unbuffered dispatch for pointer events for this view if any child requested
+ // unbuffered dispatch for pointer events. This is because we can't expect that the pointer
+ // source would dispatch to the focused view.
+ for (int i = 0; i < mChildrenCount; i++) {
+ final View child = mChildren[i];
+ if ((child.mUnbufferedInputSource & InputDevice.SOURCE_CLASS_POINTER) != 0) {
+ mUnbufferedInputSource |= InputDevice.SOURCE_CLASS_POINTER;
+ break;
+ }
+ }
+ if (mParent != null) {
+ mParent.onDescendantUnbufferedRequested();
+ }
+ }
}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index f25206dce8ff..775c15e77d5d 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -675,4 +675,18 @@ public interface ViewParent {
*/
default void subtractObscuredTouchableRegion(Region touchableRegion, View view) {
}
+
+ /**
+ * Unbuffered dispatch has been requested by a child of this view parent.
+ * This method is called by the View hierarchy to signal ancestors that a View needs to
+ * request unbuffered dispatch.
+ *
+ * @see View#requestUnbufferedDispatch(int)
+ * @hide
+ */
+ default void onDescendantUnbufferedRequested() {
+ if (getParent() != null) {
+ getParent().onDescendantUnbufferedRequested();
+ }
+ }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 043e5be8a050..204d2c8bc2ce 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -18,6 +18,7 @@ package android.view;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
+import static android.view.InputDevice.SOURCE_CLASS_NONE;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_STATUS_BAR;
import static android.view.View.PFLAG_DRAW_ANIMATION;
@@ -116,6 +117,7 @@ import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.TypedValue;
+import android.view.InputDevice.InputSourceClass;
import android.view.InsetsState.InternalInsetsType;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl.Transaction;
@@ -499,6 +501,10 @@ public final class ViewRootImpl implements ViewParent,
int mPendingInputEventCount;
boolean mProcessInputEventsScheduled;
boolean mUnbufferedInputDispatch;
+ boolean mUnbufferedInputDispatchBySource;
+ @InputSourceClass
+ int mUnbufferedInputSource = SOURCE_CLASS_NONE;
+
String mPendingInputEventQueueLengthCounterName = "pq";
InputStage mFirstInputStage;
@@ -1849,7 +1855,7 @@ public final class ViewRootImpl implements ViewParent,
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
- if (!mUnbufferedInputDispatch) {
+ if (!mUnbufferedInputDispatch && !mUnbufferedInputDispatchBySource) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
@@ -7505,6 +7511,9 @@ public final class ViewRootImpl implements ViewParent,
writer.print(mTraversalScheduled);
writer.print(innerPrefix); writer.print("mIsAmbientMode=");
writer.print(mIsAmbientMode);
+ writer.print(innerPrefix); writer.print("mUnbufferedInputSource=");
+ writer.print(Integer.toHexString(mUnbufferedInputSource));
+
if (mTraversalScheduled) {
writer.print(" (barrier="); writer.print(mTraversalBarrier); writer.println(")");
} else {
@@ -8109,6 +8118,7 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void onInputEvent(InputEvent event) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
+ processUnbufferedRequest(event);
List<InputEvent> processedEvents;
try {
processedEvents =
@@ -8134,7 +8144,7 @@ public final class ViewRootImpl implements ViewParent,
@Override
public void onBatchedInputEventPending() {
- if (mUnbufferedInputDispatch) {
+ if (mUnbufferedInputDispatch || mUnbufferedInputDispatchBySource) {
super.onBatchedInputEventPending();
} else {
scheduleConsumeBatchedInput();
@@ -8151,6 +8161,17 @@ public final class ViewRootImpl implements ViewParent,
unscheduleConsumeBatchedInput();
super.dispose();
}
+
+ private void processUnbufferedRequest(InputEvent event) {
+ if (!(event instanceof MotionEvent)) {
+ return;
+ }
+ mUnbufferedInputDispatchBySource =
+ (event.getSource() & mUnbufferedInputSource) != SOURCE_CLASS_NONE;
+ if (mUnbufferedInputDispatchBySource && mConsumeBatchedInputScheduled) {
+ scheduleConsumeBatchedInputImmediately();
+ }
+ }
}
WindowInputEventReceiver mInputEventReceiver;
@@ -9601,4 +9622,9 @@ public final class ViewRootImpl implements ViewParent,
return mSurfaceControl;
}
}
+
+ @Override
+ public void onDescendantUnbufferedRequested() {
+ mUnbufferedInputSource = mView.mUnbufferedInputSource;
+ }
}