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.volume.panel.ui.viewmodel 18 19 import android.content.Context 20 import android.content.IntentFilter 21 import android.content.res.Resources 22 import com.android.systemui.broadcast.BroadcastDispatcher 23 import com.android.systemui.dagger.qualifiers.Application 24 import com.android.systemui.res.R 25 import com.android.systemui.statusbar.policy.ConfigurationController 26 import com.android.systemui.statusbar.policy.onConfigChanged 27 import com.android.systemui.volume.VolumePanelDialogReceiver 28 import com.android.systemui.volume.panel.dagger.VolumePanelComponent 29 import com.android.systemui.volume.panel.dagger.factory.VolumePanelComponentFactory 30 import com.android.systemui.volume.panel.domain.VolumePanelStartable 31 import com.android.systemui.volume.panel.domain.interactor.ComponentsInteractor 32 import com.android.systemui.volume.panel.domain.interactor.VolumePanelGlobalStateInteractor 33 import com.android.systemui.volume.panel.ui.composable.ComponentsFactory 34 import com.android.systemui.volume.panel.ui.layout.ComponentsLayout 35 import com.android.systemui.volume.panel.ui.layout.ComponentsLayoutManager 36 import javax.inject.Inject 37 import kotlinx.coroutines.CoroutineScope 38 import kotlinx.coroutines.flow.Flow 39 import kotlinx.coroutines.flow.SharingStarted 40 import kotlinx.coroutines.flow.StateFlow 41 import kotlinx.coroutines.flow.combine 42 import kotlinx.coroutines.flow.launchIn 43 import kotlinx.coroutines.flow.map 44 import kotlinx.coroutines.flow.onEach 45 import kotlinx.coroutines.flow.onStart 46 import kotlinx.coroutines.flow.shareIn 47 import kotlinx.coroutines.flow.stateIn 48 49 // Can't inject a constructor here because VolumePanelComponent provides this view model for its 50 // components. 51 class VolumePanelViewModel( 52 resources: Resources, 53 coroutineScope: CoroutineScope, 54 daggerComponentFactory: VolumePanelComponentFactory, 55 configurationController: ConfigurationController, 56 broadcastDispatcher: BroadcastDispatcher, 57 private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor, 58 ) { 59 60 private val volumePanelComponent: VolumePanelComponent = 61 daggerComponentFactory.create(this, coroutineScope) 62 63 private val scope: CoroutineScope 64 get() = volumePanelComponent.coroutineScope() 65 66 private val componentsInteractor: ComponentsInteractor 67 get() = volumePanelComponent.componentsInteractor() 68 69 private val componentsFactory: ComponentsFactory 70 get() = volumePanelComponent.componentsFactory() 71 72 private val componentsLayoutManager: ComponentsLayoutManager 73 get() = volumePanelComponent.componentsLayoutManager() 74 75 val volumePanelState: StateFlow<VolumePanelState> = 76 configurationController.onConfigChanged 77 .onStart { emit(resources.configuration) } 78 .map { configuration -> 79 VolumePanelState( 80 orientation = configuration.orientation, 81 isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen), 82 ) 83 } 84 .stateIn( 85 scope, 86 SharingStarted.Eagerly, 87 VolumePanelState( 88 orientation = resources.configuration.orientation, 89 isLargeScreen = resources.getBoolean(R.bool.volume_panel_is_large_screen) 90 ), 91 ) 92 val componentsLayout: Flow<ComponentsLayout> = 93 combine( 94 componentsInteractor.components, 95 volumePanelState, 96 ) { components, scope -> 97 val componentStates = 98 components.map { model -> 99 ComponentState( 100 model.key, 101 componentsFactory.createComponent(model.key), 102 model.isAvailable, 103 ) 104 } 105 componentsLayoutManager.layout(scope, componentStates) 106 } 107 .shareIn( 108 scope, 109 SharingStarted.Eagerly, 110 replay = 1, 111 ) 112 113 init { 114 volumePanelComponent.volumePanelStartables().onEach(VolumePanelStartable::start) 115 broadcastDispatcher 116 .broadcastFlow(IntentFilter(VolumePanelDialogReceiver.DISMISS_ACTION)) 117 .onEach { dismissPanel() } 118 .launchIn(scope) 119 } 120 121 fun dismissPanel() { 122 volumePanelGlobalStateInteractor.setVisible(false) 123 } 124 125 class Factory 126 @Inject 127 constructor( 128 @Application private val context: Context, 129 private val daggerComponentFactory: VolumePanelComponentFactory, 130 private val configurationController: ConfigurationController, 131 private val broadcastDispatcher: BroadcastDispatcher, 132 private val volumePanelGlobalStateInteractor: VolumePanelGlobalStateInteractor, 133 ) { 134 135 fun create(coroutineScope: CoroutineScope): VolumePanelViewModel { 136 return VolumePanelViewModel( 137 context.resources, 138 coroutineScope, 139 daggerComponentFactory, 140 configurationController, 141 broadcastDispatcher, 142 volumePanelGlobalStateInteractor, 143 ) 144 } 145 } 146 } 147