aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/menu
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/menu')
-rw-r--r--src/com/android/tv/menu/ActionCardView.java6
-rw-r--r--src/com/android/tv/menu/AppLinkCardView.java198
-rw-r--r--src/com/android/tv/menu/BaseCardView.java50
-rw-r--r--src/com/android/tv/menu/ChannelCardView.java8
-rw-r--r--src/com/android/tv/menu/ChannelsRow.java10
-rw-r--r--src/com/android/tv/menu/ChannelsRowAdapter.java4
-rw-r--r--src/com/android/tv/menu/ItemListRowView.java14
-rw-r--r--src/com/android/tv/menu/Menu.java49
-rw-r--r--src/com/android/tv/menu/MenuAction.java69
-rw-r--r--src/com/android/tv/menu/MenuLayoutManager.java20
-rw-r--r--src/com/android/tv/menu/MenuRowFactory.java24
-rw-r--r--src/com/android/tv/menu/MenuUpdater.java41
-rw-r--r--src/com/android/tv/menu/OptionsRowAdapter.java50
-rw-r--r--src/com/android/tv/menu/PartnerOptionsRowAdapter.java3
-rw-r--r--src/com/android/tv/menu/PipOptionsRowAdapter.java137
-rw-r--r--src/com/android/tv/menu/PlayControlsButton.java24
-rw-r--r--src/com/android/tv/menu/PlayControlsRowView.java194
-rw-r--r--src/com/android/tv/menu/PlaybackProgressBar.java168
-rw-r--r--src/com/android/tv/menu/TvOptionsRowAdapter.java128
19 files changed, 641 insertions, 556 deletions
diff --git a/src/com/android/tv/menu/ActionCardView.java b/src/com/android/tv/menu/ActionCardView.java
index 54892cac..2fd70bfb 100644
--- a/src/com/android/tv/menu/ActionCardView.java
+++ b/src/com/android/tv/menu/ActionCardView.java
@@ -19,8 +19,8 @@ package com.android.tv.menu;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
-import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.tv.R;
@@ -28,7 +28,7 @@ import com.android.tv.R;
/**
* A view to render an item of TV options.
*/
-public class ActionCardView extends FrameLayout implements ItemListRowView.CardView<MenuAction> {
+public class ActionCardView extends RelativeLayout implements ItemListRowView.CardView<MenuAction> {
private static final String TAG = MenuView.TAG;
private static final boolean DEBUG = MenuView.DEBUG;
@@ -66,7 +66,7 @@ public class ActionCardView extends FrameLayout implements ItemListRowView.CardV
}
mIconView.setImageDrawable(action.getDrawable(getContext()));
mLabelView.setText(action.getActionName(getContext()));
- mStateView.setText(action.getActionDescription(getContext()));
+ mStateView.setText(action.getActionDescription());
if (action.isEnabled()) {
setEnabled(true);
setFocusable(true);
diff --git a/src/com/android/tv/menu/AppLinkCardView.java b/src/com/android/tv/menu/AppLinkCardView.java
index bfb5e3f1..d23d9a00 100644
--- a/src/com/android/tv/menu/AppLinkCardView.java
+++ b/src/com/android/tv/menu/AppLinkCardView.java
@@ -24,6 +24,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
import android.support.annotation.Nullable;
import android.support.v7.graphics.Palette;
import android.text.TextUtils;
@@ -55,7 +56,6 @@ public class AppLinkCardView extends BaseCardView<Channel> {
private final int mIconColorFilter;
private ImageView mImageView;
- private View mGradientView;
private TextView mAppInfoView;
private View mMetaViewHolder;
private Channel mChannel;
@@ -102,35 +102,115 @@ public class AppLinkCardView extends BaseCardView<Channel> {
int linkType = mChannel.getAppLinkType(getContext());
mIntent = mChannel.getAppLinkIntent(getContext());
+ CharSequence appLabel = null;
+ mImageView.setForeground(null);
switch (linkType) {
case Channel.APP_LINK_TYPE_CHANNEL:
setText(mChannel.getAppLinkText());
mAppInfoView.setVisibility(VISIBLE);
- mGradientView.setVisibility(VISIBLE);
mAppInfoView.setCompoundDrawablePadding(mIconPadding);
- mAppInfoView.setCompoundDrawables(null, null, null, null);
- mAppInfoView.setText(mPackageManager.getApplicationLabel(appInfo));
+ mAppInfoView.setCompoundDrawablesRelative(null, null, null, null);
+ appLabel = mTvInputManagerHelper.getTvInputApplicationLabel(channel.getInputId());
+ if (appLabel != null) {
+ mAppInfoView.setText(appLabel);
+ } else {
+ new AsyncTask<Void, Void, CharSequence>() {
+ private final String mLoadTvInputId = mChannel.getInputId();
+
+ @Override
+ protected CharSequence doInBackground(Void... params) {
+ if (appInfo != null) {
+ return mPackageManager.getApplicationLabel(appInfo);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(CharSequence appLabel) {
+ mTvInputManagerHelper.setTvInputApplicationLabel(
+ mLoadTvInputId, appLabel);
+ if (mLoadTvInputId != mChannel.getInputId() || !isAttachedToWindow()) {
+ return;
+ }
+ mAppInfoView.setText(appLabel);
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
if (!TextUtils.isEmpty(mChannel.getAppLinkIconUri())) {
mChannel.loadBitmap(getContext(), Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON,
- mIconWidth, mIconHeight, createChannelLogoCallback(this, mChannel,
- Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON));
+ mIconWidth, mIconHeight,
+ createChannelLogoCallback(
+ this, mChannel, Channel.LOAD_IMAGE_TYPE_APP_LINK_ICON));
} else if (appInfo.icon != 0) {
- Drawable appIcon = mPackageManager.getApplicationIcon(appInfo);
- BitmapUtils.setColorFilterToDrawable(mIconColorFilter, appIcon);
- appIcon.setBounds(0, 0, mIconWidth, mIconHeight);
- mAppInfoView.setCompoundDrawables(appIcon, null, null, null);
+ Drawable appIcon =
+ mTvInputManagerHelper.getTvInputApplicationIcon(mChannel.getInputId());
+ if (appIcon != null) {
+ BitmapUtils.setColorFilterToDrawable(mIconColorFilter, appIcon);
+ appIcon.setBounds(0, 0, mIconWidth, mIconHeight);
+ mAppInfoView.setCompoundDrawablesRelative(appIcon, null, null, null);
+ } else {
+ new AsyncTask<Void, Void, Drawable>() {
+ private final String mLoadTvInputId = mChannel.getInputId();
+
+ @Override
+ protected Drawable doInBackground(Void... params) {
+ if (appInfo != null) {
+ return mPackageManager.getApplicationIcon(appInfo);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Drawable appIcon) {
+ mTvInputManagerHelper.setTvInputApplicationIcon(
+ mLoadTvInputId, appIcon);
+ if (mLoadTvInputId != mChannel.getInputId()
+ || !isAttachedToWindow()) {
+ return;
+ }
+ BitmapUtils.setColorFilterToDrawable(mIconColorFilter, appIcon);
+ appIcon.setBounds(0, 0, mIconWidth, mIconHeight);
+ mAppInfoView.setCompoundDrawablesRelative(appIcon, null, null, null);
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
}
break;
case Channel.APP_LINK_TYPE_APP:
- setText(getContext().getString(
- R.string.channels_item_app_link_app_launcher,
- mPackageManager.getApplicationLabel(appInfo)));
+ appLabel = mTvInputManagerHelper.getTvInputApplicationLabel(mChannel.getInputId());
+ if (appLabel != null) {
+ setText(getContext()
+ .getString(R.string.channels_item_app_link_app_launcher, appLabel));
+ } else {
+ new AsyncTask<Void, Void, CharSequence>() {
+ private final String mLoadTvInputId = mChannel.getInputId();
+
+ @Override
+ protected CharSequence doInBackground(Void... params) {
+ if (appInfo != null) {
+ return mPackageManager.getApplicationLabel(appInfo);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(CharSequence appLabel) {
+ mTvInputManagerHelper.setTvInputApplicationLabel(
+ mLoadTvInputId, appLabel);
+ if (mLoadTvInputId != mChannel.getInputId() || !isAttachedToWindow()) {
+ return;
+ }
+ setText(getContext()
+ .getString(
+ R.string.channels_item_app_link_app_launcher,
+ appLabel));
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
mAppInfoView.setVisibility(GONE);
- mGradientView.setVisibility(GONE);
break;
default:
mAppInfoView.setVisibility(GONE);
- mGradientView.setVisibility(GONE);
Log.d(TAG, "Should not be here.");
}
@@ -148,8 +228,6 @@ public class AppLinkCardView extends BaseCardView<Channel> {
} else {
setCardImageWithBanner(appInfo);
}
- // Call super.onBind() at the end intentionally. In order to correctly handle extension of
- // text view, text should be set before calling super.onBind.
super.onBind(channel, selected);
}
@@ -182,13 +260,14 @@ public class AppLinkCardView extends BaseCardView<Channel> {
}
}
BitmapUtils.setColorFilterToDrawable(mIconColorFilter, drawable);
- mAppInfoView.setCompoundDrawables(drawable, null, null, null);
+ mAppInfoView.setCompoundDrawablesRelative(drawable, null, null, null);
} else if (type == Channel.LOAD_IMAGE_TYPE_APP_LINK_POSTER_ART) {
if (bitmap == null) {
setCardImageWithBanner(
mTvInputManagerHelper.getTvInputAppInfo(mChannel.getInputId()));
} else {
mImageView.setImageBitmap(bitmap);
+ mImageView.setForeground(getContext().getDrawable(R.drawable.card_image_gradient));
if (mChannel.getAppLinkColor() == 0) {
extractAndSetMetaViewBackgroundColor(bitmap);
}
@@ -200,7 +279,6 @@ public class AppLinkCardView extends BaseCardView<Channel> {
protected void onFinishInflate() {
super.onFinishInflate();
mImageView = (ImageView) findViewById(R.id.image);
- mGradientView = findViewById(R.id.image_gradient);
mAppInfoView = (TextView) findViewById(R.id.app_info);
mMetaViewHolder = findViewById(R.id.app_link_text_holder);
}
@@ -209,37 +287,85 @@ public class AppLinkCardView extends BaseCardView<Channel> {
// 1) Provided poster art image, 2) Activity banner, 3) Activity icon, 4) Application banner,
// 5) Application icon, and 6) default image.
private void setCardImageWithBanner(ApplicationInfo appInfo) {
- Drawable banner = null;
- if (mIntent != null) {
- try {
- banner = mPackageManager.getActivityBanner(mIntent);
- if (banner == null) {
- banner = mPackageManager.getActivityIcon(mIntent);
+ new AsyncTask<Void, Void, Drawable>() {
+ private String mLoadTvInputId = mChannel.getInputId();
+ @Override
+ protected Drawable doInBackground(Void... params) {
+ Drawable banner = null;
+ if (mIntent != null) {
+ try {
+ banner = mPackageManager.getActivityBanner(mIntent);
+ if (banner == null) {
+ banner = mPackageManager.getActivityIcon(mIntent);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // do nothing.
+ }
}
- } catch (PackageManager.NameNotFoundException e) {
- // do nothing.
+ return banner;
}
- }
- if (banner == null && appInfo != null) {
- if (appInfo.banner != 0) {
- banner = mPackageManager.getApplicationBanner(appInfo);
- }
- if (banner == null && appInfo.icon != 0) {
- banner = mPackageManager.getApplicationIcon(appInfo);
+ @Override
+ protected void onPostExecute(Drawable banner) {
+ if (mLoadTvInputId != mChannel.getInputId() || !isAttachedToWindow()) {
+ return;
+ }
+ if (banner != null) {
+ setCardImageWithBannerInternal(banner);
+ } else {
+ setCardImageWithApplicationInfoBanner(appInfo);
+ }
}
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ private void setCardImageWithApplicationInfoBanner(ApplicationInfo appInfo) {
+ Drawable appBanner =
+ mTvInputManagerHelper.getTvInputApplicationBanner(mChannel.getInputId());
+ if (appBanner != null) {
+ setCardImageWithBannerInternal(appBanner);
+ } else {
+ new AsyncTask<Void, Void, Drawable>() {
+ private final String mLoadTvInputId = mChannel.getInputId();
+ @Override
+ protected Drawable doInBackground(Void... params) {
+ Drawable banner = null;
+ if (appInfo != null) {
+ if (appInfo.banner != 0) {
+ banner = mPackageManager.getApplicationBanner(appInfo);
+ }
+ if (banner == null && appInfo.icon != 0) {
+ banner = mPackageManager.getApplicationIcon(appInfo);
+ }
+ }
+ return banner;
+ }
+
+ @Override
+ protected void onPostExecute(Drawable banner) {
+ mTvInputManagerHelper.setTvInputApplicationBanner(
+ mLoadTvInputId, banner);
+ if (mLoadTvInputId != mChannel.getInputId() || !isAttachedToWindow()) {
+ return;
+ }
+ setCardImageWithBannerInternal(banner);
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
+ }
+ private void setCardImageWithBannerInternal(Drawable banner) {
if (banner == null) {
mImageView.setImageResource(R.drawable.ic_recent_thumbnail_default);
mImageView.setBackgroundResource(R.color.channel_card);
} else {
- Bitmap bitmap =
- Bitmap.createBitmap(mCardImageWidth, mCardImageHeight, Bitmap.Config.ARGB_8888);
+ Bitmap bitmap = Bitmap.createBitmap(
+ mCardImageWidth, mCardImageHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
banner.setBounds(0, 0, mCardImageWidth, mCardImageHeight);
banner.draw(canvas);
mImageView.setImageDrawable(banner);
+ mImageView.setForeground(getContext().getDrawable(R.drawable.card_image_gradient));
if (mChannel.getAppLinkColor() == 0) {
extractAndSetMetaViewBackgroundColor(bitmap);
}
diff --git a/src/com/android/tv/menu/BaseCardView.java b/src/com/android/tv/menu/BaseCardView.java
index c6a34a5d..fa74ce3e 100644
--- a/src/com/android/tv/menu/BaseCardView.java
+++ b/src/com/android/tv/menu/BaseCardView.java
@@ -57,6 +57,7 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
private TextView mTextViewFocused;
private final int mCardImageWidth;
private final float mCardHeight;
+ private boolean mSelected;
public BaseCardView(Context context) {
this(context, null);
@@ -103,23 +104,9 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
/**
* Called when the view is displayed.
- *
- * Before onBind is called, this view's text should be set to determine if it'll be extended
- * or not in focus state.
*/
@Override
public void onBind(T item, boolean selected) {
- if (mTextView != null && mTextViewFocused != null) {
- mTextViewFocused.measure(
- MeasureSpec.makeMeasureSpec(mCardImageWidth, MeasureSpec.EXACTLY),
- MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
- mExtendViewOnFocus = mTextViewFocused.getLineCount() > 1;
- if (mExtendViewOnFocus) {
- setTextViewFocusedAlpha(selected ? 1f : 0f);
- } else {
- setTextViewFocusedAlpha(1f);
- }
- }
setFocusAnimatedValue(selected ? SCALE_FACTOR_1F : SCALE_FACTOR_0F);
}
@@ -128,6 +115,7 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
@Override
public void onSelected() {
+ mSelected = true;
if (isAttachedToWindow() && getVisibility() == View.VISIBLE) {
startFocusAnimation(SCALE_FACTOR_1F);
} else {
@@ -138,6 +126,7 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
@Override
public void onDeselected() {
+ mSelected = false;
if (isAttachedToWindow() && getVisibility() == View.VISIBLE) {
startFocusAnimation(SCALE_FACTOR_0F);
} else {
@@ -156,6 +145,7 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
if (mTextView != null) {
mTextView.setText(resId);
}
+ onTextViewUpdated();
}
/**
@@ -168,6 +158,22 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
if (mTextView != null) {
mTextView.setText(text);
}
+ onTextViewUpdated();
+ }
+
+ private void onTextViewUpdated() {
+ if (mTextView != null && mTextViewFocused != null) {
+ mTextViewFocused.measure(
+ MeasureSpec.makeMeasureSpec(mCardImageWidth, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ mExtendViewOnFocus = mTextViewFocused.getLineCount() > 1;
+ if (mExtendViewOnFocus) {
+ setTextViewFocusedAlpha(mSelected ? 1f : 0f);
+ } else {
+ setTextViewFocusedAlpha(1f);
+ }
+ }
+ setFocusAnimatedValue(mSelected ? SCALE_FACTOR_1F : SCALE_FACTOR_0F);
}
/**
@@ -209,12 +215,18 @@ public abstract class BaseCardView<T> extends LinearLayout implements ItemListRo
setScaleX(scale);
setScaleY(scale);
setTranslationZ(mFocusTranslationZ * animatedValue);
- if (mExtendViewOnFocus) {
+ if (mTextView != null && mTextViewFocused != null) {
ViewGroup.LayoutParams params = mTextView.getLayoutParams();
- params.height = Math.round(mTextViewHeight
- + (mExtendedTextViewHeight - mTextViewHeight) * animatedValue);
- setTextViewLayoutParams(params);
- setTextViewFocusedAlpha(animatedValue);
+ int height = mExtendViewOnFocus ? Math.round(mTextViewHeight
+ + (mExtendedTextViewHeight - mTextViewHeight) * animatedValue)
+ : (int) mTextViewHeight;
+ if (height != params.height) {
+ params.height = height;
+ setTextViewLayoutParams(params);
+ }
+ if (mExtendViewOnFocus) {
+ setTextViewFocusedAlpha(animatedValue);
+ }
}
}
diff --git a/src/com/android/tv/menu/ChannelCardView.java b/src/com/android/tv/menu/ChannelCardView.java
index 1c8015a6..4ee56892 100644
--- a/src/com/android/tv/menu/ChannelCardView.java
+++ b/src/com/android/tv/menu/ChannelCardView.java
@@ -45,7 +45,6 @@ public class ChannelCardView extends BaseCardView<Channel> {
private final int mCardImageHeight;
private ImageView mImageView;
- private View mGradientView;
private TextView mChannelNumberNameView;
private ProgressBar mProgressBar;
private Channel mChannel;
@@ -71,7 +70,6 @@ public class ChannelCardView extends BaseCardView<Channel> {
protected void onFinishInflate() {
super.onFinishInflate();
mImageView = (ImageView) findViewById(R.id.image);
- mGradientView = findViewById(R.id.image_gradient);
mChannelNumberNameView = (TextView) findViewById(R.id.channel_number_and_name);
mProgressBar = (ProgressBar) findViewById(R.id.progress);
}
@@ -88,7 +86,7 @@ public class ChannelCardView extends BaseCardView<Channel> {
mChannelNumberNameView.setVisibility(VISIBLE);
mImageView.setImageResource(R.drawable.ic_recent_thumbnail_default);
mImageView.setBackgroundResource(R.color.channel_card);
- mGradientView.setVisibility(View.GONE);
+ mImageView.setForeground(null);
mProgressBar.setVisibility(GONE);
setTextViewEnabled(true);
@@ -101,8 +99,6 @@ public class ChannelCardView extends BaseCardView<Channel> {
}
updateProgramInformation();
- // Call super.onBind() at the end intentionally. In order to correctly handle extension of
- // text view, text should be set before calling super.onBind.
super.onBind(channel, selected);
}
@@ -123,7 +119,7 @@ public class ChannelCardView extends BaseCardView<Channel> {
private void updatePosterArt(Bitmap posterArt) {
mImageView.setImageBitmap(posterArt);
- mGradientView.setVisibility(View.VISIBLE);
+ mImageView.setForeground(getContext().getDrawable(R.drawable.card_image_gradient));
}
private void updateProgramInformation() {
diff --git a/src/com/android/tv/menu/ChannelsRow.java b/src/com/android/tv/menu/ChannelsRow.java
index dedf0993..490d73de 100644
--- a/src/com/android/tv/menu/ChannelsRow.java
+++ b/src/com/android/tv/menu/ChannelsRow.java
@@ -26,8 +26,14 @@ import com.android.tv.recommendation.Recommender;
public class ChannelsRow extends ItemListRow {
public static final String ID = ChannelsRow.class.getName();
- private static final int MIN_COUNT_FOR_RECENT_CHANNELS = 5;
- private static final int MAX_COUNT_FOR_RECENT_CHANNELS = 10;
+ /**
+ * Minimum count for recent channels.
+ */
+ public static final int MIN_COUNT_FOR_RECENT_CHANNELS = 5;
+ /**
+ * Maximum count for recent channels.
+ */
+ public static final int MAX_COUNT_FOR_RECENT_CHANNELS = 10;
private Recommender mTvRecommendation;
private ChannelsRowAdapter mChannelsAdapter;
diff --git a/src/com/android/tv/menu/ChannelsRowAdapter.java b/src/com/android/tv/menu/ChannelsRowAdapter.java
index c8e1bd05..4ba6a93a 100644
--- a/src/com/android/tv/menu/ChannelsRowAdapter.java
+++ b/src/com/android/tv/menu/ChannelsRowAdapter.java
@@ -31,7 +31,6 @@ import com.android.tv.dvr.DvrDataManager;
import com.android.tv.recommendation.Recommender;
import com.android.tv.util.SetupUtils;
import com.android.tv.util.TvInputManagerHelper;
-import com.android.tv.util.Utils;
import java.util.ArrayList;
import java.util.List;
@@ -130,8 +129,6 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channel>
@Override
public void onBindViewHolder(MyViewHolder viewHolder, int position) {
- super.onBindViewHolder(viewHolder, position);
-
int viewType = getItemViewType(position);
if (viewType == R.layout.menu_card_guide) {
viewHolder.itemView.setOnClickListener(mGuideOnClickListener);
@@ -147,6 +144,7 @@ public class ChannelsRowAdapter extends ItemListRowView.ItemListAdapter<Channel>
viewHolder.itemView.setTag(getItemList().get(position));
viewHolder.itemView.setOnClickListener(mChannelOnClickListener);
}
+ super.onBindViewHolder(viewHolder, position);
}
@Override
diff --git a/src/com/android/tv/menu/ItemListRowView.java b/src/com/android/tv/menu/ItemListRowView.java
index 4919c595..01257628 100644
--- a/src/com/android/tv/menu/ItemListRowView.java
+++ b/src/com/android/tv/menu/ItemListRowView.java
@@ -28,6 +28,7 @@ import android.view.ViewGroup;
import com.android.tv.MainActivity;
import com.android.tv.R;
+import com.android.tv.util.ViewCache;
import java.util.Collections;
import java.util.List;
@@ -194,9 +195,20 @@ public class ItemListRowView extends MenuRowView implements OnChildSelectedListe
return mItemList.size();
}
+ /**
+ * Returns the position of the item.
+ */
+ protected int getItemPosition(T item) {
+ return mItemList.indexOf(item);
+ }
+
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View view = mLayoutInflater.inflate(getLayoutResId(viewType), parent, false);
+ int resId = getLayoutResId(viewType);
+ View view = ViewCache.getInstance().getView(resId);
+ if (view == null) {
+ view = mLayoutInflater.inflate(resId, parent, false);
+ }
return new MyViewHolder(view);
}
diff --git a/src/com/android/tv/menu/Menu.java b/src/com/android/tv/menu/Menu.java
index 1160a5b5..25e629c1 100644
--- a/src/com/android/tv/menu/Menu.java
+++ b/src/com/android/tv/menu/Menu.java
@@ -27,23 +27,30 @@ import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
import com.android.tv.ChannelTuner;
import com.android.tv.R;
import com.android.tv.TvApplication;
-import com.android.tv.analytics.DurationTimer;
+import com.android.tv.TvOptionsManager;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.TvCommonUtils;
import com.android.tv.common.WeakHandler;
import com.android.tv.menu.MenuRowFactory.PartnerRow;
-import com.android.tv.menu.MenuRowFactory.PipOptionsRow;
import com.android.tv.menu.MenuRowFactory.TvOptionsRow;
import com.android.tv.ui.TunableTvView;
+import com.android.tv.util.DurationTimer;
+import com.android.tv.util.ViewCache;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* A class which controls the menu.
@@ -81,10 +88,21 @@ public class Menu {
sRowIdListForReason.add(PlayControlsRow.ID); // REASON_PLAY_CONTROLS_JUMP_TO_NEXT
}
+ private static final Map<Integer, Integer> PRELOAD_VIEW_IDS = new HashMap<>();
+ static {
+ PRELOAD_VIEW_IDS.put(R.layout.menu_card_guide, 1);
+ PRELOAD_VIEW_IDS.put(R.layout.menu_card_setup, 1);
+ PRELOAD_VIEW_IDS.put(R.layout.menu_card_dvr, 1);
+ PRELOAD_VIEW_IDS.put(R.layout.menu_card_app_link, 1);
+ PRELOAD_VIEW_IDS.put(R.layout.menu_card_channel, ChannelsRow.MAX_COUNT_FOR_RECENT_CHANNELS);
+ PRELOAD_VIEW_IDS.put(R.layout.menu_card_action, 7);
+ }
+
private static final String SCREEN_NAME = "Menu";
private static final int MSG_HIDE_MENU = 1000;
+ private final Context mContext;
private final IMenuView mMenuView;
private final Tracker mTracker;
private final DurationTimer mVisibleTimer = new DurationTimer();
@@ -103,15 +121,16 @@ public class Menu {
@VisibleForTesting
Menu(Context context, IMenuView menuView, MenuRowFactory menuRowFactory,
OnMenuVisibilityChangeListener onMenuVisibilityChangeListener) {
- this(context, null, menuView, menuRowFactory, onMenuVisibilityChangeListener);
+ this(context, null, null, menuView, menuRowFactory, onMenuVisibilityChangeListener);
}
- public Menu(Context context, TunableTvView tvView, IMenuView menuView,
- MenuRowFactory menuRowFactory,
+ public Menu(Context context, TunableTvView tvView, TvOptionsManager optionsManager,
+ IMenuView menuView, MenuRowFactory menuRowFactory,
OnMenuVisibilityChangeListener onMenuVisibilityChangeListener) {
+ mContext = context;
mMenuView = menuView;
mTracker = TvApplication.getSingletons(context).getTracker();
- mMenuUpdater = new MenuUpdater(context, tvView, this);
+ mMenuUpdater = new MenuUpdater(this, tvView, optionsManager);
Resources res = context.getResources();
mShowDurationMillis = res.getInteger(R.integer.menu_show_duration);
mOnMenuVisibilityChangeListener = onMenuVisibilityChangeListener;
@@ -130,7 +149,6 @@ public class Menu {
addMenuRow(menuRowFactory.createMenuRow(this, ChannelsRow.class));
addMenuRow(menuRowFactory.createMenuRow(this, PartnerRow.class));
addMenuRow(menuRowFactory.createMenuRow(this, TvOptionsRow.class));
- addMenuRow(menuRowFactory.createMenuRow(this, PipOptionsRow.class));
mMenuView.setMenuRows(mMenuRows);
}
@@ -160,6 +178,23 @@ public class Menu {
}
/**
+ * Preloads the item view used for the menu.
+ */
+ public void preloadItemViews() {
+ LayoutInflater inflater =
+ (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ // Use a fake parent to make the layoutParams set correctly.
+ ViewGroup fakeParent = new LinearLayout(mContext);
+ for (int id : PRELOAD_VIEW_IDS.keySet()) {
+ int count = PRELOAD_VIEW_IDS.get(id);
+ for (int i = 0; i < count; i++) {
+ View view = inflater.inflate(id, fakeParent, false);
+ ViewCache.getInstance().putView(id, view);
+ }
+ }
+ }
+
+ /**
* Shows the main menu.
*
* @param reason A reason why this is called. See {@link MenuShowReason}
diff --git a/src/com/android/tv/menu/MenuAction.java b/src/com/android/tv/menu/MenuAction.java
index 0d59552a..b4356059 100644
--- a/src/com/android/tv/menu/MenuAction.java
+++ b/src/com/android/tv/menu/MenuAction.java
@@ -20,9 +20,9 @@ import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
-import com.android.tv.MainActivity;
import com.android.tv.R;
import com.android.tv.TvOptionsManager;
+import com.android.tv.TvOptionsManager.OptionType;
/**
* A class to define possible actions from main menu.
@@ -36,12 +36,9 @@ public class MenuAction {
public static final MenuAction SELECT_DISPLAY_MODE_ACTION =
new MenuAction(R.string.options_item_display_mode, TvOptionsManager.OPTION_DISPLAY_MODE,
R.drawable.ic_tvoption_aspect);
- public static final MenuAction PIP_IN_APP_ACTION =
- new MenuAction(R.string.options_item_pip, TvOptionsManager.OPTION_IN_APP_PIP,
- R.drawable.ic_tvoption_pip);
public static final MenuAction SYSTEMWIDE_PIP_ACTION =
new MenuAction(R.string.options_item_pip, TvOptionsManager.OPTION_SYSTEMWIDE_PIP,
- R.drawable.ic_pip_option_layout2);
+ R.drawable.ic_tvoption_pip);
public static final MenuAction SELECT_AUDIO_LANGUAGE_ACTION =
new MenuAction(R.string.options_item_multi_audio, TvOptionsManager.OPTION_MULTI_AUDIO,
R.drawable.ic_tvoption_multi_track);
@@ -51,34 +48,36 @@ public class MenuAction {
public static final MenuAction DEV_ACTION =
new MenuAction(R.string.options_item_developer,
TvOptionsManager.OPTION_DEVELOPER, R.drawable.ic_developer_mode_tv_white_48dp);
- // TODO: Change the icon.
public static final MenuAction SETTINGS_ACTION =
new MenuAction(R.string.options_item_settings, TvOptionsManager.OPTION_SETTINGS,
R.drawable.ic_settings);
- // Actions in the PIP option row.
- public static final MenuAction PIP_SELECT_INPUT_ACTION =
- new MenuAction(R.string.pip_options_item_source, TvOptionsManager.OPTION_PIP_INPUT,
- R.drawable.ic_pip_option_input);
- public static final MenuAction PIP_SWAP_ACTION =
- new MenuAction(R.string.pip_options_item_swap, TvOptionsManager.OPTION_PIP_SWAP,
- R.drawable.ic_pip_option_swap);
- public static final MenuAction PIP_SOUND_ACTION =
- new MenuAction(R.string.pip_options_item_sound, TvOptionsManager.OPTION_PIP_SOUND,
- R.drawable.ic_pip_option_swap_audio);
- public static final MenuAction PIP_LAYOUT_ACTION =
- new MenuAction(R.string.pip_options_item_layout, TvOptionsManager.OPTION_PIP_LAYOUT,
- R.drawable.ic_pip_option_layout1);
- public static final MenuAction PIP_SIZE_ACTION =
- new MenuAction(R.string.pip_options_item_size, TvOptionsManager.OPTION_PIP_SIZE,
- R.drawable.ic_pip_option_size);
private final String mActionName;
private final int mActionNameResId;
- private final int mType;
+ @OptionType private final int mType;
+ private String mActionDescription;
private Drawable mDrawable;
private int mDrawableResId;
private boolean mEnabled = true;
+ /**
+ * Sets the action description. Returns {@code trye} if the description is changed.
+ */
+ public static boolean setActionDescription(MenuAction action, String actionDescription) {
+ String oldDescription = action.mActionDescription;
+ action.mActionDescription = actionDescription;
+ return !TextUtils.equals(action.mActionDescription, oldDescription);
+ }
+
+ /**
+ * Enables or disables the action. Returns {@code true} if the value is changed.
+ */
+ public static boolean setEnabled(MenuAction action, boolean enabled) {
+ boolean changed = action.mEnabled != enabled;
+ action.mEnabled = enabled;
+ return changed;
+ }
+
public MenuAction(int actionNameResId, int type, int drawableResId) {
mActionName = null;
mActionNameResId = actionNameResId;
@@ -102,11 +101,11 @@ public class MenuAction {
return context.getString(mActionNameResId);
}
- public String getActionDescription(Context context) {
- return ((MainActivity) context).getTvOptionsManager().getOptionString(mType);
+ public String getActionDescription() {
+ return mActionDescription;
}
- public int getType() {
+ @OptionType public int getType() {
return mType;
}
@@ -120,28 +119,10 @@ public class MenuAction {
return mDrawable;
}
- /**
- * Sets drawable resource id.
- *
- * @return {@code true} if drawable is changed.
- */
- public boolean setDrawableResId(int resId) {
- if (mDrawableResId == resId) {
- return false;
- }
- mDrawable = null;
- mDrawableResId = resId;
- return true;
- }
-
public boolean isEnabled() {
return mEnabled;
}
- public void setEnabled(boolean enabled) {
- mEnabled = enabled;
- }
-
public int getActionNameResId() {
return mActionNameResId;
}
diff --git a/src/com/android/tv/menu/MenuLayoutManager.java b/src/com/android/tv/menu/MenuLayoutManager.java
index 6c767247..a16ac197 100644
--- a/src/com/android/tv/menu/MenuLayoutManager.java
+++ b/src/com/android/tv/menu/MenuLayoutManager.java
@@ -384,10 +384,15 @@ public class MenuLayoutManager {
mSelectedPosition = position;
if (DEBUG) dumpChildren("startRowAnimation()");
- MenuRowView currentView = mMenuRowViews.get(position);
// Show the children of the next row.
- currentView.getTitleView().setVisibility(View.VISIBLE);
- currentView.getContentsView().setVisibility(View.VISIBLE);
+ final MenuRowView currentView = mMenuRowViews.get(position);
+ TextView currentTitleView = currentView.getTitleView();
+ View currentContentsView = currentView.getContentsView();
+ currentTitleView.setVisibility(View.VISIBLE);
+ currentContentsView.setVisibility(View.VISIBLE);
+ if (currentView instanceof PlayControlsRowView) {
+ ((PlayControlsRowView) currentView).onPreselected();
+ }
// Request focus after the new contents view shows up.
mMenuView.requestFocus();
if (mTempTitleViewForOld == null) {
@@ -407,7 +412,7 @@ public class MenuLayoutManager {
// Old row.
MenuRow oldRow = mMenuRows.get(oldPosition);
- MenuRowView oldView = mMenuRowViews.get(oldPosition);
+ final MenuRowView oldView = mMenuRowViews.get(oldPosition);
View oldContentsView = oldView.getContentsView();
// Old contents view.
animators.add(createAlphaAnimator(oldContentsView, 1.0f, 0.0f, 1.0f, mLinearOutSlowIn)
@@ -468,8 +473,6 @@ public class MenuLayoutManager {
}
// Current row.
Rect currentLayoutRect = new Rect(layouts.get(position));
- TextView currentTitleView = currentView.getTitleView();
- View currentContentsView = currentView.getContentsView();
currentContentsView.setAlpha(0.0f);
if (scrollDown) {
// Current title view.
@@ -572,9 +575,8 @@ public class MenuLayoutManager {
for (ViewPropertyValueHolder holder : propertyValuesAfterAnimation) {
holder.property.set(holder.view, holder.value);
}
- oldTitleView.setVisibility(View.VISIBLE);
- mMenuRowViews.get(oldPosition).onDeselected();
- mMenuRowViews.get(position).onSelected(true);
+ oldView.onDeselected();
+ currentView.onSelected(true);
mTempTitleViewForOld.setVisibility(View.GONE);
mTempTitleViewForCurrent.setVisibility(View.GONE);
layout(mMenuView.getLeft(), mMenuView.getTop(), mMenuView.getRight(),
diff --git a/src/com/android/tv/menu/MenuRowFactory.java b/src/com/android/tv/menu/MenuRowFactory.java
index c67a0e04..2d5453fe 100644
--- a/src/com/android/tv/menu/MenuRowFactory.java
+++ b/src/com/android/tv/menu/MenuRowFactory.java
@@ -67,8 +67,6 @@ public class MenuRowFactory {
} else if (TvOptionsRow.class.equals(key)) {
return new TvOptionsRow(mMainActivity, menu, mTvCustomizationManager
.getCustomActions(TvCustomizationManager.ID_OPTIONS_ROW));
- } else if (PipOptionsRow.class.equals(key)) {
- return new PipOptionsRow(mMainActivity, menu);
}
return null;
}
@@ -77,6 +75,9 @@ public class MenuRowFactory {
* A menu row which represents the TV options row.
*/
public static class TvOptionsRow extends ItemListRow {
+ /** The ID of the row. */
+ public static final String ID = TvOptionsRow.class.getName();
+
private TvOptionsRow(Context context, Menu menu, List<CustomAction> customActions) {
super(context, menu, R.string.menu_title_options, R.dimen.action_card_height,
new TvOptionsRowAdapter(context, customActions));
@@ -91,25 +92,6 @@ public class MenuRowFactory {
}
/**
- * A menu row which represents the PIP options row.
- */
- public static class PipOptionsRow extends ItemListRow {
- private final MainActivity mMainActivity;
-
- private PipOptionsRow(Context context, Menu menu) {
- super(context, menu, R.string.menu_title_pip_options, R.dimen.action_card_height,
- new PipOptionsRowAdapter(context));
- mMainActivity = (MainActivity) context;
- }
-
- @Override
- public boolean isVisible() {
- // TODO: Remove the dependency on MainActivity.
- return super.isVisible() && mMainActivity.isPipEnabled();
- }
- }
-
- /**
* A menu row which represents the partner row.
*/
public static class PartnerRow extends ItemListRow {
diff --git a/src/com/android/tv/menu/MenuUpdater.java b/src/com/android/tv/menu/MenuUpdater.java
index 075b299e..7ad38e74 100644
--- a/src/com/android/tv/menu/MenuUpdater.java
+++ b/src/com/android/tv/menu/MenuUpdater.java
@@ -16,11 +16,14 @@
package com.android.tv.menu;
-import android.content.Context;
import android.support.annotation.Nullable;
import com.android.tv.ChannelTuner;
+import com.android.tv.TvOptionsManager;
+import com.android.tv.TvOptionsManager.OptionChangedListener;
+import com.android.tv.TvOptionsManager.OptionType;
import com.android.tv.data.Channel;
+import com.android.tv.menu.MenuRowFactory.TvOptionsRow;
import com.android.tv.ui.TunableTvView;
import com.android.tv.ui.TunableTvView.OnScreenBlockingChangedListener;
@@ -30,10 +33,10 @@ import com.android.tv.ui.TunableTvView.OnScreenBlockingChangedListener;
* <p>As the menu is updated when it shows up, this class handles only the dynamic updates.
*/
public class MenuUpdater {
- // Can be null for testing.
- @Nullable
- private final TunableTvView mTvView;
private final Menu mMenu;
+ // Can be null for testing.
+ @Nullable private final TunableTvView mTvView;
+ @Nullable private final TvOptionsManager mOptionsManager;
private ChannelTuner mChannelTuner;
private final ChannelTuner.Listener mChannelTunerListener = new ChannelTuner.Listener() {
@@ -42,7 +45,7 @@ public class MenuUpdater {
@Override
public void onBrowsableChannelListChanged() {
- mMenu.update();
+ mMenu.update(ChannelsRow.ID);
}
@Override
@@ -53,10 +56,17 @@ public class MenuUpdater {
mMenu.update(ChannelsRow.ID);
}
};
+ private final OptionChangedListener mOptionChangeListener = new OptionChangedListener() {
+ @Override
+ public void onOptionChanged(@OptionType int optionType, String newString) {
+ mMenu.update(TvOptionsRow.ID);
+ }
+ };
- public MenuUpdater(Context context, TunableTvView tvView, Menu menu) {
- mTvView = tvView;
+ public MenuUpdater(Menu menu, TunableTvView tvView, TvOptionsManager optionsManager) {
mMenu = menu;
+ mTvView = tvView;
+ mOptionsManager = optionsManager;
if (mTvView != null) {
mTvView.setOnScreenBlockedListener(new OnScreenBlockingChangedListener() {
@Override
@@ -65,11 +75,18 @@ public class MenuUpdater {
}
});
}
+ if (mOptionsManager != null) {
+ mOptionsManager.setOptionChangedListener(TvOptionsManager.OPTION_CLOSED_CAPTIONS,
+ mOptionChangeListener);
+ mOptionsManager.setOptionChangedListener(TvOptionsManager.OPTION_DISPLAY_MODE,
+ mOptionChangeListener);
+ mOptionsManager.setOptionChangedListener(TvOptionsManager.OPTION_MULTI_AUDIO,
+ mOptionChangeListener);
+ }
}
/**
- * Sets the instance of {@link ChannelTuner}. Call this method when the channel tuner is ready
- * or not available any more.
+ * Sets the instance of {@link ChannelTuner}. Call this method when the channel tuner is ready.
*/
public void setChannelTuner(ChannelTuner channelTuner) {
if (mChannelTuner != null) {
@@ -79,7 +96,6 @@ public class MenuUpdater {
if (mChannelTuner != null) {
mChannelTuner.addListener(mChannelTunerListener);
}
- mMenu.update();
}
/**
@@ -92,5 +108,10 @@ public class MenuUpdater {
if (mTvView != null) {
mTvView.setOnScreenBlockedListener(null);
}
+ if (mOptionsManager != null) {
+ mOptionsManager.setOptionChangedListener(TvOptionsManager.OPTION_CLOSED_CAPTIONS, null);
+ mOptionsManager.setOptionChangedListener(TvOptionsManager.OPTION_DISPLAY_MODE, null);
+ mOptionsManager.setOptionChangedListener(TvOptionsManager.OPTION_MULTI_AUDIO, null);
+ }
}
}
diff --git a/src/com/android/tv/menu/OptionsRowAdapter.java b/src/com/android/tv/menu/OptionsRowAdapter.java
index 93bd0a4d..dd6194a1 100644
--- a/src/com/android/tv/menu/OptionsRowAdapter.java
+++ b/src/com/android/tv/menu/OptionsRowAdapter.java
@@ -21,8 +21,6 @@ import android.view.View;
import com.android.tv.R;
import com.android.tv.TvApplication;
-import com.android.tv.TvOptionsManager;
-import com.android.tv.TvOptionsManager.OptionChangedListener;
import com.android.tv.analytics.Tracker;
import java.util.List;
@@ -66,12 +64,9 @@ public abstract class OptionsRowAdapter extends ItemListRowView.ItemListAdapter<
public void update() {
if (mActionList == null) {
mActionList = createActions();
- updateActions();
setItemList(mActionList);
} else {
- if (updateActions()) {
- setItemList(mActionList);
- }
+ updateActions();
}
}
@@ -81,7 +76,7 @@ public abstract class OptionsRowAdapter extends ItemListRowView.ItemListAdapter<
}
protected abstract List<MenuAction> createActions();
- protected abstract boolean updateActions();
+ protected abstract void updateActions();
protected abstract void executeAction(int type);
/**
@@ -93,37 +88,6 @@ public abstract class OptionsRowAdapter extends ItemListRowView.ItemListAdapter<
return mActionList.get(position);
}
- /**
- * Sets the action at the given position.
- * Note that action at the position may differ from returned by {@link #createActions}.
- * See {@link CustomizableOptionsRowAdapter}
- */
- protected void setAction(int position, MenuAction action) {
- mActionList.set(position, action);
- }
-
- /**
- * Adds an action to the given position.
- * Note that action at the position may differ from returned by {@link #createActions}.
- * See {@link CustomizableOptionsRowAdapter}
- */
- protected void addAction(int position, MenuAction action) {
- mActionList.add(position, action);
- }
-
- /**
- * Removes an action at the given position.
- * Note that action at the position may differ from returned by {@link #createActions}.
- * See {@link CustomizableOptionsRowAdapter}
- */
- protected void removeAction(int position) {
- mActionList.remove(position);
- }
-
- protected int getActionSize() {
- return mActionList.size();
- }
-
@Override
public void onBindViewHolder(MyViewHolder viewHolder, int position) {
super.onBindViewHolder(viewHolder, position);
@@ -139,14 +103,4 @@ public abstract class OptionsRowAdapter extends ItemListRowView.ItemListAdapter<
// be preserved.
return mActionList.get(position).getType();
}
-
- protected void setOptionChangedListener(final MenuAction action) {
- TvOptionsManager om = getMainActivity().getTvOptionsManager();
- om.setOptionChangedListener(action.getType(), new OptionChangedListener() {
- @Override
- public void onOptionChanged(String newOption) {
- setItemList(mActionList);
- }
- });
- }
}
diff --git a/src/com/android/tv/menu/PartnerOptionsRowAdapter.java b/src/com/android/tv/menu/PartnerOptionsRowAdapter.java
index f3e09f80..c8249a4c 100644
--- a/src/com/android/tv/menu/PartnerOptionsRowAdapter.java
+++ b/src/com/android/tv/menu/PartnerOptionsRowAdapter.java
@@ -38,8 +38,7 @@ public class PartnerOptionsRowAdapter extends CustomizableOptionsRowAdapter {
}
@Override
- protected boolean updateActions() {
+ protected void updateActions() {
// TODO: Support adding description for custom actions.
- return false;
}
}
diff --git a/src/com/android/tv/menu/PipOptionsRowAdapter.java b/src/com/android/tv/menu/PipOptionsRowAdapter.java
deleted file mode 100644
index 87203e9d..00000000
--- a/src/com/android/tv/menu/PipOptionsRowAdapter.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.tv.menu;
-
-import android.content.Context;
-import android.text.TextUtils;
-
-import com.android.tv.MainActivity;
-import com.android.tv.R;
-import com.android.tv.TvOptionsManager;
-import com.android.tv.ui.TvViewUiManager;
-import com.android.tv.ui.sidepanel.PipInputSelectorFragment;
-import com.android.tv.util.PipInputManager.PipInput;
-import com.android.tv.util.TvSettings;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/*
- * An adapter of PIP options.
- */
-public class PipOptionsRowAdapter extends OptionsRowAdapter {
- private static final int[] DRAWABLE_ID_FOR_LAYOUT = {
- R.drawable.ic_pip_option_layout1,
- R.drawable.ic_pip_option_layout2,
- R.drawable.ic_pip_option_layout3,
- R.drawable.ic_pip_option_layout4,
- R.drawable.ic_pip_option_layout5 };
-
- private final TvOptionsManager mTvOptionsManager;
- private final TvViewUiManager mTvViewUiManager;
-
- public PipOptionsRowAdapter(Context context) {
- super(context);
- mTvOptionsManager = getMainActivity().getTvOptionsManager();
- mTvViewUiManager = getMainActivity().getTvViewUiManager();
- }
-
- @Override
- protected List<MenuAction> createActions() {
- List<MenuAction> actionList = new ArrayList<>();
- actionList.add(MenuAction.PIP_SELECT_INPUT_ACTION);
- actionList.add(MenuAction.PIP_SWAP_ACTION);
- actionList.add(MenuAction.PIP_SOUND_ACTION);
- actionList.add(MenuAction.PIP_LAYOUT_ACTION);
- actionList.add(MenuAction.PIP_SIZE_ACTION);
- for (MenuAction action : actionList) {
- setOptionChangedListener(action);
- }
- return actionList;
- }
-
- @Override
- public boolean updateActions() {
- boolean changed = false;
- if (updateSelectInputAction()) {
- changed = true;
- }
- if (updateLayoutAction()) {
- changed = true;
- }
- if (updateSizeAction()) {
- changed = true;
- }
- return changed;
- }
-
- private boolean updateSelectInputAction() {
- String oldInputLabel = mTvOptionsManager.getOptionString(TvOptionsManager.OPTION_PIP_INPUT);
-
- MainActivity tvActivity = getMainActivity();
- PipInput newInput = tvActivity.getPipInputManager().getPipInput(tvActivity.getPipChannel());
- String newInputLabel = newInput == null ? null : newInput.getLabel();
-
- if (!TextUtils.equals(oldInputLabel, newInputLabel)) {
- mTvOptionsManager.onPipInputChanged(newInputLabel);
- return true;
- }
- return false;
- }
-
- private boolean updateLayoutAction() {
- return MenuAction.PIP_LAYOUT_ACTION.setDrawableResId(
- DRAWABLE_ID_FOR_LAYOUT[mTvViewUiManager.getPipLayout()]);
- }
-
- private boolean updateSizeAction() {
- boolean oldEnabled = MenuAction.PIP_SIZE_ACTION.isEnabled();
- boolean newEnabled = mTvViewUiManager.getPipLayout() != TvSettings.PIP_LAYOUT_SIDE_BY_SIDE;
- if (oldEnabled != newEnabled) {
- MenuAction.PIP_SIZE_ACTION.setEnabled(newEnabled);
- return true;
- }
- return false;
- }
-
- @Override
- protected void executeAction(int type) {
- switch (type) {
- case TvOptionsManager.OPTION_PIP_INPUT:
- getMainActivity().getOverlayManager().getSideFragmentManager().show(
- new PipInputSelectorFragment());
- break;
- case TvOptionsManager.OPTION_PIP_SWAP:
- getMainActivity().swapPip();
- break;
- case TvOptionsManager.OPTION_PIP_SOUND:
- getMainActivity().togglePipSoundMode();
- break;
- case TvOptionsManager.OPTION_PIP_LAYOUT:
- int oldLayout = mTvViewUiManager.getPipLayout();
- int newLayout = (oldLayout + 1) % (TvSettings.PIP_LAYOUT_LAST + 1);
- mTvViewUiManager.setPipLayout(newLayout, true);
- MenuAction.PIP_LAYOUT_ACTION.setDrawableResId(DRAWABLE_ID_FOR_LAYOUT[newLayout]);
- break;
- case TvOptionsManager.OPTION_PIP_SIZE:
- int oldSize = mTvViewUiManager.getPipSize();
- int newSize = (oldSize + 1) % (TvSettings.PIP_SIZE_LAST + 1);
- mTvViewUiManager.setPipSize(newSize, true);
- break;
- }
- }
-}
diff --git a/src/com/android/tv/menu/PlayControlsButton.java b/src/com/android/tv/menu/PlayControlsButton.java
index aff39db3..77715f28 100644
--- a/src/com/android/tv/menu/PlayControlsButton.java
+++ b/src/com/android/tv/menu/PlayControlsButton.java
@@ -39,6 +39,9 @@ public class PlayControlsButton extends FrameLayout {
private final int mIconColor;
private int mIconFocusedColor;
+ private int mImageResourceId;
+ private int mTintColor;
+
public PlayControlsButton(Context context) {
this(context, null);
}
@@ -67,10 +70,21 @@ public class PlayControlsButton extends FrameLayout {
* Sets the resource ID of the image to be displayed in the center of this control.
*/
public void setImageResId(int imageResId) {
- mIcon.setImageResource(imageResId);
- // Since on foucus changing, icons' color should be switched with animation,
+ int newTintColor = hasFocus() ? mIconFocusedColor : mIconColor;
+ if (mImageResourceId != imageResId) {
+ mImageResourceId = imageResId;
+ mIcon.setImageResource(imageResId);
+ updateTint(newTintColor);
+ } else if (newTintColor != mTintColor) {
+ updateTint(newTintColor);
+ }
+ }
+
+ private void updateTint(int tintColor) {
+ mTintColor = tintColor;
+ // Since on focus changing, icons' color should be switched with animation,
// as a result, selectors cannot be used to switch colors in this case.
- mIcon.getDrawable().setTint(hasFocus() ? mIconFocusedColor : mIconColor);
+ mIcon.getDrawable().setTint(tintColor);
}
/**
@@ -117,7 +131,9 @@ public class PlayControlsButton extends FrameLayout {
} else {
mIcon.setVisibility(View.GONE);
mLabel.setVisibility(View.VISIBLE);
- mLabel.setText(label);
+ if (!TextUtils.equals(mLabel.getText(), label)) {
+ mLabel.setText(label);
+ }
}
}
diff --git a/src/com/android/tv/menu/PlayControlsRowView.java b/src/com/android/tv/menu/PlayControlsRowView.java
index a620d4dd..4d766788 100644
--- a/src/com/android/tv/menu/PlayControlsRowView.java
+++ b/src/com/android/tv/menu/PlayControlsRowView.java
@@ -18,10 +18,10 @@ package com.android.tv.menu;
import android.content.Context;
import android.content.res.Resources;
+import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.view.View;
-import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
@@ -34,17 +34,16 @@ import com.android.tv.common.SoftPreconditions;
import com.android.tv.common.feature.CommonFeatures;
import com.android.tv.data.Channel;
import com.android.tv.data.Program;
+import com.android.tv.dialog.HalfSizedDialogFragment;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrDataManager.OnDvrScheduleLoadFinishedListener;
import com.android.tv.dvr.DvrDataManager.ScheduledRecordingListener;
import com.android.tv.dvr.DvrManager;
-import com.android.tv.dvr.DvrUiHelper;
-import com.android.tv.dvr.ScheduledRecording;
+import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.ui.DvrStopRecordingFragment;
-import com.android.tv.dvr.ui.HalfSizedDialogFragment;
+import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.menu.Menu.MenuShowReason;
import com.android.tv.ui.TunableTvView;
-import com.android.tv.util.Utils;
public class PlayControlsRowView extends MenuRowView {
private static final int NORMAL_WIDTH_MAX_BUTTON_COUNT = 5;
@@ -53,14 +52,10 @@ public class PlayControlsRowView extends MenuRowView {
private final int mTimeTextLeftMargin;
private final int mTimelineWidth;
// Views
- private View mBackgroundView;
+ private TextView mBackgroundView;
private View mTimeIndicator;
private TextView mTimeText;
- private View mProgressEmptyBefore;
- private View mProgressWatched;
- private View mProgressBuffered;
- private View mProgressEmptyAfter;
- private View mControlBar;
+ private PlaybackProgressBar mProgress;
private PlayControlsButton mJumpPreviousButton;
private PlayControlsButton mRewindButton;
private PlayControlsButton mPlayPauseButton;
@@ -69,7 +64,6 @@ public class PlayControlsRowView extends MenuRowView {
private PlayControlsButton mRecordButton;
private TextView mProgramStartTimeText;
private TextView mProgramEndTimeText;
- private View mUnavailableMessageText;
private TunableTvView mTvView;
private TimeShiftManager mTimeShiftManager;
private final DvrDataManager mDvrDataManager;
@@ -83,6 +77,8 @@ public class PlayControlsRowView extends MenuRowView {
private final int mNormalButtonMargin;
private final int mCompactButtonMargin;
+ private final String mUnavailableMessage;
+
private final ScheduledRecordingListener mScheduledRecordingListener
= new ScheduledRecordingListener() {
@Override
@@ -138,6 +134,7 @@ public class PlayControlsRowView extends MenuRowView {
mDvrManager = null;
}
mMainActivity = (MainActivity) context;
+ mUnavailableMessage = res.getString(R.string.play_controls_unavailable);
}
@Override
@@ -171,14 +168,10 @@ public class PlayControlsRowView extends MenuRowView {
super.onFinishInflate();
// Clip the ViewGroup(body) to the rounded rectangle of outline.
findViewById(R.id.body).setClipToOutline(true);
- mBackgroundView = findViewById(R.id.background);
+ mBackgroundView = (TextView) findViewById(R.id.background);
mTimeIndicator = findViewById(R.id.time_indicator);
mTimeText = (TextView) findViewById(R.id.time_text);
- mProgressEmptyBefore = findViewById(R.id.timeline_bg_start);
- mProgressWatched = findViewById(R.id.watched);
- mProgressBuffered = findViewById(R.id.buffered);
- mProgressEmptyAfter = findViewById(R.id.timeline_bg_end);
- mControlBar = findViewById(R.id.play_control_bar);
+ mProgress = (PlaybackProgressBar) findViewById(R.id.progress);
mJumpPreviousButton = (PlayControlsButton) findViewById(R.id.jump_previous);
mRewindButton = (PlayControlsButton) findViewById(R.id.rewind);
mPlayPauseButton = (PlayControlsButton) findViewById(R.id.play_pause);
@@ -187,7 +180,6 @@ public class PlayControlsRowView extends MenuRowView {
mRecordButton = (PlayControlsButton) findViewById(R.id.record);
mProgramStartTimeText = (TextView) findViewById(R.id.program_start_time);
mProgramEndTimeText = (TextView) findViewById(R.id.program_end_time);
- mUnavailableMessageText = findViewById(R.id.unavailable_text);
initializeButton(mJumpPreviousButton, R.drawable.lb_ic_skip_previous,
R.string.play_controls_description_skip_previous, null, new Runnable() {
@@ -195,7 +187,7 @@ public class PlayControlsRowView extends MenuRowView {
public void run() {
if (mTimeShiftManager.isAvailable()) {
mTimeShiftManager.jumpToPrevious();
- updateControls();
+ updateControls(true);
}
}
});
@@ -235,7 +227,7 @@ public class PlayControlsRowView extends MenuRowView {
public void run() {
if (mTimeShiftManager.isAvailable()) {
mTimeShiftManager.jumpToNext();
- updateControls();
+ updateControls(true);
}
}
});
@@ -265,18 +257,17 @@ public class PlayControlsRowView extends MenuRowView {
if (!(mDvrManager != null && mDvrManager.isChannelRecordable(currentChannel))) {
Toast.makeText(mMainActivity, R.string.dvr_msg_cannot_record_channel,
Toast.LENGTH_SHORT).show();
- } else if (DvrUiHelper.checkStorageStatusAndShowErrorMessage(mMainActivity,
- currentChannel.getInputId())) {
+ } else {
Program program = TvApplication.getSingletons(mMainActivity).getProgramDataManager()
.getCurrentProgram(currentChannel.getId());
- if (program == null) {
- DvrUiHelper.showChannelRecordDurationOptions(mMainActivity, currentChannel);
- } else if (DvrUiHelper.handleCreateSchedule(mMainActivity, program)) {
- String msg = mMainActivity.getString(R.string.dvr_msg_current_program_scheduled,
- program.getTitle(),
- Utils.toTimeString(program.getEndTimeUtcMillis(), false));
- Toast.makeText(mMainActivity, msg, Toast.LENGTH_SHORT).show();
- }
+ DvrUiHelper.checkStorageStatusAndShowErrorMessage(mMainActivity,
+ currentChannel.getInputId(), new Runnable() {
+ @Override
+ public void run() {
+ DvrUiHelper.requestRecordingCurrentProgram(mMainActivity,
+ currentChannel, program, true);
+ }
+ });
}
} else if (currentChannel != null) {
DvrUiHelper.showStopRecordingDialog(mMainActivity, currentChannel.getId(),
@@ -318,39 +309,37 @@ public class PlayControlsRowView extends MenuRowView {
@Override
public void onAvailabilityChanged() {
updateMenuVisibility();
- if (isShown()) {
- PlayControlsRowView.this.updateAll();
- }
+ PlayControlsRowView.this.updateAll(false);
}
@Override
public void onPlayStatusChanged(int status) {
updateMenuVisibility();
- if (mTimeShiftManager.isAvailable() && isShown()) {
- updateControls();
+ if (mTimeShiftManager.isAvailable()) {
+ updateControls(false);
}
}
@Override
public void onRecordTimeRangeChanged() {
- if (mTimeShiftManager.isAvailable() && isShown()) {
- updateControls();
+ if (mTimeShiftManager.isAvailable()) {
+ updateControls(false);
}
}
@Override
public void onCurrentPositionChanged() {
- if (mTimeShiftManager.isAvailable() && isShown()) {
+ if (mTimeShiftManager.isAvailable()) {
initializeTimeline();
- updateControls();
+ updateControls(false);
}
}
@Override
public void onProgramInfoChanged() {
- if (mTimeShiftManager.isAvailable() && isShown()) {
+ if (mTimeShiftManager.isAvailable()) {
initializeTimeline();
- updateControls();
+ updateControls(false);
}
}
@@ -372,7 +361,8 @@ public class PlayControlsRowView extends MenuRowView {
}
}
});
- updateAll();
+ // force update to initialize everything
+ updateAll(true);
}
private void initializeTimeline() {
@@ -380,6 +370,8 @@ public class PlayControlsRowView extends MenuRowView {
mTimeShiftManager.getCurrentPositionMs());
mProgramStartTimeMs = program.getStartTimeUtcMillis();
mProgramEndTimeMs = program.getEndTimeUtcMillis();
+ mProgress.setMax(mProgramEndTimeMs - mProgramStartTimeMs);
+ updateRecTimeText();
SoftPreconditions.checkArgument(mProgramStartTimeMs <= mProgramEndTimeMs);
}
@@ -389,10 +381,13 @@ public class PlayControlsRowView extends MenuRowView {
getMenu().setKeepVisible(keepMenuVisible);
}
+ public void onPreselected() {
+ updateControls(true);
+ }
+
@Override
public void onSelected(boolean showTitle) {
super.onSelected(showTitle);
- updateControls();
postHideRippleAnimation();
}
@@ -474,28 +469,32 @@ public class PlayControlsRowView extends MenuRowView {
* Updates the view contents. It is called from the PlayControlsRow.
*/
public void update() {
- updateAll();
+ updateAll(false);
}
- private void updateAll() {
+ private void updateAll(boolean forceUpdate) {
if (mTimeShiftManager.isAvailable() && !mTvView.isScreenBlocked()) {
setEnabled(true);
initializeTimeline();
mBackgroundView.setEnabled(true);
+ setTextIfNeeded(mBackgroundView, null);
} else {
setEnabled(false);
mBackgroundView.setEnabled(false);
+ setTextIfNeeded(mBackgroundView, mUnavailableMessage);
}
- updateControls();
+ // force the controls be updated no matter it's visible or not.
+ updateControls(forceUpdate);
}
- private void updateControls() {
- updateTime();
- updateProgress();
- updateRecTimeText();
- updateButtons();
- updateRecordButton();
- updateButtonMargin();
+ private void updateControls(boolean forceUpdate) {
+ if (forceUpdate || getContentsView().isShown()) {
+ updateTime();
+ updateProgress();
+ updateButtons();
+ updateRecordButton();
+ updateButtonMargin();
+ }
}
private void updateTime() {
@@ -504,70 +503,39 @@ public class PlayControlsRowView extends MenuRowView {
mTimeIndicator.setVisibility(View.VISIBLE);
} else {
mTimeText.setVisibility(View.INVISIBLE);
- mTimeIndicator.setVisibility(View.INVISIBLE);
+ mTimeIndicator.setVisibility(View.GONE);
return;
}
long currentPositionMs = mTimeShiftManager.getCurrentPositionMs();
- ViewGroup.MarginLayoutParams params =
- (ViewGroup.MarginLayoutParams) mTimeText.getLayoutParams();
int currentTimePositionPixel =
convertDurationToPixel(currentPositionMs - mProgramStartTimeMs);
- params.leftMargin = currentTimePositionPixel + mTimeTextLeftMargin;
- mTimeText.setLayoutParams(params);
- mTimeText.setText(getTimeString(currentPositionMs));
- params = (ViewGroup.MarginLayoutParams) mTimeIndicator.getLayoutParams();
- params.leftMargin = currentTimePositionPixel + mTimeIndicatorLeftMargin;
- mTimeIndicator.setLayoutParams(params);
+ mTimeText.setTranslationX(currentTimePositionPixel + mTimeTextLeftMargin);
+ setTextIfNeeded(mTimeText, getTimeString(currentPositionMs));
+ mTimeIndicator.setTranslationX(currentTimePositionPixel + mTimeIndicatorLeftMargin);
}
private void updateProgress() {
if (isEnabled()) {
- mProgressWatched.setVisibility(View.VISIBLE);
- mProgressBuffered.setVisibility(View.VISIBLE);
- mProgressEmptyAfter.setVisibility(View.VISIBLE);
- } else {
- mProgressWatched.setVisibility(View.INVISIBLE);
- mProgressBuffered.setVisibility(View.INVISIBLE);
- mProgressEmptyAfter.setVisibility(View.INVISIBLE);
- if (mProgramStartTimeMs < mProgramEndTimeMs) {
- layoutProgress(mProgressEmptyBefore, mProgramStartTimeMs, mProgramEndTimeMs);
- } else {
- // Not initialized yet.
- layoutProgress(mProgressEmptyBefore, mTimelineWidth);
- }
- return;
- }
-
- long progressStartTimeMs = Math.min(mProgramEndTimeMs,
+ long progressStartTimeMs = Math.min(mProgramEndTimeMs,
Math.max(mProgramStartTimeMs, mTimeShiftManager.getRecordStartTimeMs()));
- long currentPlayingTimeMs = Math.min(mProgramEndTimeMs,
+ long currentPlayingTimeMs = Math.min(mProgramEndTimeMs,
Math.max(mProgramStartTimeMs, mTimeShiftManager.getCurrentPositionMs()));
- long progressEndTimeMs = Math.min(mProgramEndTimeMs,
+ long progressEndTimeMs = Math.min(mProgramEndTimeMs,
Math.max(mProgramStartTimeMs, mTimeShiftManager.getRecordEndTimeMs()));
-
- layoutProgress(mProgressEmptyBefore, mProgramStartTimeMs, progressStartTimeMs);
- layoutProgress(mProgressWatched, progressStartTimeMs, currentPlayingTimeMs);
- layoutProgress(mProgressBuffered, currentPlayingTimeMs, progressEndTimeMs);
- }
-
- private void layoutProgress(View progress, long progressStartTimeMs, long progressEndTimeMs) {
- layoutProgress(progress, Math.max(0,
- convertDurationToPixel(progressEndTimeMs - progressStartTimeMs)) + 1);
- }
-
- private void layoutProgress(View progress, int width) {
- ViewGroup.MarginLayoutParams params =
- (ViewGroup.MarginLayoutParams) progress.getLayoutParams();
- params.width = width;
- progress.setLayoutParams(params);
+ mProgress.setProgressRange(progressStartTimeMs - mProgramStartTimeMs,
+ progressEndTimeMs - mProgramStartTimeMs);
+ mProgress.setProgress(currentPlayingTimeMs - mProgramStartTimeMs);
+ } else {
+ mProgress.setProgressRange(0, 0);
+ }
}
private void updateRecTimeText() {
if (isEnabled()) {
mProgramStartTimeText.setVisibility(View.VISIBLE);
- mProgramStartTimeText.setText(getTimeString(mProgramStartTimeMs));
+ setTextIfNeeded(mProgramStartTimeText, getTimeString(mProgramStartTimeMs));
mProgramEndTimeText.setVisibility(View.VISIBLE);
- mProgramEndTimeText.setText(getTimeString(mProgramEndTimeMs));
+ setTextIfNeeded(mProgramEndTimeText, getTimeString(mProgramEndTimeMs));
} else {
mProgramStartTimeText.setVisibility(View.GONE);
mProgramEndTimeText.setVisibility(View.GONE);
@@ -576,11 +544,17 @@ public class PlayControlsRowView extends MenuRowView {
private void updateButtons() {
if (isEnabled()) {
- mControlBar.setVisibility(View.VISIBLE);
- mUnavailableMessageText.setVisibility(View.GONE);
+ mPlayPauseButton.setVisibility(View.VISIBLE);
+ mJumpPreviousButton.setVisibility(View.VISIBLE);
+ mJumpNextButton.setVisibility(View.VISIBLE);
+ mRewindButton.setVisibility(View.VISIBLE);
+ mFastForwardButton.setVisibility(View.VISIBLE);
} else {
- mControlBar.setVisibility(View.INVISIBLE);
- mUnavailableMessageText.setVisibility(View.VISIBLE);
+ mPlayPauseButton.setVisibility(View.GONE);
+ mJumpPreviousButton.setVisibility(View.GONE);
+ mJumpNextButton.setVisibility(View.GONE);
+ mRewindButton.setVisibility(View.GONE);
+ mFastForwardButton.setVisibility(View.GONE);
return;
}
@@ -622,6 +596,12 @@ public class PlayControlsRowView extends MenuRowView {
}
private void updateRecordButton() {
+ if (isEnabled()) {
+ mRecordButton.setVisibility(VISIBLE);
+ } else {
+ mRecordButton.setVisibility(GONE);
+ return;
+ }
if (!(mDvrManager != null
&& mDvrManager.isChannelRecordable(mMainActivity.getCurrentChannel()))) {
mRecordButton.setVisibility(View.GONE);
@@ -682,4 +662,10 @@ public class PlayControlsRowView extends MenuRowView {
mDvrDataManager.removeScheduledRecordingListener(mScheduledRecordingListener);
}
}
+
+ private void setTextIfNeeded(TextView textView, String text) {
+ if (!TextUtils.equals(textView.getText(), text)) {
+ textView.setText(text);
+ }
+ }
}
diff --git a/src/com/android/tv/menu/PlaybackProgressBar.java b/src/com/android/tv/menu/PlaybackProgressBar.java
new file mode 100644
index 00000000..e8061bc6
--- /dev/null
+++ b/src/com/android/tv/menu/PlaybackProgressBar.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tv.menu;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.tv.R;
+
+/**
+ * A progress bar control which has two progresses which start in the middle of the control.
+ */
+public class PlaybackProgressBar extends View {
+ private final LayerDrawable mProgressDrawable;
+ private final Drawable mPrimaryDrawable;
+ private final Drawable mSecondaryDrawable;
+ private long mMax = 100;
+ private long mProgressStart = 0;
+ private long mProgressEnd = 0;
+ private long mProgress = 0;
+
+ public PlaybackProgressBar(Context context) {
+ this(context, null);
+ }
+
+ public PlaybackProgressBar(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PlaybackProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public PlaybackProgressBar(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.PlaybackProgressBar, defStyleAttr, defStyleRes);
+ mProgressDrawable =
+ (LayerDrawable) a.getDrawable(R.styleable.PlaybackProgressBar_progressDrawable);
+ mPrimaryDrawable = mProgressDrawable.findDrawableByLayerId(android.R.id.progress);
+ mSecondaryDrawable =
+ mProgressDrawable.findDrawableByLayerId(android.R.id.secondaryProgress);
+ a.recycle();
+ refreshProgress();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ final int saveCount = canvas.save();
+ canvas.translate(getPaddingLeft(), getPaddingTop());
+ mProgressDrawable.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ refreshProgress();
+ }
+
+ public void setMax(long max) {
+ if (max < 0) {
+ max = 0;
+ }
+ if (max != mMax) {
+ mMax = max;
+ if (mProgressStart > max) {
+ mProgressStart = max;
+ }
+ if (mProgressEnd > max) {
+ mProgressEnd = max;
+ }
+ if (mProgress > max) {
+ mProgress = max;
+ }
+ refreshProgress();
+ }
+ }
+
+ /**
+ * Sets the start and end position of the progress.
+ */
+ public void setProgressRange(long start, long end) {
+ start = constrain(start, 0, mMax);
+ end = constrain(end, start, mMax);
+ mProgress = constrain(mProgress, start, end);
+ if (start != mProgressStart || end != mProgressEnd) {
+ mProgressStart = start;
+ mProgressEnd = end;
+ setProgressLevels();
+ }
+ }
+
+ /**
+ * Sets the progress position.
+ */
+ public void setProgress(long progress) {
+ progress = constrain(progress, mProgressStart, mProgressEnd);
+ if (progress != mProgress) {
+ mProgress = progress;
+ setProgressLevels();
+ }
+ }
+
+ private long constrain(long value, long min, long max) {
+ return Math.min(Math.max(value, min), max);
+ }
+
+ private void refreshProgress() {
+ int width = getWidth() - getPaddingStart() - getPaddingEnd();
+ int height = getHeight() - getPaddingTop() - getPaddingBottom();
+ mProgressDrawable.setBounds(0, 0, width, height);
+ setProgressLevels();
+ }
+
+ private void setProgressLevels() {
+ boolean progressUpdated = setProgressBound(mPrimaryDrawable, mProgressStart, mProgress);
+ progressUpdated |= setProgressBound(mSecondaryDrawable, mProgress, mProgressEnd);
+ if (progressUpdated) {
+ postInvalidate();
+ }
+ }
+
+ private boolean setProgressBound(Drawable drawable, long start, long end) {
+ Rect oldBounds = drawable.getBounds();
+ if (mMax == 0) {
+ if (!isEqualRect(oldBounds, 0, 0, 0, 0)) {
+ drawable.setBounds(0, 0, 0, 0);
+ return true;
+ }
+ return false;
+ }
+ int width = mProgressDrawable.getBounds().width();
+ int height = mProgressDrawable.getBounds().height();
+ int left = (int) (width * start / mMax);
+ int right = (int) (width * end / mMax);
+ if (!isEqualRect(oldBounds, left, 0, right, height)) {
+ drawable.setBounds(left, 0, right, height);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean isEqualRect(Rect rect, int left, int top, int right, int bottom) {
+ return rect.left == left && rect.top == top && rect.right == right && rect.bottom == bottom;
+ }
+}
diff --git a/src/com/android/tv/menu/TvOptionsRowAdapter.java b/src/com/android/tv/menu/TvOptionsRowAdapter.java
index fb062246..220fcd3a 100644
--- a/src/com/android/tv/menu/TvOptionsRowAdapter.java
+++ b/src/com/android/tv/menu/TvOptionsRowAdapter.java
@@ -21,7 +21,6 @@ import android.media.tv.TvTrackInfo;
import android.support.annotation.VisibleForTesting;
import com.android.tv.Features;
-import com.android.tv.R;
import com.android.tv.TvOptionsManager;
import com.android.tv.customization.CustomAction;
import com.android.tv.data.DisplayMode;
@@ -30,7 +29,6 @@ import com.android.tv.ui.sidepanel.ClosedCaptionFragment;
import com.android.tv.ui.sidepanel.DeveloperOptionFragment;
import com.android.tv.ui.sidepanel.DisplayModeFragment;
import com.android.tv.ui.sidepanel.MultiAudioFragment;
-import com.android.tv.util.PipInputManager;
import java.util.ArrayList;
import java.util.List;
@@ -39,12 +37,6 @@ import java.util.List;
* An adapter of options.
*/
public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
- private static final boolean ENABLE_IN_APP_PIP = false;
-
- private int mPositionPipAction;
- // If mInAppPipAction is false, system-wide PIP is used.
- private boolean mInAppPipAction = true;
-
public TvOptionsRowAdapter(Context context, List<CustomAction> customActions) {
super(context, customActions);
}
@@ -53,123 +45,62 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
protected List<MenuAction> createBaseActions() {
List<MenuAction> actionList = new ArrayList<>();
actionList.add(MenuAction.SELECT_CLOSED_CAPTION_ACTION);
- setOptionChangedListener(MenuAction.SELECT_CLOSED_CAPTION_ACTION);
actionList.add(MenuAction.SELECT_DISPLAY_MODE_ACTION);
- setOptionChangedListener(MenuAction.SELECT_DISPLAY_MODE_ACTION);
- actionList.add(MenuAction.PIP_IN_APP_ACTION);
- setOptionChangedListener(MenuAction.PIP_IN_APP_ACTION);
- mPositionPipAction = actionList.size() - 1;
+ if (Features.PICTURE_IN_PICTURE.isEnabled(getMainActivity())) {
+ actionList.add(MenuAction.SYSTEMWIDE_PIP_ACTION);
+ }
actionList.add(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION);
- setOptionChangedListener(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION);
actionList.add(MenuAction.MORE_CHANNELS_ACTION);
if (DeveloperOptionFragment.shouldShow()) {
actionList.add(MenuAction.DEV_ACTION);
}
actionList.add(MenuAction.SETTINGS_ACTION);
- if (getCustomActions() != null) {
- // Adjust Pip action position which will be changed by applying custom actions.
- for (CustomAction customAction : getCustomActions()) {
- if (customAction.isFront()) {
- mPositionPipAction++;
- }
- }
- }
-
+ updateClosedCaptionAction();
+ updateMultiAudioAction();
+ updateDisplayModeAction();
return actionList;
}
@Override
- protected boolean updateActions() {
- boolean changed = false;
- if (updatePipAction()) {
- changed = true;
+ protected void updateActions() {
+ if (updateClosedCaptionAction()) {
+ notifyItemChanged(getItemPosition(MenuAction.SELECT_CLOSED_CAPTION_ACTION));
}
if (updateMultiAudioAction()) {
- changed = true;
+ notifyItemChanged(getItemPosition(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION));
}
if (updateDisplayModeAction()) {
- changed = true;
+ notifyItemChanged(getItemPosition(MenuAction.SELECT_DISPLAY_MODE_ACTION));
}
- return changed;
}
- private boolean updatePipAction() {
- // There are four states.
- // Case 1. The device doesn't even have any input for PIP. (e.g. OTT box without HDMI input)
- // => Remove the icon.
- // Case 2. The device has one or more inputs for PIP but none of them are currently
- // available.
- // => Show the icon but disable it.
- // Case 3. The device has one or more available PIP inputs and now it's tuned off.
- // => Show the icon with "Off".
- // Case 4. The device has one or more available PIP inputs but it's already turned on.
- // => Show the icon with "On".
-
- boolean changed = false;
-
- // Case 1
- PipInputManager pipInputManager = getMainActivity().getPipInputManager();
- if (ENABLE_IN_APP_PIP && pipInputManager.getPipInputSize(false) > 1) {
- if (!mInAppPipAction) {
- removeAction(mPositionPipAction);
- addAction(mPositionPipAction, MenuAction.PIP_IN_APP_ACTION);
- mInAppPipAction = true;
- changed = true;
- }
- } else {
- if (mInAppPipAction) {
- removeAction(mPositionPipAction);
- mInAppPipAction = false;
- if (Features.PICTURE_IN_PICTURE.isEnabled(getMainActivity())) {
- addAction(mPositionPipAction, MenuAction.SYSTEMWIDE_PIP_ACTION);
- }
- return true;
- }
- return false;
- }
-
- // Case 2
- boolean isPipEnabled = getMainActivity().isPipEnabled();
- boolean oldEnabled = MenuAction.PIP_IN_APP_ACTION.isEnabled();
- boolean newEnabled = pipInputManager.getPipInputSize(true) > 0;
- if (oldEnabled != newEnabled) {
- // Should not disable the item if the PIP is already turned on so that the user can
- // force exit it.
- if (newEnabled || !isPipEnabled) {
- MenuAction.PIP_IN_APP_ACTION.setEnabled(newEnabled);
- changed = true;
- }
- }
-
- // Case 3 & 4 - we just need to update the icon.
- MenuAction.PIP_IN_APP_ACTION.setDrawableResId(
- isPipEnabled ? R.drawable.ic_tvoption_pip : R.drawable.ic_tvoption_pip_off);
- return changed;
+ @VisibleForTesting
+ private boolean updateClosedCaptionAction() {
+ return updateActionDescription(MenuAction.SELECT_CLOSED_CAPTION_ACTION);
}
@VisibleForTesting
boolean updateMultiAudioAction() {
List<TvTrackInfo> audioTracks = getMainActivity().getTracks(TvTrackInfo.TYPE_AUDIO);
- boolean oldEnabled = MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.isEnabled();
- boolean newEnabled = audioTracks != null && audioTracks.size() > 1;
- if (oldEnabled != newEnabled) {
- MenuAction.SELECT_AUDIO_LANGUAGE_ACTION.setEnabled(newEnabled);
- return true;
- }
- return false;
+ boolean enabled = audioTracks != null && audioTracks.size() > 1;
+ // Use "|" operator for non-short-circuit evaluation.
+ return MenuAction.setEnabled(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION, enabled)
+ | updateActionDescription(MenuAction.SELECT_AUDIO_LANGUAGE_ACTION);
}
private boolean updateDisplayModeAction() {
TvViewUiManager uiManager = getMainActivity().getTvViewUiManager();
- boolean oldEnabled = MenuAction.SELECT_DISPLAY_MODE_ACTION.isEnabled();
- boolean newEnabled = uiManager.isDisplayModeAvailable(DisplayMode.MODE_FULL)
+ boolean enabled = uiManager.isDisplayModeAvailable(DisplayMode.MODE_FULL)
|| uiManager.isDisplayModeAvailable(DisplayMode.MODE_ZOOM);
- if (oldEnabled != newEnabled) {
- MenuAction.SELECT_DISPLAY_MODE_ACTION.setEnabled(newEnabled);
- return true;
- }
- return false;
+ // Use "|" operator for non-short-circuit evaluation.
+ return MenuAction.setEnabled(MenuAction.SELECT_DISPLAY_MODE_ACTION, enabled)
+ | updateActionDescription(MenuAction.SELECT_DISPLAY_MODE_ACTION);
+ }
+
+ private boolean updateActionDescription(MenuAction action) {
+ return MenuAction.setActionDescription(action,
+ getMainActivity().getTvOptionsManager().getOptionString(action.getType()));
}
@Override
@@ -183,9 +114,6 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
getMainActivity().getOverlayManager().getSideFragmentManager()
.show(new DisplayModeFragment());
break;
- case TvOptionsManager.OPTION_IN_APP_PIP:
- getMainActivity().togglePipView();
- break;
case TvOptionsManager.OPTION_SYSTEMWIDE_PIP:
getMainActivity().enterPictureInPictureMode();
break;
@@ -205,4 +133,4 @@ public class TvOptionsRowAdapter extends CustomizableOptionsRowAdapter {
break;
}
}
-}
+} \ No newline at end of file