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

Commit 36a2f0a1 authored by Jian-Syuan (Shane) Wong's avatar Jian-Syuan (Shane) Wong Committed by Android (Google) Code Review
Browse files

Merge "Add setRequestedFrameRate and getRequestedFrameRate APIs to View" into main

parents a41c20a7 347f7591
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -51977,6 +51977,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();
@@ -52353,6 +52354,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);
@@ -52535,6 +52537,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
@@ -982,6 +982,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;
@@ -3991,10 +3994,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() {
@@ -7370,7 +7377,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;
        }
@@ -12061,6 +12070,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
@@ -577,6 +577,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);