2 * Copyright (C) 2012 Andrew Neal Licensed under the Apache License, Version 2.0
3 * (the "License"); you may not use this file except in compliance with the
4 * License. You may obtain a copy of the License at
5 * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
6 * or agreed to in writing, software distributed under the License is
7 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8 * KIND, either express or implied. See the License for the specific language
9 * governing permissions and limitations under the License.
12 package com.cyngn.eleven.ui.activities;
14 import static com.cyngn.eleven.utils.MusicUtils.mService;
16 import android.animation.ObjectAnimator;
17 import android.app.ActionBar;
18 import android.app.SearchManager;
19 import android.app.SearchableInfo;
20 import android.content.BroadcastReceiver;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.ServiceConnection;
26 import android.media.AudioManager;
27 import android.media.audiofx.AudioEffect;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Message;
33 import android.os.SystemClock;
34 import android.provider.MediaStore.Audio.Playlists;
35 import android.provider.MediaStore.Audio.Albums;
36 import android.provider.MediaStore.Audio.Artists;
37 import android.support.v4.app.FragmentActivity;
38 import android.support.v4.view.ViewPager;
39 import android.view.Menu;
40 import android.view.MenuItem;
41 import android.view.View;
42 import android.view.View.OnClickListener;
43 import android.view.animation.AnimationUtils;
44 import android.widget.FrameLayout;
45 import android.widget.ImageView;
46 import android.widget.LinearLayout;
47 import android.widget.SearchView;
48 import android.widget.SearchView.OnQueryTextListener;
49 import android.widget.SeekBar;
50 import android.widget.SeekBar.OnSeekBarChangeListener;
51 import android.widget.TextView;
53 import com.cyngn.eleven.IElevenService;
54 import com.cyngn.eleven.MusicPlaybackService;
55 import com.cyngn.eleven.R;
56 import com.cyngn.eleven.adapters.PagerAdapter;
57 import com.cyngn.eleven.cache.ImageFetcher;
58 import com.cyngn.eleven.ui.fragments.QueueFragment;
59 import com.cyngn.eleven.menu.DeleteDialog;
60 import com.cyngn.eleven.utils.ApolloUtils;
61 import com.cyngn.eleven.utils.MusicUtils;
62 import com.cyngn.eleven.utils.MusicUtils.ServiceToken;
63 import com.cyngn.eleven.utils.NavUtils;
64 import com.cyngn.eleven.utils.ThemeUtils;
65 import com.cyngn.eleven.widgets.PlayPauseButton;
66 import com.cyngn.eleven.widgets.RepeatButton;
67 import com.cyngn.eleven.widgets.RepeatingImageButton;
68 import com.cyngn.eleven.widgets.ShuffleButton;
70 import java.lang.ref.WeakReference;
73 * Apollo's "now playing" interface.
75 * @author Andrew Neal (andrewdneal@gmail.com)
77 public class AudioPlayerActivity extends FragmentActivity implements ServiceConnection,
78 OnSeekBarChangeListener, DeleteDialog.DeleteDialogCallback {
80 // Message to refresh the time
81 private static final int REFRESH_TIME = 1;
84 private ServiceToken mToken;
86 // Play and pause button
87 private PlayPauseButton mPlayPauseButton;
90 private RepeatButton mRepeatButton;
93 private ShuffleButton mShuffleButton;
96 private RepeatingImageButton mPreviousButton;
99 private RepeatingImageButton mNextButton;
102 private TextView mTrackName;
105 private TextView mArtistName;
108 private ImageView mAlbumArt;
111 private ImageView mAlbumArtSmall;
114 private TextView mCurrentTime;
117 private TextView mTotalTime;
120 private ImageView mQueueSwitch;
123 private SeekBar mProgress;
125 // Broadcast receiver
126 private PlaybackStatus mPlaybackStatus;
128 // Handler used to update the current time
129 private TimeHandler mTimeHandler;
132 private ViewPager mViewPager;
135 private PagerAdapter mPagerAdapter;
137 // ViewPager container
138 private FrameLayout mPageContainer;
141 private LinearLayout mAudioPlayerHeader;
144 private ImageFetcher mImageFetcher;
147 private ThemeUtils mResources;
149 private long mPosOverride = -1;
151 private long mStartSeekPos = 0;
153 private long mLastSeekEventTime;
155 private long mLastShortSeekEventTime;
157 private boolean mIsPaused = false;
159 private boolean mFromTouch = false;
165 protected void onCreate(final Bundle savedInstanceState) {
166 super.onCreate(savedInstanceState);
168 // Initialze the theme resources
169 mResources = new ThemeUtils(this);
170 // Set the overflow style
171 mResources.setOverflowStyle(this);
174 overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
176 // Control the media volume
177 setVolumeControlStream(AudioManager.STREAM_MUSIC);
179 // Bind Apollo's service
180 mToken = MusicUtils.bindToService(this, this);
182 // Initialize the image fetcher/cache
183 mImageFetcher = ApolloUtils.getImageFetcher(this);
185 // Initialize the handler used to update the current time
186 mTimeHandler = new TimeHandler(this);
188 // Initialize the broadcast receiver
189 mPlaybackStatus = new PlaybackStatus(this);
191 // Theme the action bar
192 final ActionBar actionBar = getActionBar();
193 mResources.themeActionBar(actionBar, getString(R.string.app_name));
194 actionBar.setDisplayHomeAsUpEnabled(true);
197 setContentView(R.layout.activity_player_base);
199 // Cache all the items
200 initPlaybackControls();
207 public void onNewIntent(Intent intent) {
216 public void onServiceConnected(final ComponentName name, final IBinder service) {
217 mService = IElevenService.Stub.asInterface(service);
218 // Check whether we were asked to start any playback
220 // Set the playback drawables
221 updatePlaybackControls();
223 updateNowPlayingInfo();
224 // Update the favorites icon
225 invalidateOptionsMenu();
232 public void onServiceDisconnected(final ComponentName name) {
240 public void onProgressChanged(final SeekBar bar, final int progress, final boolean fromuser) {
241 if (!fromuser || mService == null) {
244 final long now = SystemClock.elapsedRealtime();
245 if (now - mLastSeekEventTime > 250) {
246 mLastSeekEventTime = now;
247 mLastShortSeekEventTime = now;
248 mPosOverride = MusicUtils.duration() * progress / 1000;
249 MusicUtils.seek(mPosOverride);
251 // refreshCurrentTime();
254 } else if (now - mLastShortSeekEventTime > 5) {
255 mLastShortSeekEventTime = now;
256 mPosOverride = MusicUtils.duration() * progress / 1000;
257 refreshCurrentTimeText(mPosOverride);
265 public void onStartTrackingTouch(final SeekBar bar) {
266 mLastSeekEventTime = 0;
268 mCurrentTime.setVisibility(View.VISIBLE);
275 public void onStopTrackingTouch(final SeekBar bar) {
276 if (mPosOverride != -1) {
277 MusicUtils.seek(mPosOverride);
287 public boolean onPrepareOptionsMenu(final Menu menu) {
288 // Hide the EQ option if it can't be opened
289 final Intent intent = new Intent(AudioEffect.ACTION_DISPLAY_AUDIO_EFFECT_CONTROL_PANEL);
290 if (getPackageManager().resolveActivity(intent, 0) == null) {
291 final MenuItem effects = menu.findItem(R.id.menu_audio_player_equalizer);
292 effects.setVisible(false);
294 mResources.setFavoriteIcon(menu);
302 public boolean onCreateOptionsMenu(final Menu menu) {
304 getMenuInflater().inflate(R.menu.search, menu);
305 // Theme the search icon
306 mResources.setSearchIcon(menu);
308 final SearchView searchView = (SearchView)menu.findItem(R.id.menu_search).getActionView();
310 final SearchManager searchManager = (SearchManager)getSystemService(Context.SEARCH_SERVICE);
311 final SearchableInfo searchableInfo = searchManager.getSearchableInfo(getComponentName());
312 searchView.setSearchableInfo(searchableInfo);
313 // Perform the search
314 searchView.setOnQueryTextListener(new OnQueryTextListener() {
317 public boolean onQueryTextSubmit(final String query) {
318 // Open the search activity
319 NavUtils.openSearch(AudioPlayerActivity.this, query);
324 public boolean onQueryTextChange(final String newText) {
331 getMenuInflater().inflate(R.menu.favorite, menu);
333 getMenuInflater().inflate(R.menu.shuffle, menu);
334 // Share, ringtone, and equalizer
335 getMenuInflater().inflate(R.menu.audio_player, menu);
337 getMenuInflater().inflate(R.menu.activity_base, menu);
345 public boolean onOptionsItemSelected(final MenuItem item) {
346 switch (item.getItemId()) {
347 case android.R.id.home:
348 // Go back to the home activity
349 NavUtils.goHome(this);
351 case R.id.menu_shuffle:
352 // Shuffle all the songs
353 MusicUtils.shuffleAll(this);
355 ((QueueFragment)mPagerAdapter.getFragment(0)).refreshQueue();
357 case R.id.menu_favorite:
358 // Toggle the current track as a favorite and update the menu
360 MusicUtils.toggleFavorite();
361 invalidateOptionsMenu();
363 case R.id.menu_audio_player_ringtone:
364 // Set the current track as a ringtone
365 MusicUtils.setRingtone(this, MusicUtils.getCurrentAudioId());
367 case R.id.menu_audio_player_share:
368 // Share the current meta data
371 case R.id.menu_audio_player_equalizer:
373 NavUtils.openEffectsPanel(this);
375 case R.id.menu_settings:
377 NavUtils.openSettings(this);
379 case R.id.menu_audio_player_delete:
380 // Delete current song
381 DeleteDialog.newInstance(MusicUtils.getTrackName(), new long[] {
382 MusicUtils.getCurrentAudioId()
383 }, null).show(getSupportFragmentManager(), "DeleteDialog");
388 return super.onOptionsItemSelected(item);
392 public void onDelete(long[] ids) {
393 ((QueueFragment)mPagerAdapter.getFragment(0)).refreshQueue();
394 if (MusicUtils.getQueue().length == 0) {
395 NavUtils.goHome(this);
403 public void onBackPressed() {
404 super.onBackPressed();
405 NavUtils.goHome(this);
412 protected void onResume() {
414 // Set the playback drawables
415 updatePlaybackControls();
417 updateNowPlayingInfo();
419 ((QueueFragment)mPagerAdapter.getFragment(0)).refreshQueue();
426 protected void onStart() {
428 final IntentFilter filter = new IntentFilter();
429 // Play and pause changes
430 filter.addAction(MusicPlaybackService.PLAYSTATE_CHANGED);
431 // Shuffle and repeat changes
432 filter.addAction(MusicPlaybackService.SHUFFLEMODE_CHANGED);
433 filter.addAction(MusicPlaybackService.REPEATMODE_CHANGED);
435 filter.addAction(MusicPlaybackService.META_CHANGED);
436 // Update a list, probably the playlist fragment's
437 filter.addAction(MusicPlaybackService.REFRESH);
438 registerReceiver(mPlaybackStatus, filter);
439 // Refresh the current time
440 final long next = refreshCurrentTime();
441 queueNextRefresh(next);
442 MusicUtils.notifyForegroundStateChanged(this, true);
449 protected void onStop() {
451 MusicUtils.notifyForegroundStateChanged(this, false);
452 mImageFetcher.flush();
459 protected void onDestroy() {
462 mTimeHandler.removeMessages(REFRESH_TIME);
463 // Unbind from the service
464 if (mService != null) {
465 MusicUtils.unbindFromService(mToken);
469 // Unregister the receiver
471 unregisterReceiver(mPlaybackStatus);
472 } catch (final Throwable e) {
478 * Initializes the items in the now playing screen
480 @SuppressWarnings("deprecation")
481 private void initPlaybackControls() {
482 // ViewPager container
483 mPageContainer = (FrameLayout)findViewById(R.id.audio_player_pager_container);
484 // Theme the pager container background
486 .setBackgroundDrawable(mResources.getDrawable("audio_player_pager_container"));
488 // Now playing header
489 mAudioPlayerHeader = (LinearLayout)findViewById(R.id.audio_player_header);
490 // Opens the currently playing album profile
491 mAudioPlayerHeader.setOnClickListener(mOpenAlbumProfile);
493 // Used to hide the artwork and show the queue
494 final FrameLayout mSwitch = (FrameLayout)findViewById(R.id.audio_player_switch);
495 mSwitch.setOnClickListener(mToggleHiddenPanel);
497 // Initialize the pager adapter
498 mPagerAdapter = new PagerAdapter(this);
500 mPagerAdapter.add(QueueFragment.class, null);
502 // Initialize the ViewPager
503 mViewPager = (ViewPager)findViewById(R.id.audio_player_pager);
505 mViewPager.setAdapter(mPagerAdapter);
506 // Offscreen pager loading limit
507 mViewPager.setOffscreenPageLimit(mPagerAdapter.getCount() - 1);
508 // Play and pause button
509 mPlayPauseButton = (PlayPauseButton)findViewById(R.id.action_button_play);
511 mShuffleButton = (ShuffleButton)findViewById(R.id.action_button_shuffle);
513 mRepeatButton = (RepeatButton)findViewById(R.id.action_button_repeat);
515 mPreviousButton = (RepeatingImageButton)findViewById(R.id.action_button_previous);
517 mNextButton = (RepeatingImageButton)findViewById(R.id.action_button_next);
519 mTrackName = (TextView)findViewById(R.id.audio_player_track_name);
521 mArtistName = (TextView)findViewById(R.id.audio_player_artist_name);
523 mAlbumArt = (ImageView)findViewById(R.id.audio_player_album_art);
525 mAlbumArtSmall = (ImageView)findViewById(R.id.audio_player_switch_album_art);
527 mCurrentTime = (TextView)findViewById(R.id.audio_player_current_time);
529 mTotalTime = (TextView)findViewById(R.id.audio_player_total_time);
530 // Used to show and hide the queue fragment
531 mQueueSwitch = (ImageView)findViewById(R.id.audio_player_switch_queue);
532 // Theme the queue switch icon
533 mQueueSwitch.setImageDrawable(mResources.getDrawable("btn_switch_queue"));
535 mProgress = (SeekBar)findViewById(android.R.id.progress);
537 // Set the repeat listner for the previous button
538 mPreviousButton.setRepeatListener(mRewindListener);
539 // Set the repeat listner for the next button
540 mNextButton.setRepeatListener(mFastForwardListener);
541 // Update the progress
542 mProgress.setOnSeekBarChangeListener(this);
546 * Sets the track name, album name, and album art.
548 private void updateNowPlayingInfo() {
549 // Set the track name
550 mTrackName.setText(MusicUtils.getTrackName());
551 // Set the artist name
552 mArtistName.setText(MusicUtils.getArtistName());
553 // Set the total time
554 mTotalTime.setText(MusicUtils.makeTimeString(this, MusicUtils.duration() / 1000));
556 mImageFetcher.loadCurrentArtwork(mAlbumArt);
557 // Set the small artwork
558 mImageFetcher.loadCurrentArtwork(mAlbumArtSmall);
559 // Update the current time
564 private long parseIdFromIntent(Intent intent, String longKey,
565 String stringKey, long defaultId) {
566 long id = intent.getLongExtra(longKey, -1);
568 String idString = intent.getStringExtra(stringKey);
569 if (idString != null) {
571 id = Long.parseLong(idString);
572 } catch (NumberFormatException e) {
581 * Checks whether the passed intent contains a playback request,
582 * and starts playback if that's the case
584 private void startPlayback() {
585 Intent intent = getIntent();
587 if (intent == null || mService == null) {
591 Uri uri = intent.getData();
592 String mimeType = intent.getType();
593 boolean handled = false;
595 if (uri != null && uri.toString().length() > 0) {
596 MusicUtils.playFile(this, uri);
598 } else if (Playlists.CONTENT_TYPE.equals(mimeType)) {
599 long id = parseIdFromIntent(intent, "playlistId", "playlist", -1);
601 MusicUtils.playPlaylist(this, id);
604 } else if (Albums.CONTENT_TYPE.equals(mimeType)) {
605 long id = parseIdFromIntent(intent, "albumId", "album", -1);
607 int position = intent.getIntExtra("position", 0);
608 MusicUtils.playAlbum(this, id, position);
611 } else if (Artists.CONTENT_TYPE.equals(mimeType)) {
612 long id = parseIdFromIntent(intent, "artistId", "artist", -1);
614 int position = intent.getIntExtra("position", 0);
615 MusicUtils.playArtist(this, id, position);
621 // Make sure to process intent only once
622 setIntent(new Intent());
624 ((QueueFragment)mPagerAdapter.getFragment(0)).refreshQueue();
629 * Sets the correct drawable states for the playback controls.
631 private void updatePlaybackControls() {
632 // Set the play and pause image
633 mPlayPauseButton.updateState();
634 // Set the shuffle image
635 mShuffleButton.updateShuffleState();
636 // Set the repeat image
637 mRepeatButton.updateRepeatState();
641 * @param delay When to update
643 private void queueNextRefresh(final long delay) {
645 final Message message = mTimeHandler.obtainMessage(REFRESH_TIME);
646 mTimeHandler.removeMessages(REFRESH_TIME);
647 mTimeHandler.sendMessageDelayed(message, delay);
652 * Used to scan backwards in time through the curren track
654 * @param repcnt The repeat count
655 * @param delta The long press duration
657 private void scanBackward(final int repcnt, long delta) {
658 if (mService == null) {
662 mStartSeekPos = MusicUtils.position();
663 mLastSeekEventTime = 0;
666 // seek at 10x speed for the first 5 seconds
669 // seek at 40x after that
670 delta = 50000 + (delta - 5000) * 40;
672 long newpos = mStartSeekPos - delta;
674 // move to previous track
675 MusicUtils.previous(this);
676 final long duration = MusicUtils.duration();
677 mStartSeekPos += duration;
680 if (delta - mLastSeekEventTime > 250 || repcnt < 0) {
681 MusicUtils.seek(newpos);
682 mLastSeekEventTime = delta;
685 mPosOverride = newpos;
689 refreshCurrentTime();
694 * Used to scan forwards in time through the curren track
696 * @param repcnt The repeat count
697 * @param delta The long press duration
699 private void scanForward(final int repcnt, long delta) {
700 if (mService == null) {
704 mStartSeekPos = MusicUtils.position();
705 mLastSeekEventTime = 0;
708 // seek at 10x speed for the first 5 seconds
711 // seek at 40x after that
712 delta = 50000 + (delta - 5000) * 40;
714 long newpos = mStartSeekPos + delta;
715 final long duration = MusicUtils.duration();
716 if (newpos >= duration) {
717 // move to next track
719 mStartSeekPos -= duration; // is OK to go negative
722 if (delta - mLastSeekEventTime > 250 || repcnt < 0) {
723 MusicUtils.seek(newpos);
724 mLastSeekEventTime = delta;
727 mPosOverride = newpos;
731 refreshCurrentTime();
735 private void refreshCurrentTimeText(final long pos) {
736 mCurrentTime.setText(MusicUtils.makeTimeString(this, pos / 1000));
739 /* Used to update the current time string */
740 private long refreshCurrentTime() {
741 if (mService == null) {
745 final long pos = mPosOverride < 0 ? MusicUtils.position() : mPosOverride;
746 if (pos >= 0 && MusicUtils.duration() > 0) {
747 refreshCurrentTimeText(pos);
748 final int progress = (int)(1000 * pos / MusicUtils.duration());
749 mProgress.setProgress(progress);
753 } else if (MusicUtils.isPlaying()) {
754 mCurrentTime.setVisibility(View.VISIBLE);
757 final int vis = mCurrentTime.getVisibility();
758 mCurrentTime.setVisibility(vis == View.INVISIBLE ? View.VISIBLE
763 mCurrentTime.setText("--:--");
764 mProgress.setProgress(1000);
766 // calculate the number of milliseconds until the next full second,
768 // the counter can be updated at just the right time
769 final long remaining = 1000 - pos % 1000;
770 // approximate how often we would need to refresh the slider to
772 int width = mProgress.getWidth();
776 final long smoothrefreshtime = MusicUtils.duration() / width;
777 if (smoothrefreshtime > remaining) {
780 if (smoothrefreshtime < 20) {
783 return smoothrefreshtime;
784 } catch (final Exception ignored) {
791 * @param v The view to animate
792 * @param alpha The alpha to apply
794 private void fade(final View v, final float alpha) {
795 final ObjectAnimator fade = ObjectAnimator.ofFloat(v, "alpha", alpha);
796 fade.setInterpolator(AnimationUtils.loadInterpolator(this,
797 android.R.anim.accelerate_decelerate_interpolator));
798 fade.setDuration(400);
803 * Called to show the album art and hide the queue
805 private void showAlbumArt() {
806 mPageContainer.setVisibility(View.INVISIBLE);
807 mAlbumArtSmall.setVisibility(View.GONE);
808 mQueueSwitch.setVisibility(View.VISIBLE);
809 // Fade out the pager container
810 fade(mPageContainer, 0f);
811 // Fade in the album art
816 * Called to hide the album art and show the queue
818 public void hideAlbumArt() {
819 mPageContainer.setVisibility(View.VISIBLE);
820 mQueueSwitch.setVisibility(View.GONE);
821 mAlbumArtSmall.setVisibility(View.VISIBLE);
822 // Fade out the artwork
824 // Fade in the pager container
825 fade(mPageContainer, 1f);
829 * /** Used to shared what the user is currently listening to
831 private void shareCurrentTrack() {
832 if (MusicUtils.getTrackName() == null || MusicUtils.getArtistName() == null) {
835 final Intent shareIntent = new Intent();
836 final String shareMessage = getString(R.string.now_listening_to,
837 MusicUtils.getTrackName(), MusicUtils.getArtistName());
839 shareIntent.setAction(Intent.ACTION_SEND);
840 shareIntent.setType("text/plain");
841 shareIntent.putExtra(Intent.EXTRA_TEXT, shareMessage);
842 startActivity(Intent.createChooser(shareIntent, getString(R.string.share_track_using)));
846 * Used to scan backwards through the track
848 private final RepeatingImageButton.RepeatListener mRewindListener = new RepeatingImageButton.RepeatListener() {
853 public void onRepeat(final View v, final long howlong, final int repcnt) {
854 scanBackward(repcnt, howlong);
859 * Used to scan ahead through the track
861 private final RepeatingImageButton.RepeatListener mFastForwardListener = new RepeatingImageButton.RepeatListener() {
866 public void onRepeat(final View v, final long howlong, final int repcnt) {
867 scanForward(repcnt, howlong);
872 * Switches from the large album art screen to show the queue and lyric
873 * fragments, then back again
875 private final OnClickListener mToggleHiddenPanel = new OnClickListener() {
881 public void onClick(final View v) {
882 if (mPageContainer.getVisibility() == View.VISIBLE) {
883 // Open the current album profile
884 mAudioPlayerHeader.setOnClickListener(mOpenAlbumProfile);
885 // Show the artwork, hide the queue
888 // Scroll to the current track
889 mAudioPlayerHeader.setOnClickListener(mScrollToCurrentSong);
890 // Show the queue, hide the artwork
897 * Opens to the current album profile
899 private final OnClickListener mOpenAlbumProfile = new OnClickListener() {
902 public void onClick(final View v) {
903 NavUtils.openAlbumProfile(AudioPlayerActivity.this, MusicUtils.getAlbumName(),
904 MusicUtils.getArtistName(), MusicUtils.getCurrentAlbumId());
909 * Scrolls the queue to the currently playing song
911 private final OnClickListener mScrollToCurrentSong = new OnClickListener() {
914 public void onClick(final View v) {
915 ((QueueFragment)mPagerAdapter.getFragment(0)).scrollToCurrentSong();
920 * Used to update the current time string
922 private static final class TimeHandler extends Handler {
924 private final WeakReference<AudioPlayerActivity> mAudioPlayer;
927 * Constructor of <code>TimeHandler</code>
929 public TimeHandler(final AudioPlayerActivity player) {
930 mAudioPlayer = new WeakReference<AudioPlayerActivity>(player);
934 public void handleMessage(final Message msg) {
937 final long next = mAudioPlayer.get().refreshCurrentTime();
938 mAudioPlayer.get().queueNextRefresh(next);
947 * Used to monitor the state of playback
949 private static final class PlaybackStatus extends BroadcastReceiver {
951 private final WeakReference<AudioPlayerActivity> mReference;
954 * Constructor of <code>PlaybackStatus</code>
956 public PlaybackStatus(final AudioPlayerActivity activity) {
957 mReference = new WeakReference<AudioPlayerActivity>(activity);
964 public void onReceive(final Context context, final Intent intent) {
965 final String action = intent.getAction();
966 if (action.equals(MusicPlaybackService.META_CHANGED)) {
968 mReference.get().updateNowPlayingInfo();
969 // Update the favorites icon
970 mReference.get().invalidateOptionsMenu();
971 } else if (action.equals(MusicPlaybackService.PLAYSTATE_CHANGED)) {
972 // Set the play and pause image
973 mReference.get().mPlayPauseButton.updateState();
974 } else if (action.equals(MusicPlaybackService.REPEATMODE_CHANGED)
975 || action.equals(MusicPlaybackService.SHUFFLEMODE_CHANGED)) {
976 // Set the repeat image
977 mReference.get().mRepeatButton.updateRepeatState();
978 // Set the shuffle image
979 mReference.get().mShuffleButton.updateShuffleState();