blob: 6b8307399218b17234edb8a430e2c930ee4dc89a [file] [log] [blame]
The Android Open Source Project792a2202009-03-03 19:32:30 -08001/*
2 * Copyright (C) 2007 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.android.music;
18
19import android.app.Activity;
The Android Open Source Project792a2202009-03-03 19:32:30 -080020import android.app.SearchManager;
The Android Open Source Project792a2202009-03-03 19:32:30 -080021import android.content.ComponentName;
The Android Open Source Project792a2202009-03-03 19:32:30 -080022import android.content.Intent;
The Android Open Source Project792a2202009-03-03 19:32:30 -080023import android.graphics.Bitmap;
Jack Hef02d3c62017-02-21 00:39:22 -050024import android.graphics.BitmapFactory;
25import android.graphics.drawable.BitmapDrawable;
The Android Open Source Project792a2202009-03-03 19:32:30 -080026import android.media.AudioManager;
Jack Hef02d3c62017-02-21 00:39:22 -050027import android.media.MediaDescription;
28import android.media.MediaMetadata;
29import android.media.browse.MediaBrowser;
30import android.media.session.MediaController;
31import android.media.session.PlaybackState;
The Android Open Source Project792a2202009-03-03 19:32:30 -080032import android.net.Uri;
33import android.os.Bundle;
The Android Open Source Project792a2202009-03-03 19:32:30 -080034import android.os.Handler;
The Android Open Source Project792a2202009-03-03 19:32:30 -080035import android.os.Message;
36import android.os.SystemClock;
37import android.provider.MediaStore;
38import android.text.Layout;
39import android.text.TextUtils.TruncateAt;
40import android.util.Log;
The Android Open Source Project792a2202009-03-03 19:32:30 -080041import android.view.MotionEvent;
The Android Open Source Project792a2202009-03-03 19:32:30 -080042import android.view.View;
43import android.view.ViewConfiguration;
44import android.view.Window;
Hansong Zhang80cfa562018-04-26 12:26:49 -070045import android.widget.ImageButton;
46import android.widget.ImageView;
47import android.widget.LinearLayout;
48import android.widget.ProgressBar;
49import android.widget.SeekBar;
The Android Open Source Project792a2202009-03-03 19:32:30 -080050import android.widget.SeekBar.OnSeekBarChangeListener;
Hansong Zhang80cfa562018-04-26 12:26:49 -070051import android.widget.TextView;
52import android.widget.Toast;
53
Jack Hef02d3c62017-02-21 00:39:22 -050054import com.android.music.utils.LogHelper;
55import com.android.music.utils.MediaIDHelper;
56import com.android.music.utils.MusicProvider;
The Android Open Source Project792a2202009-03-03 19:32:30 -080057
Jack Hef02d3c62017-02-21 00:39:22 -050058/*
59This is the Now Playing Activity
60 */
61public class MediaPlaybackActivity
62 extends Activity implements View.OnTouchListener, View.OnLongClickListener {
63 private static final String TAG = LogHelper.makeLogTag(MediaPlaybackActivity.class);
Marco Nelissen8d08ec22010-05-10 14:05:24 -070064
The Android Open Source Project792a2202009-03-03 19:32:30 -080065 private long mStartSeekPos = 0;
66 private long mLastSeekEventTime;
The Android Open Source Project792a2202009-03-03 19:32:30 -080067 private RepeatingImageButton mPrevButton;
Jack Hef02d3c62017-02-21 00:39:22 -050068 private ImageButton mPlayPauseButton;
The Android Open Source Project792a2202009-03-03 19:32:30 -080069 private RepeatingImageButton mNextButton;
70 private ImageButton mRepeatButton;
71 private ImageButton mShuffleButton;
72 private ImageButton mQueueButton;
The Android Open Source Project792a2202009-03-03 19:32:30 -080073 private int mTouchSlop;
74
Jack Hef02d3c62017-02-21 00:39:22 -050075 private ImageView mAlbumArt;
76 private TextView mCurrentTime;
77 private TextView mTotalTime;
78 private TextView mArtistName;
79 private TextView mAlbumName;
80 private TextView mTrackName;
81 private LinearLayout mTrackInfo;
82 private ProgressBar mProgress;
83 private BitmapDrawable mDefaultAlbumArt;
84 private Toast mToast;
85
86 private MediaBrowser mMediaBrowser;
87 private final Handler mHandler = new Handler();
The Android Open Source Project792a2202009-03-03 19:32:30 -080088
89 /** Called when the activity is first created. */
90 @Override
Jack Heb0fba8b2017-01-26 15:54:38 -080091 public void onCreate(Bundle icicle) {
Jack Hef02d3c62017-02-21 00:39:22 -050092 LogHelper.d(TAG, "onCreate()");
The Android Open Source Project792a2202009-03-03 19:32:30 -080093 super.onCreate(icicle);
94 setVolumeControlStream(AudioManager.STREAM_MUSIC);
95
The Android Open Source Project792a2202009-03-03 19:32:30 -080096 requestWindowFeature(Window.FEATURE_NO_TITLE);
97 setContentView(R.layout.audio_player);
98
99 mCurrentTime = (TextView) findViewById(R.id.currenttime);
100 mTotalTime = (TextView) findViewById(R.id.totaltime);
101 mProgress = (ProgressBar) findViewById(android.R.id.progress);
Jack Hef02d3c62017-02-21 00:39:22 -0500102 mAlbumArt = (ImageView) findViewById(R.id.album);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800103 mArtistName = (TextView) findViewById(R.id.artistname);
104 mAlbumName = (TextView) findViewById(R.id.albumname);
105 mTrackName = (TextView) findViewById(R.id.trackname);
Jack Hef02d3c62017-02-21 00:39:22 -0500106 mTrackInfo = (LinearLayout) findViewById(R.id.trackinfo);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800107
Jack Hef02d3c62017-02-21 00:39:22 -0500108 Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.albumart_mp_unknown);
109 mDefaultAlbumArt = new BitmapDrawable(getResources(), b);
110 // no filter or dither, it's a lot faster and we can't tell the difference
111 mDefaultAlbumArt.setFilterBitmap(false);
112 mDefaultAlbumArt.setDither(false);
113
114 /* Set metadata listeners */
Jack Heb0fba8b2017-01-26 15:54:38 -0800115 View v = (View) mArtistName.getParent();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800116 v.setOnTouchListener(this);
117 v.setOnLongClickListener(this);
Jack Heb0fba8b2017-01-26 15:54:38 -0800118 v = (View) mAlbumName.getParent();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800119 v.setOnTouchListener(this);
120 v.setOnLongClickListener(this);
Jack Heb0fba8b2017-01-26 15:54:38 -0800121 v = (View) mTrackName.getParent();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800122 v.setOnTouchListener(this);
123 v.setOnLongClickListener(this);
Jack Heb0fba8b2017-01-26 15:54:38 -0800124
Jack Hef02d3c62017-02-21 00:39:22 -0500125 /* Set button listeners */
The Android Open Source Project792a2202009-03-03 19:32:30 -0800126 mPrevButton = (RepeatingImageButton) findViewById(R.id.prev);
Jack Hef02d3c62017-02-21 00:39:22 -0500127 mPrevButton.setOnClickListener(new View.OnClickListener() {
128 public void onClick(View v) {
129 if (getMediaController() == null) return;
130 if (getMediaController().getPlaybackState().getPosition() < 2000) {
131 getMediaController().getTransportControls().skipToPrevious();
132 } else {
133 getMediaController().getTransportControls().seekTo(0);
134 getMediaController().getTransportControls().play();
135 }
136 }
137 });
138 mPrevButton.setRepeatListener(new RepeatingImageButton.RepeatListener() {
139 public void onRepeat(View v, long howlong, int repcnt) {
140 scanBackward(repcnt, howlong);
141 }
142 }, 260);
143 mPlayPauseButton = (ImageButton) findViewById(R.id.pause);
144 mPlayPauseButton.requestFocus();
145 mPlayPauseButton.setOnClickListener(new View.OnClickListener() {
146 public void onClick(View v) {
147 if (getMediaController() != null) {
148 if (getMediaController().getPlaybackState().getState()
149 != PlaybackState.STATE_PLAYING) {
150 getMediaController().getTransportControls().play();
151 } else {
152 getMediaController().getTransportControls().pause();
153 }
154 }
155 }
156 });
The Android Open Source Project792a2202009-03-03 19:32:30 -0800157 mNextButton = (RepeatingImageButton) findViewById(R.id.next);
Jack Hef02d3c62017-02-21 00:39:22 -0500158 mNextButton.setOnClickListener(new View.OnClickListener() {
159 public void onClick(View v) {
160 if (getMediaController() == null) return;
161 getMediaController().getTransportControls().skipToNext();
162 }
163 });
164 mNextButton.setRepeatListener(new RepeatingImageButton.RepeatListener() {
165 public void onRepeat(View v, long howlong, int repcnt) {
166 scanForward(repcnt, howlong);
167 }
168 }, 260);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800169 mQueueButton = (ImageButton) findViewById(R.id.curplaylist);
Jack Hef02d3c62017-02-21 00:39:22 -0500170 mQueueButton.setOnClickListener(new View.OnClickListener() {
171 public void onClick(View v) {
172 LogHelper.d(TAG, "mQueueButton onClick");
173 MediaBrowser.MediaItem parentItem = new MediaBrowser.MediaItem(
174 new MediaDescription.Builder()
175 .setMediaId(MediaIDHelper.createBrowseCategoryMediaID(
176 MediaIDHelper.MEDIA_ID_MUSICS_BY_PLAYLIST,
177 MediaIDHelper.MEDIA_ID_NOW_PLAYING))
178 .setTitle("Now Playing")
179 .build(),
180 MediaBrowser.MediaItem.FLAG_BROWSABLE);
181 Intent intent = new Intent(Intent.ACTION_PICK)
182 .setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track")
183 .putExtra(MusicUtils.TAG_WITH_TABS, false)
184 .putExtra(MusicUtils.TAG_PARENT_ITEM, parentItem);
185 startActivity(intent);
186 }
187 });
The Android Open Source Project792a2202009-03-03 19:32:30 -0800188 mShuffleButton = ((ImageButton) findViewById(R.id.shuffle));
Jack Hef02d3c62017-02-21 00:39:22 -0500189 mShuffleButton.setOnClickListener(new View.OnClickListener() {
190 public void onClick(View v) {
191 toggleShuffle();
192 }
193 });
The Android Open Source Project792a2202009-03-03 19:32:30 -0800194 mRepeatButton = ((ImageButton) findViewById(R.id.repeat));
Jack Hef02d3c62017-02-21 00:39:22 -0500195 mRepeatButton.setOnClickListener(new View.OnClickListener() {
196 public void onClick(View v) {
197 LogHelper.d(TAG, "Repeat button clicked");
198 if (getMediaController() == null) return;
199 Bundle extras = getMediaController().getExtras();
200 if (extras == null) return;
201 MediaPlaybackService.RepeatMode repeatMode =
202 MediaPlaybackService.RepeatMode
203 .values()[extras.getInt(MediaPlaybackService.REPEAT_MODE)];
204 MediaPlaybackService.RepeatMode nextRepeatMode;
205 switch (repeatMode) {
206 case REPEAT_NONE:
207 nextRepeatMode = MediaPlaybackService.RepeatMode.REPEAT_ALL;
208 showToast(R.string.repeat_all_notif);
209 break;
210 case REPEAT_ALL:
211 nextRepeatMode = MediaPlaybackService.RepeatMode.REPEAT_CURRENT;
212 showToast(R.string.repeat_current_notif);
213 break;
214 case REPEAT_CURRENT:
215 default:
216 nextRepeatMode = MediaPlaybackService.RepeatMode.REPEAT_NONE;
217 showToast(R.string.repeat_off_notif);
218 break;
219 }
220 setRepeatMode(nextRepeatMode);
221 // TODO(siyuanh): Should use a callback to register changes on service side
222 setRepeatButtonImage(nextRepeatMode);
223 }
224 });
Jack Heb0fba8b2017-01-26 15:54:38 -0800225
The Android Open Source Project792a2202009-03-03 19:32:30 -0800226 if (mProgress instanceof SeekBar) {
227 SeekBar seeker = (SeekBar) mProgress;
Jack Hef02d3c62017-02-21 00:39:22 -0500228 seeker.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
229 boolean mmFromTouch = false;
230 public void onStartTrackingTouch(SeekBar bar) {
231 mLastSeekEventTime = 0;
232 mmFromTouch = true;
233 }
234 public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
235 if (!fromuser || (getMediaController() == null)) return;
236 long now = SystemClock.elapsedRealtime();
237 if ((now - mLastSeekEventTime) > 250) {
238 mLastSeekEventTime = now;
239 long duration = getMediaController().getMetadata().getLong(
240 MediaMetadata.METADATA_KEY_DURATION);
241 long position = duration * progress / 1000;
242 getMediaController().getTransportControls().seekTo(position);
243 // trackball event, allow progress updates
244 if (!mmFromTouch) {
245 updateProgressBar();
246 }
247 }
248 }
249 public void onStopTrackingTouch(SeekBar bar) {
250 mmFromTouch = false;
251 }
252 });
253 } else {
254 LogHelper.d(TAG, "Seeking not supported");
The Android Open Source Project792a2202009-03-03 19:32:30 -0800255 }
256 mProgress.setMax(1000);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800257
258 mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
Jack Hef02d3c62017-02-21 00:39:22 -0500259
260 Log.d(TAG, "Creating MediaBrowser");
261 mMediaBrowser = new MediaBrowser(this, new ComponentName(this, MediaPlaybackService.class),
262 mConnectionCallback, null);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800263 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800264
Jack Hef02d3c62017-02-21 00:39:22 -0500265 // Receive callbacks from the MediaController. Here we update our state such as which queue
266 // is being shown, the current title and description and the PlaybackState.
267 private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() {
268
269 @Override
270 public void onSessionDestroyed() {
271 LogHelper.d(TAG, "Session destroyed. Need to fetch a new Media Session");
272 }
273
274 @Override
275 public void onPlaybackStateChanged(PlaybackState state) {
276 if (state == null) {
277 return;
278 }
279 LogHelper.d(TAG, "Received playback state change to state ", state.toString());
280 updateProgressBar();
281 setPauseButtonImage();
282 }
283
284 @Override
285 public void onMetadataChanged(MediaMetadata metadata) {
286 if (metadata == null) {
287 return;
288 }
289 LogHelper.d(TAG, "Received updated metadata: ", metadata);
290 updateTrackInfo();
291 }
292 };
293
294 private MediaBrowser.ConnectionCallback mConnectionCallback =
295 new MediaBrowser.ConnectionCallback() {
296 @Override
297 public void onConnected() {
298 Log.d(TAG, "onConnected: session token " + mMediaBrowser.getSessionToken());
299 if (mMediaBrowser.getSessionToken() == null) {
300 throw new IllegalArgumentException("No Session token");
301 }
302 MediaController mediaController = new MediaController(
303 MediaPlaybackActivity.this, mMediaBrowser.getSessionToken());
304 mediaController.registerCallback(mMediaControllerCallback);
305 MediaPlaybackActivity.this.setMediaController(mediaController);
306 mRepeatButton.setVisibility(View.VISIBLE);
307 mShuffleButton.setVisibility(View.VISIBLE);
308 mQueueButton.setVisibility(View.VISIBLE);
309 setRepeatButtonImage(null);
310 setShuffleButtonImage();
311 setPauseButtonImage();
312 updateTrackInfo();
313 mHandler.post(new Runnable() {
314 @Override
315 public void run() {
316 long delay = updateProgressBar();
317 mHandler.postDelayed(this, delay);
318 }
319 });
320 }
321
322 @Override
323 public void onConnectionFailed() {
324 Log.d(TAG, "onConnectionFailed");
325 }
326
327 @Override
328 public void onConnectionSuspended() {
329 Log.d(TAG, "onConnectionSuspended");
330 mHandler.removeCallbacksAndMessages(null);
331 MediaPlaybackActivity.this.setMediaController(null);
332 }
333 };
334
The Android Open Source Project792a2202009-03-03 19:32:30 -0800335 int mInitialX = -1;
336 int mLastX = -1;
337 int mTextWidth = 0;
338 int mViewWidth = 0;
339 boolean mDraggingLabel = false;
Jack Heb0fba8b2017-01-26 15:54:38 -0800340
The Android Open Source Project792a2202009-03-03 19:32:30 -0800341 TextView textViewForContainer(View v) {
342 View vv = v.findViewById(R.id.artistname);
343 if (vv != null) return (TextView) vv;
344 vv = v.findViewById(R.id.albumname);
345 if (vv != null) return (TextView) vv;
346 vv = v.findViewById(R.id.trackname);
347 if (vv != null) return (TextView) vv;
348 return null;
349 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800350
The Android Open Source Project792a2202009-03-03 19:32:30 -0800351 public boolean onTouch(View v, MotionEvent event) {
352 int action = event.getAction();
353 TextView tv = textViewForContainer(v);
354 if (tv == null) {
355 return false;
356 }
357 if (action == MotionEvent.ACTION_DOWN) {
358 v.setBackgroundColor(0xff606060);
359 mInitialX = mLastX = (int) event.getX();
360 mDraggingLabel = false;
Jack Heb0fba8b2017-01-26 15:54:38 -0800361 } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800362 v.setBackgroundColor(0);
363 if (mDraggingLabel) {
364 Message msg = mLabelScroller.obtainMessage(0, tv);
365 mLabelScroller.sendMessageDelayed(msg, 1000);
366 }
367 } else if (action == MotionEvent.ACTION_MOVE) {
368 if (mDraggingLabel) {
369 int scrollx = tv.getScrollX();
370 int x = (int) event.getX();
371 int delta = mLastX - x;
372 if (delta != 0) {
373 mLastX = x;
374 scrollx += delta;
375 if (scrollx > mTextWidth) {
376 // scrolled the text completely off the view to the left
377 scrollx -= mTextWidth;
378 scrollx -= mViewWidth;
379 }
380 if (scrollx < -mViewWidth) {
381 // scrolled the text completely off the view to the right
382 scrollx += mViewWidth;
383 scrollx += mTextWidth;
384 }
385 tv.scrollTo(scrollx, 0);
386 }
387 return true;
388 }
389 int delta = mInitialX - (int) event.getX();
390 if (Math.abs(delta) > mTouchSlop) {
391 // start moving
392 mLabelScroller.removeMessages(0, tv);
Jack Heb0fba8b2017-01-26 15:54:38 -0800393
The Android Open Source Project792a2202009-03-03 19:32:30 -0800394 // Only turn ellipsizing off when it's not already off, because it
395 // causes the scroll position to be reset to 0.
396 if (tv.getEllipsize() != null) {
397 tv.setEllipsize(null);
398 }
399 Layout ll = tv.getLayout();
400 // layout might be null if the text just changed, or ellipsizing
401 // was just turned off
402 if (ll == null) {
403 return false;
404 }
405 // get the non-ellipsized line width, to determine whether scrolling
406 // should even be allowed
407 mTextWidth = (int) tv.getLayout().getLineWidth(0);
408 mViewWidth = tv.getWidth();
409 if (mViewWidth > mTextWidth) {
410 tv.setEllipsize(TruncateAt.END);
411 v.cancelLongPress();
412 return false;
413 }
414 mDraggingLabel = true;
415 tv.setHorizontalFadingEdgeEnabled(true);
416 v.cancelLongPress();
417 return true;
418 }
419 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800420 return false;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800421 }
422
423 Handler mLabelScroller = new Handler() {
424 @Override
425 public void handleMessage(Message msg) {
426 TextView tv = (TextView) msg.obj;
427 int x = tv.getScrollX();
428 x = x * 3 / 4;
429 tv.scrollTo(x, 0);
430 if (x == 0) {
431 tv.setEllipsize(TruncateAt.END);
432 } else {
433 Message newmsg = obtainMessage(0, tv);
434 mLabelScroller.sendMessageDelayed(newmsg, 15);
435 }
436 }
437 };
The Android Open Source Project792a2202009-03-03 19:32:30 -0800438
Jack Heb0fba8b2017-01-26 15:54:38 -0800439 public boolean onLongClick(View view) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800440 CharSequence title = null;
441 String mime = null;
442 String query = null;
Jack Hef02d3c62017-02-21 00:39:22 -0500443 if (getMediaController() == null) {
444 LogHelper.d(TAG, "No media controller avalable yet");
Marco Nelissenf7b53ab2009-10-13 14:54:25 -0700445 return true;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800446 }
Jack Hef02d3c62017-02-21 00:39:22 -0500447 MediaMetadata metadata = getMediaController().getMetadata();
448 if (metadata == null) {
449 LogHelper.d(TAG, "No metadata avalable yet");
450 return true;
451 }
452 String artist = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
453 String album = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
454 String song = metadata.getString(MediaMetadata.METADATA_KEY_TITLE);
455 long audioid = metadata.getLong(MediaMetadata.METADATA_KEY_MEDIA_ID);
Marco Nelissenc5f5f132009-07-15 15:04:36 -0700456
Jack Hef02d3c62017-02-21 00:39:22 -0500457 if (album == null && artist == null && song != null && song.startsWith("recording")) {
458 LogHelper.d(TAG, "Item is not music");
Marco Nelissenc5f5f132009-07-15 15:04:36 -0700459 return false;
460 }
Marco Nelissen9df6e732009-09-02 13:00:49 -0700461
462 if (audioid < 0) {
463 return false;
464 }
465
Jack Heb0fba8b2017-01-26 15:54:38 -0800466 boolean knownartist = (artist != null) && !MediaStore.UNKNOWN_STRING.equals(artist);
Jack Heb0fba8b2017-01-26 15:54:38 -0800467 boolean knownalbum = (album != null) && !MediaStore.UNKNOWN_STRING.equals(album);
468
The Android Open Source Project792a2202009-03-03 19:32:30 -0800469 if (knownartist && view.equals(mArtistName.getParent())) {
470 title = artist;
Marco Nelissen756c3f52009-05-14 10:07:23 -0700471 query = artist;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800472 mime = MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE;
473 } else if (knownalbum && view.equals(mAlbumName.getParent())) {
474 title = album;
475 if (knownartist) {
476 query = artist + " " + album;
477 } else {
478 query = album;
479 }
480 mime = MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE;
481 } else if (view.equals(mTrackName.getParent()) || !knownartist || !knownalbum) {
Marco Nelissenf4d4b342010-01-04 15:01:18 -0800482 if ((song == null) || MediaStore.UNKNOWN_STRING.equals(song)) {
Andreas Huberc8398492009-04-13 16:15:57 -0700483 // A popup of the form "Search for null/'' using ..." is pretty
484 // unhelpful, plus, we won't find any way to buy it anyway.
485 return true;
486 }
487
The Android Open Source Project792a2202009-03-03 19:32:30 -0800488 title = song;
489 if (knownartist) {
490 query = artist + " " + song;
491 } else {
492 query = song;
493 }
494 mime = "audio/*"; // the specific type doesn't matter, so don't bother retrieving it
495 } else {
496 throw new RuntimeException("shouldn't be here");
497 }
498 title = getString(R.string.mediasearch, title);
499
500 Intent i = new Intent();
501 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
502 i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
503 i.putExtra(SearchManager.QUERY, query);
Jack Heb0fba8b2017-01-26 15:54:38 -0800504 if (knownartist) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800505 i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
506 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800507 if (knownalbum) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800508 i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, album);
509 }
510 i.putExtra(MediaStore.EXTRA_MEDIA_TITLE, song);
511 i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, mime);
512
513 startActivity(Intent.createChooser(i, title));
514 return true;
515 }
516
Jack Hef02d3c62017-02-21 00:39:22 -0500517 @Override
518 public void onStart() {
519 LogHelper.d(TAG, "onStart()");
520 super.onStart();
521 mMediaBrowser.connect();
522 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800523
The Android Open Source Project792a2202009-03-03 19:32:30 -0800524 @Override
525 public void onStop() {
Jack Hef02d3c62017-02-21 00:39:22 -0500526 LogHelper.d(TAG, "onStop()");
527 mMediaBrowser.disconnect();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800528 super.onStop();
529 }
530
531 @Override
The Android Open Source Project792a2202009-03-03 19:32:30 -0800532 public void onResume() {
Jack Hef02d3c62017-02-21 00:39:22 -0500533 LogHelper.d(TAG, "onResume()");
The Android Open Source Project792a2202009-03-03 19:32:30 -0800534 super.onResume();
535 updateTrackInfo();
536 setPauseButtonImage();
537 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800538
The Android Open Source Project792a2202009-03-03 19:32:30 -0800539 @Override
Jack Heb0fba8b2017-01-26 15:54:38 -0800540 public void onDestroy() {
Jack Hef02d3c62017-02-21 00:39:22 -0500541 LogHelper.d(TAG, "onDestroy()");
The Android Open Source Project792a2202009-03-03 19:32:30 -0800542 super.onDestroy();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800543 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800544
The Android Open Source Project792a2202009-03-03 19:32:30 -0800545 private void scanBackward(int repcnt, long delta) {
Jack Hef02d3c62017-02-21 00:39:22 -0500546 if (getMediaController() == null) return;
547 if (repcnt == 0) {
548 mStartSeekPos = getMediaController().getPlaybackState().getPosition();
549 mLastSeekEventTime = 0;
550 } else {
551 if (delta < 5000) {
552 // seek at 10x speed for the first 5 seconds
553 delta = delta * 10;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800554 } else {
Jack Hef02d3c62017-02-21 00:39:22 -0500555 // seek at 40x after that
556 delta = 50000 + (delta - 5000) * 40;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800557 }
Jack Hef02d3c62017-02-21 00:39:22 -0500558 long newpos = mStartSeekPos - delta;
559 if (newpos < 0) {
560 // move to previous track
561 getMediaController().getTransportControls().skipToPrevious();
562 long duration = getMediaController().getMetadata().getLong(
563 MediaMetadata.METADATA_KEY_DURATION);
564 mStartSeekPos += duration;
565 newpos += duration;
566 }
567 if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) {
568 getMediaController().getTransportControls().seekTo(newpos);
569 mLastSeekEventTime = delta;
570 }
571 updateProgressBar();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800572 }
573 }
574
575 private void scanForward(int repcnt, long delta) {
Jack Hef02d3c62017-02-21 00:39:22 -0500576 if (getMediaController() == null) return;
577 if (repcnt == 0) {
578 mStartSeekPos = getMediaController().getPlaybackState().getPosition();
579 mLastSeekEventTime = 0;
580 } else {
581 if (delta < 5000) {
582 // seek at 10x speed for the first 5 seconds
583 delta = delta * 10;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800584 } else {
Jack Hef02d3c62017-02-21 00:39:22 -0500585 // seek at 40x after that
586 delta = 50000 + (delta - 5000) * 40;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800587 }
Jack Hef02d3c62017-02-21 00:39:22 -0500588 long newpos = mStartSeekPos + delta;
589 long duration =
590 getMediaController().getMetadata().getLong(MediaMetadata.METADATA_KEY_DURATION);
591 if (newpos >= duration) {
592 // move to next track
593 getMediaController().getTransportControls().skipToNext();
594 mStartSeekPos -= duration; // is OK to go negative
595 newpos -= duration;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800596 }
Jack Hef02d3c62017-02-21 00:39:22 -0500597 if (((delta - mLastSeekEventTime) > 250) || repcnt < 0) {
598 getMediaController().getTransportControls().seekTo(newpos);
599 mLastSeekEventTime = delta;
600 }
601 updateProgressBar();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800602 }
603 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800604
The Android Open Source Project792a2202009-03-03 19:32:30 -0800605 private void toggleShuffle() {
Jack Hef02d3c62017-02-21 00:39:22 -0500606 // TODO(b/36371715): Implement shuffle for SHUFFLE_NORMAL, SHUFFLE_AUTO, SHUFFLE_NONE
607 LogHelper.d(TAG, "Shuffle not implemented yet");
608 Toast.makeText(this, "Shuffle not implemented yet", Toast.LENGTH_SHORT).show();
The Android Open Source Project792a2202009-03-03 19:32:30 -0800609 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800610
Jack Hef02d3c62017-02-21 00:39:22 -0500611 private void setRepeatMode(MediaPlaybackService.RepeatMode repeatMode) {
612 Bundle extras = new Bundle();
613 extras.putInt(MediaPlaybackService.REPEAT_MODE, repeatMode.ordinal());
614 getMediaController().getTransportControls().sendCustomAction(
615 MediaPlaybackService.CMD_REPEAT, extras);
616 }
617
618 private void setRepeatButtonImage(MediaPlaybackService.RepeatMode repeatMode) {
619 if (getMediaController() == null) return;
620 Bundle extras = getMediaController().getExtras();
621 if (extras == null) return;
622 if (repeatMode == null) {
623 repeatMode = MediaPlaybackService.RepeatMode
624 .values()[extras.getInt(MediaPlaybackService.REPEAT_MODE)];
The Android Open Source Project792a2202009-03-03 19:32:30 -0800625 }
Jack Hef02d3c62017-02-21 00:39:22 -0500626 switch (repeatMode) {
627 case REPEAT_CURRENT:
628 mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_once_btn);
629 break;
630 case REPEAT_ALL:
631 mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_all_btn);
632 break;
633 case REPEAT_NONE:
634 default:
635 mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_off_btn);
636 break;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800637 }
The Android Open Source Project792a2202009-03-03 19:32:30 -0800638 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800639
The Android Open Source Project792a2202009-03-03 19:32:30 -0800640 private void showToast(int resid) {
641 if (mToast == null) {
642 mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
643 }
644 mToast.setText(resid);
645 mToast.show();
646 }
647
The Android Open Source Project792a2202009-03-03 19:32:30 -0800648 private void setShuffleButtonImage() {
Jack Hef02d3c62017-02-21 00:39:22 -0500649 if (getMediaController() == null) return;
650 mShuffleButton.setImageResource(R.drawable.ic_mp_shuffle_off_btn);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800651 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800652
The Android Open Source Project792a2202009-03-03 19:32:30 -0800653 private void setPauseButtonImage() {
Jack Hef02d3c62017-02-21 00:39:22 -0500654 if (getMediaController() == null) {
655 return;
656 }
657 if (getMediaController().getPlaybackState().getState() != PlaybackState.STATE_PLAYING) {
658 mPlayPauseButton.setImageResource(android.R.drawable.ic_media_play);
659 } else {
660 mPlayPauseButton.setImageResource(android.R.drawable.ic_media_pause);
661 }
662 }
663
664 private long updateProgressBar() {
665 MediaController mediaController = getMediaController();
Hansong Zhang80cfa562018-04-26 12:26:49 -0700666 if (mediaController == null || mediaController.getMetadata() == null
667 || mediaController.getPlaybackState() == null) {
Jack Hef02d3c62017-02-21 00:39:22 -0500668 return 500;
669 }
Hansong Zhang80cfa562018-04-26 12:26:49 -0700670 long duration = mediaController.getMetadata().getLong(MediaMetadata.METADATA_KEY_DURATION);
Jack Hef02d3c62017-02-21 00:39:22 -0500671 long pos = mediaController.getPlaybackState().getPosition();
672 if ((pos >= 0) && (duration > 0)) {
673 mCurrentTime.setText(MusicUtils.makeTimeString(this, pos / 1000));
674 int progress = (int) (1000 * pos / duration);
675 mProgress.setProgress(progress);
676
677 if (mediaController.getPlaybackState().getState() == PlaybackState.STATE_PLAYING) {
678 mCurrentTime.setVisibility(View.VISIBLE);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800679 } else {
Jack Hef02d3c62017-02-21 00:39:22 -0500680 // blink the counter
681 int vis = mCurrentTime.getVisibility();
682 mCurrentTime.setVisibility(vis == View.INVISIBLE ? View.VISIBLE : View.INVISIBLE);
683 return 500;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800684 }
Jack Hef02d3c62017-02-21 00:39:22 -0500685 } else {
686 mCurrentTime.setText("--:--");
687 mProgress.setProgress(1000);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800688 }
Jack Hef02d3c62017-02-21 00:39:22 -0500689 // calculate the number of milliseconds until the next full second, so
690 // the counter can be updated at just the right time
691 long remaining = 1000 - (pos % 1000);
Jack Heb0fba8b2017-01-26 15:54:38 -0800692
Jack Hef02d3c62017-02-21 00:39:22 -0500693 // approximate how often we would need to refresh the slider to
694 // move it smoothly
695 int width = mProgress.getWidth();
696 if (width == 0) width = 320;
697 long smoothrefreshtime = duration / width;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800698
Jack Hef02d3c62017-02-21 00:39:22 -0500699 if (smoothrefreshtime > remaining) return remaining;
700 if (smoothrefreshtime < 20) return 20;
701 return smoothrefreshtime;
Marco Nelissenbd447b62009-06-29 14:52:05 -0700702 }
Jack Heb0fba8b2017-01-26 15:54:38 -0800703
The Android Open Source Project792a2202009-03-03 19:32:30 -0800704 private void updateTrackInfo() {
Jack Hef02d3c62017-02-21 00:39:22 -0500705 LogHelper.d(TAG, "updateTrackInfo()");
706 if (getMediaController() == null) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800707 return;
708 }
Jack Hef02d3c62017-02-21 00:39:22 -0500709 MediaMetadata metadata = getMediaController().getMetadata();
710 if (metadata == null) {
711 return;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800712 }
Jack Hef02d3c62017-02-21 00:39:22 -0500713 mTrackInfo.setVisibility(View.VISIBLE);
714 mTrackName.setText(metadata.getString(MediaMetadata.METADATA_KEY_TITLE));
715 LogHelper.d(TAG, "Track Name: ", mTrackName.getText());
716 String artistName = metadata.getString(MediaMetadata.METADATA_KEY_ARTIST);
717 if (artistName.equals(MusicProvider.UNKOWN)) {
718 artistName = getString(R.string.unknown_artist_name);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800719 }
Jack Hef02d3c62017-02-21 00:39:22 -0500720 mArtistName.setText(artistName);
721 String albumName = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM);
722 if (albumName.equals(MusicProvider.UNKOWN)) {
723 albumName = getString(R.string.unknown_album_name);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800724 }
Jack Hef02d3c62017-02-21 00:39:22 -0500725 mAlbumName.setText(albumName);
726 Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
727 if (albumArt != null) {
728 mAlbumArt.setImageBitmap(albumArt);
729 } else {
730 mAlbumArt.setImageDrawable(mDefaultAlbumArt);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800731 }
Jack Hef02d3c62017-02-21 00:39:22 -0500732 mAlbumArt.setVisibility(View.VISIBLE);
Jack Heb0fba8b2017-01-26 15:54:38 -0800733
Jack Hef02d3c62017-02-21 00:39:22 -0500734 long duration = metadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
735 mTotalTime.setText(MusicUtils.makeTimeString(this, duration / 1000));
The Android Open Source Project792a2202009-03-03 19:32:30 -0800736 }
737}