diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/model/Position.kt b/packages/SystemUI/src/com/android/systemui/common/domain/model/Position.kt new file mode 100644 index 0000000000000..f697c0ab3b226 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/common/domain/model/Position.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 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.common.domain.model + +import com.android.systemui.common.data.model.Position as DataLayerPosition + +/** Models a two-dimensional position */ +data class Position( + val x: Int, + val y: Int, +) { + companion object { + fun DataLayerPosition.toDomainLayer(): Position { + return Position( + x = x, + y = y, + ) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 5b2d88b53c220..4ff008ffb33ae 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -43,6 +43,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; +import com.android.systemui.keyguard.domain.usecase.KeyguardUseCaseModule; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -70,6 +71,7 @@ import dagger.Provides; includes = { FalsingModule.class, KeyguardRepositoryModule.class, + KeyguardUseCaseModule.class, }) public class KeyguardModule { /** diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/KeyguardUseCaseModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/KeyguardUseCaseModule.kt new file mode 100644 index 0000000000000..c44c2c9901a13 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/KeyguardUseCaseModule.kt @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import dagger.Binds +import dagger.Module + +@Module +interface KeyguardUseCaseModule { + + @Binds + fun launchQuickAffordance( + impl: LaunchKeyguardQuickAffordanceUseCaseImpl + ): LaunchKeyguardQuickAffordanceUseCase +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/LaunchKeyguardQuickAffordanceUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/LaunchKeyguardQuickAffordanceUseCase.kt new file mode 100644 index 0000000000000..3d60399cf5227 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/LaunchKeyguardQuickAffordanceUseCase.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import android.content.Intent +import com.android.internal.widget.LockPatternUtils +import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.StrongAuthFlags +import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.KeyguardStateController +import javax.inject.Inject + +/** Defines interface for classes that can launch a quick affordance. */ +interface LaunchKeyguardQuickAffordanceUseCase { + operator fun invoke( + intent: Intent, + canShowWhileLocked: Boolean, + animationController: ActivityLaunchAnimator.Controller?, + ) +} + +/** Real implementation of [LaunchKeyguardQuickAffordanceUseCase] */ +class LaunchKeyguardQuickAffordanceUseCaseImpl +@Inject +constructor( + private val lockPatternUtils: LockPatternUtils, + private val keyguardStateController: KeyguardStateController, + private val userTracker: UserTracker, + private val activityStarter: ActivityStarter, +) : LaunchKeyguardQuickAffordanceUseCase { + override operator fun invoke( + intent: Intent, + canShowWhileLocked: Boolean, + animationController: ActivityLaunchAnimator.Controller?, + ) { + @StrongAuthFlags + val strongAuthFlags = + lockPatternUtils.getStrongAuthForUser(userTracker.userHandle.identifier) + val needsToUnlockFirst = + when { + strongAuthFlags == + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT -> true + !canShowWhileLocked && !keyguardStateController.isUnlocked -> true + else -> false + } + if (needsToUnlockFirst) { + activityStarter.postStartActivityDismissingKeyguard( + intent, + 0 /* delay */, + animationController + ) + } else { + activityStarter.startActivity( + intent, + true /* dismissShade */, + animationController, + true /* showOverLockscreenWhenLocked */, + ) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveAnimateBottomAreaTransitionsUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveAnimateBottomAreaTransitionsUseCase.kt new file mode 100644 index 0000000000000..ca37727072f02 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveAnimateBottomAreaTransitionsUseCase.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Use-case for observing whether doze state transitions should animate the bottom area */ +class ObserveAnimateBottomAreaTransitionsUseCase +@Inject +constructor( + private val repository: KeyguardRepository, +) { + operator fun invoke(): Flow { + return repository.animateBottomAreaDozingTransitions + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveBottomAreaAlphaUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveBottomAreaAlphaUseCase.kt new file mode 100644 index 0000000000000..151b704a017bd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveBottomAreaAlphaUseCase.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Use-case for observing the alpha of the bottom area */ +class ObserveBottomAreaAlphaUseCase +@Inject +constructor( + private val repository: KeyguardRepository, +) { + operator fun invoke(): Flow { + return repository.bottomAreaAlpha + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveClockPositionUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveClockPositionUseCase.kt new file mode 100644 index 0000000000000..02c573726db04 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveClockPositionUseCase.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.common.domain.model.Position +import com.android.systemui.common.domain.model.Position.Companion.toDomainLayer +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +/** Use-case for observing the position of the clock. */ +class ObserveClockPositionUseCase +@Inject +constructor( + private val repository: KeyguardRepository, +) { + operator fun invoke(): Flow { + return repository.clockPosition.map { it.toDomainLayer() } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveDozeAmountUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveDozeAmountUseCase.kt new file mode 100644 index 0000000000000..56d61822a1210 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveDozeAmountUseCase.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Use-case for observing the amount of doze the system is in. */ +class ObserveDozeAmountUseCase +@Inject +constructor( + private val repository: KeyguardRepository, +) { + operator fun invoke(): Flow { + return repository.dozeAmount + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveIsDozingUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveIsDozingUseCase.kt new file mode 100644 index 0000000000000..1d241d90ba5f0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveIsDozingUseCase.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** Use-case for observing whether we are dozing. */ +class ObserveIsDozingUseCase +@Inject +constructor( + private val repository: KeyguardRepository, +) { + operator fun invoke(): Flow { + return repository.isDozing + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt new file mode 100644 index 0000000000000..aac3d752ce706 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository +import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel +import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +/** Use-case for observing the model of a quick affordance in the keyguard. */ +class ObserveKeyguardQuickAffordanceUseCase +@Inject +constructor( + private val repository: KeyguardQuickAffordanceRepository, + private val isDozingUseCase: ObserveIsDozingUseCase, + private val dozeAmountUseCase: ObserveDozeAmountUseCase, +) { + operator fun invoke( + position: KeyguardQuickAffordancePosition + ): Flow { + return combine( + repository.affordance(position), + isDozingUseCase(), + dozeAmountUseCase(), + ) { affordance, isDozing, dozeAmount -> + if (!isDozing && dozeAmount == 0f) { + affordance + } else { + KeyguardQuickAffordanceModel.Hidden + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/OnKeyguardQuickAffordanceClickedUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/OnKeyguardQuickAffordanceClickedUseCase.kt new file mode 100644 index 0000000000000..f8db90f025402 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/OnKeyguardQuickAffordanceClickedUseCase.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigs +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig +import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult +import javax.inject.Inject +import kotlin.reflect.KClass + +/** Use-case for handling a click on a keyguard quick affordance (e.g. bottom button). */ +class OnKeyguardQuickAffordanceClickedUseCase +@Inject +constructor( + private val configs: KeyguardQuickAffordanceConfigs, + private val launchAffordanceUseCase: LaunchKeyguardQuickAffordanceUseCase, +) { + operator fun invoke( + configKey: KClass<*>, + animationController: ActivityLaunchAnimator.Controller?, + ) { + @Suppress("UNCHECKED_CAST") + val config = configs.get(configKey as KClass) + when (val result = config.onQuickAffordanceClicked(animationController)) { + is OnClickedResult.StartActivity -> + launchAffordanceUseCase( + intent = result.intent, + canShowWhileLocked = result.canShowWhileLocked, + animationController = animationController + ) + is OnClickedResult.Handled -> Unit + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/SetClockPositionUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/SetClockPositionUseCase.kt new file mode 100644 index 0000000000000..8f746e5765afd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/SetClockPositionUseCase.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import javax.inject.Inject + +/** Use-case for setting the updated clock position. */ +class SetClockPositionUseCase +@Inject +constructor( + private val keyguardRepository: KeyguardRepository, +) { + operator fun invoke(x: Int, y: Int) { + keyguardRepository.setClockPosition(x, y) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/SetKeyguardBottomAreaAlphaUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/SetKeyguardBottomAreaAlphaUseCase.kt new file mode 100644 index 0000000000000..90be1ecded507 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/SetKeyguardBottomAreaAlphaUseCase.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import javax.inject.Inject + +/** Use-case for setting the alpha that the keyguard bottom area should use */ +class SetKeyguardBottomAreaAlphaUseCase +@Inject +constructor( + private val repository: KeyguardRepository, +) { + operator fun invoke(alpha: Float) { + repository.setBottomAreaAlpha(alpha) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/SetKeyguardBottomAreaAnimateDozingTransitionsUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/SetKeyguardBottomAreaAnimateDozingTransitionsUseCase.kt new file mode 100644 index 0000000000000..007780a6860a1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/SetKeyguardBottomAreaAnimateDozingTransitionsUseCase.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import javax.inject.Inject + +/** + * Use-case for setting whether the keyguard bottom area should animate the next doze transitions + */ +class SetKeyguardBottomAreaAnimateDozingTransitionsUseCase +@Inject +constructor( + private val repository: KeyguardRepository, +) { + operator fun invoke(animate: Boolean) { + repository.setAnimateDozingTransitions(animate) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/FakeLaunchKeyguardQuickAffordanceUseCase.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/FakeLaunchKeyguardQuickAffordanceUseCase.kt new file mode 100644 index 0000000000000..ba0c31ffa9c92 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/FakeLaunchKeyguardQuickAffordanceUseCase.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import android.content.Intent +import com.android.systemui.animation.ActivityLaunchAnimator + +/** Fake implementation of [LaunchKeyguardQuickAffordanceUseCase], for tests. */ +class FakeLaunchKeyguardQuickAffordanceUseCase : LaunchKeyguardQuickAffordanceUseCase { + + data class Invocation( + val intent: Intent, + val canShowWhileLocked: Boolean, + val animationController: ActivityLaunchAnimator.Controller? + ) + + private val _invocations = mutableListOf() + val invocations: List = _invocations + + override fun invoke( + intent: Intent, + canShowWhileLocked: Boolean, + animationController: ActivityLaunchAnimator.Controller? + ) { + _invocations.add( + Invocation( + intent = intent, + canShowWhileLocked = canShowWhileLocked, + animationController = animationController, + ) + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/LaunchKeyguardQuickAffordanceUseCaseImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/LaunchKeyguardQuickAffordanceUseCaseImplTest.kt new file mode 100644 index 0000000000000..b3c1ae0106cf3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/LaunchKeyguardQuickAffordanceUseCaseImplTest.kt @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import android.content.Intent +import androidx.test.filters.SmallTest +import com.android.internal.widget.LockPatternUtils +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import org.junit.runners.Parameterized.Parameter +import org.junit.runners.Parameterized.Parameters +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(Parameterized::class) +class LaunchKeyguardQuickAffordanceUseCaseImplTest : SysuiTestCase() { + + companion object { + private val INTENT = Intent("some.intent.action") + + @Parameters( + name = + "needStrongAuthAfterBoot={0}, canShowWhileLocked={1}," + + " keyguardIsUnlocked={2}, needsToUnlockFirst={3}" + ) + @JvmStatic + fun data() = + listOf( + arrayOf( + /* needStrongAuthAfterBoot= */ false, + /* canShowWhileLocked= */ false, + /* keyguardIsUnlocked= */ false, + /* needsToUnlockFirst= */ true, + ), + arrayOf( + /* needStrongAuthAfterBoot= */ false, + /* canShowWhileLocked= */ false, + /* keyguardIsUnlocked= */ true, + /* needsToUnlockFirst= */ false, + ), + arrayOf( + /* needStrongAuthAfterBoot= */ false, + /* canShowWhileLocked= */ true, + /* keyguardIsUnlocked= */ false, + /* needsToUnlockFirst= */ false, + ), + arrayOf( + /* needStrongAuthAfterBoot= */ false, + /* canShowWhileLocked= */ true, + /* keyguardIsUnlocked= */ true, + /* needsToUnlockFirst= */ false, + ), + arrayOf( + /* needStrongAuthAfterBoot= */ true, + /* canShowWhileLocked= */ false, + /* keyguardIsUnlocked= */ false, + /* needsToUnlockFirst= */ true, + ), + arrayOf( + /* needStrongAuthAfterBoot= */ true, + /* canShowWhileLocked= */ false, + /* keyguardIsUnlocked= */ true, + /* needsToUnlockFirst= */ true, + ), + arrayOf( + /* needStrongAuthAfterBoot= */ true, + /* canShowWhileLocked= */ true, + /* keyguardIsUnlocked= */ false, + /* needsToUnlockFirst= */ true, + ), + arrayOf( + /* needStrongAuthAfterBoot= */ true, + /* canShowWhileLocked= */ true, + /* keyguardIsUnlocked= */ true, + /* needsToUnlockFirst= */ true, + ), + ) + } + + @Mock private lateinit var lockPatternUtils: LockPatternUtils + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var activityStarter: ActivityStarter + @Mock private lateinit var animationController: ActivityLaunchAnimator.Controller + + private lateinit var underTest: LaunchKeyguardQuickAffordanceUseCase + + @JvmField @Parameter(0) var needStrongAuthAfterBoot: Boolean = false + @JvmField @Parameter(1) var canShowWhileLocked: Boolean = false + @JvmField @Parameter(2) var keyguardIsUnlocked: Boolean = false + @JvmField @Parameter(3) var needsToUnlockFirst: Boolean = false + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + underTest = + LaunchKeyguardQuickAffordanceUseCaseImpl( + lockPatternUtils = lockPatternUtils, + keyguardStateController = keyguardStateController, + userTracker = userTracker, + activityStarter = activityStarter, + ) + } + + @Test + fun invoke() { + setUpMocks( + needStrongAuthAfterBoot = needStrongAuthAfterBoot, + keyguardIsUnlocked = keyguardIsUnlocked, + ) + + underTest( + intent = INTENT, + canShowWhileLocked = canShowWhileLocked, + animationController = animationController, + ) + + if (needsToUnlockFirst) { + verify(activityStarter) + .postStartActivityDismissingKeyguard( + INTENT, + /* delay= */ 0, + animationController, + ) + } else { + verify(activityStarter) + .startActivity( + INTENT, + /* dismissShade= */ true, + animationController, + /* showOverLockscreenWhenLocked= */ true, + ) + } + } + + private fun setUpMocks( + needStrongAuthAfterBoot: Boolean = true, + keyguardIsUnlocked: Boolean = false, + ) { + whenever(userTracker.userHandle).thenReturn(mock()) + whenever(lockPatternUtils.getStrongAuthForUser(any())) + .thenReturn( + if (needStrongAuthAfterBoot) { + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT + } else { + LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED + } + ) + whenever(keyguardStateController.isUnlocked).thenReturn(keyguardIsUnlocked) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt new file mode 100644 index 0000000000000..a60657c594c96 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2022 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.keyguard.domain.usecase + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.containeddrawable.ContainedDrawable +import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig +import com.android.systemui.keyguard.data.repository.FakeKeyguardQuickAffordanceRepository +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel +import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@SmallTest +@RunWith(JUnit4::class) +class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() { + + private lateinit var underTest: ObserveKeyguardQuickAffordanceUseCase + + private lateinit var repository: FakeKeyguardRepository + private lateinit var quickAffordanceRepository: FakeKeyguardQuickAffordanceRepository + private lateinit var isDozingUseCase: ObserveIsDozingUseCase + private lateinit var dozeAmountUseCase: ObserveDozeAmountUseCase + + @Before + fun setUp() { + repository = FakeKeyguardRepository() + isDozingUseCase = ObserveIsDozingUseCase(repository) + dozeAmountUseCase = ObserveDozeAmountUseCase(repository) + quickAffordanceRepository = FakeKeyguardQuickAffordanceRepository() + + underTest = + ObserveKeyguardQuickAffordanceUseCase( + repository = quickAffordanceRepository, + isDozingUseCase = isDozingUseCase, + dozeAmountUseCase = dozeAmountUseCase, + ) + } + + @Test + fun `invoke - affordance is visible`() = runBlockingTest { + val configKey = HomeControlsKeyguardQuickAffordanceConfig::class + val model = + KeyguardQuickAffordanceModel.Visible( + configKey = configKey, + icon = ICON, + contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID, + ) + quickAffordanceRepository.setModel( + KeyguardQuickAffordancePosition.BOTTOM_END, + model, + ) + + var latest: KeyguardQuickAffordanceModel? = null + val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_END) + .onEach { latest = it } + .launchIn(this) + + assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) + val visibleModel = latest as KeyguardQuickAffordanceModel.Visible + assertThat(visibleModel.configKey).isEqualTo(configKey) + assertThat(visibleModel.icon).isEqualTo(ICON) + assertThat(visibleModel.contentDescriptionResourceId) + .isEqualTo(CONTENT_DESCRIPTION_RESOURCE_ID) + job.cancel() + } + + @Test + fun `invoke - affordance not visible while dozing`() = runBlockingTest { + repository.setDozing(true) + val configKey = HomeControlsKeyguardQuickAffordanceConfig::class + val model = + KeyguardQuickAffordanceModel.Visible( + configKey = configKey, + icon = ICON, + contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID, + ) + quickAffordanceRepository.setModel( + KeyguardQuickAffordancePosition.BOTTOM_END, + model, + ) + + var latest: KeyguardQuickAffordanceModel? = null + val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_END) + .onEach { latest = it } + .launchIn(this) + assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden) + job.cancel() + } + + @Test + fun `invoke - affordance not visible doze amount is not 0`() = runBlockingTest { + repository.setDozeAmount(0.3f) + val configKey = HomeControlsKeyguardQuickAffordanceConfig::class + val model = + KeyguardQuickAffordanceModel.Visible( + configKey = configKey, + icon = ICON, + contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID, + ) + quickAffordanceRepository.setModel( + KeyguardQuickAffordancePosition.BOTTOM_END, + model, + ) + + var latest: KeyguardQuickAffordanceModel? = null + val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_END) + .onEach { latest = it } + .launchIn(this) + assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden) + job.cancel() + } + + @Test + fun `invoke - affordance is none`() = runBlockingTest { + quickAffordanceRepository.setModel( + KeyguardQuickAffordancePosition.BOTTOM_START, + KeyguardQuickAffordanceModel.Hidden, + ) + + var latest: KeyguardQuickAffordanceModel? = null + val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_START) + .onEach { latest = it } + .launchIn(this) + assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden) + job.cancel() + } + + companion object { + private val ICON: ContainedDrawable = mock() + private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337 + } +}