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