• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.communal.domain.interactor
18 
19 import android.provider.Settings
20 import com.android.systemui.communal.data.repository.CommunalTutorialRepository
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Application
23 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
24 import com.android.systemui.log.dagger.CommunalTableLog
25 import com.android.systemui.log.table.TableLogBuffer
26 import com.android.systemui.log.table.logDiffsForTable
27 import javax.inject.Inject
28 import kotlinx.coroutines.CoroutineScope
29 import kotlinx.coroutines.flow.Flow
30 import kotlinx.coroutines.flow.SharingStarted
31 import kotlinx.coroutines.flow.StateFlow
32 import kotlinx.coroutines.flow.combine
33 import kotlinx.coroutines.flow.distinctUntilChanged
34 import kotlinx.coroutines.flow.emptyFlow
35 import kotlinx.coroutines.flow.filterNotNull
36 import kotlinx.coroutines.flow.flatMapLatest
37 import kotlinx.coroutines.flow.flowOf
38 import kotlinx.coroutines.flow.map
39 import kotlinx.coroutines.flow.stateIn
40 import kotlinx.coroutines.flow.transformWhile
41 import com.android.app.tracing.coroutines.launchTraced as launch
42 
43 /** Encapsulates business-logic related to communal tutorial state. */
44 @SysUISingleton
45 class CommunalTutorialInteractor
46 @Inject
47 constructor(
48     @Application private val scope: CoroutineScope,
49     private val communalTutorialRepository: CommunalTutorialRepository,
50     keyguardInteractor: KeyguardInteractor,
51     private val communalSettingsInteractor: CommunalSettingsInteractor,
52     communalInteractor: CommunalInteractor,
53     @CommunalTableLog tableLogBuffer: TableLogBuffer,
54 ) {
55     /** An observable for whether the tutorial is available. */
56     val isTutorialAvailable: StateFlow<Boolean> =
57         combine(
58                 communalInteractor.isCommunalAvailable,
59                 keyguardInteractor.isKeyguardVisible,
60                 communalTutorialRepository.tutorialSettingState,
61             ) { isCommunalAvailable, isKeyguardVisible, tutorialSettingState ->
62                 isCommunalAvailable &&
63                     isKeyguardVisible &&
64                     tutorialSettingState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
65             }
66             .logDiffsForTable(
67                 tableLogBuffer = tableLogBuffer,
68                 columnName = "isTutorialAvailable",
69                 initialValue = false,
70             )
71             .stateIn(
72                 scope = scope,
73                 started = SharingStarted.WhileSubscribed(),
74                 initialValue = false,
75             )
76 
77     /**
78      * A flow of the new tutorial state after transitioning. The new state will be calculated based
79      * on the current tutorial state and transition state as following:
80      * HUB_MODE_TUTORIAL_NOT_STARTED + communal scene -> HUB_MODE_TUTORIAL_STARTED
81      * HUB_MODE_TUTORIAL_STARTED + non-communal scene -> HUB_MODE_TUTORIAL_COMPLETED
82      * HUB_MODE_TUTORIAL_COMPLETED + any scene -> won't emit
83      */
84     private val tutorialStateToUpdate: Flow<Int> =
85         communalTutorialRepository.tutorialSettingState
86             .flatMapLatest { tutorialSettingState ->
87                 if (tutorialSettingState == Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) {
88                     return@flatMapLatest flowOf(null)
89                 }
90                 communalInteractor.isCommunalShowing.map { isCommunalShowing ->
91                     nextStateAfterTransition(
92                         tutorialSettingState,
93                         isCommunalShowing,
94                     )
95                 }
96             }
97             .filterNotNull()
98             .distinctUntilChanged()
99 
100     private fun nextStateAfterTransition(tutorialState: Int, isCommunalShowing: Boolean): Int? {
101         if (tutorialState == Settings.Secure.HUB_MODE_TUTORIAL_NOT_STARTED && isCommunalShowing) {
102             return Settings.Secure.HUB_MODE_TUTORIAL_STARTED
103         }
104         if (tutorialState == Settings.Secure.HUB_MODE_TUTORIAL_STARTED && !isCommunalShowing) {
105             return Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
106         }
107         return null
108     }
109 
110     private fun listenForTransitionToUpdateTutorialState() {
111         scope.launch {
112             communalSettingsInteractor.isCommunalEnabled
113                 .flatMapLatest { enabled ->
114                     if (!enabled) {
115                         emptyFlow()
116                     } else {
117                         tutorialStateToUpdate
118                     }
119                 }
120                 .transformWhile { tutorialState ->
121                     emit(tutorialState)
122                     tutorialState != Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED
123                 }
124                 .collect { tutorialState ->
125                     communalTutorialRepository.setTutorialState(tutorialState)
126                 }
127         }
128     }
129 
130     init {
131         listenForTransitionToUpdateTutorialState()
132     }
133 }
134