aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/guide/ProgramGrid.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/guide/ProgramGrid.java')
-rw-r--r--src/com/android/tv/guide/ProgramGrid.java270
1 files changed, 103 insertions, 167 deletions
diff --git a/src/com/android/tv/guide/ProgramGrid.java b/src/com/android/tv/guide/ProgramGrid.java
index 77de5827..58436425 100644
--- a/src/com/android/tv/guide/ProgramGrid.java
+++ b/src/com/android/tv/guide/ProgramGrid.java
@@ -20,17 +20,15 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
import android.support.v17.leanback.widget.VerticalGridView;
-import android.support.v7.widget.RecyclerView.LayoutManager;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Range;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import com.android.tv.R;
import com.android.tv.ui.OnRepeatedKeyInterceptListener;
-import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
/**
@@ -52,7 +50,7 @@ public class ProgramGrid extends VerticalGridView {
clearUpDownFocusState(newFocus);
}
mNextFocusByUpDown = null;
- if (newFocus != ProgramGrid.this && contains(newFocus)) {
+ if (GuideUtils.isDescendant(ProgramGrid.this, newFocus)) {
mLastFocusedView = newFocus;
}
}
@@ -90,8 +88,9 @@ public class ProgramGrid extends VerticalGridView {
private View mLastFocusedView;
private final Rect mTempRect = new Rect();
+ private int mLastUpDownDirection;
- private boolean mKeepCurrentProgram;
+ private boolean mKeepCurrentProgramFocused;
private ChildFocusListener mChildFocusListener;
private final OnRepeatedKeyInterceptListener mOnRepeatedKeyInterceptListener;
@@ -132,21 +131,6 @@ public class ProgramGrid extends VerticalGridView {
setOnKeyInterceptListener(mOnRepeatedKeyInterceptListener);
}
- /**
- * Initializes ProgramGrid. It should be called before the view is actually attached to
- * Window.
- */
- public void initialize(ProgramManager programManager) {
- mProgramManager = programManager;
- }
-
- /**
- * Registers a listener focus events occurring on children to the {@code ProgramGrid}.
- */
- public void setChildFocusListener(ChildFocusListener childFocusListener) {
- mChildFocusListener = childFocusListener;
- }
-
@Override
public void requestChildFocus(View child, View focused) {
if (mChildFocusListener != null) {
@@ -173,11 +157,11 @@ public class ProgramGrid extends VerticalGridView {
@Override
public View focusSearch(View focused, int direction) {
mNextFocusByUpDown = null;
- if (focused == null || !contains(focused)) {
+ if (focused == null || (focused != this && !GuideUtils.isDescendant(this, focused))) {
return super.focusSearch(focused, direction);
}
if (direction == View.FOCUS_UP || direction == View.FOCUS_DOWN) {
- updateUpDownFocusState(focused);
+ updateUpDownFocusState(focused, direction);
View nextFocus = focusFind(focused, direction);
if (nextFocus != null) {
return nextFocus;
@@ -186,15 +170,85 @@ public class ProgramGrid extends VerticalGridView {
return super.focusSearch(focused, direction);
}
+ @Override
+ public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
+ if (mLastFocusedView != null && mLastFocusedView.isShown()) {
+ if (mLastFocusedView.requestFocus()) {
+ return true;
+ }
+ }
+ return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
+ }
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ // It is required to properly handle OnRepeatedKeyInterceptListener. If the focused
+ // item's are at the almost end of screen, focus change to the next item doesn't work.
+ // It restricts that a focus item's position cannot be too far from the desired position.
+ View focusedView = findFocus();
+ if (focusedView != null && mOnRepeatedKeyInterceptListener.isFocusAccelerated()) {
+ int[] location = new int[2];
+ getLocationOnScreen(location);
+ int[] focusedLocation = new int[2];
+ focusedView.getLocationOnScreen(focusedLocation);
+ int y = focusedLocation[1] - location[1];
+ int minY = (mSelectionRow - 1) * mRowHeight;
+ if (y < minY) scrollBy(0, y - minY);
+ int maxY = (mSelectionRow + 1) * mRowHeight + mDetailHeight;
+ if (y > maxY) scrollBy(0, y - maxY);
+ }
+ updateInputLogo();
+ }
+
+ @Override
+ public void onViewRemoved(View view) {
+ // It is required to ensure input logo showing when the scroll is moved to most bottom.
+ updateInputLogo();
+ }
+
+ /**
+ * 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}.
+ */
+ void setChildFocusListener(ChildFocusListener childFocusListener) {
+ mChildFocusListener = childFocusListener;
+ }
+
+ void onItemSelectionReset() {
+ getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
+ }
+
/**
* Resets focus states. If the logic to keep the last focus needs to be cleared, it should
* be called.
*/
- public void resetFocusState() {
+ void resetFocusState() {
mLastFocusedView = null;
clearUpDownFocusState(null);
}
+ /** Returns the currently focused item's horizontal range. */
+ Range<Integer> getFocusRange() {
+ return new Range<>(mFocusRangeLeft, mFocusRangeRight);
+ }
+
+ /** Returns if the next focused item should be the current program if possible. */
+ boolean isKeepCurrentProgramFocused() {
+ return mKeepCurrentProgramFocused;
+ }
+
+ /** Returns the last up/down move direction of browsing */
+ int getLastUpDownDirection() {
+ return mLastUpDownDirection;
+ }
+
private View focusFind(View focused, int direction) {
int focusedChildIndex = getFocusedChildIndex();
if (focusedChildIndex == INVALID_INDEX) {
@@ -204,85 +258,26 @@ public class ProgramGrid extends VerticalGridView {
int nextChildIndex = direction == View.FOCUS_UP ? focusedChildIndex - 1
: focusedChildIndex + 1;
if (nextChildIndex < 0 || nextChildIndex >= getChildCount()) {
- return focused;
- }
- View nextChild = getChildAt(nextChildIndex);
- ArrayList<View> focusables = new ArrayList<>();
- findFocusables(nextChild, focusables);
-
- int index = INVALID_INDEX;
- if (mKeepCurrentProgram) {
- // Select the current program if possible.
- for (int i = 0; i < focusables.size(); ++i) {
- View focusable = focusables.get(i);
- if (!(focusable instanceof ProgramItemView)) {
- continue;
- }
- if (((ProgramItemView) focusable).getTableEntry().isCurrentProgram()) {
- index = i;
- break;
- }
- }
- if (index != INVALID_INDEX) {
- mNextFocusByUpDown = focusables.get(index);
- return mNextFocusByUpDown;
- } else {
- mKeepCurrentProgram = false;
- }
- }
-
- // Find the largest focusable among fully overlapped focusables.
- int maxWidth = Integer.MIN_VALUE;
- for (int i = 0; i < focusables.size(); ++i) {
- View focusable = focusables.get(i);
- Rect focusableRect = mTempRect;
- focusable.getGlobalVisibleRect(focusableRect);
- if (mFocusRangeLeft <= focusableRect.left && focusableRect.right <= mFocusRangeRight) {
- int width = focusableRect.width();
- if (width > maxWidth) {
- index = i;
- maxWidth = width;
- }
- } else if (focusableRect.left <= mFocusRangeLeft
- && mFocusRangeRight <= focusableRect.right) {
- // focusableRect contains [mLeft, mRight].
- index = i;
- break;
- }
- }
- if (index != INVALID_INDEX) {
- mNextFocusByUpDown = focusables.get(index);
- return mNextFocusByUpDown;
- }
-
- // Find the largest overlapped view among partially overlapped focusables.
- maxWidth = Integer.MIN_VALUE;
- for (int i = 0; i < focusables.size(); ++i) {
- View focusable = focusables.get(i);
- Rect focusableRect = mTempRect;
- focusable.getGlobalVisibleRect(focusableRect);
- if (mFocusRangeLeft <= focusableRect.left && focusableRect.left <= mFocusRangeRight) {
- int overlappedWidth = mFocusRangeRight - focusableRect.left;
- if (overlappedWidth > maxWidth) {
- index = i;
- maxWidth = overlappedWidth;
- }
- } else if (mFocusRangeLeft <= focusableRect.right
- && focusableRect.right <= mFocusRangeRight) {
- int overlappedWidth = focusableRect.right - mFocusRangeLeft;
- if (overlappedWidth > maxWidth) {
- index = i;
- maxWidth = overlappedWidth;
- }
+ // Wraparound if reached head or end
+ if (getSelectedPosition() == 0) {
+ scrollToPosition(getAdapter().getItemCount() - 1);
+ return null;
+ } else if (getSelectedPosition() == getAdapter().getItemCount() - 1) {
+ scrollToPosition(0);
+ return null;
}
+ return focused;
}
- if (index != INVALID_INDEX) {
- mNextFocusByUpDown = focusables.get(index);
- return mNextFocusByUpDown;
+ View nextFocusedProgram = GuideUtils.findNextFocusedProgram(getChildAt(nextChildIndex),
+ mFocusRangeLeft, mFocusRangeRight, mKeepCurrentProgramFocused);
+ if (nextFocusedProgram != null) {
+ nextFocusedProgram.getGlobalVisibleRect(mTempRect);
+ mNextFocusByUpDown = nextFocusedProgram;
+
+ } else {
+ Log.w(TAG, "focusFind doesn't find proper focusable");
}
-
- Log.w(TAG, "focusFind doesn't find proper focusable");
- return null;
+ return nextFocusedProgram;
}
// Returned value is not the position of VerticalGridView. But it's the index of ViewGroup
@@ -296,7 +291,8 @@ public class ProgramGrid extends VerticalGridView {
return INVALID_INDEX;
}
- private void updateUpDownFocusState(View focused) {
+ private void updateUpDownFocusState(View focused, int direction) {
+ mLastUpDownDirection = direction;
int rightMostFocusablePosition = getRightMostFocusablePosition();
Rect focusedRect = mTempRect;
@@ -319,11 +315,13 @@ public class ProgramGrid extends VerticalGridView {
}
private void clearUpDownFocusState(View focus) {
+ mLastUpDownDirection = 0;
mFocusRangeLeft = 0;
mFocusRangeRight = getRightMostFocusablePosition();
mNextFocusByUpDown = null;
- mKeepCurrentProgram = focus != null && focus instanceof ProgramItemView
- && ((ProgramItemView) focus).getTableEntry().isCurrentProgram();
+ // 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);
}
private int getRightMostFocusablePosition() {
@@ -333,56 +331,6 @@ public class ProgramGrid extends VerticalGridView {
return mTempRect.right - GuideUtils.convertMillisToPixel(FOCUS_AREA_RIGHT_MARGIN_MILLIS);
}
- private boolean contains(View v) {
- if (v == this) {
- return true;
- }
- if (v == null || v == v.getRootView()) {
- return false;
- }
- return contains((View) v.getParent());
- }
-
- public void onItemSelectionReset() {
- getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
- }
-
- @Override
- public boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
- if (mLastFocusedView != null && mLastFocusedView.isShown()) {
- if (mLastFocusedView.requestFocus()) {
- return true;
- }
- }
- return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
- }
-
- @Override
- protected void onScrollChanged(int l, int t, int oldl, int oldt) {
- // It is required to properly handle OnRepeatedKeyInterceptListener. If the focused
- // item's are at the almost end of screen, focus change to the next item doesn't work.
- // It restricts that a focus item's position cannot be too far from the desired position.
- View focusedView = findFocus();
- if (focusedView != null && mOnRepeatedKeyInterceptListener.isFocusAccelerated()) {
- int[] location = new int[2];
- getLocationOnScreen(location);
- int[] focusedLocation = new int[2];
- focusedView.getLocationOnScreen(focusedLocation);
- int y = focusedLocation[1] - location[1];
- int minY = (mSelectionRow - 1) * mRowHeight;
- if (y < minY) scrollBy(0, y - minY);
- int maxY = (mSelectionRow + 1) * mRowHeight + mDetailHeight;
- if (y > maxY) scrollBy(0, y - maxY);
- }
- updateInputLogo();
- }
-
- @Override
- public void onViewRemoved(View view) {
- // It is required to ensure input logo showing when the scroll is moved to most bottom.
- updateInputLogo();
- }
-
private int getFirstVisibleChildIndex() {
final LayoutManager mLayoutManager = getLayoutManager();
int top = mLayoutManager.getPaddingTop();
@@ -398,7 +346,7 @@ public class ProgramGrid extends VerticalGridView {
return -1;
}
- public void updateInputLogo() {
+ private void updateInputLogo() {
int childCount = getChildCount();
if (childCount == 0) {
return;
@@ -409,25 +357,13 @@ public class ProgramGrid extends VerticalGridView {
}
View childView = getChildAt(firstVisibleChildIndex);
int childAdapterPosition = getChildAdapterPosition(childView);
- ((ProgramTableAdapter.ProgramRowHolder) getChildViewHolder(childView))
+ ((ProgramTableAdapter.ProgramRowViewHolder) getChildViewHolder(childView))
.updateInputLogo(childAdapterPosition, true);
for (int i = firstVisibleChildIndex + 1; i < childCount; i++) {
childView = getChildAt(i);
- ((ProgramTableAdapter.ProgramRowHolder) getChildViewHolder(childView))
+ ((ProgramTableAdapter.ProgramRowViewHolder) getChildViewHolder(childView))
.updateInputLogo(childAdapterPosition, false);
childAdapterPosition = getChildAdapterPosition(childView);
}
}
-
- private static void findFocusables(View v, ArrayList<View> outFocusable) {
- if (v.isFocusable()) {
- outFocusable.add(v);
- }
- if (v instanceof ViewGroup) {
- ViewGroup viewGroup = (ViewGroup) v;
- for (int i = 0; i < viewGroup.getChildCount(); ++i) {
- findFocusables(viewGroup.getChildAt(i), outFocusable);
- }
- }
- }
}