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

Commit ee4286ee authored by Lucas Silva's avatar Lucas Silva Committed by Android (Google) Code Review
Browse files

Merge "Avoid showing lockscreen when closing an activity in hub mode." into main

parents e234e53b 633e1b6c
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -16,6 +16,9 @@

package com.android.wm.shell.recents;

import android.annotation.Nullable;
import android.graphics.Color;

import com.android.wm.shell.shared.annotations.ExternalThread;
import com.android.wm.shell.util.GroupedRecentTaskInfo;

@@ -40,4 +43,12 @@ public interface RecentTasks {
     */
    default void addAnimationStateListener(Executor listenerExecutor, Consumer<Boolean> listener) {
    }

    /**
     * Sets a background color on the transition root layered behind the outgoing task. {@code null}
     * may be used to clear any previously set colors to avoid showing a background at all. The
     * color is always shown at full opacity.
     */
    default void setTransitionBackgroundColor(@Nullable Color color) {
    }
}
+11 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Slog;
@@ -476,6 +477,16 @@ public class RecentTasksController implements TaskStackListenerCallback,
                });
            });
        }

        @Override
        public void setTransitionBackgroundColor(@Nullable Color color) {
            mMainExecutor.execute(() -> {
                if (mTransitionHandler == null) {
                    return;
                }
                mTransitionHandler.setTransitionBackgroundColor(color);
            });
        }
    }


+46 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import android.app.ActivityTaskManager;
import android.app.IApplicationThread;
import android.app.PendingIntent;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -56,6 +57,8 @@ import android.window.TransitionRequestInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

