1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 package android.support.v17.leanback.widget; 15 16 import android.content.Context; 17 import android.graphics.Rect; 18 import android.util.AttributeSet; 19 import android.view.KeyEvent; 20 import android.view.View; 21 import android.widget.FrameLayout; 22 23 /** 24 * A ViewGroup for managing focus behavior between overlapping views. 25 */ 26 public class BrowseFrameLayout extends FrameLayout { 27 28 /** 29 * Interface for selecting a focused view in a BrowseFrameLayout when the system focus finder 30 * couldn't find a view to focus. 31 */ 32 public interface OnFocusSearchListener { 33 /** 34 * Returns the view where focus should be requested given the current focused view and 35 * the direction of focus search. 36 */ onFocusSearch(View focused, int direction)37 View onFocusSearch(View focused, int direction); 38 } 39 40 /** 41 * Interface for managing child focus in a BrowseFrameLayout. 42 */ 43 public interface OnChildFocusListener { 44 /** 45 * See {@link android.view.ViewGroup#onRequestFocusInDescendants( 46 * int, android.graphics.Rect)}. 47 * @return True if handled by listener, otherwise returns {@link 48 * android.view.ViewGroup#onRequestFocusInDescendants(int, android.graphics.Rect)}. 49 */ onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)50 boolean onRequestFocusInDescendants(int direction, 51 Rect previouslyFocusedRect); 52 /** 53 * See {@link android.view.ViewGroup#requestChildFocus( 54 * android.view.View, android.view.View)}. 55 */ onRequestChildFocus(View child, View focused)56 void onRequestChildFocus(View child, View focused); 57 } 58 BrowseFrameLayout(Context context)59 public BrowseFrameLayout(Context context) { 60 this(context, null, 0); 61 } 62 BrowseFrameLayout(Context context, AttributeSet attrs)63 public BrowseFrameLayout(Context context, AttributeSet attrs) { 64 this(context, attrs, 0); 65 } 66 BrowseFrameLayout(Context context, AttributeSet attrs, int defStyle)67 public BrowseFrameLayout(Context context, AttributeSet attrs, int defStyle) { 68 super(context, attrs, defStyle); 69 } 70 71 private OnFocusSearchListener mListener; 72 private OnChildFocusListener mOnChildFocusListener; 73 private OnKeyListener mOnDispatchKeyListener; 74 75 /** 76 * Sets a {@link OnFocusSearchListener}. 77 */ setOnFocusSearchListener(OnFocusSearchListener listener)78 public void setOnFocusSearchListener(OnFocusSearchListener listener) { 79 mListener = listener; 80 } 81 82 /** 83 * Returns the {@link OnFocusSearchListener}. 84 */ getOnFocusSearchListener()85 public OnFocusSearchListener getOnFocusSearchListener() { 86 return mListener; 87 } 88 89 /** 90 * Sets a {@link OnChildFocusListener}. 91 */ setOnChildFocusListener(OnChildFocusListener listener)92 public void setOnChildFocusListener(OnChildFocusListener listener) { 93 mOnChildFocusListener = listener; 94 } 95 96 /** 97 * Returns the {@link OnChildFocusListener}. 98 */ getOnChildFocusListener()99 public OnChildFocusListener getOnChildFocusListener() { 100 return mOnChildFocusListener; 101 } 102 103 @Override onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect)104 protected boolean onRequestFocusInDescendants(int direction, 105 Rect previouslyFocusedRect) { 106 if (mOnChildFocusListener != null) { 107 if (mOnChildFocusListener.onRequestFocusInDescendants(direction, 108 previouslyFocusedRect)) { 109 return true; 110 } 111 } 112 return super.onRequestFocusInDescendants(direction, previouslyFocusedRect); 113 } 114 115 @Override focusSearch(View focused, int direction)116 public View focusSearch(View focused, int direction) { 117 if (mListener != null) { 118 View view = mListener.onFocusSearch(focused, direction); 119 if (view != null) { 120 return view; 121 } 122 } 123 return super.focusSearch(focused, direction); 124 } 125 126 @Override requestChildFocus(View child, View focused)127 public void requestChildFocus(View child, View focused) { 128 if (mOnChildFocusListener != null) { 129 mOnChildFocusListener.onRequestChildFocus(child, focused); 130 } 131 super.requestChildFocus(child, focused); 132 } 133 134 @Override dispatchKeyEvent(KeyEvent event)135 public boolean dispatchKeyEvent(KeyEvent event) { 136 boolean consumed = super.dispatchKeyEvent(event); 137 if (mOnDispatchKeyListener != null) { 138 if (!consumed) { 139 return mOnDispatchKeyListener.onKey(getRootView(), event.getKeyCode(), event); 140 } 141 } 142 return consumed; 143 } 144 145 /** 146 * Sets the {@link android.view.View.OnKeyListener} on this view. This listener would fire 147 * only for unhandled {@link KeyEvent}s. We need to provide an external key listener to handle 148 * back button clicks when we are in full screen video mode because 149 * {@link View#setOnKeyListener(OnKeyListener)} doesn't fire as the focus is not on this view. 150 */ setOnDispatchKeyListener(OnKeyListener listener)151 public void setOnDispatchKeyListener(OnKeyListener listener) { 152 this.mOnDispatchKeyListener = listener; 153 } 154 } 155