PlaybackControlsPresenter.java revision 70acb0c19be3831a2080e4f902324de16bfbf62e
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.animation.ValueAnimator;
17import android.content.Context;
18import android.content.res.Resources;
19import android.graphics.drawable.ClipDrawable;
20import android.graphics.drawable.ColorDrawable;
21import android.graphics.drawable.Drawable;
22import android.graphics.drawable.LayerDrawable;
23import android.support.annotation.ColorInt;
24import android.support.v17.leanback.R;
25import android.view.Gravity;
26import android.view.LayoutInflater;
27import android.view.View;
28import android.view.ViewGroup;
29import android.view.ViewGroup.MarginLayoutParams;
30import android.view.animation.LinearInterpolator;
31import android.widget.FrameLayout;
32import android.widget.ProgressBar;
33import android.widget.TextView;
34
35/**
36 * A presenter for a control bar that supports "more actions",
37 * and toggling the set of controls between primary and secondary
38 * sets of {@link Actions}.
39 */
40class PlaybackControlsPresenter extends ControlBarPresenter {
41
42    /**
43     * The data type expected by this presenter.
44     */
45    static class BoundData extends ControlBarPresenter.BoundData {
46        /**
47         * The adapter containing secondary actions.
48         */
49        ObjectAdapter secondaryActionsAdapter;
50    }
51
52    class ViewHolder extends ControlBarPresenter.ViewHolder {
53        ObjectAdapter mMoreActionsAdapter;
54        ObjectAdapter.DataObserver mMoreActionsObserver;
55        final FrameLayout mMoreActionsDock;
56        Presenter.ViewHolder mMoreActionsViewHolder;
57        boolean mMoreActionsShowing;
58        final TextView mCurrentTime;
59        final TextView mTotalTime;
60        final ProgressBar mProgressBar;
61        int mCurrentTimeInSeconds;
62        StringBuilder mTotalTimeStringBuilder = new StringBuilder();
63        StringBuilder mCurrentTimeStringBuilder = new StringBuilder();
64        int mCurrentTimeMarginStart;
65        int mTotalTimeMarginEnd;
66
67        ViewHolder(View rootView) {
68            super(rootView);
69            mMoreActionsDock = (FrameLayout) rootView.findViewById(R.id.more_actions_dock);
70            mCurrentTime = (TextView) rootView.findViewById(R.id.current_time);
71            mTotalTime = (TextView) rootView.findViewById(R.id.total_time);
72            mProgressBar = (ProgressBar) rootView.findViewById(R.id.playback_progress);
73            mMoreActionsObserver = new ObjectAdapter.DataObserver() {
74                @Override
75                public void onChanged() {
76                    if (mMoreActionsShowing) {
77                        showControls(mPresenter);
78                    }
79                }
80                @Override
81                public void onItemRangeChanged(int positionStart, int itemCount) {
82                    if (mMoreActionsShowing) {
83                        for (int i = 0; i < itemCount; i++) {
84                            bindControlToAction(positionStart + i, mPresenter);
85                        }
86                    }
87                }
88            };
89            mCurrentTimeMarginStart =
90                    ((MarginLayoutParams) mCurrentTime.getLayoutParams()).getMarginStart();
91            mTotalTimeMarginEnd =
92                    ((MarginLayoutParams) mTotalTime.getLayoutParams()).getMarginEnd();
93        }
94
95        void showMoreActions(boolean show) {
96            if (show) {
97                if (mMoreActionsViewHolder == null) {
98                    Action action = new PlaybackControlsRow.MoreActions(mMoreActionsDock.getContext());
99                    mMoreActionsViewHolder = mPresenter.onCreateViewHolder(mMoreActionsDock);
100                    mPresenter.onBindViewHolder(mMoreActionsViewHolder, action);
101                    mPresenter.setOnClickListener(mMoreActionsViewHolder, new View.OnClickListener() {
102                        @Override
103                        public void onClick(View v) {
104                            toggleMoreActions();
105                        }
106                    });
107                }
108                if (mMoreActionsViewHolder.view.getParent() == null) {
109                    mMoreActionsDock.addView(mMoreActionsViewHolder.view);
110                }
111            } else if (mMoreActionsViewHolder != null &&
112                    mMoreActionsViewHolder.view.getParent() != null) {
113                mMoreActionsDock.removeView(mMoreActionsViewHolder.view);
114            }
115        }
116
117        void toggleMoreActions() {
118            mMoreActionsShowing = !mMoreActionsShowing;
119            showControls(mPresenter);
120        }
121
122        @Override
123        ObjectAdapter getDisplayedAdapter() {
124            return mMoreActionsShowing ? mMoreActionsAdapter : mAdapter;
125        }
126
127        @Override
128        int getChildMarginFromCenter(Context context, int numControls) {
129            int margin = getControlIconWidth(context);
130            if (numControls < 4) {
131                margin += getChildMarginBiggest(context);
132            } else if (numControls < 6) {
133                margin += getChildMarginBigger(context);
134            } else {
135                margin += getChildMarginDefault(context);
136            }
137            return margin;
138        }
139
140        void setTotalTime(int totalTimeMs) {
141            if (totalTimeMs <= 0) {
142                mTotalTime.setVisibility(View.GONE);
143                mProgressBar.setVisibility(View.GONE);
144            } else {
145                mTotalTime.setVisibility(View.VISIBLE);
146                mProgressBar.setVisibility(View.VISIBLE);
147                formatTime(totalTimeMs / 1000, mTotalTimeStringBuilder);
148                mTotalTime.setText(mTotalTimeStringBuilder.toString());
149                mProgressBar.setMax(totalTimeMs);
150            }
151        }
152
153        int getTotalTime() {
154            return mProgressBar.getMax();
155        }
156
157        void setCurrentTime(int currentTimeMs) {
158            int seconds = currentTimeMs / 1000;
159            if (seconds != mCurrentTimeInSeconds) {
160                mCurrentTimeInSeconds = seconds;
161                formatTime(mCurrentTimeInSeconds, mCurrentTimeStringBuilder);
162                mCurrentTime.setText(mCurrentTimeStringBuilder.toString());
163            }
164            mProgressBar.setProgress(currentTimeMs);
165        }
166
167        int getCurrentTime() {
168            return mProgressBar.getProgress();
169        }
170
171        void setSecondaryProgress(int progressMs) {
172            mProgressBar.setSecondaryProgress(progressMs);
173        }
174
175        int getSecondaryProgress() {
176            return mProgressBar.getSecondaryProgress();
177        }
178    }
179
180    private static void formatTime(int seconds, StringBuilder sb) {
181        int minutes = seconds / 60;
182        int hours = minutes / 60;
183        seconds -= minutes * 60;
184        minutes -= hours * 60;
185
186        sb.setLength(0);
187        if (hours > 0) {
188            sb.append(hours).append(':');
189            if (minutes < 10) {
190                sb.append('0');
191            }
192        }
193        sb.append(minutes).append(':');
194        if (seconds < 10) {
195            sb.append('0');
196        }
197        sb.append(seconds);
198    }
199
200    private boolean mMoreActionsEnabled = true;
201    private static int sChildMarginBigger;
202    private static int sChildMarginBiggest;
203
204    /**
205     * Constructor for a PlaybackControlsRowPresenter.
206     *
207     * @param layoutResourceId The resource id of the layout for this presenter.
208     */
209    public PlaybackControlsPresenter(int layoutResourceId) {
210        super(layoutResourceId);
211    }
212
213    /**
214     * Enables the display of secondary actions.
215     * A "more actions" button will be displayed.  When "more actions" is selected,
216     * the primary actions are replaced with the secondary actions.
217     */
218    public void enableSecondaryActions(boolean enable) {
219        mMoreActionsEnabled = enable;
220    }
221
222    /**
223     * Returns true if secondary actions are enabled.
224     */
225    public boolean areMoreActionsEnabled() {
226        return mMoreActionsEnabled;
227    }
228
229    public void setProgressColor(ViewHolder vh, @ColorInt int color) {
230        Drawable drawable = new ClipDrawable(new ColorDrawable(color),
231                Gravity.LEFT, ClipDrawable.HORIZONTAL);
232        ((LayerDrawable) vh.mProgressBar.getProgressDrawable())
233                .setDrawableByLayerId(android.R.id.progress, drawable);
234    }
235
236    public void setTotalTime(ViewHolder vh, int ms) {
237        vh.setTotalTime(ms);
238    }
239
240    public int getTotalTime(ViewHolder vh) {
241        return vh.getTotalTime();
242    }
243
244    public void setCurrentTime(ViewHolder vh, int ms) {
245        vh.setCurrentTime(ms);
246    }
247
248    public int getCurrentTime(ViewHolder vh) {
249        return vh.getCurrentTime();
250    }
251
252    public void setSecondaryProgress(ViewHolder vh, int progressMs) {
253        vh.setSecondaryProgress(progressMs);
254    }
255
256    public int getSecondaryProgress(ViewHolder vh) {
257        return vh.getSecondaryProgress();
258    }
259
260    public void showPrimaryActions(ViewHolder vh) {
261        if (vh.mMoreActionsShowing) {
262            vh.toggleMoreActions();
263        }
264    }
265
266    public void enableTimeMargins(ViewHolder vh, boolean enable) {
267        MarginLayoutParams lp;
268        lp = (MarginLayoutParams) vh.mCurrentTime.getLayoutParams();
269        lp.setMarginStart(enable ? vh.mCurrentTimeMarginStart : 0);
270        vh.mCurrentTime.setLayoutParams(lp);
271
272        lp = (MarginLayoutParams) vh.mTotalTime.getLayoutParams();
273        lp.setMarginEnd(enable ? vh.mTotalTimeMarginEnd : 0);
274        vh.mTotalTime.setLayoutParams(lp);
275    }
276
277    @Override
278    public Presenter.ViewHolder onCreateViewHolder(ViewGroup parent) {
279        View v = LayoutInflater.from(parent.getContext())
280            .inflate(getLayoutResourceId(), parent, false);
281        return new ViewHolder(v);
282    }
283
284    @Override
285    public void onBindViewHolder(Presenter.ViewHolder holder, Object item) {
286        ViewHolder vh = (ViewHolder) holder;
287        BoundData data = (BoundData) item;
288
289        // If binding to a new adapter, display primary actions.
290        if (vh.mMoreActionsAdapter != data.secondaryActionsAdapter) {
291            vh.mMoreActionsAdapter = data.secondaryActionsAdapter;
292            vh.mMoreActionsAdapter.registerObserver(vh.mMoreActionsObserver);
293            vh.mMoreActionsShowing = false;
294        }
295
296        super.onBindViewHolder(holder, item);
297        vh.showMoreActions(mMoreActionsEnabled);
298    }
299
300    @Override
301    public void onUnbindViewHolder(Presenter.ViewHolder holder) {
302        super.onUnbindViewHolder(holder);
303        ViewHolder vh = (ViewHolder) holder;
304        if (vh.mMoreActionsAdapter != null) {
305            vh.mMoreActionsAdapter.unregisterObserver(vh.mMoreActionsObserver);
306            vh.mMoreActionsAdapter = null;
307        }
308    }
309
310    int getChildMarginBigger(Context context) {
311        if (sChildMarginBigger == 0) {
312            sChildMarginBigger = context.getResources().getDimensionPixelSize(
313                    R.dimen.lb_playback_controls_child_margin_bigger);
314        }
315        return sChildMarginBigger;
316    }
317
318    int getChildMarginBiggest(Context context) {
319        if (sChildMarginBiggest == 0) {
320            sChildMarginBiggest = context.getResources().getDimensionPixelSize(
321                    R.dimen.lb_playback_controls_child_margin_biggest);
322        }
323        return sChildMarginBiggest;
324    }
325}
326