Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 347f7591 authored by Shane's avatar Shane
Browse files

Add setRequestedFrameRate and getRequestedFrameRate APIs to View

This CL add public APIs setRequestedFrameRate and getRequestedFrameRate
to View. setRequestedFrameRate allows developers to set a preferred
frame rate or preferred frame rate category to a View.

Developer can pass a positive integer to setRequestedFrameRate to set
preferred frame rate or pass a constant variable (e.g.,
REQUESTED_FRAME_RATE_CATEGORY_LOW or REQUESTED_FRAME_RATE_CATEGORY_HIGH)
toe set preferred frame rate category.

Bug: 278731444
Test: atest ViewRootImplTest / ViewTest
Change-Id: Ica0ab97e96bd104e73dfe95bbd4917bf24e8f7cb
parent e91c7507
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -51916,6 +51916,7 @@ package android.view {
    method public android.view.PointerIcon getPointerIcon();
    method @NonNull public final java.util.List<android.graphics.Rect> getPreferKeepClearRects();
    method @Nullable public String[] getReceiveContentMimeTypes();
    method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public float getRequestedFrameRate();
    method public android.content.res.Resources getResources();
    method public final boolean getRevealOnFocusHint();
    method public final int getRight();
@@ -52292,6 +52293,7 @@ package android.view {
    method public final void setPreferKeepClearRects(@NonNull java.util.List<android.graphics.Rect>);
    method public void setPressed(boolean);
    method public void setRenderEffect(@Nullable android.graphics.RenderEffect);
    method @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public void setRequestedFrameRate(float);
    method public final void setRevealOnFocusHint(boolean);
    method public final void setRight(int);
    method public void setRotation(float);
@@ -52474,6 +52476,11 @@ package android.view {
    field protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
    field protected static final int[] PRESSED_STATE_SET;
    field protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
    field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0.0f;
    field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -120.0f;
    field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -30.0f;
    field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -60.0f;
    field @FlaggedApi("android.view.flags.toolkit_set_frame_rate_read_only") public static final float REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE = -1.0f;
    field public static final android.util.Property<android.view.View,java.lang.Float> ROTATION;
    field public static final android.util.Property<android.view.View,java.lang.Float> ROTATION_X;
    field public static final android.util.Property<android.view.View,java.lang.Float> ROTATION_Y;
+74 −5
Original line number Diff line number Diff line
@@ -19,8 +19,10 @@ package android.view;
import static android.content.res.Resources.ID_NULL;
import static android.os.Trace.TRACE_TAG_APP;
import static android.view.ContentInfo.SOURCE_DRAG_AND_DROP;
import static android.view.Surface.FRAME_RATE_CATEGORY_HIGH;
import static android.view.Surface.FRAME_RATE_CATEGORY_LOW;
import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL;
import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_INVALID_BOUNDS;
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_MISSING_WINDOW;
@@ -28,6 +30,7 @@ import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ER
import static android.view.displayhash.DisplayHashResultCallback.DISPLAY_HASH_ERROR_UNKNOWN;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH;
import static android.view.displayhash.DisplayHashResultCallback.EXTRA_DISPLAY_HASH_ERROR_CODE;
import static android.view.flags.Flags.FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY;
import static android.view.flags.Flags.FLAG_VIEW_VELOCITY_API;
import static android.view.flags.Flags.toolkitSetFrameRateReadOnly;
import static android.view.flags.Flags.viewVelocityApi;
@@ -5521,6 +5524,21 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
     */
    private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f;
    // The preferred frame rate of the view that is mainly used for
    // touch boosting, view velocity handling, and TextureView.
    private float mPreferredFrameRate = REQUESTED_FRAME_RATE_CATEGORY_DEFAULT;
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public static final float REQUESTED_FRAME_RATE_CATEGORY_DEFAULT = 0;
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public static final float REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE = -1;
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public static final float REQUESTED_FRAME_RATE_CATEGORY_LOW = -30;
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public static final float REQUESTED_FRAME_RATE_CATEGORY_NORMAL = -60;
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public static final float REQUESTED_FRAME_RATE_CATEGORY_HIGH = -120;
    /**
     * Simple constructor to use when creating a view from code.
     *
@@ -33015,9 +33033,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        return (float) viewSize / screenSize;
    }
    private int calculateFrameRateCategory() {
        float sizePercentage = getSizePercentage();
    private int calculateFrameRateCategory(float sizePercentage) {
        if (sizePercentage <= FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD) {
            return FRAME_RATE_CATEGORY_LOW;
        } else {
@@ -33028,9 +33044,24 @@ 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 (sToolkitSetFrameRateReadOnlyFlagValue && viewRootImpl != null
                && getSizePercentage() > 0) {
            viewRootImpl.votePreferredFrameRateCategory(calculateFrameRateCategory());
                && sizePercentage > 0) {
            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;
                }
            } else {
                viewRootImpl.votePreferredFrameRate(mPreferredFrameRate);
            }
            viewRootImpl.votePreferredFrameRateCategory(frameRateCateogry);
        }
    }
@@ -33064,4 +33095,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
        }
        return 0;
    }
    /**
     * You can set the preferred frame rate for a View using a positive number
     * or by specifying the preferred frame rate category using constants, including
     * REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
     * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, REQUESTED_FRAME_RATE_CATEGORY_HIGH.
     * Keep in mind that the preferred frame rate affects the frame rate for the next frame,
     * so use this method carefully. It's important to note that the preference is valid as
     * long as the View is invalidated.
     *
     * @param frameRate the preferred frame rate of the view.
     */
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void setRequestedFrameRate(float frameRate) {
        if (sToolkitSetFrameRateReadOnlyFlagValue) {
            mPreferredFrameRate = frameRate;
        }
    }
    /**
     * Get the current preferred frame rate of the View.
     * The value could be negative when preferred frame rate category is set
     * instead of perferred frame rate.
     * The frame rate category includes
     * REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE, REQUESTED_FRAME_RATE_CATEGORY_LOW,
     * REQUESTED_FRAME_RATE_CATEGORY_NORMAL, and REQUESTED_FRAME_RATE_CATEGORY_HIGH.
     * Note that the frame rate value is valid as long as the View is invalidated.
     *
     * @return REQUESTED_FRAME_RATE_CATEGORY_DEFAULT by default,
     * or value passed to {@link #setRequestedFrameRate(float)}.
     */
    @FlaggedApi(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public float getRequestedFrameRate() {
        if (sToolkitSetFrameRateReadOnlyFlagValue) {
            return mPreferredFrameRate;
        }
        return 0;
    }
}
+39 −1
Original line number Diff line number Diff line
@@ -981,6 +981,9 @@ public final class ViewRootImpl implements ViewParent,
    // The preferred frame rate of the view that is mainly used for
    // touch boosting, view velocity handling, and TextureView.
    private float mPreferredFrameRate = 0;
    // The last preferred frame rate of the view that is mainly used to
    // track the difference between the current preferred frame rate and the previous value.
    private float mLastPreferredFrameRate = 0;
    // Used to check if there were any view invalidations in
    // the previous time frame (FRAME_RATE_IDLENESS_REEVALUATE_TIME).
    private boolean mHasInvalidation = false;
@@ -3990,10 +3993,14 @@ public final class ViewRootImpl implements ViewParent,
        }

        // For the variable refresh rate project.
        // We set the preferred frame rate and frame rate category at the end of performTraversals
        // when the values are applicable.
        setPreferredFrameRate(mPreferredFrameRate);
        setPreferredFrameRateCategory(mPreferredFrameRateCategory);
        mLastPreferredFrameRateCategory = mPreferredFrameRateCategory;
        mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
        mLastPreferredFrameRate = mPreferredFrameRate;
        mPreferredFrameRate = 0;
    }

    private void createSyncIfNeeded() {
@@ -7369,7 +7376,9 @@ public final class ViewRootImpl implements ViewParent,
             */
            if (mIsFrameRateBoosting && (action == MotionEvent.ACTION_UP
                    || action == MotionEvent.ACTION_CANCEL)) {
                sendDelayedEmptyMessage(MSG_TOUCH_BOOST_TIMEOUT, FRAME_RATE_TOUCH_BOOST_TIME);
                mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
                mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT,
                        FRAME_RATE_TOUCH_BOOST_TIME);
            }
            return handled ? FINISH_HANDLED : FORWARD;
        }
