aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tv/guide/GuideUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/tv/guide/GuideUtils.java')
-rw-r--r--src/com/android/tv/guide/GuideUtils.java110
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() { }
}