Loading core/java/android/view/ViewRootImpl.java +10 −1 Original line number Diff line number Diff line Loading @@ -1107,6 +1107,8 @@ public final class ViewRootImpl implements ViewParent, private boolean mIsFrameRateBoosting = false; // Used to check if it is in touch boosting period. private boolean mIsTouchBoosting = false; // Used to track if an ongoing press gesture is occurring. private boolean mIsPressedGesture = false; private boolean mDrawnThisFrame = false; // Used to check if there is a conflict between different frame rate voting. // Take 24 and 30 as an example, 24 is not a divisor of 30. Loading Loading @@ -6912,6 +6914,7 @@ public final class ViewRootImpl implements ViewParent, setPreferredFrameRate(mPreferredFrameRate); setPreferredFrameRateCategory(mPreferredFrameRateCategory); mInvalidationIdleMessagePosted = false; mIsPressedGesture = false; } else { mInvalidationIdleMessagePosted = true; mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, Loading Loading @@ -7925,6 +7928,9 @@ public final class ViewRootImpl implements ViewParent, private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; final int action = event.getAction(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { mIsPressedGesture = false; } boolean handled = false; if (!disableHandwritingInitiatorForIme() || mWindowAttributes.type != TYPE_INPUT_METHOD) { Loading Loading @@ -7955,6 +7961,9 @@ public final class ViewRootImpl implements ViewParent, mWindowAttributes.type)) { // set the frame rate to the maximum value. mIsTouchBoosting = true; if (action == MotionEvent.ACTION_DOWN) { mIsPressedGesture = true; } setPreferredFrameRateCategory(mLastPreferredFrameRateCategory); } /** Loading Loading @@ -13057,7 +13066,7 @@ public final class ViewRootImpl implements ViewParent, if (frameRate <= 0) { return; } if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) { if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && !mIsPressedGesture) { mIsTouchBoosting = false; mIsFrameRateBoosting = false; if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { core/tests/coretests/src/android/view/ViewFrameRateTest.java +81 −0 Original line number Diff line number Diff line Loading @@ -175,6 +175,87 @@ public class ViewFrameRateTest { assertEquals(0f, mViewRoot.getLastPreferredFrameRate(), 0f); } @Test @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) public void highHintWhenActionMove() throws Throwable { if (!ViewProperties.vrr_enabled().orElse(true)) { return; } mActivityRule.runOnUiThread(() -> { mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; mMovingView.setLayoutParams(layoutParams); mMovingView.setOnClickListener((v) -> {}); }); waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> assertEquals(FRAME_RATE_CATEGORY_LOW, mViewRoot.getLastPreferredFrameRateCategory())); int[] position = new int[2]; mActivityRule.runOnUiThread(() -> { mMovingView.getLocationOnScreen(position); position[0] += mMovingView.getWidth() / 2; position[1] += mMovingView.getHeight() / 2; }); final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); long now = SystemClock.uptimeMillis(); MotionEvent down = MotionEvent.obtain( now, // downTime now, // eventTime MotionEvent.ACTION_DOWN, // action position[0], // x position[1], // y 0 // metaState ); down.setSource(InputDevice.SOURCE_TOUCHSCREEN); instrumentation.sendPointerSync(down); now = SystemClock.uptimeMillis(); MotionEvent move = MotionEvent.obtain( now, // downTime now, // eventTime MotionEvent.ACTION_MOVE, // action position[0], // x position[1], // y 0 // metaState ); move.setSource(InputDevice.SOURCE_TOUCHSCREEN); instrumentation.sendPointerSync(move); // We should continue to enable touch boost even when GTE compatibility is present. mActivityRule.runOnUiThread(() -> { mMovingView.offsetLeftAndRight(10); assertTrue(mViewRoot.getIsTouchBoosting()); }); now = SystemClock.uptimeMillis(); MotionEvent up = MotionEvent.obtain( now, // downTime now, // eventTime MotionEvent.ACTION_UP, // action position[0], // x position[1], // y 0 // metaState ); up.setSource(InputDevice.SOURCE_TOUCHSCREEN); instrumentation.sendPointerSync(up); // No touch boost when there is no ongoing pressed gesture. mActivityRule.runOnUiThread(() -> { mMovingView.offsetLeftAndRight(10); assertFalse(mViewRoot.getIsTouchBoosting()); }); down.recycle(); move.recycle(); up.recycle(); } @Test @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) public void frameBoostDisable() throws Throwable { Loading Loading
core/java/android/view/ViewRootImpl.java +10 −1 Original line number Diff line number Diff line Loading @@ -1107,6 +1107,8 @@ public final class ViewRootImpl implements ViewParent, private boolean mIsFrameRateBoosting = false; // Used to check if it is in touch boosting period. private boolean mIsTouchBoosting = false; // Used to track if an ongoing press gesture is occurring. private boolean mIsPressedGesture = false; private boolean mDrawnThisFrame = false; // Used to check if there is a conflict between different frame rate voting. // Take 24 and 30 as an example, 24 is not a divisor of 30. Loading Loading @@ -6912,6 +6914,7 @@ public final class ViewRootImpl implements ViewParent, setPreferredFrameRate(mPreferredFrameRate); setPreferredFrameRateCategory(mPreferredFrameRateCategory); mInvalidationIdleMessagePosted = false; mIsPressedGesture = false; } else { mInvalidationIdleMessagePosted = true; mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, Loading Loading @@ -7925,6 +7928,9 @@ public final class ViewRootImpl implements ViewParent, private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; final int action = event.getAction(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { mIsPressedGesture = false; } boolean handled = false; if (!disableHandwritingInitiatorForIme() || mWindowAttributes.type != TYPE_INPUT_METHOD) { Loading Loading @@ -7955,6 +7961,9 @@ public final class ViewRootImpl implements ViewParent, mWindowAttributes.type)) { // set the frame rate to the maximum value. mIsTouchBoosting = true; if (action == MotionEvent.ACTION_DOWN) { mIsPressedGesture = true; } setPreferredFrameRateCategory(mLastPreferredFrameRateCategory); } /** Loading Loading @@ -13057,7 +13066,7 @@ public final class ViewRootImpl implements ViewParent, if (frameRate <= 0) { return; } if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) { if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE && !mIsPressedGesture) { mIsTouchBoosting = false; mIsFrameRateBoosting = false; if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) {
core/tests/coretests/src/android/view/ViewFrameRateTest.java +81 −0 Original line number Diff line number Diff line Loading @@ -175,6 +175,87 @@ public class ViewFrameRateTest { assertEquals(0f, mViewRoot.getLastPreferredFrameRate(), 0f); } @Test @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) public void highHintWhenActionMove() throws Throwable { if (!ViewProperties.vrr_enabled().orElse(true)) { return; } mActivityRule.runOnUiThread(() -> { mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; mMovingView.setLayoutParams(layoutParams); mMovingView.setOnClickListener((v) -> {}); }); waitForFrameRateCategoryToSettle(); mActivityRule.runOnUiThread(() -> assertEquals(FRAME_RATE_CATEGORY_LOW, mViewRoot.getLastPreferredFrameRateCategory())); int[] position = new int[2]; mActivityRule.runOnUiThread(() -> { mMovingView.getLocationOnScreen(position); position[0] += mMovingView.getWidth() / 2; position[1] += mMovingView.getHeight() / 2; }); final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); long now = SystemClock.uptimeMillis(); MotionEvent down = MotionEvent.obtain( now, // downTime now, // eventTime MotionEvent.ACTION_DOWN, // action position[0], // x position[1], // y 0 // metaState ); down.setSource(InputDevice.SOURCE_TOUCHSCREEN); instrumentation.sendPointerSync(down); now = SystemClock.uptimeMillis(); MotionEvent move = MotionEvent.obtain( now, // downTime now, // eventTime MotionEvent.ACTION_MOVE, // action position[0], // x position[1], // y 0 // metaState ); move.setSource(InputDevice.SOURCE_TOUCHSCREEN); instrumentation.sendPointerSync(move); // We should continue to enable touch boost even when GTE compatibility is present. mActivityRule.runOnUiThread(() -> { mMovingView.offsetLeftAndRight(10); assertTrue(mViewRoot.getIsTouchBoosting()); }); now = SystemClock.uptimeMillis(); MotionEvent up = MotionEvent.obtain( now, // downTime now, // eventTime MotionEvent.ACTION_UP, // action position[0], // x position[1], // y 0 // metaState ); up.setSource(InputDevice.SOURCE_TOUCHSCREEN); instrumentation.sendPointerSync(up); // No touch boost when there is no ongoing pressed gesture. mActivityRule.runOnUiThread(() -> { mMovingView.offsetLeftAndRight(10); assertFalse(mViewRoot.getIsTouchBoosting()); }); down.recycle(); move.recycle(); up.recycle(); } @Test @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) public void frameBoostDisable() throws Throwable { Loading