diff options
17 files changed, 454 insertions, 20 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/dagger/FooterActionsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/dagger/FooterActionsModule.kt index 38fe34eb8f9f..42d3f81ceed6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/dagger/FooterActionsModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/dagger/FooterActionsModule.kt @@ -18,8 +18,6 @@ package com.android.systemui.qs.footer.dagger import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepositoryImpl -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepositoryImpl import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractorImpl import dagger.Binds @@ -28,7 +26,6 @@ import dagger.Module /** Dagger module to provide/bind footer actions singletons. */ @Module interface FooterActionsModule { - @Binds fun userSwitcherRepository(impl: UserSwitcherRepositoryImpl): UserSwitcherRepository @Binds fun foregroundServicesRepository( diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt index 8b2c3de18469..c91ed133a11e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt @@ -38,10 +38,10 @@ import com.android.systemui.qs.FgsManagerController import com.android.systemui.qs.QSSecurityFooterUtils import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig import com.android.systemui.security.data.repository.SecurityRepository import com.android.systemui.statusbar.policy.DeviceProvisionedController +import com.android.systemui.user.data.repository.UserSwitcherRepository import com.android.systemui.user.domain.interactor.UserInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt index 249c83166c51..e2de37fcbcbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.dagger import com.android.systemui.CoreStartable import com.android.systemui.statusbar.core.StatusBarInitializer +import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepository +import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepositoryImpl import com.android.systemui.statusbar.data.repository.StatusBarModeRepository import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryImpl import com.android.systemui.statusbar.phone.LightBarController @@ -49,6 +51,11 @@ abstract class StatusBarModule { abstract fun bindStatusBarModeRepositoryStart(impl: StatusBarModeRepositoryImpl): CoreStartable @Binds + abstract fun bindKeyguardStatusBarRepository( + impl: KeyguardStatusBarRepositoryImpl + ): KeyguardStatusBarRepository + + @Binds @IntoMap @ClassKey(OngoingCallController::class) abstract fun bindOngoingCallController(impl: OngoingCallController): CoreStartable diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt new file mode 100644 index 000000000000..8136de9b7ac4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepository.kt @@ -0,0 +1,80 @@ +/* + * 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.systemui.statusbar.data.repository + +import android.content.Context +import com.android.internal.R +import com.android.systemui.common.coroutine.ConflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.user.data.repository.UserSwitcherRepository +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge + +/** + * Repository for data that's specific to the status bar **on keyguard**. For data that applies to + * all status bars, use [StatusBarModeRepository]. + */ +interface KeyguardStatusBarRepository { + /** True if we can show the user switcher on keyguard and false otherwise. */ + val isKeyguardUserSwitcherEnabled: Flow<Boolean> +} + +@SysUISingleton +class KeyguardStatusBarRepositoryImpl +@Inject +constructor( + context: Context, + configurationController: ConfigurationController, + userSwitcherRepository: UserSwitcherRepository, +) : KeyguardStatusBarRepository { + private val relevantConfigChanges: Flow<Unit> = + ConflatedCallbackFlow.conflatedCallbackFlow { + val callback = + object : ConfigurationController.ConfigurationListener { + override fun onSmallestScreenWidthChanged() { + trySend(Unit) + } + + override fun onDensityOrFontScaleChanged() { + trySend(Unit) + } + } + configurationController.addCallback(callback) + awaitClose { configurationController.removeCallback(callback) } + } + + private val isKeyguardUserSwitcherConfigEnabled: Flow<Boolean> = + // The config depends on screen size and user enabled settings, so re-fetch whenever any of + // those change. + merge(userSwitcherRepository.isEnabled.map {}, relevantConfigChanges).map { + context.resources.getBoolean(R.bool.config_keyguardUserSwitcher) + } + + /** True if we can show the user switcher on keyguard and false otherwise. */ + override val isKeyguardUserSwitcherEnabled: Flow<Boolean> = + combine( + userSwitcherRepository.isEnabled, + isKeyguardUserSwitcherConfigEnabled, + ) { isEnabled, isKeyguardEnabled -> + isEnabled && isKeyguardEnabled + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/KeyguardStatusBarInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/KeyguardStatusBarInteractor.kt new file mode 100644 index 000000000000..e0c30e5ec5c4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/domain/interactor/KeyguardStatusBarInteractor.kt @@ -0,0 +1,33 @@ +/* + * 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.systemui.statusbar.domain.interactor + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.data.repository.KeyguardStatusBarRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +@SysUISingleton +class KeyguardStatusBarInteractor +@Inject +constructor( + keyguardStatusBarRepository: KeyguardStatusBarRepository, +) { + /** True if we can show the user switcher on keyguard and false otherwise. */ + val isKeyguardUserSwitcherEnabled: Flow<Boolean> = + keyguardStatusBarRepository.isKeyguardUserSwitcherEnabled +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 7efa705b7929..58126ae41a1d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -43,9 +43,9 @@ import android.widget.TextView; import androidx.annotation.VisibleForTesting; import com.android.settingslib.Utils; -import com.android.systemui.res.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; +import com.android.systemui.res.R; import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import com.android.systemui.user.ui.binder.StatusBarUserChipViewBinder; @@ -367,15 +367,22 @@ public class KeyguardStatusBarView extends RelativeLayout { mMultiUserAvatar.setImageDrawable(picture); } - /** Should only be called from {@link KeyguardStatusBarViewController}. */ - void onBatteryLevelChanged(boolean charging) { + /** + * Should only be called from {@link KeyguardStatusBarViewController} or + * {@link com.android.systemui.statusbar.ui.binder.KeyguardStatusBarViewBinder}. + */ + public void onBatteryChargingChanged(boolean charging) { if (mBatteryCharging != charging) { mBatteryCharging = charging; updateVisibilities(); } } - void setKeyguardUserSwitcherEnabled(boolean enabled) { + /** + * Should only be called from {@link KeyguardStatusBarViewController} or + * {@link com.android.systemui.statusbar.ui.binder.KeyguardStatusBarViewBinder}. + */ + public void setKeyguardUserSwitcherEnabled(boolean enabled) { mKeyguardUserSwitcherEnabled = enabled; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index 9cf9714c274c..2960520f00b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -172,7 +172,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat new BatteryController.BatteryStateChangeCallback() { @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { - mView.onBatteryLevelChanged(charging); + mView.onBatteryChargingChanged(charging); } }; @@ -430,11 +430,18 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat /** Sets whether user switcher is enabled. */ public void setKeyguardUserSwitcherEnabled(boolean enabled) { + if (isMigrationEnabled()) { + return; + } mView.setKeyguardUserSwitcherEnabled(enabled); } /** Sets whether this controller should listen to battery updates. */ public void setBatteryListening(boolean listening) { + if (isMigrationEnabled()) { + return; + } + if (listening == mBatteryListening) { return; } @@ -472,6 +479,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat /** Animate the keyguard status bar in. */ public void animateKeyguardStatusBarIn() { + if (isMigrationEnabled()) { + return; + } + mLogger.log(TAG, LogLevel.DEBUG, "animating status bar in"); if (mDisableStateTracker.isDisabled()) { // If our view is disabled, don't allow us to animate in. @@ -488,6 +499,10 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat /** Animate the keyguard status bar out. */ public void animateKeyguardStatusBarOut(long startDelay, long duration) { + if (isMigrationEnabled()) { + return; + } + mLogger.log(TAG, LogLevel.DEBUG, "animating status bar out"); ValueAnimator anim = ValueAnimator.ofFloat(mView.getAlpha(), 0f); anim.addUpdateListener(mAnimatorUpdateListener); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt index c63ef9e5e012..6988e211855b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/binder/KeyguardStatusBarViewBinder.kt @@ -22,6 +22,8 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.phone.KeyguardStatusBarView import com.android.systemui.statusbar.ui.viewmodel.KeyguardStatusBarViewModel +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.launch /** Binds [KeyguardStatusBarViewModel] to [KeyguardStatusBarView]. */ object KeyguardStatusBarViewBinder { @@ -32,8 +34,18 @@ object KeyguardStatusBarViewBinder { ) { view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.isVisible.collect { isVisible -> - view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + launch { + viewModel.isVisible.collect { isVisible -> + view.visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + } + } + + launch { viewModel.isBatteryCharging.collect { view.onBatteryChargingChanged(it) } } + + launch { + viewModel.isKeyguardUserSwitcherEnabled.distinctUntilChanged().collect { + view.setKeyguardUserSwitcherEnabled(it) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt index ddfed8795fcf..5da01e23e268 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModel.kt @@ -16,12 +16,18 @@ package com.android.systemui.statusbar.ui.viewmodel +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.shared.model.StatusBarState +import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor +import com.android.systemui.statusbar.policy.BatteryController +import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback import javax.inject.Inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine @@ -41,6 +47,8 @@ class KeyguardStatusBarViewModel constructor( @Application scope: CoroutineScope, keyguardInteractor: KeyguardInteractor, + keyguardStatusBarInteractor: KeyguardStatusBarInteractor, + batteryController: BatteryController, ) { /** True if this view should be visible and false otherwise. */ val isVisible: StateFlow<Boolean> = @@ -51,4 +59,26 @@ constructor( !isDozing && statusBarState == StatusBarState.KEYGUARD } .stateIn(scope, SharingStarted.WhileSubscribed(), false) + + /** True if the device's battery is currently charging and false otherwise. */ + // Note: Never make this an eagerly-started state flow so that the callback is removed when the + // keyguard status bar view isn't attached. + val isBatteryCharging: Flow<Boolean> = conflatedCallbackFlow { + val callback = + object : BatteryStateChangeCallback { + override fun onBatteryLevelChanged( + level: Int, + pluggedIn: Boolean, + charging: Boolean, + ) { + trySend(charging) + } + } + batteryController.addCallback(callback) + awaitClose { batteryController.removeCallback(callback) } + } + + /** True if we can show the user switcher on keyguard and false otherwise. */ + val isKeyguardUserSwitcherEnabled: Flow<Boolean> = + keyguardStatusBarInteractor.isKeyguardUserSwitcherEnabled } diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt index 18ae1070e1bb..71352eff026c 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepositoryModule.kt @@ -23,4 +23,6 @@ import dagger.Module @Module interface UserRepositoryModule { @Binds fun bindRepository(impl: UserRepositoryImpl): UserRepository + + @Binds fun userSwitcherRepository(impl: UserSwitcherRepositoryImpl): UserSwitcherRepository } diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/UserSwitcherRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt index 5fa75ad68165..dc7fadd5eb14 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/UserSwitcherRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserSwitcherRepository.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.qs.footer.data.repository +package com.android.systemui.user.data.repository import android.content.Context import android.graphics.drawable.Drawable @@ -22,7 +22,6 @@ import android.os.Handler import android.os.UserManager import android.provider.Settings.Global.USER_SWITCHER_ENABLED import com.android.keyguard.KeyguardUpdateMonitor -import com.android.systemui.res.R import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton @@ -30,6 +29,7 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.SettingObserver import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel +import com.android.systemui.res.R import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.UserInfoController import com.android.systemui.statusbar.policy.UserSwitcherController @@ -48,6 +48,9 @@ import kotlinx.coroutines.withContext interface UserSwitcherRepository { /** The current [UserSwitcherStatusModel]. */ val userSwitcherStatus: Flow<UserSwitcherStatusModel> + + /** Whether the user switcher is currently enabled. */ + val isEnabled: Flow<Boolean> } @SysUISingleton @@ -66,8 +69,7 @@ constructor( private val showUserSwitcherForSingleUser = context.resources.getBoolean(R.bool.qs_show_user_switcher_for_single_user) - /** Whether the user switcher is currently enabled. */ - private val isEnabled: Flow<Boolean> = conflatedCallbackFlow { + override val isEnabled: Flow<Boolean> = conflatedCallbackFlow { suspend fun updateState() { trySendWithFailureLogging(isUserSwitcherEnabled(), TAG) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt new file mode 100644 index 000000000000..f1e6a053643f --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/FakeKeyguardStatusBarRepository.kt @@ -0,0 +1,23 @@ +/* + * 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.systemui.statusbar.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeKeyguardStatusBarRepository : KeyguardStatusBarRepository { + override val isKeyguardUserSwitcherEnabled = MutableStateFlow(false) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt new file mode 100644 index 000000000000..b1c994c2374e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/data/repository/KeyguardStatusBarRepositoryImplTest.kt @@ -0,0 +1,130 @@ +/* + * 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.systemui.statusbar.data.repository + +import androidx.test.filters.SmallTest +import com.android.internal.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.user.data.repository.FakeUserSwitcherRepository +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.mockito.Mockito.verify + +@SmallTest +class KeyguardStatusBarRepositoryImplTest : SysuiTestCase() { + private val testScope = TestScope() + private val configurationController = mock<ConfigurationController>() + private val userSwitcherRepository = FakeUserSwitcherRepository() + + val underTest = + KeyguardStatusBarRepositoryImpl( + context, + configurationController, + userSwitcherRepository, + ) + + private val configurationListener: ConfigurationController.ConfigurationListener + get() { + val captor = argumentCaptor<ConfigurationController.ConfigurationListener>() + verify(configurationController).addCallback(capture(captor)) + return captor.value + } + + @Test + fun isKeyguardUserSwitcherEnabled_switcherNotEnabled_false() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + + userSwitcherRepository.isEnabled.value = false + + assertThat(latest).isFalse() + } + + @Test + fun isKeyguardUserSwitcherEnabled_keyguardConfigNotEnabled_false() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + userSwitcherRepository.isEnabled.value = true + + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, false) + + assertThat(latest).isFalse() + } + + @Test + fun isKeyguardUserSwitcherEnabled_switchEnabledAndKeyguardConfigEnabled_true() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + + userSwitcherRepository.isEnabled.value = true + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, true) + + assertThat(latest).isTrue() + } + + @Test + fun isKeyguardUserSwitcherEnabled_refetchedOnSmallestWidthChanged() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + userSwitcherRepository.isEnabled.value = true + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, true) + assertThat(latest).isTrue() + + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, false) + configurationListener.onSmallestScreenWidthChanged() + + assertThat(latest).isFalse() + } + + @Test + fun isKeyguardUserSwitcherEnabled_refetchedOnDensityChanged() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + userSwitcherRepository.isEnabled.value = true + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, true) + assertThat(latest).isTrue() + + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, false) + configurationListener.onDensityOrFontScaleChanged() + + assertThat(latest).isFalse() + } + + @Test + fun isKeyguardUserSwitcherEnabled_refetchedOnEnabledChanged() = + testScope.runTest { + val latest by collectLastValue(underTest.isKeyguardUserSwitcherEnabled) + + userSwitcherRepository.isEnabled.value = false + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, true) + assertThat(latest).isFalse() + + // WHEN the switcher becomes enabled but the keyguard switcher becomes disabled + context.orCreateTestableResources.addOverride(R.bool.config_keyguardUserSwitcher, false) + userSwitcherRepository.isEnabled.value = true + + // THEN the value is still false because the keyguard config is refetched + assertThat(latest).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index c0d248ea15e7..648438997166 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -64,12 +64,13 @@ import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.power.domain.interactor.PowerInteractorFactory; import com.android.systemui.res.R; import com.android.systemui.scene.SceneTestUtils; -import com.android.systemui.scene.shared.flag.FakeSceneContainerFlags; import com.android.systemui.shade.ShadeViewStateProvider; import com.android.systemui.shade.data.repository.FakeShadeRepository; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository; +import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -156,7 +157,6 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { public void setup() throws Exception { mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, false); mShadeViewStateProvider = new TestShadeViewStateProvider(); - mShadeViewStateProvider = new TestShadeViewStateProvider(); MockitoAnnotations.initMocks(this); @@ -176,7 +176,9 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mViewModel = new KeyguardStatusBarViewModel( mTestScope.getBackgroundScope(), - mKeyguardInteractor); + mKeyguardInteractor, + new KeyguardStatusBarInteractor(new FakeKeyguardStatusBarRepository()), + mBatteryController); allowTestableLooperAsMainThread(); TestableLooper.get(this).runWithLooper(() -> { @@ -320,6 +322,15 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { } @Test + public void setBatteryListening_true_flagOn_callbackNotAdded() { + mFeatureFlags.set(Flags.MIGRATE_KEYGUARD_STATUS_BAR_VIEW, true); + + mController.setBatteryListening(true); + + verify(mBatteryController, never()).addCallback(any()); + } + + @Test public void updateTopClipping_viewClippingUpdated() { int viewTop = 20; mKeyguardStatusBarView.setTop(viewTop); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt index f4078d59bbdb..1bc346de1568 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt @@ -29,13 +29,23 @@ import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.scene.SceneTestUtils import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.statusbar.CommandQueue +import com.android.systemui.statusbar.data.repository.FakeKeyguardStatusBarRepository +import com.android.systemui.statusbar.domain.interactor.KeyguardStatusBarInteractor +import com.android.systemui.statusbar.policy.BatteryController +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test +import org.mockito.Mockito.verify @SmallTest +@OptIn(ExperimentalCoroutinesApi::class) class KeyguardStatusBarViewModelTest : SysuiTestCase() { private val testScope = TestScope() private val sceneTestUtils = SceneTestUtils(this) @@ -54,11 +64,18 @@ class KeyguardStatusBarViewModelTest : SysuiTestCase() { ) { sceneTestUtils.sceneInteractor() } + private val keyguardStatusBarInteractor = + KeyguardStatusBarInteractor( + FakeKeyguardStatusBarRepository(), + ) + private val batteryController = mock<BatteryController>() private val underTest = KeyguardStatusBarViewModel( testScope.backgroundScope, keyguardInteractor, + keyguardStatusBarInteractor, + batteryController, ) @Test @@ -102,4 +119,46 @@ class KeyguardStatusBarViewModelTest : SysuiTestCase() { assertThat(latest).isTrue() } + + @Test + fun isBatteryCharging_matchesCallback() = + testScope.runTest { + val latest by collectLastValue(underTest.isBatteryCharging) + runCurrent() + + val captor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() + verify(batteryController).addCallback(capture(captor)) + val callback = captor.value + + callback.onBatteryLevelChanged( + /* level= */ 2, + /* pluggedIn= */ false, + /* charging= */ true, + ) + + assertThat(latest).isTrue() + + callback.onBatteryLevelChanged( + /* level= */ 2, + /* pluggedIn= */ true, + /* charging= */ false, + ) + + assertThat(latest).isFalse() + } + + @Test + fun isBatteryCharging_unregistersWhenNotListening() = + testScope.runTest { + val job = underTest.isBatteryCharging.launchIn(this) + runCurrent() + + val captor = argumentCaptor<BatteryController.BatteryStateChangeCallback>() + verify(batteryController).addCallback(capture(captor)) + + job.cancel() + runCurrent() + + verify(batteryController).removeCallback(captor.value) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserSwitcherRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserSwitcherRepository.kt new file mode 100644 index 000000000000..758fe93a658c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/FakeUserSwitcherRepository.kt @@ -0,0 +1,26 @@ +/* + * 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.systemui.user.data.repository + +import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeUserSwitcherRepository : UserSwitcherRepository { + override val isEnabled = MutableStateFlow(false) + override val userSwitcherStatus = + MutableStateFlow<UserSwitcherStatusModel>(UserSwitcherStatusModel.Disabled) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt index 1a893f8c523c..bf77b1a050cd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/footer/FooterActionsTestUtils.kt @@ -36,8 +36,6 @@ import com.android.systemui.qs.FgsManagerController import com.android.systemui.qs.QSSecurityFooterUtils import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepository import com.android.systemui.qs.footer.data.repository.ForegroundServicesRepositoryImpl -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepository -import com.android.systemui.qs.footer.data.repository.UserSwitcherRepositoryImpl import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractorImpl import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel @@ -51,6 +49,8 @@ import com.android.systemui.statusbar.policy.FakeUserInfoController import com.android.systemui.statusbar.policy.SecurityController import com.android.systemui.statusbar.policy.UserInfoController import com.android.systemui.statusbar.policy.UserSwitcherController +import com.android.systemui.user.data.repository.UserSwitcherRepository +import com.android.systemui.user.data.repository.UserSwitcherRepositoryImpl import com.android.systemui.user.domain.interactor.UserInteractor import com.android.systemui.util.mockito.mock import com.android.systemui.util.settings.FakeSettings |