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