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