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

Commit 5935cd88 authored by Franciszek Juras's avatar Franciszek Juras Committed by Android (Google) Code Review
Browse files

Merge changes from topic "franekj/logout-button/b/392052370" into main

* changes:
  Add Sign out button on keyguard's status bar
  Add log out to system user in UserLogoutInteractor
parents 8434377c 39685292
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -17,6 +17,13 @@ flag {
    bug: "416223998"
}

flag {
    name: "sign_out_button_on_keyguard_status_bar"
    namespace: "desktop_users_and_accounts"
    description: "Add a 'Sign out' button on keyguard's status bar"
    bug: "392052370"
}

flag {
    name: "user_switcher_add_sign_out_option"
    namespace: "desktop_users_and_accounts"
+60 −0
Original line number Diff line number Diff line
@@ -17,22 +17,40 @@
package com.android.systemui.keyguard.ui.composable.element

import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.modifiers.height
import com.android.compose.theme.PlatformTheme
import com.android.keyguard.dagger.KeyguardStatusBarViewComponent
import com.android.systemui.common.shared.model.Icon as IconModel
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.shade.ShadeViewStateProvider
import com.android.systemui.statusbar.phone.KeyguardStatusBarView
import com.android.systemui.statusbar.ui.viewmodel.KeyguardStatusBarViewModel
import com.android.systemui.util.Utils
import dagger.Lazy
import javax.inject.Inject
@@ -42,6 +60,7 @@ class StatusBarElement
constructor(
    private val componentFactory: KeyguardStatusBarViewComponent.Factory,
    private val notificationPanelView: Lazy<NotificationPanelView>,
    private val viewModel: KeyguardStatusBarViewModel,
) {

    @Composable
@@ -83,6 +102,12 @@ constructor(
                    (it.parent as ViewGroup).removeView(it)
                }

                if (viewModel.isSignOutButtonEnabled) {
                    view
                        .requireViewById<FrameLayout>(R.id.sign_out_button_container)
                        .addView(createSignOutButtonView(context))
                }

                viewController.init()
                view
            },
@@ -91,4 +116,39 @@ constructor(
            update = { viewController.setDisplayCutout(viewDisplayCutout) },
        )
    }

    private fun createSignOutButtonView(context: Context): ComposeView {
        return ComposeView(context).apply {
            setViewCompositionStrategy(DisposeOnViewTreeLifecycleDestroyed)
            setContent { PlatformTheme { SignOutButton() } }
        }
    }

    @Composable
    private fun SignOutButton() {
        val context = LocalContext.current
        val isVisible by viewModel.isSignOutButtonVisible.collectAsStateWithLifecycle()
        if (isVisible) {
            Button(
                onClick = viewModel.onSignOut,
                contentPadding = PaddingValues(start = 4.dp, end = 8.dp),
                modifier = Modifier.padding(end = 8.dp),
            ) {
                Icon(
                    icon =
                        IconModel.Resource(
                            com.android.internal.R.drawable.ic_logout,
                            contentDescription = null,
                        ),
                    modifier = Modifier.size(16.dp),
                )
                Spacer(modifier = Modifier.width(4.dp))
                Text(
                    checkNotNull(
                        context.getString(com.android.internal.R.string.global_action_logout)
                    )
                )
            }
        }
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -94,6 +94,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                dreamOverlayCallbackController,
                dispatcher,
                testScope.backgroundScope,
                context,
                systemClock,
                facePropertyRepository,
                userTracker,
+46 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
@@ -42,6 +43,8 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.heads
import com.android.systemui.statusbar.policy.batteryController
import com.android.systemui.statusbar.policy.fake
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.user.domain.interactor.userLogoutInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -59,6 +62,7 @@ import platform.test.runner.parameterized.Parameters
class KeyguardStatusBarViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val userRepository = kosmos.fakeUserRepository
    private val faceAuthRepository by lazy { kosmos.fakeDeviceEntryFaceAuthRepository }
    private val headsUpRepository by lazy { kosmos.headsUpNotificationRepository }
    private val headsUpNotificationInteractor by lazy { kosmos.headsUpNotificationInteractor }
@@ -66,6 +70,7 @@ class KeyguardStatusBarViewModelTest(flags: FlagsParameterization) : SysuiTestCa
    private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
    private val keyguardInteractor by lazy { kosmos.keyguardInteractor }
    private val keyguardStatusBarInteractor by lazy { kosmos.keyguardStatusBarInteractor }
    private val userLogoutInteractor by lazy { kosmos.userLogoutInteractor }
    private val batteryController = kosmos.batteryController

    lateinit var underTest: KeyguardStatusBarViewModel
@@ -91,6 +96,7 @@ class KeyguardStatusBarViewModelTest(flags: FlagsParameterization) : SysuiTestCa
                kosmos.sceneInteractor,
                keyguardInteractor,
                keyguardStatusBarInteractor,
                userLogoutInteractor,
                batteryController,
            )
    }
