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

Commit fb602d4d authored by Stefan Andonian's avatar Stefan Andonian Committed by Android (Google) Code Review
Browse files

Merge "Refactor the OnUserUnlock code out of RecentsAnimationDeviceState and...

Merge "Refactor the OnUserUnlock code out of RecentsAnimationDeviceState and into its own class." into tm-qpr-dev
parents 96bfea9d 2a58ddb8
Loading
Loading
Loading
Loading
+2 −49
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@ package com.android.quickstep;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static android.view.Display.DEFAULT_DISPLAY;

import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
@@ -50,10 +49,8 @@ import android.content.Context;
import android.graphics.Region;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.Settings;
import android.view.MotionEvent;

@@ -63,9 +60,9 @@ import androidx.annotation.NonNull;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener;
import com.android.launcher3.util.DisplayController.Info;
import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.NavigationMode;
import com.android.launcher3.util.SettingsCache;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
import com.android.quickstep.util.NavBarPosition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -109,15 +106,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
    private final boolean mIsOneHandedModeSupported;
    private boolean mPipIsActive;

    private boolean mIsUserUnlocked;
    private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
    private final SimpleBroadcastReceiver mUserUnlockedReceiver = new SimpleBroadcastReceiver(i -> {
        if (ACTION_USER_UNLOCKED.equals(i.getAction())) {
            mIsUserUnlocked = true;
            notifyUserUnlocked();
        }
    });

    private int mGestureBlockingTaskId = -1;
    private @NonNull Region mExclusionRegion = new Region();
    private SystemGestureExclusionListenerCompat mExclusionListener;
@@ -143,14 +131,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
            runOnDestroy(mRotationTouchHelper::destroy);
        }

        // Register for user unlocked if necessary
        mIsUserUnlocked = context.getSystemService(UserManager.class)
                .isUserUnlocked(Process.myUserHandle());
        if (!mIsUserUnlocked) {
            mUserUnlockedReceiver.register(mContext, ACTION_USER_UNLOCKED);
        }
        runOnDestroy(() -> mUserUnlockedReceiver.unregisterReceiverSafely(mContext));

        // Register for exclusion updates
        mExclusionListener = new SystemGestureExclusionListenerCompat(mDisplayId) {
            @Override
@@ -309,25 +289,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
        return mDisplayId;
    }

    /**
     * Adds a callback for when a user is unlocked. If the user is already unlocked, this listener
     * will be called back immediately.
     */
    public void runOnUserUnlocked(Runnable action) {
        if (mIsUserUnlocked) {
            action.run();
        } else {
            mUserUnlockedActions.add(action);
        }
    }

    /**
     * @return whether the user is unlocked.
     */
    public boolean isUserUnlocked() {
        return mIsUserUnlocked;
    }

    /**
     * @return whether the user has completed setup wizard
     */
@@ -335,14 +296,6 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
        return mIsUserSetupComplete;
    }

    private void notifyUserUnlocked() {
        for (Runnable action : mUserUnlockedActions) {
            action.run();
        }
        mUserUnlockedActions.clear();
        mUserUnlockedReceiver.unregisterReceiverSafely(mContext);
    }

    /**
     * Sets the task id where gestures should be blocked
     */