import androidx.annotation.NonNull;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IResultReceiver;
import com.android.internal.protolog.common.ProtoLog;
@@ -92,6 +95,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
    private final ArrayList<RecentsMixedHandler> mMixers = new ArrayList<>();

    private final HomeTransitionObserver mHomeTransitionObserver;
    private @Nullable Color mBackgroundColor;

    public RecentsTransitionHandler(ShellInit shellInit, Transitions transitions,
            @Nullable RecentTasksController recentTasksController,
@@ -123,6 +127,15 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
        mStateListeners.add(listener);
    }

    /**
     * Sets a background color on the transition root layered behind the outgoing task. {@code null}
     * may be used to clear any previously set colors to avoid showing a background at all. The
     * color is always shown at full opacity.
     */
    public void setTransitionBackgroundColor(@Nullable Color color) {
        mBackgroundColor = color;
    }

    @VisibleForTesting
    public IBinder startRecentsTransition(PendingIntent intent, Intent fillIn, Bundle options,
            IApplicationThread appThread, IRecentsAnimationRunner listener) {
@@ -469,6 +482,16 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
            final int belowLayers = info.getChanges().size();
            final int middleLayers = info.getChanges().size() * 2;
            final int aboveLayers = info.getChanges().size() * 3;

            // Add a background color to each transition root in this transition.
            if (mBackgroundColor != null) {
                info.getChanges().stream()
                        .mapToInt((change) -> TransitionUtil.rootIndexFor(change, info))
                        .distinct()
                        .mapToObj((rootIndex) -> info.getRoot(rootIndex).getLeash())
                        .forEach((root) -> createBackgroundSurface(t, root, middleLayers));
            }

            for (int i = 0; i < info.getChanges().size(); ++i) {
                final TransitionInfo.Change change = info.getChanges().get(i);
                final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -1107,6 +1130,29 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler {
            return true;
        }

        private void createBackgroundSurface(SurfaceControl.Transaction transaction,
                SurfaceControl parent, int layer) {
            if (mBackgroundColor == null) {
                return;
            }
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                    "  adding background color to layer=%d", layer);
            final SurfaceControl background = new SurfaceControl.Builder()
                    .setName("recents_background")
                    .setColorLayer()
                    .setOpaque(true)
                    .setParent(parent)
                    .build();
            transaction.setColor(background, colorToFloatArray(mBackgroundColor));
            transaction.setLayer(background, layer);
            transaction.setAlpha(background, 1F);
            transaction.show(background);
        }

        private static float[] colorToFloatArray(@NonNull Color color) {
            return new float[]{color.red(), color.green(), color.blue()};
        }

        private void cleanUpPausingOrClosingTask(TaskState task, WindowContainerTransaction wct,
                SurfaceControl.Transaction finishTransaction, boolean sendUserLeaveHint) {
            if (!sendUserLeaveHint && task.isLeaf()) {
+9 −5
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.dimensionResource
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.ElementKey
@@ -24,11 +25,10 @@ import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.scene.ui.composable.SceneTransitionLayoutDataSource
@@ -75,6 +75,7 @@ fun CommunalContainer(
    viewModel: CommunalViewModel,
    dataSourceDelegator: SceneDataSourceDelegator,
    dialogFactory: SystemUIDialogFactory,
    colors: CommunalColors,
) {
    val coroutineScope = rememberCoroutineScope()
    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsState(CommunalScenes.Blank)
@@ -135,7 +136,7 @@ fun CommunalContainer(
                    emptyMap()
                },
        ) {
            CommunalScene(viewModel, dialogFactory, modifier = modifier)
            CommunalScene(viewModel, colors, dialogFactory, modifier = modifier)
        }
    }
}
@@ -143,15 +144,18 @@ fun CommunalContainer(
/** Scene containing the glanceable hub UI. */
@Composable
private fun SceneScope.CommunalScene(
    viewModel: BaseCommunalViewModel,
    viewModel: CommunalViewModel,
    colors: CommunalColors,
    dialogFactory: SystemUIDialogFactory,
    modifier: Modifier = Modifier,
) {
    val backgroundColor by colors.backgroundColor.collectAsState()

    Box(
        modifier =
            Modifier.element(Communal.Elements.Scrim)
                .fillMaxSize()
                .background(LocalAndroidColorScheme.current.outlineVariant),
                .background(Color(backgroundColor.toArgb())),
    )
    Box(modifier.element(Communal.Elements.Content)) {
        CommunalHub(viewModel = viewModel, dialogFactory = dialogFactory)
+215 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.wmshell

import android.content.pm.UserInfo
import android.graphics.Color
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.Flags.FLAG_COMMUNAL_HUB
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
import com.android.systemui.communal.util.fakeCommunalColors
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.dock.DockManager
import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.ScreenLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.wakefulnessLifecycle
import com.android.systemui.kosmos.testScope
import com.android.systemui.model.SysUiState
import com.android.systemui.model.sysUiState
import com.android.systemui.notetask.NoteTaskInitializer
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.settings.UserTracker
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.commandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.configurationController
import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.kotlin.JavaAdapter
import com.android.wm.shell.desktopmode.DesktopMode
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository.VisibleTasksListener
import com.android.wm.shell.onehanded.OneHanded
import com.android.wm.shell.onehanded.OneHandedEventCallback
import com.android.wm.shell.onehanded.OneHandedTransitionCallback
import com.android.wm.shell.pip.Pip
import com.android.wm.shell.recents.RecentTasks
import com.android.wm.shell.splitscreen.SplitScreen
import com.android.wm.shell.sysui.ShellInterface
import java.util.Optional
import java.util.concurrent.Executor
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

/**
 * Tests for [WMShell].
 *
 * Build/Install/Run: atest SystemUITests:WMShellTest
 */
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class WMShellTest : SysuiTestCase() {
    val kosmos = testKosmos()
    val testScope = kosmos.testScope

    @Mock private lateinit var mShellInterface: ShellInterface
    @Mock private lateinit var mScreenLifecycle: ScreenLifecycle
    @Mock private lateinit var mPip: Pip
    @Mock private lateinit var mSplitScreen: SplitScreen
    @Mock private lateinit var mOneHanded: OneHanded
    @Mock private lateinit var mNoteTaskInitializer: NoteTaskInitializer
    @Mock private lateinit var mDesktopMode: DesktopMode
    @Mock private lateinit var mRecentTasks: RecentTasks

    private val mCommandQueue: CommandQueue = kosmos.commandQueue
    private val mConfigurationController: ConfigurationController = kosmos.configurationController
    private val mKeyguardStateController: KeyguardStateController = kosmos.keyguardStateController
    private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor = kosmos.keyguardUpdateMonitor
    private val mSysUiState: SysUiState = kosmos.sysUiState
    private val mWakefulnessLifecycle: WakefulnessLifecycle = kosmos.wakefulnessLifecycle
    private val mUserTracker: UserTracker = kosmos.userTracker
    private val mSysUiMainExecutor: Executor = kosmos.fakeExecutor
    private val communalTransitionViewModel = kosmos.communalTransitionViewModel

    private lateinit var underTest: WMShell

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        val displayTracker = FakeDisplayTracker(mContext)

        kosmos.fakeUserRepository.setUserInfos(listOf(MAIN_USER_INFO))
        kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)

        underTest =
            WMShell(
                mContext,
                mShellInterface,
                Optional.of(mPip),
                Optional.of(mSplitScreen),
                Optional.of(mOneHanded),
                Optional.of(mDesktopMode),
                Optional.of(mRecentTasks),
                mCommandQueue,
                mConfigurationController,
                mKeyguardStateController,
                mKeyguardUpdateMonitor,
                mScreenLifecycle,
                mSysUiState,
                mWakefulnessLifecycle,
                mUserTracker,
                displayTracker,
                mNoteTaskInitializer,
                communalTransitionViewModel,
                JavaAdapter(testScope.backgroundScope),
                mSysUiMainExecutor
            )
    }

    @Test
    fun initPip_registersCommandQueueCallback() {
        underTest.initPip(mPip)
        verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks::class.java))
    }

    @Test
    fun initOneHanded_registersCallbacks() {
        underTest.initOneHanded(mOneHanded)
        verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks::class.java))
        verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer::class.java))
        verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback::class.java))
        verify(mOneHanded).registerEventCallback(any(OneHandedEventCallback::class.java))
    }

    @Test
    fun initDesktopMode_registersListener() {
        underTest.initDesktopMode(mDesktopMode)
        verify(mDesktopMode)
            .addVisibleTasksListener(
                any(VisibleTasksListener::class.java),
                any(Executor::class.java)
            )
    }

    @Test
    fun initRecentTasks_registersListener() {
        underTest.initRecentTasks(mRecentTasks)
        verify(mRecentTasks).addAnimationStateListener(any(Executor::class.java), any())
    }

    @Test
    @EnableFlags(FLAG_COMMUNAL_HUB)
    fun initRecentTasks_setRecentsBackgroundColorWhenCommunal() =
        testScope.runTest {
            val black = Color.valueOf(Color.BLACK)
            kosmos.fakeCommunalColors.setBackgroundColor(black)

            kosmos.fakeKeyguardRepository.setKeyguardShowing(false)

            underTest.initRecentTasks(mRecentTasks)
            runCurrent()
            verify(mRecentTasks).setTransitionBackgroundColor(null)
            verify(mRecentTasks, never()).setTransitionBackgroundColor(black)

            setDocked(true)
            // Make communal available
            kosmos.fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
            kosmos.fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
            kosmos.fakeKeyguardRepository.setKeyguardShowing(true)

            runCurrent()

            verify(mRecentTasks).setTransitionBackgroundColor(black)
        }

    private fun TestScope.setDocked(docked: Boolean) {
        kosmos.fakeDockManager.setIsDocked(docked)
        val event =
            if (docked) {
                DockManager.STATE_DOCKED
            } else {
                DockManager.STATE_NONE
            }
        kosmos.fakeDockManager.setDockEvent(event)
        runCurrent()
    }

    private companion object {
        val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN)
    }
}
Loading