• 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.shade.ui.viewmodel
18 
19 import androidx.lifecycle.LifecycleOwner
20 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
21 import com.android.systemui.lifecycle.ExclusiveActivatable
22 import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
23 import com.android.systemui.qs.FooterActionsController
24 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
25 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
26 import com.android.systemui.scene.domain.interactor.SceneInteractor
27 import com.android.systemui.scene.shared.model.Scenes
28 import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorViewModel
29 import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
30 import com.android.systemui.shade.shared.model.ShadeMode
31 import com.android.systemui.statusbar.disableflags.domain.interactor.DisableFlagsInteractor
32 import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
33 import dagger.assisted.AssistedFactory
34 import dagger.assisted.AssistedInject
35 import java.util.concurrent.atomic.AtomicBoolean
36 import kotlinx.coroutines.awaitCancellation
37 import kotlinx.coroutines.coroutineScope
38 import kotlinx.coroutines.flow.Flow
39 import kotlinx.coroutines.flow.MutableStateFlow
40 import kotlinx.coroutines.flow.StateFlow
41 import kotlinx.coroutines.flow.asStateFlow
42 import kotlinx.coroutines.flow.launchIn
43 import kotlinx.coroutines.flow.map
44 import kotlinx.coroutines.flow.onEach
45 
46 /**
47  * Models UI state used to render the content of the shade scene.
48  *
49  * Different from [ShadeUserActionsViewModel], which only models user actions that can be performed
50  * to navigate to other scenes.
51  */
52 class ShadeSceneContentViewModel
53 @AssistedInject
54 constructor(
55     val qsSceneAdapter: QSSceneAdapter,
56     val shadeHeaderViewModelFactory: ShadeHeaderViewModel.Factory,
57     val brightnessMirrorViewModelFactory: BrightnessMirrorViewModel.Factory,
58     val mediaCarouselInteractor: MediaCarouselInteractor,
59     shadeModeInteractor: ShadeModeInteractor,
60     private val disableFlagsInteractor: DisableFlagsInteractor,
61     private val footerActionsViewModelFactory: FooterActionsViewModel.Factory,
62     private val footerActionsController: FooterActionsController,
63     private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
64     private val deviceEntryInteractor: DeviceEntryInteractor,
65     private val sceneInteractor: SceneInteractor,
66 ) : ExclusiveActivatable() {
67 
68     val shadeMode: StateFlow<ShadeMode> = shadeModeInteractor.shadeMode
69 
70     private val _isEmptySpaceClickable =
71         MutableStateFlow(!deviceEntryInteractor.isDeviceEntered.value)
72     /** Whether clicking on the empty area of the shade does something */
73     val isEmptySpaceClickable: StateFlow<Boolean> = _isEmptySpaceClickable.asStateFlow()
74 
75     val isMediaVisible: StateFlow<Boolean> = mediaCarouselInteractor.hasActiveMediaOrRecommendation
76 
77     private val _isQsEnabled =
78         MutableStateFlow(!disableFlagsInteractor.disableFlags.value.isQuickSettingsEnabled())
79     val isQsEnabled: StateFlow<Boolean> = _isQsEnabled.asStateFlow()
80 
81     private val footerActionsControllerInitialized = AtomicBoolean(false)
82 
83     override suspend fun onActivated(): Nothing = coroutineScope {
84         deviceEntryInteractor.isDeviceEntered
85             .onEach { isDeviceEntered -> _isEmptySpaceClickable.value = !isDeviceEntered }
86             .launchIn(this)
87         disableFlagsInteractor.disableFlags
88             .map { it.isQuickSettingsEnabled() }
89             .onEach { _isQsEnabled.value = it }
90             .launchIn(this)
91         awaitCancellation()
92     }
93 
94     /**
95      * Amount of X-axis translation to apply to various elements as the unfolded foldable is folded
96      * slightly, in pixels.
97      */
98     fun unfoldTranslationX(isOnStartSide: Boolean): Flow<Float> {
99         return unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide)
100     }
101 
102     fun getFooterActionsViewModel(lifecycleOwner: LifecycleOwner): FooterActionsViewModel {
103         if (footerActionsControllerInitialized.compareAndSet(false, true)) {
104             footerActionsController.init()
105         }
106         return footerActionsViewModelFactory.create(lifecycleOwner)
107     }
108 
109     /** Notifies that the empty space in the shade has been clicked. */
110     fun onEmptySpaceClicked() {
111         if (!isEmptySpaceClickable.value) {
112             return
113         }
114 
115         sceneInteractor.changeScene(Scenes.Lockscreen, "Shade empty space clicked.")
116     }
117 
118     @AssistedFactory
119     interface Factory {
120         fun create(): ShadeSceneContentViewModel
121     }
122 }
123