com.fastbootmobile.encore.app.fragments.PlaybackQueueFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.fastbootmobile.encore.app.fragments.PlaybackQueueFragment.java

Source

/*
 * Copyright (C) 2014 Fastboot Mobile, LLC.
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program;
 * if not, see <http://www.gnu.org/licenses>.
 */

package com.fastbootmobile.encore.app.fragments;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityOptions;
import android.support.v4.app.Fragment;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.graphics.Palette;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.Toast;

import com.echonest.api.v4.EchoNestException;

import com.fastbootmobile.encore.api.echonest.AutoMixBucket;
import com.fastbootmobile.encore.api.echonest.AutoMixManager;
import com.fastbootmobile.encore.app.AlbumActivity;
import com.fastbootmobile.encore.app.MainActivity;
import com.fastbootmobile.encore.app.R;
import com.fastbootmobile.encore.app.adapters.PlaybackQueueAdapter;
import com.fastbootmobile.encore.app.ui.MaterialTransitionDrawable;
import com.fastbootmobile.encore.app.ui.PlayPauseDrawable;
import com.fastbootmobile.encore.framework.ListenLogger;
import com.fastbootmobile.encore.framework.PlaybackProxy;
import com.fastbootmobile.encore.model.Album;
import com.fastbootmobile.encore.model.Artist;
import com.fastbootmobile.encore.model.Playlist;
import com.fastbootmobile.encore.model.SearchResult;
import com.fastbootmobile.encore.model.Song;
import com.fastbootmobile.encore.providers.ILocalCallback;
import com.fastbootmobile.encore.providers.IMusicProvider;
import com.fastbootmobile.encore.providers.ProviderAggregator;
import com.fastbootmobile.encore.service.BasePlaybackCallback;
import com.fastbootmobile.encore.service.PlaybackService;
import com.fastbootmobile.encore.utils.Utils;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
 * Simple fragment for the activity contents
 */
public class PlaybackQueueFragment extends Fragment {
    private static final String TAG = "PlaybackQueueFrag";

    private View.OnClickListener mArtClickListener = new View.OnClickListener() {
        @Override
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public void onClick(View view) {
            Bitmap hero = ((MaterialTransitionDrawable) ((ImageView) view).getDrawable()).getFinalDrawable()
                    .getBitmap();
            Palette palette = Palette.from(hero).generate();

            Palette.Swatch darkVibrantColor = palette.getDarkVibrantSwatch();
            Palette.Swatch darkMutedColor = palette.getDarkMutedSwatch();

            int color;
            if (darkVibrantColor != null) {
                color = darkVibrantColor.getRgb();
            } else if (darkMutedColor != null) {
                color = darkMutedColor.getRgb();
            } else {
                color = getResources().getColor(R.color.default_album_art_background);
            }

            Song song = (Song) view.getTag();

            Intent intent = AlbumActivity.craftIntent(getActivity(), hero, song.getAlbum(), song.getProvider(),
                    color);

            if (Utils.hasLollipop()) {
                ActivityOptions opt = ActivityOptions.makeSceneTransitionAnimation(getActivity(), view,
                        "itemImage");

                getActivity().startActivity(intent, opt.toBundle());
            } else {
                getActivity().startActivity(intent);
            }
        }
    };

    private BasePlaybackCallback mPlaybackListener = new BasePlaybackCallback() {
        @Override
        public void onSongStarted(final boolean buffering, Song s) throws RemoteException {
            mHandler.sendEmptyMessage(MSG_UPDATE_SEEKBAR);
            mHandler.sendEmptyMessage(MSG_UPDATE_QUEUE);
            mHandler.obtainMessage(MSG_UPDATE_PLAYSTATE,
                    buffering ? PLAYSTATE_ARG1_BUFFERING : PLAYSTATE_ARG1_NOT_BUFFERING,
                    PlayPauseDrawable.SHAPE_PAUSE).sendToTarget();
        }

        @Override
        public void onPlaybackPause() throws RemoteException {
            mHandler.obtainMessage(MSG_UPDATE_PLAYSTATE, PLAYSTATE_ARG1_NOT_BUFFERING, PlayPauseDrawable.SHAPE_PLAY)
                    .sendToTarget();
        }

        @Override
        public void onPlaybackResume() throws RemoteException {
            mHandler.sendEmptyMessage(MSG_UPDATE_SEEKBAR);
            mHandler.obtainMessage(MSG_UPDATE_PLAYSTATE, PLAYSTATE_ARG1_NOT_BUFFERING,
                    PlayPauseDrawable.SHAPE_PAUSE).sendToTarget();
        }

        @Override
        public void onPlaybackQueueChanged() throws RemoteException {
            mHandler.sendEmptyMessage(MSG_UPDATE_QUEUE);
        }
    };

