Merge "Allow unbuffered MotionEvent dispatch for View (1/2)"

This commit is contained in:
TreeHugger Robot
2020-02-19 18:24:52 +00:00
committed by Android (Google) Code Review
6 changed files with 125 additions and 3 deletions

View File

@@ -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);

View File

@@ -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.
*/

View File

@@ -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;
@@ -5204,6 +5205,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@StyleRes
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.
*
@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}