diff options
Diffstat (limited to 'src/com/android/tv/guide/ProgramGrid.java')
-rw-r--r-- | src/com/android/tv/guide/ProgramGrid.java | 270 |
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); - } - } - } } |