@@ -12060,6 +12069,35 @@ public final class ViewRootImpl implements ViewParent,
        mHasInvalidation = true;
    }

    /**
     * Allow Views to vote for the preferred frame rate
     * When determining the preferred frame rate value,
     * we follow this logic: If no preferred frame rate has been set yet,
     * we assign the value of frameRate as the preferred frame rate.
     * If either the current or the new preferred frame rate exceeds 60 Hz,
     * we select the higher value between them.
     * However, if both values are 60 Hz or lower, we set the preferred frame rate
     * to 60 Hz to maintain optimal performance.
     *
     * @param frameRate the preferred frame rate of a View
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED)
    public void votePreferredFrameRate(float frameRate) {
        if (frameRate <= 0) {
            return;
        }

        if (mPreferredFrameRate == 0) {
            mPreferredFrameRate = frameRate;
        } else if (frameRate > 60 || mPreferredFrameRate > 60) {
            mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate);
        } else if (mPreferredFrameRate != frameRate) {
            mPreferredFrameRate = 60;
        }

        mHasInvalidation = true;
    }

    /**
     * Get the value of mPreferredFrameRateCategory
     */
+52 −0
Original line number Diff line number Diff line
@@ -576,6 +576,58 @@ public class ViewRootImplTest {
        });
    }

    /**
     * Test the accurate aggregation of frame rate values as follows:
     * 1. When values exceed 60Hz, select the maximum value.
     * 2. If frame rates are less than 60Hz and multiple frame rates are voted,
     * prioritize 60Hz..
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRate_aggregate() {
        View view = new View(sContext);
        attachViewToWindow(view);
        sInstrumentation.runOnMainSync(() -> {
            ViewRootImpl viewRootImpl = view.getViewRootImpl();
            assertEquals(viewRootImpl.getPreferredFrameRate(), 0, 0.1);
            viewRootImpl.votePreferredFrameRate(24);
            assertEquals(viewRootImpl.getPreferredFrameRate(), 24, 0.1);
            viewRootImpl.votePreferredFrameRate(30);
            assertEquals(viewRootImpl.getPreferredFrameRate(), 60, 0.1);
            viewRootImpl.votePreferredFrameRate(120);
            assertEquals(viewRootImpl.getPreferredFrameRate(), 120, 0.1);
        });
    }

    /**
     * Override the frame rate category value with setRequestedFrameRate method.
     * This function can replace the existing frameRateCategory value and
     * submit your preferred choice to the ViewRootImpl.
     */
    @Test
    @RequiresFlagsEnabled(FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY)
    public void votePreferredFrameRate_voteFrameRate_category() {
        View view = new View(sContext);
        attachViewToWindow(view);
        sInstrumentation.waitForIdleSync();

        ViewRootImpl viewRootImpl = view.getViewRootImpl();
        sInstrumentation.runOnMainSync(() -> {
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(),
                    FRAME_RATE_CATEGORY_NO_PREFERENCE);
            view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_LOW);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_LOW);
            view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_NORMAL);
            view.setRequestedFrameRate(view.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
            view.invalidate();
            assertEquals(viewRootImpl.getPreferredFrameRateCategory(), FRAME_RATE_CATEGORY_HIGH);
        });
    }


    @Test
    public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
        mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);