diff options
| -rw-r--r-- | api/current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/view/InputDevice.java | 16 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 41 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 26 | ||||
| -rw-r--r-- | core/java/android/view/ViewParent.java | 14 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 30 |
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; + } } |