ControlBarPresenter.java revision cf992de2d34abb8228dc6cb39fffe97346823a37
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     * Listener for control selected events.
52     */
53    interface OnControlSelectedListener {
54        void onControlSelected(Presenter.ViewHolder controlViewHolder, Object item,
55                BoundData data);
56    }
57
58    /**
59     * Listener for control clicked events.
60     */
61    interface OnControlClickedListener {
62        void onControlClicked(Presenter.ViewHolder controlViewHolder, Object item,
63                BoundData data);
64    }
65
66    class ViewHolder extends Presenter.ViewHolder {
67        ObjectAdapter mAdapter;
68        BoundData mData;
69        Presenter mPresenter;
70        ControlBar mControlBar;
71        SparseArray<Presenter.ViewHolder> mViewHolders =
72                new SparseArray<Presenter.ViewHolder>();
73        ObjectAdapter.DataObserver mDataObserver;
74
75        /**
76         * Constructor for the ViewHolder.
77         */
78        ViewHolder(View rootView) {
79            super(rootView);
80            mControlBar = (ControlBar) rootView.findViewById(R.id.control_bar);
81            if (mControlBar == null) {
82                throw new IllegalStateException("Couldn't find control_bar");
83            }
84            mControlBar.setOnChildFocusedListener(new ControlBar.OnChildFocusedListener() {
85                @Override
86                public void onChildFocusedListener(View child, View focused) {
87                    if (mOnControlSelectedListener == null) {
88                        return;
89                    }
90                    for (int position = 0; position < mViewHolders.size(); position++) {
91                        if (mViewHolders.get(position).view == child) {
92                            mOnControlSelectedListener.onControlSelected(
93                                    mViewHolders.get(position),
94                                    getDisplayedAdapter().get(position), mData);
95                            break;
96                        }
97                    }
98                }
99            });
100            mDataObserver = new ObjectAdapter.DataObserver() {
101                @Override
102                public void onChanged() {
103                    if (mAdapter == getDisplayedAdapter()) {
104                        showControls(mPresenter);
105                    }
106                }
107                @Override
108                public void onItemRangeChanged(int positionStart, int itemCount) {
109                    if (mAdapter == getDisplayedAdapter()) {
110                        for (int i = 0; i < itemCount; i++) {
111                            bindControlToAction(positionStart + i, mPresenter);
112                        }
113                    }
114                }
115            };
116        }
117
118        int getChildMarginFromCenter(Context context, int numControls) {
119            // Includes margin between icons plus two times half the icon width.
120            return getChildMarginDefault(context) + getControlIconWidth(context);
121        }
122
123        void showControls(Presenter presenter) {
124            ObjectAdapter adapter = getDisplayedAdapter();
125            // Shrink the number of attached views
126            View focusedView = mControlBar.getFocusedChild();
127            if (focusedView != null && adapter.size() > 0 &&
128                    mControlBar.indexOfChild(focusedView) >= adapter.size()) {
129                mControlBar.getChildAt(adapter.size() - 1).requestFocus();
130            }
131            for (int i = mControlBar.getChildCount() - 1; i >= adapter.size(); i--) {
132                mControlBar.removeViewAt(i);
133            }
134            for (int position = 0; position < adapter.size() && position < MAX_CONTROLS;
135                    position++) {
136                bindControlToAction(position, adapter, presenter);
137            }
138            mControlBar.setChildMarginFromCenter(
139                    getChildMarginFromCenter(mControlBar.getContext(), adapter.size()));
140        }
141
142        void bindControlToAction(int position, Presenter presenter) {
143            bindControlToAction(position, getDisplayedAdapter(), presenter);
144        }
145
146        private void bindControlToAction(final int position,
147                ObjectAdapter adapter, Presenter presenter) {
148            Presenter.ViewHolder vh = mViewHolders.get(position);
149            Object item = adapter.get(position);
150            if (vh == null) {
151                vh = presenter.onCreateViewHolder(mControlBar);
152                mViewHolders.put(position, vh);
153
154                final Presenter.ViewHolder itemViewHolder = vh;
155                presenter.setOnClickListener(vh, new View.OnClickListener() {
156                    @Override
157                    public void onClick(View v) {
158                        Object item = getDisplayedAdapter().get(position);
159                        if (mOnControlClickedListener != null) {
160                            mOnControlClickedListener.onControlClicked(itemViewHolder, item,
161                                    mData);
162                        }
163                    }
164                });
165            }
166            if (vh.view.getParent() == null) {
167                mControlBar.addView(vh.view);
168            }
169            presenter.onBindViewHolder(vh, item);
170        }
171
172        /**
173         * Returns the adapter currently bound to the displayed controls.
174         * May be overridden in a subclass.
175         */
176        ObjectAdapter getDisplayedAdapter() {
177            return mAdapter;
178        }
179    }
180
181    private OnControlClickedListener mOnControlClickedListener;
182    private OnControlSelectedListener mOnControlSelectedListener;
183    private int mLayoutResourceId;
184    private static int sChildMarginDefault;
185    private static int sControlIconWidth;
186
187    /**
188     * Constructor for a ControlBarPresenter.
189     *
190     * @param layoutResourceId The resource id of the layout for this presenter.
191     */
192    public ControlBarPresenter(int layoutResourceId) {
193        mLayoutResourceId = layoutResourceId;
194    }
195
196    /**
197     * Returns the layout resource id.
198     */
199    public int getLayoutResourceId() {
200        return mLayoutResourceId;
201    }
202
203    /**
204     * Sets the listener for control clicked events.
205     */
206    public void setOnControlClickedListener(OnControlClickedListener listener) {
207        mOnControlClickedListener = listener;
208    }
209
210    /**
211     * Returns the listener for control clicked events.
212     */
213    public OnControlClickedListener getOnItemViewClickedListener() {
214        return mOnControlClickedListener;
215    }
216
217    /**
218     * Sets the listener for control selection.
219     */
220    public void setOnControlSelectedListener(OnControlSelectedListener listener) {
221        mOnControlSelectedListener = listener;
222    }
223
224    /**
225     * Returns the listener for control selection.
226     */
227    public OnControlSelectedListener getOnItemControlListener() {
228        return mOnControlSelectedListener;
229    }
230
231    @Override
232    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
233        View v = LayoutInflater.from(parent.getContext())
234            .inflate(getLayoutResourceId(), parent, false);
235        return new ViewHolder(v);
236    }
237
238    @Override
239    public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
240        ViewHolder vh = (ViewHolder) holder;
241        BoundData data = (BoundData) item;
242        if (vh.mAdapter != data.adapter) {
243            vh.mAdapter = data.adapter;
244            vh.mAdapter.registerObserver(vh.mDataObserver);
245        }
246        vh.mPresenter = data.presenter;
247        vh.mData = data;
248        vh.showControls(vh.mPresenter);
249    }
250
251    @Override
252    public void onUnbindViewHolder(Presenter.ViewHolder holder) {
253        ViewHolder vh = (ViewHolder) holder;
254        vh.mAdapter.unregisterObserver(vh.mDataObserver);
255        vh.mAdapter = null;
256        vh.mData = null;
257    }
258
259    int getChildMarginDefault(Context context) {
260        if (sChildMarginDefault == 0) {
261            sChildMarginDefault = context.getResources().getDimensionPixelSize(
262                    R.dimen.lb_playback_controls_child_margin_default);
263        }
264        return sChildMarginDefault;
265    }
266
267    int getControlIconWidth(Context context) {
268        if (sControlIconWidth == 0) {
269            sControlIconWidth = context.getResources().getDimensionPixelSize(
270                    R.dimen.lb_control_icon_width);
271        }
272        return sControlIconWidth;
273    }
274}
275