com.adityarathi.muo.ui.activities.NowPlayingActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.adityarathi.muo.ui.activities.NowPlayingActivity.java

Source

/*
 * Copyright (C) 2014 Saravan Pantham
 *
 * 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.adityarathi.muo.ui.activities;

import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.ScaleAnimation;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;

import com.adityarathi.muo.R;
import com.adityarathi.muo.animations.FadeAnimation;
import com.adityarathi.muo.ui.fragments.BlankFragment;
import com.adityarathi.muo.utils.Common;

import java.util.HashMap;

public class NowPlayingActivity extends FragmentActivity {

    //Common objects.
    private Context mContext;
    private Common mApp;

    //Layouts.
    private DrawerLayout mDrawerLayout;
    private FrameLayout mDrawerParentLayout;
    private RelativeLayout mCurrentQueueLayout;

    //Song info/seekbar elements.
    private SeekBar mSeekbar;
    private ProgressBar mStreamingProgressBar;

    //Playback Controls.
    private RelativeLayout mControlsLayoutHeaderParent;
    private RelativeLayout mControlsLayoutHeader;
    private ImageButton mPlayPauseButton;
    private RelativeLayout mPlayPauseButtonBackground;
    private ImageButton mNextButton;
    private ImageButton mPreviousButton;
    private ImageButton mShuffleButton;
    private ImageButton mRepeatButton;

    //Seekbar indicator.
    private RelativeLayout mSeekbarIndicatorLayoutParent;
    private RelativeLayout mSeekbarIndicatorLayout;
    private TextView mSeekbarIndicatorText;

    //Seekbar strobe effect.
    private AlphaAnimation mSeekbarStrobeAnim;
    private static final int SEEKBAR_STROBE_ANIM_REPEAT = Animation.INFINITE;

    //Playlist pager.
    private ViewPager mViewPager;
    private PlaylistPagerAdapter mViewPagerAdapter;

    //Handler object.
    private Handler mHandler = new Handler();

    //Differentiates between a user's scroll input and a programmatic scroll.
    private boolean USER_SCROLL = true;

    //HashMap that passes on song information to the "Download from Cloud" dialog.
    private HashMap<String, String> metadata;

    //Interface instance and flags.
    private NowPlayingActivityListener mNowPlayingActivityListener;
    public static final String START_SERVICE = "StartService";
    private boolean mIsCreating = true;
    private Animation mFadeInAnimation;

    @SuppressLint("NewApi")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mContext = getApplicationContext();
        mApp = (Common) getApplicationContext();
        mApp.setNowPlayingActivity(this);
        setNowPlayingActivityListener(mApp.getPlaybackKickstarter());
        mFadeInAnimation = AnimationUtils.loadAnimation(mContext, R.anim.slide_in_from_top);
        mFadeInAnimation.setDuration(700);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_now_playing);

        //Set the volume stream for this activity.
        this.setVolumeControlStream(AudioManager.STREAM_MUSIC);

        //ViewPager.
        mViewPager = (ViewPager) findViewById(R.id.nowPlayingPlaylistPager);

        //Seekbar indicator.
        mSeekbarIndicatorLayoutParent = (RelativeLayout) findViewById(R.id.seekbarIndicatorParent);
        mSeekbarIndicatorLayout = (RelativeLayout) findViewById(R.id.seekbarIndicator);
        mSeekbarIndicatorText = (TextView) findViewById(R.id.seekbarIndicatorText);

        mSeekbarIndicatorLayoutParent.setVisibility(View.GONE);

        //Playback Controls.
        mControlsLayoutHeaderParent = (RelativeLayout) findViewById(R.id.now_playing_controls_header_parent);
        mControlsLayoutHeader = (RelativeLayout) findViewById(R.id.now_playing_controls_header);
        mPlayPauseButtonBackground = (RelativeLayout) findViewById(R.id.playPauseButtonBackground);
        mPlayPauseButton = (ImageButton) findViewById(R.id.playPauseButton);
        mNextButton = (ImageButton) findViewById(R.id.nextButton);
        mPreviousButton = (ImageButton) findViewById(R.id.previousButton);
        mShuffleButton = (ImageButton) findViewById(R.id.shuffleButton);
        mRepeatButton = (ImageButton) findViewById(R.id.repeatButton);

        //Song info/seekbar elements.
        mSeekbar = (SeekBar) findViewById(R.id.nowPlayingSeekBar);
        mStreamingProgressBar = (ProgressBar) findViewById(R.id.startingStreamProgressBar);
        mStreamingProgressBar.setVisibility(View.GONE);

        try {
            mSeekbar.setThumb(getResources().getDrawable(R.drawable.transparent_drawable));
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (mApp.getCurrentTheme() == Common.DARK_THEME) {
            mNextButton.setAlpha(1f);
            mPreviousButton.setAlpha(1f);
        }

        //KitKat specific layout code.
        setKitKatTranslucentBars();

        //Set the control buttons and background.
        //setControlButtonsBackground();
        setPlayPauseButton();
        setShuffleButtonIcon();
        setRepeatButtonIcon();

        //Set the click listeners.
        mSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
        mNextButton.setOnClickListener(mOnClickNextListener);
        mPreviousButton.setOnClickListener(mOnClickPreviousListener);
        mPlayPauseButton.setOnClickListener(playPauseClickListener);
        mPlayPauseButtonBackground.setOnClickListener(playPauseClickListener);
        mShuffleButton.setOnClickListener(shuffleButtonClickListener);
        mRepeatButton.setOnClickListener(repeatButtonClickListener);

        //Apply haptic feedback to the play/pause button.
        mPlayPauseButtonBackground.setHapticFeedbackEnabled(true);
        mPlayPauseButton.setHapticFeedbackEnabled(true);

    }

    /**
     * Updates this activity's UI elements based on the passed intent's 
     * update flag(s).
     */
    BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {

            //Grab the bundle from the intent.
            Bundle bundle = intent.getExtras();

            //Initializes the ViewPager.
            if (intent.hasExtra(Common.INIT_PAGER) || intent.hasExtra(Common.NEW_QUEUE_ORDER))
                initViewPager();

            //Updates the ViewPager's current page/position.
            if (intent.hasExtra(Common.UPDATE_PAGER_POSTIION)) {
                int currentPosition = mViewPager.getCurrentItem();
                int newPosition = Integer.parseInt(bundle.getString(Common.UPDATE_PAGER_POSTIION));
                if (currentPosition != newPosition) {

                    if (newPosition > 0 && Math.abs(newPosition - currentPosition) <= 5) {
                        //Smooth scroll to the new index.
                        //scrollViewPager(newPosition, true, 1, false);
                    } else {
                        //The new index is too far away, so avoid smooth scrolling to it.
                        mViewPager.setCurrentItem(newPosition, false);
                    }

                    //Reinit the seekbar update handler.
                    mHandler.post(seekbarUpdateRunnable);

                }

            }

            //Updates the playback control buttons.
            if (intent.hasExtra(Common.UPDATE_PLAYBACK_CONTROLS)) {
                setPlayPauseButton();
                setRepeatButtonIcon();
                setShuffleButtonIcon();

            }

            //Displays the audibook toast.
            if (intent.hasExtra(Common.SHOW_AUDIOBOOK_TOAST))
                displayAudiobookToast(Long.parseLong(bundle.getString(Common.SHOW_AUDIOBOOK_TOAST)));

            //Updates the duration of the SeekBar.
            if (intent.hasExtra(Common.UPDATE_SEEKBAR_DURATION))
                setSeekbarDuration(Integer.parseInt(bundle.getString(Common.UPDATE_SEEKBAR_DURATION)));

            //Hides the seekbar and displays the streaming progress bar.
            if (intent.hasExtra(Common.SHOW_STREAMING_BAR)) {
                mSeekbar.setVisibility(View.INVISIBLE);
                mStreamingProgressBar.setVisibility(View.VISIBLE);
                mHandler.removeCallbacks(seekbarUpdateRunnable);

            }

            //Shows the seekbar and hides the streaming progress bar.
            if (intent.hasExtra(Common.HIDE_STREAMING_BAR)) {
                mSeekbar.setVisibility(View.VISIBLE);
                mStreamingProgressBar.setVisibility(View.INVISIBLE);
                mHandler.postDelayed(seekbarUpdateRunnable, 100);
            }

            //Updates the buffering progress on the seekbar.
            if (intent.hasExtra(Common.UPDATE_BUFFERING_PROGRESS))
                mSeekbar.setSecondaryProgress(Integer.parseInt(bundle.getString(Common.UPDATE_BUFFERING_PROGRESS)));

            //Close this activity if the service is about to stop running.
            if (intent.hasExtra(Common.SERVICE_STOPPING)) {
                mHandler.removeCallbacks(seekbarUpdateRunnable);
                finish();
            }

        }

    };

    /**
     * Initializes the view pager.
     */
    private void initViewPager() {

        try {
            mViewPager.setVisibility(View.INVISIBLE);
            mViewPagerAdapter = new PlaylistPagerAdapter(getSupportFragmentManager());
            mViewPager.setAdapter(mViewPagerAdapter);
            mViewPager.setOffscreenPageLimit(0);
            mViewPager.setOnPageChangeListener(mPageChangeListener);
            mViewPager.setCurrentItem(mApp.getService().getCurrentSongIndex(), false);

        } catch (IllegalStateException e) {
            /*
             * Catches any exceptions that may occur
             * as a result of the user rapidly changing
             * their device's orientation.
             */
        }

        //Delay loading extra fragments by 1000ms.
        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                mViewPager.setOffscreenPageLimit(10);
            }

        }, 1000);

    }

    /**
     * Initializes the current queue drawer/layout.
     */
    private void initDrawer() {
        //Load the current queue drawer.

    }

    /**
     * Slides in the controls bar from the bottom along with a
     * slight rotation.
     */
    private void animateInControlsBar() {
        android.view.animation.TranslateAnimation slideUp = new android.view.animation.TranslateAnimation(
                Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF, 0.0f, Animation.RELATIVE_TO_SELF,
                2.0f, Animation.RELATIVE_TO_SELF, 0.0f);
        slideUp.setDuration(300);
        slideUp.setInterpolator(new DecelerateInterpolator(2.0f));

        slideUp.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {
                mControlsLayoutHeaderParent.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {

            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }

        });

        mControlsLayoutHeaderParent.startAnimation(slideUp);
    }

    /**
     * Sets the play/pause button states.
     */
    private void setPlayPauseButton() {
        if (mApp.isServiceRunning()) {
            if (mApp.getService().isPlayingMusic()) {
                animatePlayToPause();
                stopSeekbarStrobeEffect();
            } else {
                animatePauseToPlay();
                initSeekbarStrobeEffect();
            }

        }

    }

    /**
     * Animates the play button to a pause button.
     */
    private void animatePlayToPause() {

        //Check to make sure the current icon is the play icon.
        if (mPlayPauseButton.getId() != R.drawable.ic_play)
            return;

        //Fade out the play button.
        final ScaleAnimation scaleOut = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f, mPlayPauseButton.getWidth() / 2,
                mPlayPauseButton.getHeight() / 2);
        scaleOut.setDuration(150);
        scaleOut.setInterpolator(new AccelerateInterpolator());

        //Scale in the pause button.
        final ScaleAnimation scaleIn = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, mPlayPauseButton.getWidth() / 2,
                mPlayPauseButton.getHeight() / 2);
        scaleIn.setDuration(150);
        scaleIn.setInterpolator(new DecelerateInterpolator());

        scaleOut.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mPlayPauseButton.setImageResource(R.drawable.ic_play);
                mPlayPauseButton.setPadding(0, 0, 0, 0);
                mPlayPauseButton.startAnimation(scaleIn);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }

        });

        scaleIn.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mPlayPauseButton.setScaleX(1.0f);
                mPlayPauseButton.setScaleY(1.0f);
                mPlayPauseButton.setId(R.drawable.ic_play);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }

        });

        mPlayPauseButton.startAnimation(scaleOut);
    }

    /**
     * Animates the pause button to a play button.
     */
    private void animatePauseToPlay() {

        //Check to make sure the current icon is the pause icon.
        if (mPlayPauseButton.getId() != R.drawable.ic_play)
            return;

        //Scale out the pause button.
        final ScaleAnimation scaleOut = new ScaleAnimation(1.0f, 0.0f, 1.0f, 0.0f, mPlayPauseButton.getWidth() / 2,
                mPlayPauseButton.getHeight() / 2);
        scaleOut.setDuration(150);
        scaleOut.setInterpolator(new AccelerateInterpolator());

        //Scale in the play button.
        final ScaleAnimation scaleIn = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, mPlayPauseButton.getWidth() / 2,
                mPlayPauseButton.getHeight() / 2);
        scaleIn.setDuration(150);
        scaleIn.setInterpolator(new DecelerateInterpolator());

        scaleOut.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mPlayPauseButton.setImageResource(R.drawable.ic_play);
                mPlayPauseButton.setPadding(0, 0, -5, 0);
                mPlayPauseButton.startAnimation(scaleIn);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }

        });

        scaleIn.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mPlayPauseButton.setScaleX(1.0f);
                mPlayPauseButton.setScaleY(1.0f);
                mPlayPauseButton.setId(R.drawable.ic_play);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }

        });

        mPlayPauseButton.startAnimation(scaleOut);
    }

    /**
     * Sets the repeat button icon based on the current repeat mode.
     */
    private void setRepeatButtonIcon() {
        if (mApp.isServiceRunning())
            if (mApp.getService().getRepeatMode() == Common.REPEAT_OFF) {
                mRepeatButton.setImageResource(R.drawable.ic_play);
            } else if (mApp.getService().getRepeatMode() == Common.REPEAT_PLAYLIST) {
                mRepeatButton.setImageResource(R.drawable.ic_play);
            } else if (mApp.getService().getRepeatMode() == Common.REPEAT_SONG) {
                mRepeatButton.setImageResource(R.drawable.ic_play);
            } else if (mApp.getService().getRepeatMode() == Common.A_B_REPEAT) {
                mRepeatButton.setImageResource(R.drawable.ic_play);
            }

            else
                mRepeatButton.setImageResource(R.drawable.ic_play);

    }

    /**
     * Sets the shuffle button icon based on the current shuffle mode.
     */
    private void setShuffleButtonIcon() {
        if (mApp.isServiceRunning())
            if (mApp.getService().isShuffleOn() == true) {
                mShuffleButton.setImageResource(R.drawable.ic_play);
            } else {
                mShuffleButton.setImageResource(R.drawable.ic_play);
            }

        else
            mShuffleButton.setImageResource(R.drawable.ic_play);

    }

    /**
     * Sets the seekbar's duration. Also updates the 
     * elapsed/remaining duration text.
     */
    private void setSeekbarDuration(int duration) {
        mSeekbar.setMax(duration);
        mSeekbar.setProgress(mApp.getService().getCurrentMediaPlayer().getCurrentPosition() / 1000);
        mHandler.postDelayed(seekbarUpdateRunnable, 100);
    }

    /**
     * Sets the KitKat translucent status/nav bar and adjusts 
     * the views' boundaries.
     */
    private void setKitKatTranslucentBars() {
        //KitKat translucent status bar.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            int statusBarHeight = Common.getStatusBarHeight(mContext);
            int navigationBarHeight = Common.getNavigationBarHeight(mContext);

            if (mDrawerParentLayout != null) {
                mDrawerParentLayout.setClipToPadding(false);
                mDrawerParentLayout.setPadding(0, 0, 0, 0);
            }

            if (mControlsLayoutHeaderParent != null) {
                int bottomPadding = mControlsLayoutHeaderParent.getPaddingBottom();
                mControlsLayoutHeaderParent.setClipToPadding(false);
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) mControlsLayoutHeaderParent
                        .getLayoutParams();

                if (navigationBarHeight > 0) {
                    /* The nav bar already has padding, so remove the extra 15dp
                     * margin that was applied in the layout file.
                     */
                    params.bottomMargin = 0;
                }

                params.bottomMargin += navigationBarHeight;
                mControlsLayoutHeaderParent.setLayoutParams(params);

            }

        }

    }

    /**
     * Seekbar change listener.
     */
    private OnSeekBarChangeListener seekBarChangeListener = new OnSeekBarChangeListener() {

        @Override
        public void onProgressChanged(SeekBar seekBar, int seekBarPosition, boolean changedByUser) {

            try {
                long currentSongDuration = mApp.getService().getCurrentMediaPlayer().getDuration();
                seekBar.setMax((int) currentSongDuration / 1000);

                if (changedByUser)
                    mSeekbarIndicatorText.setText(mApp.convertMillisToMinsSecs(seekBar.getProgress() * 1000));
            } catch (Exception e) {
                e.printStackTrace();
            }

        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            mHandler.removeCallbacks(seekbarUpdateRunnable);
            mHandler.removeCallbacks(fadeOutSeekbarIndicator);

            mSeekbarIndicatorLayoutParent.setVisibility(View.VISIBLE);
            mSeekbarIndicatorLayout.setAlpha(0.8f);

        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            int seekBarPosition = seekBar.getProgress();
            mApp.getService().getCurrentMediaPlayer().seekTo(seekBarPosition * 1000);

            //Reinitiate the handler.
            mHandler.post(seekbarUpdateRunnable);

            //Fade out the indicator after 1000ms.
            mHandler.postDelayed(fadeOutSeekbarIndicator, 1000);

        }

    };

    /**
     * Seekbar change indicator.
     */
    private Runnable fadeOutSeekbarIndicator = new Runnable() {

        @Override
        public void run() {
            FadeAnimation fadeOut = new FadeAnimation(mSeekbarIndicatorLayoutParent, 300, 0.9f, 0.0f, null);
            fadeOut.animate();
        }

    };

    /**
     * Repeat button click listener.
     */
    private OnClickListener repeatButtonClickListener = new OnClickListener() {

        @Override
        public void onClick(View arg0) {

            mApp.getService().clearABRepeatRange();
            if (mApp.getService().getRepeatMode() == Common.REPEAT_OFF) {
                mRepeatButton.setImageResource(R.drawable.ic_play);
                mApp.getService().setRepeatMode(Common.REPEAT_PLAYLIST);

            } else if (mApp.getService().getRepeatMode() == Common.REPEAT_PLAYLIST) {
                mRepeatButton.setImageResource(R.drawable.ic_play);
                mApp.getService().setRepeatMode(Common.REPEAT_SONG);

            } else {
                mRepeatButton.setImageResource(R.drawable.ic_play);
                mApp.getService().setRepeatMode(Common.REPEAT_OFF);

            }

        }

    };

    /**
     * Shuffle button click listener.
     */
    private OnClickListener shuffleButtonClickListener = new OnClickListener() {

        @Override
        public void onClick(View arg0) {
            //Toggle shuffle on/off.
            boolean shuffleOn = mApp.getService().toggleShuffleMode();

            if (shuffleOn)
                mShuffleButton.setImageResource(R.drawable.ic_play);
            else
                mShuffleButton.setImageResource(R.drawable.ic_play);

        }

    };

    /**
     * Click listener for the play/pause button.
     */
    private OnClickListener playPauseClickListener = new OnClickListener() {

        @Override
        public void onClick(View view) {

            //BZZZT! Give the user a brief haptic feedback touch response.
            view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);

            //Update the playback UI elements.
            if (mApp.getService().isPlayingMusic()) {
                animatePauseToPlay();
                mHandler.removeCallbacks(seekbarUpdateRunnable);
            } else {
                animatePlayToPause();
                mHandler.post(seekbarUpdateRunnable);
            }

            /*
             * Toggle the playback state in a separate thread. This
             * will allow the play/pause button animation to remain
             * buttery smooth.
             */
            new AsyncTask() {

                @Override
                protected Object doInBackground(Object[] params) {
                    mApp.getService().togglePlaybackState();
                    return null;
                }

            }.execute();

        }

    };

    /**
     * Click listener for the previous button.
     */
    private OnClickListener mOnClickPreviousListener = new OnClickListener() {

        @Override
        public void onClick(View arg0) {

            //Remove the seekbar update runnable.
            mHandler.removeCallbacks(seekbarUpdateRunnable);

            /*
             * Scrolling the pager will automatically call the skipToTrack() method. 
             * Since we're passing true for the dispatchToListener parameter, the 
             * onPageSelected() listener will receive a callback once the scrolling 
             * animation completes. This has the side-benefit of letting the animation 
             * finish before starting playback (keeps the animation buttery smooth).
             */
            int newPosition = mViewPager.getCurrentItem() - 1;
            if (newPosition > -1) {
                //scrollViewPager(newPosition, true, 1, true);
            } else {
                mViewPager.setCurrentItem(0, false);
            }

        }

    };

    /**
     * Click listener for the next button.
     */
    private OnClickListener mOnClickNextListener = new OnClickListener() {

        @Override
        public void onClick(View arg0) {

            //Remove the seekbar update runnable.
            mHandler.removeCallbacks(seekbarUpdateRunnable);

            /*
             * Scrolling the pager will automatically call the skipToTrack() method. 
             * Since we're passing true for the dispatchToListener parameter, the 
             * onPageSelected() listener will receive a callback once the scrolling 
             * animation completes. This has the side-benefit of letting the animation 
             * finish before starting playback (keeps the animation buttery smooth).
             */
            int newPosition = mViewPager.getCurrentItem() + 1;
            if (newPosition < mViewPagerAdapter.getCount()) {
                //scrollViewPager(newPosition, true, 1, true);
            } else {
                if (mApp.getService().getRepeatMode() == Common.REPEAT_PLAYLIST)
                    mViewPager.setCurrentItem(0, false);
                else
                    Toast.makeText(mContext, R.string.no_songs_to_skip_to, Toast.LENGTH_SHORT).show();
            }

            //mApp.getService().skipToNextTrack();

        }

    };

    /**
     * Downloads a GMusic song for local playback.
     */
    /*private void pinSong() {
           
       //Check if the app is getting pinned songs from the official GMusic app.
      if (mApp.isFetchingPinnedSongs()==false) {
         
     //Retrieve the name of the song.
     mApp.getService().getCursor().moveToPosition(mApp.getService().getPlaybackIndecesList().get(mApp.getService().getCurrentSongIndex()));
         
     //Get the song's ID/title.
     String songID = mApp.getService().getCursor().getString(mApp.getService().getCursor().getColumnIndex(DBAccessHelper.SONG_ID));
     String songTitle = mApp.getService().getCursor().getString(mApp.getService().getCursor().getColumnIndex(DBAccessHelper.SONG_TITLE));
        
      //Check if a local copy of the song exists.
      String localCopyPath = mApp.getDBAccessHelper().getLocalCopyPath(songID);
          
     if (localCopyPath!=null) {
         if (localCopyPath.isEmpty() || localCopyPath.equals("")) {
            if (mMenu!=null) {
               mMenu.findItem(R.id.action_pin).setIcon(R.drawable.pin_highlighted);
            }
                
            //Get a mCursor with the current song and initiate the download process.
            String selection = " AND " + DBAccessHelper.SONG_ID + "=" + "'" + songID + "'";
            mApp.queueSongsToPin(false, false, selection);
            String toastMessage = getResources().getString(R.string.downloading_no_dot) + " " + songTitle + ".";
           Toast.makeText(mContext, toastMessage, Toast.LENGTH_SHORT).show();
         } else {
            if (mMenu!=null) {
               mMenu.findItem(R.id.action_pin).setIcon(R.drawable.pin_light);
            }
                
             String selection = " AND " + DBAccessHelper.SONG_ID + "=" + "'" + songID + "'";
             AsyncRemovePinnedSongsTask task = new AsyncRemovePinnedSongsTask(mContext, selection, null);
             task.execute();
         }
      } else {
        if (mMenu!=null) {
           mMenu.findItem(R.id.action_pin).setIcon(R.drawable.pin_highlighted);
        }
             
        //Get a mCursor with the current song and initiate the download process.
         String selection = " AND " + DBAccessHelper.SONG_ID + "=" + "'" + songID + "'";
        mApp.queueSongsToPin(false, false, selection);
      }
         
      } else {
     Toast.makeText(mContext, R.string.wait_until_pinning_complete, Toast.LENGTH_SHORT).show();
      }
          
    }*/

    /**
     * Provides callback methods when the ViewPager's position/current page has changed.
     */
    private ViewPager.OnPageChangeListener mPageChangeListener = new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageScrollStateChanged(int scrollState) {
            if (scrollState == ViewPager.SCROLL_STATE_DRAGGING)
                USER_SCROLL = true;

        }

        @Override
        public void onPageScrolled(final int pagerPosition, float swipeVelocity, int offsetFromCurrentPosition) {

            /* swipeVelocity determines whether the viewpager has finished scrolling or not.
             * Throw in an if statement that only allows the track to change when
             * swipeVelocity is 0 (which means the page is done scrolling). This ensures
             * that the tracks don't jump around or get truncated while the user is 
             * swiping between different pages.
             */

            if (mApp.isServiceRunning() && mApp.getService().getCursor().getCount() != 1) {

                /* Change tracks ONLY when the user has finished the swiping gesture (swipeVelocity will be zero).
                 * Also, don't skip tracks if the new pager position is the same as the current mCursor position (indicates 
                 * that the starting and ending position of the pager is the same).
                 */
                if (swipeVelocity == 0.0f && pagerPosition != mApp.getService().getCurrentSongIndex()) {
                    if (USER_SCROLL) {
                        mHandler.removeCallbacks(seekbarUpdateRunnable);
                        smoothScrollSeekbar(0);

                        mHandler.postDelayed(new Runnable() {

                            @Override
                            public void run() {
                                mApp.getService().skipToTrack(pagerPosition);
                            }

                        }, 200);

                    }

                }

            }

        }

        @Override
        public void onPageSelected(int newPosition) {
            //TODO Auto-generated method stub.

        }

    };

    /**
     * @deprecated
     * Applies the correct transformer effect to the ViewPager.
     */
    @Deprecated
    private void setPlaylistPagerAnimation() {
        if (mApp.getSharedPreferences().getInt("TRACK_CHANGE_ANIMATION", 0) == 0) {
            //mViewPager.setPageTransformer(true, new ZoomOutPageTransformer(0.85f));
        } else if (mApp.getSharedPreferences().getInt("TRACK_CHANGE_ANIMATION", 0) == 1) {
            //mViewPager.setPageTransformer(true, new ZoomOutPageTransformer(0.85f));
        } else if (mApp.getSharedPreferences().getInt("TRACK_CHANGE_ANIMATION", 0) == 2) {
            //mViewPager.setPageTransformer(true, new ZoomOutPageTransformer(0.85f));
        }

    }

    /**
     * Create a new Runnable to update the seekbar and time every 100ms.
     */
    public Runnable seekbarUpdateRunnable = new Runnable() {

        public void run() {

            try {
                long currentPosition = mApp.getService().getCurrentMediaPlayer().getCurrentPosition();
                int currentPositionInSecs = (int) currentPosition / 1000;
                smoothScrollSeekbar(currentPositionInSecs);

                //mSeekbar.setProgress(currentPositionInSecs);
                mHandler.postDelayed(seekbarUpdateRunnable, 100);

            } catch (Exception e) {
                e.printStackTrace();
            }

        }

    };

    /**
     * Smoothly scrolls the seekbar to the indicated position.
     */
    private void smoothScrollSeekbar(int progress) {
        ObjectAnimator animation = ObjectAnimator.ofInt(mSeekbar, "progress", progress);
        animation.setDuration(200);
        animation.setInterpolator(new DecelerateInterpolator());
        animation.start();

    }

    /**
     * Initiates the strobe effect on the seekbar.
     */
    private void initSeekbarStrobeEffect() {
        mSeekbarStrobeAnim = new AlphaAnimation(1.0f, 0.0f);
        mSeekbarStrobeAnim.setRepeatCount(SEEKBAR_STROBE_ANIM_REPEAT);
        mSeekbarStrobeAnim.setDuration(700);
        mSeekbarStrobeAnim.setRepeatMode(Animation.REVERSE);

        mSeekbar.startAnimation(mSeekbarStrobeAnim);

    }

    /**
     * Stops the seekbar strobe effect.
     */
    private void stopSeekbarStrobeEffect() {
        mSeekbarStrobeAnim = new AlphaAnimation(mSeekbar.getAlpha(), 1.0f);
        mSeekbarStrobeAnim.setDuration(700);
        mSeekbar.startAnimation(mSeekbarStrobeAnim);

    }

    public class PlaylistPagerAdapter extends FragmentStatePagerAdapter {

        public PlaylistPagerAdapter(FragmentManager fm) {
            super(fm);

        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            super.destroyItem(container, position, object);

        }

        //This method controls the layout that is shown on each screen.
        @Override
        public Fragment getItem(int position) {

            /* PlaylistPagerFragment.java will be shown on every pager screen. However, 
             * the fragment will check which screen (position) is being shown, and will
             * update its TextViews and ImageViews to match the song that's being played. */
            Fragment fragment = new BlankFragment();

            Bundle bundle = new Bundle();
            bundle.putInt("POSITION", position);
            fragment.setArguments(bundle);
            return fragment;

        }

        @Override
        public int getCount() {

            try {
                if (mApp.getService().getPlaybackIndecesList() != null) {
                    return mApp.getService().getPlaybackIndecesList().size();
                } else {
                    mApp.getService().stopSelf();
                    return 0;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }

        }

    }

    /**
     * Displays the "Resuming from xx:xx" toast.
     */
    public void displayAudiobookToast(long resumePlaybackPosition) {
        try {
            String resumingFrom = mContext.getResources().getString(R.string.resuming_from) + " "
                    + mApp.convertMillisToMinsSecs(resumePlaybackPosition) + ".";

            Toast.makeText(mContext, resumingFrom, Toast.LENGTH_LONG).show();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * Toggles the open/closed state of the current queue drawer.
     */
    public void toggleCurrentQueueDrawer() {
        if (mDrawerLayout == null)
            return;

        if (mDrawerLayout.isDrawerOpen(Gravity.END))
            mDrawerLayout.closeDrawer(Gravity.END);
        else
            mDrawerLayout.openDrawer(Gravity.END);

    }

    /*
     * Getter and setter methods.
     */

    public SeekBar getSeekbar() {
        return mSeekbar;
    }

    public ProgressBar getStreamingProgressBar() {
        return mStreamingProgressBar;
    }

    public ViewPager getPlaylistViewPager() {
        return mViewPager;
    }

    public NowPlayingActivityListener getNowPlayingActivityListener() {
        return mNowPlayingActivityListener;
    }

    public void setNowPlayingActivityListener(NowPlayingActivityListener listener) {
        mNowPlayingActivityListener = listener;
    }

    /**
     * Interface that provides callbacks once this activity is 
     * up and running.
     */
    public interface NowPlayingActivityListener {

        /**
         * Called once this activity's onResume() method finishes 
         * executing.
         */
        public void onNowPlayingActivityReady();

    }

    @Override
    public void onResume() {
        super.onResume();

        if (mIsCreating == false) {
            setKitKatTranslucentBars();
            mHandler.postDelayed(seekbarUpdateRunnable, 100);
            mIsCreating = false;
        }

        //Animate the controls bar in.
        //animateInControlsBar();
        //Update the seekbar.
        try {
            setSeekbarDuration(mApp.getService().getCurrentMediaPlayer().getDuration() / 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //Load the drawer 1000ms after the activity is loaded.
        mHandler.postDelayed(new Runnable() {

            @Override
            public void run() {
                initDrawer();
            }

        }, 1000);

        if (getIntent().hasExtra(START_SERVICE) && getNowPlayingActivityListener() != null) {
            getNowPlayingActivityListener().onNowPlayingActivityReady();

            /**
             * To prevent the service from being restarted every time this
             * activity is resume, we're gonna have to remove the "START_SERVICE"
             * extra from the intent.
             */
            getIntent().removeExtra(START_SERVICE);

        }

    }

    @Override
    public void onPause() {
        super.onPause();
        if (isFinishing())
            mApp.setNowPlayingActivity(null);

    }

    @Override
    public void onStart() {
        super.onStart();
        //Initialize the broadcast manager that will listen for track changes.
        LocalBroadcastManager.getInstance(mContext).registerReceiver((mReceiver),
                new IntentFilter(Common.UPDATE_UI_BROADCAST));

        /* Check if the service is up and running. If so, send out a broadcast message 
         * that will initialize this activity fully. This code block is what will 
         * initialize this activity fully if it is opened after the service is already 
         * up and running (the onServiceRunning() callback isn't available at this point).
         */
        if (mApp.isServiceRunning() && mApp.getService().getCursor() != null) {
            String[] updateFlags = new String[] { Common.UPDATE_PAGER_POSTIION, Common.UPDATE_SEEKBAR_DURATION,
                    Common.HIDE_STREAMING_BAR, Common.INIT_PAGER, Common.UPDATE_PLAYBACK_CONTROLS,
                    Common.UPDATE_EQ_FRAGMENT };

            String[] flagValues = new String[] { "" + mApp.getService().getCurrentSongIndex(),
                    "" + mApp.getService().getCurrentMediaPlayer().getDuration(), "", "", "", "" };
            mApp.broadcastUpdateUICommand(updateFlags, flagValues);
        }

    }

    @Override
    public void onStop() {
        //Unregister the broadcast receivers.
        LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);
        super.onStop();

    }

    @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
        super.onSaveInstanceState(savedInstanceState);
        savedInstanceState.putBoolean("CALLED_FROM_FOOTER", true);
        savedInstanceState.putBoolean("CALLED_FROM_NOTIF", true);
    }

}