1 /* 2 * Copyright (C) 2021 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.view.LayoutInflater 20 import android.view.View 21 import android.view.ViewGroup 22 import android.widget.ImageButton 23 import android.widget.ImageView 24 import android.widget.SeekBar 25 import android.widget.TextView 26 import androidx.constraintlayout.widget.Barrier 27 import com.android.internal.widget.CachingIconView 28 import com.android.systemui.R 29 import com.android.systemui.media.controls.models.GutsViewHolder 30 import com.android.systemui.surfaceeffects.ripple.MultiRippleView 31 import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView 32 import com.android.systemui.util.animation.TransitionLayout 33 34 private const val TAG = "MediaViewHolder" 35 36 /** Holder class for media player view */ 37 class MediaViewHolder constructor(itemView: View) { 38 val player = itemView as TransitionLayout 39 40 // Player information 41 val albumView = itemView.requireViewById<ImageView>(R.id.album_art) 42 val multiRippleView = itemView.requireViewById<MultiRippleView>(R.id.touch_ripple_view) 43 val turbulenceNoiseView = 44 itemView.requireViewById<TurbulenceNoiseView>(R.id.turbulence_noise_view) 45 val appIcon = itemView.requireViewById<ImageView>(R.id.icon) 46 val titleText = itemView.requireViewById<TextView>(R.id.header_title) 47 val artistText = itemView.requireViewById<TextView>(R.id.header_artist) 48 val explicitIndicator = itemView.requireViewById<CachingIconView>(R.id.media_explicit_indicator) 49 50 // Output switcher 51 val seamless = itemView.requireViewById<ViewGroup>(R.id.media_seamless) 52 val seamlessIcon = itemView.requireViewById<ImageView>(R.id.media_seamless_image) 53 val seamlessText = itemView.requireViewById<TextView>(R.id.media_seamless_text) 54 val seamlessButton = itemView.requireViewById<View>(R.id.media_seamless_button) 55 56 // Seekbar views 57 val seekBar = itemView.requireViewById<SeekBar>(R.id.media_progress_bar) 58 // These views are only shown while the user is actively scrubbing 59 val scrubbingElapsedTimeView: TextView = 60 itemView.requireViewById(R.id.media_scrubbing_elapsed_time) 61 val scrubbingTotalTimeView: TextView = itemView.requireViewById(R.id.media_scrubbing_total_time) 62 63 val gutsViewHolder = GutsViewHolder(itemView) 64 65 // Action Buttons 66 val actionPlayPause = itemView.requireViewById<ImageButton>(R.id.actionPlayPause) 67 val actionNext = itemView.requireViewById<ImageButton>(R.id.actionNext) 68 val actionPrev = itemView.requireViewById<ImageButton>(R.id.actionPrev) 69 val action0 = itemView.requireViewById<ImageButton>(R.id.action0) 70 val action1 = itemView.requireViewById<ImageButton>(R.id.action1) 71 val action2 = itemView.requireViewById<ImageButton>(R.id.action2) 72 val action3 = itemView.requireViewById<ImageButton>(R.id.action3) 73 val action4 = itemView.requireViewById<ImageButton>(R.id.action4) 74 75 val actionsTopBarrier = itemView.requireViewById<Barrier>(R.id.media_action_barrier_top) 76 77 fun getAction(id: Int): ImageButton { 78 return when (id) { 79 R.id.actionPlayPause -> actionPlayPause 80 R.id.actionNext -> actionNext 81 R.id.actionPrev -> actionPrev 82 R.id.action0 -> action0 83 R.id.action1 -> action1 84 R.id.action2 -> action2 85 R.id.action3 -> action3 86 R.id.action4 -> action4 87 else -> { 88 throw IllegalArgumentException() 89 } 90 } 91 } 92 93 fun getTransparentActionButtons(): List<ImageButton> { 94 return listOf(actionNext, actionPrev, action0, action1, action2, action3, action4) 95 } 96 97 fun marquee(start: Boolean, delay: Long) { 98 gutsViewHolder.marquee(start, delay, TAG) 99 } 100 101 companion object { 102 /** 103 * Creates a MediaViewHolder. 104 * 105 * @param inflater LayoutInflater to use to inflate the layout. 106 * @param parent Parent of inflated view. 107 */ 108 @JvmStatic 109 fun create(inflater: LayoutInflater, parent: ViewGroup): MediaViewHolder { 110 val mediaView = inflater.inflate(R.layout.media_session_view, parent, false) 111 mediaView.setLayerType(View.LAYER_TYPE_HARDWARE, null) 112 // Because this media view (a TransitionLayout) is used to measure and layout the views 113 // in various states before being attached to its parent, we can't depend on the default 114 // LAYOUT_DIRECTION_INHERIT to correctly resolve the ltr direction. 115 mediaView.layoutDirection = View.LAYOUT_DIRECTION_LOCALE 116 return MediaViewHolder(mediaView).apply { 117 // Media playback is in the direction of tape, not time, so it stays LTR 118 seekBar.layoutDirection = View.LAYOUT_DIRECTION_LTR 119 } 120 } 121 122 val controlsIds = 123 setOf( 124 R.id.icon, 125 R.id.app_name, 126 R.id.header_title, 127 R.id.header_artist, 128 R.id.media_explicit_indicator, 129 R.id.media_seamless, 130 R.id.media_progress_bar, 131 R.id.actionPlayPause, 132 R.id.actionNext, 133 R.id.actionPrev, 134 R.id.action0, 135 R.id.action1, 136 R.id.action2, 137 R.id.action3, 138 R.id.action4, 139 R.id.icon, 140 R.id.media_scrubbing_elapsed_time, 141 R.id.media_scrubbing_total_time 142 ) 143 144 // Buttons used for notification-based actions 145 val genericButtonIds = 146 setOf(R.id.action0, R.id.action1, R.id.action2, R.id.action3, R.id.action4) 147 148 val expandedBottomActionIds = 149 setOf( 150 R.id.media_progress_bar, 151 R.id.actionPrev, 152 R.id.actionNext, 153 R.id.action0, 154 R.id.action1, 155 R.id.action2, 156 R.id.action3, 157 R.id.action4, 158 R.id.media_scrubbing_elapsed_time, 159 R.id.media_scrubbing_total_time, 160 ) 161 162 val detailIds = 163 setOf( 164 R.id.header_title, 165 R.id.header_artist, 166 R.id.media_explicit_indicator, 167 R.id.actionPlayPause, 168 ) 169 170 val backgroundIds = 171 setOf( 172 R.id.album_art, 173 R.id.turbulence_noise_view, 174 R.id.touch_ripple_view, 175 ) 176 } 177 } 178