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 18 19 import com.android.systemui.dagger.SysUISingleton 20 import com.android.systemui.util.animation.MeasurementOutput 21 import com.android.systemui.util.traceSection 22 import javax.inject.Inject 23 24 /** 25 * A class responsible for managing all media host states of the various host locations and 26 * coordinating the heights among different players. This class can be used to get the most up to 27 * date state for any location. 28 */ 29 @SysUISingleton 30 class MediaHostStatesManager @Inject constructor() { 31 32 private val callbacks: MutableSet<Callback> = mutableSetOf() 33 private val controllers: MutableSet<MediaViewController> = mutableSetOf() 34 35 /** 36 * The overall sizes of the carousel. This is needed to make sure all players in the carousel 37 * have equal size. 38 */ 39 val carouselSizes: MutableMap<Int, MeasurementOutput> = mutableMapOf() 40 41 /** A map with all media states of all locations. */ 42 val mediaHostStates: MutableMap<Int, MediaHostState> = mutableMapOf() 43 44 /** 45 * Notify that a media state for a given location has changed. Should only be called from Media 46 * hosts themselves. 47 */ 48 fun updateHostState(@MediaLocation location: Int, hostState: MediaHostState) = 49 traceSection("MediaHostStatesManager#updateHostState") { 50 val currentState = mediaHostStates.get(location) 51 if (!hostState.equals(currentState)) { 52 val newState = hostState.copy() 53 mediaHostStates.put(location, newState) 54 updateCarouselDimensions(location, hostState) 55 // First update all the controllers to ensure they get the chance to measure 56 for (controller in controllers) { 57 controller.stateCallback.onHostStateChanged(location, newState) 58 } 59 60 // Then update all other callbacks which may depend on the controllers above 61 for (callback in callbacks) { 62 callback.onHostStateChanged(location, newState) 63 } 64 } 65 } 66 67 /** 68 * Get the dimensions of all players combined, which determines the overall height of the media 69 * carousel and the media hosts. 70 */ 71 fun updateCarouselDimensions( 72 @MediaLocation location: Int, 73 hostState: MediaHostState 74 ): MeasurementOutput = 75 traceSection("MediaHostStatesManager#updateCarouselDimensions") { 76 val result = MeasurementOutput(0, 0) 77 for (controller in controllers) { 78 val measurement = controller.getMeasurementsForState(hostState) 79 measurement?.let { 80 if (it.measuredHeight > result.measuredHeight) { 81 result.measuredHeight = it.measuredHeight 82 } 83 if (it.measuredWidth > result.measuredWidth) { 84 result.measuredWidth = it.measuredWidth 85 } 86 } 87 } 88 carouselSizes[location] = result 89 return result 90 } 91 92 /** Add a callback to be called when a MediaState has updated */ 93 fun addCallback(callback: Callback) { 94 callbacks.add(callback) 95 } 96 97 /** Remove a callback that listens to media states */ 98 fun removeCallback(callback: Callback) { 99 callbacks.remove(callback) 100 } 101 102 /** 103 * Register a controller that listens to media states and is used to determine the size of the 104 * media carousel 105 */ 106 fun addController(controller: MediaViewController) { 107 controllers.add(controller) 108 } 109 110 /** Notify the manager about the removal of a controller. */ 111 fun removeController(controller: MediaViewController) { 112 controllers.remove(controller) 113 } 114 115 interface Callback { 116 /** 117 * Notify the callbacks that a media state for a host has changed, and that the 118 * corresponding view states should be updated and applied 119 */ 120 fun onHostStateChanged(@MediaLocation location: Int, mediaHostState: MediaHostState) 121 } 122 } 123