diff options
Diffstat (limited to 'src/com/android/tv/guide/GuideUtils.java')
-rw-r--r-- | src/com/android/tv/guide/GuideUtils.java | 110 |
1 files changed, 105 insertions, 5 deletions
diff --git a/src/com/android/tv/guide/GuideUtils.java b/src/com/android/tv/guide/GuideUtils.java index 5d11f061..403d00b5 100644 --- a/src/com/android/tv/guide/GuideUtils.java +++ b/src/com/android/tv/guide/GuideUtils.java @@ -16,30 +16,38 @@ 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; -public class GuideUtils { +class GuideUtils { + private static final int INVALID_INDEX = -1; 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. */ - public static void setWidthPerHour(int widthPerHour) { + static void setWidthPerHour(int widthPerHour) { sWidthPerHour = widthPerHour; } /** * Gets the number of pixels in program guide table that corresponds to the given milliseconds. */ - public static int convertMillisToPixel(long millis) { + static int convertMillisToPixel(long millis) { return (int) (millis * sWidthPerHour / TimeUnit.HOURS.toMillis(1)); } /** * Gets the number of pixels in program guide table that corresponds to the given range. */ - public static int convertMillisToPixel(long startMillis, long endMillis) { + static int convertMillisToPixel(long startMillis, long endMillis) { // Convert to pixels first to avoid accumulation of rounding errors. return GuideUtils.convertMillisToPixel(endMillis) - GuideUtils.convertMillisToPixel(startMillis); @@ -48,9 +56,101 @@ public class GuideUtils { /** * Gets the time in millis that corresponds to the given pixels in the program guide. */ - public static long convertPixelToMillis(int pixel) { + 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. + */ + static View findNextFocusedProgram(View programRow, int focusRangeLeft, + int focusRangeRight, boolean keepCurrentProgramFocused) { + ArrayList<View> focusables = new ArrayList<>(); + findFocusables(programRow, focusables); + + if (keepCurrentProgramFocused) { + // Select the current program if possible. + for (int i = 0; i < focusables.size(); ++i) { + View focusable = focusables.get(i); + if (focusable instanceof ProgramItemView + && isCurrentProgram((ProgramItemView) focusable)) { + return focusable; + } + } + } + + // Find the largest focusable among fully overlapped focusables. + int maxFullyOverlappedWidth = Integer.MIN_VALUE; + int maxPartiallyOverlappedWidth = Integer.MIN_VALUE; + int nextFocusIndex = INVALID_INDEX; + for (int i = 0; i < focusables.size(); ++i) { + View focusable = focusables.get(i); + Rect focusableRect = new Rect(); + focusable.getGlobalVisibleRect(focusableRect); + if (focusableRect.left <= focusRangeLeft && focusRangeRight <= focusableRect.right) { + // the old focused range is fully inside the focusable, return directly. + return focusable; + } else if (focusRangeLeft <= focusableRect.left + && focusableRect.right <= focusRangeRight) { + // the focusable is fully inside the old focused range, choose the widest one. + int width = focusableRect.width(); + if (width > maxFullyOverlappedWidth) { + nextFocusIndex = i; + maxFullyOverlappedWidth = width; + } + } else if (maxFullyOverlappedWidth == Integer.MIN_VALUE) { + int overlappedWidth = (focusRangeLeft <= focusableRect.left) ? + focusRangeRight - focusableRect.left + : focusableRect.right - focusRangeLeft; + if (overlappedWidth > maxPartiallyOverlappedWidth) { + nextFocusIndex = i; + maxPartiallyOverlappedWidth = overlappedWidth; + } + } + } + if (nextFocusIndex != INVALID_INDEX) { + return focusables.get(nextFocusIndex); + } + return null; + } + + /** + * 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. + */ + static boolean isDescendant(ViewGroup container, View view) { + if (view == null) { + return false; + } + for (ViewParent p = view.getParent(); p != null; p = p.getParent()) { + if (p == container) { + return true; + } + } + return false; + } + + 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); + } + } + } + private GuideUtils() { } } |