blob: b8612fea88e07d5d18710156f6738aca82aa929c [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;
20import android.app.AlertDialog;
21import android.app.SearchManager;
22import android.content.BroadcastReceiver;
23import android.content.ComponentName;
Marco Nelissenb7841ac2009-05-05 14:36:25 -070024import android.content.ContentResolver;
The Android Open Source Project792a2202009-03-03 19:32:30 -080025import android.content.Context;
26import android.content.DialogInterface;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.ServiceConnection;
Marco Nelissenb7841ac2009-05-05 14:36:25 -070030import android.content.res.Configuration;
31import android.content.res.Resources;
The Android Open Source Project792a2202009-03-03 19:32:30 -080032import android.graphics.Bitmap;
33import android.media.AudioManager;
34import android.media.MediaFile;
35import android.net.Uri;
36import android.os.Bundle;
37import android.os.RemoteException;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Looper;
41import android.os.Message;
42import android.os.SystemClock;
43import android.provider.MediaStore;
44import android.text.Layout;
45import android.text.TextUtils.TruncateAt;
46import android.util.Log;
47import android.view.KeyEvent;
48import android.view.Menu;
49import android.view.MenuItem;
50import android.view.MotionEvent;
51import android.view.SubMenu;
52import android.view.View;
53import android.view.ViewConfiguration;
54import android.view.Window;
55import android.widget.ImageButton;
56import android.widget.ImageView;
57import android.widget.ProgressBar;
58import android.widget.SeekBar;
59import android.widget.TextView;
60import android.widget.Toast;
61import android.widget.SeekBar.OnSeekBarChangeListener;
62
63
64public class MediaPlaybackActivity extends Activity implements MusicUtils.Defs,
65 View.OnTouchListener, View.OnLongClickListener
66{
67 private static final int USE_AS_RINGTONE = CHILD_MENU_BASE;
68
69 private boolean mOneShot = false;
70 private boolean mSeeking = false;
Marco Nelissenb7841ac2009-05-05 14:36:25 -070071 private boolean mDeviceHasNoDpad;
The Android Open Source Project792a2202009-03-03 19:32:30 -080072 private long mStartSeekPos = 0;
73 private long mLastSeekEventTime;
74 private IMediaPlaybackService mService = null;
75 private RepeatingImageButton mPrevButton;
76 private ImageButton mPauseButton;
77 private RepeatingImageButton mNextButton;
78 private ImageButton mRepeatButton;
79 private ImageButton mShuffleButton;
80 private ImageButton mQueueButton;
81 private Worker mAlbumArtWorker;
82 private AlbumArtHandler mAlbumArtHandler;
83 private Toast mToast;
The Android Open Source Project792a2202009-03-03 19:32:30 -080084 private int mTouchSlop;
85
86 public MediaPlaybackActivity()
87 {
88 }
89
90 /** Called when the activity is first created. */
91 @Override
92 public void onCreate(Bundle icicle)
93 {
94 super.onCreate(icicle);
95 setVolumeControlStream(AudioManager.STREAM_MUSIC);
96
97 mAlbumArtWorker = new Worker("album art worker");
98 mAlbumArtHandler = new AlbumArtHandler(mAlbumArtWorker.getLooper());
99
100 requestWindowFeature(Window.FEATURE_NO_TITLE);
101 setContentView(R.layout.audio_player);
102
103 mCurrentTime = (TextView) findViewById(R.id.currenttime);
104 mTotalTime = (TextView) findViewById(R.id.totaltime);
105 mProgress = (ProgressBar) findViewById(android.R.id.progress);
106 mAlbum = (ImageView) findViewById(R.id.album);
107 mArtistName = (TextView) findViewById(R.id.artistname);
108 mAlbumName = (TextView) findViewById(R.id.albumname);
109 mTrackName = (TextView) findViewById(R.id.trackname);
110
111 View v = (View)mArtistName.getParent();
112 v.setOnTouchListener(this);
113 v.setOnLongClickListener(this);
114
115 v = (View)mAlbumName.getParent();
116 v.setOnTouchListener(this);
117 v.setOnLongClickListener(this);
118
119 v = (View)mTrackName.getParent();
120 v.setOnTouchListener(this);
121 v.setOnLongClickListener(this);
122
123 mPrevButton = (RepeatingImageButton) findViewById(R.id.prev);
124 mPrevButton.setOnClickListener(mPrevListener);
125 mPrevButton.setRepeatListener(mRewListener, 260);
126 mPauseButton = (ImageButton) findViewById(R.id.pause);
127 mPauseButton.requestFocus();
128 mPauseButton.setOnClickListener(mPauseListener);
129 mNextButton = (RepeatingImageButton) findViewById(R.id.next);
130 mNextButton.setOnClickListener(mNextListener);
131 mNextButton.setRepeatListener(mFfwdListener, 260);
132 seekmethod = 1;
133
Marco Nelissenb7841ac2009-05-05 14:36:25 -0700134 mDeviceHasNoDpad = (getResources().getConfiguration().navigation !=
135 Configuration.NAVIGATION_DPAD);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800136
137 mQueueButton = (ImageButton) findViewById(R.id.curplaylist);
138 mQueueButton.setOnClickListener(mQueueListener);
139 mShuffleButton = ((ImageButton) findViewById(R.id.shuffle));
140 mShuffleButton.setOnClickListener(mShuffleListener);
141 mRepeatButton = ((ImageButton) findViewById(R.id.repeat));
142 mRepeatButton.setOnClickListener(mRepeatListener);
143
144 if (mProgress instanceof SeekBar) {
145 SeekBar seeker = (SeekBar) mProgress;
146 seeker.setOnSeekBarChangeListener(mSeekListener);
147 }
148 mProgress.setMax(1000);
149
150 if (icicle != null) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800151 mOneShot = icicle.getBoolean("oneshot");
152 } else {
153 mOneShot = getIntent().getBooleanExtra("oneshot", false);
154 }
155
156 mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
157 }
158
159 int mInitialX = -1;
160 int mLastX = -1;
161 int mTextWidth = 0;
162 int mViewWidth = 0;
163 boolean mDraggingLabel = false;
164
165 TextView textViewForContainer(View v) {
166 View vv = v.findViewById(R.id.artistname);
167 if (vv != null) return (TextView) vv;
168 vv = v.findViewById(R.id.albumname);
169 if (vv != null) return (TextView) vv;
170 vv = v.findViewById(R.id.trackname);
171 if (vv != null) return (TextView) vv;
172 return null;
173 }
174
175 public boolean onTouch(View v, MotionEvent event) {
176 int action = event.getAction();
177 TextView tv = textViewForContainer(v);
178 if (tv == null) {
179 return false;
180 }
181 if (action == MotionEvent.ACTION_DOWN) {
182 v.setBackgroundColor(0xff606060);
183 mInitialX = mLastX = (int) event.getX();
184 mDraggingLabel = false;
185 } else if (action == MotionEvent.ACTION_UP ||
186 action == MotionEvent.ACTION_CANCEL) {
187 v.setBackgroundColor(0);
188 if (mDraggingLabel) {
189 Message msg = mLabelScroller.obtainMessage(0, tv);
190 mLabelScroller.sendMessageDelayed(msg, 1000);
191 }
192 } else if (action == MotionEvent.ACTION_MOVE) {
193 if (mDraggingLabel) {
194 int scrollx = tv.getScrollX();
195 int x = (int) event.getX();
196 int delta = mLastX - x;
197 if (delta != 0) {
198 mLastX = x;
199 scrollx += delta;
200 if (scrollx > mTextWidth) {
201 // scrolled the text completely off the view to the left
202 scrollx -= mTextWidth;
203 scrollx -= mViewWidth;
204 }
205 if (scrollx < -mViewWidth) {
206 // scrolled the text completely off the view to the right
207 scrollx += mViewWidth;
208 scrollx += mTextWidth;
209 }
210 tv.scrollTo(scrollx, 0);
211 }
212 return true;
213 }
214 int delta = mInitialX - (int) event.getX();
215 if (Math.abs(delta) > mTouchSlop) {
216 // start moving
217 mLabelScroller.removeMessages(0, tv);
218
219 // Only turn ellipsizing off when it's not already off, because it
220 // causes the scroll position to be reset to 0.
221 if (tv.getEllipsize() != null) {
222 tv.setEllipsize(null);
223 }
224 Layout ll = tv.getLayout();
225 // layout might be null if the text just changed, or ellipsizing
226 // was just turned off
227 if (ll == null) {
228 return false;
229 }
230 // get the non-ellipsized line width, to determine whether scrolling
231 // should even be allowed
232 mTextWidth = (int) tv.getLayout().getLineWidth(0);
233 mViewWidth = tv.getWidth();
234 if (mViewWidth > mTextWidth) {
235 tv.setEllipsize(TruncateAt.END);
236 v.cancelLongPress();
237 return false;
238 }
239 mDraggingLabel = true;
240 tv.setHorizontalFadingEdgeEnabled(true);
241 v.cancelLongPress();
242 return true;
243 }
244 }
245 return false;
246 }
247
248 Handler mLabelScroller = new Handler() {
249 @Override
250 public void handleMessage(Message msg) {
251 TextView tv = (TextView) msg.obj;
252 int x = tv.getScrollX();
253 x = x * 3 / 4;
254 tv.scrollTo(x, 0);
255 if (x == 0) {
256 tv.setEllipsize(TruncateAt.END);
257 } else {
258 Message newmsg = obtainMessage(0, tv);
259 mLabelScroller.sendMessageDelayed(newmsg, 15);
260 }
261 }
262 };
263
264 public boolean onLongClick(View view) {
265
266 CharSequence title = null;
267 String mime = null;
268 String query = null;
269 String artist;
270 String album;
271 String song;
272
273 try {
274 artist = mService.getArtistName();
275 album = mService.getAlbumName();
276 song = mService.getTrackName();
277 } catch (RemoteException ex) {
278 return true;
279 }
280
Andreas Huberc8398492009-04-13 16:15:57 -0700281 boolean knownartist =
282 (artist != null) && !MediaFile.UNKNOWN_STRING.equals(artist);
283
284 boolean knownalbum =
285 (album != null) && !MediaFile.UNKNOWN_STRING.equals(album);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800286
287 if (knownartist && view.equals(mArtistName.getParent())) {
288 title = artist;
Marco Nelissen756c3f52009-05-14 10:07:23 -0700289 query = artist;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800290 mime = MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE;
291 } else if (knownalbum && view.equals(mAlbumName.getParent())) {
292 title = album;
293 if (knownartist) {
294 query = artist + " " + album;
295 } else {
296 query = album;
297 }
298 mime = MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE;
299 } else if (view.equals(mTrackName.getParent()) || !knownartist || !knownalbum) {
Andreas Huberc8398492009-04-13 16:15:57 -0700300 if ((song == null) || MediaFile.UNKNOWN_STRING.equals(song)) {
301 // A popup of the form "Search for null/'' using ..." is pretty
302 // unhelpful, plus, we won't find any way to buy it anyway.
303 return true;
304 }
305
The Android Open Source Project792a2202009-03-03 19:32:30 -0800306 title = song;
307 if (knownartist) {
308 query = artist + " " + song;
309 } else {
310 query = song;
311 }
312 mime = "audio/*"; // the specific type doesn't matter, so don't bother retrieving it
313 } else {
314 throw new RuntimeException("shouldn't be here");
315 }
316 title = getString(R.string.mediasearch, title);
317
318 Intent i = new Intent();
319 i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
320 i.setAction(MediaStore.INTENT_ACTION_MEDIA_SEARCH);
321 i.putExtra(SearchManager.QUERY, query);
322 if(knownartist) {
323 i.putExtra(MediaStore.EXTRA_MEDIA_ARTIST, artist);
324 }
325 if(knownalbum) {
326 i.putExtra(MediaStore.EXTRA_MEDIA_ALBUM, album);
327 }
328 i.putExtra(MediaStore.EXTRA_MEDIA_TITLE, song);
329 i.putExtra(MediaStore.EXTRA_MEDIA_FOCUS, mime);
330
331 startActivity(Intent.createChooser(i, title));
332 return true;
333 }
334
335 private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
336 public void onStartTrackingTouch(SeekBar bar) {
337 mLastSeekEventTime = 0;
The Android Open Source Projectc85f6772009-03-18 17:39:48 -0700338 mFromTouch = true;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800339 }
The Android Open Source Projectc85f6772009-03-18 17:39:48 -0700340 public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
341 if (!fromuser || (mService == null)) return;
342 long now = SystemClock.elapsedRealtime();
343 if ((now - mLastSeekEventTime) > 250) {
344 mLastSeekEventTime = now;
345 mPosOverride = mDuration * progress / 1000;
346 try {
347 mService.seek(mPosOverride);
348 } catch (RemoteException ex) {
349 }
350
351 // trackball event, allow progress updates
352 if (!mFromTouch) {
353 refreshNow();
354 mPosOverride = -1;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800355 }
356 }
357 }
358 public void onStopTrackingTouch(SeekBar bar) {
359 mPosOverride = -1;
The Android Open Source Projectc85f6772009-03-18 17:39:48 -0700360 mFromTouch = false;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800361 }
362 };
363
364 private View.OnClickListener mQueueListener = new View.OnClickListener() {
365 public void onClick(View v) {
366 startActivity(
367 new Intent(Intent.ACTION_EDIT)
368 .setDataAndType(Uri.EMPTY, "vnd.android.cursor.dir/track")
369 .putExtra("playlist", "nowplaying")
370 );
371 }
372 };
373
374 private View.OnClickListener mShuffleListener = new View.OnClickListener() {
375 public void onClick(View v) {
376 toggleShuffle();
377 }
378 };
379
380 private View.OnClickListener mRepeatListener = new View.OnClickListener() {
381 public void onClick(View v) {
382 cycleRepeat();
383 }
384 };
385
386 private View.OnClickListener mPauseListener = new View.OnClickListener() {
387 public void onClick(View v) {
388 doPauseResume();
389 }
390 };
391
392 private View.OnClickListener mPrevListener = new View.OnClickListener() {
393 public void onClick(View v) {
394 if (mService == null) return;
395 try {
396 if (mService.position() < 2000) {
397 mService.prev();
398 } else {
399 mService.seek(0);
400 mService.play();
401 }
402 } catch (RemoteException ex) {
403 }
404 }
405 };
406
407 private View.OnClickListener mNextListener = new View.OnClickListener() {
408 public void onClick(View v) {
409 if (mService == null) return;
410 try {
411 mService.next();
412 } catch (RemoteException ex) {
413 }
414 }
415 };
416
417 private RepeatingImageButton.RepeatListener mRewListener =
418 new RepeatingImageButton.RepeatListener() {
419 public void onRepeat(View v, long howlong, int repcnt) {
420 scanBackward(repcnt, howlong);
421 }
422 };
423
424 private RepeatingImageButton.RepeatListener mFfwdListener =
425 new RepeatingImageButton.RepeatListener() {
426 public void onRepeat(View v, long howlong, int repcnt) {
427 scanForward(repcnt, howlong);
428 }
429 };
430
431 @Override
432 public void onStop() {
433 paused = true;
434 if (mService != null && mOneShot && getChangingConfigurations() == 0) {
435 try {
436 mService.stop();
437 } catch (RemoteException ex) {
438 }
439 }
440 mHandler.removeMessages(REFRESH);
441 unregisterReceiver(mStatusListener);
442 MusicUtils.unbindFromService(this);
Marco Nelissen47b989b2009-05-27 12:57:06 -0700443 mService = null;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800444 super.onStop();
445 }
446
447 @Override
448 public void onSaveInstanceState(Bundle outState) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800449 outState.putBoolean("oneshot", mOneShot);
450 super.onSaveInstanceState(outState);
451 }
452
453 @Override
454 public void onStart() {
455 super.onStart();
456 paused = false;
457
458 if (false == MusicUtils.bindToService(this, osc)) {
459 // something went wrong
460 mHandler.sendEmptyMessage(QUIT);
461 }
462
463 IntentFilter f = new IntentFilter();
464 f.addAction(MediaPlaybackService.PLAYSTATE_CHANGED);
465 f.addAction(MediaPlaybackService.META_CHANGED);
466 f.addAction(MediaPlaybackService.PLAYBACK_COMPLETE);
467 registerReceiver(mStatusListener, new IntentFilter(f));
468 updateTrackInfo();
469 long next = refreshNow();
470 queueNextRefresh(next);
471 }
472
473 @Override
474 public void onNewIntent(Intent intent) {
475 setIntent(intent);
476 mOneShot = intent.getBooleanExtra("oneshot", false);
477 }
478
479 @Override
480 public void onResume() {
481 super.onResume();
482 updateTrackInfo();
483 setPauseButtonImage();
484 }
485
486 @Override
487 public void onDestroy()
488 {
489 mAlbumArtWorker.quit();
490 super.onDestroy();
491 //System.out.println("***************** playback activity onDestroy\n");
492 }
493
494 @Override
495 public boolean onCreateOptionsMenu(Menu menu) {
496 super.onCreateOptionsMenu(menu);
Marco Nelissen9d989c42009-05-13 15:31:23 -0700497 // Don't show the menu items if we got launched by path/filedescriptor, or
498 // if we're in one shot mode. In most cases, these menu items are not
499 // useful in those modes, so for consistency we never show them in these
500 // modes, instead of tailoring them to the specific file being played.
501 if (MusicUtils.getCurrentAudioId() >= 0 && !mOneShot) {
502 menu.add(0, GOTO_START, 0, R.string.goto_start).setIcon(R.drawable.ic_menu_music_library);
503 menu.add(0, PARTY_SHUFFLE, 0, R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu()
The Android Open Source Project792a2202009-03-03 19:32:30 -0800504 SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0,
505 R.string.add_to_playlist).setIcon(android.R.drawable.ic_menu_add);
506 MusicUtils.makePlaylistMenu(this, sub);
507 menu.add(0, USE_AS_RINGTONE, 0, R.string.ringtone_menu_short).setIcon(R.drawable.ic_menu_set_as_ringtone);
508 menu.add(0, DELETE_ITEM, 0, R.string.delete_item).setIcon(R.drawable.ic_menu_delete);
Marco Nelissen9d989c42009-05-13 15:31:23 -0700509 return true;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800510 }
Marco Nelissen9d989c42009-05-13 15:31:23 -0700511 return false;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800512 }
513
514 @Override
515 public boolean onPrepareOptionsMenu(Menu menu) {
516 MenuItem item = menu.findItem(PARTY_SHUFFLE);
517 if (item != null) {
518 int shuffle = MusicUtils.getCurrentShuffleMode();
519 if (shuffle == MediaPlaybackService.SHUFFLE_AUTO) {
520 item.setIcon(R.drawable.ic_menu_party_shuffle);
521 item.setTitle(R.string.party_shuffle_off);
522 } else {
523 item.setIcon(R.drawable.ic_menu_party_shuffle);
524 item.setTitle(R.string.party_shuffle);
525 }
526 }
527 return true;
528 }
529
530 @Override
531 public boolean onOptionsItemSelected(MenuItem item) {
532 Intent intent;
533 try {
534 switch (item.getItemId()) {
535 case GOTO_START:
536 intent = new Intent();
537 intent.setClass(this, MusicBrowserActivity.class);
538 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
539 | Intent.FLAG_ACTIVITY_CLEAR_TOP);
540 startActivity(intent);
541 break;
542 case USE_AS_RINGTONE: {
543 // Set the system setting to make this the current ringtone
544 if (mService != null) {
545 MusicUtils.setRingtone(this, mService.getAudioId());
546 }
547 return true;
548 }
549 case PARTY_SHUFFLE:
550 if (mService != null) {
551 int shuffle = mService.getShuffleMode();
552 if (shuffle == MediaPlaybackService.SHUFFLE_AUTO) {
553 mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE);
554 } else {
555 mService.setShuffleMode(MediaPlaybackService.SHUFFLE_AUTO);
556 }
557 }
558 setShuffleButtonImage();
559 break;
560
561 case NEW_PLAYLIST: {
562 intent = new Intent();
563 intent.setClass(this, CreatePlaylist.class);
564 startActivityForResult(intent, NEW_PLAYLIST);
565 return true;
566 }
567
568 case PLAYLIST_SELECTED: {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700569 long [] list = new long[1];
The Android Open Source Project792a2202009-03-03 19:32:30 -0800570 list[0] = MusicUtils.getCurrentAudioId();
Marco Nelissenbd447b62009-06-29 14:52:05 -0700571 long playlist = item.getIntent().getLongExtra("playlist", 0);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800572 MusicUtils.addToPlaylist(this, list, playlist);
573 return true;
574 }
575
576 case DELETE_ITEM: {
577 if (mService != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700578 long [] list = new long[1];
The Android Open Source Project792a2202009-03-03 19:32:30 -0800579 list[0] = MusicUtils.getCurrentAudioId();
580 Bundle b = new Bundle();
581 b.putString("description", getString(R.string.delete_song_desc,
582 mService.getTrackName()));
Marco Nelissenbd447b62009-06-29 14:52:05 -0700583 b.putLongArray("items", list);
The Android Open Source Project792a2202009-03-03 19:32:30 -0800584 intent = new Intent();
585 intent.setClass(this, DeleteItems.class);
586 intent.putExtras(b);
587 startActivityForResult(intent, -1);
588 }
589 return true;
590 }
591 }
592 } catch (RemoteException ex) {
593 }
594 return super.onOptionsItemSelected(item);
595 }
596
597 @Override
598 protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
599 if (resultCode != RESULT_OK) {
600 return;
601 }
602 switch (requestCode) {
603 case NEW_PLAYLIST:
604 Uri uri = intent.getData();
605 if (uri != null) {
Marco Nelissenbd447b62009-06-29 14:52:05 -0700606 long [] list = new long[1];
The Android Open Source Project792a2202009-03-03 19:32:30 -0800607 list[0] = MusicUtils.getCurrentAudioId();
608 int playlist = Integer.parseInt(uri.getLastPathSegment());
609 MusicUtils.addToPlaylist(this, list, playlist);
610 }
611 break;
612 }
613 }
614 private final int keyboard[][] = {
615 {
616 KeyEvent.KEYCODE_Q,
617 KeyEvent.KEYCODE_W,
618 KeyEvent.KEYCODE_E,
619 KeyEvent.KEYCODE_R,
620 KeyEvent.KEYCODE_T,
621 KeyEvent.KEYCODE_Y,
622 KeyEvent.KEYCODE_U,
623 KeyEvent.KEYCODE_I,
624 KeyEvent.KEYCODE_O,
625 KeyEvent.KEYCODE_P,
626 },
627 {
628 KeyEvent.KEYCODE_A,
629 KeyEvent.KEYCODE_S,
630 KeyEvent.KEYCODE_D,
631 KeyEvent.KEYCODE_F,
632 KeyEvent.KEYCODE_G,
633 KeyEvent.KEYCODE_H,
634 KeyEvent.KEYCODE_J,
635 KeyEvent.KEYCODE_K,
636 KeyEvent.KEYCODE_L,
637 KeyEvent.KEYCODE_DEL,
638 },
639 {
640 KeyEvent.KEYCODE_Z,
641 KeyEvent.KEYCODE_X,
642 KeyEvent.KEYCODE_C,
643 KeyEvent.KEYCODE_V,
644 KeyEvent.KEYCODE_B,
645 KeyEvent.KEYCODE_N,
646 KeyEvent.KEYCODE_M,
647 KeyEvent.KEYCODE_COMMA,
648 KeyEvent.KEYCODE_PERIOD,
649 KeyEvent.KEYCODE_ENTER
650 }
651
652 };
653
654 private int lastX;
655 private int lastY;
656
657 private boolean seekMethod1(int keyCode)
658 {
659 for(int x=0;x<10;x++) {
660 for(int y=0;y<3;y++) {
661 if(keyboard[y][x] == keyCode) {
662 int dir = 0;
663 // top row
664 if(x == lastX && y == lastY) dir = 0;
665 else if (y == 0 && lastY == 0 && x > lastX) dir = 1;
666 else if (y == 0 && lastY == 0 && x < lastX) dir = -1;
667 // bottom row
668 else if (y == 2 && lastY == 2 && x > lastX) dir = -1;
669 else if (y == 2 && lastY == 2 && x < lastX) dir = 1;
670 // moving up
671 else if (y < lastY && x <= 4) dir = 1;
672 else if (y < lastY && x >= 5) dir = -1;
673 // moving down
674 else if (y > lastY && x <= 4) dir = -1;
675 else if (y > lastY && x >= 5) dir = 1;
676 lastX = x;
677 lastY = y;
678 try {
679 mService.seek(mService.position() + dir * 5);
680 } catch (RemoteException ex) {
681 }
682 refreshNow();
683 return true;
684 }
685 }
686 }
687 lastX = -1;
688 lastY = -1;
689 return false;
690 }
691
692 private boolean seekMethod2(int keyCode)
693 {
694 if (mService == null) return false;
695 for(int i=0;i<10;i++) {
696 if(keyboard[0][i] == keyCode) {
697 int seekpercentage = 100*i/10;
698 try {
699 mService.seek(mService.duration() * seekpercentage / 100);
700 } catch (RemoteException ex) {
701 }
702 refreshNow();
703 return true;
704 }
705 }
706 return false;
707 }
708
709 @Override
710 public boolean onKeyUp(int keyCode, KeyEvent event) {
711 try {
712 switch(keyCode)
713 {
714 case KeyEvent.KEYCODE_DPAD_LEFT:
Marco Nelissenb7841ac2009-05-05 14:36:25 -0700715 if (mDeviceHasNoDpad) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800716 break;
717 }
718 if (mService != null) {
719 if (!mSeeking && mStartSeekPos >= 0) {
720 mPauseButton.requestFocus();
721 if (mStartSeekPos < 1000) {
722 mService.prev();
723 } else {
724 mService.seek(0);
725 }
726 } else {
727 scanBackward(-1, event.getEventTime() - event.getDownTime());
728 mPauseButton.requestFocus();
729 mStartSeekPos = -1;
730 }
731 }
732 mSeeking = false;
733 mPosOverride = -1;
734 return true;
735 case KeyEvent.KEYCODE_DPAD_RIGHT:
Marco Nelissenb7841ac2009-05-05 14:36:25 -0700736 if (mDeviceHasNoDpad) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800737 break;
738 }
739 if (mService != null) {
740 if (!mSeeking && mStartSeekPos >= 0) {
741 mPauseButton.requestFocus();
742 mService.next();
743 } else {
744 scanForward(-1, event.getEventTime() - event.getDownTime());
745 mPauseButton.requestFocus();
746 mStartSeekPos = -1;
747 }
748 }
749 mSeeking = false;
750 mPosOverride = -1;
751 return true;
752 }
753 } catch (RemoteException ex) {
754 }
755 return super.onKeyUp(keyCode, event);
756 }
757
758 @Override
759 public boolean onKeyDown(int keyCode, KeyEvent event)
760 {
761 int direction = -1;
762 int repcnt = event.getRepeatCount();
763
764 if((seekmethod==0)?seekMethod1(keyCode):seekMethod2(keyCode))
765 return true;
766
767 switch(keyCode)
768 {
769/*
770 // image scale
771 case KeyEvent.KEYCODE_Q: av.adjustParams(-0.05, 0.0, 0.0, 0.0, 0.0,-1.0); break;
772 case KeyEvent.KEYCODE_E: av.adjustParams( 0.05, 0.0, 0.0, 0.0, 0.0, 1.0); break;
773 // image translate
774 case KeyEvent.KEYCODE_W: av.adjustParams( 0.0, 0.0,-1.0, 0.0, 0.0, 0.0); break;
775 case KeyEvent.KEYCODE_X: av.adjustParams( 0.0, 0.0, 1.0, 0.0, 0.0, 0.0); break;
776 case KeyEvent.KEYCODE_A: av.adjustParams( 0.0,-1.0, 0.0, 0.0, 0.0, 0.0); break;
777 case KeyEvent.KEYCODE_D: av.adjustParams( 0.0, 1.0, 0.0, 0.0, 0.0, 0.0); break;
778 // camera rotation
779 case KeyEvent.KEYCODE_R: av.adjustParams( 0.0, 0.0, 0.0, 0.0, 0.0,-1.0); break;
780 case KeyEvent.KEYCODE_U: av.adjustParams( 0.0, 0.0, 0.0, 0.0, 0.0, 1.0); break;
781 // camera translate
782 case KeyEvent.KEYCODE_Y: av.adjustParams( 0.0, 0.0, 0.0, 0.0,-1.0, 0.0); break;
783 case KeyEvent.KEYCODE_N: av.adjustParams( 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); break;
784 case KeyEvent.KEYCODE_G: av.adjustParams( 0.0, 0.0, 0.0,-1.0, 0.0, 0.0); break;
785 case KeyEvent.KEYCODE_J: av.adjustParams( 0.0, 0.0, 0.0, 1.0, 0.0, 0.0); break;
786
787*/
788
789 case KeyEvent.KEYCODE_SLASH:
790 seekmethod = 1 - seekmethod;
791 return true;
792
793 case KeyEvent.KEYCODE_DPAD_LEFT:
Marco Nelissenb7841ac2009-05-05 14:36:25 -0700794 if (mDeviceHasNoDpad) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800795 break;
796 }
797 if (!mPrevButton.hasFocus()) {
798 mPrevButton.requestFocus();
799 }
800 scanBackward(repcnt, event.getEventTime() - event.getDownTime());
801 return true;
802 case KeyEvent.KEYCODE_DPAD_RIGHT:
Marco Nelissenb7841ac2009-05-05 14:36:25 -0700803 if (mDeviceHasNoDpad) {
The Android Open Source Project792a2202009-03-03 19:32:30 -0800804 break;
805 }
806 if (!mNextButton.hasFocus()) {
807 mNextButton.requestFocus();
808 }
809 scanForward(repcnt, event.getEventTime() - event.getDownTime());
810 return true;
811
812 case KeyEvent.KEYCODE_S:
813 toggleShuffle();
814 return true;
815
816 case KeyEvent.KEYCODE_DPAD_CENTER:
817 case KeyEvent.KEYCODE_SPACE:
818 doPauseResume();
819 return true;
820 }
821 return super.onKeyDown(keyCode, event);
822 }
823
824 private void scanBackward(int repcnt, long delta) {
825 if(mService == null) return;
826 try {
827 if(repcnt == 0) {
828 mStartSeekPos = mService.position();
829 mLastSeekEventTime = 0;
830 mSeeking = false;
831 } else {
832 mSeeking = true;
833 if (delta < 5000) {
834 // seek at 10x speed for the first 5 seconds
835 delta = delta * 10;
836 } else {
837 // seek at 40x after that
838 delta = 50000 + (delta - 5000) * 40;
839 }
840 long newpos = mStartSeekPos - delta;
841 if (newpos < 0) {
842 // move to previous track
843 mService.prev();
844 long duration = mService.duration();
845 mStartSeekPos += duration;
846 newpos += duration;
847 }
848 if (((delta - mLastSeekEventTime) > 250) || repcnt < 0){
849 mService.seek(newpos);
850 mLastSeekEventTime = delta;
851 }
852 if (repcnt >= 0) {
853 mPosOverride = newpos;
854 } else {
855 mPosOverride = -1;
856 }
857 refreshNow();
858 }
859 } catch (RemoteException ex) {
860 }
861 }
862
863 private void scanForward(int repcnt, long delta) {
864 if(mService == null) return;
865 try {
866 if(repcnt == 0) {
867 mStartSeekPos = mService.position();
868 mLastSeekEventTime = 0;
869 mSeeking = false;
870 } else {
871 mSeeking = true;
872 if (delta < 5000) {
873 // seek at 10x speed for the first 5 seconds
874 delta = delta * 10;
875 } else {
876 // seek at 40x after that
877 delta = 50000 + (delta - 5000) * 40;
878 }
879 long newpos = mStartSeekPos + delta;
880 long duration = mService.duration();
881 if (newpos >= duration) {
882 // move to next track
883 mService.next();
884 mStartSeekPos -= duration; // is OK to go negative
885 newpos -= duration;
886 }
887 if (((delta - mLastSeekEventTime) > 250) || repcnt < 0){
888 mService.seek(newpos);
889 mLastSeekEventTime = delta;
890 }
891 if (repcnt >= 0) {
892 mPosOverride = newpos;
893 } else {
894 mPosOverride = -1;
895 }
896 refreshNow();
897 }
898 } catch (RemoteException ex) {
899 }
900 }
901
902 private void doPauseResume() {
903 try {
904 if(mService != null) {
905 if (mService.isPlaying()) {
906 mService.pause();
907 } else {
908 mService.play();
909 }
910 refreshNow();
911 setPauseButtonImage();
912 }
913 } catch (RemoteException ex) {
914 }
915 }
916
917 private void toggleShuffle() {
918 if (mService == null) {
919 return;
920 }
921 try {
922 int shuffle = mService.getShuffleMode();
923 if (shuffle == MediaPlaybackService.SHUFFLE_NONE) {
924 mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NORMAL);
925 if (mService.getRepeatMode() == MediaPlaybackService.REPEAT_CURRENT) {
926 mService.setRepeatMode(MediaPlaybackService.REPEAT_ALL);
927 setRepeatButtonImage();
928 }
929 showToast(R.string.shuffle_on_notif);
930 } else if (shuffle == MediaPlaybackService.SHUFFLE_NORMAL ||
931 shuffle == MediaPlaybackService.SHUFFLE_AUTO) {
932 mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE);
933 showToast(R.string.shuffle_off_notif);
934 } else {
935 Log.e("MediaPlaybackActivity", "Invalid shuffle mode: " + shuffle);
936 }
937 setShuffleButtonImage();
938 } catch (RemoteException ex) {
939 }
940 }
941
942 private void cycleRepeat() {
943 if (mService == null) {
944 return;
945 }
946 try {
947 int mode = mService.getRepeatMode();
948 if (mode == MediaPlaybackService.REPEAT_NONE) {
949 mService.setRepeatMode(MediaPlaybackService.REPEAT_ALL);
950 showToast(R.string.repeat_all_notif);
951 } else if (mode == MediaPlaybackService.REPEAT_ALL) {
952 mService.setRepeatMode(MediaPlaybackService.REPEAT_CURRENT);
953 if (mService.getShuffleMode() != MediaPlaybackService.SHUFFLE_NONE) {
954 mService.setShuffleMode(MediaPlaybackService.SHUFFLE_NONE);
955 setShuffleButtonImage();
956 }
957 showToast(R.string.repeat_current_notif);
958 } else {
959 mService.setRepeatMode(MediaPlaybackService.REPEAT_NONE);
960 showToast(R.string.repeat_off_notif);
961 }
962 setRepeatButtonImage();
963 } catch (RemoteException ex) {
964 }
965
966 }
967
968 private void showToast(int resid) {
969 if (mToast == null) {
970 mToast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
971 }
972 mToast.setText(resid);
973 mToast.show();
974 }
975
976 private void startPlayback() {
977
978 if(mService == null)
979 return;
980 Intent intent = getIntent();
981 String filename = "";
982 Uri uri = intent.getData();
983 if (uri != null && uri.toString().length() > 0) {
984 // If this is a file:// URI, just use the path directly instead
985 // of going through the open-from-filedescriptor codepath.
986 String scheme = uri.getScheme();
987 if ("file".equals(scheme)) {
988 filename = uri.getPath();
989 } else {
990 filename = uri.toString();
991 }
992 try {
Marco Nelissenb7841ac2009-05-05 14:36:25 -0700993 if (! ContentResolver.SCHEME_CONTENT.equals(scheme) ||
994 ! MediaStore.AUTHORITY.equals(uri.getAuthority())) {
995 mOneShot = true;
The Android Open Source Project792a2202009-03-03 19:32:30 -0800996 }
Marco Nelissenb7841ac2009-05-05 14:36:25 -0700997 mService.stop();
998 mService.openFile(filename, mOneShot);
999 mService.play();
1000 setIntent(new Intent());
The Android Open Source Project792a2202009-03-03 19:32:30 -08001001 } catch (Exception ex) {
1002 Log.d("MediaPlaybackActivity", "couldn't start playback: " + ex);
1003 }
1004 }
1005
1006 updateTrackInfo();
1007 long next = refreshNow();
1008 queueNextRefresh(next);
1009 }
1010
1011 private ServiceConnection osc = new ServiceConnection() {
1012 public void onServiceConnected(ComponentName classname, IBinder obj) {
1013 mService = IMediaPlaybackService.Stub.asInterface(obj);
The Android Open Source Project792a2202009-03-03 19:32:30 -08001014 startPlayback();
1015 try {
1016 // Assume something is playing when the service says it is,
1017 // but also if the audio ID is valid but the service is paused.
1018 if (mService.getAudioId() >= 0 || mService.isPlaying() ||
1019 mService.getPath() != null) {
1020 // something is playing now, we're done
1021 if (mOneShot || mService.getAudioId() < 0) {
1022 mRepeatButton.setVisibility(View.INVISIBLE);
1023 mShuffleButton.setVisibility(View.INVISIBLE);
1024 mQueueButton.setVisibility(View.INVISIBLE);
1025 } else {
1026 mRepeatButton.setVisibility(View.VISIBLE);
1027 mShuffleButton.setVisibility(View.VISIBLE);
1028 mQueueButton.setVisibility(View.VISIBLE);
1029 setRepeatButtonImage();
1030 setShuffleButtonImage();
1031 }
1032 setPauseButtonImage();
1033 return;
1034 }
1035 } catch (RemoteException ex) {
1036 }
1037 // Service is dead or not playing anything. If we got here as part
1038 // of a "play this file" Intent, exit. Otherwise go to the Music
1039 // app start screen.
1040 if (getIntent().getData() == null) {
1041 Intent intent = new Intent(Intent.ACTION_MAIN);
1042 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1043 intent.setClass(MediaPlaybackActivity.this, MusicBrowserActivity.class);
1044 startActivity(intent);
1045 }
1046 finish();
1047 }
1048 public void onServiceDisconnected(ComponentName classname) {
1049 }
1050 };
1051
1052 private void setRepeatButtonImage() {
1053 try {
1054 switch (mService.getRepeatMode()) {
1055 case MediaPlaybackService.REPEAT_ALL:
1056 mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_all_btn);
1057 break;
1058 case MediaPlaybackService.REPEAT_CURRENT:
1059 mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_once_btn);
1060 break;
1061 default:
1062 mRepeatButton.setImageResource(R.drawable.ic_mp_repeat_off_btn);
1063 break;
1064 }
1065 } catch (RemoteException ex) {
1066 }
1067 }
1068
1069 private void setShuffleButtonImage() {
1070 try {
1071 switch (mService.getShuffleMode()) {
1072 case MediaPlaybackService.SHUFFLE_NONE:
1073 mShuffleButton.setImageResource(R.drawable.ic_mp_shuffle_off_btn);
1074 break;
1075 case MediaPlaybackService.SHUFFLE_AUTO:
1076 mShuffleButton.setImageResource(R.drawable.ic_mp_partyshuffle_on_btn);
1077 break;
1078 default:
1079 mShuffleButton.setImageResource(R.drawable.ic_mp_shuffle_on_btn);
1080 break;
1081 }
1082 } catch (RemoteException ex) {
1083 }
1084 }
1085
1086 private void setPauseButtonImage() {
1087 try {
1088 if (mService != null && mService.isPlaying()) {
1089 mPauseButton.setImageResource(android.R.drawable.ic_media_pause);
1090 } else {
1091 mPauseButton.setImageResource(android.R.drawable.ic_media_play);
1092 }
1093 } catch (RemoteException ex) {
1094 }
1095 }
1096
1097 private ImageView mAlbum;
1098 private TextView mCurrentTime;
1099 private TextView mTotalTime;
1100 private TextView mArtistName;
1101 private TextView mAlbumName;
1102 private TextView mTrackName;
1103 private ProgressBar mProgress;
1104 private long mPosOverride = -1;
The Android Open Source Projectc85f6772009-03-18 17:39:48 -07001105 private boolean mFromTouch = false;
The Android Open Source Project792a2202009-03-03 19:32:30 -08001106 private long mDuration;
1107 private int seekmethod;
1108 private boolean paused;
1109
1110 private static final int REFRESH = 1;
1111 private static final int QUIT = 2;
1112 private static final int GET_ALBUM_ART = 3;
1113 private static final int ALBUM_ART_DECODED = 4;
1114
1115 private void queueNextRefresh(long delay) {
1116 if (!paused) {
1117 Message msg = mHandler.obtainMessage(REFRESH);
1118 mHandler.removeMessages(REFRESH);
1119 mHandler.sendMessageDelayed(msg, delay);
1120 }
1121 }
1122
1123 private long refreshNow() {
1124 if(mService == null)
1125 return 500;
1126 try {
1127 long pos = mPosOverride < 0 ? mService.position() : mPosOverride;
1128 long remaining = 1000 - (pos % 1000);
1129 if ((pos >= 0) && (mDuration > 0)) {
1130 mCurrentTime.setText(MusicUtils.makeTimeString(this, pos / 1000));
1131
1132 if (mService.isPlaying()) {
1133 mCurrentTime.setVisibility(View.VISIBLE);
1134 } else {
1135 // blink the counter
1136 int vis = mCurrentTime.getVisibility();
1137 mCurrentTime.setVisibility(vis == View.INVISIBLE ? View.VISIBLE : View.INVISIBLE);
1138 remaining = 500;
1139 }
1140
1141 mProgress.setProgress((int) (1000 * pos / mDuration));
1142 } else {
1143 mCurrentTime.setText("--:--");
1144 mProgress.setProgress(1000);
1145 }
1146 // return the number of milliseconds until the next full second, so
1147 // the counter can be updated at just the right time
1148 return remaining;
1149 } catch (RemoteException ex) {
1150 }
1151 return 500;
1152 }
1153
1154 private final Handler mHandler = new Handler() {
1155 @Override
1156 public void handleMessage(Message msg) {
1157 switch (msg.what) {
1158 case ALBUM_ART_DECODED:
1159 mAlbum.setImageBitmap((Bitmap)msg.obj);
1160 mAlbum.getDrawable().setDither(true);
1161 break;
1162
1163 case REFRESH:
1164 long next = refreshNow();
1165 queueNextRefresh(next);
1166 break;
1167
1168 case QUIT:
1169 // This can be moved back to onCreate once the bug that prevents
1170 // Dialogs from being started from onCreate/onResume is fixed.
1171 new AlertDialog.Builder(MediaPlaybackActivity.this)
1172 .setTitle(R.string.service_start_error_title)
1173 .setMessage(R.string.service_start_error_msg)
1174 .setPositiveButton(R.string.service_start_error_button,
1175 new DialogInterface.OnClickListener() {
1176 public void onClick(DialogInterface dialog, int whichButton) {
1177 finish();
1178 }
1179 })
1180 .setCancelable(false)
1181 .show();
1182 break;
1183
1184 default:
1185 break;
1186 }
1187 }
1188 };
1189
1190 private BroadcastReceiver mStatusListener = new BroadcastReceiver() {
1191 @Override
1192 public void onReceive(Context context, Intent intent) {
1193 String action = intent.getAction();
1194 if (action.equals(MediaPlaybackService.META_CHANGED)) {
1195 // redraw the artist/title info and
1196 // set new max for progress bar
1197 updateTrackInfo();
1198 setPauseButtonImage();
1199 queueNextRefresh(1);
1200 } else if (action.equals(MediaPlaybackService.PLAYBACK_COMPLETE)) {
1201 if (mOneShot) {
1202 finish();
1203 } else {
1204 setPauseButtonImage();
1205 }
1206 } else if (action.equals(MediaPlaybackService.PLAYSTATE_CHANGED)) {
1207 setPauseButtonImage();
1208 }
1209 }
1210 };
1211
Marco Nelissenbd447b62009-06-29 14:52:05 -07001212 private static class AlbumSongIdWrapper {
1213 public long albumid;
1214 public long songid;
1215 AlbumSongIdWrapper(long aid, long sid) {
1216 albumid = aid;
1217 songid = sid;
1218 }
1219 }
1220
The Android Open Source Project792a2202009-03-03 19:32:30 -08001221 private void updateTrackInfo() {
1222 if (mService == null) {
1223 return;
1224 }
1225 try {
1226 String path = mService.getPath();
1227 if (path == null) {
1228 finish();
1229 return;
1230 }
1231
Marco Nelissenbd447b62009-06-29 14:52:05 -07001232 long songid = mService.getAudioId();
Marco Nelissen7a16cc72009-06-16 08:54:17 -07001233 if (songid < 0 && path.toLowerCase().startsWith("http://")) {
1234 // Once we can get album art and meta data from MediaPlayer, we
1235 // can show that info again when streaming.
The Android Open Source Project792a2202009-03-03 19:32:30 -08001236 ((View) mArtistName.getParent()).setVisibility(View.INVISIBLE);
1237 ((View) mAlbumName.getParent()).setVisibility(View.INVISIBLE);
1238 mAlbum.setVisibility(View.GONE);
1239 mTrackName.setText(path);
1240 mAlbumArtHandler.removeMessages(GET_ALBUM_ART);
Marco Nelissenbd447b62009-06-29 14:52:05 -07001241 mAlbumArtHandler.obtainMessage(GET_ALBUM_ART, new AlbumSongIdWrapper(-1, -1)).sendToTarget();
The Android Open Source Project792a2202009-03-03 19:32:30 -08001242 } else {
1243 ((View) mArtistName.getParent()).setVisibility(View.VISIBLE);
1244 ((View) mAlbumName.getParent()).setVisibility(View.VISIBLE);
1245 String artistName = mService.getArtistName();
1246 if (MediaFile.UNKNOWN_STRING.equals(artistName)) {
1247 artistName = getString(R.string.unknown_artist_name);
1248 }
1249 mArtistName.setText(artistName);
1250 String albumName = mService.getAlbumName();
Marco Nelissenbd447b62009-06-29 14:52:05 -07001251 long albumid = mService.getAlbumId();
The Android Open Source Project792a2202009-03-03 19:32:30 -08001252 if (MediaFile.UNKNOWN_STRING.equals(albumName)) {
1253 albumName = getString(R.string.unknown_album_name);
1254 albumid = -1;
1255 }
1256 mAlbumName.setText(albumName);
1257 mTrackName.setText(mService.getTrackName());
1258 mAlbumArtHandler.removeMessages(GET_ALBUM_ART);
Marco Nelissenbd447b62009-06-29 14:52:05 -07001259 mAlbumArtHandler.obtainMessage(GET_ALBUM_ART, new AlbumSongIdWrapper(albumid, songid)).sendToTarget();
The Android Open Source Project792a2202009-03-03 19:32:30 -08001260 mAlbum.setVisibility(View.VISIBLE);
1261 }
1262 mDuration = mService.duration();
1263 mTotalTime.setText(MusicUtils.makeTimeString(this, mDuration / 1000));
1264 } catch (RemoteException ex) {
1265 finish();
1266 }
1267 }
1268
1269 public class AlbumArtHandler extends Handler {
Marco Nelissenbd447b62009-06-29 14:52:05 -07001270 private long mAlbumId = -1;
The Android Open Source Project792a2202009-03-03 19:32:30 -08001271
1272 public AlbumArtHandler(Looper looper) {
1273 super(looper);
1274 }
Marco Nelissenbd447b62009-06-29 14:52:05 -07001275 @Override
The Android Open Source Project792a2202009-03-03 19:32:30 -08001276 public void handleMessage(Message msg)
1277 {
Marco Nelissenbd447b62009-06-29 14:52:05 -07001278 long albumid = ((AlbumSongIdWrapper) msg.obj).albumid;
1279 long songid = ((AlbumSongIdWrapper) msg.obj).songid;
The Android Open Source Project792a2202009-03-03 19:32:30 -08001280 if (msg.what == GET_ALBUM_ART && (mAlbumId != albumid || albumid < 0)) {
1281 // while decoding the new image, show the default album art
1282 Message numsg = mHandler.obtainMessage(ALBUM_ART_DECODED, null);
1283 mHandler.removeMessages(ALBUM_ART_DECODED);
1284 mHandler.sendMessageDelayed(numsg, 300);
Marco Nelissen7a16cc72009-06-16 08:54:17 -07001285 Bitmap bm = MusicUtils.getArtwork(MediaPlaybackActivity.this, songid, albumid);
The Android Open Source Project792a2202009-03-03 19:32:30 -08001286 if (bm == null) {
Marco Nelissen7a16cc72009-06-16 08:54:17 -07001287 bm = MusicUtils.getArtwork(MediaPlaybackActivity.this, songid, -1);
The Android Open Source Project792a2202009-03-03 19:32:30 -08001288 albumid = -1;
1289 }
1290 if (bm != null) {
1291 numsg = mHandler.obtainMessage(ALBUM_ART_DECODED, bm);
1292 mHandler.removeMessages(ALBUM_ART_DECODED);
1293 mHandler.sendMessage(numsg);
1294 }
1295 mAlbumId = albumid;
1296 }
1297 }
1298 }
1299
Marco Nelissen756c3f52009-05-14 10:07:23 -07001300 private static class Worker implements Runnable {
The Android Open Source Project792a2202009-03-03 19:32:30 -08001301 private final Object mLock = new Object();
1302 private Looper mLooper;
1303
1304 /**
1305 * Creates a worker thread with the given name. The thread
1306 * then runs a {@link android.os.Looper}.
1307 * @param name A name for the new thread
1308 */
1309 Worker(String name) {
1310 Thread t = new Thread(null, this, name);
1311 t.setPriority(Thread.MIN_PRIORITY);
1312 t.start();
1313 synchronized (mLock) {
1314 while (mLooper == null) {
1315 try {
1316 mLock.wait();
1317 } catch (InterruptedException ex) {
1318 }
1319 }
1320 }
1321 }
1322
1323 public Looper getLooper() {
1324 return mLooper;
1325 }
1326
1327 public void run() {
1328 synchronized (mLock) {
1329 Looper.prepare();
1330 mLooper = Looper.myLooper();
1331 mLock.notifyAll();
1332 }
1333 Looper.loop();
1334 }
1335
1336 public void quit() {
1337 mLooper.quit();
1338 }
1339 }
1340}
1341