    private ILocalCallback mProviderCallback = new ILocalCallback() {
        @Override
        public void onSongUpdate(List<Song> s) {
            boolean contains = false;
            final List<Song> playbackQueue = PlaybackProxy.getCurrentPlaybackQueue();
            for (Song song : s) {
                if (playbackQueue.contains(song)) {
                    contains = true;
                    break;
                }
            }

            if (contains) {
                mHandler.sendEmptyMessage(MSG_UPDATE_QUEUE);
            }
        }

        @Override
        public void onAlbumUpdate(List<Album> a) {
        }

        @Override
        public void onPlaylistUpdate(List<Playlist> p) {
        }

        @Override
        public void onPlaylistRemoved(String ref) {
        }

        @Override
        public void onArtistUpdate(List<Artist> a) {
            boolean contains = false;
            List<Song> playbackQueue = PlaybackProxy.getCurrentPlaybackQueue();
            for (Song song : playbackQueue) {
                for (Artist artist : a) {
                    if (artist.getRef().equals(song.getArtist())) {
                        contains = true;
                        break;
                    }
                }

                if (contains) {
                    break;
                }
            }

            if (contains) {
                mHandler.sendEmptyMessage(MSG_UPDATE_QUEUE);
            }
        }

        @Override
        public void onProviderConnected(IMusicProvider provider) {
        }

        @Override
        public void onSearchResult(List<SearchResult> searchResult) {
        }
    };

    private static final int SEEK_UPDATE_DELAY = 1000 / 15;

    private static final int MSG_UPDATE_SEEKBAR = 1;
    private static final int MSG_UPDATE_QUEUE = 2;
    private static final int MSG_UPDATE_PLAYSTATE = 3;

    private static final int PLAYSTATE_ARG1_NOT_BUFFERING = 0;
    private static final int PLAYSTATE_ARG1_BUFFERING = 1;

    private PlaybackQueueHandler mHandler;
    private boolean mLockSeekBarUpdate;
    private FrameLayout mRootView;
    private ListView mListView;
    private PlaybackQueueAdapter mAdapter;
    private View.OnClickListener mPlayFabClickListener;
    private View.OnClickListener mNextClickListener;
    private View.OnClickListener mPreviousClickListener;
    private SeekBar.OnSeekBarChangeListener mSeekListener;
    private View.OnClickListener mRepeatClickListener;
    private View.OnClickListener mLikeClickListener;
    private View.OnClickListener mDislikeClickListener;
    private View.OnClickListener mAlbumArtClickListener;
    private View.OnClickListener mShuffleClickListener;

    private static class PlaybackQueueHandler extends Handler {
        private WeakReference<PlaybackQueueFragment> mParent;

