BrowseFragment.java revision 2f97594742886d045ca1ce409ebc6e6e780452f6
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 */
14package android.support.v17.leanback.app;
15
16import android.support.v17.leanback.R;
17import android.support.v17.leanback.widget.HorizontalGridView;
18import android.support.v17.leanback.widget.Presenter;
19import android.support.v17.leanback.widget.PresenterSelector;
20import android.support.v17.leanback.widget.VerticalGridView;
21import android.support.v17.leanback.widget.Row;
22import android.support.v17.leanback.widget.ObjectAdapter;
23import android.support.v17.leanback.widget.OnItemSelectedListener;
24import android.support.v17.leanback.widget.OnItemClickedListener;
25import android.support.v17.leanback.widget.SearchOrbView;
26import android.support.v7.widget.RecyclerView;
27import android.util.Log;
28import android.util.SparseIntArray;
29import android.app.Fragment;
30import android.content.res.TypedArray;
31import android.os.Bundle;
32import android.view.LayoutInflater;
33import android.view.View;
34import android.view.View.OnClickListener;
35import android.view.ViewGroup;
36import android.view.ViewGroup.MarginLayoutParams;
37import android.widget.ImageView;
38import android.widget.TextView;
39import android.graphics.drawable.Drawable;
40
41import java.util.ArrayList;
42
43import static android.support.v7.widget.RecyclerView.NO_POSITION;
44
45/**
46 * Wrapper fragment for leanback browse screens. Composed of a
47 * RowsFragment and a HeadersFragment.
48 *
49 */
50public class BrowseFragment extends Fragment {
51    private static final String TAG = "BrowseFragment";
52    private static boolean DEBUG = false;
53
54    /** The fastlane navigation panel is enabled and shown by default. */
55    public static final int HEADERS_ENABLED = 1;
56
57    /** The fastlane navigation panel is enabled and hidden by default. */
58    public static final int HEADERS_HIDDEN = 2;
59
60    /** The fastlane navigation panel is disabled and will never be shown. */
61    public static final int HEADERS_DISABLED = 3;
62
63    private static final float SLIDE_DISTANCE_FACTOR = 2;
64
65    private RowsFragment mRowsFragment;
66    private HeadersFragment mHeadersFragment;
67
68    private ObjectAdapter mAdapter;
69
70    private Params mParams;
71    private BrowseFrameLayout mBrowseFrame;
72    private ImageView mBadgeView;
73    private TextView mTitleView;
74    private ViewGroup mBrowseTitle;
75    private SearchOrbView mSearchOrbView;
76    private boolean mShowingTitle = true;
77    private boolean mShowingHeaders = true;
78    private boolean mCanShowHeaders = true;
79    private int mContainerListMarginLeft;
80    private int mContainerListAlignTop;
81    private OnItemSelectedListener mExternalOnItemSelectedListener;
82    private OnClickListener mExternalOnSearchClickedListener;
83    private OnItemClickedListener mOnItemClickedListener;
84    private int mSelectedPosition = -1;
85
86    private PresenterSelector mHeaderPresenterSelector;
87
88    // transition related:
89    private static TransitionHelper sTransitionHelper = TransitionHelper.getInstance();
90    private int mReparentHeaderId = View.generateViewId();
91    private Object mSceneWithTitle;
92    private Object mSceneWithoutTitle;
93    private Object mSceneWithHeaders;
94    private Object mSceneWithoutHeaders;
95    private Object mTitleTransition;
96    private Object mHeadersTransition;
97    private int mHeadersTransitionStartDelay;
98    private int mHeadersTransitionDuration;
99
100    private static final String ARG_TITLE = BrowseFragment.class.getCanonicalName() + ".title";
101    private static final String ARG_BADGE_URI = BrowseFragment.class.getCanonicalName() + ".badge";
102    private static final String ARG_HEADERS_STATE =
103        BrowseFragment.class.getCanonicalName() + ".headersState";
104
105    /**
106     * @param args Bundle to use for the arguments, if null a new Bundle will be created.
107     */
108    public static Bundle createArgs(Bundle args, String title, String badgeUri) {
109        return createArgs(args, title, badgeUri, HEADERS_ENABLED);
110    }
111
112    public static Bundle createArgs(Bundle args, String title, String badgeUri, int headersState) {
113        if (args == null) {
114            args = new Bundle();
115        }
116        args.putString(ARG_TITLE, title);
117        args.putString(ARG_BADGE_URI, badgeUri);
118        args.putInt(ARG_HEADERS_STATE, headersState);
119        return args;
120    }
121
122    public static class Params {
123        private String mTitle;
124        private Drawable mBadgeDrawable;
125        private int mHeadersState;
126
127        /**
128         * Sets the badge image.
129         */
130        public void setBadgeImage(Drawable drawable) {
131            mBadgeDrawable = drawable;
132        }
133
134        /**
135         * Returns the badge image.
136         */
137        public Drawable getBadgeImage() {
138            return mBadgeDrawable;
139        }
140
141        /**
142         * Sets a title for the browse fragment.
143         */
144        public void setTitle(String title) {
145            mTitle = title;
146        }
147
148        /**
149         * Returns the title for the browse fragment.
150         */
151        public String getTitle() {
152            return mTitle;
153        }
154
155        /**
156         * Sets the state for the headers column in the browse fragment.
157         */
158        public void setHeadersState(int headersState) {
159            if (headersState < HEADERS_ENABLED || headersState > HEADERS_DISABLED) {
160                Log.e(TAG, "Invalid headers state: " + headersState
161                        + ", default to enabled and shown.");
162                mHeadersState = HEADERS_ENABLED;
163            } else {
164                mHeadersState = headersState;
165            }
166        }
167
168        /**
169         * Returns the state for the headers column in the browse fragment.
170         */
171        public int getHeadersState() {
172            return mHeadersState;
173        }
174    }
175
176    /**
177     * Set browse parameters.
178     */
179    public void setBrowseParams(Params params) {
180        mParams = params;
181        setBadgeDrawable(mParams.mBadgeDrawable);
182        setTitle(mParams.mTitle);
183        setHeadersState(mParams.mHeadersState);
184    }
185
186    /**
187     * Returns browse parameters.
188     */
189    public Params getBrowseParams() {
190        return mParams;
191    }
192
193    /**
194     * Sets the list of rows for the fragment.
195     */
196    public void setAdapter(ObjectAdapter adapter) {
197        mAdapter = adapter;
198        if (mRowsFragment != null) {
199            mRowsFragment.setAdapter(adapter);
200            mHeadersFragment.setAdapter(adapter);
201        }
202    }
203
204    /**
205     * Returns the list of rows.
206     */
207    public ObjectAdapter getAdapter() {
208        return mAdapter;
209    }
210
211    /**
212     * Sets an item selection listener.
213     */
214    public void setOnItemSelectedListener(OnItemSelectedListener listener) {
215        mExternalOnItemSelectedListener = listener;
216    }
217
218    /**
219     * Sets an item clicked listener on the fragment.
220     * OnItemClickedListener will override {@link View.OnClickListener} that
221     * item presenter sets during {@link Presenter#onCreateViewHolder(ViewGroup)}.
222     * So in general,  developer should choose one of the listeners but not both.
223     */
224    public void setOnItemClickedListener(OnItemClickedListener listener) {
225        mOnItemClickedListener = listener;
226        if (mRowsFragment != null) {
227            mRowsFragment.setOnItemClickedListener(listener);
228        }
229    }
230
231    /**
232     * Returns the item Clicked listener.
233     */
234    public OnItemClickedListener getOnItemClickedListener() {
235        return mOnItemClickedListener;
236    }
237
238    /**
239     * Sets a click listener for the search affordance.
240     *
241     * The presence of a listener will change the visibility of the search affordance in the
242     * title area. When set to non-null the title area will contain a call to search action.
243     *
244     * The listener onClick method will be invoked when the user click on the search action.
245     *
246     * @param listener The listener.
247     */
248    public void setOnSearchClickedListener(View.OnClickListener listener) {
249        mExternalOnSearchClickedListener = listener;
250        if (mSearchOrbView != null) {
251            mSearchOrbView.setOnOrbClickedListener(listener);
252        }
253    }
254
255    private void onHeadersTransitionStart(boolean withHeaders) {
256        mRowsFragment.onTransitionStart();
257        mHeadersFragment.onTransitionStart();
258        createHeadersTransition(withHeaders);
259    }
260
261    private boolean isVerticalScrolling() {
262        // don't run transition
263        return mHeadersFragment.getVerticalGridView().getScrollState()
264                != HorizontalGridView.SCROLL_STATE_IDLE
265                || mRowsFragment.getVerticalGridView().getScrollState()
266                != HorizontalGridView.SCROLL_STATE_IDLE;
267    }
268
269    private final BrowseFrameLayout.OnFocusSearchListener mOnFocusSearchListener =
270            new BrowseFrameLayout.OnFocusSearchListener() {
271        @Override
272        public View onFocusSearch(View focused, int direction) {
273            // If fastlane is disabled, just return null.
274            if (!mCanShowHeaders) return null;
275
276            // if fast lane is running transition,  focus stays
277            if (mHeadersTransition != null) return focused;
278            if (DEBUG) Log.v(TAG, "onFocusSearch focused " + focused + " + direction " + direction);
279            if (direction == View.FOCUS_LEFT) {
280                if (isVerticalScrolling() || mShowingHeaders) {
281                    return focused;
282                }
283                return mHeadersFragment.getVerticalGridView();
284            } else if (direction == View.FOCUS_RIGHT) {
285                if (isVerticalScrolling() || !mShowingHeaders) {
286                    return focused;
287                }
288                return mRowsFragment.getVerticalGridView();
289            } else if (focused == mSearchOrbView && direction == View.FOCUS_DOWN) {
290                return mShowingHeaders ? mHeadersFragment.getVerticalGridView() :
291                    mRowsFragment.getVerticalGridView();
292
293            } else if (focused != mSearchOrbView && mSearchOrbView.getVisibility() == View.VISIBLE
294                    && direction == View.FOCUS_UP) {
295                return mSearchOrbView;
296
297            } else {
298                return null;
299            }
300        }
301    };
302
303    private final BrowseFrameLayout.OnChildFocusListener mOnChildFocusListener =
304            new BrowseFrameLayout.OnChildFocusListener() {
305        @Override
306        public void onRequestChildFocus(View child, View focused) {
307            int childId = child.getId();
308            if (mHeadersTransition != null) return;
309            if (childId == R.id.browse_container_dock && mShowingHeaders) {
310                mShowingHeaders = false;
311                onHeadersTransitionStart(false);
312                sTransitionHelper.runTransition(mSceneWithoutHeaders, mHeadersTransition);
313            } else if (childId == R.id.browse_headers_dock && !mShowingHeaders) {
314                mShowingHeaders = true;
315                //mHeadersFragment.getView().setAlpha(1f);
316                onHeadersTransitionStart(true);
317                sTransitionHelper.runTransition(mSceneWithHeaders, mHeadersTransition);
318            }
319        }
320    };
321
322    @Override
323    public void onCreate(Bundle savedInstanceState) {
324        super.onCreate(savedInstanceState);
325        TypedArray ta = getActivity().obtainStyledAttributes(R.styleable.LeanbackTheme);
326        mContainerListMarginLeft = (int) ta.getDimension(
327                R.styleable.LeanbackTheme_browseRowsMarginStart, 0);
328        mContainerListAlignTop = (int) ta.getDimension(
329                R.styleable.LeanbackTheme_browseRowsMarginTop, 0);
330        ta.recycle();
331        mHeadersTransitionStartDelay = getResources()
332                .getInteger(R.integer.lb_browse_headers_transition_delay);
333        mHeadersTransitionDuration = getResources()
334                .getInteger(R.integer.lb_browse_headers_transition_duration);
335    }
336
337    @Override
338    public View onCreateView(LayoutInflater inflater, ViewGroup container,
339            Bundle savedInstanceState) {
340        if (getChildFragmentManager().findFragmentById(R.id.browse_container_dock) == null) {
341            mRowsFragment = new RowsFragment();
342            mHeadersFragment = new HeadersFragment();
343            getChildFragmentManager().beginTransaction()
344                    .replace(R.id.browse_headers_dock, mHeadersFragment)
345                    .replace(R.id.browse_container_dock, mRowsFragment).commit();
346        } else {
347            mHeadersFragment = (HeadersFragment) getChildFragmentManager()
348                    .findFragmentById(R.id.browse_headers_dock);
349            mRowsFragment = (RowsFragment) getChildFragmentManager()
350                    .findFragmentById(R.id.browse_container_dock);
351        }
352        mRowsFragment.setReparentHeaderId(mReparentHeaderId);
353        mRowsFragment.setAdapter(mAdapter);
354        if (mHeaderPresenterSelector != null) {
355            mHeadersFragment.setPresenterSelector(mHeaderPresenterSelector);
356        }
357        mHeadersFragment.setReparentHeaderId(mReparentHeaderId);
358        mHeadersFragment.setAdapter(mAdapter);
359
360        mRowsFragment.setOnItemSelectedListener(mRowSelectedListener);
361        mHeadersFragment.setOnItemSelectedListener(mHeaderSelectedListener);
362        mHeadersFragment.setOnHeaderClickedListener(mHeaderClickedListener);
363        mRowsFragment.setOnItemClickedListener(mOnItemClickedListener);
364
365        View root = inflater.inflate(R.layout.lb_browse_fragment, container, false);
366
367        mBrowseFrame = (BrowseFrameLayout) root.findViewById(R.id.browse_frame);
368        mBrowseFrame.setOnFocusSearchListener(mOnFocusSearchListener);
369        mBrowseFrame.setOnChildFocusListener(mOnChildFocusListener);
370
371        mBrowseTitle = (ViewGroup) root.findViewById(R.id.browse_title_group);
372        mBadgeView = (ImageView) mBrowseTitle.findViewById(R.id.browse_badge);
373        mTitleView = (TextView) mBrowseTitle.findViewById(R.id.browse_title);
374        mSearchOrbView = (SearchOrbView) mBrowseTitle.findViewById(R.id.browse_orb);
375        if (mExternalOnSearchClickedListener != null) {
376            mSearchOrbView.setOnOrbClickedListener(mExternalOnSearchClickedListener);
377        }
378
379        readArguments(getArguments());
380        if (mParams != null) {
381            setBadgeDrawable(mParams.mBadgeDrawable);
382            setTitle(mParams.mTitle);
383            setHeadersState(mParams.mHeadersState);
384        }
385
386        mSceneWithTitle = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
387            @Override
388            public void run() {
389                showTitle(true);
390            }
391        });
392        mSceneWithoutTitle = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
393            @Override
394            public void run() {
395                showTitle(false);
396            }
397        });
398        mSceneWithHeaders = sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
399            @Override
400            public void run() {
401                showHeaders(true);
402            }
403        });
404        mSceneWithoutHeaders =  sTransitionHelper.createScene(mBrowseFrame, new Runnable() {
405            @Override
406            public void run() {
407                showHeaders(false);
408            }
409        });
410        mTitleTransition = sTransitionHelper.createAutoTransition();
411        sTransitionHelper.excludeChildren(mTitleTransition, R.id.browse_headers, true);
412        sTransitionHelper.excludeChildren(mTitleTransition, R.id.container_list, true);
413
414        return root;
415    }
416
417    private void createHeadersTransition(boolean withHeaders) {
418        mHeadersTransition = sTransitionHelper.createTransitionSet(false);
419        sTransitionHelper.excludeChildren(mHeadersTransition, R.id.browse_title_group, true);
420        Object changeBounds = sTransitionHelper.createChangeBounds(true);
421        Object fadeIn = sTransitionHelper.createFadeTransition(TransitionHelper.FADE_IN);
422        Object fadeOut = sTransitionHelper.createFadeTransition(TransitionHelper.FADE_OUT);
423
424        sTransitionHelper.exclude(fadeIn, mReparentHeaderId, true);
425        sTransitionHelper.exclude(fadeOut, mReparentHeaderId, true);
426        if (!withHeaders) {
427            sTransitionHelper.setChangeBoundsDefaultStartDelay(changeBounds,
428                    mHeadersTransitionStartDelay);
429        }
430        sTransitionHelper.setChangeBoundsStartDelay(changeBounds, mReparentHeaderId,
431                withHeaders ? mHeadersTransitionStartDelay : 0);
432
433        final int selectedPosition = mSelectedPosition;
434        Object slide = sTransitionHelper.createSlide(new SlideCallback() {
435            @Override
436            public boolean getSlide(View view, boolean appear, int[] edge, float[] distance) {
437                // we only care about the view with specific transition position Tag.
438                Integer position = (Integer) view.getTag(R.id.lb_header_transition_position);
439                if (position == null) {
440                    return false;
441                }
442                distance[0] = view.getHeight() * SLIDE_DISTANCE_FACTOR;
443                if (position < selectedPosition) {
444                    edge[0] = TransitionHelper.SLIDE_TOP;
445                    return true;
446                } else if (position > selectedPosition) {
447                    edge[0] = TransitionHelper.SLIDE_BOTTOM;
448                    return true;
449                }
450                return false;
451            }
452        });
453        sTransitionHelper.exclude(slide, mReparentHeaderId, true);
454        sTransitionHelper.setDuration(slide, mHeadersTransitionDuration);
455        if (withHeaders) {
456            sTransitionHelper.setStartDelay(slide, mHeadersTransitionStartDelay);
457        }
458        sTransitionHelper.addTransition(mHeadersTransition, slide);
459
460        sTransitionHelper.setDuration(fadeOut, mHeadersTransitionDuration);
461        sTransitionHelper.addTransition(mHeadersTransition, fadeOut);
462        sTransitionHelper.setDuration(changeBounds, mHeadersTransitionDuration);
463        sTransitionHelper.addTransition(mHeadersTransition, changeBounds);
464        sTransitionHelper.setDuration(fadeIn, mHeadersTransitionDuration);
465        sTransitionHelper.setStartDelay(fadeIn, mHeadersTransitionStartDelay);
466        sTransitionHelper.addTransition(mHeadersTransition, fadeIn);
467
468        sTransitionHelper.setTransitionListener(mHeadersTransition, new TransitionListener() {
469            @Override
470            public void onTransitionStart(Object transition) {
471            }
472            @Override
473            public void onTransitionEnd(Object transition) {
474                mHeadersTransition = null;
475                mRowsFragment.onTransitionEnd();
476                mHeadersFragment.onTransitionEnd();
477                if (mShowingHeaders) {
478                    VerticalGridView headerGridView = mHeadersFragment.getVerticalGridView();
479                    if (headerGridView != null && !headerGridView.hasFocus()) {
480                        headerGridView.requestFocus();
481                    }
482                } else {
483                    VerticalGridView rowsGridView = mRowsFragment.getVerticalGridView();
484                    if (rowsGridView != null && !rowsGridView.hasFocus()) {
485                        rowsGridView.requestFocus();
486                    }
487                }
488            }
489        });
490    }
491
492    public void setHeaderPresenterSelector(PresenterSelector headerPresenterSelector) {
493        mHeaderPresenterSelector = headerPresenterSelector;
494        if (mHeadersFragment != null) {
495            mHeadersFragment.setPresenterSelector(mHeaderPresenterSelector);
496        }
497    }
498
499    private void showTitle(boolean show) {
500        mBrowseTitle.setVisibility(show ? View.VISIBLE : View.GONE);
501    }
502
503    private void showHeaders(boolean show) {
504        if (DEBUG) Log.v(TAG, "showHeaders " + show);
505        mHeadersFragment.setHeadersVisiblity(show);
506
507        View containerList = mRowsFragment.getView();
508        MarginLayoutParams lp;
509        lp = (MarginLayoutParams) containerList.getLayoutParams();
510        lp.leftMargin = show ? mContainerListMarginLeft : 0;
511        containerList.setLayoutParams(lp);
512        mRowsFragment.setExpand(!show);
513    }
514
515    private HeadersFragment.OnHeaderClickedListener mHeaderClickedListener =
516        new HeadersFragment.OnHeaderClickedListener() {
517            @Override
518            public void onHeaderClicked() {
519                if (!mCanShowHeaders || !mShowingHeaders) return;
520
521                if (mHeadersTransition != null) {
522                    return;
523                }
524                mShowingHeaders = false;
525                onHeadersTransitionStart(false);
526                sTransitionHelper.runTransition(mSceneWithoutHeaders, mHeadersTransition);
527                mRowsFragment.getVerticalGridView().requestFocus();
528            }
529        };
530
531    private OnItemSelectedListener mRowSelectedListener = new OnItemSelectedListener() {
532        @Override
533        public void onItemSelected(Object item, Row row) {
534            int position = mRowsFragment.getVerticalGridView().getSelectedPosition();
535            if (DEBUG) Log.v(TAG, "row selected position " + position);
536            onRowSelected(position);
537            if (mExternalOnItemSelectedListener != null) {
538                mExternalOnItemSelectedListener.onItemSelected(item, row);
539            }
540        }
541    };
542
543    private OnItemSelectedListener mHeaderSelectedListener = new OnItemSelectedListener() {
544        @Override
545        public void onItemSelected(Object item, Row row) {
546            int position = mHeadersFragment.getVerticalGridView().getSelectedPosition();
547            if (DEBUG) Log.v(TAG, "header selected position " + position);
548            onRowSelected(position);
549        }
550    };
551
552    private void onRowSelected(int position) {
553        if (position != mSelectedPosition) {
554            mSetSelectionRunnable.mPosition = position;
555            mBrowseFrame.getHandler().post(mSetSelectionRunnable);
556
557            if (position == 0) {
558                if (!mShowingTitle) {
559                    sTransitionHelper.runTransition(mSceneWithTitle, mTitleTransition);
560                    mShowingTitle = true;
561                }
562            } else if (mShowingTitle) {
563                sTransitionHelper.runTransition(mSceneWithoutTitle, mTitleTransition);
564                mShowingTitle = false;
565            }
566        }
567    }
568
569    private class SetSelectionRunnable implements Runnable {
570        int mPosition;
571        @Override
572        public void run() {
573            setSelection(mPosition);
574        }
575    }
576
577    private final SetSelectionRunnable mSetSelectionRunnable = new SetSelectionRunnable();
578
579    private void setSelection(int position) {
580        if (position != NO_POSITION) {
581            mRowsFragment.setSelectedPosition(position);
582            mHeadersFragment.setSelectedPosition(position);
583        }
584        mSelectedPosition = position;
585    }
586
587    private void setVerticalVerticalGridViewLayout(VerticalGridView listview, int extraOffset) {
588        // align the top edge of item to a fixed position
589        listview.setItemAlignmentOffset(0);
590        listview.setItemAlignmentOffsetPercent(VerticalGridView.ITEM_ALIGN_OFFSET_PERCENT_DISABLED);
591        listview.setWindowAlignmentOffset(mContainerListAlignTop + extraOffset);
592        listview.setWindowAlignmentOffsetPercent(VerticalGridView.WINDOW_ALIGN_OFFSET_PERCENT_DISABLED);
593        listview.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_NO_EDGE);
594    }
595
596    /**
597     * Setup dimensions that are only meaningful when the child Fragments are inside
598     * BrowseFragment.
599     */
600    private void setupChildFragmentsLayout() {
601        VerticalGridView headerList = mHeadersFragment.getVerticalGridView();
602        VerticalGridView containerList = mRowsFragment.getVerticalGridView();
603
604        // Both fragments list view has the same alignment
605        setVerticalVerticalGridViewLayout(headerList, 16);
606        setVerticalVerticalGridViewLayout(containerList, 0);
607    }
608
609    @Override
610    public void onStart() {
611        super.onStart();
612        setupChildFragmentsLayout();
613        if (mCanShowHeaders && mShowingHeaders && mHeadersFragment.getView() != null) {
614            mHeadersFragment.getView().requestFocus();
615        } else if ((!mCanShowHeaders || !mShowingHeaders)
616                && mRowsFragment.getView() != null) {
617            mRowsFragment.getView().requestFocus();
618        }
619        showHeaders(mCanShowHeaders && mShowingHeaders);
620    }
621
622    private void readArguments(Bundle args) {
623        if (args == null) {
624            return;
625        }
626        if (args.containsKey(ARG_TITLE)) {
627            setTitle(args.getString(ARG_TITLE));
628        }
629
630        if (args.containsKey(ARG_BADGE_URI)) {
631            setBadgeUri(args.getString(ARG_BADGE_URI));
632        }
633
634        if (args.containsKey(ARG_HEADERS_STATE)) {
635            setHeadersState(args.getInt(ARG_HEADERS_STATE));
636        }
637    }
638
639    private void setBadgeUri(String badgeUri) {
640        // TODO - need a drawable downloader
641    }
642
643    private void setBadgeDrawable(Drawable drawable) {
644        if (mBadgeView == null) {
645            return;
646        }
647        mBadgeView.setImageDrawable(drawable);
648        if (drawable != null) {
649            mBadgeView.setVisibility(View.VISIBLE);
650        } else {
651            mBadgeView.setVisibility(View.GONE);
652        }
653    }
654
655    private void setTitle(String title) {
656        if (mTitleView != null) {
657            mTitleView.setText(title);
658        }
659    }
660
661    private void setHeadersState(int headersState) {
662        if (DEBUG) Log.v(TAG, "setHeadersState " + headersState);
663        switch (headersState) {
664            case HEADERS_ENABLED:
665                mCanShowHeaders = true;
666                mShowingHeaders = true;
667                break;
668            case HEADERS_HIDDEN:
669                mCanShowHeaders = true;
670                mShowingHeaders = false;
671                break;
672            case HEADERS_DISABLED:
673                mCanShowHeaders = false;
674                mShowingHeaders = false;
675                break;
676            default:
677                Log.w(TAG, "Unknown headers state: " + headersState);
678                break;
679        }
680    }
681}
682