Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(497)

Side by Side Diff: third_party/android_media/java/src/org/chromium/third_party/android/media/MediaController.java

Issue 2904683002: [Cast, Android] The 2nd try to replace TransportXX classes in Cast UI (Closed)
Patch Set: Added the fix on top of the revert Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/android_media/java/res/values/colors.xml ('k') | tools/android/eclipse/.classpath » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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.session.PlaybackStateCompat;
21 import android.util.AttributeSet;
22 import android.view.LayoutInflater;
23 import android.view.View;
24 import android.view.ViewGroup;
25 import android.view.accessibility.AccessibilityEvent;
26 import android.view.accessibility.AccessibilityNodeInfo;
27 import android.widget.FrameLayout;
28 import android.widget.ImageButton;
29 import android.widget.SeekBar;
30 import android.widget.TextView;
31
32 import org.chromium.third_party.android.media.R;
33
34 import java.util.Formatter;
35 import java.util.Locale;
36
37 /**
38 * Helper for implementing media controls in an application.
39 * We use this custom version instead of {@link android.widget.MediaController} so that we can
40 * customize the look as we want. This file is taken directly from the public An droid sample for
41 * supportv4, with tiny bug fixes.
42 */
43 public class MediaController extends FrameLayout {
44 /**
45 * The interface that allows media controller to actually control media and provides some
46 * essential metadata for the UI like the current position, duration, etc.
47 */
48 public interface Delegate {
49 /**
50 * Called when the user wants to resume or start.
51 */
52 void play();
53
54 /**
55 * Called when the user wants to pause.
56 */
57 void pause();
58
59 /**
60 * Called when the user wants to seek.
61 */
62 void seekTo(long pos);
63
64 /**
65 * @return the current media duration, in milliseconds.
66 */
67 long getDuration();
68
69 /**
70 * @return the current playback position, in milliseconds.
71 */
72 long getPosition();
73
74 /**
75 * @return if the media is currently playing.
76 */
77 boolean isPlaying();
78
79 /**
80 * @return a combination of {@link PlaybackStateCompat} flags defining w hat UI elements will
81 * be available to the user.
82 */
83 long getActionFlags();
84 }
85
86 private Delegate mDelegate;
87 private Context mContext;
88 private ViewGroup mProgressGroup;
89 private SeekBar mProgressBar;
90 private TextView mEndTime, mCurrentTime;
91 private boolean mDragging;
92 private boolean mUseFastForward;
93 private boolean mListenersSet;
94 private boolean mShowNext, mShowPrev;
95 private View.OnClickListener mNextListener, mPrevListener;
96 private StringBuilder mFormatBuilder;
97 private Formatter mFormatter;
98 private ImageButton mPauseButton;
99 private ImageButton mFfwdButton;
100 private ImageButton mRewButton;
101 private ImageButton mNextButton;
102 private ImageButton mPrevButton;
103
104 public MediaController(Context context, AttributeSet attrs) {
105 super(context, attrs);
106 mContext = context;
107 mUseFastForward = true;
108 LayoutInflater inflate =
109 (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLAT ER_SERVICE);
110 inflate.inflate(R.layout.media_controller, this, true);
111 initControllerView();
112 }
113
114 public MediaController(Context context, boolean useFastForward) {
115 super(context);
116 mContext = context;
117 mUseFastForward = useFastForward;
118 }
119
120 public MediaController(Context context) {
121 this(context, true);
122 }
123
124 public void setDelegate(Delegate delegate) {
125 mDelegate = delegate;
126 updatePausePlay();
127 }
128
129 private void initControllerView() {
130 mPauseButton = (ImageButton) findViewById(R.id.pause);
131 if (mPauseButton != null) {
132 mPauseButton.requestFocus();
133 mPauseButton.setOnClickListener(mPauseListener);
134 }
135
136 mFfwdButton = (ImageButton) findViewById(R.id.ffwd);
137 if (mFfwdButton != null) {
138 mFfwdButton.setOnClickListener(mFfwdListener);
139 mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE );
140 }
141
142 mRewButton = (ImageButton) findViewById(R.id.rew);
143 if (mRewButton != null) {
144 mRewButton.setOnClickListener(mRewListener);
145 mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE) ;
146 }
147
148 // By default these are hidden. They will be enabled when setPrevNextLis teners() is called
149 mNextButton = (ImageButton) findViewById(R.id.next);
150 if (mNextButton != null && !mListenersSet) {
151 mNextButton.setVisibility(View.GONE);
152 }
153 mPrevButton = (ImageButton) findViewById(R.id.prev);
154 if (mPrevButton != null && !mListenersSet) {
155 mPrevButton.setVisibility(View.GONE);
156 }
157
158 mProgressGroup = (ViewGroup) findViewById(R.id.mediacontroller_progress_ container);
159
160 if (mProgressGroup != null) {
161 mProgressBar = (SeekBar) mProgressGroup.findViewById(R.id.mediacontr oller_progress_bar);
162 if (mProgressBar != null) {
163 mProgressBar.setOnSeekBarChangeListener(mSeekListener);
164 mProgressBar.setMax(1000);
165 }
166 }
167
168 mEndTime = (TextView) findViewById(R.id.time);
169 mCurrentTime = (TextView) findViewById(R.id.time_current);
170 mFormatBuilder = new StringBuilder();
171 mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());
172
173 installPrevNextListeners();
174 }
175
176 /**
177 * Disable pause or seek buttons if the stream cannot be paused or seeked.
178 * This requires the control interface to be a MediaPlayerControlExt
179 */
180 void updateButtons() {
181 if (mDelegate == null) return;
182
183 long flags = mDelegate.getActionFlags();
184 boolean enabled = isEnabled();
185 if (mPauseButton != null) {
186 boolean needPlayPauseButton = (flags & PlaybackStateCompat.ACTION_PL AY) != 0
187 || (flags & PlaybackStateCompat.ACTION_PAUSE) != 0;
188 mPauseButton.setEnabled(enabled && needPlayPauseButton);
189 }
190 if (mRewButton != null) {
191 mRewButton.setEnabled(enabled && (flags & PlaybackStateCompat.ACTION _REWIND) != 0);
192 }
193 if (mFfwdButton != null) {
194 mFfwdButton.setEnabled(
195 enabled && (flags & PlaybackStateCompat.ACTION_FAST_FORWARD) != 0);
196 }
197 if (mPrevButton != null) {
198 mShowPrev =
199 (flags & PlaybackStateCompat.ACTION_SKIP_TO_NEXT) != 0 || mP revListener != null;
200 mPrevButton.setEnabled(enabled && mShowPrev);
201 }
202 if (mNextButton != null) {
203 mShowNext = (flags & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) != 0
204 || mNextListener != null;
205 mNextButton.setEnabled(enabled && mShowNext);
206 }
207 }
208
209 public void refresh() {
210 updateProgress();
211 updateButtons();
212 updatePausePlay();
213 }
214
215 private String stringForTime(int timeMs) {
216 int totalSeconds = timeMs / 1000;
217
218 int seconds = totalSeconds % 60;
219 int minutes = (totalSeconds / 60) % 60;
220 int hours = totalSeconds / 3600;
221
222 mFormatBuilder.setLength(0);
223 if (hours > 0) {
224 return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).to String();
225 } else {
226 return mFormatter.format("%02d:%02d", minutes, seconds).toString();
227 }
228 }
229
230 public long updateProgress() {
231 if (mDelegate == null || mDragging) return 0;
232
233 long position = mDelegate.getPosition();
234 long duration = mDelegate.getDuration();
235 if (duration <= 0) {
236 // If there is no valid duration, hide the progress bar and time ind icators.
237 if (mProgressGroup != null) mProgressGroup.setVisibility(View.INVISI BLE);
238 } else if (mProgressBar != null) {
239 if (mProgressGroup != null) mProgressGroup.setVisibility(View.VISIBL E);
240 // use long to avoid overflow
241 long pos = 1000L * position / duration;
242 mProgressBar.setProgress((int) pos);
243 mProgressBar.setSecondaryProgress((int) pos);
244 }
245
246 if (mEndTime != null) mEndTime.setText(stringForTime((int) duration));
247 if (mCurrentTime != null) mCurrentTime.setText(stringForTime((int) posit ion));
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 (mDelegate == null || mPauseButton == null) return;
261
262 if (mDelegate.isPlaying()) {
263 mPauseButton.setImageResource(android.R.drawable.ic_media_pause);
264 } else {
265 mPauseButton.setImageResource(android.R.drawable.ic_media_play);
266 }
267 }
268
269 private void doPauseResume() {
270 if (mDelegate == null) return;
271
272 if (mDelegate.isPlaying()) {
273 mDelegate.pause();
274 } else {
275 mDelegate.play();
276 }
277 updatePausePlay();
278 }
279
280 // There are two scenarios that can trigger the seekbar listener to trigger:
281 //
282 // The first is the user using the touchpad to adjust the posititon of the
283 // seekbar's thumb. In this case onStartTrackingTouch is called followed by
284 // a number of onProgressChanged notifications, concluded by onStopTrackingT ouch.
285 // We're setting the field "mDragging" to true for the duration of the dragg ing
286 // session to avoid jumps in the position in case of ongoing playback.
287 //
288 // The second scenario involves the user operating the scroll ball, in this
289 // case there WON'T BE onStartTrackingTouch/onStopTrackingTouch notification s,
290 // we will simply apply the updated position without suspending regular upda tes.
291 private SeekBar.OnSeekBarChangeListener mSeekListener = new SeekBar.OnSeekBa rChangeListener() {
292 @Override
293 public void onStartTrackingTouch(SeekBar bar) {
294 mDragging = true;
295 }
296
297 @Override
298 public void onProgressChanged(SeekBar bar, int progress, boolean fromuse r) {
299 if (mDelegate == null) return;
300
301 if (!fromuser) {
302 // We're not interested in programmatically generated changes to
303 // the progress bar's position.
304 return;
305 }
306
307 long duration = mDelegate.getDuration();
308 long newposition = (duration * progress) / 1000L;
309 mDelegate.seekTo(newposition);
310 if (mCurrentTime != null) mCurrentTime.setText(stringForTime((int) n ewposition));
311 }
312
313 @Override
314 public void onStopTrackingTouch(SeekBar bar) {
315 mDragging = false;
316 updateProgress();
317 updatePausePlay();
318 }
319 };
320
321 @Override
322 public void setEnabled(boolean enabled) {
323 super.setEnabled(enabled);
324 updateButtons();
325 }
326
327 @Override
328 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
329 super.onInitializeAccessibilityEvent(event);
330 event.setClassName(MediaController.class.getName());
331 }
332
333 @Override
334 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
335 super.onInitializeAccessibilityNodeInfo(info);
336 info.setClassName(MediaController.class.getName());
337 }
338
339 private View.OnClickListener mRewListener = new View.OnClickListener() {
340 @Override
341 public void onClick(View v) {
342 if (mDelegate == null) return;
343
344 long pos = mDelegate.getPosition();
345 pos -= 5000; // milliseconds
346 mDelegate.seekTo(pos);
347 updateProgress();
348 }
349 };
350
351 private View.OnClickListener mFfwdListener = new View.OnClickListener() {
352 @Override
353 public void onClick(View v) {
354 if (mDelegate == null) return;
355
356 long pos = mDelegate.getPosition();
357 pos += 15000; // milliseconds
358 mDelegate.seekTo(pos);
359 updateProgress();
360 }
361 };
362
363 private void installPrevNextListeners() {
364 if (mNextButton != null) {
365 mNextButton.setOnClickListener(mNextListener);
366 mNextButton.setEnabled(mShowNext);
367 }
368
369 if (mPrevButton != null) {
370 mPrevButton.setOnClickListener(mPrevListener);
371 mPrevButton.setEnabled(mShowPrev);
372 }
373 }
374
375 public void setPrevNextListeners(View.OnClickListener next, View.OnClickList ener prev) {
376 mNextListener = next;
377 mPrevListener = prev;
378 mListenersSet = true;
379
380 installPrevNextListeners();
381
382 if (mNextButton != null) {
383 mNextButton.setVisibility(View.VISIBLE);
384 mShowNext = true;
385 }
386 if (mPrevButton != null) {
387 mPrevButton.setVisibility(View.VISIBLE);
388 mShowPrev = true;
389 }
390 }
391 }
OLDNEW
« no previous file with comments | « third_party/android_media/java/res/values/colors.xml ('k') | tools/android/eclipse/.classpath » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698