Home | History | Annotate | Download | only in app
      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.app;
     15 
     16 import android.graphics.drawable.Drawable;
     17 import android.os.Bundle;
     18 import android.util.TypedValue;
     19 import android.view.LayoutInflater;
     20 import android.view.View;
     21 import android.view.ViewGroup;
     22 
     23 import androidx.annotation.NonNull;
     24 import androidx.annotation.Nullable;
     25 import androidx.fragment.app.Fragment;
     26 import androidx.leanback.R;
     27 import androidx.leanback.widget.SearchOrbView;
     28 import androidx.leanback.widget.TitleHelper;
     29 import androidx.leanback.widget.TitleViewAdapter;
     30 
     31 /**
     32  * Fragment class for managing search and branding using a view that implements
     33  * {@link TitleViewAdapter.Provider}.
     34  */
     35 public class BrandedSupportFragment extends Fragment {
     36 
     37     // BUNDLE attribute for title is showing
     38     private static final String TITLE_SHOW = "titleShow";
     39 
     40     private boolean mShowingTitle = true;
     41     private CharSequence mTitle;
     42     private Drawable mBadgeDrawable;
     43     private View mTitleView;
     44     private TitleViewAdapter mTitleViewAdapter;
     45     private SearchOrbView.Colors mSearchAffordanceColors;
     46     private boolean mSearchAffordanceColorSet;
     47     private View.OnClickListener mExternalOnSearchClickedListener;
     48     private TitleHelper mTitleHelper;
     49 
     50     /**
     51      * Called by {@link #installTitleView(LayoutInflater, ViewGroup, Bundle)} to inflate
     52      * title view.  Default implementation uses layout file lb_browse_title.
     53      * Subclass may override and use its own layout, the layout must have a descendant with id
     54      * browse_title_group that implements {@link TitleViewAdapter.Provider}. Subclass may return
     55      * null if no title is needed.
     56      *
     57      * @param inflater           The LayoutInflater object that can be used to inflate
     58      *                           any views in the fragment,
     59      * @param parent             Parent of title view.
     60      * @param savedInstanceState If non-null, this fragment is being re-constructed
     61      *                           from a previous saved state as given here.
     62      * @return Title view which must have a descendant with id browse_title_group that implements
     63      *         {@link TitleViewAdapter.Provider}, or null for no title view.
     64      */
     65     public View onInflateTitleView(LayoutInflater inflater, ViewGroup parent,
     66                                 Bundle savedInstanceState) {
     67         TypedValue typedValue = new TypedValue();
     68         boolean found = parent.getContext().getTheme().resolveAttribute(
     69                 R.attr.browseTitleViewLayout, typedValue, true);
     70         return inflater.inflate(found ? typedValue.resourceId : R.layout.lb_browse_title,
     71                 parent, false);
     72     }
     73 
     74     /**
     75      * Inflate title view and add to parent.  This method should be called in
     76      * {@link Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle)}.
     77      * @param inflater The LayoutInflater object that can be used to inflate
     78      * any views in the fragment,
     79      * @param parent Parent of title view.
     80      * @param savedInstanceState If non-null, this fragment is being re-constructed
     81      * from a previous saved state as given here.
     82      */
     83     public void installTitleView(LayoutInflater inflater, ViewGroup parent,
     84                             Bundle savedInstanceState) {
     85         View titleLayoutRoot = onInflateTitleView(inflater, parent, savedInstanceState);
     86         if (titleLayoutRoot != null) {
     87             parent.addView(titleLayoutRoot);
     88             setTitleView(titleLayoutRoot.findViewById(R.id.browse_title_group));
     89         } else {
     90             setTitleView(null);
     91         }
     92     }
     93 
     94     /**
     95      * Sets the view that implemented {@link TitleViewAdapter}.
     96      * @param titleView The view that implemented {@link TitleViewAdapter.Provider}.
     97      */
     98     public void setTitleView(View titleView) {
     99         mTitleView = titleView;
    100         if (mTitleView == null) {
    101             mTitleViewAdapter = null;
    102             mTitleHelper = null;
    103         } else {
    104             mTitleViewAdapter = ((TitleViewAdapter.Provider) mTitleView).getTitleViewAdapter();
    105             mTitleViewAdapter.setTitle(mTitle);
    106             mTitleViewAdapter.setBadgeDrawable(mBadgeDrawable);
    107             if (mSearchAffordanceColorSet) {
    108                 mTitleViewAdapter.setSearchAffordanceColors(mSearchAffordanceColors);
    109             }
    110             if (mExternalOnSearchClickedListener != null) {
    111                 setOnSearchClickedListener(mExternalOnSearchClickedListener);
    112             }
    113             if (getView() instanceof ViewGroup) {
    114                 mTitleHelper = new TitleHelper((ViewGroup) getView(), mTitleView);
    115             }
    116         }
    117     }
    118 
    119     /**
    120      * Returns the view that implements {@link TitleViewAdapter.Provider}.
    121      * @return The view that implements {@link TitleViewAdapter.Provider}.
    122      */
    123     public View getTitleView() {
    124         return mTitleView;
    125     }
    126 
    127     /**
    128      * Returns the {@link TitleViewAdapter} implemented by title view.
    129      * @return The {@link TitleViewAdapter} implemented by title view.
    130      */
    131     public TitleViewAdapter getTitleViewAdapter() {
    132         return mTitleViewAdapter;
    133     }
    134 
    135     /**
    136      * Returns the {@link TitleHelper}.
    137      */
    138     TitleHelper getTitleHelper() {
    139         return mTitleHelper;
    140     }
    141 
    142     @Override
    143     public void onSaveInstanceState(Bundle outState) {
    144         super.onSaveInstanceState(outState);
    145         outState.putBoolean(TITLE_SHOW, mShowingTitle);
    146     }
    147 
    148     @Override
    149     public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    150         super.onViewCreated(view, savedInstanceState);
    151         if (savedInstanceState != null) {
    152             mShowingTitle = savedInstanceState.getBoolean(TITLE_SHOW);
    153         }
    154         if (mTitleView != null && view instanceof ViewGroup) {
    155             mTitleHelper = new TitleHelper((ViewGroup) view, mTitleView);
    156             mTitleHelper.showTitle(mShowingTitle);
    157         }
    158     }
    159 
    160     @Override
    161     public void onDestroyView() {
    162         super.onDestroyView();
    163         mTitleHelper = null;
    164     }
    165 
    166     /**
    167      * Shows or hides the title view.
    168      * @param show True to show title view, false to hide title view.
    169      */
    170     public void showTitle(boolean show) {
    171         // TODO: handle interruptions?
    172         if (show == mShowingTitle) {
    173             return;
    174         }
    175         mShowingTitle = show;
    176         if (mTitleHelper != null) {
    177             mTitleHelper.showTitle(show);
    178         }
    179     }
    180 
    181     /**
    182      * Changes title view's components visibility and shows title.
    183      * @param flags Flags representing the visibility of components inside title view.
    184      * @see TitleViewAdapter#SEARCH_VIEW_VISIBLE
    185      * @see TitleViewAdapter#BRANDING_VIEW_VISIBLE
    186      * @see TitleViewAdapter#FULL_VIEW_VISIBLE
    187      * @see TitleViewAdapter#updateComponentsVisibility(int)
    188      */
    189     public void showTitle(int flags) {
    190         if (mTitleViewAdapter != null) {
    191             mTitleViewAdapter.updateComponentsVisibility(flags);
    192         }
    193         showTitle(true);
    194     }
    195 
    196     /**
    197      * Sets the drawable displayed in the fragment title.
    198      *
    199      * @param drawable The Drawable to display in the fragment title.
    200      */
    201     public void setBadgeDrawable(Drawable drawable) {
    202         if (mBadgeDrawable != drawable) {
    203             mBadgeDrawable = drawable;
    204             if (mTitleViewAdapter != null) {
    205                 mTitleViewAdapter.setBadgeDrawable(drawable);
    206             }
    207         }
    208     }
    209 
    210     /**
    211      * Returns the badge drawable used in the fragment title.
    212      * @return The badge drawable used in the fragment title.
    213      */
    214     public Drawable getBadgeDrawable() {
    215         return mBadgeDrawable;
    216     }
    217 
    218     /**
    219      * Sets title text for the fragment.
    220      *
    221      * @param title The title text of the fragment.
    222      */
    223     public void setTitle(CharSequence title) {
    224         mTitle = title;
    225         if (mTitleViewAdapter != null) {
    226             mTitleViewAdapter.setTitle(title);
    227         }
    228     }
    229 
    230     /**
    231      * Returns the title text for the fragment.
    232      * @return Title text for the fragment.
    233      */
    234     public CharSequence getTitle() {
    235         return mTitle;
    236     }
    237 
    238     /**
    239      * Sets a click listener for the search affordance.
    240      *
    241      * <p>The presence of a listener will change the visibility of the search
    242      * affordance in the fragment title. When set to non-null, the title will
    243      * contain an element that a user may click to begin a search.
    244      *
    245      * <p>The listener's {@link View.OnClickListener#onClick onClick} method
    246      * will be invoked when the user clicks on the search element.
    247      *
    248      * @param listener The listener to call when the search element is clicked.
    249      */
    250     public void setOnSearchClickedListener(View.OnClickListener listener) {
    251         mExternalOnSearchClickedListener = listener;
    252         if (mTitleViewAdapter != null) {
    253             mTitleViewAdapter.setOnSearchClickedListener(listener);
    254         }
    255     }
    256 
    257     /**
    258      * Sets the {@link androidx.leanback.widget.SearchOrbView.Colors} used to draw the
    259      * search affordance.
    260      *
    261      * @param colors Colors used to draw search affordance.
    262      */
    263     public void setSearchAffordanceColors(SearchOrbView.Colors colors) {
    264         mSearchAffordanceColors = colors;
    265         mSearchAffordanceColorSet = true;
    266         if (mTitleViewAdapter != null) {
    267             mTitleViewAdapter.setSearchAffordanceColors(mSearchAffordanceColors);
    268         }
    269     }
    270 
    271     /**
    272      * Returns the {@link androidx.leanback.widget.SearchOrbView.Colors}
    273      * used to draw the search affordance.
    274      */
    275     public SearchOrbView.Colors getSearchAffordanceColors() {
    276         if (mSearchAffordanceColorSet) {
    277             return mSearchAffordanceColors;
    278         }
    279         if (mTitleViewAdapter == null) {
    280             throw new IllegalStateException("Fragment views not yet created");
    281         }
    282         return mTitleViewAdapter.getSearchAffordanceColors();
    283     }
    284 
    285     /**
    286      * Sets the color used to draw the search affordance.
    287      * A default brighter color will be set by the framework.
    288      *
    289      * @param color The color to use for the search affordance.
    290      */
    291     public void setSearchAffordanceColor(int color) {
    292         setSearchAffordanceColors(new SearchOrbView.Colors(color));
    293     }
    294 
    295     /**
    296      * Returns the color used to draw the search affordance.
    297      */
    298     public int getSearchAffordanceColor() {
    299         return getSearchAffordanceColors().color;
    300     }
    301 
    302     @Override
    303     public void onStart() {
    304         super.onStart();
    305         if (mTitleViewAdapter != null) {
    306             showTitle(mShowingTitle);
    307             mTitleViewAdapter.setAnimationEnabled(true);
    308         }
    309     }
    310 
    311     @Override
    312     public void onPause() {
    313         if (mTitleViewAdapter != null) {
    314             mTitleViewAdapter.setAnimationEnabled(false);
    315         }
    316         super.onPause();
    317     }
    318 
    319     @Override
    320     public void onResume() {
    321         super.onResume();
    322         if (mTitleViewAdapter != null) {
    323             mTitleViewAdapter.setAnimationEnabled(true);
    324         }
    325     }
    326 
    327     /**
    328      * Returns true/false to indicate the visibility of TitleView.
    329      *
    330      * @return boolean to indicate whether or not it's showing the title.
    331      */
    332     public final boolean isShowingTitle() {
    333         return mShowingTitle;
    334     }
    335 
    336 }
    337