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

Commit a5fdefdb authored by Shivangi Dubey's avatar Shivangi Dubey
Browse files

Separate setRotationAtAngle and Rotation Suggestion Button Flows

This commit separates the setRotationAtAngle and rotation suggestion button click flows, which were unintentionally merged.

When enable_device_state_auto_rotate_setting_refactor is enabled, the isRotationChoiceAllowed check (intended exclusively for explicit user clicks on the rotation suggestion button) was incorrectly triggered by calls to RotationButtonController#setRotationAtAngle.

The setRotationAtAngle method is called by the system (e.g., home screen to enforce ROTATION_0 device rotation value) to change the device's rotation. This unintended check prevented rotation changes when they were necessary.

This change ensures that isRotationChoiceAllowed is evaluated only when a user explicitly clicks the rotation suggestion button, allowing setRotationAtAngle to function as intended without unnecessary checks.

Fixes: 430756509
Flag: EXEMPT BUGFIX
Test: atest RotationButtonControllerTest
Test: Flash device with this change -> click rotation suggestion -> go to home screen -> change device state and check if rotation persists after opening an app

Change-Id: I924bdbd96fc40a4c7116d2bf6ee7ec6de17996e0
parent aa7929f6
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import androidx.test.runner.AndroidJUnit4;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.rotation.RotationPolicyWrapper;
import com.android.systemui.shared.rotation.RotationButton;
import com.android.systemui.shared.rotation.RotationButtonController;
import com.android.systemui.statusbar.policy.RotationLockController;
@@ -40,7 +41,6 @@ import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;

import java.util.function.Supplier;