@@ -585,7 +538,7 @@ public class RecentsAnimationDeviceState implements DisplayInfoChangeListener {
        pw.println("  assistantAvailable=" + mAssistantAvailable);
        pw.println("  assistantDisabled="
                + QuickStepContract.isAssistantGestureDisabled(mSystemUiStateFlags));
        pw.println("  isUserUnlocked=" + mIsUserUnlocked);
        pw.println("  isUserUnlocked=" + LockedUserState.get(mContext).isUserUnlocked());
        pw.println("  isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
        pw.println("  isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
        pw.println("  deferredGestureRegion=" + mDeferredGestureRegion.getBounds());
+14 −12
Original line number Diff line number Diff line
@@ -87,6 +87,7 @@ import com.android.launcher3.tracing.LauncherTraceProto;
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.LockedUserState;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
@@ -406,8 +407,8 @@ public class TouchInteractionService extends Service
        mRotationTouchHelper = mDeviceState.getRotationTouchHelper();

        // Call runOnUserUnlocked() before any other callbacks to ensure everything is initialized.
        mDeviceState.runOnUserUnlocked(this::onUserUnlocked);
        mDeviceState.runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
        LockedUserState.get(this).runOnUserUnlocked(this::onUserUnlocked);
        LockedUserState.get(this).runOnUserUnlocked(mTaskbarManager::onUserUnlocked);
        mDeviceState.addNavigationModeChangedCallback(this::onNavigationModeChanged);

        ProtoTracer.INSTANCE.get(this).add(this);
@@ -477,7 +478,7 @@ public class TouchInteractionService extends Service
    }

    private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
        if (!mDeviceState.isUserUnlocked() || mDeviceState.isButtonNavMode()) {
        if (!LockedUserState.get(this).isUserUnlocked() || mDeviceState.isButtonNavMode()) {
            // Skip if not yet unlocked (can't read user shared prefs) or if the current navigation
            // mode doesn't have gestures
            return;
@@ -520,7 +521,7 @@ public class TouchInteractionService extends Service

    @UiThread
    private void onSystemUiFlagsChanged(int lastSysUIFlags) {
        if (mDeviceState.isUserUnlocked()) {
        if (LockedUserState.get(this).isUserUnlocked()) {
            int systemUiStateFlags = mDeviceState.getSystemUiStateFlags();
            SystemUiProxy.INSTANCE.get(this).setLastSystemUiStateFlags(systemUiStateFlags);
            mOverviewComponentObserver.onSystemUiStateChanged();
@@ -565,7 +566,7 @@ public class TouchInteractionService extends Service

    @UiThread
    private void onAssistantVisibilityChanged() {
        if (mDeviceState.isUserUnlocked()) {
        if (LockedUserState.get(this).isUserUnlocked()) {
            mOverviewComponentObserver.getActivityInterface().onAssistantVisibilityChanged(
                    mDeviceState.getAssistantVisibility());
        }
@@ -575,7 +576,7 @@ public class TouchInteractionService extends Service
    public void onDestroy() {
        Log.d(TAG, "Touch service destroyed: user=" + getUserId());
        sIsInitialized = false;
        if (mDeviceState.isUserUnlocked()) {
        if (LockedUserState.get(this).isUserUnlocked()) {
            mInputConsumer.unregisterInputConsumer();
            mOverviewComponentObserver.onDestroy();
        }
@@ -609,7 +610,7 @@ public class TouchInteractionService extends Service
        TestLogging.recordMotionEvent(
                TestProtocol.SEQUENCE_TIS, "TouchInteractionService.onInputEvent", event);

        if (!mDeviceState.isUserUnlocked()) {
        if (!LockedUserState.get(this).isUserUnlocked()) {
            return;
        }

@@ -631,7 +632,8 @@ public class TouchInteractionService extends Service
                mGestureState = newGestureState;
                mConsumer = newConsumer(prevGestureState, mGestureState, event);
                mUncheckedConsumer = mConsumer;
            } else if (mDeviceState.isUserUnlocked() && mDeviceState.isFullyGesturalNavMode()
            } else if (LockedUserState.get(this).isUserUnlocked()
                    && mDeviceState.isFullyGesturalNavMode()
                    && mDeviceState.canTriggerAssistantAction(event)) {
                mGestureState = createGestureState(mGestureState);
                // Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we
@@ -751,7 +753,7 @@ public class TouchInteractionService extends Service

        boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();

        if (!mDeviceState.isUserUnlocked()) {
        if (!LockedUserState.get(this).isUserUnlocked()) {
            CompoundString reasonString = newCompoundString("device locked");
            InputConsumer consumer;
            if (canStartSystemGesture) {
@@ -1098,7 +1100,7 @@ public class TouchInteractionService extends Service
    }

    private void preloadOverview(boolean fromInit, boolean forSUWAllSet) {
        if (!mDeviceState.isUserUnlocked()) {
        if (!LockedUserState.get(this).isUserUnlocked()) {
            return;
        }

@@ -1130,7 +1132,7 @@ public class TouchInteractionService extends Service

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        if (!mDeviceState.isUserUnlocked()) {
        if (!LockedUserState.get(this).isUserUnlocked()) {
            return;
        }
        final BaseActivityInterface activityInterface =
@@ -1171,7 +1173,7 @@ public class TouchInteractionService extends Service
        } else {
            // Dump everything
            FeatureFlags.dump(pw);
            if (mDeviceState.isUserUnlocked()) {
            if (LockedUserState.get(this).isUserUnlocked()) {
                PluginManagerWrapper.INSTANCE.get(getBaseContext()).dump(pw);
            }
            mDeviceState.dump(pw);
+57 −0
Original line number Diff line number Diff line
package com.android.launcher3.util

import android.content.Context
import android.content.Intent
import android.os.Process
import android.os.UserManager
import androidx.annotation.VisibleForTesting

class LockedUserState(private val mContext: Context) : SafeCloseable {
    var isUserUnlocked: Boolean
        private set
    private val mUserUnlockedActions: RunnableList = RunnableList()

    @VisibleForTesting
    val mUserUnlockedReceiver = SimpleBroadcastReceiver {
        if (Intent.ACTION_USER_UNLOCKED == it.action) {
            isUserUnlocked = true
            notifyUserUnlocked()
        }
    }

    init {
        isUserUnlocked =
            mContext
                .getSystemService(UserManager::class.java)!!
                .isUserUnlocked(Process.myUserHandle())
        if (isUserUnlocked) {
            notifyUserUnlocked()
        } else {
            mUserUnlockedReceiver.register(mContext, Intent.ACTION_USER_UNLOCKED)
        }
    }

    private fun notifyUserUnlocked() {
        mUserUnlockedActions.executeAllAndDestroy()
        mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
    }

    /** Stops the receiver from listening for ACTION_USER_UNLOCK broadcasts. */
    override fun close() {
        mUserUnlockedReceiver.unregisterReceiverSafely(mContext)
    }

    /**
     * Adds a `Runnable` to be executed when a user is unlocked. If the user is already unlocked,
     * this runnable will run immediately because RunnableList will already have been destroyed.
     */
    fun runOnUserUnlocked(action: Runnable) {
        mUserUnlockedActions.add(action)
    }

    companion object {
        @VisibleForTesting val INSTANCE = MainThreadInitializedObject { LockedUserState(it) }

        @JvmStatic fun get(context: Context): LockedUserState = INSTANCE.get(context)
    }
}
+88 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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 com.android.launcher3.util

import android.content.Context
import android.content.Intent
import android.os.Process
import android.os.UserManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations

/** Unit tests for {@link LockedUserUtil} */
@SmallTest
@RunWith(AndroidJUnit4::class)
class LockedUserStateTest {

    @Mock lateinit var userManager: UserManager
    @Mock lateinit var context: Context

    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        `when`(context.getSystemService(UserManager::class.java)).thenReturn(userManager)
    }

    @Test
    fun runOnUserUnlocked_runs_action_immediately_if_already_unlocked() {
        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
        val action: Runnable = mock()

        LockedUserState.get(context).runOnUserUnlocked(action)
        verify(action).run()
    }

    @Test
    fun runOnUserUnlocked_waits_to_run_action_until_user_is_unlocked() {
        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
        val action: Runnable = mock()

        LockedUserState.get(context).runOnUserUnlocked(action)
        verifyZeroInteractions(action)

        LockedUserState.get(context)
            .mUserUnlockedReceiver
            .onReceive(context, Intent(Intent.ACTION_USER_UNLOCKED))

        verify(action).run()
    }

    @Test
    fun isUserUnlocked_returns_true_when_user_is_unlocked() {
        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(true)
        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
        assertThat(LockedUserState.get(context).isUserUnlocked).isTrue()
    }

    @Test
    fun isUserUnlocked_returns_false_when_user_is_locked() {
        `when`(userManager.isUserUnlocked(Process.myUserHandle())).thenReturn(false)
        LockedUserState.INSTANCE.initializeForTesting(LockedUserState(context))
        assertThat(LockedUserState.get(context).isUserUnlocked).isFalse()
    }
}