PlaybackControlGlue.java revision 946a01cfedb6838e98d7a37e30f64c39b603893d
1/* 2 * Copyright (C) 2016 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 17package com.example.android.leanback; 18 19import android.content.Context; 20import android.graphics.drawable.Drawable; 21import android.os.Handler; 22import android.support.v17.leanback.widget.Action; 23import android.support.v17.leanback.widget.ArrayObjectAdapter; 24import android.support.v17.leanback.widget.PlaybackControlsRow; 25import android.support.v17.leanback.widget.SparseArrayObjectAdapter; 26import android.view.KeyEvent; 27import android.view.View; 28import android.widget.Toast; 29 30abstract class PlaybackControlGlue extends android.support.v17.leanback.media.PlaybackControlGlue { 31 /** 32 * Change the location of the thumbs up/down controls 33 */ 34 private static final boolean THUMBS_PRIMARY = true; 35 36 private static final String FAUX_TITLE = "A short song of silence"; 37 private static final String FAUX_SUBTITLE = "2014"; 38 private static final int FAUX_DURATION = 33 * 1000; 39 40 // These should match the playback service FF behavior 41 private static int[] sFastForwardSpeeds = { 2, 3, 4, 5 }; 42 43 private boolean mIsPlaying; 44 private int mSpeed = PlaybackControlGlue.PLAYBACK_SPEED_PAUSED; 45 private long mStartTime; 46 private long mStartPosition = 0; 47 48 private PlaybackControlsRow.RepeatAction mRepeatAction; 49 private PlaybackControlsRow.ThumbsUpAction mThumbsUpAction; 50 private PlaybackControlsRow.ThumbsDownAction mThumbsDownAction; 51 private PlaybackControlsRow.PictureInPictureAction mPipAction; 52 53 private Handler mHandler = new Handler(); 54 private final Runnable mUpdateProgressRunnable = new Runnable() { 55 @Override 56 public void run() { 57 updateProgress(); 58 mHandler.postDelayed(this, getUpdatePeriod()); 59 } 60 }; 61 62 PlaybackControlGlue(Context context) { 63 super(context, sFastForwardSpeeds); 64 mThumbsUpAction = new PlaybackControlsRow.ThumbsUpAction(context); 65 mThumbsUpAction.setIndex(PlaybackControlsRow.ThumbsUpAction.INDEX_OUTLINE); 66 mThumbsDownAction = new PlaybackControlsRow.ThumbsDownAction(context); 67 mThumbsDownAction.setIndex(PlaybackControlsRow.ThumbsDownAction.INDEX_OUTLINE); 68 mRepeatAction = new PlaybackControlsRow.RepeatAction(context); 69 mPipAction = new PlaybackControlsRow.PictureInPictureAction(context); 70 } 71 72 @Override 73 protected void onCreateSecondaryActions(ArrayObjectAdapter adapter) { 74 if (!THUMBS_PRIMARY) { 75 adapter.add(mThumbsDownAction); 76 } 77 if (android.os.Build.VERSION.SDK_INT > 23) { 78 adapter.add(mPipAction); 79 } 80 adapter.add(mRepeatAction); 81 if (!THUMBS_PRIMARY) { 82 adapter.add(mThumbsUpAction); 83 } 84 } 85 86 @Override 87 protected void onCreatePrimaryActions(SparseArrayObjectAdapter adapter) { 88 if (THUMBS_PRIMARY) { 89 adapter.set(PlaybackControlGlue.ACTION_CUSTOM_LEFT_FIRST, mThumbsUpAction); 90 adapter.set(PlaybackControlGlue.ACTION_CUSTOM_RIGHT_FIRST, mThumbsDownAction); 91 } 92 } 93 94 @Override 95 public void onActionClicked(Action action) { 96 if (shouldDispatchAction(action)) { 97 dispatchAction(action); 98 return; 99 } 100 super.onActionClicked(action); 101 } 102 103 @Override 104 public boolean onKey(View view, int keyCode, KeyEvent keyEvent) { 105 if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) { 106 Action action = getControlsRow().getActionForKeyCode(keyEvent.getKeyCode()); 107 if (shouldDispatchAction(action)) { 108 dispatchAction(action); 109 return true; 110 } 111 } 112 return super.onKey(view, keyCode, keyEvent); 113 } 114 115 private boolean shouldDispatchAction(Action action) { 116 return action == mRepeatAction || action == mThumbsUpAction || action == mThumbsDownAction; 117 } 118 119 private void dispatchAction(Action action) { 120 Toast.makeText(getContext(), action.toString(), Toast.LENGTH_SHORT).show(); 121 PlaybackControlsRow.MultiAction multiAction = (PlaybackControlsRow.MultiAction) action; 122 multiAction.nextIndex(); 123 notifyActionChanged(multiAction); 124 } 125 126 private void notifyActionChanged(PlaybackControlsRow.MultiAction action) { 127 int index; 128 index = getPrimaryActionsAdapter().indexOf(action); 129 if (index >= 0) { 130 getPrimaryActionsAdapter().notifyArrayItemRangeChanged(index, 1); 131 } else { 132 index = getSecondaryActionsAdapter().indexOf(action); 133 if (index >= 0) { 134 getSecondaryActionsAdapter().notifyArrayItemRangeChanged(index, 1); 135 } 136 } 137 } 138 139 private SparseArrayObjectAdapter getPrimaryActionsAdapter() { 140 return (SparseArrayObjectAdapter) getControlsRow().getPrimaryActionsAdapter(); 141 } 142 143 private ArrayObjectAdapter getSecondaryActionsAdapter() { 144 return (ArrayObjectAdapter) getControlsRow().getSecondaryActionsAdapter(); 145 } 146 147 @Override 148 public boolean hasValidMedia() { 149 return true; 150 } 151 152 @Override 153 public boolean isMediaPlaying() { 154 return mIsPlaying; 155 } 156 157 @Override 158 public CharSequence getMediaTitle() { 159 return FAUX_TITLE; 160 } 161 162 @Override 163 public CharSequence getMediaSubtitle() { 164 return FAUX_SUBTITLE; 165 } 166 167 @Override 168 public int getMediaDuration() { 169 return FAUX_DURATION; 170 } 171 172 @Override 173 public Drawable getMediaArt() { 174 return null; 175 } 176 177 @Override 178 public long getSupportedActions() { 179 return PlaybackControlGlue.ACTION_PLAY_PAUSE 180 | PlaybackControlGlue.ACTION_FAST_FORWARD 181 | PlaybackControlGlue.ACTION_REWIND; 182 } 183 184 @Override 185 public int getCurrentSpeedId() { 186 return mSpeed; 187 } 188 189 @Override 190 public int getCurrentPosition() { 191 int speed; 192 if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED) { 193 speed = 0; 194 } else if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_NORMAL) { 195 speed = 1; 196 } else if (mSpeed >= PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) { 197 int index = mSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0; 198 speed = getFastForwardSpeeds()[index]; 199 } else if (mSpeed <= -PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0) { 200 int index = -mSpeed - PlaybackControlGlue.PLAYBACK_SPEED_FAST_L0; 201 speed = -getRewindSpeeds()[index]; 202 } else { 203 return -1; 204 } 205 long position = mStartPosition + (System.currentTimeMillis() - mStartTime) * speed; 206 if (position > getMediaDuration()) { 207 position = getMediaDuration(); 208 onPlaybackComplete(true); 209 } else if (position < 0) { 210 position = 0; 211 onPlaybackComplete(false); 212 } 213 return (int) position; 214 } 215 216 void onPlaybackComplete(final boolean ended) { 217 mHandler.post(new Runnable() { 218 @Override 219 public void run() { 220 if (mRepeatAction.getIndex() == PlaybackControlsRow.RepeatAction.INDEX_NONE) { 221 pause(); 222 } else { 223 play(PlaybackControlGlue.PLAYBACK_SPEED_NORMAL); 224 } 225 mStartPosition = 0; 226 onStateChanged(); 227 } 228 }); 229 } 230 231 @Override 232 public void play(int speed) { 233 if (speed == mSpeed) { 234 return; 235 } 236 mStartPosition = getCurrentPosition(); 237 mSpeed = speed; 238 mIsPlaying = true; 239 mStartTime = System.currentTimeMillis(); 240 } 241 242 @Override 243 public void pause() { 244 if (mSpeed == PlaybackControlGlue.PLAYBACK_SPEED_PAUSED) { 245 return; 246 } 247 mStartPosition = getCurrentPosition(); 248 mSpeed = PlaybackControlGlue.PLAYBACK_SPEED_PAUSED; 249 mIsPlaying = false; 250 } 251 252 @Override 253 public void next() { 254 // Not supported 255 } 256 257 @Override 258 public void previous() { 259 // Not supported 260 } 261 262 @Override 263 public void enableProgressUpdating(boolean enable) { 264 mHandler.removeCallbacks(mUpdateProgressRunnable); 265 if (enable) { 266 mUpdateProgressRunnable.run(); 267 } 268 } 269} 270