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<MediaNotificationAction> = 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 /** Smartspace Id, used for logging. */ 104 var smartspaceId: Int = -1, 105 106 /** If media card was visible to user, used for logging. */ 107 var isImpressed: Boolean = false, 108 ) { 109 companion object { 110 /** Media is playing on the local device */ 111 const val PLAYBACK_LOCAL = 0 112 /** Media is cast but originated on the local device */ 113 const val PLAYBACK_CAST_LOCAL = 1 114 /** Media is from a remote cast notification */ 115 const val PLAYBACK_CAST_REMOTE = 2 116 } 117 isLocalSessionnull118 fun isLocalSession(): Boolean { 119 return playbackLocation == PLAYBACK_LOCAL 120 } 121 } 122 123 /** Contains [MediaAction] objects which represent specific buttons in the UI */ 124 data class MediaButton( 125 /** Play/pause button */ 126 val playOrPause: MediaAction? = null, 127 /** Next button, or custom action */ 128 val nextOrCustom: MediaAction? = null, 129 /** Previous button, or custom action */ 130 val prevOrCustom: MediaAction? = null, 131 /** First custom action space */ 132 val custom0: MediaAction? = null, 133 /** Second custom action space */ 134 val custom1: MediaAction? = null, 135 /** Whether to reserve the empty space when the nextOrCustom is null */ 136 val reserveNext: Boolean = false, 137 /** Whether to reserve the empty space when the prevOrCustom is null */ 138 val reservePrev: Boolean = false 139 ) { getActionByIdnull140 fun getActionById(id: Int): MediaAction? { 141 return when (id) { 142 R.id.actionPlayPause -> playOrPause 143 R.id.actionNext -> nextOrCustom 144 R.id.actionPrev -> prevOrCustom 145 R.id.action0 -> custom0 146 R.id.action1 -> custom1 147 else -> null 148 } 149 } 150 } 151 152 /** State of a media action. */ 153 data class MediaAction( 154 val icon: Drawable?, 155 val action: Runnable?, 156 val contentDescription: CharSequence?, 157 val background: Drawable?, 158 159 // Rebind Id is used to detect identical rebinds and ignore them. It is intended 160 // to prevent continuously looping animations from restarting due to the arrival 161 // of repeated media notifications that are visually identical. 162 val rebindId: Int? = null 163 ) 164 165 /** State of a media action from notification. */ 166 data class MediaNotificationAction( 167 val isAuthenticationRequired: Boolean, 168 val actionIntent: PendingIntent?, 169 val icon: Drawable?, 170 val contentDescription: CharSequence? 171 ) 172 173 /** State of the media device. */ 174 data class MediaDeviceData 175 @JvmOverloads 176 constructor( 177 /** Whether or not to enable the chip */ 178 val enabled: Boolean, 179 180 /** Device icon to show in the chip */ 181 val icon: Drawable?, 182 183 /** Device display name */ 184 val name: CharSequence?, 185 186 /** Optional intent to override the default output switcher for this control */ 187 val intent: PendingIntent? = null, 188 189 /** Unique id for this device */ 190 val id: String? = null, 191 192 /** Whether or not to show the broadcast button */ 193 val showBroadcastButton: Boolean 194 ) { 195 /** 196 * Check whether [MediaDeviceData] objects are equal in all fields except the icon. The icon is 197 * ignored because it can change by reference frequently depending on the device type's 198 * implementation, but this is not usually relevant unless other info has changed 199 */ equalsWithoutIconnull200 fun equalsWithoutIcon(other: MediaDeviceData?): Boolean { 201 if (other == null) { 202 return false 203 } 204 205 return enabled == other.enabled && 206 name == other.name && 207 intent == other.intent && 208 id == other.id && 209 showBroadcastButton == other.showBroadcastButton 210 } 211 } 212