• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.example.android.leanback;
18 
19 import android.content.Context;
20 import android.graphics.drawable.Drawable;
21 import android.os.Handler;
22 import android.support.v17.leanback.app.PlaybackControlGlue;
23 import android.support.v17.leanback.widget.Action;
24 import android.support.v17.leanback.widget.ArrayObjectAdapter;
25 import android.support.v17.leanback.widget.ControlButtonPresenterSelector;
26 import android.support.v17.leanback.widget.PlaybackControlsRow;
27 import android.support.v17.leanback.widget.PlaybackControlsRowPresenter;
28 import android.support.v17.leanback.widget.PresenterSelector;
29 import android.support.v17.leanback.widget.SparseArrayObjectAdapter;
30 import android.view.KeyEvent;
31 import android.view.View;
32 import android.widget.Toast;
33 
34 abstract class PlaybackControlHelper extends PlaybackControlGlue {
35     /**
36      * Change the location of the thumbs up/down controls
37      */
38     private static final boolean THUMBS_PRIMARY = true;
39 
40     private static final String FAUX_TITLE = "A short song of silence";
41     private static final String FAUX_SUBTITLE = "2014";
42     private static final int FAUX_DURATION = 33 * 1000;
43 
44     // These should match the playback service FF behavior
45     private static int[] sFastForwardSpeeds = { 2, 3, 4, 5 };
46 
47     private boolean mIsPlaying;
48     private int mSpeed = PlaybackControlGlue.PLAYBACK_SPEED_PAUSED;
49     private long mStartTime;
50     private long mStartPosition = 0;
51 
52     private PlaybackControlsRow.RepeatAction mRepeatAction;
53     private PlaybackControlsRow.ThumbsUpAction mThumbsUpAction;
54     private PlaybackControlsRow.ThumbsDownAction mThumbsDownAction;
55 
56     private Handler mHandler = new Handler();
57     private final Runnable mUpdateProgressRunnable = new Runnable() {
58         @Override
59         public void run() {
60             updateProgress();
61             mHandler.postDelayed(this, getUpdatePeriod());
62         }
63     };
64 
PlaybackControlHelper(Context context, PlaybackOverlayFragment fragment)65     public PlaybackControlHelper(Context context, PlaybackOverlayFragment fragment) {
66         super(context, fragment, sFastForwardSpeeds);
67         mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context);
68         mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.OUTLINE);
69         mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context);
70         mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.OUTLINE);
71         mRepeatAction = new PlaybackControlsRow.RepeatAction(context);
72     }
73 
74     @Override
createControlsRowAndPresenter()75     public PlaybackControlsRowPresenter createControlsRowAndPresenter() {
76         PlaybackControlsRowPresenter presenter = super.createControlsRowAndPresenter();
77 
78         ArrayObjectAdapter adapter = new ArrayObjectAdapter(new ControlButtonPresenterSelector());
79         getControlsRow().setSecondaryActionsAdapter(adapter);
80         if (!THUMBS_PRIMARY) {
81             adapter.add(mThumbsDownAction);
82         }
83         adapter.add(mRepeatAction);
84         if (!THUMBS_PRIMARY) {
85             adapter.add(mThumbsUpAction);
86         }
87 
88         return presenter;
89     }
90 
91     @Override
createPrimaryActionsAdapter( PresenterSelector presenterSelector)92     protected SparseArrayObjectAdapter createPrimaryActionsAdapter(
93             PresenterSelector presenterSelector) {
94         SparseArrayObjectAdapter adapter = new SparseArrayObjectAdapter(presenterSelector);
95         if (THUMBS_PRIMARY) {
96             adapter.set(PlaybackControlGlue.ACTION_CUSTOM_LEFT_FIRST, mThumbsUpAction);
97             adapter.set(PlaybackControlGlue.ACTION_CUSTOM_RIGHT_FIRST, mThumbsDownAction);
98         }
99         return adapter;
100     }
101 
102     @Override
onActionClicked(Action action)103     public void onActionClicked(Action action) {
104         if (shouldDispatchAction(action)) {
105             dispatchAction(action);
106             return;
107         }
108         super.onActionClicked(action);
109     }
110 
111     @Override
onKey(View view, int keyCode, KeyEvent keyEvent)112     public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
113         if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
114             Action action = getControlsRow().getActionForKeyCode(keyEvent.getKeyCode());
115             if (shouldDispatchAction(action)) {
116                 dispatchAction(action);
117                 return true;
118             }
119         }
120         return super.onKey(view, keyCode, keyEvent);
121     }
122 
shouldDispatchAction(Action action)123     private boolean shouldDispatchAction(Action action) {
124         return action == mRepeatAction || action == mThumbsUpAction || action == mThumbsDownAction;
125     }
126 
dispatchAction(Action action)127     private void dispatchAction(Action action) {
128         Toast.makeText(getContext(), action.toString(), Toast.LENGTH_SHORT).show();
129         PlaybackControlsRow.MultiAction multiAction = (PlaybackControlsRow.MultiAction) action;
130         multiAction.nextIndex();
131         notifyActionChanged(multiAction);
132     }
133 
notifyActionChanged(PlaybackControlsRow.MultiAction action)134     private void notifyActionChanged(PlaybackControlsRow.MultiAction action) {
135         int index;
136         index = getPrimaryActionsAdapter().indexOf(action);
137         if (index >= 0) {
138             getPrimaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
139         } else {
140             index = getSecondaryActionsAdapter().indexOf(action);
141             if (index >= 0) {
142                 getSecondaryActionsAdapter().notifyArrayItemRangeChanged(index, 1);
143             }
144         }
145     }
146 
getPrimaryActionsAdapter()147     private SparseArrayObjectAdapter getPrimaryActionsAdapter() {
148         return (SparseArrayObjectAdapter) getControlsRow().getPrimaryActionsAdapter();
149     }
150 
getSecondaryActionsAdapter()151     private ArrayObjectAdapter getSecondaryActionsAdapter() {
152         return (ArrayObjectAdapter) getControlsRow().getSecondaryActionsAdapter();
153     }
154 
155     @Override
hasValidMedia()156     public boolean hasValidMedia() {
157         return true;
158     }
159 
160     @Override
isMediaPlaying()161     public boolean isMediaPlaying() {
162         return mIsPlaying;
163     }
164 
165     @Override
getMediaTitle()166     public CharSequence getMediaTitle() {
167         return FAUX_TITLE;
168     }
169 
170     @Override
getMediaSubtitle()171     public CharSequence getMediaSubtitle() {
172         return FAUX_SUBTITLE;
173     }
174 
175     @Override
getMediaDuration()176     public int getMediaDuration() {
177         return FAUX_DURATION;
178     }
179 
180     @Override
getMediaArt()181     public Drawable getMediaArt() {
182         return null;
183     }
184 
185     @Override
getSupportedActions()186     public long getSupportedActions() {
187         return PlaybackControlGlue.ACTION_PLAY_PAUSE |
188                 PlaybackControlGlue.ACTION_FAST_FORWARD |
189                 PlaybackControlGlue.ACTION_REWIND;
190     }
191 
192     @Override
getCurrentSpeedId()193     public int getCurrentSpeedId() {
194         return mSpeed;
195     }
196 
197     @Override
getCurrentPosition()198     public int getCurrentPosition() {
199         int speed;
200         if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED) {
201             speed = 0;
202         } else if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_NORMAL) {
203             speed = 1;
204         } else if (mSpeed >= PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) {
205             int index = mSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0;
206             speed = getFastForwardSpeeds()[index];
207         } else if (mSpeed <= -PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) {
208             int index = -mSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0;
209             speed = -getRewindSpeeds()[index];
210         } else {
211             return -1;
212         }
213         long position = mStartPosition +
214                 (System.currentTimeMillis() - mStartTime) * speed;
215         if (position > getMediaDuration()) {
216             position = getMediaDuration();
217             onPlaybackComplete(true);
218         } else if (position < 0) {
219             position = 0;
220             onPlaybackComplete(false);
221         }
222         return (int) position;
223     }
224 
onPlaybackComplete(final boolean ended)225     void onPlaybackComplete(final boolean ended) {
226         mHandler.post(new Runnable() {
227             @Override
228             public void run() {
229                 if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.NONE) {
230                     pausePlayback();
231                 } else {
232                     startPlayback(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL);
233                 }
234                 mStartPosition = 0;
235                 onStateChanged();
236             }
237         });
238     }
239 
240     @Override
startPlayback(int speed)241     protected void startPlayback(int speed) {
242         if (speed == mSpeed) {
243             return;
244         }
245         mStartPosition = getCurrentPosition();
246         mSpeed = speed;
247         mIsPlaying = true;
248         mStartTime = System.currentTimeMillis();
249     }
250 
251     @Override
pausePlayback()252     protected void pausePlayback() {
253         if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED) {
254             return;
255         }
256         mStartPosition = getCurrentPosition();
257         mSpeed = PlaybackControlGlue.PLAYBACK_SPEED_PAUSED;
258         mIsPlaying = false;
259     }
260 
261     @Override
skipToNext()262     protected void skipToNext() {
263         // Not supported
264     }
265 
266     @Override
skipToPrevious()267     protected void skipToPrevious() {
268         // Not supported
269     }
270 
271     @Override
enableProgressUpdating(boolean enable)272     public void enableProgressUpdating(boolean enable) {
273         mHandler.removeCallbacks(mUpdateProgressRunnable);
274         if (enable) {
275             mUpdateProgressRunnable.run();
276         }
277     }
278 };