| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2013 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 org.chromium.third_party.android.media; | |
| 18 | |
| 19 import android.content.Context; | |
| 20 import android.support.v4.media.TransportController; | |
| 21 import android.support.v4.media.TransportMediator; | |
| 22 import android.support.v4.media.TransportStateListener; | |
| 23 import android.util.AttributeSet; | |
| 24 import android.view.LayoutInflater; | |
| 25 import android.view.View; | |
| 26 import android.view.ViewGroup; | |
| 27 import android.view.accessibility.AccessibilityEvent; | |
| 28 import android.view.accessibility.AccessibilityNodeInfo; | |
| 29 import android.widget.FrameLayout; | |
| 30 import android.widget.ImageButton; | |
| 31 import android.widget.SeekBar; | |
| 32 import android.widget.TextView; | |
| 33 | |
| 34 import org.chromium.third_party.android.media.R; | |
| 35 | |
| 36 import java.util.Formatter; | |
| 37 import java.util.Locale; | |
| 38 | |
| 39 /** | |
| 40 * Helper for implementing media controls in an application. | |
| 41 * We use this custom version instead of {@link android.widget.MediaController}
so that we can | |
| 42 * customize the look as we want. This file is taken directly from the public An
droid sample for | |
| 43 * supportv4, with tiny bug fixes. | |
| 44 */ | |
| 45 public class MediaController extends FrameLayout { | |
| 46 | |
| 47 private TransportController mController; | |
| 48 private Context mContext; | |
| 49 private ViewGroup mProgressGroup; | |
| 50 private SeekBar mProgressBar; | |
| 51 private TextView mEndTime, mCurrentTime; | |
| 52 private boolean mDragging; | |
| 53 private boolean mUseFastForward; | |
| 54 private boolean mListenersSet; | |
| 55 private boolean mShowNext, mShowPrev; | |
| 56 private View.OnClickListener mNextListener, mPrevListener; | |
| 57 private StringBuilder mFormatBuilder; | |
| 58 private Formatter mFormatter; | |
| 59 private ImageButton mPauseButton; | |
| 60 private ImageButton mFfwdButton; | |
| 61 private ImageButton mRewButton; | |
| 62 private ImageButton mNextButton; | |
| 63 private ImageButton mPrevButton; | |
| 64 | |
| 65 private TransportStateListener mStateListener = new TransportStateListener()
{ | |
| 66 @Override | |
| 67 public void onPlayingChanged(TransportController controller) { | |
| 68 updatePausePlay(); | |
| 69 } | |
| 70 @Override | |
| 71 public void onTransportControlsChanged(TransportController controller) { | |
| 72 updateButtons(); | |
| 73 } | |
| 74 }; | |
| 75 | |
| 76 public MediaController(Context context, AttributeSet attrs) { | |
| 77 super(context, attrs); | |
| 78 mContext = context; | |
| 79 mUseFastForward = true; | |
| 80 LayoutInflater inflate = (LayoutInflater) | |
| 81 mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); | |
| 82 inflate.inflate(R.layout.media_controller, this, true); | |
| 83 initControllerView(); | |
| 84 } | |
| 85 | |
| 86 public MediaController(Context context, boolean useFastForward) { | |
| 87 super(context); | |
| 88 mContext = context; | |
| 89 mUseFastForward = useFastForward; | |
| 90 } | |
| 91 | |
| 92 public MediaController(Context context) { | |
| 93 this(context, true); | |
| 94 } | |
| 95 | |
| 96 public void setMediaPlayer(TransportController controller) { | |
| 97 if (getWindowToken() != null) { | |
| 98 if (mController != null) { | |
| 99 mController.unregisterStateListener(mStateListener); | |
| 100 } | |
| 101 if (controller != null) { | |
| 102 controller.registerStateListener(mStateListener); | |
| 103 } | |
| 104 } | |
| 105 mController = controller; | |
| 106 updatePausePlay(); | |
| 107 } | |
| 108 | |
| 109 @Override | |
| 110 protected void onAttachedToWindow() { | |
| 111 super.onAttachedToWindow(); | |
| 112 if (mController != null) { | |
| 113 mController.registerStateListener(mStateListener); | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 @Override | |
| 118 protected void onDetachedFromWindow() { | |
| 119 super.onDetachedFromWindow(); | |
| 120 if (mController != null) { | |
| 121 mController.unregisterStateListener(mStateListener); | |
| 122 } | |
| 123 } | |
| 124 | |
| 125 private void initControllerView() { | |
| 126 mPauseButton = (ImageButton) findViewById(R.id.pause); | |
| 127 if (mPauseButton != null) { | |
| 128 mPauseButton.requestFocus(); | |
| 129 mPauseButton.setOnClickListener(mPauseListener); | |
| 130 } | |
| 131 | |
| 132 mFfwdButton = (ImageButton) findViewById(R.id.ffwd); | |
| 133 if (mFfwdButton != null) { | |
| 134 mFfwdButton.setOnClickListener(mFfwdListener); | |
| 135 mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE
); | |
| 136 } | |
| 137 | |
| 138 mRewButton = (ImageButton) findViewById(R.id.rew); | |
| 139 if (mRewButton != null) { | |
| 140 mRewButton.setOnClickListener(mRewListener); | |
| 141 mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE)
; | |
| 142 } | |
| 143 | |
| 144 // By default these are hidden. They will be enabled when setPrevNextLis
teners() is called | |
| 145 mNextButton = (ImageButton) findViewById(R.id.next); | |
| 146 if (mNextButton != null && !mListenersSet) { | |
| 147 mNextButton.setVisibility(View.GONE); | |
| 148 } | |
| 149 mPrevButton = (ImageButton) findViewById(R.id.prev); | |
| 150 if (mPrevButton != null && !mListenersSet) { | |
| 151 mPrevButton.setVisibility(View.GONE); | |
| 152 } | |
| 153 | |
| 154 mProgressGroup = (ViewGroup) findViewById(R.id.mediacontroller_progress_
container); | |
| 155 | |
| 156 if (mProgressGroup != null) { | |
| 157 mProgressBar = (SeekBar) mProgressGroup.findViewById(R.id.mediacontr
oller_progress_bar); | |
| 158 if (mProgressBar != null) { | |
| 159 mProgressBar.setOnSeekBarChangeListener(mSeekListener); | |
| 160 mProgressBar.setMax(1000); | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 mEndTime = (TextView) findViewById(R.id.time); | |
| 165 mCurrentTime = (TextView) findViewById(R.id.time_current); | |
| 166 mFormatBuilder = new StringBuilder(); | |
| 167 mFormatter = new Formatter(mFormatBuilder, Locale.getDefault()); | |
| 168 | |
| 169 installPrevNextListeners(); | |
| 170 } | |
| 171 | |
| 172 /** | |
| 173 * Disable pause or seek buttons if the stream cannot be paused or seeked. | |
| 174 * This requires the control interface to be a MediaPlayerControlExt | |
| 175 */ | |
| 176 void updateButtons() { | |
| 177 int flags = mController.getTransportControlFlags(); | |
| 178 boolean enabled = isEnabled(); | |
| 179 if (mPauseButton != null) { | |
| 180 boolean needPlayPauseButton = (flags & TransportMediator.FLAG_KEY_ME
DIA_PAUSE) != 0 || | |
| 181 (flags & TransportMediator.FLAG_KEY_ME
DIA_PLAY) != 0; | |
| 182 mPauseButton.setEnabled(enabled && needPlayPauseButton); | |
| 183 } | |
| 184 if (mRewButton != null) { | |
| 185 mRewButton.setEnabled(enabled && | |
| 186 (flags & TransportMediator.FLAG_KEY_MEDIA_REWI
ND) != 0); | |
| 187 } | |
| 188 if (mFfwdButton != null) { | |
| 189 mFfwdButton.setEnabled(enabled && | |
| 190 (flags & TransportMediator.FLAG_KEY_MEDIA_FAST_FORWARD) != 0
); | |
| 191 } | |
| 192 if (mPrevButton != null) { | |
| 193 mShowPrev = (flags & TransportMediator.FLAG_KEY_MEDIA_PREVIOUS) != 0 | |
| 194 || mPrevListener != null; | |
| 195 mPrevButton.setEnabled(enabled && mShowPrev); | |
| 196 } | |
| 197 if (mNextButton != null) { | |
| 198 mShowNext = (flags & TransportMediator.FLAG_KEY_MEDIA_NEXT) != 0 | |
| 199 || mNextListener != null; | |
| 200 mNextButton.setEnabled(enabled && mShowNext); | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 public void refresh() { | |
| 205 updateProgress(); | |
| 206 updateButtons(); | |
| 207 updatePausePlay(); | |
| 208 } | |
| 209 | |
| 210 private String stringForTime(int timeMs) { | |
| 211 int totalSeconds = timeMs / 1000; | |
| 212 | |
| 213 int seconds = totalSeconds % 60; | |
| 214 int minutes = (totalSeconds / 60) % 60; | |
| 215 int hours = totalSeconds / 3600; | |
| 216 | |
| 217 mFormatBuilder.setLength(0); | |
| 218 if (hours > 0) { | |
| 219 return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).to
String(); | |
| 220 } else { | |
| 221 return mFormatter.format("%02d:%02d", minutes, seconds).toString(); | |
| 222 } | |
| 223 } | |
| 224 | |
| 225 public long updateProgress() { | |
| 226 if (mController == null || mDragging) { | |
| 227 return 0; | |
| 228 } | |
| 229 long position = mController.getCurrentPosition(); | |
| 230 long duration = mController.getDuration(); | |
| 231 if (duration <= 0) { | |
| 232 // If there is no valid duration, hide the progress bar and time ind
icators. | |
| 233 if (mProgressGroup != null) mProgressGroup.setVisibility(View.INVISI
BLE); | |
| 234 } else if (mProgressBar != null) { | |
| 235 if (mProgressGroup != null) mProgressGroup.setVisibility(View.VISIBL
E); | |
| 236 // use long to avoid overflow | |
| 237 long pos = 1000L * position / duration; | |
| 238 mProgressBar.setProgress( (int) pos); | |
| 239 | |
| 240 int percent = mController.getBufferPercentage(); | |
| 241 mProgressBar.setSecondaryProgress(percent * 10); | |
| 242 } | |
| 243 | |
| 244 if (mEndTime != null) | |
| 245 mEndTime.setText(stringForTime((int)duration)); | |
| 246 if (mCurrentTime != null) | |
| 247 mCurrentTime.setText(stringForTime((int)position)); | |
| 248 | |
| 249 return position; | |
| 250 } | |
| 251 | |
| 252 private View.OnClickListener mPauseListener = new View.OnClickListener() { | |
| 253 @Override | |
| 254 public void onClick(View v) { | |
| 255 doPauseResume(); | |
| 256 } | |
| 257 }; | |
| 258 | |
| 259 private void updatePausePlay() { | |
| 260 if (mPauseButton == null) | |
| 261 return; | |
| 262 | |
| 263 if (mController.isPlaying()) { | |
| 264 mPauseButton.setImageResource(android.R.drawable.ic_media_pause); | |
| 265 } else { | |
| 266 mPauseButton.setImageResource(android.R.drawable.ic_media_play); | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 private void doPauseResume() { | |
| 271 if (mController.isPlaying()) { | |
| 272 mController.pausePlaying(); | |
| 273 } else { | |
| 274 mController.startPlaying(); | |
| 275 } | |
| 276 updatePausePlay(); | |
| 277 } | |
| 278 | |
| 279 // There are two scenarios that can trigger the seekbar listener to trigger: | |
| 280 // | |
| 281 // The first is the user using the touchpad to adjust the posititon of the | |
| 282 // seekbar's thumb. In this case onStartTrackingTouch is called followed by | |
| 283 // a number of onProgressChanged notifications, concluded by onStopTrackingT
ouch. | |
| 284 // We're setting the field "mDragging" to true for the duration of the dragg
ing | |
| 285 // session to avoid jumps in the position in case of ongoing playback. | |
| 286 // | |
| 287 // The second scenario involves the user operating the scroll ball, in this | |
| 288 // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notification
s, | |
| 289 // we will simply apply the updated position without suspending regular upda
tes. | |
| 290 private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBa
rChangeListener() { | |
| 291 @Override | |
| 292 public void onStartTrackingTouch(SeekBar bar) { | |
| 293 mDragging = true; | |
| 294 } | |
| 295 | |
| 296 @Override | |
| 297 public void onProgressChanged(SeekBar bar, int progress, boolean fromuse
r) { | |
| 298 if (!fromuser) { | |
| 299 // We're not interested in programmatically generated changes to | |
| 300 // the progress bar's position. | |
| 301 return; | |
| 302 } | |
| 303 | |
| 304 long duration = mController.getDuration(); | |
| 305 long newposition = (duration * progress) / 1000L; | |
| 306 mController.seekTo((int) newposition); | |
| 307 if (mCurrentTime != null) | |
| 308 mCurrentTime.setText(stringForTime( (int) newposition)); | |
| 309 } | |
| 310 | |
| 311 @Override | |
| 312 public void onStopTrackingTouch(SeekBar bar) { | |
| 313 mDragging = false; | |
| 314 updateProgress(); | |
| 315 updatePausePlay(); | |
| 316 } | |
| 317 }; | |
| 318 | |
| 319 @Override | |
| 320 public void setEnabled(boolean enabled) { | |
| 321 super.setEnabled(enabled); | |
| 322 updateButtons(); | |
| 323 } | |
| 324 | |
| 325 @Override | |
| 326 public void onInitializeAccessibilityEvent(AccessibilityEvent event) { | |
| 327 super.onInitializeAccessibilityEvent(event); | |
| 328 event.setClassName(MediaController.class.getName()); | |
| 329 } | |
| 330 | |
| 331 @Override | |
| 332 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { | |
| 333 super.onInitializeAccessibilityNodeInfo(info); | |
| 334 info.setClassName(MediaController.class.getName()); | |
| 335 } | |
| 336 | |
| 337 private View.OnClickListener mRewListener = new View.OnClickListener() { | |
| 338 @Override | |
| 339 public void onClick(View v) { | |
| 340 long pos = mController.getCurrentPosition(); | |
| 341 pos -= 5000; // milliseconds | |
| 342 mController.seekTo(pos); | |
| 343 updateProgress(); | |
| 344 } | |
| 345 }; | |
| 346 | |
| 347 private View.OnClickListener mFfwdListener = new View.OnClickListener() { | |
| 348 @Override | |
| 349 public void onClick(View v) { | |
| 350 long pos = mController.getCurrentPosition(); | |
| 351 pos += 15000; // milliseconds | |
| 352 mController.seekTo(pos); | |
| 353 updateProgress(); | |
| 354 } | |
| 355 }; | |
| 356 | |
| 357 private void installPrevNextListeners() { | |
| 358 if (mNextButton != null) { | |
| 359 mNextButton.setOnClickListener(mNextListener); | |
| 360 mNextButton.setEnabled(mShowNext); | |
| 361 } | |
| 362 | |
| 363 if (mPrevButton != null) { | |
| 364 mPrevButton.setOnClickListener(mPrevListener); | |
| 365 mPrevButton.setEnabled(mShowPrev); | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 public void setPrevNextListeners(View.OnClickListener next, View.OnClickList
ener prev) { | |
| 370 mNextListener = next; | |
| 371 mPrevListener = prev; | |
| 372 mListenersSet = true; | |
| 373 | |
| 374 installPrevNextListeners(); | |
| 375 | |
| 376 if (mNextButton != null) { | |
| 377 mNextButton.setVisibility(View.VISIBLE); | |
| 378 mShowNext = true; | |
| 379 } | |
| 380 if (mPrevButton != null) { | |
| 381 mPrevButton.setVisibility(View.VISIBLE); | |
| 382 mShowPrev = true; | |
| 383 } | |
| 384 } | |
| 385 } | |
| 386 | |
| OLD | NEW |