ControlBarPresenter.java revision aa67105babce5fb14e1f39b57d4c84ce634afa62
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.widget;
15
16import android.content.Context;
17import android.support.v17.leanback.R;
18import android.util.SparseArray;
19import android.view.LayoutInflater;
20import android.view.View;
21import android.view.ViewGroup;
22import android.widget.LinearLayout;
23
24/**
25 * A presenter that assumes a LinearLayout container for a series
26 * of control buttons backed by objects of type {@link Action}.
27 *
28 * Different layouts may be passed to the presenter constructor.
29 * The layout must contain a view with id control_bar.
30 */
31class ControlBarPresenter extends Presenter {
32
33    private static final int MAX_CONTROLS = 7;
34
35    /**
36     * The data type expected by this presenter.
37     */
38    static class BoundData {
39        /**
40         * Adapter containing objects of type {@link Action}.
41         */
42        ObjectAdapter adapter;
43
44        /**
45         * The presenter to be used for the adapter objects.
46         */
47        Presenter presenter;
48    }
49
50    /**
51     * A ViewHolder for an actions bar.
52     */
53    class ViewHolder extends Presenter.ViewHolder {
54        ObjectAdapter mAdapter;
55        Presenter mPresenter;
56        ControlBar mControlBar;
57        SparseArray<Presenter.ViewHolder> mViewHolders =
58                new SparseArray<Presenter.ViewHolder>();
59        ObjectAdapter.DataObserver mDataObserver;
60
61        /**
62         * Constructor for the ViewHolder.
63         */
64        ViewHolder(View rootView) {
65            super(rootView);
66            mControlBar = (ControlBar) rootView.findViewById(R.id.control_bar);
67            if (mControlBar == null) {
68                throw new IllegalStateException("Couldn't find control_bar");
69            }
70            mControlBar.setOnChildFocusedListener(new ControlBar.OnChildFocusedListener() {
71                @Override
72                public void onChildFocusedListener(View child, View focused) {
73                    if (mOnItemViewSelectedListener == null) {
74                        return;
75                    }
76                    for (int position = 0; position < mViewHolders.size(); position++) {
77                        if (mViewHolders.get(position).view == child) {
78                            mOnItemViewSelectedListener.onItemSelected(
79                                    mViewHolders.get(position), getDisplayedAdapter().get(position),
80                                            null, null);
81                            break;
82                        }
83                    }
84                }
85            });
86            mDataObserver = new ObjectAdapter.DataObserver() {
87                @Override
88                public void onChanged() {
89                    if (mAdapter == getDisplayedAdapter()) {
90                        showControls(mPresenter);
91                    }
92                }
93                @Override
94                public void onItemRangeChanged(int positionStart, int itemCount) {
95                    if (mAdapter == getDisplayedAdapter()) {
96                        for (int i = 0; i < itemCount; i++) {
97                            bindControlToAction(positionStart + i, mPresenter);
98                        }
99                    }
100                }
101            };
102        }
103
104        int getChildMarginFromCenter(Context context, int numControls) {
105            // Includes margin between icons plus two times half the icon width.
106            return getChildMarginDefault(context) + getControlIconWidth(context);
107        }
108
109        void showControls(Presenter presenter) {
110            View focusedChild = mControlBar.getFocusedChild();
111            ObjectAdapter adapter = getDisplayedAdapter();
112            mControlBar.removeAllViews();
113            for (int position = 0; position < adapter.size() && position < MAX_CONTROLS;
114                    position++) {
115                bindControlToAction(position, adapter, presenter);
116            }
117            if (focusedChild != null) {
118                focusedChild.requestFocus();
119            }
120            mControlBar.setChildMarginFromCenter(
121                    getChildMarginFromCenter(mControlBar.getContext(), adapter.size()));
122        }
123
124        void bindControlToAction(int position, Presenter presenter) {
125            bindControlToAction(position, getDisplayedAdapter(), presenter);
126        }
127
128        private void bindControlToAction(final int position,
129                ObjectAdapter adapter, Presenter presenter) {
130            Presenter.ViewHolder vh = mViewHolders.get(position);
131            Object item = adapter.get(position);
132            if (vh == null) {
133                vh = presenter.onCreateViewHolder(mControlBar);
134                mViewHolders.put(position, vh);
135                presenter.setOnClickListener(vh, new View.OnClickListener() {
136                    @Override
137                    public void onClick(View v) {
138                        Object item = getDisplayedAdapter().get(position);
139                        if (mOnActionClickedListener != null && item instanceof Action) {
140                            mOnActionClickedListener.onActionClicked((Action) item);
141                        }
142                    }
143                });
144            }
145            if (vh.view.getParent() == null) {
146                mControlBar.addView(vh.view);
147            }
148            presenter.onBindViewHolder(vh, item);
149        }
150
151        /**
152         * Returns the adapter currently bound to the displayed controls.
153         * May be overridden in a subclass.
154         */
155        ObjectAdapter getDisplayedAdapter() {
156            return mAdapter;
157        }
158    }
159
160    private OnActionClickedListener mOnActionClickedListener;
161    private OnItemViewSelectedListener mOnItemViewSelectedListener;
162    private int mLayoutResourceId;
163    private static int sChildMarginDefault;
164    private static int sControlIconWidth;
165
166    /**
167     * Constructor for a ControlBarPresenter.
168     *
169     * @param layoutResourceId The resource id of the layout for this presenter.
170     */
171    public ControlBarPresenter(int layoutResourceId) {
172        mLayoutResourceId = layoutResourceId;
173    }
174
175    /**
176     * Returns the layout resource id.
177     */
178    public int getLayoutResourceId() {
179        return mLayoutResourceId;
180    }
181
182    /**
183     * Sets the listener for {@link Action} click events.
184     */
185    public void setOnActionClickedListener(OnActionClickedListener listener) {
186        mOnActionClickedListener = listener;
187    }
188
189    /**
190     * Gets the listener for {@link Action} click events.
191     */
192    public OnActionClickedListener getOnActionClickedListener() {
193        return mOnActionClickedListener;
194    }
195
196    /**
197     * Sets the listener for item selection.  When this listener is invoked,
198     *  the rowViewHolder and row are always null.
199     */
200    public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
201        mOnItemViewSelectedListener = listener;
202    }
203
204    /**
205     * Gets the listener for item selection.
206     */
207    public OnItemViewSelectedListener getOnItemViewSelectedListener() {
208        return mOnItemViewSelectedListener;
209    }
210
211    @Override
212    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
213        View v = LayoutInflater.from(parent.getContext())
214            .inflate(getLayoutResourceId(), parent, false);
215        return new ViewHolder(v);
216    }
217
218    @Override
219    public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
220        ViewHolder vh = (ViewHolder) holder;
221        BoundData data = (BoundData) item;
222        if (vh.mAdapter != data.adapter) {
223            vh.mAdapter = data.adapter;
224            vh.mAdapter.registerObserver(vh.mDataObserver);
225        }
226        vh.mPresenter = data.presenter;
227        vh.showControls(vh.mPresenter);
228    }
229
230    @Override
231    public void onUnbindViewHolder(Presenter.ViewHolder holder) {
232        ViewHolder vh = (ViewHolder) holder;
233        vh.mAdapter.unregisterObserver(vh.mDataObserver);
234        vh.mAdapter = null;
235    }
236
237    int getChildMarginDefault(Context context) {
238        if (sChildMarginDefault == 0) {
239            sChildMarginDefault = context.getResources().getDimensionPixelSize(
240                    R.dimen.lb_playback_controls_child_margin_default);
241        }
242        return sChildMarginDefault;
243    }
244
245    int getControlIconWidth(Context context) {
246        if (sControlIconWidth == 0) {
247            sControlIconWidth = context.getResources().getDimensionPixelSize(
248                    R.dimen.lb_control_icon_width);
249        }
250        return sControlIconWidth;
251    }
252}
253