@@ -238,4 +244,44 @@ class KeyguardStatusBarViewModelTest(flags: FlagsParameterization) : SysuiTestCa

            assertThat(batteryController.fake.listeners).isEmpty()
        }

    @Test
    @EnableFlags(Flags.FLAG_SIGN_OUT_BUTTON_ON_KEYGUARD_STATUS_BAR)
    fun signOutButton_isVisible_whenUserManagerLogoutIsEnabled() {
        testScope.runTest {
            kosmos.fakeKeyguardRepository.setIsSignOutButtonOnStatusBarEnabledInConfig(true)
            val isSignOutButtonVisible by collectLastValue(underTest.isSignOutButtonVisible)
            val logoutToSystemUserCount = userRepository.logOutWithUserManagerCallCount
            userRepository.setUserManagerLogoutEnabled(true)
            userRepository.setPolicyManagerLogoutEnabled(false)
            assertThat(underTest.isSignOutButtonEnabled).isTrue()
            assertThat(isSignOutButtonVisible).isTrue()
            underTest.onSignOut()
            runCurrent()
            assertThat(userRepository.logOutWithUserManagerCallCount)
                .isEqualTo(logoutToSystemUserCount + 1)
        }
    }

    @Test
    @EnableFlags(Flags.FLAG_SIGN_OUT_BUTTON_ON_KEYGUARD_STATUS_BAR)
    fun signOutButton_isNotVisible_whenUserManagerLogoutIsDisabled() {
        testScope.runTest {
            kosmos.fakeKeyguardRepository.setIsSignOutButtonOnStatusBarEnabledInConfig(true)
            val isSignOutButtonVisible by collectLastValue(underTest.isSignOutButtonVisible)
            userRepository.setUserManagerLogoutEnabled(false)
            userRepository.setPolicyManagerLogoutEnabled(true)
            assertThat(underTest.isSignOutButtonEnabled).isTrue()
            assertThat(isSignOutButtonVisible).isFalse()
        }
    }

    @Test
    @EnableFlags(Flags.FLAG_SIGN_OUT_BUTTON_ON_KEYGUARD_STATUS_BAR)
    fun signOutButton_isDisabled_whenDisabledInConfig() {
        testScope.runTest {
            kosmos.fakeKeyguardRepository.setIsSignOutButtonOnStatusBarEnabledInConfig(false)
            assertThat(underTest.isSignOutButtonEnabled).isFalse()
        }
    }
}
+35 −0
Original line number Diff line number Diff line
@@ -116,6 +116,41 @@ class UserLogoutInteractorTest : SysuiTestCase() {
        }
    }

    @Test
    fun logOutToSystemUser_doesNothing_whenPolicyManagerLogoutIsEnabled() {
        testScope.runTest {
            val isLogoutToSystemUserEnabled by
                collectLastValue(underTest.isLogoutToSystemUserEnabled)
            val secondaryUserLogoutCount = userRepository.logOutWithPolicyManagerCallCount
            val logoutToSystemUserCount = userRepository.logOutWithUserManagerCallCount
            userRepository.setPolicyManagerLogoutEnabled(true)
            assertThat(isLogoutToSystemUserEnabled).isFalse()
            underTest.logOutToSystemUser()
            assertThat(userRepository.logOutWithPolicyManagerCallCount)
                .isEqualTo(secondaryUserLogoutCount)
            assertThat(userRepository.logOutWithUserManagerCallCount)
                .isEqualTo(logoutToSystemUserCount)
        }
    }

    @Test
    fun logOutToSystemUser_whenBothLogoutOptionsAreEnabled() {
        testScope.runTest {
            val isLogoutToSystemUserEnabled by
                collectLastValue(underTest.isLogoutToSystemUserEnabled)
            val secondaryUserLogoutCount = userRepository.logOutWithPolicyManagerCallCount
            val logoutToSystemUserCount = userRepository.logOutWithUserManagerCallCount
            userRepository.setUserManagerLogoutEnabled(true)
            userRepository.setPolicyManagerLogoutEnabled(true)
            assertThat(isLogoutToSystemUserEnabled).isTrue()
            underTest.logOutToSystemUser()
            assertThat(userRepository.logOutWithPolicyManagerCallCount)
                .isEqualTo(secondaryUserLogoutCount)
            assertThat(userRepository.logOutWithUserManagerCallCount)
                .isEqualTo(logoutToSystemUserCount + 1)
        }
    }

    companion object {
        private val USER_INFOS =
            listOf(
Loading