        public PlaybackQueueHandler(WeakReference<PlaybackQueueFragment> parent) {
            mParent = parent;
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MSG_UPDATE_QUEUE:
                mParent.get().updateQueueLayout();
                break;

            case MSG_UPDATE_SEEKBAR:
                mParent.get().updateSeekbar();
                break;

            case MSG_UPDATE_PLAYSTATE:
                mParent.get().updatePlaystate(msg.arg1, msg.arg2);
                break;
            }
        }
    }

    public static PlaybackQueueFragment newInstance() {
        return new PlaybackQueueFragment();
    }

    public PlaybackQueueFragment() {
        mHandler = new PlaybackQueueHandler(new WeakReference<>(this));

        mPlayFabClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                switch (PlaybackProxy.getState()) {
                case PlaybackService.STATE_PAUSED:
                case PlaybackService.STATE_STOPPED:
                case PlaybackService.STATE_PAUSING:
                    PlaybackProxy.play();
                    break;

                case PlaybackService.STATE_BUFFERING:
                case PlaybackService.STATE_PLAYING:
                    PlaybackProxy.pause();
                    break;

                }
            }
        };

        mNextClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                PlaybackProxy.next();
            }
        };

        mPreviousClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                PlaybackProxy.previous();
            }
        };

        mSeekListener = new SeekBar.OnSeekBarChangeListener() {
            private int mStartProgress;

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                mLockSeekBarUpdate = true;
                mStartProgress = seekBar.getProgress();
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                mLockSeekBarUpdate = false;
                int endProgress = seekBar.getProgress();

                if (endProgress != mStartProgress) {
                    PlaybackProxy.seek(endProgress);
                }
            }
        };

        mRepeatClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean currentMode = PlaybackProxy.isRepeatMode();
                boolean newMode = !currentMode;

                PlaybackProxy.setRepeatMode(newMode);
                v.animate().rotationBy(-360).setDuration(600).start();

                if (newMode) {
                    ((ImageView) v).setImageResource(R.drawable.ic_replay);
                } else {
                    ((ImageView) v).setImageResource(R.drawable.ic_replay_gray);
                }
            }
        };

        mShuffleClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean currentMode = PlaybackProxy.isShuffleMode();
                boolean newMode = !currentMode;

                PlaybackProxy.setShuffleMode(newMode);

                if (newMode) {
                    ((ImageView) v).setImageResource(R.drawable.ic_shuffle);
                } else {
                    ((ImageView) v).setImageResource(R.drawable.ic_shuffle_gray);
                }
            }
        };

        mLikeClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ListenLogger logger = new ListenLogger(getActivity());
                PlaybackQueueAdapter.ViewHolder tag = (PlaybackQueueAdapter.ViewHolder) v.getTag();
                boolean isLiked = logger.isLiked(tag.song.getRef());

                if (isLiked) {
                    logger.removeLike(tag.song);
                    ((ImageView) v).setImageResource(R.drawable.ic_thumbs_up_gray);
                } else {
                    logger.addLike(tag.song);

                    final AutoMixBucket bucket = AutoMixManager.getDefault().getCurrentPlayingBucket();
                    if (bucket != null) {
                        new Thread() {
                            public void run() {
                                try {
                                    bucket.notifyLike();
                                } catch (EchoNestException e) {
                                    Log.e(TAG, "Unable to notify like event to EchoNest");
                                }
                            }
                        }.start();
                    }

                    ((ImageView) v).setImageResource(R.drawable.ic_thumbs_up);
                }
            }
        };

        mDislikeClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ListenLogger logger = new ListenLogger(getActivity());
                PlaybackQueueAdapter.ViewHolder tag = (PlaybackQueueAdapter.ViewHolder) v.getTag();
                boolean isDisliked = logger.isDisliked(tag.song.getRef());

                if (isDisliked) {
                    logger.removeDislike(tag.song);
                    ((ImageView) v).setImageResource(R.drawable.ic_thumb_down_gray);
                } else {
                    logger.addDislike(tag.song);

                    final AutoMixBucket bucket = AutoMixManager.getDefault().getCurrentPlayingBucket();
                    if (bucket != null) {
                        new Thread() {
                            public void run() {
                                try {
                                    bucket.notifyDislike();
                                } catch (EchoNestException e) {
                                    Log.e(TAG, "Unable to notify dislike event to EchoNest");
                                }
                            }
                        }.start();
                    }

                    ((ImageView) v).setImageResource(R.drawable.ic_thumb_down);
                }
            }
        };

        mAlbumArtClickListener = new View.OnClickListener() {
            @Override
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            public void onClick(View v) {
                PlaybackQueueAdapter.ViewHolder tag = (PlaybackQueueAdapter.ViewHolder) v.getTag();
                if (tag.song.getAlbum() != null) {
                    Bitmap hero = ((MaterialTransitionDrawable) tag.ivAlbumArt.getDrawable()).getFinalDrawable()
                            .getBitmap();
                    Intent intent = AlbumActivity.craftIntent(getActivity(), hero, tag.song.getAlbum(),
                            tag.song.getProvider(), 0xFF333333);

                    if (Utils.hasLollipop()) {
                        ActivityOptions opts = ActivityOptions.makeSceneTransitionAnimation(getActivity(), v,
                                "itemImage");
                        getActivity().startActivity(intent, opts.toBundle());
                    } else {
                        startActivity(intent);
                    }
                } else {
                    Toast.makeText(getActivity(), R.string.toast_song_no_album, Toast.LENGTH_SHORT).show();
                }
            }
        };
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mRootView = (FrameLayout) inflater.inflate(R.layout.fragment_playback_queue, container, false);
        mListView = (ListView) mRootView.findViewById(R.id.lvPlaybackQueue);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                PlaybackProxy.playAtIndex(position);
            }
        });

        if (mAdapter != null) {
            mListView.setAdapter(mAdapter);
        }

        updateQueueLayout();

        return mRootView;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        mAdapter = new PlaybackQueueAdapter(mPlayFabClickListener, mNextClickListener, mPreviousClickListener,
                mSeekListener, mRepeatClickListener, mLikeClickListener, mDislikeClickListener,
                mAlbumArtClickListener, mShuffleClickListener);

        if (mListView != null) {
            mListView.setAdapter(mAdapter);
        }

        if (activity instanceof MainActivity) {
            MainActivity mainActivity = (MainActivity) activity;
            mainActivity.onSectionAttached(MainActivity.SECTION_NOW_PLAYING);
        }
    }

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

        // Attach this fragment as Playback Listener
        PlaybackProxy.addCallback(mPlaybackListener);
        mHandler.sendEmptyMessageDelayed(MSG_UPDATE_SEEKBAR, 1000);

        ProviderAggregator.getDefault().addUpdateCallback(mProviderCallback);

        if (!mHandler.hasMessages(MSG_UPDATE_QUEUE)) {
            mHandler.sendEmptyMessage(MSG_UPDATE_QUEUE);
            mHandler.sendEmptyMessageDelayed(MSG_UPDATE_QUEUE, 500);
        }
    }

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

        // Remove callback on various places
        PlaybackProxy.removeCallback(mPlaybackListener);
        ProviderAggregator.getDefault().removeUpdateCallback(mProviderCallback);

        // Stop updating the seekbar
        mHandler.removeMessages(MSG_UPDATE_SEEKBAR);
    }

    public void updateQueueLayout() {
        final List<Song> songs = new ArrayList<>(PlaybackProxy.getCurrentPlaybackQueue());
        mAdapter.setPlaybackQueue(songs);

        final int trackIndex = PlaybackProxy.getCurrentTrackIndex();
        if (trackIndex >= 0) {
            mListView.smoothScrollToPosition(trackIndex + 1);
        }

        if (songs.size() <= 0) {
            mRootView.findViewById(R.id.txtEmptyQueue).setVisibility(View.VISIBLE);
        } else {
            mRootView.findViewById(R.id.txtEmptyQueue).setVisibility(View.GONE);
        }
    }

    public void updateSeekbar() {
        PlaybackQueueAdapter.ViewHolder tag = mAdapter.getCurrentTrackTag();

        if (tag != null && tag.sbSeek != null) {
            int state = PlaybackProxy.getState();
            if (state == PlaybackService.STATE_PLAYING || state == PlaybackService.STATE_PAUSING
                    || state == PlaybackService.STATE_PAUSED) {
                if (!mLockSeekBarUpdate) {
                    final int length = PlaybackProxy.getCurrentTrackLength();
                    final int position = PlaybackProxy.getCurrentTrackPosition();

                    tag.sbSeek.setMax(length);
                    tag.sbSeek.setProgress(position);
                    tag.tvTotalTime.setText(Utils.formatTrackLength(length));
                    tag.tvCurrentTime.setText(Utils.formatTrackLength(position));
                }

                mHandler.sendEmptyMessageDelayed(MSG_UPDATE_SEEKBAR, SEEK_UPDATE_DELAY);
            }
        }
    }

    public void updatePlaystate(int arg1, int arg2) {
        PlaybackQueueAdapter.ViewHolder tag = mAdapter.getCurrentTrackTag();

        if (tag != null && tag.fabPlayDrawable != null) {
            tag.fabPlayDrawable.setBuffering(arg1 == PLAYSTATE_ARG1_BUFFERING);
            tag.fabPlayDrawable.setShape(arg2);
        }
    }
}