aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/guide
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/guide')
-rw-r--r--src/com/android/tv/guide/GenreListAdapter.java59
-rw-r--r--src/com/android/tv/guide/GuideUtils.java42
-rw-r--r--src/com/android/tv/guide/ProgramGrid.java40
-rw-r--r--src/com/android/tv/guide/ProgramGuide.java684
-rw-r--r--src/com/android/tv/guide/ProgramItemView.java522
-rw-r--r--src/com/android/tv/guide/ProgramListAdapter.java14
-rw-r--r--src/com/android/tv/guide/ProgramManager.java379
-rw-r--r--src/com/android/tv/guide/ProgramRow.java129
-rw-r--r--src/com/android/tv/guide/ProgramTableAdapter.java303
-rw-r--r--src/com/android/tv/guide/TimeListAdapter.java13
-rw-r--r--src/com/android/tv/guide/TimelineGridView.java17
-rw-r--r--src/com/android/tv/guide/TimelineRow.java13
12 files changed, 1236 insertions, 979 deletions
diff --git a/src/com/android/tv/guide/GenreListAdapter.java b/src/com/android/tv/guide/GenreListAdapter.java
index ce19eb2d..b4baf421 100644
--- a/src/com/android/tv/guide/GenreListAdapter.java
+++ b/src/com/android/tv/guide/GenreListAdapter.java
@@ -24,15 +24,11 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.data.GenreItems;
-
import java.util.List;
-/**
- * Adapts the genre items obtained from {@link GenreItems} to the program guide side panel.
- */
+/** Adapts the genre items obtained from {@link GenreItems} to the program guide side panel. */
class GenreListAdapter extends RecyclerView.Adapter<GenreListAdapter.GenreRowHolder> {
private static final String TAG = "GenreListAdapter";
private static final boolean DEBUG = false;
@@ -45,13 +41,14 @@ class GenreListAdapter extends RecyclerView.Adapter<GenreListAdapter.GenreRowHol
GenreListAdapter(Context context, ProgramManager programManager, ProgramGuide guide) {
mContext = context;
mProgramManager = programManager;
- mProgramManager.addListener(new ProgramManager.ListenerAdapter() {
- @Override
- public void onGenresUpdated() {
- mGenreLabels = GenreItems.getLabels(mContext);
- notifyDataSetChanged();
- }
- });
+ mProgramManager.addListener(
+ new ProgramManager.ListenerAdapter() {
+ @Override
+ public void onGenresUpdated() {
+ mGenreLabels = GenreItems.getLabels(mContext);
+ notifyDataSetChanged();
+ }
+ });
mProgramGuide = guide;
}
@@ -80,23 +77,24 @@ class GenreListAdapter extends RecyclerView.Adapter<GenreListAdapter.GenreRowHol
@Override
public GenreRowHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
- itemView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View view) {
- // Animation is not meaningful now, skip it.
- view.getStateListAnimator().jumpToCurrentState();
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- // Do nothing
- }
- });
+ itemView.addOnAttachStateChangeListener(
+ new View.OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View view) {
+ // Animation is not meaningful now, skip it.
+ view.getStateListAnimator().jumpToCurrentState();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View view) {
+ // Do nothing
+ }
+ });
return new GenreRowHolder(itemView, mProgramGuide);
}
- static class GenreRowHolder extends RecyclerView.ViewHolder implements
- View.OnFocusChangeListener {
+ static class GenreRowHolder extends RecyclerView.ViewHolder
+ implements View.OnFocusChangeListener {
private final ProgramGuide mProgramGuide;
private int mGenreId;
@@ -119,8 +117,13 @@ class GenreListAdapter extends RecyclerView.Adapter<GenreListAdapter.GenreRowHol
public void onFocusChange(View view, boolean hasFocus) {
if (hasFocus) {
if (DEBUG) {
- Log.d(TAG, "onFocusChanged " + ((TextView) view).getText()
- + "(" + mGenreId + ") hasFocus");
+ Log.d(
+ TAG,
+ "onFocusChanged "
+ + ((TextView) view).getText()
+ + "("
+ + mGenreId
+ + ") hasFocus");
}
mProgramGuide.requestGenreChange(mGenreId);
}
diff --git a/src/com/android/tv/guide/GuideUtils.java b/src/com/android/tv/guide/GuideUtils.java
index 403d00b5..51c14fd4 100644
--- a/src/com/android/tv/guide/GuideUtils.java
+++ b/src/com/android/tv/guide/GuideUtils.java
@@ -17,11 +17,9 @@
package com.android.tv.guide;
import android.graphics.Rect;
-import android.support.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
-
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
@@ -30,8 +28,8 @@ class GuideUtils {
private static int sWidthPerHour = 0;
/**
- * Sets the width in pixels that corresponds to an hour in program guide.
- * Assume that this is called from main thread only, so, no synchronization.
+ * Sets the width in pixels that corresponds to an hour in program guide. Assume that this is
+ * called from main thread only, so, no synchronization.
*/
static void setWidthPerHour(int widthPerHour) {
sWidthPerHour = widthPerHour;
@@ -44,30 +42,29 @@ class GuideUtils {
return (int) (millis * sWidthPerHour / TimeUnit.HOURS.toMillis(1));
}
- /**
- * Gets the number of pixels in program guide table that corresponds to the given range.
- */
+ /** Gets the number of pixels in program guide table that corresponds to the given range. */
static int convertMillisToPixel(long startMillis, long endMillis) {
// Convert to pixels first to avoid accumulation of rounding errors.
return GuideUtils.convertMillisToPixel(endMillis)
- GuideUtils.convertMillisToPixel(startMillis);
}
- /**
- * Gets the time in millis that corresponds to the given pixels in the program guide.
- */
+ /** Gets the time in millis that corresponds to the given pixels in the program guide. */
static long convertPixelToMillis(int pixel) {
return pixel * TimeUnit.HOURS.toMillis(1) / sWidthPerHour;
}
/**
* Return the view should be focused in the given program row according to the focus range.
-
+ *
* @param keepCurrentProgramFocused If {@code true}, focuses on the current program if possible,
- * else falls back the general logic.
+ * else falls back the general logic.
*/
- static View findNextFocusedProgram(View programRow, int focusRangeLeft,
- int focusRangeRight, boolean keepCurrentProgramFocused) {
+ static View findNextFocusedProgram(
+ View programRow,
+ int focusRangeLeft,
+ int focusRangeRight,
+ boolean keepCurrentProgramFocused) {
ArrayList<View> focusables = new ArrayList<>();
findFocusables(programRow, focusables);
@@ -102,9 +99,10 @@ class GuideUtils {
maxFullyOverlappedWidth = width;
}
} else if (maxFullyOverlappedWidth == Integer.MIN_VALUE) {
- int overlappedWidth = (focusRangeLeft <= focusableRect.left) ?
- focusRangeRight - focusableRect.left
- : focusableRect.right - focusRangeLeft;
+ int overlappedWidth =
+ (focusRangeLeft <= focusableRect.left)
+ ? focusRangeRight - focusableRect.left
+ : focusableRect.right - focusRangeLeft;
if (overlappedWidth > maxPartiallyOverlappedWidth) {
nextFocusIndex = i;
maxPartiallyOverlappedWidth = overlappedWidth;
@@ -118,16 +116,14 @@ class GuideUtils {
}
/**
- * Returns {@code true} if the program displayed in the give
- * {@link com.android.tv.guide.ProgramItemView} is a current program.
+ * Returns {@code true} if the program displayed in the give {@link
+ * com.android.tv.guide.ProgramItemView} is a current program.
*/
static boolean isCurrentProgram(ProgramItemView view) {
return view.getTableEntry().isCurrentProgram();
}
- /**
- * Returns {@code true} if the given view is a descendant of the give container.
- */
+ /** Returns {@code true} if the given view is a descendant of the give container. */
static boolean isDescendant(ViewGroup container, View view) {
if (view == null) {
return false;
@@ -152,5 +148,5 @@ class GuideUtils {
}
}
- private GuideUtils() { }
+ private GuideUtils() {}
}
diff --git a/src/com/android/tv/guide/ProgramGrid.java b/src/com/android/tv/guide/ProgramGrid.java
index 58436425..caafb045 100644
--- a/src/com/android/tv/guide/ProgramGrid.java
+++ b/src/com/android/tv/guide/ProgramGrid.java
@@ -25,15 +25,11 @@ import android.util.Log;
import android.util.Range;
import android.view.View;
import android.view.ViewTreeObserver;
-
import com.android.tv.R;
import com.android.tv.ui.OnRepeatedKeyInterceptListener;
-
import java.util.concurrent.TimeUnit;
-/**
- * A {@link VerticalGridView} for the program table view.
- */
+/** A {@link VerticalGridView} for the program table view. */
public class ProgramGrid extends VerticalGridView {
private static final String TAG = "ProgramGrid";
@@ -84,7 +80,7 @@ public class ProgramGrid extends VerticalGridView {
private final int mRowHeight;
private final int mDetailHeight;
- private final int mSelectionRow; // Row that is focused
+ private final int mSelectionRow; // Row that is focused
private View mLastFocusedView;
private final Rect mTempRect = new Rect();
@@ -97,8 +93,8 @@ public class ProgramGrid extends VerticalGridView {
interface ChildFocusListener {
/**
- * Is called before focus is moved. Only children to {@code ProgramGrid} will be passed.
- * See {@code ProgramGrid#setChildFocusListener(ChildFocusListener)}.
+ * Is called before focus is moved. Only children to {@code ProgramGrid} will be passed. See
+ * {@code ProgramGrid#setChildFocusListener(ChildFocusListener)}.
*/
void onRequestChildFocus(View oldFocus, View newFocus);
}
@@ -207,16 +203,13 @@ public class ProgramGrid extends VerticalGridView {
}
/**
- * Initializes ProgramGrid. It should be called before the view is actually attached to
- * Window.
+ * Initializes ProgramGrid. It should be called before the view is actually attached to Window.
*/
void initialize(ProgramManager programManager) {
mProgramManager = programManager;
}
- /**
- * Registers a listener focus events occurring on children to the {@code ProgramGrid}.
- */
+ /** Registers a listener focus events occurring on children to the {@code ProgramGrid}. */
void setChildFocusListener(ChildFocusListener childFocusListener) {
mChildFocusListener = childFocusListener;
}
@@ -226,8 +219,8 @@ public class ProgramGrid extends VerticalGridView {
}
/**
- * Resets focus states. If the logic to keep the last focus needs to be cleared, it should
- * be called.
+ * Resets focus states. If the logic to keep the last focus needs to be cleared, it should be
+ * called.
*/
void resetFocusState() {
mLastFocusedView = null;
@@ -255,8 +248,8 @@ public class ProgramGrid extends VerticalGridView {
Log.w(TAG, "No child view has focus");
return null;
}
- int nextChildIndex = direction == View.FOCUS_UP ? focusedChildIndex - 1
- : focusedChildIndex + 1;
+ int nextChildIndex =
+ direction == View.FOCUS_UP ? focusedChildIndex - 1 : focusedChildIndex + 1;
if (nextChildIndex < 0 || nextChildIndex >= getChildCount()) {
// Wraparound if reached head or end
if (getSelectedPosition() == 0) {
@@ -268,8 +261,12 @@ public class ProgramGrid extends VerticalGridView {
}
return focused;
}
- View nextFocusedProgram = GuideUtils.findNextFocusedProgram(getChildAt(nextChildIndex),
- mFocusRangeLeft, mFocusRangeRight, mKeepCurrentProgramFocused);
+ View nextFocusedProgram =
+ GuideUtils.findNextFocusedProgram(
+ getChildAt(nextChildIndex),
+ mFocusRangeLeft,
+ mFocusRangeRight,
+ mKeepCurrentProgramFocused);
if (nextFocusedProgram != null) {
nextFocusedProgram.getGlobalVisibleRect(mTempRect);
mNextFocusByUpDown = nextFocusedProgram;
@@ -320,8 +317,9 @@ public class ProgramGrid extends VerticalGridView {
mFocusRangeRight = getRightMostFocusablePosition();
mNextFocusByUpDown = null;
// If focus is not a program item, drop focus to the current program when back to the grid
- mKeepCurrentProgramFocused = !(focus instanceof ProgramItemView)
- || GuideUtils.isCurrentProgram((ProgramItemView) focus);
+ mKeepCurrentProgramFocused =
+ !(focus instanceof ProgramItemView)
+ || GuideUtils.isCurrentProgram((ProgramItemView) focus);
}
private int getRightMostFocusablePosition() {
diff --git a/src/com/android/tv/guide/ProgramGuide.java b/src/com/android/tv/guide/ProgramGuide.java
index dd5444e2..5b53f904 100644
--- a/src/com/android/tv/guide/ProgramGuide.java
+++ b/src/com/android/tv/guide/ProgramGuide.java
@@ -43,14 +43,14 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityManager;
-
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
import com.android.tv.ChannelTuner;
-import com.android.tv.Features;
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.util.DurationTimer;
+import com.android.tv.TvFeatures;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.WeakHandler;
+import com.android.tv.common.util.DurationTimer;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.GenreItems;
import com.android.tv.data.ProgramDataManager;
@@ -58,17 +58,16 @@ import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.ui.HardwareLayerAnimatorListenerAdapter;
import com.android.tv.ui.ViewUtils;
+import com.android.tv.ui.hideable.AutoHideScheduler;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
-/**
- * The program guide.
- */
-public class ProgramGuide implements ProgramGrid.ChildFocusListener {
+/** The program guide. */
+public class ProgramGuide
+ implements ProgramGrid.ChildFocusListener, AccessibilityStateChangeListener {
private static final String TAG = "ProgramGuide";
private static final boolean DEBUG = false;
@@ -83,8 +82,8 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
// We clip out the first program entry in ProgramManager, if it does not have enough width.
// In order to prevent from clipping out the current program, this value need be larger than
// or equal to ProgramManager.FIRST_ENTRY_MIN_DURATION.
- private static final long MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME
- = ProgramManager.FIRST_ENTRY_MIN_DURATION;
+ private static final long MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME =
+ ProgramManager.FIRST_ENTRY_MIN_DURATION;
private static final int MSG_PROGRAM_TABLE_FADE_IN_ANIM = 1000;
@@ -103,7 +102,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
private final long mViewPortMillis;
private final int mRowHeight;
private final int mDetailHeight;
- private final int mSelectionRow; // Row that is focused
+ private final int mSelectionRow; // Row that is focused
private final int mTableFadeAnimDuration;
private final int mAnimationDuration;
private final int mDetailPadding;
@@ -145,34 +144,44 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
private final Handler mHandler = new ProgramGuideHandler(this);
private boolean mActive;
- private final Runnable mHideRunnable = new Runnable() {
- @Override
- public void run() {
- hide();
- }
- };
+ private final AutoHideScheduler mAutoHideScheduler;
private final long mShowDurationMillis;
private ViewTreeObserver.OnGlobalLayoutListener mOnLayoutListenerForShow;
private final ProgramManagerListener mProgramManagerListener = new ProgramManagerListener();
- private final Runnable mUpdateTimeIndicator = new Runnable() {
- @Override
- public void run() {
- positionCurrentTimeIndicator();
- mHandler.postAtTime(this,
- Utils.ceilTime(SystemClock.uptimeMillis(), TIME_INDICATOR_UPDATE_FREQUENCY));
- }
- };
-
- public ProgramGuide(MainActivity activity, ChannelTuner channelTuner,
- TvInputManagerHelper tvInputManagerHelper, ChannelDataManager channelDataManager,
- ProgramDataManager programDataManager, @Nullable DvrDataManager dvrDataManager,
- @Nullable DvrScheduleManager dvrScheduleManager, Tracker tracker,
- Runnable preShowRunnable, Runnable postHideRunnable) {
+ private final Runnable mUpdateTimeIndicator =
+ new Runnable() {
+ @Override
+ public void run() {
+ positionCurrentTimeIndicator();
+ mHandler.postAtTime(
+ this,
+ Utils.ceilTime(
+ SystemClock.uptimeMillis(), TIME_INDICATOR_UPDATE_FREQUENCY));
+ }
+ };
+
+ @SuppressWarnings("RestrictTo")
+ public ProgramGuide(
+ MainActivity activity,
+ ChannelTuner channelTuner,
+ TvInputManagerHelper tvInputManagerHelper,
+ ChannelDataManager channelDataManager,
+ ProgramDataManager programDataManager,
+ @Nullable DvrDataManager dvrDataManager,
+ @Nullable DvrScheduleManager dvrScheduleManager,
+ Tracker tracker,
+ Runnable preShowRunnable,
+ Runnable postHideRunnable) {
mActivity = activity;
- mProgramManager = new ProgramManager(tvInputManagerHelper, channelDataManager,
- programDataManager, dvrDataManager, dvrScheduleManager);
+ mProgramManager =
+ new ProgramManager(
+ tvInputManagerHelper,
+ channelDataManager,
+ programDataManager,
+ dvrDataManager,
+ dvrScheduleManager);
mChannelTuner = channelTuner;
mTracker = tracker;
mPreShowRunnable = preShowRunnable;
@@ -185,9 +194,11 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
Point displaySize = new Point();
mActivity.getWindowManager().getDefaultDisplay().getSize(displaySize);
- int gridWidth = displaySize.x
- - res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_start)
- - res.getDimensionPixelSize(R.dimen.program_guide_table_header_column_width);
+ int gridWidth =
+ displaySize.x
+ - res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_start)
+ - res.getDimensionPixelSize(
+ R.dimen.program_guide_table_header_column_width);
mViewPortMillis = (gridWidth * HOUR_IN_MILLIS) / mWidthPerHour;
mRowHeight = res.getDimensionPixelSize(R.dimen.program_guide_table_item_row_height);
@@ -201,43 +212,49 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mDetailPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_detail_padding);
mContainer = mActivity.findViewById(R.id.program_guide);
- ViewTreeObserver.OnGlobalFocusChangeListener globalFocusChangeListener
- = new GlobalFocusChangeListener();
+ ViewTreeObserver.OnGlobalFocusChangeListener globalFocusChangeListener =
+ new GlobalFocusChangeListener();
mContainer.getViewTreeObserver().addOnGlobalFocusChangeListener(globalFocusChangeListener);
GenreListAdapter genreListAdapter = new GenreListAdapter(mActivity, mProgramManager, this);
mSidePanel = mContainer.findViewById(R.id.program_guide_side_panel);
- mSidePanelGridView = (VerticalGridView) mContainer.findViewById(
- R.id.program_guide_side_panel_grid_view);
- mSidePanelGridView.getRecycledViewPool().setMaxRecycledViews(
- R.layout.program_guide_side_panel_row,
- res.getInteger(R.integer.max_recycled_view_pool_epg_side_panel_row));
+ mSidePanelGridView =
+ (VerticalGridView) mContainer.findViewById(R.id.program_guide_side_panel_grid_view);
+ mSidePanelGridView
+ .getRecycledViewPool()
+ .setMaxRecycledViews(
+ R.layout.program_guide_side_panel_row,
+ res.getInteger(R.integer.max_recycled_view_pool_epg_side_panel_row));
mSidePanelGridView.setAdapter(genreListAdapter);
mSidePanelGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
- mSidePanelGridView.setWindowAlignmentOffset(mActivity.getResources()
- .getDimensionPixelOffset(R.dimen.program_guide_side_panel_alignment_y));
+ mSidePanelGridView.setWindowAlignmentOffset(
+ mActivity
+ .getResources()
+ .getDimensionPixelOffset(R.dimen.program_guide_side_panel_alignment_y));
mSidePanelGridView.setWindowAlignmentOffsetPercent(
VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
- if (Features.EPG_SEARCH.isEnabled(mActivity)) {
- mSearchOrb = (SearchOrbView) mContainer.findViewById(
- R.id.program_guide_side_panel_search_orb);
+ if (TvFeatures.EPG_SEARCH.isEnabled(mActivity)) {
+ mSearchOrb =
+ (SearchOrbView)
+ mContainer.findViewById(R.id.program_guide_side_panel_search_orb);
mSearchOrb.setVisibility(View.VISIBLE);
- mSearchOrb.setOnOrbClickedListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- hide();
- mActivity.showProgramGuideSearchFragment();
- }
- });
+ mSearchOrb.setOnOrbClickedListener(
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ hide();
+ mActivity.showProgramGuideSearchFragment();
+ }
+ });
mSidePanelGridView.setOnChildSelectedListener(
new android.support.v17.leanback.widget.OnChildSelectedListener() {
- @Override
- public void onChildSelected(ViewGroup viewGroup, View view, int i, long l) {
- mSearchOrb.animate().alpha(i == 0 ? 1.0f : 0.0f);
- }
- });
+ @Override
+ public void onChildSelected(ViewGroup viewGroup, View view, int i, long l) {
+ mSearchOrb.animate().alpha(i == 0 ? 1.0f : 0.0f);
+ }
+ });
} else {
mSearchOrb = null;
}
@@ -246,134 +263,156 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mTimelineRow = (TimelineRow) mTable.findViewById(R.id.time_row);
mTimeListAdapter = new TimeListAdapter(res);
- mTimelineRow.getRecycledViewPool().setMaxRecycledViews(
- R.layout.program_guide_table_header_row_item,
- res.getInteger(R.integer.max_recycled_view_pool_epg_header_row_item));
+ mTimelineRow
+ .getRecycledViewPool()
+ .setMaxRecycledViews(
+ R.layout.program_guide_table_header_row_item,
+ res.getInteger(R.integer.max_recycled_view_pool_epg_header_row_item));
mTimelineRow.setAdapter(mTimeListAdapter);
ProgramTableAdapter programTableAdapter = new ProgramTableAdapter(mActivity, this);
- programTableAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
- @Override
- public void onChanged() {
- // It is usually called when Genre is changed.
- // Reset selection of ProgramGrid
- resetRowSelection();
- updateGuidePosition();
- }
- });
+ programTableAdapter.registerAdapterDataObserver(
+ new RecyclerView.AdapterDataObserver() {
+ @Override
+ public void onChanged() {
+ // It is usually called when Genre is changed.
+ // Reset selection of ProgramGrid
+ resetRowSelection();
+ updateGuidePosition();
+ }
+ });
mGrid = (ProgramGrid) mTable.findViewById(R.id.grid);
mGrid.initialize(mProgramManager);
- mGrid.getRecycledViewPool().setMaxRecycledViews(
- R.layout.program_guide_table_row,
- res.getInteger(R.integer.max_recycled_view_pool_epg_table_row));
+ mGrid.getRecycledViewPool()
+ .setMaxRecycledViews(
+ R.layout.program_guide_table_row,
+ res.getInteger(R.integer.max_recycled_view_pool_epg_table_row));
mGrid.setAdapter(programTableAdapter);
mGrid.setChildFocusListener(this);
- mGrid.setOnChildSelectedListener(new OnChildSelectedListener() {
- @Override
- public void onChildSelected(ViewGroup parent, View view, int position, long id) {
- if (mIsDuringResetRowSelection) {
- // Ignore if it's during the first resetRowSelection, because onChildSelected
- // will be called again when rows are bound to the program table. if selectRow
- // is called here, mSelectedRow is set and the second selectRow call doesn't
- // work as intended.
- mIsDuringResetRowSelection = false;
- return;
- }
- selectRow(view);
- }
- });
+ mGrid.setOnChildSelectedListener(
+ new OnChildSelectedListener() {
+ @Override
+ public void onChildSelected(
+ ViewGroup parent, View view, int position, long id) {
+ if (mIsDuringResetRowSelection) {
+ // Ignore if it's during the first resetRowSelection, because
+ // onChildSelected
+ // will be called again when rows are bound to the program table. if
+ // selectRow
+ // is called here, mSelectedRow is set and the second selectRow call
+ // doesn't
+ // work as intended.
+ mIsDuringResetRowSelection = false;
+ return;
+ }
+ selectRow(view);
+ }
+ });
mGrid.setFocusScrollStrategy(ProgramGrid.FOCUS_SCROLL_ALIGNED);
mGrid.setWindowAlignmentOffset(mSelectionRow * mRowHeight);
mGrid.setWindowAlignmentOffsetPercent(ProgramGrid.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
mGrid.setItemAlignmentOffset(0);
mGrid.setItemAlignmentOffsetPercent(ProgramGrid.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
- RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- onHorizontalScrolled(dx);
- }
- };
+ RecyclerView.OnScrollListener onScrollListener =
+ new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ onHorizontalScrolled(dx);
+ }
+ };
mTimelineRow.addOnScrollListener(onScrollListener);
mCurrentTimeIndicator = mTable.findViewById(R.id.current_time_indicator);
- mShowAnimatorFull = createAnimator(
- R.animator.program_guide_side_panel_enter_full,
- 0,
- R.animator.program_guide_table_enter_full);
-
- mShowAnimatorPartial = createAnimator(
- R.animator.program_guide_side_panel_enter_partial,
- 0,
- R.animator.program_guide_table_enter_partial);
- mShowAnimatorPartial.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- mSidePanelGridView.setVisibility(View.VISIBLE);
- mSidePanelGridView.setAlpha(1.0f);
- }
- });
-
- mHideAnimatorFull = createAnimator(
- R.animator.program_guide_side_panel_exit,
- 0,
- R.animator.program_guide_table_exit);
- mHideAnimatorFull.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mContainer.setVisibility(View.GONE);
- }
- });
- mHideAnimatorPartial = createAnimator(
- R.animator.program_guide_side_panel_exit,
- 0,
- R.animator.program_guide_table_exit);
- mHideAnimatorPartial.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mContainer.setVisibility(View.GONE);
- }
- });
-
- mPartialToFullAnimator = createAnimator(
- R.animator.program_guide_side_panel_hide,
- R.animator.program_guide_side_panel_grid_fade_out,
- R.animator.program_guide_table_partial_to_full);
- mFullToPartialAnimator = createAnimator(
- R.animator.program_guide_side_panel_reveal,
- R.animator.program_guide_side_panel_grid_fade_in,
- R.animator.program_guide_table_full_to_partial);
-
- mProgramTableFadeOutAnimator = AnimatorInflater.loadAnimator(mActivity,
- R.animator.program_guide_table_fade_out);
+ mShowAnimatorFull =
+ createAnimator(
+ R.animator.program_guide_side_panel_enter_full,
+ 0,
+ R.animator.program_guide_table_enter_full);
+
+ mShowAnimatorPartial =
+ createAnimator(
+ R.animator.program_guide_side_panel_enter_partial,
+ 0,
+ R.animator.program_guide_table_enter_partial);
+ mShowAnimatorPartial.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mSidePanelGridView.setVisibility(View.VISIBLE);
+ mSidePanelGridView.setAlpha(1.0f);
+ }
+ });
+
+ mHideAnimatorFull =
+ createAnimator(
+ R.animator.program_guide_side_panel_exit,
+ 0,
+ R.animator.program_guide_table_exit);
+ mHideAnimatorFull.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mContainer.setVisibility(View.GONE);
+ }
+ });
+ mHideAnimatorPartial =
+ createAnimator(
+ R.animator.program_guide_side_panel_exit,
+ 0,
+ R.animator.program_guide_table_exit);
+ mHideAnimatorPartial.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mContainer.setVisibility(View.GONE);
+ }
+ });
+
+ mPartialToFullAnimator =
+ createAnimator(
+ R.animator.program_guide_side_panel_hide,
+ R.animator.program_guide_side_panel_grid_fade_out,
+ R.animator.program_guide_table_partial_to_full);
+ mFullToPartialAnimator =
+ createAnimator(
+ R.animator.program_guide_side_panel_reveal,
+ R.animator.program_guide_side_panel_grid_fade_in,
+ R.animator.program_guide_table_full_to_partial);
+
+ mProgramTableFadeOutAnimator =
+ AnimatorInflater.loadAnimator(mActivity, R.animator.program_guide_table_fade_out);
mProgramTableFadeOutAnimator.setTarget(mTable);
- mProgramTableFadeOutAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(mTable) {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
-
- if (!isActive()) {
- return;
- }
- mProgramManager.resetChannelListWithGenre(mLastRequestedGenreId);
- resetTimelineScroll();
- if (!mHandler.hasMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM)) {
- mHandler.sendEmptyMessage(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
- }
- }
- });
- mProgramTableFadeInAnimator = AnimatorInflater.loadAnimator(mActivity,
- R.animator.program_guide_table_fade_in);
+ mProgramTableFadeOutAnimator.addListener(
+ new HardwareLayerAnimatorListenerAdapter(mTable) {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+
+ if (!isActive()) {
+ return;
+ }
+ mProgramManager.resetChannelListWithGenre(mLastRequestedGenreId);
+ resetTimelineScroll();
+ if (!mHandler.hasMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM)) {
+ mHandler.sendEmptyMessage(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
+ }
+ }
+ });
+ mProgramTableFadeInAnimator =
+ AnimatorInflater.loadAnimator(mActivity, R.animator.program_guide_table_fade_in);
mProgramTableFadeInAnimator.setTarget(mTable);
mProgramTableFadeInAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(mTable));
mSharedPreference = PreferenceManager.getDefaultSharedPreferences(mActivity);
mAccessibilityManager =
(AccessibilityManager) mActivity.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mShowGuidePartial = mAccessibilityManager.isEnabled()
- || mSharedPreference.getBoolean(KEY_SHOW_GUIDE_PARTIAL, true);
+ mShowGuidePartial =
+ mAccessibilityManager.isEnabled()
+ || mSharedPreference.getBoolean(KEY_SHOW_GUIDE_PARTIAL, true);
+ mAutoHideScheduler = new AutoHideScheduler(activity, this::hide);
}
@Override
@@ -397,12 +436,12 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
}
/**
- * Show the program guide. This reveals the side panel, and the program guide table is shown
+ * Show the program guide. This reveals the side panel, and the program guide table is shown
* partially.
*
- * <p>Note: the animation which starts together with ProgramGuide showing animation needs to
- * be initiated in {@code runnableAfterAnimatorReady}. If the animation starts together
- * with show(), the animation may drop some frames.
+ * <p>Note: the animation which starts together with ProgramGuide showing animation needs to be
+ * initiated in {@code runnableAfterAnimatorReady}. If the animation starts together with
+ * show(), the animation may drop some frames.
*/
public void show(final Runnable runnableAfterAnimatorReady) {
if (mContainer.getVisibility() == View.VISIBLE) {
@@ -416,9 +455,10 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mVisibleDuration.start();
mProgramManager.programGuideVisibilityChanged(true);
- mStartUtcTime = Utils.floorTime(
- System.currentTimeMillis() - MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME,
- HALF_HOUR_IN_MILLIS);
+ mStartUtcTime =
+ Utils.floorTime(
+ System.currentTimeMillis() - MIN_DURATION_FROM_START_TIME_TO_CURRENT_TIME,
+ HALF_HOUR_IN_MILLIS);
mProgramManager.updateInitialTimeRange(mStartUtcTime, mStartUtcTime + mViewPortMillis);
mProgramManager.addListener(mProgramManagerListener);
mLastRequestedGenreId = GenreItems.ID_ALL_CHANNELS;
@@ -435,51 +475,60 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
if (DEBUG) {
Log.d(TAG, "show()");
}
- mOnLayoutListenerForShow = new ViewTreeObserver.OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- mContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- mTable.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mSidePanelGridView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mTable.buildLayer();
- mSidePanelGridView.buildLayer();
- mOnLayoutListenerForShow = null;
- mTimelineAnimation = true;
- // Make sure that time indicator update starts after animation is finished.
- startCurrentTimeIndicator(TIME_INDICATOR_UPDATE_FREQUENCY);
- if (DEBUG) {
- mContainer.getViewTreeObserver().addOnDrawListener(
- new ViewTreeObserver.OnDrawListener() {
- long time = System.currentTimeMillis();
- int count = 0;
-
- @Override
- public void onDraw() {
- long curtime = System.currentTimeMillis();
- Log.d(TAG, "onDraw " + count++ + " " + (curtime - time) + "ms");
- time = curtime;
- if (count > 10) {
- mContainer.getViewTreeObserver().removeOnDrawListener(this);
- }
- }
- });
- }
- updateGuidePosition();
- runnableAfterAnimatorReady.run();
- if (mShowGuidePartial) {
- mShowAnimatorPartial.start();
- } else {
- mShowAnimatorFull.start();
- }
- }
- };
+ mOnLayoutListenerForShow =
+ new ViewTreeObserver.OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ mContainer.getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ mTable.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mSidePanelGridView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ mTable.buildLayer();
+ mSidePanelGridView.buildLayer();
+ mOnLayoutListenerForShow = null;
+ mTimelineAnimation = true;
+ // Make sure that time indicator update starts after animation is finished.
+ startCurrentTimeIndicator(TIME_INDICATOR_UPDATE_FREQUENCY);
+ if (DEBUG) {
+ mContainer
+ .getViewTreeObserver()
+ .addOnDrawListener(
+ new ViewTreeObserver.OnDrawListener() {
+ long time = System.currentTimeMillis();
+ int count = 0;
+
+ @Override
+ public void onDraw() {
+ long curtime = System.currentTimeMillis();
+ Log.d(
+ TAG,
+ "onDraw "
+ + count++
+ + " "
+ + (curtime - time)
+ + "ms");
+ time = curtime;
+ if (count > 10) {
+ mContainer
+ .getViewTreeObserver()
+ .removeOnDrawListener(this);
+ }
+ }
+ });
+ }
+ updateGuidePosition();
+ runnableAfterAnimatorReady.run();
+ if (mShowGuidePartial) {
+ mShowAnimatorPartial.start();
+ } else {
+ mShowAnimatorFull.start();
+ }
+ }
+ };
mContainer.getViewTreeObserver().addOnGlobalLayoutListener(mOnLayoutListenerForShow);
scheduleHide();
}
- /**
- * Hide the program guide.
- */
+ /** Hide the program guide. */
public void hide() {
if (!isActive()) {
return;
@@ -516,52 +565,43 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
}
}
- /**
- * Schedules hiding the program guide.
- */
+ /** Schedules hiding the program guide. */
public void scheduleHide() {
- cancelHide();
- mHandler.postDelayed(mHideRunnable, mShowDurationMillis);
+ mAutoHideScheduler.schedule(mShowDurationMillis);
}
- /**
- * Cancels hiding the program guide.
- */
+ /** Cancels hiding the program guide. */
public void cancelHide() {
- mHandler.removeCallbacks(mHideRunnable);
+ mAutoHideScheduler.cancel();
}
- /**
- * Process the {@code KEYCODE_BACK} key event.
- */
+ /** Process the {@code KEYCODE_BACK} key event. */
public void onBackPressed() {
hide();
}
- /**
- * Returns {@code true} if the program guide should process the input events.
- */
+ /** Returns {@code true} if the program guide should process the input events. */
public boolean isActive() {
return mActive;
}
/**
- * Returns {@code true} if the program guide is shown, i.e. showing animation is done and
- * hiding animation is not started yet.
+ * Returns {@code true} if the program guide is shown, i.e. showing animation is done and hiding
+ * animation is not started yet.
*/
public boolean isRunningAnimation() {
- return mShowAnimatorPartial.isStarted() || mShowAnimatorFull.isStarted()
- || mHideAnimatorPartial.isStarted() || mHideAnimatorFull.isStarted();
+ return mShowAnimatorPartial.isStarted()
+ || mShowAnimatorFull.isStarted()
+ || mHideAnimatorPartial.isStarted()
+ || mHideAnimatorFull.isStarted();
}
- /** Returns if program table is in full screen mode. **/
+ /** Returns if program table is in full screen mode. * */
boolean isFull() {
return !mShowGuidePartial;
}
- /**
- * Requests change genre to {@code genreId}.
- */
+ /** Requests change genre to {@code genreId}. */
void requestGenreChange(int genreId) {
if (mLastRequestedGenreId == genreId) {
// When Recycler.onLayout() removes its children to recycle,
@@ -575,15 +615,15 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
// When requestGenreChange is called repeatedly in short time, we keep the fade-out
// state for mTableFadeAnimDuration from now. Without it, we'll see blinks.
mHandler.removeMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
- mHandler.sendEmptyMessageDelayed(MSG_PROGRAM_TABLE_FADE_IN_ANIM,
- mTableFadeAnimDuration);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_PROGRAM_TABLE_FADE_IN_ANIM, mTableFadeAnimDuration);
return;
}
if (mHandler.hasMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM)) {
mProgramManager.resetChannelListWithGenre(mLastRequestedGenreId);
mHandler.removeMessages(MSG_PROGRAM_TABLE_FADE_IN_ANIM);
- mHandler.sendEmptyMessageDelayed(MSG_PROGRAM_TABLE_FADE_IN_ANIM,
- mTableFadeAnimDuration);
+ mHandler.sendEmptyMessageDelayed(
+ MSG_PROGRAM_TABLE_FADE_IN_ANIM, mTableFadeAnimDuration);
return;
}
if (mProgramTableFadeInAnimator.isStarted()) {
@@ -593,9 +633,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mProgramTableFadeOutAnimator.start();
}
- /**
- * Returns the scroll offset of the time line row in pixels.
- */
+ /** Returns the scroll offset of the time line row in pixels. */
int getTimelineRowScrollOffset() {
return mTimelineRow.getScrollOffset();
}
@@ -605,9 +643,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
return mGrid;
}
- /**
- * Gets {@link VerticalGridView} for "genre select" side panel.
- */
+ /** Gets {@link VerticalGridView} for "genre select" side panel. */
VerticalGridView getSidePanel() {
return mSidePanelGridView;
}
@@ -628,9 +664,12 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
int startPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_start);
int topPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_top);
int bottomPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_margin_bottom);
- int tableHeight = res.getDimensionPixelOffset(R.dimen.program_guide_table_header_row_height)
- + mDetailHeight + mRowHeight * mGrid.getAdapter().getItemCount() + topPadding
- + bottomPadding;
+ int tableHeight =
+ res.getDimensionPixelOffset(R.dimen.program_guide_table_header_row_height)
+ + mDetailHeight
+ + mRowHeight * mGrid.getAdapter().getItemCount()
+ + topPadding
+ + bottomPadding;
if (tableHeight > screenHeight) {
// EPG height is longer that the screen height.
mTable.setPaddingRelative(startPadding, topPadding, 0, 0);
@@ -645,8 +684,8 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
}
}
- private Animator createAnimator(int sidePanelAnimResId, int sidePanelGridAnimResId,
- int tableAnimResId) {
+ private Animator createAnimator(
+ int sidePanelAnimResId, int sidePanelGridAnimResId, int tableAnimResId) {
List<Animator> animatorList = new ArrayList<>();
Animator sidePanelAnimator = AnimatorInflater.loadAnimator(mActivity, sidePanelAnimResId);
@@ -654,8 +693,8 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
animatorList.add(sidePanelAnimator);
if (sidePanelGridAnimResId != 0) {
- Animator sidePanelGridAnimator = AnimatorInflater.loadAnimator(mActivity,
- sidePanelGridAnimResId);
+ Animator sidePanelGridAnimator =
+ AnimatorInflater.loadAnimator(mActivity, sidePanelGridAnimResId);
sidePanelGridAnimator.setTarget(mSidePanelGridView);
sidePanelGridAnimator.addListener(
new HardwareLayerAnimatorListenerAdapter(mSidePanelGridView));
@@ -700,8 +739,9 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
}
private void positionCurrentTimeIndicator() {
- int offset = GuideUtils.convertMillisToPixel(mStartUtcTime, System.currentTimeMillis())
- - mTimelineRow.getScrollOffset();
+ int offset =
+ GuideUtils.convertMillisToPixel(mStartUtcTime, System.currentTimeMillis())
+ - mTimelineRow.getScrollOffset();
if (offset < 0) {
mCurrentTimeIndicator.setVisibility(View.GONE);
} else {
@@ -743,8 +783,7 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
mSelectedRow = null;
mIsDuringResetRowSelection = true;
mGrid.setSelectedPosition(
- Math.max(mProgramManager.getChannelIndex(mChannelTuner.getCurrentChannel()),
- 0));
+ Math.max(mProgramManager.getChannelIndex(mChannelTuner.getCurrentChannel()), 0));
mGrid.resetFocusState();
mGrid.onItemSelectionReset();
mIsDuringResetRowSelection = false;
@@ -767,12 +806,13 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
detailView.setVisibility(View.VISIBLE);
final ProgramRow programRow = (ProgramRow) row.findViewById(R.id.row);
- programRow.post(new Runnable() {
- @Override
- public void run() {
- programRow.focusCurrentProgram();
- }
- });
+ programRow.post(
+ new Runnable() {
+ @Override
+ public void run() {
+ programRow.focusCurrentProgram();
+ }
+ });
} else {
animateRowChange(mSelectedRow, row);
}
@@ -799,38 +839,45 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
if (outDetail != null && outDetail.isShown()) {
final View outDetailContent = outDetail.findViewById(R.id.detail_content_full);
- Animator fadeOutAnimator = ObjectAnimator.ofPropertyValuesHolder(outDetailContent,
- PropertyValuesHolder.ofFloat(View.ALPHA, outDetail.getAlpha(), 0f),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_Y,
- outDetailContent.getTranslationY(), animationPadding));
+ Animator fadeOutAnimator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ outDetailContent,
+ PropertyValuesHolder.ofFloat(View.ALPHA, outDetail.getAlpha(), 0f),
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_Y,
+ outDetailContent.getTranslationY(),
+ animationPadding));
fadeOutAnimator.setStartDelay(0);
fadeOutAnimator.setDuration(mAnimationDuration);
fadeOutAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(outDetailContent));
- Animator collapseAnimator = ViewUtils
- .createHeightAnimator(outDetail, ViewUtils.getLayoutHeight(outDetail), 0);
+ Animator collapseAnimator =
+ ViewUtils.createHeightAnimator(
+ outDetail, ViewUtils.getLayoutHeight(outDetail), 0);
collapseAnimator.setStartDelay(mAnimationDuration);
collapseAnimator.setDuration(mTableFadeAnimDuration);
- collapseAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- outDetailContent.setVisibility(View.GONE);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- outDetailContent.setVisibility(View.VISIBLE);
- }
- });
+ collapseAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ outDetailContent.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ outDetailContent.setVisibility(View.VISIBLE);
+ }
+ });
AnimatorSet outAnimator = new AnimatorSet();
outAnimator.playTogether(fadeOutAnimator, collapseAnimator);
- outAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mDetailOutAnimator = null;
- }
- });
+ outAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mDetailOutAnimator = null;
+ }
+ });
mDetailOutAnimator = outAnimator;
outAnimator.start();
}
@@ -842,39 +889,49 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
Animator expandAnimator = ViewUtils.createHeightAnimator(inDetail, 0, mDetailHeight);
expandAnimator.setStartDelay(mAnimationDuration);
expandAnimator.setDuration(mTableFadeAnimDuration);
- expandAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animator) {
- inDetailContent.setVisibility(View.GONE);
- }
-
- @Override
- public void onAnimationEnd(Animator animator) {
- inDetailContent.setVisibility(View.VISIBLE);
- inDetailContent.setAlpha(0);
- }
- });
- Animator fadeInAnimator = ObjectAnimator.ofPropertyValuesHolder(inDetailContent,
- PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, -animationPadding, 0f));
+ expandAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animator) {
+ inDetailContent.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ inDetailContent.setVisibility(View.VISIBLE);
+ inDetailContent.setAlpha(0);
+ }
+ });
+ Animator fadeInAnimator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ inDetailContent,
+ PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_Y, -animationPadding, 0f));
fadeInAnimator.setDuration(mAnimationDuration);
fadeInAnimator.addListener(new HardwareLayerAnimatorListenerAdapter(inDetailContent));
AnimatorSet inAnimator = new AnimatorSet();
inAnimator.playSequentially(expandAnimator, fadeInAnimator);
- inAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animator) {
- mDetailInAnimator = null;
- }
- });
+ inAnimator.addListener(
+ new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ mDetailInAnimator = null;
+ }
+ });
mDetailInAnimator = inAnimator;
inAnimator.start();
}
}
- private class GlobalFocusChangeListener implements
- ViewTreeObserver.OnGlobalFocusChangeListener {
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ mAutoHideScheduler.onAccessibilityStateChanged(enabled);
+ }
+
+ private class GlobalFocusChangeListener
+ implements ViewTreeObserver.OnGlobalFocusChangeListener {
private static final int UNKNOWN = 0;
private static final int SIDE_PANEL = 1;
private static final int PROGRAM_TABLE = 2;
@@ -912,11 +969,16 @@ public class ProgramGuide implements ProgramGrid.ChildFocusListener {
private class ProgramManagerListener extends ProgramManager.ListenerAdapter {
@Override
public void onTimeRangeUpdated() {
- int scrollOffset = (int) (mWidthPerHour * mProgramManager.getShiftedTime()
- / HOUR_IN_MILLIS);
+ int scrollOffset =
+ (int) (mWidthPerHour * mProgramManager.getShiftedTime() / HOUR_IN_MILLIS);
if (DEBUG) {
- Log.d(TAG, "Horizontal scroll to " + scrollOffset + " pixels ("
- + mProgramManager.getShiftedTime() + " millis)");
+ Log.d(
+ TAG,
+ "Horizontal scroll to "
+ + scrollOffset
+ + " pixels ("
+ + mProgramManager.getShiftedTime()
+ + " millis)");
}
mTimelineRow.scrollTo(scrollOffset, mTimelineAnimation);
}
diff --git a/src/com/android/tv/guide/ProgramItemView.java b/src/com/android/tv/guide/ProgramItemView.java
index b23d578c..9f379e43 100644
--- a/src/com/android/tv/guide/ProgramItemView.java
+++ b/src/com/android/tv/guide/ProgramItemView.java
@@ -24,7 +24,6 @@ import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Handler;
-import android.os.SystemClock;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextUtils;
@@ -35,21 +34,21 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
-
-import com.android.tv.ApplicationSingletons;
import com.android.tv.MainActivity;
import com.android.tv.R;
-import com.android.tv.TvApplication;
+import com.android.tv.TvSingletons;
import com.android.tv.analytics.Tracker;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.Clock;
+import com.android.tv.data.ChannelDataManager;
+import com.android.tv.data.Program;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.dvr.ui.DvrUiHelper;
import com.android.tv.guide.ProgramManager.TableEntry;
import com.android.tv.util.ToastUtils;
import com.android.tv.util.Utils;
-
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.TimeUnit;
@@ -60,10 +59,10 @@ public class ProgramItemView extends TextView {
private static final int MAX_PROGRESS = 10000; // From android.widget.ProgressBar.MAX_VALUE
// State indicating the focused program is the current program
- private static final int[] STATE_CURRENT_PROGRAM = { R.attr.state_current_program };
+ private static final int[] STATE_CURRENT_PROGRAM = {R.attr.state_current_program};
// Workaround state in order to not use too much texture memory for RippleDrawable
- private static final int[] STATE_TOO_WIDE = { R.attr.state_program_too_wide };
+ private static final int[] STATE_TOO_WIDE = {R.attr.state_program_too_wide};
private static int sVisibleThreshold;
private static int sItemPadding;
@@ -73,8 +72,10 @@ public class ProgramItemView extends TextView {
private static TextAppearanceSpan sEpisodeTitleStyle;
private static TextAppearanceSpan sGrayedOutEpisodeTitleStyle;
+ private final DvrManager mDvrManager;
+ private final Clock mClock;
+ private final ChannelDataManager mChannelDataManager;
private ProgramGuide mProgramGuide;
- private DvrManager mDvrManager;
private TableEntry mTableEntry;
private int mMaxWidthForRipple;
private int mTextWidth;
@@ -84,96 +85,119 @@ public class ProgramItemView extends TextView {
// as a result of the re-layout (see b/21378855).
private boolean mPreventParentRelayout;
- private static final View.OnClickListener ON_CLICKED = new View.OnClickListener() {
- @Override
- public void onClick(final View view) {
- TableEntry entry = ((ProgramItemView) view).mTableEntry;
- if (entry == null) {
- //do nothing
- return;
- }
- ApplicationSingletons singletons = TvApplication.getSingletons(view.getContext());
- Tracker tracker = singletons.getTracker();
- tracker.sendEpgItemClicked();
- final MainActivity tvActivity = (MainActivity) view.getContext();
- final Channel channel = tvActivity.getChannelDataManager().getChannel(entry.channelId);
- if (entry.isCurrentProgram()) {
- view.postDelayed(new Runnable() {
- @Override
- public void run() {
- tvActivity.tuneToChannel(channel);
- tvActivity.hideOverlaysForTune();
+ private static final View.OnClickListener ON_CLICKED =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(final View view) {
+ TableEntry entry = ((ProgramItemView) view).mTableEntry;
+ Clock clock = ((ProgramItemView) view).mClock;
+ if (entry == null) {
+ // do nothing
+ return;
}
- }, entry.getWidth() > ((ProgramItemView) view).mMaxWidthForRipple ? 0
- : view.getResources()
- .getInteger(R.integer.program_guide_ripple_anim_duration));
- } else if (entry.program != null && CommonFeatures.DVR.isEnabled(view.getContext())) {
- DvrManager dvrManager = singletons.getDvrManager();
- if (entry.entryStartUtcMillis > System.currentTimeMillis()
- && dvrManager.isProgramRecordable(entry.program)) {
- if (entry.scheduledRecording == null) {
- DvrUiHelper.checkStorageStatusAndShowErrorMessage(tvActivity,
- channel.getInputId(), new Runnable() {
+ TvSingletons singletons = TvSingletons.getSingletons(view.getContext());
+ Tracker tracker = singletons.getTracker();
+ tracker.sendEpgItemClicked();
+ final MainActivity tvActivity = (MainActivity) view.getContext();
+ final Channel channel =
+ tvActivity.getChannelDataManager().getChannel(entry.channelId);
+ if (entry.isCurrentProgram()) {
+ view.postDelayed(
+ new Runnable() {
@Override
public void run() {
- DvrUiHelper.requestRecordingFutureProgram(tvActivity,
- entry.program, false);
+ tvActivity.tuneToChannel(channel);
+ tvActivity.hideOverlaysForTune();
}
- });
- } else {
- dvrManager.removeScheduledRecording(entry.scheduledRecording);
- String msg = view.getResources().getString(
- R.string.dvr_schedules_deletion_info, entry.program.getTitle());
- ToastUtils.show(view.getContext(), msg, Toast.LENGTH_SHORT);
+ },
+ entry.getWidth() > ((ProgramItemView) view).mMaxWidthForRipple
+ ? 0
+ : view.getResources()
+ .getInteger(
+ R.integer
+ .program_guide_ripple_anim_duration));
+ } else if (entry.program != null
+ && CommonFeatures.DVR.isEnabled(view.getContext())) {
+ DvrManager dvrManager = singletons.getDvrManager();
+ if (entry.entryStartUtcMillis > clock.currentTimeMillis()
+ && dvrManager.isProgramRecordable(entry.program)) {
+ if (entry.scheduledRecording == null) {
+ DvrUiHelper.checkStorageStatusAndShowErrorMessage(
+ tvActivity,
+ channel.getInputId(),
+ new Runnable() {
+ @Override
+ public void run() {
+ DvrUiHelper.requestRecordingFutureProgram(
+ tvActivity, entry.program, false);
+ }
+ });
+ } else {
+ dvrManager.removeScheduledRecording(entry.scheduledRecording);
+ String msg =
+ view.getResources()
+ .getString(
+ R.string.dvr_schedules_deletion_info,
+ entry.program.getTitle());
+ ToastUtils.show(view.getContext(), msg, Toast.LENGTH_SHORT);
+ }
+ } else {
+ ToastUtils.show(
+ view.getContext(),
+ view.getResources()
+ .getString(R.string.dvr_msg_cannot_record_program),
+ Toast.LENGTH_SHORT);
+ }
}
- } else {
- ToastUtils.show(view.getContext(), view.getResources()
- .getString(R.string.dvr_msg_cannot_record_program), Toast.LENGTH_SHORT);
}
- }
- }
- };
+ };
private static final View.OnFocusChangeListener ON_FOCUS_CHANGED =
new View.OnFocusChangeListener() {
- @Override
- public void onFocusChange(View view, boolean hasFocus) {
- if (hasFocus) {
- ((ProgramItemView) view).mUpdateFocus.run();
- } else {
- Handler handler = view.getHandler();
- if (handler != null) {
- handler.removeCallbacks(((ProgramItemView) view).mUpdateFocus);
+ @Override
+ public void onFocusChange(View view, boolean hasFocus) {
+ if (hasFocus) {
+ ((ProgramItemView) view).mUpdateFocus.run();
+ } else {
+ Handler handler = view.getHandler();
+ if (handler != null) {
+ handler.removeCallbacks(((ProgramItemView) view).mUpdateFocus);
+ }
+ }
}
- }
- }
- };
-
- private final Runnable mUpdateFocus = new Runnable() {
- @Override
- public void run() {
- refreshDrawableState();
- TableEntry entry = mTableEntry;
- if (entry == null) {
- //do nothing
- return;
- }
- if (entry.isCurrentProgram()) {
- Drawable background = getBackground();
- if (!mProgramGuide.isActive() || mProgramGuide.isRunningAnimation()) {
- // If program guide is not active or is during showing/hiding,
- // the animation is unnecessary, skip it.
- background.jumpToCurrentState();
+ };
+
+ private final Runnable mUpdateFocus =
+ new Runnable() {
+ @Override
+ public void run() {
+ refreshDrawableState();
+ TableEntry entry = mTableEntry;
+ if (entry == null) {
+ // do nothing
+ return;
+ }
+ if (entry.isCurrentProgram()) {
+ Drawable background = getBackground();
+ if (!mProgramGuide.isActive() || mProgramGuide.isRunningAnimation()) {
+ // If program guide is not active or is during showing/hiding,
+ // the animation is unnecessary, skip it.
+ background.jumpToCurrentState();
+ }
+ int progress =
+ getProgress(
+ mClock, entry.entryStartUtcMillis, entry.entryEndUtcMillis);
+ setProgress(background, R.id.reverse_progress, MAX_PROGRESS - progress);
+ }
+ if (getHandler() != null) {
+ getHandler()
+ .postAtTime(
+ this,
+ Utils.ceilTime(
+ mClock.uptimeMillis(), FOCUS_UPDATE_FREQUENCY));
+ }
}
- int progress = getProgress(entry.entryStartUtcMillis, entry.entryEndUtcMillis);
- setProgress(background, R.id.reverse_progress, MAX_PROGRESS - progress);
- }
- if (getHandler() != null) {
- getHandler().postAtTime(this,
- Utils.ceilTime(SystemClock.uptimeMillis(), FOCUS_UPDATE_FREQUENCY));
- }
- }
- };
+ };
public ProgramItemView(Context context) {
this(context, null);
@@ -187,7 +211,10 @@ public class ProgramItemView extends TextView {
super(context, attrs, defStyle);
setOnClickListener(ON_CLICKED);
setOnFocusChangeListener(ON_FOCUS_CHANGED);
- mDvrManager = TvApplication.getSingletons(getContext()).getDvrManager();
+ TvSingletons singletons = TvSingletons.getSingletons(getContext());
+ mDvrManager = singletons.getDvrManager();
+ mChannelDataManager = singletons.getChannelDataManager();
+ mClock = singletons.getClock();
}
private void initIfNeeded() {
@@ -196,35 +223,46 @@ public class ProgramItemView extends TextView {
}
Resources res = getContext().getResources();
- sVisibleThreshold = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_item_visible_threshold);
+ sVisibleThreshold =
+ res.getDimensionPixelOffset(R.dimen.program_guide_table_item_visible_threshold);
sItemPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_item_padding);
- sCompoundDrawablePadding = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_item_compound_drawable_padding);
-
- ColorStateList programTitleColor = ColorStateList.valueOf(res.getColor(
- R.color.program_guide_table_item_program_title_text_color, null));
- ColorStateList grayedOutProgramTitleColor = res.getColorStateList(
- R.color.program_guide_table_item_grayed_out_program_text_color, null);
- ColorStateList episodeTitleColor = ColorStateList.valueOf(res.getColor(
- R.color.program_guide_table_item_program_episode_title_text_color, null));
- ColorStateList grayedOutEpisodeTitleColor = ColorStateList.valueOf(res.getColor(
- R.color.program_guide_table_item_grayed_out_program_episode_title_text_color,
- null));
- int programTitleSize = res.getDimensionPixelSize(
- R.dimen.program_guide_table_item_program_title_font_size);
- int episodeTitleSize = res.getDimensionPixelSize(
- R.dimen.program_guide_table_item_program_episode_title_font_size);
-
- sProgramTitleStyle = new TextAppearanceSpan(null, 0, programTitleSize, programTitleColor,
- null);
- sGrayedOutProgramTitleStyle = new TextAppearanceSpan(null, 0, programTitleSize,
- grayedOutProgramTitleColor, null);
- sEpisodeTitleStyle = new TextAppearanceSpan(null, 0, episodeTitleSize, episodeTitleColor,
- null);
- sGrayedOutEpisodeTitleStyle = new TextAppearanceSpan(null, 0, episodeTitleSize,
- grayedOutEpisodeTitleColor, null);
+ sCompoundDrawablePadding =
+ res.getDimensionPixelOffset(
+ R.dimen.program_guide_table_item_compound_drawable_padding);
+
+ ColorStateList programTitleColor =
+ ColorStateList.valueOf(
+ res.getColor(
+ R.color.program_guide_table_item_program_title_text_color, null));
+ ColorStateList grayedOutProgramTitleColor =
+ res.getColorStateList(
+ R.color.program_guide_table_item_grayed_out_program_text_color, null);
+ ColorStateList episodeTitleColor =
+ ColorStateList.valueOf(
+ res.getColor(
+ R.color.program_guide_table_item_program_episode_title_text_color,
+ null));
+ ColorStateList grayedOutEpisodeTitleColor =
+ ColorStateList.valueOf(
+ res.getColor(
+ R.color
+ .program_guide_table_item_grayed_out_program_episode_title_text_color,
+ null));
+ int programTitleSize =
+ res.getDimensionPixelSize(R.dimen.program_guide_table_item_program_title_font_size);
+ int episodeTitleSize =
+ res.getDimensionPixelSize(
+ R.dimen.program_guide_table_item_program_episode_title_font_size);
+
+ sProgramTitleStyle =
+ new TextAppearanceSpan(null, 0, programTitleSize, programTitleColor, null);
+ sGrayedOutProgramTitleStyle =
+ new TextAppearanceSpan(null, 0, programTitleSize, grayedOutProgramTitleColor, null);
+ sEpisodeTitleStyle =
+ new TextAppearanceSpan(null, 0, episodeTitleSize, episodeTitleColor, null);
+ sGrayedOutEpisodeTitleStyle =
+ new TextAppearanceSpan(null, 0, episodeTitleSize, grayedOutEpisodeTitleColor, null);
}
@Override
@@ -236,8 +274,9 @@ public class ProgramItemView extends TextView {
@Override
protected int[] onCreateDrawableState(int extraSpace) {
if (mTableEntry != null) {
- int states[] = super.onCreateDrawableState(extraSpace
- + STATE_CURRENT_PROGRAM.length + STATE_TOO_WIDE.length);
+ int[] states =
+ super.onCreateDrawableState(
+ extraSpace + STATE_CURRENT_PROGRAM.length + STATE_TOO_WIDE.length);
if (mTableEntry.isCurrentProgram()) {
mergeDrawableStates(states, STATE_CURRENT_PROGRAM);
}
@@ -254,86 +293,168 @@ public class ProgramItemView extends TextView {
}
@SuppressLint("SwitchIntDef")
- public void setValues(ProgramGuide programGuide, TableEntry entry, int selectedGenreId,
- long fromUtcMillis, long toUtcMillis, String gapTitle) {
+ public void setValues(
+ ProgramGuide programGuide,
+ TableEntry entry,
+ int selectedGenreId,
+ long fromUtcMillis,
+ long toUtcMillis,
+ String gapTitle) {
mProgramGuide = programGuide;
mTableEntry = entry;
ViewGroup.LayoutParams layoutParams = getLayoutParams();
- layoutParams.width = entry.getWidth();
- setLayoutParams(layoutParams);
+ if (layoutParams != null) {
+ // There is no layoutParams in the tests so we skip this
+ layoutParams.width = entry.getWidth();
+ setLayoutParams(layoutParams);
+ }
+ String title = mTableEntry.program != null ? mTableEntry.program.getTitle() : null;
+ if (mTableEntry.isGap()) {
+ title = gapTitle;
+ }
+ if (TextUtils.isEmpty(title)) {
+ title = getResources().getString(R.string.program_title_for_no_information);
+ }
+ updateText(selectedGenreId, title);
+ updateIcons();
+ updateContentDescription(title);
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mTextWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
+ // Maximum width for us to use a ripple
+ mMaxWidthForRipple = GuideUtils.convertMillisToPixel(fromUtcMillis, toUtcMillis);
+ }
- String title = entry.program != null ? entry.program.getTitle() : null;
- String episode = entry.program != null ?
- entry.program.getEpisodeDisplayTitle(getContext()) : null;
+ private boolean isEntryWideEnough() {
+ return mTableEntry != null && mTableEntry.getWidth() >= sVisibleThreshold;
+ }
+
+ private void updateText(int selectedGenreId, String title) {
+ if (!isEntryWideEnough()) {
+ setText(null);
+ return;
+ }
+
+ String episode =
+ mTableEntry.program != null
+ ? mTableEntry.program.getEpisodeDisplayTitle(getContext())
+ : null;
TextAppearanceSpan titleStyle = sGrayedOutProgramTitleStyle;
TextAppearanceSpan episodeStyle = sGrayedOutEpisodeTitleStyle;
+ if (mTableEntry.isGap()) {
- if (entry.getWidth() < sVisibleThreshold) {
- setText(null);
+ episode = null;
+ } else if (mTableEntry.hasGenre(selectedGenreId)) {
+ titleStyle = sProgramTitleStyle;
+ episodeStyle = sEpisodeTitleStyle;
+ }
+ SpannableStringBuilder description = new SpannableStringBuilder();
+ description.append(title);
+ if (!TextUtils.isEmpty(episode)) {
+ description.append('\n');
+
+ // Add a 'zero-width joiner'/ZWJ in order to ensure we have the same line height for
+ // all lines. This is a non-printing character so it will not change the horizontal
+ // spacing however it will affect the line height. As we ensure the ZWJ has the same
+ // text style as the title it will make sure the line height is consistent.
+ description.append('\u200D');
+
+ int middle = description.length();
+ description.append(episode);
+
+ description.setSpan(titleStyle, 0, middle, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ description.setSpan(
+ episodeStyle, middle, description.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
- if (entry.isGap()) {
- title = gapTitle;
- episode = null;
- } else if (entry.hasGenre(selectedGenreId)) {
- titleStyle = sProgramTitleStyle;
- episodeStyle = sEpisodeTitleStyle;
+ description.setSpan(
+ titleStyle, 0, description.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ setText(description);
+ }
+
+ private void updateIcons() {
+ // Sets recording icons if needed.
+ int iconResId = 0;
+ if (isEntryWideEnough() && mTableEntry.scheduledRecording != null) {
+ if (mDvrManager.isConflicting(mTableEntry.scheduledRecording)) {
+ iconResId = R.drawable.ic_warning_white_18dp;
+ } else {
+ switch (mTableEntry.scheduledRecording.getState()) {
+ case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
+ iconResId = R.drawable.ic_scheduled_recording;
+ break;
+ case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
+ iconResId = R.drawable.ic_recording_program;
+ break;
+ default:
+ // leave the iconResId=0
+ }
}
- if (TextUtils.isEmpty(title)) {
- title = getResources().getString(R.string.program_title_for_no_information);
+ }
+ setCompoundDrawablePadding(iconResId != 0 ? sCompoundDrawablePadding : 0);
+ setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, iconResId, 0);
+ }
+
+ private void updateContentDescription(String title) {
+ // The content description includes extra information that is displayed on the detail view
+ Resources resources = getResources();
+ String description = title;
+ // TODO(b/73282818): only say channel name when the row changes
+ Channel channel = mChannelDataManager.getChannel(mTableEntry.channelId);
+ if (channel != null) {
+ description = channel.getDisplayNumber() + " " + description;
+ }
+ description +=
+ " "
+ + Utils.getDurationString(
+ getContext(),
+ mClock,
+ mTableEntry.entryStartUtcMillis,
+ mTableEntry.entryEndUtcMillis,
+ true);
+ Program program = mTableEntry.program;
+ if (program != null) {
+ String episodeDescription = program.getEpisodeContentDescription(getContext());
+ if (!TextUtils.isEmpty(episodeDescription)) {
+ description += " " + episodeDescription;
}
- SpannableStringBuilder description = new SpannableStringBuilder();
- description.append(title);
- if (!TextUtils.isEmpty(episode)) {
- description.append('\n');
-
- // Add a 'zero-width joiner'/ZWJ in order to ensure we have the same line height for
- // all lines. This is a non-printing character so it will not change the horizontal
- // spacing however it will affect the line height. As we ensure the ZWJ has the same
- // text style as the title it will make sure the line height is consistent.
- description.append('\u200D');
-
- int middle = description.length();
- description.append(episode);
-
- description.setSpan(titleStyle, 0, middle, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- description.setSpan(episodeStyle, middle, description.length(),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ if (mTableEntry.scheduledRecording != null) {
+ if (mDvrManager.isConflicting(mTableEntry.scheduledRecording)) {
+ description +=
+ " " + resources.getString(R.string.dvr_epg_program_recording_conflict);
} else {
- description.setSpan(titleStyle, 0, description.length(),
- Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
- setText(description);
-
- // Sets recording icons if needed.
- int iconResId = 0;
- if (mTableEntry.scheduledRecording != null) {
- if (mDvrManager.isConflicting(mTableEntry.scheduledRecording)) {
- iconResId = R.drawable.ic_warning_white_18dp;
- } else {
- switch (mTableEntry.scheduledRecording.getState()) {
- case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
- iconResId = R.drawable.ic_scheduled_recording;
- break;
- case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
- iconResId = R.drawable.ic_recording_program;
- break;
- }
+ switch (mTableEntry.scheduledRecording.getState()) {
+ case ScheduledRecording.STATE_RECORDING_NOT_STARTED:
+ description +=
+ " "
+ + resources.getString(
+ R.string.dvr_epg_program_recording_scheduled);
+ break;
+ case ScheduledRecording.STATE_RECORDING_IN_PROGRESS:
+ description +=
+ " "
+ + resources.getString(
+ R.string.dvr_epg_program_recording_in_progress);
+ break;
+ default:
+ // do nothing
}
}
- setCompoundDrawablePadding(iconResId != 0 ? sCompoundDrawablePadding : 0);
- setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, iconResId, 0);
}
- measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
- mTextWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
- // Maximum width for us to use a ripple
- mMaxWidthForRipple = GuideUtils.convertMillisToPixel(fromUtcMillis, toUtcMillis);
+ if (mTableEntry.isBlocked()) {
+ description += " " + resources.getString(R.string.program_guide_content_locked);
+ } else if (program != null) {
+ String programDescription = program.getDescription();
+ if (!TextUtils.isEmpty(programDescription)) {
+ description += " " + programDescription;
+ }
+ }
+ setContentDescription(description);
}
- /**
- * Update programItemView to handle alignments of text.
- */
+ /** Update programItemView to handle alignments of text. */
public void updateVisibleArea() {
View parentView = ((View) getParent());
if (parentView == null) {
@@ -341,7 +462,7 @@ public class ProgramItemView extends TextView {
}
if (getLayoutDirection() == LAYOUT_DIRECTION_LTR) {
layoutVisibleArea(parentView.getLeft() - getLeft(), getRight() - parentView.getRight());
- } else {
+ } else {
layoutVisibleArea(getRight() - parentView.getRight(), parentView.getLeft() - getLeft());
}
}
@@ -349,16 +470,14 @@ public class ProgramItemView extends TextView {
/**
* Layout title and episode according to visible area.
*
- * Here's the spec.
- * 1. Don't show text if it's shorter than 48dp.
- * 2. Try showing whole text in visible area by placing and wrapping text,
- * but do not wrap text less than 30min.
- * 3. Episode title is visible only if title isn't multi-line.
+ * <p>Here's the spec. 1. Don't show text if it's shorter than 48dp. 2. Try showing whole text
+ * in visible area by placing and wrapping text, but do not wrap text less than 30min. 3.
+ * Episode title is visible only if title isn't multi-line.
*
* @param startOffset Offset of the start position from the enclosing view's start position.
* @param endOffset Offset of the end position from the enclosing view's end position.
*/
- private void layoutVisibleArea(int startOffset, int endOffset) {
+ private void layoutVisibleArea(int startOffset, int endOffset) {
int width = mTableEntry.getWidth();
int startPadding = Math.max(0, startOffset);
int endPadding = Math.max(0, endOffset);
@@ -388,8 +507,8 @@ public class ProgramItemView extends TextView {
mTableEntry = null;
}
- private static int getProgress(long start, long end) {
- long currentTime = System.currentTimeMillis();
+ private static int getProgress(Clock clock, long start, long end) {
+ long currentTime = clock.currentTimeMillis();
if (currentTime <= start) {
return 0;
} else if (currentTime >= end) {
@@ -417,11 +536,15 @@ public class ProgramItemView extends TextView {
private static int getStateCount(StateListDrawable stateListDrawable) {
try {
- Object stateCount = StateListDrawable.class.getDeclaredMethod("getStateCount")
- .invoke(stateListDrawable);
+ Object stateCount =
+ StateListDrawable.class
+ .getDeclaredMethod("getStateCount")
+ .invoke(stateListDrawable);
return (int) stateCount;
- } catch (NoSuchMethodException|IllegalAccessException|IllegalArgumentException
- |InvocationTargetException e) {
+ } catch (NoSuchMethodException
+ | IllegalAccessException
+ | IllegalArgumentException
+ | InvocationTargetException e) {
Log.e(TAG, "Failed to call StateListDrawable.getStateCount()", e);
return 0;
}
@@ -429,12 +552,15 @@ public class ProgramItemView extends TextView {
private static Drawable getStateDrawable(StateListDrawable stateListDrawable, int index) {
try {
- Object drawable = StateListDrawable.class
- .getDeclaredMethod("getStateDrawable", Integer.TYPE)
- .invoke(stateListDrawable, index);
+ Object drawable =
+ StateListDrawable.class
+ .getDeclaredMethod("getStateDrawable", Integer.TYPE)
+ .invoke(stateListDrawable, index);
return (Drawable) drawable;
- } catch (NoSuchMethodException|IllegalAccessException|IllegalArgumentException
- |InvocationTargetException e) {
+ } catch (NoSuchMethodException
+ | IllegalAccessException
+ | IllegalArgumentException
+ | InvocationTargetException e) {
Log.e(TAG, "Failed to call StateListDrawable.getStateDrawable(" + index + ")", e);
return null;
}
diff --git a/src/com/android/tv/guide/ProgramListAdapter.java b/src/com/android/tv/guide/ProgramListAdapter.java
index c1fcdd40..397bacfb 100644
--- a/src/com/android/tv/guide/ProgramListAdapter.java
+++ b/src/com/android/tv/guide/ProgramListAdapter.java
@@ -22,9 +22,8 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.tv.R;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.guide.ProgramManager.TableEntriesUpdatedListener;
import com.android.tv.guide.ProgramManager.TableEntry;
@@ -111,9 +110,14 @@ class ProgramListAdapter extends RecyclerView.Adapter<ProgramListAdapter.Program
Log.d(TAG, "onBind. View = " + itemView + ", Entry = " + entry);
}
ProgramManager programManager = programGuide.getProgramManager();
- ((ProgramItemView) itemView).setValues(programGuide, entry,
- programManager.getSelectedGenreId(), programManager.getFromUtcMillis(),
- programManager.getToUtcMillis(), gapTitle);
+ ((ProgramItemView) itemView)
+ .setValues(
+ programGuide,
+ entry,
+ programManager.getSelectedGenreId(),
+ programManager.getFromUtcMillis(),
+ programManager.getToUtcMillis(),
+ gapTitle);
}
void onUnbind() {
diff --git a/src/com/android/tv/guide/ProgramManager.java b/src/com/android/tv/guide/ProgramManager.java
index 4ec3f77e..3f20a837 100644
--- a/src/com/android/tv/guide/ProgramManager.java
+++ b/src/com/android/tv/guide/ProgramManager.java
@@ -18,21 +18,20 @@ package com.android.tv.guide;
import android.support.annotation.MainThread;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.util.ArraySet;
import android.util.Log;
-
-import com.android.tv.data.Channel;
import com.android.tv.data.ChannelDataManager;
import com.android.tv.data.GenreItems;
import com.android.tv.data.Program;
import com.android.tv.data.ProgramDataManager;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrScheduleManager;
import com.android.tv.dvr.DvrScheduleManager.OnConflictStateChangeListener;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
-
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -40,9 +39,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-/**
- * Manages the channels and programs for the program guide.
- */
+/** Manages the channels and programs for the program guide. */
@MainThread
public class ProgramManager {
private static final String TAG = "ProgramManager";
@@ -60,7 +57,7 @@ public class ProgramManager {
private final TvInputManagerHelper mTvInputManagerHelper;
private final ChannelDataManager mChannelDataManager;
private final ProgramDataManager mProgramDataManager;
- private final DvrDataManager mDvrDataManager; // Only set if DVR is enabled
+ private final DvrDataManager mDvrDataManager; // Only set if DVR is enabled
private final DvrScheduleManager mDvrScheduleManager;
private long mStartUtcMillis;
@@ -127,51 +124,67 @@ public class ProgramManager {
private final DvrDataManager.ScheduledRecordingListener mScheduledRecordingListener =
new DvrDataManager.ScheduledRecordingListener() {
- @Override
- public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
- for (ScheduledRecording schedule : scheduledRecordings) {
- TableEntry oldEntry = getTableEntry(schedule);
- if (oldEntry != null) {
- TableEntry newEntry = new TableEntry(oldEntry.channelId, oldEntry.program,
- schedule, oldEntry.entryStartUtcMillis,
- oldEntry.entryEndUtcMillis, oldEntry.isBlocked());
- updateEntry(oldEntry, newEntry);
+ @Override
+ public void onScheduledRecordingAdded(ScheduledRecording... scheduledRecordings) {
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ TableEntry oldEntry = getTableEntry(schedule);
+ if (oldEntry != null) {
+ TableEntry newEntry =
+ new TableEntry(
+ oldEntry.channelId,
+ oldEntry.program,
+ schedule,
+ oldEntry.entryStartUtcMillis,
+ oldEntry.entryEndUtcMillis,
+ oldEntry.isBlocked());
+ updateEntry(oldEntry, newEntry);
+ }
+ }
}
- }
- }
- @Override
- public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
- for (ScheduledRecording schedule : scheduledRecordings) {
- TableEntry oldEntry = getTableEntry(schedule);
- if (oldEntry != null) {
- TableEntry newEntry = new TableEntry(oldEntry.channelId, oldEntry.program, null,
- oldEntry.entryStartUtcMillis, oldEntry.entryEndUtcMillis,
- oldEntry.isBlocked());
- updateEntry(oldEntry, newEntry);
+ @Override
+ public void onScheduledRecordingRemoved(ScheduledRecording... scheduledRecordings) {
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ TableEntry oldEntry = getTableEntry(schedule);
+ if (oldEntry != null) {
+ TableEntry newEntry =
+ new TableEntry(
+ oldEntry.channelId,
+ oldEntry.program,
+ null,
+ oldEntry.entryStartUtcMillis,
+ oldEntry.entryEndUtcMillis,
+ oldEntry.isBlocked());
+ updateEntry(oldEntry, newEntry);
+ }
+ }
}
- }
- }
- @Override
- public void onScheduledRecordingStatusChanged(ScheduledRecording... scheduledRecordings) {
- for (ScheduledRecording schedule : scheduledRecordings) {
- TableEntry oldEntry = getTableEntry(schedule);
- if (oldEntry != null) {
- TableEntry newEntry = new TableEntry(oldEntry.channelId, oldEntry.program,
- schedule, oldEntry.entryStartUtcMillis,
- oldEntry.entryEndUtcMillis, oldEntry.isBlocked());
- updateEntry(oldEntry, newEntry);
+ @Override
+ public void onScheduledRecordingStatusChanged(
+ ScheduledRecording... scheduledRecordings) {
+ for (ScheduledRecording schedule : scheduledRecordings) {
+ TableEntry oldEntry = getTableEntry(schedule);
+ if (oldEntry != null) {
+ TableEntry newEntry =
+ new TableEntry(
+ oldEntry.channelId,
+ oldEntry.program,
+ schedule,
+ oldEntry.entryStartUtcMillis,
+ oldEntry.entryEndUtcMillis,
+ oldEntry.isBlocked());
+ updateEntry(oldEntry, newEntry);
+ }
+ }
}
- }
- }
- };
+ };
private final OnConflictStateChangeListener mOnConflictStateChangeListener =
new OnConflictStateChangeListener() {
@Override
- public void onConflictStateChange(boolean conflict,
- ScheduledRecording... schedules) {
+ public void onConflictStateChange(
+ boolean conflict, ScheduledRecording... schedules) {
for (ScheduledRecording schedule : schedules) {
TableEntry entry = getTableEntry(schedule);
if (entry != null) {
@@ -181,8 +194,10 @@ public class ProgramManager {
}
};
- public ProgramManager(TvInputManagerHelper tvInputManagerHelper,
- ChannelDataManager channelDataManager, ProgramDataManager programDataManager,
+ public ProgramManager(
+ TvInputManagerHelper tvInputManagerHelper,
+ ChannelDataManager channelDataManager,
+ ProgramDataManager programDataManager,
@Nullable DvrDataManager dvrDataManager,
@Nullable DvrScheduleManager dvrScheduleManager) {
mTvInputManagerHelper = tvInputManagerHelper;
@@ -221,52 +236,39 @@ public class ProgramManager {
}
}
- /**
- * Adds a {@link Listener}.
- */
+ /** Adds a {@link Listener}. */
void addListener(Listener listener) {
mListeners.add(listener);
}
- /**
- * Registers a listener to be invoked when table entries are updated.
- */
+ /** Registers a listener to be invoked when table entries are updated. */
void addTableEntriesUpdatedListener(TableEntriesUpdatedListener listener) {
mTableEntriesUpdatedListeners.add(listener);
}
- /**
- * Registers a listener to be invoked when a table entry is changed.
- */
+ /** Registers a listener to be invoked when a table entry is changed. */
void addTableEntryChangedListener(TableEntryChangedListener listener) {
mTableEntryChangedListeners.add(listener);
}
- /**
- * Removes a {@link Listener}.
- */
+ /** Removes a {@link Listener}. */
void removeListener(Listener listener) {
mListeners.remove(listener);
}
- /**
- * Removes a previously installed table entries update listener.
- */
+ /** Removes a previously installed table entries update listener. */
void removeTableEntriesUpdatedListener(TableEntriesUpdatedListener listener) {
mTableEntriesUpdatedListeners.remove(listener);
}
- /**
- * Removes a previously installed table entry changed listener.
- */
+ /** Removes a previously installed table entry changed listener. */
void removeTableEntryChangedListener(TableEntryChangedListener listener) {
mTableEntryChangedListeners.remove(listener);
}
/**
- * Resets channel list with given genre.
- * Caller should call {@link #buildGenreFilters()} prior to call this API to make
- * This notifies channel updates to listeners.
+ * Resets channel list with given genre. Caller should call {@link #buildGenreFilters()} prior
+ * to call this API to make This notifies channel updates to listeners.
*/
void resetChannelListWithGenre(int genreId) {
if (genreId == mSelectedGenreId) {
@@ -275,8 +277,14 @@ public class ProgramManager {
mFilteredChannels = mGenreChannelList.get(genreId);
mSelectedGenreId = genreId;
if (DEBUG) {
- Log.d(TAG, "resetChannelListWithGenre: " + GenreItems.getCanonicalGenre(genreId)
- + " has " + mFilteredChannels.size() + " channels out of " + mChannels.size());
+ Log.d(
+ TAG,
+ "resetChannelListWithGenre: "
+ + GenreItems.getCanonicalGenre(genreId)
+ + " has "
+ + mFilteredChannels.size()
+ + " channels out of "
+ + mChannels.size());
}
if (mGenreChannelList.get(mSelectedGenreId) == null) {
throw new IllegalStateException("Genre filter isn't ready.");
@@ -284,9 +292,7 @@ public class ProgramManager {
notifyChannelsUpdated();
}
- /**
- * Update the initial time range to manage. It updates program entries and genre as well.
- */
+ /** Update the initial time range to manage. It updates program entries and genre as well. */
void updateInitialTimeRange(long startUtcMillis, long endUtcMillis) {
mStartUtcMillis = startUtcMillis;
if (endUtcMillis > mEndUtcMillis) {
@@ -298,10 +304,7 @@ public class ProgramManager {
setTimeRange(startUtcMillis, endUtcMillis);
}
-
- /**
- * Shifts the time range by the given time. Also makes ProgramGuide scroll the views.
- */
+ /** Shifts the time range by the given time. Also makes ProgramGuide scroll the views. */
void shiftTime(long timeMillisToScroll) {
long fromUtcMillis = mFromUtcMillis + timeMillisToScroll;
long toUtcMillis = mToUtcMillis + timeMillisToScroll;
@@ -316,23 +319,17 @@ public class ProgramManager {
setTimeRange(fromUtcMillis, toUtcMillis);
}
- /**
- * Returned the scrolled(shifted) time in milliseconds.
- */
+ /** Returned the scrolled(shifted) time in milliseconds. */
long getShiftedTime() {
return mFromUtcMillis - mStartUtcMillis;
}
- /**
- * Returns the start time set by {@link #updateInitialTimeRange}.
- */
+ /** Returns the start time set by {@link #updateInitialTimeRange}. */
long getStartTime() {
return mStartUtcMillis;
}
- /**
- * Returns the program index of the program with {@code entryId} or -1 if not found.
- */
+ /** Returns the program index of the program with {@code entryId} or -1 if not found. */
int getProgramIdIndex(long channelId, long entryId) {
List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
if (entries != null) {
@@ -345,38 +342,29 @@ public class ProgramManager {
return -1;
}
- /**
- * Returns the program index of the program at {@code time} or -1 if not found.
- */
+ /** Returns the program index of the program at {@code time} or -1 if not found. */
int getProgramIndexAtTime(long channelId, long time) {
List<TableEntry> entries = mChannelIdEntriesMap.get(channelId);
for (int i = 0; i < entries.size(); ++i) {
TableEntry entry = entries.get(i);
- if (entry.entryStartUtcMillis <= time
- && time < entry.entryEndUtcMillis) {
+ if (entry.entryStartUtcMillis <= time && time < entry.entryEndUtcMillis) {
return i;
}
}
return -1;
}
- /**
- * Returns the start time of currently managed time range, in UTC millisecond.
- */
+ /** Returns the start time of currently managed time range, in UTC millisecond. */
long getFromUtcMillis() {
return mFromUtcMillis;
}
- /**
- * Returns the end time of currently managed time range, in UTC millisecond.
- */
+ /** Returns the end time of currently managed time range, in UTC millisecond. */
long getToUtcMillis() {
return mToUtcMillis;
}
- /**
- * Returns the number of the currently managed channels.
- */
+ /** Returns the number of the currently managed channels. */
int getChannelCount() {
return mFilteredChannels.size();
}
@@ -393,15 +381,15 @@ public class ProgramManager {
}
/**
- * Returns the index of provided {@link Channel} within the currently managed channels.
- * Returns -1 if such a channel is not found.
+ * Returns the index of provided {@link Channel} within the currently managed channels. Returns
+ * -1 if such a channel is not found.
*/
int getChannelIndex(Channel channel) {
return mFilteredChannels.indexOf(channel);
}
/**
- * Returns the index of channel with {@code channelId} within the currently managed channels.
+ * Returns the index of channel with {@code channelId} within the currently managed channels.
* Returns -1 if such a channel is not found.
*/
int getChannelIndex(long channelId) {
@@ -425,9 +413,7 @@ public class ProgramManager {
return mChannelIdEntriesMap.get(channelId).get(index);
}
- /**
- * Returns list genre ID's which has a channel.
- */
+ /** Returns list genre ID's which has a channel. */
List<Integer> getFilteredGenreIds() {
return mFilteredGenreIds;
}
@@ -457,15 +443,13 @@ public class ProgramManager {
buildGenreFilters();
}
- /**
- * Updates the table entries without notifying the change.
- */
+ /** Updates the table entries without notifying the change. */
private void updateTableEntriesWithoutNotification(boolean clear) {
if (clear) {
mChannelIdEntriesMap.clear();
}
- boolean parentalControlsEnabled = mTvInputManagerHelper.getParentalControlSettings()
- .isParentalControlsEnabled();
+ boolean parentalControlsEnabled =
+ mTvInputManagerHelper.getParentalControlSettings().isParentalControlsEnabled();
for (Channel channel : mChannels) {
long channelId = channel.getId();
// Inline the updating of the mChannelIdEntriesMap here so we can only call
@@ -475,8 +459,12 @@ public class ProgramManager {
int size = entries.size();
if (DEBUG) {
- Log.d(TAG, "Programs are loaded for channel " + channel.getId()
- + ", loaded size = " + size);
+ Log.d(
+ TAG,
+ "Programs are loaded for channel "
+ + channel.getId()
+ + ", loaded size = "
+ + size);
}
if (size == 0) {
continue;
@@ -496,14 +484,19 @@ public class ProgramManager {
} else {
TableEntry lastEntry = entries.get(entries.size() - 1);
if (mEndUtcMillis > lastEntry.entryEndUtcMillis) {
- entries.add(new TableEntry(channelId, lastEntry.entryEndUtcMillis,
- mEndUtcMillis));
+ entries.add(
+ new TableEntry(
+ channelId, lastEntry.entryEndUtcMillis, mEndUtcMillis));
} else if (lastEntry.entryEndUtcMillis == Long.MAX_VALUE) {
entries.remove(entries.size() - 1);
- entries.add(new TableEntry(lastEntry.channelId, lastEntry.program,
- lastEntry.scheduledRecording,
- lastEntry.entryStartUtcMillis, mEndUtcMillis,
- lastEntry.mIsBlocked));
+ entries.add(
+ new TableEntry(
+ lastEntry.channelId,
+ lastEntry.program,
+ lastEntry.scheduledRecording,
+ lastEntry.entryStartUtcMillis,
+ mEndUtcMillis,
+ lastEntry.mIsBlocked));
}
}
}
@@ -511,11 +504,10 @@ public class ProgramManager {
}
/**
- * Build genre filters based on the current programs.
- * This categories channels by its current program's canonical genres
- * and subsequent @{link resetChannelListWithGenre(int)} calls will reset channel list
- * with built channel list.
- * This is expected to be called whenever program guide is shown.
+ * Build genre filters based on the current programs. This categories channels by its current
+ * program's canonical genres and subsequent @{link resetChannelListWithGenre(int)} calls will
+ * reset channel list with built channel list. This is expected to be called whenever program
+ * guide is shown.
*/
private void buildGenreFilters() {
if (DEBUG) Log.d(TAG, "buildGenreFilters");
@@ -572,9 +564,13 @@ public class ProgramManager {
private void setTimeRange(long fromUtcMillis, long toUtcMillis) {
if (DEBUG) {
- Log.d(TAG, "setTimeRange. {FromTime="
- + Utils.toTimeString(fromUtcMillis) + ", ToTime="
- + Utils.toTimeString(toUtcMillis) + "}");
+ Log.d(
+ TAG,
+ "setTimeRange. {FromTime="
+ + Utils.toTimeString(fromUtcMillis)
+ + ", ToTime="
+ + Utils.toTimeString(toUtcMillis)
+ + "}");
}
if (mFromUtcMillis != fromUtcMillis || mToUtcMillis != toUtcMillis) {
mFromUtcMillis = fromUtcMillis;
@@ -585,8 +581,8 @@ public class ProgramManager {
private List<TableEntry> createProgramEntries(long channelId, boolean parentalControlsEnabled) {
List<TableEntry> entries = new ArrayList<>();
- boolean channelLocked = parentalControlsEnabled
- && mChannelDataManager.getChannel(channelId).isLocked();
+ boolean channelLocked =
+ parentalControlsEnabled && mChannelDataManager.getChannel(channelId).isLocked();
if (channelLocked) {
entries.add(new TableEntry(channelId, mStartUtcMillis, Long.MAX_VALUE, true));
} else {
@@ -597,20 +593,27 @@ public class ProgramManager {
// Dummy program.
continue;
}
- long programStartTime = Math.max(program.getStartTimeUtcMillis(),
- mStartUtcMillis);
+ long programStartTime = Math.max(program.getStartTimeUtcMillis(), mStartUtcMillis);
long programEndTime = program.getEndTimeUtcMillis();
if (programStartTime > lastProgramEndTime) {
// Gap since the last program.
- entries.add(new TableEntry(channelId, lastProgramEndTime,
- programStartTime));
+ entries.add(new TableEntry(channelId, lastProgramEndTime, programStartTime));
lastProgramEndTime = programStartTime;
}
if (programEndTime > lastProgramEndTime) {
- ScheduledRecording scheduledRecording = mDvrDataManager == null ? null
- : mDvrDataManager.getScheduledRecordingForProgramId(program.getId());
- entries.add(new TableEntry(channelId, program, scheduledRecording,
- lastProgramEndTime, programEndTime, false));
+ ScheduledRecording scheduledRecording =
+ mDvrDataManager == null
+ ? null
+ : mDvrDataManager.getScheduledRecordingForProgramId(
+ program.getId());
+ entries.add(
+ new TableEntry(
+ channelId,
+ program,
+ scheduledRecording,
+ lastProgramEndTime,
+ programEndTime,
+ false));
lastProgramEndTime = programEndTime;
}
}
@@ -622,9 +625,15 @@ public class ProgramManager {
// If the first entry's width doesn't have enough width, it is not good to show
// the first entry from UI perspective. So we clip it out.
entries.remove(0);
- entries.set(0, new TableEntry(secondEntry.channelId, secondEntry.program,
- secondEntry.scheduledRecording, mStartUtcMillis,
- secondEntry.entryEndUtcMillis, secondEntry.mIsBlocked));
+ entries.set(
+ 0,
+ new TableEntry(
+ secondEntry.channelId,
+ secondEntry.program,
+ secondEntry.scheduledRecording,
+ mStartUtcMillis,
+ secondEntry.entryEndUtcMillis,
+ secondEntry.mIsBlocked));
}
}
return entries;
@@ -662,8 +671,8 @@ public class ProgramManager {
/**
* Entry for program guide table. An "entry" can be either an actual program or a gap between
- * programs. This is needed for {@link ProgramListAdapter} because
- * {@link android.support.v17.leanback.widget.HorizontalGridView} ignores margins between items.
+ * programs. This is needed for {@link ProgramListAdapter} because {@link
+ * android.support.v17.leanback.widget.HorizontalGridView} ignores margins between items.
*/
static class TableEntry {
/** Channel ID which this entry is included. */
@@ -686,18 +695,27 @@ public class ProgramManager {
this(channelId, null, startUtcMillis, endUtcMillis, false);
}
- private TableEntry(long channelId, long startUtcMillis, long endUtcMillis,
- boolean blocked) {
+ private TableEntry(
+ long channelId, long startUtcMillis, long endUtcMillis, boolean blocked) {
this(channelId, null, null, startUtcMillis, endUtcMillis, blocked);
}
- private TableEntry(long channelId, Program program, long entryStartUtcMillis,
- long entryEndUtcMillis, boolean isBlocked) {
+ private TableEntry(
+ long channelId,
+ Program program,
+ long entryStartUtcMillis,
+ long entryEndUtcMillis,
+ boolean isBlocked) {
this(channelId, program, null, entryStartUtcMillis, entryEndUtcMillis, isBlocked);
}
- private TableEntry(long channelId, Program program, ScheduledRecording scheduledRecording,
- long entryStartUtcMillis, long entryEndUtcMillis, boolean isBlocked) {
+ private TableEntry(
+ long channelId,
+ Program program,
+ ScheduledRecording scheduledRecording,
+ long entryStartUtcMillis,
+ long entryEndUtcMillis,
+ boolean isBlocked) {
this.channelId = channelId;
this.program = program;
this.scheduledRecording = scheduledRecording;
@@ -706,46 +724,34 @@ public class ProgramManager {
mIsBlocked = isBlocked;
}
- /**
- * A stable id useful for {@link android.support.v7.widget.RecyclerView.Adapter}.
- */
+ /** A stable id useful for {@link android.support.v7.widget.RecyclerView.Adapter}. */
long getId() {
// using a negative entryEndUtcMillis keeps it from conflicting with program Id
return program != null ? program.getId() : -entryEndUtcMillis;
}
- /**
- * Returns true if this is a gap.
- */
+ /** Returns true if this is a gap. */
boolean isGap() {
- return !Program.isValid(program);
+ return !Program.isProgramValid(program);
}
- /**
- * Returns true if this channel is blocked.
- */
+ /** Returns true if this channel is blocked. */
boolean isBlocked() {
return mIsBlocked;
}
- /**
- * Returns true if this program is on the air.
- */
+ /** Returns true if this program is on the air. */
boolean isCurrentProgram() {
long current = System.currentTimeMillis();
return entryStartUtcMillis <= current && entryEndUtcMillis > current;
}
- /**
- * Returns if this program has the genre.
- */
+ /** Returns if this program has the genre. */
boolean hasGenre(int genreId) {
return !isGap() && program.hasGenre(genreId);
}
- /**
- * Returns the width of table entry, in pixels.
- */
+ /** Returns the width of table entry, in pixels. */
int getWidth() {
return GuideUtils.convertMillisToPixel(entryStartUtcMillis, entryEndUtcMillis);
}
@@ -753,17 +759,42 @@ public class ProgramManager {
@Override
public String toString() {
return "TableEntry{"
- + "hashCode=" + hashCode()
- + ", channelId=" + channelId
- + ", program=" + program
- + ", startTime=" + Utils.toTimeString(entryStartUtcMillis)
- + ", endTimeTime=" + Utils.toTimeString(entryEndUtcMillis) + "}";
- }
+ + "hashCode="
+ + hashCode()
+ + ", channelId="
+ + channelId
+ + ", program="
+ + program
+ + ", startTime="
+ + Utils.toTimeString(entryStartUtcMillis)
+ + ", endTimeTime="
+ + Utils.toTimeString(entryEndUtcMillis)
+ + "}";
+ }
+ }
+
+ @VisibleForTesting
+ public static TableEntry createTableEntryForTest(
+ long channelId,
+ Program program,
+ ScheduledRecording scheduledRecording,
+ long entryStartUtcMillis,
+ long entryEndUtcMillis,
+ boolean isBlocked) {
+ return new TableEntry(
+ channelId,
+ program,
+ scheduledRecording,
+ entryStartUtcMillis,
+ entryEndUtcMillis,
+ isBlocked);
}
interface Listener {
void onGenresUpdated();
+
void onChannelsUpdated();
+
void onTimeRangeUpdated();
}
@@ -777,12 +808,12 @@ public class ProgramManager {
static class ListenerAdapter implements Listener {
@Override
- public void onGenresUpdated() { }
+ public void onGenresUpdated() {}
@Override
- public void onChannelsUpdated() { }
+ public void onChannelsUpdated() {}
@Override
- public void onTimeRangeUpdated() { }
+ public void onTimeRangeUpdated() {}
}
}
diff --git a/src/com/android/tv/guide/ProgramRow.java b/src/com/android/tv/guide/ProgramRow.java
index fefc724c..83175bb6 100644
--- a/src/com/android/tv/guide/ProgramRow.java
+++ b/src/com/android/tv/guide/ProgramRow.java
@@ -24,12 +24,9 @@ import android.util.Log;
import android.util.Range;
import android.view.View;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
-
-import com.android.tv.MainActivity;
-import com.android.tv.data.Channel;
+import com.android.tv.data.api.Channel;
import com.android.tv.guide.ProgramManager.TableEntry;
import com.android.tv.util.Utils;
-
import java.util.concurrent.TimeUnit;
public class ProgramRow extends TimelineGridView {
@@ -47,25 +44,23 @@ public class ProgramRow extends TimelineGridView {
interface ChildFocusListener {
/**
- * Is called after focus is moved. Caller should check if old and new focuses are
- * listener's children.
- * See {@code ProgramRow#setChildFocusListener(ChildFocusListener)}.
+ * Is called after focus is moved. Caller should check if old and new focuses are listener's
+ * children. See {@code ProgramRow#setChildFocusListener(ChildFocusListener)}.
*/
void onChildFocus(View oldFocus, View newFocus);
}
- /**
- * Used only for debugging.
- */
+ /** Used only for debugging. */
private Channel mChannel;
- private final OnGlobalLayoutListener mLayoutListener = new OnGlobalLayoutListener() {
- @Override
- public void onGlobalLayout() {
- getViewTreeObserver().removeOnGlobalLayoutListener(this);
- updateChildVisibleArea();
- }
- };
+ private final OnGlobalLayoutListener mLayoutListener =
+ new OnGlobalLayoutListener() {
+ @Override
+ public void onGlobalLayout() {
+ getViewTreeObserver().removeOnGlobalLayoutListener(this);
+ updateChildVisibleArea();
+ }
+ };
public ProgramRow(Context context) {
this(context, null);
@@ -79,9 +74,7 @@ public class ProgramRow extends TimelineGridView {
super(context, attrs, defStyle);
}
- /**
- * Registers a listener focus events occurring on children to the {@code ProgramRow}.
- */
+ /** Registers a listener focus events occurring on children to the {@code ProgramRow}. */
public void setChildFocusListener(ChildFocusListener childFocusListener) {
mChildFocusListener = childFocusListener;
}
@@ -108,9 +101,7 @@ public class ProgramRow extends TimelineGridView {
updateChildVisibleArea();
}
- /**
- * Moves focus to the current program.
- */
+ /** Moves focus to the current program. */
public void focusCurrentProgram() {
View currentProgram = getCurrentProgramView();
if (currentProgram == null) {
@@ -124,13 +115,15 @@ public class ProgramRow extends TimelineGridView {
// Call this API after RTL is resolved. (i.e. View is measured.)
private boolean isDirectionStart(int direction) {
return getLayoutDirection() == LAYOUT_DIRECTION_LTR
- ? direction == View.FOCUS_LEFT : direction == View.FOCUS_RIGHT;
+ ? direction == View.FOCUS_LEFT
+ : direction == View.FOCUS_RIGHT;
}
// Call this API after RTL is resolved. (i.e. View is measured.)
private boolean isDirectionEnd(int direction) {
return getLayoutDirection() == LAYOUT_DIRECTION_LTR
- ? direction == View.FOCUS_RIGHT : direction == View.FOCUS_LEFT;
+ ? direction == View.FOCUS_RIGHT
+ : direction == View.FOCUS_LEFT;
}
@Override
@@ -142,8 +135,8 @@ public class ProgramRow extends TimelineGridView {
if (isDirectionStart(direction) || direction == View.FOCUS_BACKWARD) {
if (focusedEntry.entryStartUtcMillis < fromMillis) {
// The current entry starts outside of the view; Align or scroll to the left.
- scrollByTime(Math.max(-ONE_HOUR_MILLIS,
- focusedEntry.entryStartUtcMillis - fromMillis));
+ scrollByTime(
+ Math.max(-ONE_HOUR_MILLIS, focusedEntry.entryStartUtcMillis - fromMillis));
return focused;
}
} else if (isDirectionEnd(direction) || direction == View.FOCUS_FORWARD) {
@@ -169,17 +162,19 @@ public class ProgramRow extends TimelineGridView {
TableEntry targetEntry = ((ProgramItemView) target).getTableEntry();
if (isDirectionStart(direction) || direction == View.FOCUS_BACKWARD) {
- if (targetEntry.entryStartUtcMillis < fromMillis &&
- targetEntry.entryEndUtcMillis < fromMillis + HALF_HOUR_MILLIS) {
+ if (targetEntry.entryStartUtcMillis < fromMillis
+ && targetEntry.entryEndUtcMillis < fromMillis + HALF_HOUR_MILLIS) {
// The target entry starts outside the view; Align or scroll to the left.
- scrollByTime(Math.max(-ONE_HOUR_MILLIS,
- targetEntry.entryStartUtcMillis - fromMillis));
+ scrollByTime(
+ Math.max(-ONE_HOUR_MILLIS, targetEntry.entryStartUtcMillis - fromMillis));
}
} else if (isDirectionEnd(direction) || direction == View.FOCUS_FORWARD) {
if (targetEntry.entryStartUtcMillis > fromMillis + ONE_HOUR_MILLIS + HALF_HOUR_MILLIS) {
// The target entry starts outside the view; Align or scroll to the right.
- scrollByTime(Math.min(ONE_HOUR_MILLIS,
- targetEntry.entryStartUtcMillis - fromMillis - ONE_HOUR_MILLIS));
+ scrollByTime(
+ Math.min(
+ ONE_HOUR_MILLIS,
+ targetEntry.entryStartUtcMillis - fromMillis - ONE_HOUR_MILLIS));
}
}
@@ -188,8 +183,11 @@ public class ProgramRow extends TimelineGridView {
private void scrollByTime(long timeToScroll) {
if (DEBUG) {
- Log.d(TAG, "scrollByTime(timeToScroll="
- + TimeUnit.MILLISECONDS.toMinutes(timeToScroll) + "min)");
+ Log.d(
+ TAG,
+ "scrollByTime(timeToScroll="
+ + TimeUnit.MILLISECONDS.toMinutes(timeToScroll)
+ + "min)");
}
mProgramManager.shiftTime(timeToScroll);
}
@@ -203,12 +201,13 @@ public class ProgramRow extends TimelineGridView {
// The focus is lost due to information loaded. Requests focus immediately.
// (Because this entry is detached after real entries attached, we can't take
// the below approach to resume focus on entry being attached.)
- post(new Runnable() {
- @Override
- public void run() {
- requestFocus();
- }
- });
+ post(
+ new Runnable() {
+ @Override
+ public void run() {
+ requestFocus();
+ }
+ });
} else if (entry.isCurrentProgram()) {
if (DEBUG) Log.d(TAG, "Keep focus to the current program");
// Current program is visible in the guide.
@@ -227,12 +226,13 @@ public class ProgramRow extends TimelineGridView {
TableEntry entry = ((ProgramItemView) child).getTableEntry();
if (entry.isCurrentProgram()) {
mKeepFocusToCurrentProgram = false;
- post(new Runnable() {
- @Override
- public void run() {
- requestFocus();
- }
- });
+ post(
+ new Runnable() {
+ @Override
+ public void run() {
+ requestFocus();
+ }
+ });
}
}
}
@@ -243,8 +243,12 @@ public class ProgramRow extends TimelineGridView {
// Give focus according to the previous focused range
Range<Integer> focusRange = programGrid.getFocusRange();
- View nextFocus = GuideUtils.findNextFocusedProgram(this, focusRange.getLower(),
- focusRange.getUpper(), programGrid.isKeepCurrentProgramFocused());
+ View nextFocus =
+ GuideUtils.findNextFocusedProgram(
+ this,
+ focusRange.getLower(),
+ focusRange.getUpper(),
+ programGrid.isKeepCurrentProgramFocused());
if (nextFocus != null) {
return nextFocus.requestFocus();
@@ -279,30 +283,29 @@ public class ProgramRow extends TimelineGridView {
mChannel = channel;
}
- /**
- * Sets the instance of {@link ProgramGuide}
- */
+ /** Sets the instance of {@link ProgramGuide} */
public void setProgramGuide(ProgramGuide programGuide) {
mProgramGuide = programGuide;
mProgramManager = programGuide.getProgramManager();
}
- /**
- * Resets the scroll with the initial offset {@code scrollOffset}.
- */
+ /** Resets the scroll with the initial offset {@code scrollOffset}. */
public void resetScroll(int scrollOffset) {
- long startTime = GuideUtils.convertPixelToMillis(scrollOffset)
- + mProgramManager.getStartTime();
- int position = mChannel == null ? -1 : mProgramManager.getProgramIndexAtTime(
- mChannel.getId(), startTime);
+ long startTime =
+ GuideUtils.convertPixelToMillis(scrollOffset) + mProgramManager.getStartTime();
+ int position =
+ mChannel == null
+ ? -1
+ : mProgramManager.getProgramIndexAtTime(mChannel.getId(), startTime);
if (position < 0) {
getLayoutManager().scrollToPosition(0);
} else {
TableEntry entry = mProgramManager.getTableEntry(mChannel.getId(), position);
- int offset = GuideUtils.convertMillisToPixel(
- mProgramManager.getStartTime(), entry.entryStartUtcMillis) - scrollOffset;
- ((LinearLayoutManager) getLayoutManager())
- .scrollToPositionWithOffset(position, offset);
+ int offset =
+ GuideUtils.convertMillisToPixel(
+ mProgramManager.getStartTime(), entry.entryStartUtcMillis)
+ - scrollOffset;
+ ((LinearLayoutManager) getLayoutManager()).scrollToPositionWithOffset(position, offset);
// Workaround to b/31598505. When a program's duration is too long,
// RecyclerView.onScrolled() will not be called after scrollToPositionWithOffset().
// Therefore we have to update children's visible areas by ourselves in this case.
diff --git a/src/com/android/tv/guide/ProgramTableAdapter.java b/src/com/android/tv/guide/ProgramTableAdapter.java
index 99f853b1..6e7485ac 100644
--- a/src/com/android/tv/guide/ProgramTableAdapter.java
+++ b/src/com/android/tv/guide/ProgramTableAdapter.java
@@ -16,8 +16,6 @@
package com.android.tv.guide;
-import static com.android.tv.util.ImageLoader.ImageLoaderCallback;
-
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
@@ -49,32 +47,30 @@ import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeL
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-
import com.android.tv.R;
-import com.android.tv.TvApplication;
-import com.android.tv.common.TvCommonUtils;
+import com.android.tv.TvSingletons;
import com.android.tv.common.feature.CommonFeatures;
-import com.android.tv.data.Channel;
+import com.android.tv.common.util.CommonUtils;
import com.android.tv.data.Program;
import com.android.tv.data.Program.CriticScore;
+import com.android.tv.data.api.Channel;
import com.android.tv.dvr.DvrDataManager;
import com.android.tv.dvr.DvrManager;
import com.android.tv.dvr.data.ScheduledRecording;
import com.android.tv.guide.ProgramManager.TableEntriesUpdatedListener;
import com.android.tv.parental.ParentalControlSettings;
import com.android.tv.ui.HardwareLayerAnimatorListenerAdapter;
-import com.android.tv.util.ImageCache;
-import com.android.tv.util.ImageLoader;
-import com.android.tv.util.ImageLoader.LoadTvInputLogoTask;
import com.android.tv.util.TvInputManagerHelper;
import com.android.tv.util.Utils;
+import com.android.tv.util.images.ImageCache;
+import com.android.tv.util.images.ImageLoader;
+import com.android.tv.util.images.ImageLoader.ImageLoaderCallback;
+import com.android.tv.util.images.ImageLoader.LoadTvInputLogoTask;
import java.util.ArrayList;
import java.util.List;
-/**
- * Adapts the {@link ProgramListAdapter} list to the body of the program guide table.
- */
+/** Adapts the {@link ProgramListAdapter} list to the body of the program guide table. */
class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.ProgramRowViewHolder>
implements ProgramManager.TableEntryChangedListener {
private static final String TAG = "ProgramTableAdapter";
@@ -118,10 +114,10 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mContext = context;
mAccessibilityManager =
(AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mTvInputManagerHelper = TvApplication.getSingletons(context).getTvInputManagerHelper();
+ mTvInputManagerHelper = TvSingletons.getSingletons(context).getTvInputManagerHelper();
if (CommonFeatures.DVR.isEnabled(context)) {
- mDvrManager = TvApplication.getSingletons(context).getDvrManager();
- mDvrDataManager = TvApplication.getSingletons(context).getDvrDataManager();
+ mDvrManager = TvSingletons.getSingletons(context).getDvrManager();
+ mDvrDataManager = TvSingletons.getSingletons(context).getDvrDataManager();
} else {
mDvrManager = null;
mDvrDataManager = null;
@@ -130,58 +126,62 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mProgramManager = programGuide.getProgramManager();
Resources res = context.getResources();
- mChannelLogoWidth = res.getDimensionPixelSize(
- R.dimen.program_guide_table_header_column_channel_logo_width);
- mChannelLogoHeight = res.getDimensionPixelSize(
- R.dimen.program_guide_table_header_column_channel_logo_height);
- mImageWidth = res.getDimensionPixelSize(
- R.dimen.program_guide_table_detail_image_width);
- mImageHeight = res.getDimensionPixelSize(
- R.dimen.program_guide_table_detail_image_height);
- mProgramTitleForNoInformation = res.getString(
- R.string.program_title_for_no_information);
- mProgramTitleForBlockedChannel = res.getString(
- R.string.program_title_for_blocked_channel);
- mChannelTextColor = res.getColor(
- R.color.program_guide_table_header_column_channel_number_text_color, null);
- mChannelBlockedTextColor = res.getColor(
- R.color.program_guide_table_header_column_channel_number_blocked_text_color, null);
- mDetailTextColor = res.getColor(
- R.color.program_guide_table_detail_title_text_color, null);
- mDetailGrayedTextColor = res.getColor(
- R.color.program_guide_table_detail_title_grayed_text_color, null);
+ mChannelLogoWidth =
+ res.getDimensionPixelSize(
+ R.dimen.program_guide_table_header_column_channel_logo_width);
+ mChannelLogoHeight =
+ res.getDimensionPixelSize(
+ R.dimen.program_guide_table_header_column_channel_logo_height);
+ mImageWidth = res.getDimensionPixelSize(R.dimen.program_guide_table_detail_image_width);
+ mImageHeight = res.getDimensionPixelSize(R.dimen.program_guide_table_detail_image_height);
+ mProgramTitleForNoInformation = res.getString(R.string.program_title_for_no_information);
+ mProgramTitleForBlockedChannel = res.getString(R.string.program_title_for_blocked_channel);
+ mChannelTextColor =
+ res.getColor(
+ R.color.program_guide_table_header_column_channel_number_text_color, null);
+ mChannelBlockedTextColor =
+ res.getColor(
+ R.color.program_guide_table_header_column_channel_number_blocked_text_color,
+ null);
+ mDetailTextColor = res.getColor(R.color.program_guide_table_detail_title_text_color, null);
+ mDetailGrayedTextColor =
+ res.getColor(R.color.program_guide_table_detail_title_grayed_text_color, null);
mAnimationDuration =
res.getInteger(R.integer.program_guide_table_detail_fade_anim_duration);
- mDetailPadding = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_detail_padding);
+ mDetailPadding = res.getDimensionPixelOffset(R.dimen.program_guide_table_detail_padding);
mProgramRecordableText = res.getString(R.string.dvr_epg_program_recordable);
mRecordingScheduledText = res.getString(R.string.dvr_epg_program_recording_scheduled);
mRecordingConflictText = res.getString(R.string.dvr_epg_program_recording_conflict);
mRecordingFailedText = res.getString(R.string.dvr_epg_program_recording_failed);
mRecordingInProgressText = res.getString(R.string.dvr_epg_program_recording_in_progress);
- mDvrPaddingStartWithTrack = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_detail_dvr_margin_start);
- mDvrPaddingStartWithOutTrack = res.getDimensionPixelOffset(
- R.dimen.program_guide_table_detail_dvr_margin_start_without_track);
-
- int episodeTitleSize = res.getDimensionPixelSize(
- R.dimen.program_guide_table_detail_episode_title_text_size);
- ColorStateList episodeTitleColor = ColorStateList.valueOf(
- res.getColor(R.color.program_guide_table_detail_episode_title_text_color, null));
- mEpisodeTitleStyle = new TextAppearanceSpan(null, 0, episodeTitleSize,
- episodeTitleColor, null);
+ mDvrPaddingStartWithTrack =
+ res.getDimensionPixelOffset(R.dimen.program_guide_table_detail_dvr_margin_start);
+ mDvrPaddingStartWithOutTrack =
+ res.getDimensionPixelOffset(
+ R.dimen.program_guide_table_detail_dvr_margin_start_without_track);
+
+ int episodeTitleSize =
+ res.getDimensionPixelSize(
+ R.dimen.program_guide_table_detail_episode_title_text_size);
+ ColorStateList episodeTitleColor =
+ ColorStateList.valueOf(
+ res.getColor(
+ R.color.program_guide_table_detail_episode_title_text_color, null));
+ mEpisodeTitleStyle =
+ new TextAppearanceSpan(null, 0, episodeTitleSize, episodeTitleColor, null);
mCriticScoreViews = new ArrayList<>();
mRecycledViewPool = new RecycledViewPool();
- mRecycledViewPool.setMaxRecycledViews(R.layout.program_guide_table_item,
- context.getResources().getInteger(
- R.integer.max_recycled_view_pool_epg_table_item));
- mProgramManager.addListener(new ProgramManager.ListenerAdapter() {
- @Override
- public void onChannelsUpdated() {
- update();
- }
- });
+ mRecycledViewPool.setMaxRecycledViews(
+ R.layout.program_guide_table_item,
+ context.getResources().getInteger(R.integer.max_recycled_view_pool_epg_table_item));
+ mProgramManager.addListener(
+ new ProgramManager.ListenerAdapter() {
+ @Override
+ public void onChannelsUpdated() {
+ update();
+ }
+ });
update();
mProgramManager.addTableEntryChangedListener(this);
}
@@ -193,8 +193,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
}
mProgramListAdapters.clear();
for (int i = 0; i < mProgramManager.getChannelCount(); i++) {
- ProgramListAdapter listAdapter = new ProgramListAdapter(mContext.getResources(),
- mProgramGuide, i);
+ ProgramListAdapter listAdapter =
+ new ProgramListAdapter(mContext.getResources(), mProgramGuide, i);
mProgramManager.addTableEntriesUpdatedListener(listAdapter);
mProgramListAdapters.add(listAdapter);
}
@@ -250,29 +250,31 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
private ProgramManager.TableEntry mSelectedEntry;
private Animator mDetailOutAnimator;
private Animator mDetailInAnimator;
- private final Runnable mDetailInStarter = new Runnable() {
- @Override
- public void run() {
- mProgramRow.removeOnScrollListener(mOnScrollListener);
- if (mDetailInAnimator != null) {
- mDetailInAnimator.start();
- }
- }
- };
- private final Runnable mUpdateDetailViewRunnable = new Runnable() {
- @Override
- public void run() {
- updateDetailView();
- }
- };
+ private final Runnable mDetailInStarter =
+ new Runnable() {
+ @Override
+ public void run() {
+ mProgramRow.removeOnScrollListener(mOnScrollListener);
+ if (mDetailInAnimator != null) {
+ mDetailInAnimator.start();
+ }
+ }
+ };
+ private final Runnable mUpdateDetailViewRunnable =
+ new Runnable() {
+ @Override
+ public void run() {
+ updateDetailView();
+ }
+ };
private final RecyclerView.OnScrollListener mOnScrollListener =
new RecyclerView.OnScrollListener() {
- @Override
- public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
- onHorizontalScrolled();
- }
- };
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ onHorizontalScrolled();
+ }
+ };
private final ViewTreeObserver.OnGlobalFocusChangeListener mGlobalFocusChangeListener =
new ViewTreeObserver.OnGlobalFocusChangeListener() {
@@ -313,8 +315,7 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
new AccessibilityManager.AccessibilityStateChangeListener() {
@Override
public void onAccessibilityStateChanged(boolean enable) {
- enable &= !TvCommonUtils.isRunningInTest();
- mDetailView.setFocusable(enable);
+ enable &= !CommonUtils.isRunningInTest();
mChannelHeaderView.setFocusable(enable);
}
};
@@ -327,7 +328,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
- mContainer.getViewTreeObserver()
+ mContainer
+ .getViewTreeObserver()
.addOnGlobalFocusChangeListener(mGlobalFocusChangeListener);
mAccessibilityManager.addAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
@@ -335,7 +337,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
@Override
public void onViewDetachedFromWindow(View v) {
- mContainer.getViewTreeObserver()
+ mContainer
+ .getViewTreeObserver()
.removeOnGlobalFocusChangeListener(mGlobalFocusChangeListener);
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
@@ -364,9 +367,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mChannelBlockView = (ImageView) mContainer.findViewById(R.id.channel_block);
mInputLogoView = (ImageView) mContainer.findViewById(R.id.input_logo);
- boolean accessibilityEnabled = mAccessibilityManager.isEnabled()
- && !TvCommonUtils.isRunningInTest();
- mDetailView.setFocusable(accessibilityEnabled);
+ boolean accessibilityEnabled =
+ mAccessibilityManager.isEnabled() && !CommonUtils.isRunningInTest();
mChannelHeaderView.setFocusable(accessibilityEnabled);
}
@@ -382,9 +384,10 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mDetailView.setVisibility(View.GONE);
// The bottom-left of the last channel header view will have a rounded corner.
- mChannelHeaderView.setBackgroundResource((position < mProgramListAdapters.size() - 1)
- ? R.drawable.program_guide_table_header_column_item_background
- : R.drawable.program_guide_table_header_column_last_item_background);
+ mChannelHeaderView.setBackgroundResource(
+ (position < mProgramListAdapters.size() - 1)
+ ? R.drawable.program_guide_table_header_column_item_background
+ : R.drawable.program_guide_table_header_column_last_item_background);
}
private void onBindChannel(Channel channel) {
@@ -411,7 +414,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
} else {
size = R.dimen.program_guide_table_header_column_channel_number_small_font_size;
}
- mChannelNumberView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
+ mChannelNumberView.setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
mChannelNumberView.getContext().getResources().getDimension(size));
mChannelNumberView.setText(displayNumber);
mChannelNumberView.setVisibility(View.VISIBLE);
@@ -429,8 +433,11 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mChannelNameView.setVisibility(View.VISIBLE);
mChannelBlockView.setVisibility(View.GONE);
- mChannel.loadBitmap(itemView.getContext(), Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
- mChannelLogoWidth, mChannelLogoHeight,
+ mChannel.loadBitmap(
+ itemView.getContext(),
+ Channel.LOAD_IMAGE_TYPE_CHANNEL_LOGO,
+ mChannelLogoWidth,
+ mChannelLogoHeight,
createChannelLogoLoadedCallback(this, channel.getId()));
}
}
@@ -439,7 +446,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
public void onChildFocus(View oldFocus, View newFocus) {
if (newFocus == null) {
return;
- } // When the accessibility service is enabled, focus might be put on channel's header or
+ } // When the accessibility service is enabled, focus might be put on channel's header
+ // or
// detail view, besides program items.
if (newFocus == mChannelHeaderView) {
mSelectedEntry = ((ProgramItemView) mProgramRow.getChildAt(0)).getTableEntry();
@@ -461,7 +469,7 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return;
}
- if (Program.isValid(mSelectedEntry.program)) {
+ if (Program.isProgramValid(mSelectedEntry.program)) {
Program program = mSelectedEntry.program;
if (getProgramBlock(program) == null) {
program.prefetchPosterArt(itemView.getContext(), mImageWidth, mImageHeight);
@@ -473,10 +481,12 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
View detailContentView = mDetailView.findViewById(R.id.detail_content);
if (mDetailInAnimator == null) {
- mDetailOutAnimator = ObjectAnimator.ofPropertyValuesHolder(detailContentView,
- PropertyValuesHolder.ofFloat(View.ALPHA, 1f, 0f),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_X,
- 0f, direction * mDetailPadding));
+ mDetailOutAnimator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ detailContentView,
+ PropertyValuesHolder.ofFloat(View.ALPHA, 1f, 0f),
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_X, 0f, direction * mDetailPadding));
mDetailOutAnimator.setDuration(mAnimationDuration);
mDetailOutAnimator.addListener(
new HardwareLayerAnimatorListenerAdapter(detailContentView) {
@@ -501,10 +511,12 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mHandler.postDelayed(mDetailInStarter, mAnimationDuration);
}
- mDetailInAnimator = ObjectAnimator.ofPropertyValuesHolder(detailContentView,
- PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
- PropertyValuesHolder.ofFloat(View.TRANSLATION_X,
- direction * -mDetailPadding, 0f));
+ mDetailInAnimator =
+ ObjectAnimator.ofPropertyValuesHolder(
+ detailContentView,
+ PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
+ PropertyValuesHolder.ofFloat(
+ View.TRANSLATION_X, direction * -mDetailPadding, 0f));
mDetailInAnimator.setDuration(mAnimationDuration);
mDetailInAnimator.addListener(
new HardwareLayerAnimatorListenerAdapter(detailContentView) {
@@ -529,7 +541,7 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
}
if (DEBUG) Log.d(TAG, "updateDetailView");
mCriticScoresLayout.removeAllViews();
- if (Program.isValid(mSelectedEntry.program)) {
+ if (Program.isProgramValid(mSelectedEntry.program)) {
mTitleView.setTextColor(mDetailTextColor);
Context context = itemView.getContext();
Program program = mSelectedEntry.program;
@@ -538,7 +550,10 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
updatePosterArt(null);
if (blockedRating == null) {
- program.loadPosterArt(context, mImageWidth, mImageHeight,
+ program.loadPosterArt(
+ context,
+ mImageWidth,
+ mImageHeight,
createProgramPosterArtCallback(this, program));
}
@@ -550,24 +565,35 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
String fullTitle = title + " " + episodeTitle;
SpannableString text = new SpannableString(fullTitle);
- text.setSpan(mEpisodeTitleStyle,
- fullTitle.length() - episodeTitle.length(), fullTitle.length(),
+ text.setSpan(
+ mEpisodeTitleStyle,
+ fullTitle.length() - episodeTitle.length(),
+ fullTitle.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
mTitleView.setText(text);
}
- updateTextView(mTimeView, Utils.getDurationString(context,
- program.getStartTimeUtcMillis(),
- program.getEndTimeUtcMillis(), false));
-
- boolean trackMetaDataVisible = updateTextView(mAspectRatioView, Utils
- .getAspectRatioString(program.getVideoWidth(), program.getVideoHeight()));
-
- int videoDefinitionLevel = Utils.getVideoDefinitionLevelFromSize(
- program.getVideoWidth(), program.getVideoHeight());
+ updateTextView(
+ mTimeView,
+ Utils.getDurationString(
+ context,
+ program.getStartTimeUtcMillis(),
+ program.getEndTimeUtcMillis(),
+ false));
+
+ boolean trackMetaDataVisible =
+ updateTextView(
+ mAspectRatioView,
+ Utils.getAspectRatioString(
+ program.getVideoWidth(), program.getVideoHeight()));
+
+ int videoDefinitionLevel =
+ Utils.getVideoDefinitionLevelFromSize(
+ program.getVideoWidth(), program.getVideoHeight());
trackMetaDataVisible |=
- updateTextView(mResolutionView, Utils.getVideoDefinitionLevelString(
- context, videoDefinitionLevel));
+ updateTextView(
+ mResolutionView,
+ Utils.getVideoDefinitionLevelString(context, videoDefinitionLevel));
if (mDvrManager != null && mDvrManager.isProgramRecordable(program)) {
ScheduledRecording scheduledRecording =
@@ -616,7 +642,6 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mDvrIndicator.setVisibility(View.GONE);
}
-
if (blockedRating == null) {
mBlockView.setVisibility(View.GONE);
updateTextView(mDescriptionView, program.getDescription());
@@ -655,8 +680,10 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
}
private String getBlockedDescription(TvContentRating blockedRating) {
- String name = mTvInputManagerHelper.getContentRatingsManager()
- .getDisplayNameForRating(blockedRating);
+ String name =
+ mTvInputManagerHelper
+ .getContentRatingsManager()
+ .getDisplayNameForRating(blockedRating);
if (TextUtils.isEmpty(name)) {
return mContext.getString(R.string.program_guide_content_locked);
} else {
@@ -691,8 +718,9 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mIsInputLogoVisible = true;
TvInputInfo info = mTvInputManagerHelper.getTvInputInfo(mChannel.getInputId());
if (info != null) {
- LoadTvInputLogoTask task = new LoadTvInputLogoTask(
- itemView.getContext(), ImageCache.getInstance(), info);
+ LoadTvInputLogoTask task =
+ new LoadTvInputLogoTask(
+ itemView.getContext(), ImageCache.getInstance(), info);
ImageLoader.loadBitmap(createTvInputLogoLoadedCallback(info, this), task);
}
}
@@ -735,23 +763,28 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
mInputLogoView.setVisibility(View.VISIBLE);
}
- private void updateCriticScoreView(ProgramRowViewHolder holder, final long programId,
- CriticScore criticScore, View view) {
+ private void updateCriticScoreView(
+ ProgramRowViewHolder holder,
+ final long programId,
+ CriticScore criticScore,
+ View view) {
TextView criticScoreSource = (TextView) view.findViewById(R.id.critic_score_source);
TextView criticScoreText = (TextView) view.findViewById(R.id.critic_score_score);
ImageView criticScoreLogo = (ImageView) view.findViewById(R.id.critic_score_logo);
- //set the appropriate information in the views
+ // set the appropriate information in the views
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
- criticScoreSource.setText(Html.fromHtml(criticScore.source,
- Html.FROM_HTML_MODE_LEGACY));
+ criticScoreSource.setText(
+ Html.fromHtml(criticScore.source, Html.FROM_HTML_MODE_LEGACY));
} else {
criticScoreSource.setText(Html.fromHtml(criticScore.source));
}
criticScoreText.setText(criticScore.score);
criticScoreSource.setVisibility(View.VISIBLE);
criticScoreText.setVisibility(View.VISIBLE);
- ImageLoader.loadBitmap(mContext, criticScore.logoUrl,
+ ImageLoader.loadBitmap(
+ mContext,
+ criticScore.logoUrl,
createCriticScoreLogoCallback(holder, programId, criticScoreLogo));
}
@@ -768,7 +801,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return new ImageLoaderCallback<ProgramRowViewHolder>(holder) {
@Override
public void onBitmapLoaded(ProgramRowViewHolder holder, @Nullable Bitmap logoImage) {
- if (logoImage == null || holder.mSelectedEntry == null
+ if (logoImage == null
+ || holder.mSelectedEntry == null
|| holder.mSelectedEntry.program == null
|| holder.mSelectedEntry.program.getId() != programId) {
logoView.setVisibility(View.GONE);
@@ -785,7 +819,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return new ImageLoaderCallback<ProgramRowViewHolder>(holder) {
@Override
public void onBitmapLoaded(ProgramRowViewHolder holder, @Nullable Bitmap posterArt) {
- if (posterArt == null || holder.mSelectedEntry == null
+ if (posterArt == null
+ || holder.mSelectedEntry == null
|| holder.mSelectedEntry.program == null) {
return;
}
@@ -803,7 +838,8 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return new ImageLoaderCallback<ProgramRowViewHolder>(holder) {
@Override
public void onBitmapLoaded(ProgramRowViewHolder holder, @Nullable Bitmap logo) {
- if (logo == null || holder.mChannel == null
+ if (logo == null
+ || holder.mChannel == null
|| holder.mChannel.getId() != channelId) {
return;
}
@@ -817,8 +853,9 @@ class ProgramTableAdapter extends RecyclerView.Adapter<ProgramTableAdapter.Progr
return new ImageLoaderCallback<ProgramRowViewHolder>(holder) {
@Override
public void onBitmapLoaded(ProgramRowViewHolder holder, @Nullable Bitmap logo) {
- if (logo != null && holder.mChannel != null && info.getId()
- .equals(holder.mChannel.getInputId())) {
+ if (logo != null
+ && holder.mChannel != null
+ && info.getId().equals(holder.mChannel.getInputId())) {
holder.updateInputLogoInternal(logo);
}
}
diff --git a/src/com/android/tv/guide/TimeListAdapter.java b/src/com/android/tv/guide/TimeListAdapter.java
index d9e96a40..9c10c952 100644
--- a/src/com/android/tv/guide/TimeListAdapter.java
+++ b/src/com/android/tv/guide/TimeListAdapter.java
@@ -16,7 +16,6 @@
package com.android.tv.guide;
-import android.content.Context;
import android.content.res.Resources;
import android.support.v7.widget.RecyclerView;
import android.text.format.DateFormat;
@@ -24,17 +23,15 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
-
import com.android.tv.R;
import com.android.tv.util.Utils;
-
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
/**
- * Adapts the time range from {@link ProgramManager} to the timeline header row of the program
- * guide table.
+ * Adapts the time range from {@link ProgramManager} to the timeline header row of the program guide
+ * table.
*/
class TimeListAdapter extends RecyclerView.Adapter<TimeListAdapter.TimeViewHolder> {
private static final long TIME_UNIT_MS = TimeUnit.MINUTES.toMillis(30);
@@ -53,8 +50,10 @@ class TimeListAdapter extends RecyclerView.Adapter<TimeListAdapter.TimeViewHolde
TimeListAdapter(Resources res) {
if (sRowHeaderOverlapping == 0) {
- sRowHeaderOverlapping = Math.abs(res.getDimensionPixelOffset(
- R.dimen.program_guide_table_header_row_overlap));
+ sRowHeaderOverlapping =
+ Math.abs(
+ res.getDimensionPixelOffset(
+ R.dimen.program_guide_table_header_row_overlap));
}
Locale locale = res.getConfiguration().locale;
mTimePatternSameDay = DateFormat.getBestDateTimePattern(locale, TIME_PATTERN_SAME_DAY);
diff --git a/src/com/android/tv/guide/TimelineGridView.java b/src/com/android/tv/guide/TimelineGridView.java
index 8af1c8a3..c4922b75 100644
--- a/src/com/android/tv/guide/TimelineGridView.java
+++ b/src/com/android/tv/guide/TimelineGridView.java
@@ -34,14 +34,15 @@ public class TimelineGridView extends RecyclerView {
public TimelineGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) {
- @Override
- public boolean onRequestChildFocus(RecyclerView parent, State state, View child,
- View focused) {
- // This disables the default scroll behavior for focus movement.
- return true;
- }
- });
+ setLayoutManager(
+ new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) {
+ @Override
+ public boolean onRequestChildFocus(
+ RecyclerView parent, State state, View child, View focused) {
+ // This disables the default scroll behavior for focus movement.
+ return true;
+ }
+ });
// RecyclerView is always focusable, however this is not desirable for us, so disable.
// See b/18863217 (ag/634046) for reasons to why RecyclerView is focusable.
diff --git a/src/com/android/tv/guide/TimelineRow.java b/src/com/android/tv/guide/TimelineRow.java
index 3f0c8678..b6a10ab1 100644
--- a/src/com/android/tv/guide/TimelineRow.java
+++ b/src/com/android/tv/guide/TimelineRow.java
@@ -40,19 +40,16 @@ public class TimelineRow extends TimelineGridView {
getLayoutManager().scrollToPosition(0);
}
- /**
- * Returns the current scroll position
- */
+ /** Returns the current scroll position */
public int getScrollOffset() {
return Math.abs(mScrollPosition);
}
- /**
- * Scrolls horizontally to the given position.
- */
+ /** Scrolls horizontally to the given position. */
public void scrollTo(int scrollOffset, boolean smoothScroll) {
- int dx = (scrollOffset - getScrollOffset())
- * (getLayoutDirection() == LAYOUT_DIRECTION_LTR ? 1 : -1);
+ int dx =
+ (scrollOffset - getScrollOffset())
+ * (getLayoutDirection() == LAYOUT_DIRECTION_LTR ? 1 : -1);
if (smoothScroll) {
smoothScrollBy(dx, 0);
} else {