/* * Copyright (C) 2012 Andrew Neal Licensed under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law * or agreed to in writing, software distributed under the License is * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package com.bt.download.android.gui.activities; import static com.andrew.apollo.utils.MusicUtils.mService; import com.andrew.apollo.IApolloService; import com.andrew.apollo.MusicPlaybackService; import com.andrew.apollo.cache.ImageFetcher; import com.andrew.apollo.menu.DeleteDialog; import com.andrew.apollo.utils.MusicUtils; import com.andrew.apollo.utils.MusicUtils.ServiceToken; import com.andrew.apollo.widgets.PlayPauseButton; import com.andrew.apollo.widgets.RepeatButton; import com.andrew.apollo.widgets.RepeatingImageButton; import com.andrew.apollo.widgets.ShuffleButton; import com.bt.download.android.R; import com.bt.download.android.core.Constants; import com.bt.download.android.core.FileDescriptor; import com.bt.download.android.gui.Librarian; import com.bt.download.android.gui.adapters.PagerAdapter; import com.bt.download.android.gui.fragments.QueueFragment; import com.bt.download.android.gui.views.AbstractSwipeDetector; import com.frostwire.util.Ref; import com.frostwire.uxstats.UXAction; import com.frostwire.uxstats.UXStats; import com.umeng.analytics.MobclickAgent; import android.animation.ObjectAnimator; import android.app.ActionBar; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; import android.provider.MediaStore.Audio.Albums; import android.provider.MediaStore.Audio.Artists; import android.provider.MediaStore.Audio.Playlists; import android.support.v4.view.ViewPager; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import java.lang.ref.WeakReference; import java.util.Arrays; /** * Apollo's "now playing" interface. * * @author Andrew Neal (andrewdneal@gmail.com) */ public class AudioPlayerActivity extends Activity implements ServiceConnection, OnSeekBarChangeListener, DeleteDialog.DeleteDialogCallback { //private static final Logger log = Logger.getLogger(AudioPlayerActivity.class); // Message to refresh the time private static final int REFRESH_TIME = 1; // The service token private ServiceToken mToken; // Play and pause button private PlayPauseButton mPlayPauseButton; // Repeat button private RepeatButton mRepeatButton; // Shuffle button private ShuffleButton mShuffleButton; // Previous button private RepeatingImageButton mPreviousButton; // Next button private RepeatingImageButton mNextButton; // Track name private TextView mTrackName; // Artist name private TextView mArtistName; // Album art private ImageView mAlbumArt; // Tiny artwork private ImageView mAlbumArtSmall; // Current time private TextView mCurrentTime; // Total time private TextView mTotalTime; // Queue switch private ImageView mQueueSwitch; // Progess private SeekBar mProgress; // Broadcast receiver private PlaybackStatus mPlaybackStatus; // Handler used to update the current time private TimeHandler mTimeHandler; // View pager private ViewPager mViewPager; // Pager adpater private PagerAdapter mPagerAdapter; // ViewPager container private FrameLayout mPageContainer; // Header private LinearLayout mAudioPlayerHeader; // Image cache private ImageFetcher mImageFetcher; private long mPosOverride = -1; private long mStartSeekPos = 0; private long mLastSeekEventTime; private long mLastShortSeekEventTime; private boolean mIsPaused = false; private boolean mFromTouch = false; /** * {@inheritDoc} */ @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Fade it in overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); // Control the media volume setVolumeControlStream(AudioManager.STREAM_MUSIC); // Bind Apollo's service mToken = MusicUtils.bindToService(this, this); // Initialize the image fetcher/cache mImageFetcher = ImageFetcher.getInstance(this); // Initialize the handler used to update the current time mTimeHandler = new TimeHandler(this); // Initialize the broadcast receiver mPlaybackStatus = new PlaybackStatus(this); // Theme the action bar setTitle(R.string.now_playing); final ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayShowHomeEnabled(false); // Set the layout setContentView(R.layout.activity_player_base); // Cache all the items initPlaybackControls(); initGestures(); mPlayPauseButton.setOnLongClickListener(new StopListener(this)); } /** * {@inheritDoc} */ @Override public void onNewIntent(Intent intent) { setIntent(intent); startPlayback(); } /** * {@inheritDoc} */ @Override public void onServiceConnected(final ComponentName name, final IBinder service) { mService = IApolloService.Stub.asInterface(service); // Check whether we were asked to start any playback startPlayback(); // Set the playback drawables updatePlaybackControls(); // Current info updateNowPlayingInfo(); // Update the favorites icon invalidateOptionsMenu(); } /** * {@inheritDoc} */ @Override public void onServiceDisconnected(final ComponentName name) { mService = null; } /** * {@inheritDoc} */ @Override public void onProgressChanged(final SeekBar bar, final int progress, final boolean fromuser) { if (!fromuser || mService == null) { return; } final long now = SystemClock.elapsedRealtime(); if (now - mLastSeekEventTime > 250) { mLastSeekEventTime = now; mLastShortSeekEventTime = now; mPosOverride = MusicUtils.duration() * progress / 1000; MusicUtils.seek(mPosOverride); if (!mFromTouch) { // refreshCurrentTime(); mPosOverride = -1; } } else if (now - mLastShortSeekEventTime > 5) { mLastShortSeekEventTime = now; mPosOverride = MusicUtils.duration() * progress / 1000; refreshCurrentTimeText(mPosOverride); } } /** * {@inheritDoc} */ @Override public void onStartTrackingTouch(final SeekBar bar) { mLastSeekEventTime = 0; mFromTouch = true; mCurrentTime.setVisibility(View.VISIBLE); } /** * {@inheritDoc} */ @Override public void onStopTrackingTouch(final SeekBar bar) { if (mPosOverride != -1) { MusicUtils.seek(mPosOverride); } mPosOverride = -1; mFromTouch = false; } /** * {@inheritDoc} */ @Override public boolean onPrepareOptionsMenu(final Menu menu) { setShareText(menu); setFavoriteIcon(menu); return true; } /** * {@inheritDoc} */ @Override public boolean onCreateOptionsMenu(final Menu menu) { // Favorite action (later) //getMenuInflater().inflate(R.menu.favorite, menu); // Share, ringtone, and equalizer getMenuInflater().inflate(R.menu.audio_player, menu); return true; } /** * {@inheritDoc} */ @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); return true; case R.id.menu_favorite: // Toggle the current track as a favorite and update the menu // item MusicUtils.toggleFavorite(); invalidateOptionsMenu(); return true; case R.id.menu_audio_player_share: // Share the current meta data shareCurrentTrack(); return true; case R.id.menu_audio_player_stop: try { MusicUtils.mService.stop(); } catch (RemoteException e) { // ignore } finish(); return true; case R.id.menu_audio_player_delete: // Delete current song DeleteDialog.newInstance(MusicUtils.getTrackName(), new long[] { MusicUtils.getCurrentAudioId() }, null).show(getFragmentManager(), "DeleteDialog"); return true; default: break; } return super.onOptionsItemSelected(item); } @Override public void onDelete(long[] ids) { ((QueueFragment) mPagerAdapter.getFragment(0)).refreshQueue(); if (MusicUtils.getQueue().length == 0) { finish(); } } /** * {@inheritDoc} */ @Override public void onBackPressed() { super.onBackPressed(); //NavUtils.goHome(this); } /** * {@inheritDoc} */ @Override protected void onResume() { super.onResume(); // Set the playback drawables updatePlaybackControls(); // Current info updateNowPlayingInfo(); // Refresh the queue ((QueueFragment) mPagerAdapter.getFragment(0)).refreshQueue(); MobclickAgent.onResume(this); } @Override protected void onPause() { super.onPause(); MobclickAgent.onPause(this); } /** * {@inheritDoc} */ @Override protected void onStart() { super.onStart(); final IntentFilter filter = new IntentFilter(); // Play and pause changes filter.addAction(MusicPlaybackService.PLAYSTATE_CHANGED); // Shuffle and repeat changes filter.addAction(MusicPlaybackService.SHUFFLEMODE_CHANGED); filter.addAction(MusicPlaybackService.REPEATMODE_CHANGED); // Track changes filter.addAction(MusicPlaybackService.META_CHANGED); // Update a list, probably the playlist fragment's filter.addAction(MusicPlaybackService.REFRESH); registerReceiver(mPlaybackStatus, filter); // Refresh the current time final long next = refreshCurrentTime(); queueNextRefresh(next); MusicUtils.notifyForegroundStateChanged(this, true); } /** * {@inheritDoc} */ @Override protected void onStop() { super.onStop(); MusicUtils.notifyForegroundStateChanged(this, false); } /** * {@inheritDoc} */ @Override protected void onDestroy() { super.onDestroy(); mIsPaused = false; mTimeHandler.removeMessages(REFRESH_TIME); // Unbind from the service if (mService != null) { MusicUtils.unbindFromService(mToken); mToken = null; } // Unregister the receiver try { unregisterReceiver(mPlaybackStatus); } catch (final Throwable e) { //$FALL-THROUGH$ } } /** * Initializes the items in the now playing screen */ private void initPlaybackControls() { // ViewPager container mPageContainer = (FrameLayout) findViewById(R.id.audio_player_pager_container); // Theme the pager container background mPageContainer.setBackgroundResource(R.drawable.audio_player_pager_container); // Now playing header mAudioPlayerHeader = (LinearLayout) findViewById(R.id.audio_player_header); // Opens the currently playing album profile mAudioPlayerHeader.setOnClickListener(mOpenAlbumProfile); // Used to hide the artwork and show the queue final FrameLayout mSwitch = (FrameLayout) findViewById(R.id.audio_player_switch); mSwitch.setOnClickListener(mToggleHiddenPanel); // Initialize the pager adapter mPagerAdapter = new PagerAdapter(this); // Queue mPagerAdapter.add(QueueFragment.class, null); // Initialize the ViewPager mViewPager = (ViewPager) findViewById(R.id.audio_player_pager); // Attch the adapter mViewPager.setAdapter(mPagerAdapter); // Offscreen pager loading limit mViewPager.setOffscreenPageLimit(mPagerAdapter.getCount() - 1); // Play and pause button mPlayPauseButton = (PlayPauseButton) findViewById(R.id.action_button_play); // Shuffle button mShuffleButton = (ShuffleButton) findViewById(R.id.action_button_shuffle); // Repeat button mRepeatButton = (RepeatButton) findViewById(R.id.action_button_repeat); // Previous button mPreviousButton = (RepeatingImageButton) findViewById(R.id.action_button_previous); // Next button mNextButton = (RepeatingImageButton) findViewById(R.id.action_button_next); // Track name mTrackName = (TextView) findViewById(R.id.audio_player_track_name); // Artist name mArtistName = (TextView) findViewById(R.id.audio_player_artist_name); // Album art mAlbumArt = (ImageView) findViewById(R.id.audio_player_album_art); // Small album art mAlbumArtSmall = (ImageView) findViewById(R.id.audio_player_switch_album_art); // Current time mCurrentTime = (TextView) findViewById(R.id.audio_player_current_time); // Total time mTotalTime = (TextView) findViewById(R.id.audio_player_total_time); // Used to show and hide the queue fragment mQueueSwitch = (ImageView) findViewById(R.id.audio_player_switch_queue); // Progress mProgress = (SeekBar) findViewById(R.id.activity_audio_player_progress); // Set the repeat listner for the previous button mPreviousButton.setRepeatListener(mRewindListener); // Set the repeat listner for the next button mNextButton.setRepeatListener(mFastForwardListener); // Update the progress mProgress.setOnSeekBarChangeListener(this); } /** * Sets the track name, album name, and album art. */ private void updateNowPlayingInfo() { // Set the track name mTrackName.setText(MusicUtils.getTrackName()); // Set the artist and album name mArtistName.setText(getArtistAndAlbumName()); // Set the total time mTotalTime.setText(MusicUtils.makeTimeString(this, MusicUtils.duration() / 1000)); // Set the album art mImageFetcher.loadCurrentArtwork(mAlbumArt); // Set the small artwork mImageFetcher.loadCurrentArtwork(mAlbumArtSmall); // Update the current time queueNextRefresh(1); } private String getArtistAndAlbumName() { String str = ""; String artist = MusicUtils.getArtistName(); String album = MusicUtils.getAlbumName(); if (artist != null && album != null) { str = artist + " - " + album; } else if (artist != null) { str = artist; } else if (album != null) { str = album; } return str; } private long parseIdFromIntent(Intent intent, String longKey, String stringKey, long defaultId) { long id = intent.getLongExtra(longKey, -1); if (id < 0) { String idString = intent.getStringExtra(stringKey); if (idString != null) { try { id = Long.parseLong(idString); } catch (NumberFormatException e) { // ignore } } } return id; } /** * Checks whether the passed intent contains a playback request, * and starts playback if that's the case */ private void startPlayback() { Intent intent = getIntent(); if (intent == null || mService == null) { return; } Uri uri = intent.getData(); String mimeType = intent.getType(); boolean handled = false; if (uri != null && uri.toString().length() > 0) { MusicUtils.playFile(this, uri); handled = true; } else if (Playlists.CONTENT_TYPE.equals(mimeType)) { long id = parseIdFromIntent(intent, "playlistId", "playlist", -1); if (id >= 0) { MusicUtils.playPlaylist(this, id); handled = true; } } else if (Albums.CONTENT_TYPE.equals(mimeType)) { long id = parseIdFromIntent(intent, "albumId", "album", -1); if (id >= 0) { int position = intent.getIntExtra("position", 0); MusicUtils.playAlbum(this, id, position); handled = true; } } else if (Artists.CONTENT_TYPE.equals(mimeType)) { long id = parseIdFromIntent(intent, "artistId", "artist", -1); if (id >= 0) { int position = intent.getIntExtra("position", 0); MusicUtils.playArtist(this, id, position); handled = true; } } if (handled) { // Make sure to process intent only once setIntent(new Intent()); // Refresh the queue ((QueueFragment) mPagerAdapter.getFragment(0)).refreshQueue(); } } /** * Sets the correct drawable states for the playback controls. */ private void updatePlaybackControls() { // Set the play and pause image mPlayPauseButton.updateState(); // Set the shuffle image mShuffleButton.updateShuffleState(); // Set the repeat image mRepeatButton.updateRepeatState(); } private void updateQueueFragmentCurrentSong() { QueueFragment qFragment = (QueueFragment) mPagerAdapter.getFragment(0); qFragment.notifyAdapterDataSetChanged(); } /** * @param delay When to update */ private void queueNextRefresh(final long delay) { if (!mIsPaused) { final Message message = mTimeHandler.obtainMessage(REFRESH_TIME); mTimeHandler.removeMessages(REFRESH_TIME); mTimeHandler.sendMessageDelayed(message, delay); } } /** * Used to scan backwards in time through the curren track * * @param repcnt The repeat count * @param delta The long press duration */ private void scanBackward(final int repcnt, long delta) { if (mService == null) { return; } if (repcnt == 0) { mStartSeekPos = MusicUtils.position(); mLastSeekEventTime = 0; } else { if (delta < 5000) { // seek at 10x speed for the first 5 seconds delta = delta * 10; } else { // seek at 40x after that delta = 50000 + (delta - 5000) * 40; } long newpos = mStartSeekPos - delta; if (newpos < 0) { // move to previous track MusicUtils.previous(this); final long duration = MusicUtils.duration(); mStartSeekPos += duration; newpos += duration; } if (delta - mLastSeekEventTime > 250 || repcnt < 0) { MusicUtils.seek(newpos); mLastSeekEventTime = delta; } if (repcnt >= 0) { mPosOverride = newpos; } else { mPosOverride = -1; } refreshCurrentTime(); } } /** * Used to scan forwards in time through the curren track * * @param repcnt The repeat count * @param delta The long press duration */ private void scanForward(final int repcnt, long delta) { if (mService == null) { return; } if (repcnt == 0) { mStartSeekPos = MusicUtils.position(); mLastSeekEventTime = 0; } else { if (delta < 5000) { // seek at 10x speed for the first 5 seconds delta = delta * 10; } else { // seek at 40x after that delta = 50000 + (delta - 5000) * 40; } long newpos = mStartSeekPos + delta; final long duration = MusicUtils.duration(); if (newpos >= duration) { // move to next track MusicUtils.next(); mStartSeekPos -= duration; // is OK to go negative newpos -= duration; } if (delta - mLastSeekEventTime > 250 || repcnt < 0) { MusicUtils.seek(newpos); mLastSeekEventTime = delta; } if (repcnt >= 0) { mPosOverride = newpos; } else { mPosOverride = -1; } refreshCurrentTime(); } } private void refreshCurrentTimeText(final long pos) { mCurrentTime.setText(MusicUtils.makeTimeString(this, pos / 1000)); } /* Used to update the current time string */ private long refreshCurrentTime() { if (mService == null) { return 500; } try { final long pos = mPosOverride < 0 ? MusicUtils.position() : mPosOverride; if (pos >= 0 && MusicUtils.duration() > 0) { refreshCurrentTimeText(pos); final int progress = (int) (1000 * pos / MusicUtils.duration()); mProgress.setProgress(progress); if (mFromTouch) { return 500; } else if (MusicUtils.isPlaying()) { mCurrentTime.setVisibility(View.VISIBLE); } else { // blink the counter final int vis = mCurrentTime.getVisibility(); mCurrentTime.setVisibility(vis == View.INVISIBLE ? View.VISIBLE : View.INVISIBLE); return 500; } } else { mCurrentTime.setText("--:--"); mProgress.setProgress(1000); } // calculate the number of milliseconds until the next full second, // so // the counter can be updated at just the right time final long remaining = 1000 - pos % 1000; // approximate how often we would need to refresh the slider to // move it smoothly int width = mProgress.getWidth(); if (width == 0) { width = 320; } final long smoothrefreshtime = MusicUtils.duration() / width; if (smoothrefreshtime > remaining) { return remaining; } if (smoothrefreshtime < 20) { return 20; } return smoothrefreshtime; } catch (final Exception ignored) { } return 500; } /** * @param v The view to animate * @param alpha The alpha to apply */ private void fade(final View v, final float alpha) { final ObjectAnimator fade = ObjectAnimator.ofFloat(v, "alpha", alpha); fade.setInterpolator(AnimationUtils.loadInterpolator(this, android.R.anim.accelerate_decelerate_interpolator)); fade.setDuration(400); fade.start(); } /** * Called to show the album art and hide the queue */ private void showAlbumArt() { mPageContainer.setVisibility(View.INVISIBLE); mAlbumArtSmall.setVisibility(View.GONE); mQueueSwitch.setVisibility(View.VISIBLE); // Fade out the pager container fade(mPageContainer, 0f); // Fade in the album art fade(mAlbumArt, 1f); } /** * Called to hide the album art and show the queue */ public void hideAlbumArt() { mPageContainer.setVisibility(View.VISIBLE); mQueueSwitch.setVisibility(View.GONE); mAlbumArtSmall.setVisibility(View.VISIBLE); // Fade out the artwork fade(mAlbumArt, 0f); // Fade in the pager container fade(mPageContainer, 1f); } /** * /** Used to shared what the user is currently listening to */ // private void shareCurrentTrack() { // if (MusicUtils.getTrackName() == null || MusicUtils.getArtistName() == null) { // return; // } // final Intent shareIntent = new Intent(); // final String shareMessage = getString(R.string.now_listening_to, MusicUtils.getTrackName(), MusicUtils.getArtistName()); // // shareIntent.setAction(Intent.ACTION_SEND); // shareIntent.setType("text/plain"); // shareIntent.putExtra(Intent.EXTRA_TEXT, shareMessage); // startActivity(Intent.createChooser(shareIntent, getString(R.string.share_track_using))); // } private void shareCurrentTrack() { try { FileDescriptor fd = Librarian.instance().getFileDescriptor(Constants.FILE_TYPE_AUDIO, (int) MusicUtils.getCurrentAudioId(), false); if (fd != null) { fd.shared = !fd.shared; Librarian.instance().updateSharedStates(fd.fileType, Arrays.asList(fd)); invalidateOptionsMenu(); } } catch (Throwable e) { e.printStackTrace(); } } private void setShareText(Menu menu) { MenuItem item = menu.findItem(R.id.menu_audio_player_share); try { FileDescriptor fd = Librarian.instance().getFileDescriptor(Constants.FILE_TYPE_AUDIO, (int) MusicUtils.getCurrentAudioId()); if (fd != null) { item.setTitle(fd.shared ? R.string.unshare : R.string.share); item.setIcon(fd.shared ? R.drawable.contextmenu_icon_unshare : R.drawable.contextmenu_icon_share); } } catch (Throwable e) { e.printStackTrace(); } } private void setFavoriteIcon(final Menu favorite) { final MenuItem favoriteAction = favorite.findItem(R.id.menu_favorite); if (favoriteAction != null) { if (MusicUtils.isFavorite()) { favoriteAction.setIcon(R.drawable.ic_action_favorite_selected); } else { favoriteAction.setIcon(R.drawable.ic_action_favorite_normal); } } } private void initGestures() { findViewById(R.id.audio_player_album_art).setOnTouchListener(new SwipeDetector()); } private static final class SwipeDetector extends AbstractSwipeDetector { @Override public void onLeftToRightSwipe() { try { MusicUtils.mService.prev(); } catch (RemoteException e) { // ignore } UXStats.instance().log(UXAction.PLAYER_GESTURE_SWIPE_SONG); } @Override public void onRightToLeftSwipe() { try { MusicUtils.mService.next(); } catch (RemoteException e) { // ignore } UXStats.instance().log(UXAction.PLAYER_GESTURE_SWIPE_SONG); } @Override public boolean onMultiTouchEvent(View v, MotionEvent event) { MusicUtils.playOrPause(); UXStats.instance().log(UXAction.PLAYER_GESTURE_PAUSE_RESUME); return true; } } /** * Used to scan backwards through the track */ private final RepeatingImageButton.RepeatListener mRewindListener = new RepeatingImageButton.RepeatListener() { /** * {@inheritDoc} */ @Override public void onRepeat(final View v, final long howlong, final int repcnt) { scanBackward(repcnt, howlong); } }; /** * Used to scan ahead through the track */ private final RepeatingImageButton.RepeatListener mFastForwardListener = new RepeatingImageButton.RepeatListener() { /** * {@inheritDoc} */ @Override public void onRepeat(final View v, final long howlong, final int repcnt) { scanForward(repcnt, howlong); } }; /** * Switches from the large album art screen to show the queue and lyric * fragments, then back again */ private final OnClickListener mToggleHiddenPanel = new OnClickListener() { /** * {@inheritDoc} */ @Override public void onClick(final View v) { if (mPageContainer.getVisibility() == View.VISIBLE) { // Open the current album profile mAudioPlayerHeader.setOnClickListener(mOpenAlbumProfile); // Show the artwork, hide the queue showAlbumArt(); } else { // Scroll to the current track mAudioPlayerHeader.setOnClickListener(mScrollToCurrentSong); // Show the queue, hide the artwork hideAlbumArt(); } } }; /** * Opens to the current album profile */ private final OnClickListener mOpenAlbumProfile = new OnClickListener() { @Override public void onClick(final View v) { // NavUtils.openAlbumProfile(AudioPlayerActivity.this, MusicUtils.getAlbumName(), // MusicUtils.getArtistName(), MusicUtils.getCurrentAlbumId()); } }; /** * Scrolls the queue to the currently playing song */ private final OnClickListener mScrollToCurrentSong = new OnClickListener() { @Override public void onClick(final View v) { ((QueueFragment) mPagerAdapter.getFragment(0)).scrollToCurrentSong(); } }; private final static class StopListener implements View.OnLongClickListener { private WeakReference<Activity> activityRef; public StopListener(Activity activity) { this.activityRef = Ref.weak(activity); } @Override public boolean onLongClick(View v) { try { MusicUtils.mService.stop(); if (Ref.alive(activityRef)) { v.getContext().sendBroadcast(new Intent(Constants.ACTION_MEDIA_PLAYER_STOPPED)); activityRef.get().finish(); } } catch (RemoteException e) { // ignore } return true; } }; /** * Used to update the current time string */ private static final class TimeHandler extends Handler { private final WeakReference<AudioPlayerActivity> mAudioPlayer; /** * Constructor of <code>TimeHandler</code> */ public TimeHandler(final AudioPlayerActivity player) { mAudioPlayer = new WeakReference<AudioPlayerActivity>(player); } @Override public void handleMessage(final Message msg) { switch (msg.what) { case REFRESH_TIME: final long next = mAudioPlayer.get().refreshCurrentTime(); mAudioPlayer.get().queueNextRefresh(next); break; default: break; } } }; /** * Used to monitor the state of playback */ private static final class PlaybackStatus extends BroadcastReceiver { private final WeakReference<AudioPlayerActivity> mReference; /** * Constructor of <code>PlaybackStatus</code> */ public PlaybackStatus(final AudioPlayerActivity activity) { mReference = new WeakReference<AudioPlayerActivity>(activity); } /** * {@inheritDoc} */ @Override public void onReceive(final Context context, final Intent intent) { final String action = intent.getAction(); if (action.equals(MusicPlaybackService.META_CHANGED)) { // Current info mReference.get().updateNowPlayingInfo(); // Update the favorites icon mReference.get().invalidateOptionsMenu(); mReference.get().updateQueueFragmentCurrentSong(); } else if (action.equals(MusicPlaybackService.PLAYSTATE_CHANGED)) { // Set the play and pause image mReference.get().mPlayPauseButton.updateState(); } else if (action.equals(MusicPlaybackService.REPEATMODE_CHANGED) || action.equals(MusicPlaybackService.SHUFFLEMODE_CHANGED)) { // Set the repeat image mReference.get().mRepeatButton.updateRepeatState(); // Set the shuffle image mReference.get().mShuffleButton.updateShuffleState(); } } } }