HeadersFragment.java revision 9a49e322d10c1a67860d812352a5ea710843b75e
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
15package android.support.v17.leanback.app;
16
17import android.content.Context;
18import android.graphics.Color;
19import android.graphics.drawable.ColorDrawable;
20import android.graphics.drawable.Drawable;
21import android.graphics.drawable.GradientDrawable;
22import android.os.Bundle;
23import android.support.v17.leanback.R;
24import android.support.v17.leanback.widget.FocusHighlightHelper;
25import android.support.v17.leanback.widget.ItemBridgeAdapter;
26import android.support.v17.leanback.widget.PresenterSelector;
27import android.support.v17.leanback.widget.OnItemViewSelectedListener;
28import android.support.v17.leanback.widget.Row;
29import android.support.v17.leanback.widget.RowHeaderPresenter;
30import android.support.v17.leanback.widget.SinglePresenterSelector;
31import android.support.v17.leanback.widget.VerticalGridView;
32import android.support.v7.widget.RecyclerView;
33import android.util.TypedValue;
34import android.view.View;
35import android.view.ViewGroup;
36import android.view.View.OnLayoutChangeListener;
37import android.widget.FrameLayout;
38
39/**
40 * An internal fragment containing a list of row headers.
41 */
42public class HeadersFragment extends BaseRowFragment {
43
44    interface OnHeaderClickedListener {
45        void onHeaderClicked();
46    }
47
48    interface OnHeaderViewSelectedListener {
49        void onHeaderSelected(RowHeaderPresenter.ViewHolder viewHolder, Row row);
50    }
51
52    private OnHeaderViewSelectedListener mOnHeaderViewSelectedListener;
53    private OnHeaderClickedListener mOnHeaderClickedListener;
54    private boolean mHeadersEnabled = true;
55    private boolean mHeadersGone = false;
56    private int mBackgroundColor;
57    private boolean mBackgroundColorSet;
58
59    private static final PresenterSelector sHeaderPresenter = new SinglePresenterSelector(
60            new RowHeaderPresenter(R.layout.lb_header));
61
62    public HeadersFragment() {
63        setPresenterSelector(sHeaderPresenter);
64    }
65
66    public void setOnHeaderClickedListener(OnHeaderClickedListener listener) {
67        mOnHeaderClickedListener = listener;
68    }
69
70    public void setOnHeaderViewSelectedListener(OnHeaderViewSelectedListener listener) {
71        mOnHeaderViewSelectedListener = listener;
72    }
73
74    @Override
75    VerticalGridView findGridViewFromRoot(View view) {
76        return (VerticalGridView) view.findViewById(R.id.browse_headers);
77    }
78
79    @Override
80    void onRowSelected(RecyclerView parent, RecyclerView.ViewHolder viewHolder,
81            int position, int subposition) {
82        if (mOnHeaderViewSelectedListener != null) {
83            if (viewHolder != null && position >= 0) {
84                Row row = (Row) getAdapter().get(position);
85                ItemBridgeAdapter.ViewHolder vh = (ItemBridgeAdapter.ViewHolder) viewHolder;
86                mOnHeaderViewSelectedListener.onHeaderSelected(
87                        (RowHeaderPresenter.ViewHolder) vh.getViewHolder(), row);
88            } else {
89                mOnHeaderViewSelectedListener.onHeaderSelected(null, null);
90            }
91        }
92    }
93
94    private final ItemBridgeAdapter.AdapterListener mAdapterListener =
95            new ItemBridgeAdapter.AdapterListener() {
96        @Override
97        public void onCreate(ItemBridgeAdapter.ViewHolder viewHolder) {
98            View headerView = viewHolder.getViewHolder().view;
99            headerView.setOnClickListener(new View.OnClickListener() {
100                @Override
101                public void onClick(View v) {
102                    if (mOnHeaderClickedListener != null) {
103                        mOnHeaderClickedListener.onHeaderClicked();
104                    }
105                }
106            });
107            headerView.setFocusable(true);
108            headerView.setFocusableInTouchMode(true);
109            if (mWrapper != null) {
110                viewHolder.itemView.addOnLayoutChangeListener(sLayoutChangeListener);
111            } else {
112                headerView.addOnLayoutChangeListener(sLayoutChangeListener);
113            }
114        }
115
116    };
117
118    private static OnLayoutChangeListener sLayoutChangeListener = new OnLayoutChangeListener() {
119        @Override
120        public void onLayoutChange(View v, int left, int top, int right, int bottom,
121            int oldLeft, int oldTop, int oldRight, int oldBottom) {
122            v.setPivotX(v.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? v.getWidth() : 0);
123            v.setPivotY(v.getMeasuredHeight() / 2);
124        }
125    };
126
127    @Override
128    int getLayoutResourceId() {
129        return R.layout.lb_headers_fragment;
130    }
131
132    @Override
133    public void onViewCreated(View view, Bundle savedInstanceState) {
134        super.onViewCreated(view, savedInstanceState);
135        final VerticalGridView listView = getVerticalGridView();
136        if (listView == null) {
137            return;
138        }
139        if (getBridgeAdapter() != null) {
140            FocusHighlightHelper.setupHeaderItemFocusHighlight(listView);
141        }
142        if (mBackgroundColorSet) {
143            listView.setBackgroundColor(mBackgroundColor);
144            updateFadingEdgeToBrandColor(mBackgroundColor);
145        } else {
146            Drawable d = listView.getBackground();
147            if (d instanceof ColorDrawable) {
148                updateFadingEdgeToBrandColor(((ColorDrawable) d).getColor());
149            }
150        }
151        updateListViewVisibility();
152    }
153
154    private void updateListViewVisibility() {
155        final VerticalGridView listView = getVerticalGridView();
156        if (listView != null) {
157            getView().setVisibility(mHeadersGone ? View.GONE : View.VISIBLE);
158            if (!mHeadersGone) {
159                if (mHeadersEnabled) {
160                    listView.setChildrenVisibility(View.VISIBLE);
161                } else {
162                    listView.setChildrenVisibility(View.INVISIBLE);
163                }
164            }
165        }
166    }
167
168    void setHeadersEnabled(boolean enabled) {
169        mHeadersEnabled = enabled;
170        updateListViewVisibility();
171    }
172
173    void setHeadersGone(boolean gone) {
174        mHeadersGone = gone;
175        updateListViewVisibility();
176    }
177
178    static class NoOverlappingFrameLayout extends FrameLayout {
179
180        public NoOverlappingFrameLayout(Context context) {
181            super(context);
182        }
183
184        /**
185         * Avoid creating hardware layer for header dock.
186         */
187        @Override
188        public boolean hasOverlappingRendering() {
189            return false;
190        }
191    }
192
193    // Wrapper needed because of conflict between RecyclerView's use of alpha
194    // for ADD animations, and RowHeaderPresnter's use of alpha for selected level.
195    private final ItemBridgeAdapter.Wrapper mWrapper = new ItemBridgeAdapter.Wrapper() {
196        @Override
197        public void wrap(View wrapper, View wrapped) {
198            ((FrameLayout) wrapper).addView(wrapped);
199        }
200
201        @Override
202        public View createWrapper(View root) {
203            return new NoOverlappingFrameLayout(root.getContext());
204        }
205    };
206    @Override
207    void updateAdapter() {
208        super.updateAdapter();
209        ItemBridgeAdapter adapter = getBridgeAdapter();
210        if (adapter != null) {
211            adapter.setAdapterListener(mAdapterListener);
212            adapter.setWrapper(mWrapper);
213        }
214        if (adapter != null && getVerticalGridView() != null) {
215            FocusHighlightHelper.setupHeaderItemFocusHighlight(getVerticalGridView());
216        }
217    }
218
219    void setBackgroundColor(int color) {
220        mBackgroundColor = color;
221        mBackgroundColorSet = true;
222
223        if (getVerticalGridView() != null) {
224            getVerticalGridView().setBackgroundColor(mBackgroundColor);
225            updateFadingEdgeToBrandColor(mBackgroundColor);
226        }
227    }
228
229    private void updateFadingEdgeToBrandColor(int backgroundColor) {
230        View fadingView = getView().findViewById(R.id.fade_out_edge);
231        Drawable background = fadingView.getBackground();
232        if (background instanceof GradientDrawable) {
233            background.mutate();
234            ((GradientDrawable) background).setColors(
235                    new int[] {Color.TRANSPARENT, backgroundColor});
236        }
237    }
238
239    @Override
240    void onTransitionStart() {
241        super.onTransitionStart();
242        if (!mHeadersEnabled) {
243            // When enabling headers fragment,  the RowHeaderView gets a focus but
244            // isShown() is still false because its parent is INVSIBILE, accessibility
245            // event is not sent.
246            // Workaround is: prevent focus to a child view during transition and put
247            // focus on it after transition is done.
248            final VerticalGridView listView = getVerticalGridView();
249            if (listView != null) {
250                listView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
251                if (listView.hasFocus()) {
252                    listView.requestFocus();
253                }
254            }
255        }
256    }
257
258    @Override
259    void onTransitionEnd() {
260        if (mHeadersEnabled) {
261            final VerticalGridView listView = getVerticalGridView();
262            if (listView != null) {
263                listView.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
264                if (listView.hasFocus()) {
265                    listView.requestFocus();
266                }
267            }
268        }
269        super.onTransitionEnd();
270    }
271}
272