@@ -60,12 +60,12 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mDependency.injectMockDependency(RotationLockController.class);

        final View view = new View(mContext);
        mRotationButton = mock(RotationButton.class);
        mRotationButtonController = new RotationButtonController(mContext,
        mRotationButtonController = new RotationButtonController(mock(RotationPolicyWrapper.class),
                mContext,
                /* lightIconColor */ 0,
                /* darkIconColor */ 0,
                /* iconCcwStart0 */ 0,
+4 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ import com.android.systemui.navigationbar.views.buttons.NavBarButtonClickLogger;
import com.android.systemui.navigationbar.views.buttons.NavbarOrientationTrackingLogger;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.Recents;
import com.android.systemui.rotation.RotationPolicyWrapper;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserContextProvider;
@@ -196,6 +197,8 @@ public class NavigationBarTest extends SysuiTestCase {
    private SysUiState mMockSysUiState;
    @Mock
    private Handler mHandler;
    @Mock
    private RotationPolicyWrapper mMockRotationPolicyWrapper;

    @Mock
    private Handler mBgHandler;
@@ -687,6 +690,7 @@ public class NavigationBarTest extends SysuiTestCase {
                mock(PanelExpansionInteractor.class),
                mock(NotificationRemoteInputManager.class),
                mock(NotificationShadeDepthController.class),
                mMockRotationPolicyWrapper,
                mHandler,
                mFakeExecutor,
                mFakeExecutor,
+95 −28
Original line number Diff line number Diff line
@@ -15,43 +15,74 @@
 */
package com.android.systemui.shared.rotation

import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.SetFlagsRule
import android.testing.TestableLooper.RunWithLooper
import android.view.Display
import android.view.Surface
import android.view.View
import android.view.WindowInsetsController
import android.view.WindowManagerPolicyConstants
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.rotation.RotationPolicyWrapper
import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback
import com.android.window.flags.Flags
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify

@RunWith(AndroidJUnit4::class)
@SmallTest
@RunWithLooper
class RotationButtonControllerTest : SysuiTestCase() {
    @get:Rule
    val setFlagsRule: SetFlagsRule = SetFlagsRule()
    @get:Rule
    val mocks = MockitoJUnit.rule()

    private lateinit var mController: RotationButtonController
    @Mock private lateinit var rotationPolicyWrapper: RotationPolicyWrapper
    @Mock private lateinit var rotationButton: RotationButton
    @Mock private lateinit var view: View
    @Captor private lateinit var onRotateSuggestionClick: ArgumentCaptor<View.OnClickListener>

    @Before
    fun setUp() {
    mController = RotationButtonController(
        mController =
            RotationButtonController(
                rotationPolicyWrapper,
                mContext,
                /* lightIconColor = */ 0,
                /* darkIconColor = */ 0,
                /* iconCcwStart0ResId = */ 0,
                /* iconCcwStart90ResId = */ 0,
                /* iconCwStart0ResId = */ 0,
      /* iconCwStart90ResId = */ 0
    ) { 0 }
                /* iconCwStart90ResId = */ 0,
            ) {
                0
            }
    }

    @Test
    fun ifGestural_showRotationSuggestion() {
        mController.onNavigationBarWindowVisibilityChange(/* showing= */ false)
    mController.onBehaviorChanged(Display.DEFAULT_DISPLAY,
                                  WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
        mController.onBehaviorChanged(
            Display.DEFAULT_DISPLAY,
            WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE,
        )
        mController.onNavigationModeChanged(WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON)
        mController.onTaskbarStateChange(/* visible= */ false, /* stashed= */ false)
        assertThat(mController.canShowRotationButton()).isFalse()
@@ -60,4 +91,40 @@ class RotationButtonControllerTest : SysuiTestCase() {

        assertThat(mController.canShowRotationButton()).isTrue()
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR)
    fun onRotateSuggestionClick_rotationButtonClicked_setRotationAtAngleIfAllowedIsCalled() {
        clickRotationSuggestionButton()

        verify(rotationPolicyWrapper, times(1)).setRotationAtAngleIfAllowed(any(), any())
        verify(rotationPolicyWrapper, never()).setRotationLockAtAngle(any(), any(), any())
    }

    @Test
    fun setRotationAtAngle_forceSetRotationAtAngle_setRotationAtAngleIsCalled() {
        mController.setRotationAtAngle(
            /* isLocked= */ true,
            /* rotationSuggestion = */ Surface.ROTATION_270,
            /* caller= */ "",
        )

        verify(rotationPolicyWrapper, never()).setRotationAtAngleIfAllowed(any(), any())
        verify(rotationPolicyWrapper, times(1))
            .setRotationLockAtAngle(eq(true), eq(Surface.ROTATION_270), eq(""))
    }

    private fun clickRotationSuggestionButton() {
        mController.setRotationButton(
            rotationButton,
            object : RotationButtonUpdatesCallback {
                override fun onVisibilityChanged(isVisible: Boolean) {}

                override fun onPositionChanged() {}
            },
        )

        verify(rotationButton, times(1)).setOnClickListener(onRotateSuggestionClick.capture())
        onRotateSuggestionClick.value.onClick(view)
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -60,6 +60,8 @@ android_library {
        "SystemUIUnfoldLib",
        "SystemUISharedLib-Keyguard",
        "WindowManager-Shell-shared",
        "com.android.systemui.rotation-api",
        "com.android.systemui.rotation-impl",
        "//frameworks/libs/systemui:tracinglib-platform",
        "androidx.dynamicanimation_dynamicanimation",
        "androidx.concurrent_concurrent-futures",
+63 −22
Original line number Diff line number Diff line
@@ -66,7 +66,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.UiEventLoggerImpl;
import com.android.internal.view.RotationPolicy;
import com.android.systemui.rotation.RotationPolicyWrapper;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.recents.utilities.ViewRippler;
import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
@@ -75,6 +75,10 @@ import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.window.flags.Flags;

import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;

import java.io.PrintWriter;
import java.util.List;
import java.util.Optional;
@@ -102,6 +106,7 @@ public class RotationButtonController {
    private final UiEventLogger mUiEventLogger = new UiEventLoggerImpl();
    private final ViewRippler mViewRippler = new ViewRippler();
    private final Supplier<Integer> mWindowRotationProvider;
    private final RotationPolicyWrapper mRotationPolicyWrapper;
    @Nullable private RotationButton mRotationButton;

    private boolean mIsRecentsAnimationRunning;
@@ -207,12 +212,14 @@ public class RotationButtonController {
        return (disable2Flags & StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS) != 0;
    }

    public RotationButtonController(Context context,
        @ColorInt int lightIconColor, @ColorInt int darkIconColor,
        @DrawableRes int iconCcwStart0ResId,
        @DrawableRes int iconCcwStart90ResId,
        @DrawableRes int iconCwStart0ResId,
        @DrawableRes int iconCwStart90ResId,
    @AssistedInject
    public RotationButtonController(RotationPolicyWrapper rotationPolicyWrapper, Context context,
            @Assisted("lightIconColor") @ColorInt int lightIconColor,
            @Assisted("darkIconColor") @ColorInt int darkIconColor,
            @Assisted("iconCcwStart0ResId") @DrawableRes int iconCcwStart0ResId,
            @Assisted("iconCcwStart90ResId") @DrawableRes int iconCcwStart90ResId,
            @Assisted("iconCwStart0ResId") @DrawableRes int iconCwStart0ResId,
            @Assisted("iconCwStart90ResId") @DrawableRes int iconCwStart90ResId,
            Supplier<Integer> windowRotationProvider) {

        mContext = context;
@@ -228,6 +235,7 @@ public class RotationButtonController {
        mAccessibilityManager = AccessibilityManager.getInstance(context);
        mTaskStackListener = new TaskStackListenerImpl();
        mWindowRotationProvider = windowRotationProvider;
        mRotationPolicyWrapper = rotationPolicyWrapper;

        mBgExecutor = context.getMainExecutor();
    }
@@ -337,24 +345,27 @@ public class RotationButtonController {
    }

    /**
     * Sets device rotation to {@code rotationSuggestion} if {@code isLocked} is true and
     * {@link Flags#enableDeviceStateAutoRotateSettingRefactor()} is disabled.
     * <p> When {@link Flags#enableDeviceStateAutoRotateSettingRefactor()} is enabled, the rotation
     * change in system server is conditional on auto-rotate still being OFF.
     * Sets the device rotation to the specified {@code rotation} if {@code isLocked} is true.
     *
     * This method is used to change the device's rotation in scenarios other than a rotation
     * suggestion button click. For example, if a user is in an app with a rotation value of
     * {@code ROTATION_270} but then navigates to the home screen where only {@code ROTATION_0}
     * is allowed, this method will be called to set the rotation to {@code ROTATION_0}. The
     * rotation is changed without further checks beyond verifying the value of the 'isLocked'
     * parameter.
     *
     * @param rotation The desired rotation.
     * @param isLocked {@code true} to apply the rotation; {@code false} to do nothing.
     */
    public void setRotationAtAngle(
            @Nullable Boolean isLocked, int rotationSuggestion, String caller) {
            @Nullable Boolean isLocked, int rotation, String caller) {
        if (isLocked == null) {
            // Ignore if we can't read the setting for the current user
            return;
        }
        if (isFoldable() && Flags.enableDeviceStateAutoRotateSettingRefactor()) {
            RotationPolicy.setRotationAtAngleIfAllowed(rotationSuggestion, caller);
            return;
        }

        RotationPolicy.setRotationLockAtAngle(mContext, /* enabled= */ isLocked,
                /* rotation= */ rotationSuggestion, caller);
        mRotationPolicyWrapper.setRotationLockAtAngle(/* enabled= */ isLocked,
                /* rotation= */ rotation, /* caller= */ caller);
    }

    public void setRotateSuggestionButtonState(boolean visible) {
@@ -652,13 +663,28 @@ public class RotationButtonController {
    private void onRotateSuggestionClick(View v) {
        mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
        incrementNumAcceptedRotationSuggestionsIfNeeded();
        setRotationAtAngle(
                RotationPolicyUtil.isRotationLocked(mContext), mLastRotationSuggestion,
                /* caller= */ "RotationButtonController#onRotateSuggestionClick");
        setRotationForSuggestionIfAllowed(RotationPolicyUtil.isRotationLocked(mContext),
                mLastRotationSuggestion);
        Log.i(TAG, "onRotateSuggestionClick() mLastRotationSuggestion=" + mLastRotationSuggestion);
        v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
    }

    private void setRotationForSuggestionIfAllowed(@Nullable Boolean isLocked,
            int rotationSuggestion) {
        if (isLocked == null) {
            // Ignore if we can't read the setting for the current user
            return;
        }
        final String caller = "RotationButtonController#onRotateSuggestionClick";

        if (Flags.enableDeviceStateAutoRotateSettingRefactor()) {
            mRotationPolicyWrapper.setRotationAtAngleIfAllowed(rotationSuggestion, caller);
            return;
        }

        setRotationAtAngle(isLocked, rotationSuggestion, caller);
    }

    private boolean onRotateSuggestionHover(View v, MotionEvent event) {
        final int action = event.getActionMasked();
        mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER)
@@ -798,6 +824,21 @@ public class RotationButtonController {
        }
    }

    /**
     * Assisted factory for creating instances of {@link RotationButtonController}.
     */
    @AssistedFactory
    public interface Factory {
        RotationButtonController create(
                @Assisted("lightIconColor") @ColorInt int lightIconColor,
                @Assisted("darkIconColor") @ColorInt int darkIconColor,
                @Assisted("iconCcwStart0ResId") @DrawableRes int iconCcwStart0ResId,
                @Assisted("iconCcwStart90ResId") @DrawableRes int iconCcwStart90ResId,
                @Assisted("iconCwStart0ResId") @DrawableRes int iconCwStart0ResId,
                @Assisted("iconCwStart90ResId") @DrawableRes int iconCwStart90ResId
        );
    }

    enum RotationButtonEvent implements UiEventLogger.UiEventEnum {
        @UiEvent(doc = "The rotation button was shown")
        ROTATION_SUGGESTION_SHOWN(206),
Loading