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