diff options
| -rw-r--r-- | core/java/android/view/TextureView.java | 4 | ||||
| -rw-r--r-- | core/java/android/view/View.java | 120 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 35 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/view/ViewFrameRateTest.java | 240 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/view/ViewVelocityTest.java | 108 |
5 files changed, 358 insertions, 149 deletions
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index 124aecef1d5a..c66abe8afdfc 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -889,11 +889,11 @@ public class TextureView extends View { * @hide */ @Override - protected int calculateFrameRateCategory(float sizePercentage) { + protected int calculateFrameRateCategory(int width, int height) { if (mMinusTwoFrameIntervalMillis > 15 && mMinusOneFrameIntervalMillis > 15) { return FRAME_RATE_CATEGORY_NORMAL; } - return super.calculateFrameRateCategory(sizePercentage); + return super.calculateFrameRateCategory(width, height); } @UnsupportedAppUsage diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a6f380d4d483..f0bde9705c5a 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2371,6 +2371,39 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET; + /** + * This indicates that the frame rate category was chosen because it was a small area update. + * @hide + */ + public static final int FRAME_RATE_CATEGORY_REASON_SMALL = 0x0100_0000; + + /** + * This indicates that the frame rate category was chosen because it was an intermittent update. + * @hide + */ + public static final int FRAME_RATE_CATEGORY_REASON_INTERMITTENT = 0x0200_0000; + + /** + * This indicates that the frame rate category was chosen because it was a large View. + * @hide + */ + public static final int FRAME_RATE_CATEGORY_REASON_LARGE = 0x03000000; + + /** + * This indicates that the frame rate category was chosen because it was requested. + * @hide + */ + public static final int FRAME_RATE_CATEGORY_REASON_REQUESTED = 0x0400_0000; + + /** + * This indicates that the frame rate category was chosen because an invalid frame rate was + * requested. + * @hide + */ + public static final int FRAME_RATE_CATEGORY_REASON_INVALID = 0x0500_0000; + + private static final int FRAME_RATE_CATEGORY_REASON_MASK = 0xFFFF_0000; + private static boolean sToolkitSetFrameRateReadOnlyFlagValue; private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; // Used to set frame rate compatibility. @@ -5637,10 +5670,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private ViewTranslationResponse mViewTranslationResponse; /** - * A threshold value to determine the frame rate category of the View based on the size. + * Threshold size for something to be considered a small area update (in DP). + * This is the dimension for both width and height. */ - private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f; + private static final float FRAME_RATE_SMALL_SIZE_THRESHOLD = 40f; + /** + * Threshold size for something to be considered a small area update (in DP) if + * it is narrow. This is for either width OR height. For example, a narrow progress + * bar could be considered a small area. + */ + private static final float FRAME_RATE_NARROW_THRESHOLD = 10f; private static final long INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; @@ -33655,18 +33695,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - protected int calculateFrameRateCategory(float sizePercentage) { + protected int calculateFrameRateCategory(int width, int height) { if (mMinusTwoFrameIntervalMillis + mMinusOneFrameIntervalMillis < INFREQUENT_UPDATE_INTERVAL_MILLIS) { - if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) { - return FRAME_RATE_CATEGORY_NORMAL; + DisplayMetrics displayMetrics = mResources.getDisplayMetrics(); + float density = displayMetrics.density; + if (density == 0f) { + density = 1f; + } + float widthDp = width / density; + float heightDp = height / density; + if (widthDp <= FRAME_RATE_NARROW_THRESHOLD + || heightDp <= FRAME_RATE_NARROW_THRESHOLD + || (widthDp <= FRAME_RATE_SMALL_SIZE_THRESHOLD + && heightDp <= FRAME_RATE_SMALL_SIZE_THRESHOLD)) { + return FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_SMALL; } else { - return FRAME_RATE_CATEGORY_HIGH; + return FRAME_RATE_CATEGORY_HIGH | FRAME_RATE_CATEGORY_REASON_LARGE; } } if (mInfrequentUpdateCount == INFREQUENT_UPDATE_COUNTS) { - return FRAME_RATE_CATEGORY_NORMAL; + return FRAME_RATE_CATEGORY_NORMAL | FRAME_RATE_CATEGORY_REASON_INTERMITTENT; } return mLastFrameRateCategory; } @@ -33674,12 +33724,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private void votePreferredFrameRate() { // use toolkitSetFrameRate flag to gate the change ViewRootImpl viewRootImpl = getViewRootImpl(); - float sizePercentage = getSizePercentage(); - int frameRateCateogry = calculateFrameRateCategory(sizePercentage); - if (viewRootImpl != null && sizePercentage > 0) { - if (sToolkitMetricsForFrameRateDecisionFlagValue) { - viewRootImpl.recordViewPercentage(sizePercentage); - } + int width = mRight - mLeft; + int height = mBottom - mTop; + if (viewRootImpl != null && (width != 0 && height != 0)) { if (viewVelocityApi()) { float velocity = mFrameContentVelocity; if (velocity < 0f) { @@ -33691,25 +33738,40 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } } - if (!Float.isNaN(mPreferredFrameRate)) { - if (mPreferredFrameRate < 0) { - if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) { - frameRateCateogry = FRAME_RATE_CATEGORY_NO_PREFERENCE; - } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) { - frameRateCateogry = FRAME_RATE_CATEGORY_LOW; - } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) { - frameRateCateogry = FRAME_RATE_CATEGORY_NORMAL; - } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) { - frameRateCateogry = FRAME_RATE_CATEGORY_HIGH; - } + int frameRateCategory; + if (Float.isNaN(mPreferredFrameRate)) { + frameRateCategory = calculateFrameRateCategory(width, height); + } else if (mPreferredFrameRate < 0) { + if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE) { + frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE + | FRAME_RATE_CATEGORY_REASON_REQUESTED; + } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_LOW) { + frameRateCategory = FRAME_RATE_CATEGORY_LOW + | FRAME_RATE_CATEGORY_REASON_REQUESTED; + } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_NORMAL) { + frameRateCategory = FRAME_RATE_CATEGORY_NORMAL + | FRAME_RATE_CATEGORY_REASON_REQUESTED; + } else if (mPreferredFrameRate == REQUESTED_FRAME_RATE_CATEGORY_HIGH) { + frameRateCategory = FRAME_RATE_CATEGORY_HIGH + | FRAME_RATE_CATEGORY_REASON_REQUESTED; } else { - viewRootImpl.votePreferredFrameRate(mPreferredFrameRate, - mFrameRateCompatibility); - return; + // invalid frame rate, default to HIGH + frameRateCategory = FRAME_RATE_CATEGORY_HIGH + | FRAME_RATE_CATEGORY_REASON_INVALID; } + } else { + viewRootImpl.votePreferredFrameRate(mPreferredFrameRate, + mFrameRateCompatibility); + return; + } + + int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK; + if (sToolkitMetricsForFrameRateDecisionFlagValue) { + int reason = frameRateCategory & FRAME_RATE_CATEGORY_REASON_MASK; + viewRootImpl.recordCategory(category, reason, this); } - viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry); - mLastFrameRateCategory = frameRateCateogry; + viewRootImpl.votePreferredFrameRateCategory(category); + mLastFrameRateCategory = frameRateCategory; } } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 42f64052d987..3bd77bc11e49 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -32,6 +32,11 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL; import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE; import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE; +import static android.view.View.FRAME_RATE_CATEGORY_REASON_INTERMITTENT; +import static android.view.View.FRAME_RATE_CATEGORY_REASON_INVALID; +import static android.view.View.FRAME_RATE_CATEGORY_REASON_LARGE; +import static android.view.View.FRAME_RATE_CATEGORY_REASON_REQUESTED; +import static android.view.View.FRAME_RATE_CATEGORY_REASON_SMALL; import static android.view.View.PFLAG_DRAW_ANIMATION; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; @@ -841,8 +846,9 @@ public final class ViewRootImpl implements ViewParent, private boolean mInsetsAnimationRunning; private long mPreviousFrameDrawnTime = -1; - // The largest view size percentage to the display size. Used on trace to collect metric. - private float mLargestChildPercentage = 0.0f; + // The reason the category was changed. + private int mFrameRateCategoryChangeReason = 0; + private String mFrameRateCategoryView; /** * The resolved pointer icon type requested by this window. @@ -4847,10 +4853,6 @@ public final class ViewRootImpl implements ViewParent, long fps = NANOS_PER_SEC / timeDiff; Trace.setCounter(mFpsTraceName, fps); mPreviousFrameDrawnTime = expectedDrawnTime; - - long percentage = (long) (mLargestChildPercentage * 100); - Trace.setCounter(mLargestViewTraceName, percentage); - mLargestChildPercentage = 0.0f; } private void reportDrawFinished(@Nullable Transaction t, int seqId) { @@ -12367,9 +12369,20 @@ public final class ViewRootImpl implements ViewParent, try { if (mLastPreferredFrameRateCategory != frameRateCategory) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + String reason = "none"; + switch (mFrameRateCategoryChangeReason) { + case FRAME_RATE_CATEGORY_REASON_INTERMITTENT -> reason = "intermittent"; + case FRAME_RATE_CATEGORY_REASON_SMALL -> reason = "small"; + case FRAME_RATE_CATEGORY_REASON_LARGE -> reason = "large"; + case FRAME_RATE_CATEGORY_REASON_REQUESTED -> reason = "requested"; + case FRAME_RATE_CATEGORY_REASON_INVALID -> reason = "invalid frame rate"; + } + String sourceView = mFrameRateCategoryView == null ? "No View Given" + : mFrameRateCategoryView; Trace.traceBegin( Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRateCategory " - + frameRateCategory); + + frameRateCategory + ", reason " + reason + ", " + + sourceView); } mFrameRateTransaction.setFrameRateCategory(mSurfaceControl, frameRateCategory, false).applyAsyncUnsafe(); @@ -12592,10 +12605,12 @@ public final class ViewRootImpl implements ViewParent, mWindowlessBackKeyCallback = callback; } - void recordViewPercentage(float percentage) { + void recordCategory(int category, int reason, View view) { if (!Trace.isEnabled()) return; - // Record the largest view of percentage to the display size. - mLargestChildPercentage = Math.max(percentage, mLargestChildPercentage); + if (category > mPreferredFrameRateCategory) { + mFrameRateCategoryChangeReason = reason; + mFrameRateCategoryView = view.getClass().getSimpleName(); + } } /** diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java new file mode 100644 index 000000000000..90a8c5c57fc2 --- /dev/null +++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2024 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 static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API; + +import static junit.framework.Assert.assertEquals; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.os.SystemClock; +import android.platform.test.annotations.RequiresFlagsEnabled; + +import androidx.test.annotation.UiThreadTest; +import androidx.test.filters.SmallTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + +import com.android.frameworks.coretests.R; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ViewFrameRateTest { + + @Rule + public ActivityTestRule<ViewCaptureTestActivity> mActivityRule = new ActivityTestRule<>( + ViewCaptureTestActivity.class); + + private Activity mActivity; + private View mMovingView; + private ViewRootImpl mViewRoot; + + @Before + public void setUp() throws Throwable { + mActivity = mActivityRule.getActivity(); + mActivityRule.runOnUiThread(() -> { + mActivity.setContentView(R.layout.view_velocity_test); + mMovingView = mActivity.findViewById(R.id.moving_view); + }); + ViewParent parent = mActivity.getWindow().getDecorView().getParent(); + while (parent instanceof View) { + parent = parent.getParent(); + } + mViewRoot = (ViewRootImpl) parent; + } + + @UiThreadTest + @Test + @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) + public void frameRateChangesWhenContentMoves() { + mMovingView.offsetLeftAndRight(100); + float frameRate = mViewRoot.getPreferredFrameRate(); + assertTrue(frameRate > 0); + } + + @UiThreadTest + @Test + @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) + public void firstFrameNoMovement() { + assertEquals(0f, mViewRoot.getPreferredFrameRate(), 0f); + } + + @Test + @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) + public void touchBoostDisable() throws Throwable { + mActivityRule.runOnUiThread(() -> { + long now = SystemClock.uptimeMillis(); + MotionEvent down = MotionEvent.obtain( + /* downTime */ now, + /* eventTime */ now, + /* action */ MotionEvent.ACTION_DOWN, + /* x */ 0f, + /* y */ 0f, + /* metaState */ 0 + ); + mActivity.dispatchTouchEvent(down); + mMovingView.offsetLeftAndRight(10); + }); + mActivityRule.runOnUiThread(() -> { + mMovingView.invalidate(); + }); + + mActivityRule.runOnUiThread(() -> { + assertFalse(mViewRoot.getIsTouchBoosting()); + }); + } + + private void waitForFrameRateCategoryToSettle() throws Throwable { + for (int i = 0; i < 5; i++) { + final CountDownLatch drawLatch = new CountDownLatch(1); + + // Now that it is small, any invalidation should have a normal category + mActivityRule.runOnUiThread(() -> { + mMovingView.invalidate(); + mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch::countDown); + }); + + assertTrue(drawLatch.await(1, TimeUnit.SECONDS)); + } + } + + @Test + public void noVelocityUsesCategorySmall() throws Throwable { + final CountDownLatch drawLatch1 = new CountDownLatch(1); + mActivityRule.runOnUiThread(() -> { + float density = mActivity.getResources().getDisplayMetrics().density; + ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); + layoutParams.height = (int) (40 * density); + layoutParams.width = (int) (40 * density); + mMovingView.setLayoutParams(layoutParams); + mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown); + }); + + assertTrue(drawLatch1.await(1, TimeUnit.SECONDS)); + waitForFrameRateCategoryToSettle(); + + // Now that it is small, any invalidation should have a normal category + mActivityRule.runOnUiThread(() -> { + mMovingView.invalidate(); + assertEquals(Surface.FRAME_RATE_CATEGORY_NORMAL, + mViewRoot.getPreferredFrameRateCategory()); + }); + } + + @Test + public void noVelocityUsesCategoryNarrowWidth() throws Throwable { + final CountDownLatch drawLatch1 = new CountDownLatch(1); + mActivityRule.runOnUiThread(() -> { + float density = mActivity.getResources().getDisplayMetrics().density; + ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; + layoutParams.width = (int) (10 * density); + mMovingView.setLayoutParams(layoutParams); + mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown); + }); + + assertTrue(drawLatch1.await(1, TimeUnit.SECONDS)); + waitForFrameRateCategoryToSettle(); + + // Now that it is small, any invalidation should have a normal category + mActivityRule.runOnUiThread(() -> { + mMovingView.invalidate(); + assertEquals(Surface.FRAME_RATE_CATEGORY_NORMAL, + mViewRoot.getPreferredFrameRateCategory()); + }); + } + + @Test + public void noVelocityUsesCategoryNarrowHeight() throws Throwable { + final CountDownLatch drawLatch1 = new CountDownLatch(1); + mActivityRule.runOnUiThread(() -> { + float density = mActivity.getResources().getDisplayMetrics().density; + ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); + layoutParams.height = (int) (10 * density); + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + mMovingView.setLayoutParams(layoutParams); + mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown); + }); + + assertTrue(drawLatch1.await(1, TimeUnit.SECONDS)); + waitForFrameRateCategoryToSettle(); + + // Now that it is small, any invalidation should have a normal category + mActivityRule.runOnUiThread(() -> { + mMovingView.invalidate(); + assertEquals(Surface.FRAME_RATE_CATEGORY_NORMAL, + mViewRoot.getPreferredFrameRateCategory()); + }); + } + + @Test + public void noVelocityUsesCategoryLargeWidth() throws Throwable { + final CountDownLatch drawLatch1 = new CountDownLatch(1); + mActivityRule.runOnUiThread(() -> { + float density = mActivity.getResources().getDisplayMetrics().density; + ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); + layoutParams.height = (int) (40 * density); + layoutParams.width = (int) Math.ceil(41 * density); + mMovingView.setLayoutParams(layoutParams); + mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown); + }); + + assertTrue(drawLatch1.await(1, TimeUnit.SECONDS)); + waitForFrameRateCategoryToSettle(); + + // Now that it is small, any invalidation should have a high category + mActivityRule.runOnUiThread(() -> { + mMovingView.invalidate(); + assertEquals(Surface.FRAME_RATE_CATEGORY_HIGH, + mViewRoot.getPreferredFrameRateCategory()); + }); + } + + @Test + public void noVelocityUsesCategoryLargeHeight() throws Throwable { + final CountDownLatch drawLatch1 = new CountDownLatch(1); + mActivityRule.runOnUiThread(() -> { + float density = mActivity.getResources().getDisplayMetrics().density; + ViewGroup.LayoutParams layoutParams = mMovingView.getLayoutParams(); + layoutParams.height = (int) Math.ceil(41 * density); + layoutParams.width = (int) (40 * density); + mMovingView.setLayoutParams(layoutParams); + mMovingView.getViewTreeObserver().addOnDrawListener(drawLatch1::countDown); + }); + + assertTrue(drawLatch1.await(1, TimeUnit.SECONDS)); + waitForFrameRateCategoryToSettle(); + + // Now that it is small, any invalidation should have a high category + mActivityRule.runOnUiThread(() -> { + mMovingView.invalidate(); + assertEquals(Surface.FRAME_RATE_CATEGORY_HIGH, + mViewRoot.getPreferredFrameRateCategory()); + }); + } +} diff --git a/core/tests/coretests/src/android/view/ViewVelocityTest.java b/core/tests/coretests/src/android/view/ViewVelocityTest.java deleted file mode 100644 index d437f7bc4060..000000000000 --- a/core/tests/coretests/src/android/view/ViewVelocityTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2024 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 static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API; - -import static junit.framework.Assert.assertEquals; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.app.Activity; -import android.os.SystemClock; -import android.platform.test.annotations.RequiresFlagsEnabled; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.filters.SmallTest; -import androidx.test.rule.ActivityTestRule; -import androidx.test.runner.AndroidJUnit4; - -import com.android.frameworks.coretests.R; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class ViewVelocityTest { - - @Rule - public ActivityTestRule<ViewCaptureTestActivity> mActivityRule = new ActivityTestRule<>( - ViewCaptureTestActivity.class); - - private Activity mActivity; - private View mMovingView; - private ViewRootImpl mViewRoot; - - @Before - public void setUp() throws Throwable { - mActivity = mActivityRule.getActivity(); - mActivityRule.runOnUiThread(() -> { - mActivity.setContentView(R.layout.view_velocity_test); - mMovingView = mActivity.findViewById(R.id.moving_view); - }); - ViewParent parent = mActivity.getWindow().getDecorView().getParent(); - while (parent instanceof View) { - parent = parent.getParent(); - } - mViewRoot = (ViewRootImpl) parent; - } - - @UiThreadTest - @Test - @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) - public void frameRateChangesWhenContentMoves() { - mMovingView.offsetLeftAndRight(100); - float frameRate = mViewRoot.getPreferredFrameRate(); - assertTrue(frameRate > 0); - } - - @UiThreadTest - @Test - @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) - public void firstFrameNoMovement() { - assertEquals(0f, mViewRoot.getPreferredFrameRate(), 0f); - } - - @Test - @RequiresFlagsEnabled(FLAG_VIEW_VELOCITY_API) - public void touchBoostDisable() throws Throwable { - mActivityRule.runOnUiThread(() -> { - long now = SystemClock.uptimeMillis(); - MotionEvent down = MotionEvent.obtain( - /* downTime */ now, - /* eventTime */ now, - /* action */ MotionEvent.ACTION_DOWN, - /* x */ 0f, - /* y */ 0f, - /* metaState */ 0 - ); - mActivity.dispatchTouchEvent(down); - mMovingView.offsetLeftAndRight(10); - }); - mActivityRule.runOnUiThread(() -> { - mMovingView.invalidate(); - }); - - mActivityRule.runOnUiThread(() -> { - assertFalse(mViewRoot.getIsTouchBoosting()); - }); - } -} |