• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.qs.panels.ui.viewmodel
18 
19 import android.content.res.Configuration
20 import android.content.res.Resources
21 import androidx.compose.runtime.getValue
22 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
23 import com.android.systemui.lifecycle.ExclusiveActivatable
24 import com.android.systemui.lifecycle.Hydrator
25 import com.android.systemui.media.controls.ui.controller.MediaHostStatesManager
26 import com.android.systemui.media.controls.ui.controller.MediaLocation
27 import com.android.systemui.media.controls.ui.view.MediaHostState
28 import com.android.systemui.qs.composefragment.dagger.QSFragmentComposeModule
29 import com.android.systemui.shade.ShadeDisplayAware
30 import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
31 import com.android.systemui.shade.shared.model.ShadeMode
32 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
33 import dagger.assisted.Assisted
34 import dagger.assisted.AssistedFactory
35 import dagger.assisted.AssistedInject
36 import javax.inject.Named
37 import kotlinx.coroutines.channels.awaitClose
38 import kotlinx.coroutines.flow.map
39 import kotlinx.coroutines.flow.onStart
40 
41 /**
42  * Indicates whether a particular UMO in [LOCATION_QQS] or [LOCATION_QS] should currently show in a
43  * row with the tiles, based on its visibility and device configuration. If the player is not
44  * visible, it will never indicate that media should show in row.
45  */
46 class MediaInRowInLandscapeViewModel
47 @AssistedInject
48 constructor(
49     @ShadeDisplayAware resources: Resources,
50     configurationInteractor: ConfigurationInteractor,
51     shadeModeInteractor: ShadeModeInteractor,
52     private val mediaHostStatesManager: MediaHostStatesManager,
53     @Named(QSFragmentComposeModule.QS_USING_MEDIA_PLAYER) private val usingMedia: Boolean,
54     @Assisted @MediaLocation private val inLocation: Int,
55 ) : ExclusiveActivatable() {
56 
57     private val hydrator = Hydrator("MediaInRowInLanscapeViewModel - $inLocation")
58 
59     val shouldMediaShowInRow: Boolean
60         get() = usingMedia && inSingleShade && isLandscapeAndLong && isMediaVisible
61 
62     private val inSingleShade: Boolean by
63         hydrator.hydratedStateOf(
64             traceName = "inSingleShade",
65             initialValue = shadeModeInteractor.shadeMode.value == ShadeMode.Single,
<lambda>null66             source = shadeModeInteractor.shadeMode.map { it == ShadeMode.Single },
67         )
68 
69     private val isLandscapeAndLong: Boolean by
70         hydrator.hydratedStateOf(
71             traceName = "isLandscapeAndLong",
72             initialValue = resources.configuration.isLandscapeAndLong,
<lambda>null73             source = configurationInteractor.configurationValues.map { it.isLandscapeAndLong },
74         )
75 
76     private val isMediaVisible by
77         hydrator.hydratedStateOf(
78             traceName = "isMediaVisible",
79             initialValue = false,
80             source =
<lambda>null81                 conflatedCallbackFlow {
82                         val callback =
83                             object : MediaHostStatesManager.Callback {
84                                 override fun onHostStateChanged(
85                                     location: Int,
86                                     mediaHostState: MediaHostState,
87                                 ) {
88                                     if (location == inLocation) {
89                                         trySend(mediaHostState.visible)
90                                     }
91                                 }
92                             }
93                         mediaHostStatesManager.addCallback(callback)
94 
95                         awaitClose { mediaHostStatesManager.removeCallback(callback) }
96                     }
<lambda>null97                     .onStart {
98                         emit(
99                             mediaHostStatesManager.mediaHostStates.get(inLocation)?.visible ?: false
100                         )
101                     },
102         )
103 
onActivatednull104     override suspend fun onActivated(): Nothing {
105         hydrator.activate()
106     }
107 
108     @AssistedFactory
109     interface Factory {
createnull110         fun create(@MediaLocation inLocation: Int): MediaInRowInLandscapeViewModel
111     }
112 }
113 
114 private val Configuration.isLandscapeAndLong: Boolean
115     get() =
116         orientation == Configuration.ORIENTATION_LANDSCAPE &&
117             (screenLayout and Configuration.SCREENLAYOUT_LONG_MASK) ==
118                 Configuration.SCREENLAYOUT_LONG_YES
119