ControlBarPresenter.java revision 4cc255c1c9ce5f3a718970b6e0be2c3ae6abf9ed
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), getAdapter().get(position),
80                                            null, null);
81                            break;
82                        }
83                    }
84                }
85            });
86            mDataObserver = new ObjectAdapter.DataObserver() {
87                @Override
88                public void onChanged() {
89                    showControls(mAdapter, mPresenter);
90                }
91                @Override
92                public void onItemRangeChanged(int positionStart, int itemCount) {
93                    for (int i = 0; i < itemCount; i++) {
94                        bindControlToAction(positionStart + i, mAdapter, mPresenter);
95                    }
96                }
97            };
98        }
99
100        int getChildMarginFromCenter(Context context, int numControls) {
101            // Includes margin between icons plus two times half the icon width.
102            return getChildMarginDefault(context) + getControlIconWidth(context);
103        }
104
105        void showControls(ObjectAdapter adapter, Presenter presenter) {
106            View focusedChild = mControlBar.getFocusedChild();
107            mControlBar.removeAllViews();
108            for (int position = 0; position < adapter.size() && position < MAX_CONTROLS;
109                    position++) {
110                bindControlToAction(position, adapter, presenter);
111            }
112            if (focusedChild != null) {
113                focusedChild.requestFocus();
114            }
115            mControlBar.setChildMarginFromCenter(
116                    getChildMarginFromCenter(mControlBar.getContext(), adapter.size()));
117        }
118
119        void bindControlToAction(final int position, ObjectAdapter adapter, Presenter presenter) {
120            Presenter.ViewHolder vh = mViewHolders.get(position);
121            Object item = adapter.get(position);
122            if (vh == null) {
123                vh = presenter.onCreateViewHolder(mControlBar);
124                mViewHolders.put(position, vh);
125                presenter.setOnClickListener(vh, new View.OnClickListener() {
126                    @Override
127                    public void onClick(View v) {
128                        Object item = getAdapter().get(position);
129                        if (mOnActionClickedListener != null && item instanceof Action) {
130                            mOnActionClickedListener.onActionClicked((Action) item);
131                        }
132                    }
133                });
134            }
135            if (vh.view.getParent() == null) {
136                mControlBar.addView(vh.view);
137            }
138            presenter.onBindViewHolder(vh, item);
139        }
140
141        ObjectAdapter getAdapter() {
142            return mAdapter;
143        }
144    }
145
146    private OnActionClickedListener mOnActionClickedListener;
147    private OnItemViewSelectedListener mOnItemViewSelectedListener;
148    private int mLayoutResourceId;
149    private static int sChildMarginDefault;
150    private static int sControlIconWidth;
151
152    /**
153     * Constructor for a ControlBarPresenter.
154     *
155     * @param layoutResourceId The resource id of the layout for this presenter.
156     */
157    public ControlBarPresenter(int layoutResourceId) {
158        mLayoutResourceId = layoutResourceId;
159    }
160
161    /**
162     * Returns the layout resource id.
163     */
164    public int getLayoutResourceId() {
165        return mLayoutResourceId;
166    }
167
168    /**
169     * Sets the listener for {@link Action} click events.
170     */
171    public void setOnActionClickedListener(OnActionClickedListener listener) {
172        mOnActionClickedListener = listener;
173    }
174
175    /**
176     * Gets the listener for {@link Action} click events.
177     */
178    public OnActionClickedListener getOnActionClickedListener() {
179        return mOnActionClickedListener;
180    }
181
182    /**
183     * Sets the listener for item selection.  When this listener is invoked,
184     *  the rowViewHolder and row are always null.
185     */
186    public void setOnItemViewSelectedListener(OnItemViewSelectedListener listener) {
187        mOnItemViewSelectedListener = listener;
188    }
189
190    /**
191     * Gets the listener for item selection.
192     */
193    public OnItemViewSelectedListener getOnItemViewSelectedListener() {
194        return mOnItemViewSelectedListener;
195    }
196
197    @Override
198    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
199        View v = LayoutInflater.from(parent.getContext())
200            .inflate(getLayoutResourceId(), parent, false);
201        return new ViewHolder(v);
202    }
203
204    @Override
205    public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
206        ViewHolder vh = (ViewHolder) holder;
207        BoundData data = (BoundData) item;
208        if (vh.mAdapter != data.adapter) {
209            vh.mAdapter = data.adapter;
210            vh.mAdapter.registerObserver(vh.mDataObserver);
211        }
212        vh.mPresenter = data.presenter;
213        vh.showControls(vh.mAdapter, vh.mPresenter);
214    }
215
216    @Override
217    public void onUnbindViewHolder(Presenter.ViewHolder holder) {
218        ViewHolder vh = (ViewHolder) holder;
219        vh.mAdapter.unregisterObserver(vh.mDataObserver);
220        vh.mAdapter = null;
221    }
222
223    int getChildMarginDefault(Context context) {
224        if (sChildMarginDefault == 0) {
225            sChildMarginDefault = context.getResources().getDimensionPixelSize(
226                    R.dimen.lb_playback_controls_child_margin_default);
227        }
228        return sChildMarginDefault;
229    }
230
231    int getControlIconWidth(Context context) {
232        if (sControlIconWidth == 0) {
233            sControlIconWidth = context.getResources().getDimensionPixelSize(
234                    R.dimen.lb_control_icon_width);
235        }
236        return sControlIconWidth;
237    }
238}
239