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