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.shared.model 18 19 import android.app.PendingIntent 20 import android.graphics.drawable.Drawable 21 import android.graphics.drawable.Icon 22 import android.media.session.MediaSession 23 import android.os.Process 24 import com.android.internal.logging.InstanceId 25 import com.android.systemui.res.R 26 27 /** State of a media view. */ 28 data class MediaData( 29 val userId: Int = -1, 30 val initialized: Boolean = false, 31 /** App name that will be displayed on the player. */ 32 val app: String? = null, 33 /** App icon shown on player. */ 34 val appIcon: Icon? = null, 35 /** Artist name. */ 36 val artist: CharSequence? = null, 37 /** Song name. */ 38 val song: CharSequence? = null, 39 /** Album artwork. */ 40 val artwork: Icon? = null, 41 /** List of generic action buttons for the media player, based on notification actions */ 42 val actions: List<MediaAction> = emptyList(), 43 /** Same as above, but shown on smaller versions of the player, like in QQS or keyguard. */ 44 val actionsToShowInCompact: List<Int> = emptyList(), 45 /** 46 * Semantic actions buttons, based on the PlaybackState of the media session. If present, these 47 * actions will be preferred in the UI over [actions] 48 */ 49 val semanticActions: MediaButton? = null, 50 /** Package name of the app that's posting the media. */ 51 val packageName: String = "INVALID", 52 /** Unique media session identifier. */ 53 val token: MediaSession.Token? = null, 54 /** Action to perform when the player is tapped. This is unrelated to {@link #actions}. */ 55 val clickIntent: PendingIntent? = null, 56 /** Where the media is playing: phone, headphones, ear buds, remote session. */ 57 val device: MediaDeviceData? = null, 58 /** 59 * When active, a player will be displayed on keyguard and quick-quick settings. This is 60 * unrelated to the stream being playing or not, a player will not be active if timed out, or in 61 * resumption mode. 62 */ 63 var active: Boolean = true, 64 /** Action that should be performed to restart a non active session. */ 65 var resumeAction: Runnable? = null, 66 /** Playback location: one of PLAYBACK_LOCAL, PLAYBACK_CAST_LOCAL, or PLAYBACK_CAST_REMOTE */ 67 var playbackLocation: Int = PLAYBACK_LOCAL, 68 /** 69 * Indicates that this player is a resumption player (ie. It only shows a play actions which 70 * will start the app and start playing). 71 */ 72 var resumption: Boolean = false, 73 /** 74 * Notification key for cancelling a media player after a timeout (when not using resumption.) 75 */ 76 val notificationKey: String? = null, 77 var hasCheckedForResume: Boolean = false, 78 79 /** If apps do not report PlaybackState, set as null to imply 'undetermined' */ 80 val isPlaying: Boolean? = null, 81 82 /** Set from the notification and used as fallback when PlaybackState cannot be determined */ 83 val isClearable: Boolean = true, 84 85 /** Milliseconds since boot when this player was last active. */ 86 var lastActive: Long = 0L, 87 88 /** Timestamp in milliseconds when this player was created. */ 89 var createdTimestampMillis: Long = 0L, 90 91 /** Instance ID for logging purposes */ 92 val instanceId: InstanceId = InstanceId.fakeInstanceId(-1), 93 94 /** The UID of the app, used for logging */ 95 val appUid: Int = Process.INVALID_UID, 96 97 /** Whether explicit indicator exists */ 98 val isExplicit: Boolean = false, 99 100 /** Track progress (0 - 1) to display for players where [resumption] is true */ 101 val resumeProgress: Double? = null, 102 ) { 103 companion object { 104 /** Media is playing on the local device */ 105 const val PLAYBACK_LOCAL = 0 106 /** Media is cast but originated on the local device */ 107 const val PLAYBACK_CAST_LOCAL = 1 108 /** Media is from a remote cast notification */ 109 const val PLAYBACK_CAST_REMOTE = 2 110 } 111 isLocalSessionnull112 fun isLocalSession(): Boolean { 113 return playbackLocation == PLAYBACK_LOCAL 114 } 115 } 116 117 /** Contains [MediaAction] objects which represent specific buttons in the UI */ 118 data class MediaButton( 119 /** Play/pause button */ 120 val playOrPause: MediaAction? = null, 121 /** Next button, or custom action */ 122 val nextOrCustom: MediaAction? = null, 123 /** Previous button, or custom action */ 124 val prevOrCustom: MediaAction? = null, 125 /** First custom action space */ 126 val custom0: MediaAction? = null, 127 /** Second custom action space */ 128 val custom1: MediaAction? = null, 129 /** Whether to reserve the empty space when the nextOrCustom is null */ 130 val reserveNext: Boolean = false, 131 /** Whether to reserve the empty space when the prevOrCustom is null */ 132 val reservePrev: Boolean = false 133 ) { getActionByIdnull134 fun getActionById(id: Int): MediaAction? { 135 return when (id) { 136 R.id.actionPlayPause -> playOrPause 137 R.id.actionNext -> nextOrCustom 138 R.id.actionPrev -> prevOrCustom 139 R.id.action0 -> custom0 140 R.id.action1 -> custom1 141 else -> null 142 } 143 } 144 } 145 146 /** State of a media action. */ 147 data class MediaAction( 148 val icon: Drawable?, 149 val action: Runnable?, 150 val contentDescription: CharSequence?, 151 val background: Drawable?, 152 153 // Rebind Id is used to detect identical rebinds and ignore them. It is intended 154 // to prevent continuously looping animations from restarting due to the arrival 155 // of repeated media notifications that are visually identical. 156 val rebindId: Int? = null 157 ) 158 159 /** State of the media device. */ 160 data class MediaDeviceData 161 @JvmOverloads 162 constructor( 163 /** Whether or not to enable the chip */ 164 val enabled: Boolean, 165 166 /** Device icon to show in the chip */ 167 val icon: Drawable?, 168 169 /** Device display name */ 170 val name: CharSequence?, 171 172 /** Optional intent to override the default output switcher for this control */ 173 val intent: PendingIntent? = null, 174 175 /** Unique id for this device */ 176 val id: String? = null, 177 178 /** Whether or not to show the broadcast button */ 179 val showBroadcastButton: Boolean 180 ) { 181 /** 182 * Check whether [MediaDeviceData] objects are equal in all fields except the icon. The icon is 183 * ignored because it can change by reference frequently depending on the device type's 184 * implementation, but this is not usually relevant unless other info has changed 185 */ equalsWithoutIconnull186 fun equalsWithoutIcon(other: MediaDeviceData?): Boolean { 187 if (other == null) { 188 return false 189 } 190 191 return enabled == other.enabled && 192 name == other.name && 193 intent == other.intent && 194 id == other.id && 195 showBroadcastButton == other.showBroadcastButton 196 } 197 } 198