Home | History | Annotate | Download | only in widget
      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 androidx.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          */
     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          */
     50         boolean onRequestFocusInDescendants(int direction,
     51                 Rect previouslyFocusedRect);
     52         /**
     53          * See {@link android.view.ViewGroup#requestChildFocus(
     54          * android.view.View, android.view.View)}.
     55          */
     56         void onRequestChildFocus(View child, View focused);
     57     }
     58 
     59     public BrowseFrameLayout(Context context) {
     60         this(context, null, 0);
     61     }
     62 
     63     public BrowseFrameLayout(Context context, AttributeSet attrs) {
     64         this(context, attrs, 0);
     65     }
     66 
     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      */
     78     public void setOnFocusSearchListener(OnFocusSearchListener listener) {
     79         mListener = listener;
     80     }
     81 
     82     /**
     83      * Returns the {@link OnFocusSearchListener}.
     84      */
     85     public OnFocusSearchListener getOnFocusSearchListener() {
     86         return mListener;
     87     }
     88 
     89     /**
     90      * Sets a {@link OnChildFocusListener}.
     91      */
     92     public void setOnChildFocusListener(OnChildFocusListener listener) {
     93         mOnChildFocusListener = listener;
     94     }
     95 
     96     /**
     97      * Returns the {@link OnChildFocusListener}.
     98      */
     99     public OnChildFocusListener getOnChildFocusListener() {
    100         return mOnChildFocusListener;
    101     }
    102 
    103     @Override
    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
    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
    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
    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      */
    151     public void setOnDispatchKeyListener(OnKeyListener listener) {
    152         this.mOnDispatchKeyListener = listener;
    153     }
    154 }
    155