• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.media.controls.ui.controller
18 
19 import com.android.app.tracing.traceSection
20 import com.android.systemui.Dumpable
21 import com.android.systemui.Flags.mediaControlsUmoInflationInBackground
22 import com.android.systemui.dagger.SysUISingleton
23 import com.android.systemui.dump.DumpManager
24 import com.android.systemui.media.controls.ui.view.MediaHostState
25 import com.android.systemui.util.animation.MeasurementOutput
26 import java.io.PrintWriter
27 import javax.inject.Inject
28 
29 private val TAG = "MediaHostStatesManager"
30 
31 /**
32  * A class responsible for managing all media host states of the various host locations and
33  * coordinating the heights among different players. This class can be used to get the most up to
34  * date state for any location.
35  */
36 @SysUISingleton
37 class MediaHostStatesManager @Inject constructor(dumpManager: DumpManager) : Dumpable {
38 
39     private val callbacks: MutableSet<Callback> = mutableSetOf()
40     private val controllers: MutableSet<MediaViewController> = mutableSetOf()
41 
42     /**
43      * The overall sizes of the carousel. This is needed to make sure all players in the carousel
44      * have equal size.
45      */
46     val carouselSizes: MutableMap<Int, MeasurementOutput> = mutableMapOf()
47 
48     /** A map with all media states of all locations. */
49     val mediaHostStates: MutableMap<Int, MediaHostState> = mutableMapOf()
50 
51     init {
52         dumpManager.registerNormalDumpable(TAG, this)
53     }
54 
55     /**
56      * Notify that a media state for a given location has changed. Should only be called from Media
57      * hosts themselves.
58      */
updateHostStatenull59     fun updateHostState(@MediaLocation location: Int, hostState: MediaHostState) =
60         traceSection("MediaHostStatesManager#updateHostState") {
61             val currentState = mediaHostStates.get(location)
62             if (!hostState.equals(currentState)) {
63                 val newState = hostState.copy()
64                 mediaHostStates.put(location, newState)
65                 updateCarouselDimensions(location, hostState)
66                 // First update all the controllers to ensure they get the chance to measure
67                 for (controller in controllers) {
68                     controller.stateCallback.onHostStateChanged(location, newState)
69                 }
70 
71                 // Then update all other callbacks which may depend on the controllers above
72                 for (callback in callbacks) {
73                     callback.onHostStateChanged(location, newState)
74                 }
75             }
76         }
77 
78     /**
79      * Get the dimensions of all players combined, which determines the overall height of the media
80      * carousel and the media hosts.
81      */
updateCarouselDimensionsnull82     fun updateCarouselDimensions(
83         @MediaLocation location: Int,
84         hostState: MediaHostState,
85     ): MeasurementOutput =
86         traceSection("MediaHostStatesManager#updateCarouselDimensions") {
87             val result = MeasurementOutput(0, 0)
88             var changed = false
89             for (controller in controllers) {
90                 val measurement = controller.getMeasurementsForState(hostState)
91                 measurement?.let {
92                     if (it.measuredHeight > result.measuredHeight) {
93                         result.measuredHeight = it.measuredHeight
94                         changed = true
95                     }
96                     if (it.measuredWidth > result.measuredWidth) {
97                         result.measuredWidth = it.measuredWidth
98                         changed = true
99                     }
100                 }
101             }
102             if (mediaControlsUmoInflationInBackground()) {
103                 // Set carousel size if result measurements changed. This avoids setting carousel
104                 // size when this method gets called before the addition of media view controllers
105                 if (!carouselSizes.contains(location) || changed) {
106                     carouselSizes[location] = result
107                 }
108             } else {
109                 carouselSizes[location] = result
110             }
111             return carouselSizes[location] ?: result
112         }
113 
114     /** Add a callback to be called when a MediaState has updated */
addCallbacknull115     fun addCallback(callback: Callback) {
116         callbacks.add(callback)
117     }
118 
119     /** Remove a callback that listens to media states */
removeCallbacknull120     fun removeCallback(callback: Callback) {
121         callbacks.remove(callback)
122     }
123 
124     /**
125      * Register a controller that listens to media states and is used to determine the size of the
126      * media carousel
127      */
addControllernull128     fun addController(controller: MediaViewController) {
129         controllers.add(controller)
130     }
131 
132     /** Notify the manager about the removal of a controller. */
removeControllernull133     fun removeController(controller: MediaViewController) {
134         controllers.remove(controller)
135     }
136 
dumpnull137     override fun dump(pw: PrintWriter, args: Array<out String>) {
138         pw.apply {
139             println("Controllers: $controllers")
140             println("Callbacks: $callbacks")
141             for ((location, size) in carouselSizes) {
142                 println("Size $location: ${size.measuredWidth} x ${size.measuredHeight}")
143             }
144             for ((location, state) in mediaHostStates) {
145                 println("Host $location: visible ${state.visible}")
146             }
147         }
148     }
149 
150     interface Callback {
151         /**
152          * Notify the callbacks that a media state for a host has changed, and that the
153          * corresponding view states should be updated and applied
154          */
onHostStateChangednull155         fun onHostStateChanged(@MediaLocation location: Int, mediaHostState: MediaHostState)
156     }
157 }
158