• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016, 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 package com.android.car.overview;
17 
18 import android.app.PendingIntent;
19 import android.content.Context;
20 import android.text.TextUtils;
21 import android.util.Log;
22 import android.view.View;
23 import android.widget.ImageButton;
24 import android.widget.ImageView;
25 import android.widget.TextView;
26 import com.android.car.stream.MediaPlaybackExtension;
27 import com.android.car.stream.StreamCard;
28 
29 /**
30  * A {@link StreamViewHolder} that binds a {@link MediaPlaybackExtension} to
31  * an interactive in playback UI card.
32  */
33 public class MediaStreamViewHolder extends StreamViewHolder {
34     private static final String TAG = "MediaStreamViewHolder";
35 
36     private static final String ELLIPSIS = "\u2026";
37     // limit the subtitle to 20 chars, and the rest space is given to the app name.
38     private static final int MAX_TEXT_LENGTH = 20;
39     private static final String UNICODE_BULLET_SPACER = " \u2022 ";
40 
41     private final ImageView mPlayerBackground;
42     private final TextView mTitleTextView;
43     private final TextView mSubtitleTextview;
44 
45     private final ImageButton mSkipToPreviousButton;
46     private final ImageButton mSkipToNextButton;
47     private final OverviewFabButton mPlayPauseButton;
48 
49     private PendingIntent mPlayPauseAction;
50     private PendingIntent mSkipToNextAction;
51     private PendingIntent mSkipToPreviousAction;
52 
53     private PendingIntent mContentPendingIntent;
54 
MediaStreamViewHolder(Context context, View itemView)55     public MediaStreamViewHolder(Context context, View itemView) {
56         super(context, itemView);
57 
58         mPlayerBackground = (ImageView) itemView.findViewById(R.id.media_player_background);
59         mTitleTextView = (TextView) itemView.findViewById(R.id.title);
60         mSubtitleTextview = (TextView) itemView.findViewById(R.id.subtitle);
61 
62         mSkipToPreviousButton = (ImageButton) itemView.findViewById(R.id.skip_previous);
63         mSkipToNextButton = (ImageButton) itemView.findViewById(R.id.skip_next);
64         mPlayPauseButton = (OverviewFabButton) itemView.findViewById(R.id.play_pause);
65 
66         mSkipToPreviousButton.setImageResource(R.drawable.ic_skip_previous);
67         mSkipToNextButton.setImageResource(R.drawable.ic_skip_next);
68 
69         mPlayPauseButton.setOnClickListener(new View.OnClickListener() {
70             @Override
71             public void onClick(View v) {
72                 try {
73                     if (mPlayPauseAction != null) {
74                         mPlayPauseAction.send();
75                     }
76                 } catch (PendingIntent.CanceledException e) {
77                     Log.e(TAG, "Failed to send play/pause action pending intent", e);
78                 }
79             }
80         });
81 
82         mSkipToPreviousButton.setOnClickListener(new View.OnClickListener() {
83             @Override
84             public void onClick(View v) {
85                 try {
86                     if (mSkipToPreviousAction != null) {
87                         mSkipToPreviousAction.send();
88                     }
89                 } catch (PendingIntent.CanceledException e) {
90                     Log.e(TAG, "Failed to send play/pause action pending intent", e);
91                 }
92             }
93         });
94 
95         mSkipToNextButton.setOnClickListener(new View.OnClickListener() {
96             @Override
97             public void onClick(View v) {
98                 try {
99                     if (mSkipToNextAction != null) {
100                         mSkipToNextAction.send();
101                     }
102                 } catch (PendingIntent.CanceledException e) {
103                     Log.e(TAG, "Failed to send play/pause action pending intent", e);
104                 }
105             }
106         });
107 
108         mActionContainer.setOnClickListener(new View.OnClickListener() {
109             @Override
110             public void onClick(View v) {
111                 try {
112                     if (mContentPendingIntent != null) {
113                         mContentPendingIntent.send();
114                     }
115                 } catch (PendingIntent.CanceledException e) {
116                     Log.e(TAG, "Failed to send content pending intent in media card", e);
117                 }
118             }
119         });
120     }
121 
122     @Override
bindStreamCard(StreamCard card)123     public void bindStreamCard(StreamCard card) {
124         super.bindStreamCard(card);
125         MediaPlaybackExtension extension = (MediaPlaybackExtension) card.getCardExtension();
126 
127         if (extension.getAlbumArt() != null) {
128             mPlayerBackground.setImageBitmap(extension.getAlbumArt());
129             mPlayerBackground.setVisibility(View.VISIBLE);
130         }
131 
132         String title = extension.getTitle();
133         if (!TextUtils.isEmpty(title)) {
134             mTitleTextView.setVisibility(View.VISIBLE);
135             mTitleTextView.setText(title);
136         }
137 
138         String subtitle = getSubtitle(extension);
139         if (!TextUtils.isEmpty(title)) {
140             mSubtitleTextview.setVisibility(View.VISIBLE);
141             mSubtitleTextview.setText(subtitle);
142         }
143 
144         int playPauseIcon;
145         if (extension.isPlaying()) {
146             if (extension.hasPause()) {
147                 playPauseIcon = R.drawable.ic_pause;
148                 mPlayPauseAction = extension.getPauseAction();
149             } else {
150                 playPauseIcon = R.drawable.ic_stop;
151                 mPlayPauseAction = extension.getStopAction();
152             }
153         } else {
154             playPauseIcon = R.drawable.ic_play_arrow;
155             mPlayPauseAction = extension.getPlayAction();
156         }
157 
158         mPlayPauseButton.setImageResource(playPauseIcon);
159         mPlayPauseButton.setAccentColor(extension.getAppAccentColor());
160 
161         mSkipToPreviousAction = extension.getSkipToPreviousAction();
162         mSkipToNextAction = extension.getSkipToNextAction();
163 
164         if (!extension.canSkipToNext()) {
165             mSkipToNextButton.setColorFilter(mContext.getColor(R.color.car_grey_700));
166             mSkipToNextButton.setEnabled(false);
167         }
168 
169         if (!extension.canSkipToPrevious()) {
170             mSkipToPreviousButton.setColorFilter(mContext.getColor(R.color.car_grey_700));
171             mSkipToPreviousButton.setEnabled(false);
172         }
173 
174         mContentPendingIntent = card.getContentPendingIntent();
175     }
176 
177     @Override
resetViews()178     protected void resetViews() {
179         mSubtitleTextview.setText(null);
180         mTitleTextView.setText(null);
181 
182         mSubtitleTextview.setVisibility(View.GONE);
183         mTitleTextView.setVisibility(View.GONE);
184         mPlayerBackground.setVisibility(View.GONE);
185 
186         mSkipToNextButton.setEnabled(true);
187         mSkipToPreviousButton.setEnabled(true);
188         mSkipToNextButton.setColorFilter(0);
189         mSkipToPreviousButton.setColorFilter(0);
190     }
191 
192     /**
193      * Ellipsize the subtitle and append the app name if available.
194      */
getSubtitle(MediaPlaybackExtension extension)195     private String getSubtitle(MediaPlaybackExtension extension) {
196         String appName = extension.getAppName();
197 
198         StringBuilder sb = new StringBuilder();
199         if (!TextUtils.isEmpty(extension.getSubtitle())) {
200             sb.append(ellipsizeText(extension.getSubtitle(), MAX_TEXT_LENGTH));
201             if (!TextUtils.isEmpty(appName)) {
202                 sb.append(UNICODE_BULLET_SPACER);
203                 sb.append(appName);
204             }
205         }
206         return sb.toString();
207     }
208 
209     /**
210      * Ellipsize text and append "..." to the end if its length exceeds the {@code maxLen}.
211      * <p/>
212      * We are not using the ellipsize in the TextView because we are ellipsizing
213      * 2 strings separately.
214      */
ellipsizeText(CharSequence text, int maxLen)215     private static CharSequence ellipsizeText(CharSequence text, int maxLen) {
216         if (text == null || text.length() <= maxLen) {
217             return text;
218         }
219 
220         if (maxLen <= ELLIPSIS.length()) {
221             Log.e(TAG, "Unable to truncate string to " + maxLen);
222             return text;
223         }
224         StringBuilder sb = new StringBuilder();
225         // now, maxLen > ellip.length()
226         sb.append(text.subSequence(0, maxLen - ELLIPSIS.length()));
227         sb.append(ELLIPSIS);
228         return sb.toString();
229     }
230 }
231