1 /*
2  * Copyright (C) 2015 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.tv.menu;
18 
19 import android.content.Context;
20 import android.graphics.Bitmap;
21 import android.support.annotation.Nullable;
22 import android.text.TextUtils;
23 import android.util.AttributeSet;
24 import android.util.Log;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.widget.ImageView;
28 import android.widget.ProgressBar;
29 import android.widget.TextView;
30 
31 import com.android.tv.MainActivity;
32 import com.android.tv.R;
33 import com.android.tv.data.Channel;
34 import com.android.tv.data.Program;
35 import com.android.tv.parental.ParentalControlSettings;
36 import com.android.tv.util.ImageLoader;
37 
38 /**
39  * A view to render channel card.
40  */
41 public class ChannelCardView extends BaseCardView<Channel> {
42     private static final String TAG = MenuView.TAG;
43     private static final boolean DEBUG = MenuView.DEBUG;
44 
45     private final float mCardHeight;
46     private final float mExtendedCardHeight;
47     private final float mProgramNameViewHeight;
48     private final float mExtendedTextViewCardHeight;
49     private final int mCardImageWidth;
50     private final int mCardImageHeight;
51 
52     private ImageView mImageView;
53     private View mGradientView;
54     private TextView mChannelNumberNameView;
55     private ProgressBar mProgressBar;
56     private TextView mMetaViewFocused;
57     private TextView mMetaViewUnfocused;
58     private Channel mChannel;
59     private Program mProgram;
60     private boolean mExtendViewOnFocus;
61     private final MainActivity mMainActivity;
62 
ChannelCardView(Context context)63     public ChannelCardView(Context context) {
64         this(context, null);
65     }
66 
ChannelCardView(Context context, AttributeSet attrs)67     public ChannelCardView(Context context, AttributeSet attrs) {
68         this(context, attrs, 0);
69     }
70 
ChannelCardView(Context context, AttributeSet attrs, int defStyle)71     public ChannelCardView(Context context, AttributeSet attrs, int defStyle) {
72         super(context, attrs, defStyle);
73 
74         mCardImageWidth = getResources().getDimensionPixelSize(R.dimen.card_image_layout_width);
75         mCardImageHeight = getResources().getDimensionPixelSize(R.dimen.card_image_layout_height);
76         mCardHeight = getResources().getDimensionPixelSize(R.dimen.card_layout_height);
77         mExtendedCardHeight = getResources().getDimensionPixelSize(
78                 R.dimen.card_layout_height_extended);
79         mProgramNameViewHeight = getResources().getDimensionPixelSize(
80                 R.dimen.card_meta_layout_height);
81         mExtendedTextViewCardHeight = getResources().getDimensionPixelOffset(
82                 R.dimen.card_meta_layout_height_extended);
83 
84         mMainActivity = (MainActivity) context;
85     }
86 
87     @Override
onFinishInflate()88     protected void onFinishInflate() {
89         super.onFinishInflate();
90         mImageView = (ImageView) findViewById(R.id.image);
91         mGradientView = findViewById(R.id.image_gradient);
92         mChannelNumberNameView = (TextView) findViewById(R.id.channel_number_and_name);
93         mMetaViewFocused = (TextView) findViewById(R.id.channel_title_focused);
94         mMetaViewUnfocused = (TextView) findViewById(R.id.channel_title_unfocused);
95         mProgressBar = (ProgressBar) findViewById(R.id.progress);
96     }
97 
98     @Override
onBind(Channel channel, boolean selected)99     public void onBind(Channel channel, boolean selected) {
100         if (DEBUG) {
101             Log.d(TAG, "onBind(channelName=" + channel.getDisplayName() + ", selected=" + selected
102                     + ")");
103         }
104         mChannel = channel;
105         mProgram = null;
106         if (TextUtils.isEmpty(mChannel.getDisplayName())) {
107             mChannelNumberNameView.setText(mChannel.getDisplayNumber());
108         } else {
109             mChannelNumberNameView.setText(mChannel.getDisplayNumber() + " "
110                     + mChannel.getDisplayName());
111         }
112         mChannelNumberNameView.setVisibility(VISIBLE);
113         mImageView.setImageResource(R.drawable.ic_recent_thumbnail_default);
114         mImageView.setBackgroundResource(R.color.channel_card);
115         mGradientView.setVisibility(View.GONE);
116         mProgressBar.setVisibility(GONE);
117 
118         setMetaViewEnabled(true);
119         if (mMainActivity.getParentalControlSettings().isParentalControlsEnabled()
120                 && mChannel.isLocked()) {
121             setMetaViewText(R.string.program_title_for_blocked_channel);
122             return;
123         } else {
124             setMetaViewText("");
125         }
126 
127         updateProgramInformation();
128         mMetaViewFocused.measure(
129                 MeasureSpec.makeMeasureSpec(mCardImageWidth, MeasureSpec.EXACTLY),
130                 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
131         if (mExtendViewOnFocus = mMetaViewFocused.getLineCount() > 1) {
132             setMetaViewFocusedAlpha(selected ? 1f : 0f);
133         } else {
134             setMetaViewFocusedAlpha(1f);
135         }
136 
137         // Call super.onBind() at the end in order to make getCardHeight() return a proper value.
138         super.onBind(channel, selected);
139     }
140 
createProgramPosterArtCallback( ChannelCardView cardView, final Program program)141     private static ImageLoader.ImageLoaderCallback<ChannelCardView> createProgramPosterArtCallback(
142             ChannelCardView cardView, final Program program) {
143         return new ImageLoader.ImageLoaderCallback<ChannelCardView>(cardView) {
144             @Override
145             public void onBitmapLoaded(ChannelCardView cardView, @Nullable Bitmap posterArt) {
146                 if (posterArt == null || cardView.mProgram == null
147                         || program.getChannelId() != cardView.mProgram.getChannelId()
148                         || program.getChannelId() != cardView.mChannel.getId()) {
149                     return;
150                 }
151                 cardView.updatePosterArt(posterArt);
152             }
153         };
154     }
155 
156     private void updatePosterArt(Bitmap posterArt) {
157         mImageView.setImageBitmap(posterArt);
158         mGradientView.setVisibility(View.VISIBLE);
159     }
160 
161     @Override
162     protected void onFocusAnimationStart(boolean selected) {
163         if (mExtendViewOnFocus) {
164             setMetaViewFocusedAlpha(selected ? 1f : 0f);
165         }
166     }
167 
168     @Override
169     protected void onSetFocusAnimatedValue(float animatedValue) {
170         super.onSetFocusAnimatedValue(animatedValue);
171         if (mExtendViewOnFocus) {
172             ViewGroup.LayoutParams params = mMetaViewUnfocused.getLayoutParams();
173             params.height = Math.round(mProgramNameViewHeight
174                     + (mExtendedTextViewCardHeight - mProgramNameViewHeight) * animatedValue);
175             setMetaViewLayoutParams(params);
176             setMetaViewFocusedAlpha(animatedValue);
177         }
178     }
179 
180     @Override
181     protected float getCardHeight() {
182         return (mExtendViewOnFocus && isFocused()) ? mExtendedCardHeight : mCardHeight;
183     }
184 
185     private void updateProgramInformation() {
186         if (mChannel == null) {
187             return;
188         }
189         mProgram = mMainActivity.getProgramDataManager().getCurrentProgram(mChannel.getId());
190         if (mProgram == null || TextUtils.isEmpty(mProgram.getTitle())) {
191             setMetaViewEnabled(false);
192             setMetaViewText(R.string.program_title_for_no_information);
193         } else {
194             setMetaViewText(mProgram.getTitle());
195         }
196 
197         if (mProgram == null) {
198             return;
199         }
200 
201         long startTime = mProgram.getStartTimeUtcMillis();
202         long endTime = mProgram.getEndTimeUtcMillis();
203         long currTime = System.currentTimeMillis();
204         mProgressBar.setVisibility(View.VISIBLE);
205         if (currTime <= startTime) {
206             mProgressBar.setProgress(0);
207         } else if (currTime >= endTime) {
208             mProgressBar.setProgress(100);
209         } else {
210             mProgressBar.setProgress((int) (100 * (currTime - startTime) / (endTime - startTime)));
211         }
212 
213         if (!(getContext() instanceof MainActivity)) {
214             Log.e(TAG, "Fails to check program's content rating.");
215             return;
216         }
217         ParentalControlSettings parental = mMainActivity.getParentalControlSettings();
218         if ((!parental.isParentalControlsEnabled()
219                 || !parental.isRatingBlocked(mProgram.getContentRatings()))
220                 && !TextUtils.isEmpty(mProgram.getPosterArtUri())) {
221             mProgram.loadPosterArt(getContext(), mCardImageWidth, mCardImageHeight,
222                     createProgramPosterArtCallback(this, mProgram));
223         }
224     }
225 
226     private void setMetaViewLayoutParams(ViewGroup.LayoutParams params) {
227         mMetaViewFocused.setLayoutParams(params);
228         mMetaViewUnfocused.setLayoutParams(params);
229     }
230 
231     private void setMetaViewText(String text) {
232         mMetaViewFocused.setText(text);
233         mMetaViewUnfocused.setText(text);
234     }
235 
236     private void setMetaViewText(int resId) {
237         mMetaViewFocused.setText(resId);
238         mMetaViewUnfocused.setText(resId);
239     }
240 
241     private void setMetaViewEnabled(boolean enabled) {
242         mMetaViewFocused.setEnabled(enabled);
243         mMetaViewUnfocused.setEnabled(enabled);
244     }
245 
246     private void setMetaViewFocusedAlpha(float focusedAlpha) {
247         mMetaViewFocused.setAlpha(focusedAlpha);
248         mMetaViewUnfocused.setAlpha(1f - focusedAlpha);
249     }
250 }
251