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