<repo_name>dariushooks/RookMusicPlayer<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/adapters/SongsAdapter.java
package com.rookieandroid.rookiemusicplayer.adapters;

import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.support.v4.media.session.PlaybackStateCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.rookieandroid.rookiemusicplayer.R;
import com.rookieandroid.rookiemusicplayer.helpers.SectionIndexFixer;
import com.rookieandroid.rookiemusicplayer.Songs;

import java.util.ArrayList;
import java.util.Locale;

import static com.rookieandroid.rookiemusicplayer.App.currentState;
import static com.rookieandroid.rookiemusicplayer.App.lettersSongs;
import static com.rookieandroid.rookiemusicplayer.App.sectionsSongs;

public class SongsAdapter extends RecyclerView.Adapter<SongsAdapter.SongViewHolder>
{
    private final String TAG = SongsAdapter.class.getSimpleName();
    private ArrayList<Songs> songs;
    private ListItemClickListener listener;
    private Context context;
    private int previousPosition;
    private int currentlyPlaying = 1;
    private ImageView currentSong;
    private ImageView previousSong;
    private int position;
    private Handler playingHandler = new Handler();

    public interface ListItemClickListener
    {
        void onListItemClick(int position);
        void onLongListItemClick(int position);
    }

    public SongsAdapter(ArrayList<Songs> songs, ListItemClickListener listener)
    {
        this.songs = songs;
        this.listener = listener;
        setHasStableIds(true);
        lettersSongs.clear(); sectionsSongs.clear();
        for (int i = 0; i < this.songs.size(); i++)
        {
            String song = this.songs.get(i).getTitle();
            if(Character.isLetter(song.charAt(0)))
            {
                String letter = song.charAt(0) + "";
                if(lettersSongs.isEmpty())
                {
                    lettersSongs.add(letter.toUpperCase());
                    sectionsSongs.add(i);
                }
                else if(!lettersSongs.contains(letter) && lettersSongs.size() < 26)
                {
                    lettersSongs.add(letter.toUpperCase());
                    sectionsSongs.add(i);
                }
            }

            else
            {
                if(!lettersSongs.contains("#") && lettersSongs.size() < 26)
                {
                    lettersSongs.add("#");
                    sectionsSongs.add(i);
                }
            }
        }

        new SectionIndexFixer().fixIndex(lettersSongs, sectionsSongs);
        /*for(int i = 0; i < 27; i++)
        {
            //Log.i(TAG, "Letter " + lettersSongs.get(i) + "\tSection " + sectionsSongs.get(i));
        }*/
    }

    @NonNull
    @Override
    public SongViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
    {
        context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.songs_list, parent, false);
        return new SongViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull SongViewHolder holder, int position)
    {
        holder.bind(position);
    }

    @Override
    public long getItemId(int position)
    {
        return Integer.parseInt(songs.get(position).getId());
    }

    @Override
    public int getItemViewType(int position) { return Integer.parseInt(songs.get(position).getId()); }

    @Override
    public int getItemCount() { return songs.size(); }

    private Runnable updateCurrentlyPlaying = new Runnable()
    {
        @Override
        public void run()
        {
            if(currentState == PlaybackStateCompat.STATE_PLAYING)
            {
                switch(currentlyPlaying)
                {
                    case 1:
                        currentSong.setBackgroundColor(context.getColor(R.color.nowPlaying));
                        currentSong.setImageResource(R.drawable.ic_currentlyplaying2);
                        currentlyPlaying = 2;
                        break;

                    case 2:
                        currentSong.setBackgroundColor(context.getColor(R.color.nowPlaying));
                        currentSong.setImageResource(R.drawable.ic_currentlyplaying3);
                        currentlyPlaying = 3;
                        break;

                    case 3:
                        currentSong.setBackgroundColor(context.getColor(R.color.nowPlaying));
                        currentSong.setImageResource(R.drawable.ic_currentlyplaying4);
                        currentlyPlaying = 4;
                        break;

                    case 4:
                        currentSong.setBackgroundColor(context.getColor(R.color.nowPlaying));
                        currentSong.setImageResource(R.drawable.ic_currentlyplaying1);
                        currentlyPlaying = 1;
                        break;
                }
                playingHandler.postDelayed(this, 300);
            }

            else
            {
                currentSong.setBackgroundColor(context.getColor(R.color.nowPlaying));
                currentSong.setImageResource(R.drawable.ic_currentlyplaying1);
                currentlyPlaying = 1;
                playingHandler.post(this);
            }
        }
    };

    class SongViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener
    {

        private ImageView albumArt;
        private ImageView songPlaying;
        private TextView songName;
        private TextView songArtist;
        private TextView songDuration;

        public SongViewHolder(@NonNull View itemView)
        {
            super(itemView);
            albumArt = itemView.findViewById(R.id.songArt);
            songPlaying = itemView.findViewById(R.id.songPlaying);
            songName = itemView.findViewById(R.id.songName);
            songArtist = itemView.findViewById(R.id.songArtist);
            songDuration = itemView.findViewById(R.id.songDuration);
            itemView.setOnClickListener(this);
            itemView.setOnLongClickListener(this);
        }


        public void bind(int position)
        {
            Uri artUri = Uri.parse(songs.get(position).getArt());
            Glide.with(context).load(artUri).placeholder(R.drawable.noalbumart).fallback(R.drawable.noalbumart).error(R.drawable.noalbumart).into(albumArt);

            songName.setText(songs.get(position).getTitle());
            songArtist.setText(songs.get(position).getArtist());

            String duration;
            long minutes = (songs.get(position).getDuration() / 1000) / 60;
            long seconds = (songs.get(position).getDuration() / 1000) % 60;
            if(seconds > 9)
                duration = String.format(Locale.US, "%d:%d", minutes, seconds);
            else
                duration = String.format(Locale.US, "%d:%d%d", minutes, 0, seconds);
            songDuration.setText(duration);
        }


        @Override
        public void onClick(View view)
        {
            /*if(previousSong == null)
            {
                previousSong = songPlaying;
                previousPosition = getAdapterPosition();
            }

            else
            {
                playingHandler.removeCallbacks(updateCurrentlyPlaying);
                previousSong.setBackgroundColor(context.getColor(R.color.transparent));
                previousSong.setImageResource(0);
                previousSong = songPlaying;
                previousPosition = getAdapterPosition();
            }*/

            position = getAdapterPosition();
            listener.onListItemClick(position);
            /*currentSong = songPlaying;
            currentSong.setBackgroundColor(context.getColor(R.color.nowPlaying));
            currentSong.setImageResource(R.drawable.ic_currentlyplaying2);
            currentlyPlaying = 1;
            playingHandler.post(updateCurrentlyPlaying);*/
        }

        @Override
        public boolean onLongClick(View view)
        {
            int clickedPosition = getAdapterPosition();
            listener.onLongListItemClick(clickedPosition);
            return true;
        }
    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/MediaPlaybackService.java
package com.rookieandroid.rookiemusicplayer;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
import android.os.ResultReceiver;
import android.provider.MediaStore;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.media.MediaBrowserServiceCompat;
import androidx.media.session.MediaButtonReceiver;

import com.rookieandroid.rookiemusicplayer.adapters.QueueAdapter;
import com.rookieandroid.rookiemusicplayer.architecture.SavedDetails;
import com.rookieandroid.rookiemusicplayer.architecture.StateViewModel;
import com.rookieandroid.rookiemusicplayer.helpers.ControlReceiver;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;

import static com.rookieandroid.rookiemusicplayer.App.ACTIVITY_RESTORE;
import static com.rookieandroid.rookiemusicplayer.App.ADD_ALBUM_ARTIST_PLAYLIST;
import static com.rookieandroid.rookiemusicplayer.App.ADD_SONG;
import static com.rookieandroid.rookiemusicplayer.App.CHANNEL_1;
import static com.rookieandroid.rookiemusicplayer.App.CLEAR;
import static com.rookieandroid.rookiemusicplayer.App.FROM_ALBUM;
import static com.rookieandroid.rookiemusicplayer.App.FROM_PLAYLIST;
import static com.rookieandroid.rookiemusicplayer.App.GET_ARTIST_ALBUM;
import static com.rookieandroid.rookiemusicplayer.App.GET_CURRENT_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.GET_PLAYBACKSTATE;
import static com.rookieandroid.rookiemusicplayer.App.GET_QUEUE_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.INITIALIZE_QUEUE_CHANGE;
import static com.rookieandroid.rookiemusicplayer.App.MEDIA_DELETED;
import static com.rookieandroid.rookiemusicplayer.App.PLAY_BUTTON_START;
import static com.rookieandroid.rookiemusicplayer.App.QUEUE_CLICK;
import static com.rookieandroid.rookiemusicplayer.App.QUEUE_END;
import static com.rookieandroid.rookiemusicplayer.App.QUEUE_NEXT;
import static com.rookieandroid.rookiemusicplayer.App.RECEIVE_ARTIST_ALBUM;
import static com.rookieandroid.rookiemusicplayer.App.RECEIVE_CURRENT_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.RECEIVE_PLAYBACKSTATE;
import static com.rookieandroid.rookiemusicplayer.App.RECEIVE_QUEUE_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.RECENTLY_ADDED;
import static com.rookieandroid.rookiemusicplayer.App.RESTORE_SAVED_QUEUE;
import static com.rookieandroid.rookiemusicplayer.App.SAVE_QUEUE;
import static com.rookieandroid.rookiemusicplayer.App.SET_ELAPSED_TIME;
import static com.rookieandroid.rookiemusicplayer.App.SET_FROM;
import static com.rookieandroid.rookiemusicplayer.App.SET_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.SET_QUEUE_ALBUM;
import static com.rookieandroid.rookiemusicplayer.App.SET_QUEUE_ARTIST;
import static com.rookieandroid.rookiemusicplayer.App.SET_QUEUE_LIBRARY;
import static com.rookieandroid.rookiemusicplayer.App.SET_QUEUE_LIBRARY_SONGS;
import static com.rookieandroid.rookiemusicplayer.App.SET_QUEUE_PLAYLIST;
import static com.rookieandroid.rookiemusicplayer.App.SET_QUEUE_SEARCH;
import static com.rookieandroid.rookiemusicplayer.App.SET_UP_NEXT;
import static com.rookieandroid.rookiemusicplayer.App.SHUFFLE_QUEUE;
import static com.rookieandroid.rookiemusicplayer.App.SKIP_NEXT;
import static com.rookieandroid.rookiemusicplayer.App.SKIP_PREVIOUS;
import static com.rookieandroid.rookiemusicplayer.App.addToQueue;
import static com.rookieandroid.rookiemusicplayer.App.albumSongs;
import static com.rookieandroid.rookiemusicplayer.App.artistSongs;
import static com.rookieandroid.rookiemusicplayer.App.deleteFromQueue;
import static com.rookieandroid.rookiemusicplayer.App.librarySongs;
import static com.rookieandroid.rookiemusicplayer.App.nowPlayingFrom;
import static com.rookieandroid.rookiemusicplayer.App.playlistSongs;
import static com.rookieandroid.rookiemusicplayer.App.queueAdapter;
import static com.rookieandroid.rookiemusicplayer.App.queueDisplay;
import static com.rookieandroid.rookiemusicplayer.App.savedSongs;
import static com.rookieandroid.rookiemusicplayer.App.savedState;
import static com.rookieandroid.rookiemusicplayer.App.searchSong;
import static com.rookieandroid.rookiemusicplayer.App.songs;

public class MediaPlaybackService extends MediaBrowserServiceCompat implements AudioManager.OnAudioFocusChangeListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, QueueAdapter.QueueChange
{
    private static final String MY_MEDIA_ROOT_ID = "media_root_id";
    private static final String TAG = MediaPlaybackService.class.getSimpleName();

    private ArrayList<Songs> queue = new ArrayList<>();
    private int position;
    private int from;
    private int currentState = PlaybackStateCompat.STATE_NONE;
    private int shuffle;
    private int repeat;
    private int elapsed;
    private boolean repeatAll = false;
    private boolean repeatOne = false;
    private MediaSessionCompat mediaSession;
    private ComponentName mediaButtonReceiver;
    private PlaybackStateCompat.Builder stateBuilder;
    private MediaPlayer mediaPlayer;
    private Handler handler = new Handler();
    private NotificationCompat.Builder builder;

    //SYNC UI STATE
    private StateViewModel stateViewModel;
    private int syncPosition;
    private int syncElapsed;
    private int syncDuration;
    private int syncShuffle;
    private int syncRepeat;
    private int syncPlayState;
    private String syncNowPlayingFrom;
    private int syncFrom;

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

        //MediaPlayer
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
        mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());
        mediaPlayer.setVolume(1f, 1f);
        mediaPlayer.setOnCompletionListener(this);
        mediaPlayer.setOnErrorListener(this);
        mediaPlayer.setOnPreparedListener(this);

        // Create a MediaSessionCompat
        mediaButtonReceiver = new ComponentName(this, ControlReceiver.class);
        mediaSession = new MediaSessionCompat(this, MediaPlaybackService.class.getSimpleName(), mediaButtonReceiver, null);

        // Enable callbacks from MediaButtons and TransportControls
        mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS | MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS);

        // Set an initial PlaybackState with ACTION_PLAY, so media buttons can start the player
        stateBuilder = new PlaybackStateCompat.Builder()
                .setState(PlaybackStateCompat.STATE_NONE, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0)
                .setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY);
        mediaSession.setPlaybackState(stateBuilder.build());

        // MySessionCallback() has methods that handle callbacks from a media controller
        mediaSession.setCallback(mediaCallbacks);
        mediaSession.setActive(true);

        Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
        mediaButtonIntent.setClass(this, ControlReceiver.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, 0);
        mediaSession.setMediaButtonReceiver(pendingIntent);
        // Set the session's token so that client activities can communicate with it.
        setSessionToken(mediaSession.getSessionToken());

        //BroadcastReceiver
        //Handles headphones coming unplugged. cannot be done through a manifest receiver
        IntentFilter filter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
        registerReceiver(broadcastReceiver, filter);

        stateViewModel = new StateViewModel(getApplication());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        MediaButtonReceiver.handleIntent(mediaSession, intent);
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        //Log.i(TAG, "SERVICE BEING DESTROYED");
        abandonAudioFocus();
        unregisterReceiver(broadcastReceiver);
        mediaPlayer.release();
        mediaSession.release();
        NotificationManagerCompat.from(this).cancel(1);
    }

    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
                                       SESSION CALLBACKS
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    private MediaSessionCompat.Callback mediaCallbacks = new MediaSessionCompat.Callback()
    {
        @Override
        public void onPlay()
        {
            super.onPlay();
            if(successfullyRetrievedAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
            {
                startForegroundService(new Intent(MediaPlaybackService.this, MediaPlaybackService.class));
                mediaSession.setActive(true);
                mediaPlayer.start();

                //SET PLAYBACK STATE
                currentState = PlaybackStateCompat.STATE_PLAYING;
                stateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, mediaPlayer.getCurrentPosition(), 1);
                stateBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE |
                        PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
                mediaSession.setPlaybackState(stateBuilder.build());

                //SHOW PLAY NOTIFICATION
                buildNotification(MediaPlaybackService.this, queue.get(position));
                handler.post(updateQueueState);
            }
        }


        @Override
        public void onStop()
        {
            super.onStop();
            abandonAudioFocus();
            stopService(new Intent(MediaPlaybackService.this, MediaPlaybackService.class));
            mediaSession.setActive(false);
            mediaPlayer.stop();
        }

        @Override
        public void onPlayFromMediaId(String mediaId, Bundle extras)
        {
            super.onPlayFromMediaId(mediaId, extras);
            //Log.i(TAG, "Playing from media id: " + mediaId);
            if(successfullyRetrievedAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
            {
                startForegroundService(new Intent(MediaPlaybackService.this, MediaPlaybackService.class));
                mediaSession.setActive(true);
                playSong(mediaId);
                buildMetadata(queue.get(position));

                //SET PLAYBACK STATE
                currentState = PlaybackStateCompat.STATE_PLAYING;
                stateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, mediaPlayer.getCurrentPosition(), 1);
                stateBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE |
                        PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
                mediaSession.setPlaybackState(stateBuilder.build());

                //SHOW PLAY NOTIFICATION
                buildNotification(MediaPlaybackService.this, queue.get(position));
                handler.post(updateQueueState);
            }
        }

        @Override
        public void onPause()
        {
            super.onPause();
            if(mediaPlayer.isPlaying())
            {
                mediaPlayer.pause();

                //SET PLAYBACK STATE
                currentState = PlaybackStateCompat.STATE_PAUSED;
                stateBuilder.setState(PlaybackStateCompat.STATE_PAUSED, mediaPlayer.getCurrentPosition(), 0);
                stateBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE |
                        PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
                mediaSession.setPlaybackState(stateBuilder.build());

                //SHOW PAUSE NOTIFICATION
                buildNotification(MediaPlaybackService.this, queue.get(position));
            }
        }

        @Override
        public void onCommand(String command, Bundle extras, ResultReceiver cb)
        {
            super.onCommand(command, extras, cb);
            switch (command)
            {
                case GET_CURRENT_POSITION:
                    Bundle currentPosition = new Bundle();
                    currentPosition.putInt("currentPosition", getCurrentPosition());
                    cb.send(RECEIVE_CURRENT_POSITION, currentPosition);
                    break;

                case GET_ARTIST_ALBUM:
                    Bundle currentArtistAlbum = new Bundle();
                    currentArtistAlbum.putParcelable("currentArtistAlbum", getCurrentArtistAlbum());
                    cb.send(RECEIVE_ARTIST_ALBUM, currentArtistAlbum);
                    break;

                case GET_QUEUE_POSITION:
                    Bundle queuePosition = new Bundle();
                    queuePosition.putInt("queuePosition", position);
                    queuePosition.putInt("currentElapsed", mediaPlayer.getCurrentPosition());
                    queuePosition.putInt("currentFrom", from);
                    cb.send(RECEIVE_QUEUE_POSITION, queuePosition);
                    break;

                case GET_PLAYBACKSTATE:
                    Bundle playbackState = new Bundle();
                    playbackState.putInt("currentState", currentState);
                    cb.send(RECEIVE_PLAYBACKSTATE, playbackState);
                    break;
            }
        }

        @Override
        public void onCustomAction(String action, Bundle extras)
        {
            super.onCustomAction(action, extras);
            switch (action)
            {
                case ADD_SONG: addSongToQueue(extras.getParcelable("ADD_SONG"), extras.getInt("QUEUE_POSITION")); break;
                case ADD_ALBUM_ARTIST_PLAYLIST: addAlbumOrArtistToQueue(extras.getInt("QUEUE_POSITION")); break;
                case SET_POSITION: setPosition(extras.getInt("CURRENT_QUEUE_POSITION")); break;
                case CLEAR: queue.clear(); break;
                case ACTIVITY_RESTORE: activityRestore(); break;
                case SET_QUEUE_LIBRARY: queue.addAll(songs); from = extras.getInt("FROM"); break;
                case SET_QUEUE_LIBRARY_SONGS: queue.addAll(librarySongs); from = extras.getInt("FROM"); break;
                case SET_QUEUE_ARTIST: queue.addAll(artistSongs); from = extras.getInt("FROM"); break;
                case SET_QUEUE_ALBUM: queue.addAll(albumSongs); from = extras.getInt("FROM"); break;
                case SET_QUEUE_SEARCH: queue.addAll(searchSong); from = extras.getInt("FROM"); break;
                case SET_QUEUE_PLAYLIST: queue.addAll(playlistSongs); from = extras.getInt("FROM"); break;
                case SET_UP_NEXT: setQueueDisplay(); break;
                case QUEUE_CLICK: queueClick(extras.getInt("clickedIndex")); break;
                case SHUFFLE_QUEUE: mediaSession.setShuffleMode(PlaybackStateCompat.SHUFFLE_MODE_ALL); break;
                case SAVE_QUEUE: saveQueue(); break;
                case RESTORE_SAVED_QUEUE: queue.addAll(savedSongs); break;
                case INITIALIZE_QUEUE_CHANGE: queueAdapter.initializeQueueChange(MediaPlaybackService.this); break;
                case SET_ELAPSED_TIME: elapsed = extras.getInt("CURRENT_ELAPSED_TIME"); setSong(queue.get(position).getId()); break;
                case SET_FROM: from = extras.getInt("CURRENT_FROM"); break;
                case PLAY_BUTTON_START: playButtonStart(); break;
                case MEDIA_DELETED: mediaDeleted(extras.getParcelable("DELETE_SONG")); break;
            }
        }

        @Override
        public void onSkipToPrevious()
        {
            super.onSkipToPrevious();
            if(!queue.isEmpty())
            {
                String mediaId;
                if(mediaPlayer.getCurrentPosition() < 5000 && position > 0)
                {
                    stateBuilder.setState(PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0);
                    mediaSession.setPlaybackState(stateBuilder.build());
                    updateQueueDisplay(SKIP_PREVIOUS);
                    position--;
                }

                mediaId = queue.get(position).getId();
                if(currentState == PlaybackStateCompat.STATE_PLAYING)
                    playSong(mediaId);
                else
                    setSong(mediaId);
                buildMetadata(queue.get(position));
                buildNotification(MediaPlaybackService.this, queue.get(position));
            }
        }

        @Override
        public void onSkipToNext()
        {
            super.onSkipToNext();
            if(repeatAll && position == queue.size() - 1)
            {
                position = 0;
                setQueueDisplay();
                position = -1;
            }

            if(!queue.isEmpty() && position < queue.size() - 1)
            {
                stateBuilder.setState(PlaybackStateCompat.STATE_SKIPPING_TO_NEXT, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0);
                mediaSession.setPlaybackState(stateBuilder.build());
                position++;
                String mediaId = queue.get(position).getId();
                if(currentState == PlaybackStateCompat.STATE_PLAYING)
                    playSong(mediaId);
                else
                    setSong(mediaId);
                buildMetadata(queue.get(position));
                updateQueueDisplay(SKIP_NEXT);
                buildNotification(MediaPlaybackService.this, queue.get(position));
            }
        }

        @Override
        public void onSeekTo(long pos)
        {
            super.onSeekTo(pos);
            mediaPlayer.seekTo((int)pos);
            stateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, mediaPlayer.getCurrentPosition(), 1);
            stateBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE |
                    PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
            mediaSession.setPlaybackState(stateBuilder.build());
        }

        @Override
        public void onSetRepeatMode(int repeatMode)
        {
            super.onSetRepeatMode(repeatMode);
            mediaSession.setRepeatMode(repeatMode);
            repeat = repeatMode;
            switch (repeatMode)
            {
                case PlaybackStateCompat.REPEAT_MODE_ALL: repeatAll = true; break;
                case PlaybackStateCompat.REPEAT_MODE_ONE: repeatAll = false; repeatOne = true; break;
                case PlaybackStateCompat.REPEAT_MODE_NONE: repeatAll = false; repeatOne = false; break;
            }
        }

        @Override
        public void onSetShuffleMode(int shuffleMode)
        {
            super.onSetShuffleMode(shuffleMode);
            mediaSession.setShuffleMode(shuffleMode);
            shuffle = shuffleMode;
            Songs current;
            switch (shuffleMode)
            {
                case PlaybackStateCompat.SHUFFLE_MODE_ALL:
                    if(!queue.isEmpty())
                    {
                        current = queue.get(position);
                        queue.remove(position);
                        Collections.shuffle(queue);
                        queue.add(0, current); position = 0;
                        setQueueDisplay();
                    }
                    break;

                case PlaybackStateCompat.SHUFFLE_MODE_NONE:
                    if(!queue.isEmpty())
                    {
                        current = queue.get(position);
                        if(from == FROM_ALBUM || from == FROM_PLAYLIST)
                            Collections.sort(queue, Comparator.comparing(Songs::getTrack));
                        else if(from == RECENTLY_ADDED)
                            Collections.sort(queue, Comparator.comparing(Songs::getTrack).reversed());
                        else
                            Collections.sort(queue, Comparator.comparing(Songs::getTitle));
                        position = queue.indexOf(current);
                        setQueueDisplay();
                    }
                    break;
            }
            saveQueue();
        }
    };

    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/


    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
                                 METHODS FOR SAVING UI STATE
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    private void saveQueue()
    {
        stateViewModel.deleteSavedQueue();
        stateViewModel.insertAll(queue);
        /*for(int i = 0; i < queue.size(); i++)
        {
            String id = queue.get(i).getId();
            String title = queue.get(i).getTitle();
            String album = queue.get(i).getAlbum();
            String albumKey = queue.get(i).getAlbumKey();
            String art = queue.get(i).getArt();
            String artist = queue.get(i).getArtist();
            String artistKey = queue.get(i).getArtistKey();
            long duration = queue.get(i).getDuration();
            String path = queue.get(i).getPath();
            int track = queue.get(i).getTrack();
            stateViewModel.insert(new SavedQueue(id, title, album, albumKey, art, artist, artistKey, duration, path, track));
        }*/

        //Log.i(TAG, "QUEUE SAVED");
    }

    private void updateSavedState()
    {
        syncPosition = position;
        syncShuffle = shuffle;
        syncRepeat = repeat;
        syncPlayState = currentState;
        syncElapsed = mediaPlayer.getCurrentPosition();
        syncDuration = mediaPlayer.getDuration();
        syncNowPlayingFrom = nowPlayingFrom;
        syncFrom = from;
        SavedDetails details = new SavedDetails(syncPosition, syncShuffle, syncRepeat, syncPlayState, syncElapsed, syncDuration, syncNowPlayingFrom, syncFrom);
        if(savedState.isEmpty())
            stateViewModel.insert(details);
        else
        {
            details.setId(savedState.get(0).getId());
            stateViewModel.update(details);
        }

        //Log.i(TAG, "SAVED STATE UPDATED");
    }

    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    private void activityRestore()
    {
        if(mediaPlayer.isPlaying())
        {
            stateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, mediaPlayer.getCurrentPosition(), 1);
            stateBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
            mediaSession.setPlaybackState(stateBuilder.build());
        }

        else
        {
            stateBuilder.setState(PlaybackStateCompat.STATE_PAUSED, mediaPlayer.getCurrentPosition(), 0);
            stateBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
            mediaSession.setPlaybackState(stateBuilder.build());
        }

        buildMetadata(queue.get(position));
    }

    private void buildMetadata(Songs currentSong)
    {
        MediaMetadataCompat.Builder metadata = new MediaMetadataCompat.Builder();
        metadata.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, currentSong.getId());
        metadata.putString(MediaMetadataCompat.METADATA_KEY_TITLE, currentSong.getTitle());
        metadata.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, currentSong.getArtist());
        metadata.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, currentSong.getAlbum());
        metadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, currentSong.getDuration());
        metadata.putString(MediaMetadataCompat.METADATA_KEY_MEDIA_URI, currentSong.getPath());
        metadata.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, currentSong.getArt());
        mediaSession.setMetadata(metadata.build());
    }

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            if(mediaPlayer != null && mediaPlayer.isPlaying())
                mediaPlayer.pause();
        }
    };

    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
                                  METHODS FOR AUDIO FOCUS
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    private int successfullyRetrievedAudioFocus()
    {
        int result;
        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);

        if(audioManager != null)
        {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O)
            {
                AudioAttributes playbackAttributes = new AudioAttributes.Builder()
                        .setUsage(AudioAttributes.USAGE_GAME)
                        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                        .build();

                AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                        .setAudioAttributes(playbackAttributes)
                        .setAcceptsDelayedFocusGain(true)
                        .setOnAudioFocusChangeListener(this)
                        .build();
                result = audioManager.requestAudioFocus(focusRequest);
            }

            else
                result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        }

        else
            result = AudioManager.AUDIOFOCUS_REQUEST_FAILED;

        return result;
    }

    private void abandonAudioFocus()
    {
        AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        if(audioManager != null)
            audioManager.abandonAudioFocus(this);
    }

    @Override
    public void onAudioFocusChange(int focusChange)
    {
        switch (focusChange)
        {
            case AudioManager.AUDIOFOCUS_LOSS:
            {
                if(mediaPlayer.isPlaying())
                    mediaPlayer.stop();
                break;
            }
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            {
                mediaPlayer.pause();
                break;
            }
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
            {
                if( mediaPlayer != null )
                    mediaPlayer.setVolume(0.3f, 0.3f);
                break;
            }
            case AudioManager.AUDIOFOCUS_GAIN:
            {
                if( mediaPlayer != null )
                {
                    if( !mediaPlayer.isPlaying() )
                        mediaPlayer.start();
                    mediaPlayer.setVolume(1.0f, 1.0f);
                }
                break;
            }
        }
    }

    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/


    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
                                  METHODS FOR NOTIFICATIONS
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    private void buildNotification(Context context, Songs currentSong)
    {
        Intent intent = new Intent(context, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

        Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Long.parseLong(currentSong.getId()));
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        retriever.setDataSource(context, uri);
        byte[] cover = retriever.getEmbeddedPicture();
        builder = new NotificationCompat.Builder(context, CHANNEL_1);
        builder.setSmallIcon(R.drawable.ic_noartistart);
        builder.setContentTitle(currentSong.getTitle())
                .setContentText(currentSong.getArtist() + " - " + currentSong.getAlbum());

        if(cover != null)
            builder.setLargeIcon(BitmapFactory.decodeByteArray(cover, 0, cover.length));
        else
            builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.noalbumart));

        builder.setContentIntent(pendingIntent)
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setVisibility(NotificationCompat.VISIBILITY_PUBLIC);

        if(currentState == PlaybackStateCompat.STATE_PLAYING)
        {
            builder.setOngoing(true);
            builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle().setShowActionsInCompactView(0,1,2).setMediaSession(mediaSession.getSessionToken()));
            builder.addAction(R.drawable.ic_fast_rewind, "Back",
                    MediaButtonReceiver.buildMediaButtonPendingIntent(MediaPlaybackService.this, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS));
            builder.addAction(R.drawable.ic_pause, "Pause",
                    MediaButtonReceiver.buildMediaButtonPendingIntent(MediaPlaybackService.this, PlaybackStateCompat.ACTION_PLAY_PAUSE));
            builder.addAction(R.drawable.ic_fast_forward, "Forward",
                    MediaButtonReceiver.buildMediaButtonPendingIntent(MediaPlaybackService.this, PlaybackStateCompat.ACTION_SKIP_TO_NEXT));
            startForeground(1, builder.build());
        }

        else
        {
            builder.setOngoing(false);
            builder.setStyle(new androidx.media.app.NotificationCompat.MediaStyle().setShowActionsInCompactView(0, 1, 2).setMediaSession(mediaSession.getSessionToken()));
            builder.addAction(R.drawable.ic_fast_rewind, "Back",
                    MediaButtonReceiver.buildMediaButtonPendingIntent(MediaPlaybackService.this, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS));
            builder.addAction(R.drawable.ic_play, "Play",
                    MediaButtonReceiver.buildMediaButtonPendingIntent(MediaPlaybackService.this, PlaybackStateCompat.ACTION_PLAY_PAUSE));
            builder.addAction(R.drawable.ic_fast_forward, "Forward",
                    MediaButtonReceiver.buildMediaButtonPendingIntent(MediaPlaybackService.this, PlaybackStateCompat.ACTION_SKIP_TO_NEXT));
            NotificationManagerCompat.from(MediaPlaybackService.this).notify(1, builder.build());
        }
    }

    private Runnable updateQueueState = new Runnable()
    {
        @Override
        public void run()
        {
            if(currentState == PlaybackStateCompat.STATE_PLAYING)
            {
                //Log.i(TAG, "Time Updating to " + calculateTime(mediaPlayer.getCurrentPosition()));
                updateSavedState();
                handler.postDelayed(this, 2000);
            }

            else
            {
                //Log.i(TAG, "Time Paused at " + calculateTime(mediaPlayer.getCurrentPosition()));
                updateSavedState();
                stopForeground(false);
            }
        }
    };

    private String calculateTime(int time)
    {
        String calculated;
        int minutes = time / 1000 / 60;
        int seconds = time/ 1000 % 60;
        if(seconds > 9)
            calculated = String.format(Locale.US, "%d:%d", minutes, seconds);
        else
            calculated = String.format(Locale.US, "%d:%d%d", minutes, 0, seconds);
        return calculated;
    }

    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    private int getCurrentPosition(){ return mediaPlayer.getCurrentPosition(); }

    private Songs getCurrentArtistAlbum() { return queue.get(position); }


    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
                                   METHODS FOR QUEUE CONTROL
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    private void mediaDeleted(Songs deleteSong)
    {
        deleteFromQueue.clear();

        for(int i = 0; i < queue.size(); i++)
            if(queue.get(i).getTitle().equals(deleteSong.getTitle()))
                deleteFromQueue.add(queue.get(i));

        queue.removeAll(deleteFromQueue);
        setQueueDisplay();
    }

    private void playButtonStart()
    {
        if(successfullyRetrievedAudioFocus() == AudioManager.AUDIOFOCUS_REQUEST_GRANTED)
        {
            startForegroundService(new Intent(MediaPlaybackService.this, MediaPlaybackService.class));
            mediaSession.setActive(true);
            if(shuffle == PlaybackStateCompat.SHUFFLE_MODE_ALL)
                Collections.shuffle(queue);
            playSong(queue.get(position).getId());
            buildMetadata(queue.get(position));

            //SET PLAYBACK STATE
            currentState = PlaybackStateCompat.STATE_PLAYING;
            stateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, mediaPlayer.getCurrentPosition(), 1);
            stateBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE |
                    PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
            mediaSession.setPlaybackState(stateBuilder.build());

            //SHOW PLAY NOTIFICATION
            buildNotification(MediaPlaybackService.this, queue.get(position));
            handler.post(updateQueueState);
        }
    }

    private void setQueueDisplay()
    {
        queueDisplay.clear();
        if(queue.size() - position > 20)
            queueDisplay.addAll(queue.subList(position + 1, position + 21));
        else if(queue.size() - position < 20 && queue.size() != 1)
            queueDisplay.addAll(queue.subList(position + 1, queue.size()));
        queueAdapter.notifyDataSetChanged();
    }

    @Override
    public void updateQueueOrder(int from, int to)
    {
        int fromPosition = queue.indexOf(queueDisplay.get(from));
        if (from < to)
            for (int i = from; i < to; i++)
                Collections.swap(queueDisplay, i, i + 1);
        else
            for (int i = from; i > to; i--)
                Collections.swap(queueDisplay, i, i - 1);
        queueAdapter.notifyItemMoved(from, to);
        Collections.swap(queue, fromPosition, fromPosition + (to - from));
        saveQueue();
    }

    @Override
    public void updateQueueDismiss(int index)
    {
        queue.remove(queueDisplay.get(index));
        saveQueue();
        queueDisplay.remove(index);
        queueAdapter.notifyItemRemoved(index);
    }

    private void updateQueueDisplay(int action)
    {
        switch (action)
        {
            case SKIP_NEXT:
                if (queue.size() - position > 0)
                {
                    queueDisplay.remove(0); queueAdapter.notifyItemRemoved(0);
                    if(queue.size() - position > 20)
                    {
                        queueDisplay.add(queue.get(position + 20));
                        queueAdapter.notifyItemInserted(queueDisplay.size() - 1);
                    }
                }
                break;

            case SKIP_PREVIOUS:
                if(queue.size() - position > 20)
                {
                    queueDisplay.remove(queueDisplay.size() - 1);
                    queueAdapter.notifyItemRemoved(queueDisplay.size() - 1);
                }
                queueDisplay.add(0, queue.get(position));
                queueAdapter.notifyItemInserted(0);
                break;
        }
    }

    private void queueClick(int index) { position += index + 1; }

    private void setPosition(int position){ this.position = position; }

    private void addSongToQueue(Songs song, int add)
    {
        switch (add)
        {
            case QUEUE_NEXT: queue.add(position + 1, song);
                queueDisplay.remove(queueDisplay.size() - 1);
                queueDisplay.add(0, queue.get(position + 1));
                queueAdapter.notifyItemRemoved(queueDisplay.size() - 1);
                queueAdapter.notifyItemInserted(0);
                break;

            case QUEUE_END: queue.add(song);
                break;
        }
        saveQueue();
    }

    private void addAlbumOrArtistToQueue(int add)
    {
        switch (add)
        {
            case QUEUE_NEXT: queue.addAll(position + 1, addToQueue);
                queueDisplay.addAll(0, addToQueue);
                if(queueDisplay.size() > 20)
                {
                    queueDisplay.subList(20, queueDisplay.size()).clear();
                }
                queueAdapter.notifyDataSetChanged();
                break;

            case QUEUE_END: queue.addAll(addToQueue); break;
        }
        saveQueue();
    }

    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
                                   METHODS FOR MEDIA PLAYER
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    private void playSong(String mediaId)
    {
        saveQueue();
        Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Long.parseLong(mediaId));
        try
        {
            mediaPlayer.reset();
            mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());
            mediaPlayer.setDataSource(this, uri);
            mediaPlayer.prepareAsync();

            stateBuilder.setState(PlaybackStateCompat.STATE_PLAYING, mediaPlayer.getCurrentPosition(), 1);
            stateBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
            mediaSession.setPlaybackState(stateBuilder.build());
            handler.post(updateQueueState);
        } catch (IOException e) { e.printStackTrace(); }
    }

    private void setSong(String mediaId)
    {
        saveQueue();
        Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, Long.parseLong(mediaId));
        try
        {
            mediaPlayer.reset();
            mediaPlayer.setAudioAttributes(new AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).build());
            mediaPlayer.setDataSource(this, uri);
            mediaPlayer.prepareAsync();

            stateBuilder.setState(PlaybackStateCompat.STATE_PAUSED, mediaPlayer.getCurrentPosition(), 0);
            stateBuilder.setActions(PlaybackStateCompat.ACTION_SEEK_TO | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
            mediaSession.setPlaybackState(stateBuilder.build());
        } catch (IOException e) { e.printStackTrace(); }
    }

    @Override
    public void onCompletion(MediaPlayer mediaPlayer)
    {
        if(currentState != PlaybackStateCompat.STATE_NONE)
        {
            if(!repeatOne)
            {
                if(repeatAll && position == queue.size() - 1)
                    position = 0;
                else
                    position++;
            }

            if(!queue.isEmpty() && position < queue.size() - 1)
            {
                String mediaId = queue.get(position).getId();
                playSong(mediaId);
                buildMetadata(queue.get(position));
                updateQueueDisplay(SKIP_NEXT);
                buildNotification(MediaPlaybackService.this, queue.get(position));
            }
        }
    }

    @Override
    public void onPrepared(MediaPlayer mediaPlayer)
    {
        if(currentState == PlaybackStateCompat.STATE_PLAYING)
            mediaPlayer.start();
        else
            mediaPlayer.seekTo(elapsed);
    }

    @Override
    public boolean onError(MediaPlayer mediaPlayer, int i, int i1)
    {
        if(!queue.isEmpty() && position < queue.size() - 1)
        {
            String mediaId = queue.get(position).getId();
            playSong(mediaId);
            buildMetadata(queue.get(position));
            updateQueueDisplay(SKIP_NEXT);
            buildNotification(MediaPlaybackService.this, queue.get(position));
        }

        else
        {

        }
        return false;
    }

    /*/////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////*/

    @Nullable
    @Override
    public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints)
    {
        return new BrowserRoot(MY_MEDIA_ROOT_ID, null);
    }

    @Override
    public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result)
    {
        ArrayList<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();
        MediaBrowserCompat.MediaItem mediaItem;
        result.sendResult(null);
    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/helpers/SectionContent.java
package com.rookieandroid.rookiemusicplayer.helpers;

import com.rookieandroid.rookiemusicplayer.Albums;
import com.rookieandroid.rookiemusicplayer.AlbumsSections;

import java.util.ArrayList;

public class SectionContent
{
    private Albums album;
    private ArrayList<Albums> albums;
    private ArrayList<AlbumsSections> albumsSections;
    private ArrayList<Albums> section0 = new ArrayList<>();
    private ArrayList<Albums> sectionA = new ArrayList<>();
    private ArrayList<Albums> sectionB = new ArrayList<>();
    private ArrayList<Albums> sectionC = new ArrayList<>();
    private ArrayList<Albums> sectionD = new ArrayList<>();
    private ArrayList<Albums> sectionE = new ArrayList<>();
    private ArrayList<Albums> sectionF = new ArrayList<>();
    private ArrayList<Albums> sectionG = new ArrayList<>();
    private ArrayList<Albums> sectionH = new ArrayList<>();
    private ArrayList<Albums> sectionI = new ArrayList<>();
    private ArrayList<Albums> sectionJ = new ArrayList<>();
    private ArrayList<Albums> sectionK = new ArrayList<>();
    private ArrayList<Albums> sectionL = new ArrayList<>();
    private ArrayList<Albums> sectionM = new ArrayList<>();
    private ArrayList<Albums> sectionN = new ArrayList<>();
    private ArrayList<Albums> sectionO = new ArrayList<>();
    private ArrayList<Albums> sectionP = new ArrayList<>();
    private ArrayList<Albums> sectionQ = new ArrayList<>();
    private ArrayList<Albums> sectionR = new ArrayList<>();
    private ArrayList<Albums> sectionS = new ArrayList<>();
    private ArrayList<Albums> sectionT = new ArrayList<>();
    private ArrayList<Albums> sectionU = new ArrayList<>();
    private ArrayList<Albums> sectionV = new ArrayList<>();
    private ArrayList<Albums> sectionW = new ArrayList<>();
    private ArrayList<Albums> sectionX = new ArrayList<>();
    private ArrayList<Albums> sectionY = new ArrayList<>();
    private ArrayList<Albums> sectionZ = new ArrayList<>();

    public SectionContent(ArrayList<Albums> albums, ArrayList<AlbumsSections> albumsSections)
    {
        this.albums = albums;
        this.albumsSections = albumsSections;
    }

    public SectionContent(Albums album, ArrayList<AlbumsSections> albumsSections)
    {
        this.album = album;
        this.albumsSections = albumsSections;
    }

    public void sectionAlbums()
    {
        for(int i = 0; i < albums.size(); i++)
        {
            String album = albums.get(i).getAlbum();
            char sectionLetter = album.charAt(0);
            boolean letter = Character.isLetter(sectionLetter);
            if(!letter)
                section0.add(albums.get(i));
            else
                addToSection(albums.get(i), sectionLetter);
        }
        addAllSections();
    }

    private void addToSection(Albums album, Character letter)
    {
        switch(Character.toUpperCase(letter))
        {
            case 'A': sectionA.add(album); break;
            case 'B': sectionB.add(album); break;
            case 'C': sectionC.add(album); break;
            case 'D': sectionD.add(album); break;
            case 'E': sectionE.add(album); break;
            case 'F': sectionF.add(album); break;
            case 'G': sectionG.add(album); break;
            case 'H': sectionH.add(album); break;
            case 'I': sectionI.add(album); break;
            case 'J': sectionJ.add(album); break;
            case 'K': sectionK.add(album); break;
            case 'L': sectionL.add(album); break;
            case 'M': sectionM.add(album); break;
            case 'N': sectionN.add(album); break;
            case 'O': sectionO.add(album); break;
            case 'P': sectionP.add(album); break;
            case 'Q': sectionQ.add(album); break;
            case 'R': sectionR.add(album); break;
            case 'S': sectionS.add(album); break;
            case 'T': sectionT.add(album); break;
            case 'U': sectionU.add(album); break;
            case 'V': sectionV.add(album); break;
            case 'W': sectionW.add(album); break;
            case 'X': sectionX.add(album); break;
            case 'Y': sectionY.add(album); break;
            case 'Z': sectionZ.add(album); break;
        }
    }

    private void addAllSections()
    {
        albumsSections.add(new AlbumsSections(section0, null));
        albumsSections.add(new AlbumsSections(sectionA, null));
        albumsSections.add(new AlbumsSections(sectionB, null));
        albumsSections.add(new AlbumsSections(sectionC, null));
        albumsSections.add(new AlbumsSections(sectionD, null));
        albumsSections.add(new AlbumsSections(sectionE, null));
        albumsSections.add(new AlbumsSections(sectionF, null));
        albumsSections.add(new AlbumsSections(sectionG, null));
        albumsSections.add(new AlbumsSections(sectionH, null));
        albumsSections.add(new AlbumsSections(sectionI, null));
        albumsSections.add(new AlbumsSections(sectionJ, null));
        albumsSections.add(new AlbumsSections(sectionK, null));
        albumsSections.add(new AlbumsSections(sectionL, null));
        albumsSections.add(new AlbumsSections(sectionM, null));
        albumsSections.add(new AlbumsSections(sectionN, null));
        albumsSections.add(new AlbumsSections(sectionO, null));
        albumsSections.add(new AlbumsSections(sectionP, null));
        albumsSections.add(new AlbumsSections(sectionQ, null));
        albumsSections.add(new AlbumsSections(sectionR, null));
        albumsSections.add(new AlbumsSections(sectionS, null));
        albumsSections.add(new AlbumsSections(sectionT, null));
        albumsSections.add(new AlbumsSections(sectionU, null));
        albumsSections.add(new AlbumsSections(sectionV, null));
        albumsSections.add(new AlbumsSections(sectionW, null));
        albumsSections.add(new AlbumsSections(sectionX, null));
        albumsSections.add(new AlbumsSections(sectionY, null));
        albumsSections.add(new AlbumsSections(sectionZ, null));
    }

    public AlbumsSections getSection()
    {
        AlbumsSections section = null;
        char sectionLetter = album.getAlbum().charAt(0);
        if(!Character.isLetter(sectionLetter))
            section = albumsSections.get(0);
        else
        {
            switch (Character.toString(sectionLetter).toUpperCase())
            {
                case "A":
                    section = albumsSections.get(1);
                    break;

                case "B":
                    section = albumsSections.get(2);
                    break;

                case "C":
                    section = albumsSections.get(3);
                    break;

                case "D":
                    section = albumsSections.get(4);
                    break;

                case "E":
                    section = albumsSections.get(5);
                    break;

                case "F":
                    section = albumsSections.get(6);
                    break;

                case "G":
                    section = albumsSections.get(7);
                    break;

                case "H":
                    section = albumsSections.get(8);
                    break;

                case "I":
                    section = albumsSections.get(9);
                    break;

                case "J":
                    section = albumsSections.get(10);
                    break;

                case "K":
                    section = albumsSections.get(11);
                    break;

                case "L":
                    section = albumsSections.get(12);
                    break;

                case "M":
                    section = albumsSections.get(13);
                    break;

                case "N":
                    section = albumsSections.get(14);
                    break;

                case "O":
                    section = albumsSections.get(15);
                    break;

                case "P":
                    section = albumsSections.get(16);
                    break;

                case "Q":
                    section = albumsSections.get(17);
                    break;

                case "R":
                    section = albumsSections.get(18);
                    break;

                case "S":
                    section = albumsSections.get(19);
                    break;

                case "T":
                    section = albumsSections.get(20);
                    break;

                case "U":
                    section = albumsSections.get(21);
                    break;

                case "V":
                    section = albumsSections.get(22);
                    break;

                case "W":
                    section = albumsSections.get(23);
                    break;

                case "X":
                    section = albumsSections.get(24);
                    break;

                case "Y":
                    section = albumsSections.get(25);
                    break;

                case "Z":
                    section = albumsSections.get(26);
                    break;
            }
        }

        return section;
    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/Songs.java
package com.rookieandroid.rookiemusicplayer;

import android.os.Parcelable;
import android.os.Parcel;

import androidx.annotation.Nullable;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

@Entity(tableName = "saved_queue")
public class Songs implements Parcelable
{
    @PrimaryKey(autoGenerate = true)
    private int rowId;

    private String id;
    private String title;
    private String album;
    private String albumKey;
    private String art;
    private String artist;
    private String artistKey;
    private Long duration;
    private String path;
    private int track;

    public Songs(String id, String title, String album, String albumKey, String art, String artist, String artistKey, Long duration, String path, int track)
    {
        this.id = id;
        this.title = title;
        this.album = album;
        this.albumKey = albumKey;
        this.art = art;
        this.artist = artist;
        this.artistKey = artistKey;
        this.duration = duration;
        this.path = path;
        this.track = track;
    }

    //GETTERS
    public int getRowId() { return rowId; }

    public String getId() {return id;}

    public String getTitle() {return title;}

    public String getAlbum() {return album;}

    public String getAlbumKey() {return albumKey;}

    public String getArt() {return art;}

    public String getArtist() {return artist;}

    public String getArtistKey() {return artistKey;}

    public String getPath() {return path;}

    public Long getDuration() {return duration;}

    public int getTrack() {return track;}

    //SETTERS
    public void setRowId(int rowId) { this.rowId = rowId; }

    public void setId(String id) { this.id = id; }

    public void setTitle(String title) { this.title = title; }

    public void setAlbum(String album) { this.album = album; }

    public void setAlbumKey(String albumKey) { this.albumKey = albumKey; }

    public void setArt(String art) { this.art = art; }

    public void setArtist(String artist) { this.artist = artist; }

    public void setArtistKey(String artistKey) { this.artistKey = artistKey; }

    public void setDuration(Long duration) { this.duration = duration; }

    public void setPath(String path) { this.path = path; }

    public void setTrack(int track) { this.track = track; }

    protected Songs(Parcel in)
    {
        id = in.readString();
        title = in.readString();
        album = in.readString();
        albumKey = in.readString();
        art = in.readString();
        artist = in.readString();
        artistKey = in.readString();
        path = in.readString();
        if (in.readByte() == 0) {
            duration = null;
        } else {
            duration = in.readLong();
        }
        track = in.readInt();
    }

    public static final Creator<Songs> CREATOR = new Creator<Songs>() {
        @Override
        public Songs createFromParcel(Parcel in) {
            return new Songs(in);
        }

        @Override
        public Songs[] newArray(int size) {
            return new Songs[size];
        }
    };

    @Override
    public int describeContents() {return 0;}

    @Override
    public void writeToParcel(Parcel parcel, int i)
    {
        parcel.writeString(id);
        parcel.writeString(title);
        parcel.writeString(album);
        parcel.writeString(albumKey);
        parcel.writeString(art);
        parcel.writeString(artist);
        parcel.writeString(artistKey);
        parcel.writeString(path);
        if (duration == null) {
            parcel.writeByte((byte) 0);
        } else {
            parcel.writeByte((byte) 1);
            parcel.writeLong(duration);
        }
        parcel.writeInt(track);
    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/fragments/AlbumsFragment.java
package com.rookieandroid.rookiemusicplayer.fragments;

import android.content.Context;
import android.os.Bundle;
import android.transition.Fade;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.SharedElementCallback;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.rookieandroid.rookiemusicplayer.Albums;
import com.rookieandroid.rookiemusicplayer.AlbumsSections;
import com.rookieandroid.rookiemusicplayer.helpers.IndexScroller;
import com.rookieandroid.rookiemusicplayer.helpers.MediaControlDialog;
import com.rookieandroid.rookiemusicplayer.R;
import com.rookieandroid.rookiemusicplayer.adapters.AlbumsAdapter;
import com.rookieandroid.rookiemusicplayer.adapters.AlbumsSectionsAdapter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static com.rookieandroid.rookiemusicplayer.App.ARTIST_DETAIL;
import static com.rookieandroid.rookiemusicplayer.App.FROM_ARTIST;
import static com.rookieandroid.rookiemusicplayer.App.FROM_LIBRARY;
import static com.rookieandroid.rookiemusicplayer.App.FROM_SEARCH;
import static com.rookieandroid.rookiemusicplayer.App.SEARCH;
import static com.rookieandroid.rookiemusicplayer.App.lettersAlbums;
import static com.rookieandroid.rookiemusicplayer.App.sectionsAlbums;

public class AlbumsFragment extends Fragment implements AlbumsAdapter.ListItemClickListener
{
    private View rootView;
    private AlbumsSectionsAdapter albumsSectionsAdapter;
    private AlbumsAdapter albumsAdapter;
    private RecyclerView recyclerView;
    private ArrayList<AlbumsSections> albumsSections;
    private ArrayList<Albums> albums;
    private NowPlayingAlbum nowPlayingAlbum;
    private MediaControlDialog mediaControlDialog;
    private IndexScroller indexScroller;
    private int code;
    private int from;
    private int position;
    private MediaControlDialog.UpdateLibrary updateLibrary;

    public AlbumsFragment(ArrayList<AlbumsSections> albumsSections, MediaControlDialog.UpdateLibrary updateLibrary)
    {
        this.albumsSections = albumsSections;
        this.updateLibrary = updateLibrary;
    }

    public AlbumsFragment(ArrayList<Albums> albums, int code, MediaControlDialog.UpdateLibrary updateLibrary)
    {
        this.albums = albums;
        this.code = code;
        this.updateLibrary = updateLibrary;
    }

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        //setSharedElementEnterTransition(new App.DetailsTransition().setDuration(500));
        //setSharedElementReturnTransition(new App.DetailsTransition());
        setExitSharedElementCallback(new SharedElementCallback() {
            @Override
            public void onMapSharedElements(List<String> names, Map<String, View> sharedElements)
            {
                RecyclerView.ViewHolder viewHolder = recyclerView.findViewHolderForAdapterPosition(position);
                if(viewHolder == null)
                    return;
                sharedElements.put(names.get(0), viewHolder.itemView.findViewById(R.id.gridArt));
            }
        });
        setExitTransition(new Fade());
        setReenterTransition(new Fade().setStartDelay(500));
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
    {
        super.onCreateView(inflater, container, savedInstanceState);
        rootView = getLayoutInflater().inflate(R.layout.fragment_albums, container, false);
        recyclerView = rootView.findViewById(R.id.albumSections);

        switch (code)
        {
            case ARTIST_DETAIL:
                albumsAdapter = new AlbumsAdapter(albums, this, this);
                GridLayoutManager gridlayoutManager1 = new GridLayoutManager(getContext(), 2);
                recyclerView.setLayoutManager(gridlayoutManager1);
                recyclerView.setAdapter(albumsAdapter);
                rootView.findViewById(R.id.indexScroller).setVisibility(View.GONE);
                rootView.findViewById(R.id.indexLetter).setVisibility(View.GONE);
                from = FROM_ARTIST;
                break;

            case SEARCH:
                albumsAdapter = new AlbumsAdapter(albums, this, this);
                GridLayoutManager gridlayoutManager2 = new GridLayoutManager(getContext(), 2);
                recyclerView.setLayoutManager(gridlayoutManager2);
                recyclerView.setAdapter(albumsAdapter);
                rootView.findViewById(R.id.indexScroller).setVisibility(View.GONE);
                rootView.findViewById(R.id.indexLetter).setVisibility(View.GONE);
                from = FROM_SEARCH;
                break;

            default:
                albumsAdapter = new AlbumsAdapter(albums, this, this);
                //albumsSectionsAdapter = new AlbumsSectionsAdapter(albumsSections,this);
                //LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
                GridLayoutManager layoutManager = new GridLayoutManager(getContext(), 2);
                layoutManager.setInitialPrefetchItemCount(albums.size());
                recyclerView.setHasFixedSize(true);
                recyclerView.setLayoutManager(layoutManager);
                recyclerView.setAdapter(albumsAdapter);
                //rootView.findViewById(R.id.indexLetter).setVisibility(View.GONE);
                indexScroller = new IndexScroller(getContext(), rootView, recyclerView, layoutManager, sectionsAlbums, lettersAlbums);
                indexScroller.setScrolling();
                from = FROM_LIBRARY;
                break;
        }

        postponeEnterTransition();
        return rootView;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState)
    {
        super.onViewCreated(view, savedInstanceState);
        /*recyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
            @Override
            public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom)
            {
                recyclerView.removeOnLayoutChangeListener(this);
                final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                View viewAtPosition = layoutManager.findViewByPosition(position);
                // Scroll to position if the view for the current position is null (not currently part of
                // layout manager children), or it's not completely visible.
                if (viewAtPosition == null || layoutManager.isViewPartiallyVisible(viewAtPosition, false, true))
                {
                    recyclerView.post(() -> layoutManager.scrollToPosition(position));
                }
            }
        });*/
    }

    @Override
    public void onListItemClick(Albums album, ImageView art, int position)
    {
        this.position = position;
        nowPlayingAlbum.sendAlbum(album, art, from, updateLibrary, position);
    }

    @Override
    public void onLongItemClick(Albums album)
    {
        if(code != SEARCH)
        {
            if(code == ARTIST_DETAIL)
            {
                mediaControlDialog = new MediaControlDialog(getContext(), album, albums, albumsAdapter, updateLibrary, FROM_ARTIST);
                mediaControlDialog.OpenDialog();
            }

            else
            {
                /*SectionContent sectionContent = new SectionContent(album, albumsSections);
                AlbumsSections section = sectionContent.getSection();
                albums = section.getSectionedAlbums();
                albumsAdapter = section.getAlbumsAdapter();*/
                mediaControlDialog = new MediaControlDialog(getContext(), album, albums, albumsAdapter, updateLibrary, FROM_LIBRARY);
                mediaControlDialog.OpenDialog();
            }
        }

    }

    public interface NowPlayingAlbum
    {
        void sendAlbum(Albums album, ImageView sharedArt, int from, MediaControlDialog.UpdateLibrary updateLibrary, int position);
    }

    @Override
    public void onAttach(@NonNull Context context)
    {
        super.onAttach(context);
        try
        {
            nowPlayingAlbum = (NowPlayingAlbum) context;
        }
        catch (ClassCastException e)
        {
            throw new ClassCastException(context.toString().trim() + " must implement sendAlbum");
        }
    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/adapters/AlbumDetailsAdapter.java
package com.rookieandroid.rookiemusicplayer.adapters;

import android.content.Context;
import android.os.Handler;
import android.support.v4.media.session.PlaybackStateCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.rookieandroid.rookiemusicplayer.R;
import com.rookieandroid.rookiemusicplayer.Songs;

import java.util.ArrayList;
import java.util.Locale;

import static com.rookieandroid.rookiemusicplayer.App.currentState;

public class AlbumDetailsAdapter extends RecyclerView.Adapter<AlbumDetailsAdapter.AlbumDetailsViewHolder>
{
    private ArrayList<Songs> songs;
    private Context context;
    private ListItemClickListener listener;
    private int previousPosition;
    private int currentlyPlaying = 1;
    private ImageView currentSong;
    private ImageView previousSong;
    private int position;
    private Handler playingHandler = new Handler();

    public interface ListItemClickListener
    {
        void onListItemClick(int position);
        void onLongListItemClick(int position);
    }

    public AlbumDetailsAdapter(ArrayList<Songs> songs, ListItemClickListener listener)
    {
        this.songs = songs;
        this.listener = listener;
        setHasStableIds(true);
    }

    @NonNull
    @Override
    public AlbumDetailsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
    {
        context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.album_songs_list, parent, false);
        return new AlbumDetailsViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull AlbumDetailsViewHolder holder, int position)
    {
        holder.bind(position);
    }

    @Override
    public int getItemCount() { return songs.size(); }

    @Override
    public int getItemViewType(int position) { return Integer.parseInt(songs.get(position).getId()); }

    @Override
    public long getItemId(int position) { return Integer.parseInt(songs.get(position).getId()); }

    private Runnable updateTrackPlaying = new Runnable()
    {
        @Override
        public void run()
        {
            if(currentState == PlaybackStateCompat.STATE_PLAYING)
            {
                switch(currentlyPlaying)
                {
                    case 1:
                        currentSong.setBackgroundColor(context.getColor(R.color.black));
                        currentSong.setImageResource(R.drawable.ic_currentlyplaying2);
                        currentlyPlaying = 2;
                        break;

                    case 2:
                        currentSong.setBackgroundColor(context.getColor(R.color.black));
                        currentSong.setImageResource(R.drawable.ic_currentlyplaying3);
                        currentlyPlaying = 3;
                        break;

                    case 3:
                        currentSong.setBackgroundColor(context.getColor(R.color.black));
                        currentSong.setImageResource(R.drawable.ic_currentlyplaying4);
                        currentlyPlaying = 4;
                        break;

                    case 4:
                        currentSong.setBackgroundColor(context.getColor(R.color.black));
                        currentSong.setImageResource(R.drawable.ic_currentlyplaying1);
                        currentlyPlaying = 1;
                        break;
                }
                playingHandler.postDelayed(this, 300);
            }

            else
            {
                currentSong.setBackgroundColor(context.getColor(R.color.black));
                currentSong.setImageResource(R.drawable.ic_currentlyplaying1);
                currentlyPlaying = 1;
                playingHandler.post(this);
            }
        }
    };

    class AlbumDetailsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener
    {

        private TextView trackName;
        private TextView trackDuration;
        private TextView trackNumber;
        private TextView trackNumberOffset;
        private ImageView trackPlaying;

        public AlbumDetailsViewHolder(@NonNull View itemView)
        {
            super(itemView);
            trackName = itemView.findViewById(R.id.trackName);
            trackDuration = itemView.findViewById(R.id.trackDuration);
            trackNumber = itemView.findViewById(R.id.trackNumber);
            trackNumberOffset = itemView.findViewById(R.id.trackNumberOffset);
            trackPlaying = itemView.findViewById(R.id.trackPlaying);
            itemView.setOnClickListener(this);
            itemView.setOnLongClickListener(this);
        }

        public void bind(int position)
        {
            trackName.setText(songs.get(position).getTitle());

            String duration;
            long minutes = (songs.get(position).getDuration() / 1000) / 60;
            long seconds = (songs.get(position).getDuration() / 1000) % 60;
            if(seconds > 9)
                duration = String.format(Locale.US, "%d:%d", minutes, seconds);
            else
                duration = String.format(Locale.US, "%d:%d%d", minutes, 0, seconds);
            trackDuration.setText(duration);

            setTrackNumber(songs.get(position).getTrack());
        }

        private void setTrackNumber(int trackNum)
        {
            if(trackNum == 0 || trackNum == -1)
                trackNumber.setText("");

            else if(trackNum > 1000)
            {
                if((trackNum - 1000) > 9)
                    trackNumberOffset.setVisibility(View.GONE);
                trackNumber.setText(String.valueOf(trackNum - 1000));
            }

            else
            {
                if(trackNum > 9)
                    trackNumberOffset.setVisibility(View.GONE);
                trackNumber.setText(trackNum + "");
            }
        }

        @Override
        public void onClick(View view)
        {
            /*if(previousSong == null)
            {
                previousSong = trackPlaying;
                previousPosition = getAdapterPosition();
            }

            else
            {
                playingHandler.removeCallbacks(updateTrackPlaying);
                previousSong.setBackgroundColor(context.getColor(R.color.transparent));
                previousSong.setImageResource(0);
                previousSong = trackPlaying;
                previousPosition = getAdapterPosition();
            }*/

            position = getAdapterPosition();
            listener.onListItemClick(position);
            /*currentSong = trackPlaying;
            currentSong.setBackgroundColor(context.getColor(R.color.black));
            currentSong.setImageResource(R.drawable.ic_currentlyplaying2);
            currentlyPlaying = 1;
            playingHandler.post(updateTrackPlaying);*/
        }


        @Override
        public boolean onLongClick(View view)
        {
            int clickedPosition = getAdapterPosition();
            listener.onLongListItemClick(clickedPosition);
            return true;
        }
    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/architecture/StateRepository.java
package com.rookieandroid.rookiemusicplayer.architecture;

import android.app.Application;
import android.os.AsyncTask;

import androidx.lifecycle.LiveData;

import com.rookieandroid.rookiemusicplayer.SavedStateDetails;
import com.rookieandroid.rookiemusicplayer.Songs;

import java.util.List;

public class StateRepository
{
    private SavedQueueDao savedQueueDao;
    private SavedDetailsDao savedDetailsDao;
    private LiveData<List<Songs>> savedQueue;
    private LiveData<List<SavedStateDetails>> savedStateDetails;

    public StateRepository(Application application)
    {
        StateDatabase stateDatabase = StateDatabase.getInstance(application);
        savedQueueDao = stateDatabase.savedQueueDao();
        savedDetailsDao = stateDatabase.savedDetailsDao();
        savedQueue = savedQueueDao.getSavedQueue();
        savedStateDetails = savedDetailsDao.getSavedDetails();
    }

    public void insertAll(List<Songs> savedQueue)
    {
        new InsertAllQueueAsyncTask(savedQueueDao).execute(savedQueue);
    }

    public void deleteSavedQueue()
    {
        new DeleteSavedQueueAsyncTask(savedQueueDao).execute();
    }

    public void insert(SavedDetails savedDetails)
    {
        new InsertDetailsAsyncTask(savedDetailsDao).execute(savedDetails);
    }

    public void update(SavedDetails savedDetails)
    {
        new UpdateDetailsAsyncTask(savedDetailsDao).execute(savedDetails);
    }

    public void delete(SavedDetails savedDetails)
    {
        new DeleteDetailsAsyncTask(savedDetailsDao).execute(savedDetails);
    }

    public void deleteSavedDetails()
    {
        new DeleteDetailsAsyncTask(savedDetailsDao).execute();
    }

    public LiveData<List<Songs>> getSavedQueue()
    {
        return savedQueue;
    }

    public LiveData<List<SavedStateDetails>> getSavedStateDetails()
    {
        return  savedStateDetails;
    }

    private static class InsertAllQueueAsyncTask extends AsyncTask<List<Songs>, Void, Void>
    {
        private SavedQueueDao savedQueueDao;

        private InsertAllQueueAsyncTask(SavedQueueDao savedQueueDao)
        {
            this.savedQueueDao = savedQueueDao;
        }

        @Override
        protected Void doInBackground(List<Songs>... lists)
        {
            savedQueueDao.insertAll(lists[0]);
            return null;
        }
    }

    private static class DeleteSavedQueueAsyncTask extends AsyncTask<Void, Void, Void>
    {
        private SavedQueueDao savedQueueDao;

        private DeleteSavedQueueAsyncTask(SavedQueueDao savedQueueDao)
        {
            this.savedQueueDao = savedQueueDao;
        }

        @Override
        protected Void doInBackground(Void... voids)
        {
            savedQueueDao.deleteSavedQueue();
            return null;
        }
    }

    private static class InsertDetailsAsyncTask extends AsyncTask<SavedDetails, Void, Void>
    {
        private SavedDetailsDao savedDetailsDao;

        private InsertDetailsAsyncTask(SavedDetailsDao savedDetailsDao)
        {
            this.savedDetailsDao = savedDetailsDao;
        }

        @Override
        protected Void doInBackground(SavedDetails... savedDetails)
        {
            savedDetailsDao.insert(savedDetails[0]);
            return null;
        }
    }

    private static class UpdateDetailsAsyncTask extends AsyncTask<SavedDetails, Void, Void>
    {
        private SavedDetailsDao savedDetailsDao;

        private UpdateDetailsAsyncTask(SavedDetailsDao savedDetailsDao)
        {
            this.savedDetailsDao = savedDetailsDao;
        }

        @Override
        protected Void doInBackground(SavedDetails... savedDetails)
        {
            savedDetailsDao.update(savedDetails[0]);
            return null;
        }
    }

    private static class DeleteDetailsAsyncTask extends AsyncTask<SavedDetails, Void, Void>
    {
        private SavedDetailsDao savedDetailsDao;

        private DeleteDetailsAsyncTask(SavedDetailsDao savedDetailsDao)
        {
            this.savedDetailsDao = savedDetailsDao;
        }

        @Override
        protected Void doInBackground(SavedDetails... savedDetails)
        {
            savedDetailsDao.delete(savedDetails[0]);
            return null;
        }
    }

    private static class DeleteSavedDetailsAsyncTask extends AsyncTask<Void, Void, Void>
    {
        private SavedDetailsDao savedDetailsDao;

        private DeleteSavedDetailsAsyncTask(SavedDetailsDao savedDetailsDao)
        {
            this.savedDetailsDao = savedDetailsDao;
        }

        @Override
        protected Void doInBackground(Void... voids)
        {
            savedDetailsDao.deleteSavedDetails();
            return null;
        }
    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/fragments/PlaylistDetailsFragment.java
package com.rookieandroid.rookiemusicplayer.fragments;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.rookieandroid.rookiemusicplayer.App;
import com.rookieandroid.rookiemusicplayer.helpers.GetMedia;
import com.rookieandroid.rookiemusicplayer.helpers.MediaControlDialog;
import com.rookieandroid.rookiemusicplayer.Playlists;
import com.rookieandroid.rookiemusicplayer.R;
import com.rookieandroid.rookiemusicplayer.Songs;
import com.rookieandroid.rookiemusicplayer.adapters.PlaylistsAdapter;
import com.rookieandroid.rookiemusicplayer.adapters.SongsAdapter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import static com.rookieandroid.rookiemusicplayer.App.CLEAR;
import static com.rookieandroid.rookiemusicplayer.App.FROM_PLAYLIST;
import static com.rookieandroid.rookiemusicplayer.App.GET_PLAYLIST_SONGS;
import static com.rookieandroid.rookiemusicplayer.App.GET_RECENT_SONGS;
import static com.rookieandroid.rookiemusicplayer.App.PLAYLIST_MEDIA_LOADER;
import static com.rookieandroid.rookiemusicplayer.App.RECENTLY_ADDED;
import static com.rookieandroid.rookiemusicplayer.App.SET_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.SET_QUEUE_PLAYLIST;
import static com.rookieandroid.rookiemusicplayer.App.SET_UP_NEXT;
import static com.rookieandroid.rookiemusicplayer.App.SHUFFLE_QUEUE;
import static com.rookieandroid.rookiemusicplayer.App.mediaBrowserHelper;
import static com.rookieandroid.rookiemusicplayer.App.nowPlayingFrom;


public class PlaylistDetailsFragment extends Fragment implements LoaderManager.LoaderCallbacks<ArrayList>, SongsAdapter.ListItemClickListener, View.OnClickListener, MediaControlDialog.UpdatePlaylist
{
    private final String TAG = PlaylistDetailsFragment.class.getSimpleName();

    private View rootView;
    private Playlists currentPlaylist;
    private ArrayList<Songs> playlistSongs;
    private SongsAdapter playlistSongsAdapter;
    private RecyclerView recyclerView;
    private TextView playlistName;
    private TextView playlistCount;
    private TextView playlistDescription;
    private Button playAlbum;
    private Button shuffleAlbum;
    private ImageView albumMediaControl;
    private MediaControlDialog mediaControlDialog;

    public PlaylistDetailsFragment(Playlists currentPlaylist) { this.currentPlaylist = currentPlaylist; }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
    {
        super.onCreateView(inflater, container, savedInstanceState);
        rootView = inflater.inflate(R.layout.fragment_playlist_details, container, false);

        playlistName = rootView.findViewById(R.id.albumDetailName);
        playlistName.setText(currentPlaylist.getPlaylist());

        playlistCount = rootView.findViewById(R.id.numberOfSongs);

        rootView.findViewById(R.id.albumDetailArtist).setVisibility(View.GONE);

        playlistDescription = rootView.findViewById(R.id.playlistDescription);
        playlistDescription.setText(currentPlaylist.getDescription());

        playAlbum = rootView.findViewById(R.id.playAlbum);
        playAlbum.setOnClickListener(this);
        shuffleAlbum = rootView.findViewById(R.id.shuffleAlbum);
        shuffleAlbum.setOnClickListener(this);
        albumMediaControl = rootView.findViewById(R.id.albumMediaControl);
        albumMediaControl.setOnClickListener(this);

        recyclerView = rootView.findViewById(R.id.albumDetailSongs);

        LoaderManager.getInstance(this).initLoader(PLAYLIST_MEDIA_LOADER, null, this);

        //Log.i(TAG, "FRAGMENT CURRENTLY VISIBLE: " + TAG);
        return rootView;
    }

    private void setQueue(int position)
    {
        mediaBrowserHelper.getMediaController().getTransportControls().sendCustomAction(CLEAR, null);
        Bundle queue = new Bundle();
        queue.putInt("CURRENT_QUEUE_POSITION", position);
        if(currentPlaylist.getPlaylist().equals("Recently Added"))
            queue.putInt("FROM", RECENTLY_ADDED);
        else
            queue.putInt("FROM", FROM_PLAYLIST);
        mediaBrowserHelper.getMediaController().getTransportControls().sendCustomAction(SET_POSITION, queue);
        mediaBrowserHelper.getMediaController().getTransportControls().sendCustomAction(SET_QUEUE_PLAYLIST, queue);
        mediaBrowserHelper.getMediaController().getTransportControls().playFromMediaId(playlistSongs.get(position).getId(), null);
        mediaBrowserHelper.getMediaController().getTransportControls().sendCustomAction(SET_UP_NEXT, null);
        nowPlayingFrom = "Now playing from " + currentPlaylist.getPlaylist();
        mediaBrowserHelper.setBottomSheetQueue();
    }

    private void setPlaylistCount()
    {
        int pCount = playlistSongs.size();
        String count;
        if(pCount == 1)
        {
            count = pCount + " song";
            playlistCount.setText(count);
        }

        else
        {
            count = pCount + " songs";
            playlistCount.setText(count);
        }
    }

    @Override
    public void onClick(View view)
    {
        Bundle from = new Bundle();
        switch (view.getId())
        {
            case R.id.playAlbum:
                Collections.sort(playlistSongs, Comparator.comparing(Songs::getTrack));
                App.playlistSongs = playlistSongs;
                setQueue(0);
                break;

            case R.id.shuffleAlbum:
                Collections.shuffle(playlistSongs);
                App.playlistSongs = playlistSongs;
                setQueue(0);
                if(currentPlaylist.getPlaylist().equals("Recently Added"))
                    from.putInt("FROM", RECENTLY_ADDED);
                else
                    from.putInt("FROM", FROM_PLAYLIST);
                mediaBrowserHelper.getMediaController().getTransportControls().sendCustomAction(SHUFFLE_QUEUE, from);
                break;

            case R.id.albumMediaControl:
                ArrayList<Playlists> playlists = null;
                PlaylistsAdapter playlistsAdapter = null;
                mediaControlDialog = new MediaControlDialog(getContext(), currentPlaylist, playlists, playlistsAdapter, FROM_PLAYLIST);
                mediaControlDialog.OpenDialog();
                break;
        }
    }

    @Override
    public void onListItemClick(int position)
    {
        Collections.sort(playlistSongs, Comparator.comparing(Songs::getTrack));
        Collections.reverse(playlistSongs);
        App.playlistSongs = playlistSongs;
        setQueue(position);
    }

    @Override
    public void onLongListItemClick(int position)
    {
        if(!currentPlaylist.getPlaylist().equals("Recently Added"))
        {
            mediaControlDialog = new MediaControlDialog(getContext(), playlistSongs.get(position), currentPlaylist, playlistSongs, playlistSongsAdapter, this, FROM_PLAYLIST);
            mediaControlDialog.OpenDialog();
        }
    }

    @Override
    public void updatePlaylistCount()
    {
        setPlaylistCount();
    }

    @NonNull
    @Override
    public Loader<ArrayList> onCreateLoader(int id, @Nullable Bundle args)
    {
        if(currentPlaylist.getPlaylist().equals("Recently Added"))
        {
            return new GetMedia(getContext(), GET_RECENT_SONGS, -1);
            //playlistSongs = getMedia.getRecentlyAddedSongs();
        }

        else
        {
            return new GetMedia(getContext(), GET_PLAYLIST_SONGS, -1, currentPlaylist);
            //playlistSongs = getMedia.getPlaylistSongs(currentPlaylist);
        }
    }

    @Override
    public void onLoadFinished(@NonNull Loader<ArrayList> loader, ArrayList data)
    {
        playlistSongs = (ArrayList<Songs>) data;
        setPlaylistCount();
        LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
        playlistSongsAdapter = new SongsAdapter(playlistSongs, this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(playlistSongsAdapter);
    }

    @Override
    public void onLoaderReset(@NonNull Loader<ArrayList> loader){}
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/architecture/SavedDetailsDao.java
package com.rookieandroid.rookiemusicplayer.architecture;

import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

import com.rookieandroid.rookiemusicplayer.SavedStateDetails;

import java.util.List;

@Dao
public interface SavedDetailsDao
{
    @Insert
    void insert(SavedDetails savedDetails);

    @Delete
    void delete(SavedDetails savedDetails);

    @Update
    void update(SavedDetails savedDetails);

    @Query("DELETE FROM saved_details")
    void deleteSavedDetails();

    @Query("SELECT * FROM saved_details")
    LiveData<List<SavedStateDetails>> getSavedDetails();
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/helpers/MediaBrowserHelperMotion.java
package com.rookieandroid.rookiemusicplayer.helpers;

import android.app.Activity;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.ColorStateList;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.provider.MediaStore;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.cardview.widget.CardView;
import androidx.constraintlayout.motion.widget.MotionLayout;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.rookieandroid.rookiemusicplayer.Albums;
import com.rookieandroid.rookiemusicplayer.Artists;
import com.rookieandroid.rookiemusicplayer.MainActivity;
import com.rookieandroid.rookiemusicplayer.MediaPlaybackService;
import com.rookieandroid.rookiemusicplayer.Playlists;
import com.rookieandroid.rookiemusicplayer.R;
import com.rookieandroid.rookiemusicplayer.Songs;
import com.rookieandroid.rookiemusicplayer.adapters.QueueAdapter;
import com.rookieandroid.rookiemusicplayer.architecture.SavedDetails;
import com.rookieandroid.rookiemusicplayer.architecture.StateViewModel;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import static com.rookieandroid.rookiemusicplayer.App.ACTIVITY_RESTORE;
import static com.rookieandroid.rookiemusicplayer.App.ADD_ALBUM_ARTIST_PLAYLIST;
import static com.rookieandroid.rookiemusicplayer.App.ADD_SONG;
import static com.rookieandroid.rookiemusicplayer.App.CLEAR;
import static com.rookieandroid.rookiemusicplayer.App.FROM_LIBRARY;
import static com.rookieandroid.rookiemusicplayer.App.GET_ALBUM_SONGS;
import static com.rookieandroid.rookiemusicplayer.App.GET_ARTIST_SONGS;
import static com.rookieandroid.rookiemusicplayer.App.GET_CURRENT_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.GET_ARTIST_ALBUM;
import static com.rookieandroid.rookiemusicplayer.App.GET_PLAYLIST_SONGS;
import static com.rookieandroid.rookiemusicplayer.App.GET_QUEUE_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.INITIALIZE_QUEUE_CHANGE;
import static com.rookieandroid.rookiemusicplayer.App.PLAYLIST_MEDIA_LOADER;
import static com.rookieandroid.rookiemusicplayer.App.PLAY_BUTTON_START;
import static com.rookieandroid.rookiemusicplayer.App.QUEUE_CLICK;
import static com.rookieandroid.rookiemusicplayer.App.QUEUE_END;
import static com.rookieandroid.rookiemusicplayer.App.BROWSER_MEDIA_LOADER;
import static com.rookieandroid.rookiemusicplayer.App.QUEUE_NEXT;
import static com.rookieandroid.rookiemusicplayer.App.RECEIVE_ARTIST_ALBUM;
import static com.rookieandroid.rookiemusicplayer.App.RECEIVE_PLAYBACKSTATE;
import static com.rookieandroid.rookiemusicplayer.App.RECEIVE_QUEUE_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.RESTORE_SAVED_QUEUE;
import static com.rookieandroid.rookiemusicplayer.App.SAVE_QUEUE;
import static com.rookieandroid.rookiemusicplayer.App.SET_ELAPSED_TIME;
import static com.rookieandroid.rookiemusicplayer.App.SET_FROM;
import static com.rookieandroid.rookiemusicplayer.App.SET_POSITION;
import static com.rookieandroid.rookiemusicplayer.App.SET_QUEUE_LIBRARY;
import static com.rookieandroid.rookiemusicplayer.App.SET_UP_NEXT;
import static com.rookieandroid.rookiemusicplayer.App.addToQueue;
import static com.rookieandroid.rookiemusicplayer.App.currentState;
import static com.rookieandroid.rookiemusicplayer.App.itemTouchHelper;
import static com.rookieandroid.rookiemusicplayer.App.nowPlayingFrom;
import static com.rookieandroid.rookiemusicplayer.App.queueAdapter;
import static com.rookieandroid.rookiemusicplayer.App.queueDisplay;
import static com.rookieandroid.rookiemusicplayer.App.repeat;
import static com.rookieandroid.rookiemusicplayer.App.savedSongs;
import static com.rookieandroid.rookiemusicplayer.App.savedState;
import static com.rookieandroid.rookiemusicplayer.App.shuffle;

public class MediaBrowserHelperMotion implements QueueAdapter.ListItemClickListener
{
    private final String TAG = MediaBrowserHelperMotion.class.getSimpleName();

    private Context context;
    private MediaBrowserCompat mediaBrowserCompat;
    private MediaControllerCompat mediaControllerCompat;
    private int currentDuration;
    private int currentElapsed;
    private Songs artist_album;

    //ADDING TO QUEUE OR PLAYLIST
    private int content;
    private Songs song;

    private Albums album;
    private ArrayList<Songs> albumSongs;

    private Artists artist;
    private ArrayList<Songs> artistSongs;

    private Playlists playlist;
    private Playlists playlistToAdd;
    private ArrayList<Songs> playlistSongs;


    //UI
    private View rootView;
    private Handler timeHandler = new Handler();
    private AudioManager audioManager;
    private RecyclerView recyclerView;

    private ImageView nowPlayingArt;
    private CardView nowPlayingArtHolder;
    private TextView nowPlayingName;
    private TextView nowPlayingArtist;
    private ImageButton nowPlayingButton;
    private ImageButton nowPlayingForward;
    private MotionLayout motionLayout;
    private FrameLayout frameLayout;

    private TextView nowPlayingNameExpanded;
    private TextView nowPlayingArtistAlbumExpanded;
    private TextView nowPlayingDurationExpanded;
    private TextView nowPlayingElapsedExpanded;
    private TextView nowPlayingFromExpanded;
    private ImageButton nowPlayingButtonExpanded;
    private ImageButton nowPlayingBackExpanded;
    private ImageButton nowPlayingForwardExpanded;
    private ImageButton nowPlayingSlideDown;
    private ProgressBar progressBar;
    private SeekBar seekBar;
    private SeekBar volumeBar;
    private Button shuffleExpanded;
    private View shuffleBackground;
    private Button repeatExpanded;
    private View repeatBackground;
    private ImageView upNextQueue;
    private ImageView upNextBackground;
    private ImageView upNextShuffle;
    private View upNextShuffleBackground;
    private ImageView upNextRepeat;
    private View upNextRepeatBackground;
    private boolean upNextIsShowing;

    //SAVING UI STATE
    private int savedPosition;
    private int savedElapsed;
    private int savedDuration;
    private int savedShuffle;
    private int savedRepeat;
    private int savedPlayState;
    private String savedNowPlayingFrom;
    private int savedFrom;
    private StateViewModel stateViewModel;

    public MediaBrowserHelperMotion(Context context, View rootView, StateViewModel stateViewModel)
    {
        this.context = context;
        this.rootView = rootView;
        this.stateViewModel = stateViewModel;
    }

    public void onCreate()
    {
        mediaBrowserCompat = new MediaBrowserCompat(context, new ComponentName(context, MediaPlaybackService.class), connectionCallbacks, null);
        mediaBrowserCompat.connect();
    }

    public void onRestart()
    {
        if(!mediaBrowserCompat.isConnected())
            mediaBrowserCompat.connect();
    }

    public void onStop()
    {
        mediaControllerCompat.sendCommand(GET_QUEUE_POSITION, null, resultReceiver);
    }

    public void onDestroy()
    {
        if (MediaControllerCompat.getMediaController((Activity) context) != null)
            MediaControllerCompat.getMediaController((Activity) context).unregisterCallback(controllerCallbacks);
        mediaBrowserCompat.unsubscribe(mediaBrowserCompat.getRoot());
        mediaBrowserCompat.disconnect();
    }

    public void setVolumeBar()
    {
        int currentVolume = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
        volumeBar.setProgress(currentVolume);
    }

    private final MediaBrowserCompat.ConnectionCallback connectionCallbacks =
            new MediaBrowserCompat.ConnectionCallback()
            {
                @Override
                public void onConnected()
                {
                    String root = mediaBrowserCompat.getRoot();
                    mediaBrowserCompat.subscribe(root, subscriptionCallbacks);
                    try
                    {
                        // Get the token for the MediaSession
                        MediaSessionCompat.Token token = mediaBrowserCompat.getSessionToken();

                        // Create a MediaControllerCompat
                        mediaControllerCompat = new MediaControllerCompat(context, token);
                        mediaControllerCompat.registerCallback(controllerCallbacks);
                        mediaControllerCompat.getTransportControls().setShuffleMode(PlaybackStateCompat.SHUFFLE_MODE_NONE);
                        mediaControllerCompat.getTransportControls().setRepeatMode(PlaybackStateCompat.REPEAT_MODE_NONE);
                        // Save the controller
                        MediaControllerCompat.setMediaController((Activity) context, mediaControllerCompat);

                    } catch (RemoteException e) { /*Log.e(MainActivity.class.getSimpleName(), "Error creating controller", e);*/ }

                    // Finish building the UI
                    buildTransportControls();

                    if(!savedSongs.isEmpty() && !savedState.isEmpty())
                    {
                        if(currentState != PlaybackStateCompat.STATE_PLAYING)
                        {
                            savedPosition = savedState.get(0).getPosition();
                            savedElapsed = savedState.get(0).getElapsed();
                            savedDuration = savedState.get(0).getDuration();
                            savedFrom = savedState.get(0).getFrom();
                            shuffle = savedState.get(0).getShuffle();
                            mediaControllerCompat.getTransportControls().setShuffleMode(shuffle);
                            repeat = savedState.get(0).getRepeat();
                            mediaControllerCompat.getTransportControls().setRepeatMode(repeat);
                            currentState = savedState.get(0).getState();
                            nowPlayingFrom = savedState.get(0).getNow_playing_from();
                            mediaControllerCompat.getTransportControls().sendCustomAction(CLEAR, null);
                            Bundle queuePosition = new Bundle(); queuePosition.putInt("CURRENT_QUEUE_POSITION", savedPosition);
                            Bundle elapsedTime = new Bundle(); elapsedTime.putInt("CURRENT_ELAPSED_TIME", savedElapsed);
                            Bundle from = new Bundle(); from.putInt("CURRENT_FROM", savedFrom);
                            mediaControllerCompat.getTransportControls().sendCustomAction(SET_POSITION, queuePosition);
                            mediaControllerCompat.getTransportControls().sendCustomAction(SET_FROM, from);
                            mediaControllerCompat.getTransportControls().sendCustomAction(RESTORE_SAVED_QUEUE, null);
                            mediaControllerCompat.getTransportControls().sendCustomAction(SET_ELAPSED_TIME, elapsedTime);
                            progressBar.setMax(savedDuration);
                            seekBar.setMax(savedDuration);
                            String duration = calculateTime(savedDuration);
                            nowPlayingDurationExpanded.setText(duration);
                            progressBar.setProgress(savedElapsed);
                            seekBar.setProgress(savedElapsed);
                            String elapsed = calculateTime(savedElapsed);
                            nowPlayingElapsedExpanded.setText(elapsed);
                        }
                        mediaControllerCompat.getTransportControls().sendCustomAction(ACTIVITY_RESTORE, null);
                        mediaControllerCompat.getTransportControls().sendCustomAction(SET_UP_NEXT, null);
                        setBottomSheetQueue();
                    }
                }

                @Override
                public void onConnectionSuspended()
                {
                    // The Service has crashed. Disable transport controls until it automatically reconnects
                }

                @Override
                public void onConnectionFailed()
                {
                    // The Service has refused our connection
                }

            };

    private final MediaBrowserCompat.SubscriptionCallback subscriptionCallbacks =
            new MediaBrowserCompat.SubscriptionCallback()
            {
                @Override
                public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaBrowserCompat.MediaItem> children)
                {
                    super.onChildrenLoaded(parentId, children);
                    if(children == null || children.isEmpty())
                        return;
                    MediaBrowserCompat.MediaItem firstItem = children.get(0);
                    // Play the first item?
                    // Probably should check firstItem.isPlayable()
                }
            };

    private Runnable updateTime = new Runnable()
    {
        @Override
        public void run()
        {
            if(currentState == PlaybackStateCompat.STATE_PLAYING)
            {
                mediaControllerCompat.sendCommand(GET_CURRENT_POSITION, null, new ResultReceiver(new Handler())
                {
                    @Override
                    protected void onReceiveResult(int resultCode, Bundle resultData)
                    {
                        super.onReceiveResult(resultCode, resultData);
                        currentElapsed = resultData.getInt("currentPosition");
                        progressBar.setProgress(currentElapsed);
                        seekBar.setProgress(currentElapsed);
                        String elapsedTime = calculateTime(currentElapsed);
                        nowPlayingElapsedExpanded.setText(elapsedTime);
                    }
                });
                timeHandler.postDelayed(this, 1000);
            }
        }
    };

    private final ResultReceiver resultReceiver =
            new ResultReceiver(new Handler())
            {
                @Override
                protected void onReceiveResult(int resultCode, Bundle resultData)
                {
                    super.onReceiveResult(resultCode, resultData);
                    switch (resultCode)
                    {
                        /*case RECEIVE_CURRENT_POSITION:
                            currentElapsed = resultData.getInt("currentPosition");
                            progressBar.setProgress(currentElapsed);
                            seekBar.setProgress(currentElapsed);
                            String elapsedTime = calculateTime(currentElapsed);
                            nowPlayingElapsedExpanded.setText(elapsedTime);
                            break;*/

                        case RECEIVE_ARTIST_ALBUM: artist_album = resultData.getParcelable("currentArtistAlbum");
                            GoToDialog goToDialog = new GoToDialog(context, artist_album);
                            goToDialog.OpenDialog();
                            break;

                        case RECEIVE_QUEUE_POSITION:
                            savedPosition = resultData.getInt("queuePosition");
                            savedShuffle = shuffle;
                            savedRepeat = repeat;
                            savedPlayState = currentState;
                            savedElapsed = resultData.getInt("currentElapsed");
                            savedDuration = currentDuration;
                            savedNowPlayingFrom = nowPlayingFrom;
                            savedFrom = resultData.getInt("currentFrom");
                            SavedDetails details = new SavedDetails(savedPosition, savedShuffle, savedRepeat, savedPlayState, savedElapsed, savedDuration, savedNowPlayingFrom, savedFrom);
                            if(savedState.isEmpty())
                                stateViewModel.insert(details);
                            else
                            {
                                details.setId(savedState.get(0).getId());
                                stateViewModel.update(details);
                            }
                            mediaControllerCompat.getTransportControls().sendCustomAction(SAVE_QUEUE, null);
                            break;

                        case RECEIVE_PLAYBACKSTATE:
                            int state = resultData.getInt("currentState");
                            break;
                    }
                }
            };

    private final MediaControllerCompat.Callback controllerCallbacks =
            new MediaControllerCompat.Callback()
            {

                @Override
                public void onMetadataChanged(MediaMetadataCompat metadata)
                {
                    super.onMetadataChanged(metadata);

                    Uri uri = Uri.parse(metadata.getString(MediaMetadataCompat.METADATA_KEY_ART_URI));
                    Glide.with(context).load(uri).fallback(R.drawable.noalbumart).error(R.drawable.noalbumart).override(400).into(nowPlayingArt);

                    if(motionLayout.getCurrentState() == R.id.end && currentState == PlaybackStateCompat.STATE_PLAYING)
                        nowPlayingArtHolder.setCardElevation(30);
                    nowPlayingName.setText(metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
                    nowPlayingArtist.setText(metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));

                    nowPlayingNameExpanded.setText(metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
                    String artist = metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST);
                    String album = metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM);
                    String expanded = artist + " - " + album;
                    nowPlayingArtistAlbumExpanded.setText(expanded);

                    if (currentState == PlaybackStateCompat.STATE_PLAYING)
                    {
                        nowPlayingButton.setBackground(context.getResources().getDrawable(R.drawable.ic_pause));
                        nowPlayingButtonExpanded.setBackground(context.getResources().getDrawable(R.drawable.ic_pause));
                    }

                    else
                    {
                        nowPlayingButton.setBackground(context.getResources().getDrawable(R.drawable.ic_play));
                        nowPlayingButtonExpanded.setBackground(context.getResources().getDrawable(R.drawable.ic_play));
                    }

                    setShuffle(shuffle);
                    setRepeat(repeat);

                    currentDuration = (int) metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION);
                    progressBar.setMax(currentDuration);
                    seekBar.setMax(currentDuration);
                    String d = calculateTime(currentDuration);
                    nowPlayingDurationExpanded.setText(d);
                }

                @Override
                public void onRepeatModeChanged(int repeatMode)
                {
                    super.onRepeatModeChanged(repeatMode);
                    repeat = repeatMode;
                    setRepeat(repeatMode);
                }

                @Override
                public void onShuffleModeChanged(int shuffleMode)
                {
                    super.onShuffleModeChanged(shuffleMode);
                    shuffle = shuffleMode;
                    setShuffle(shuffleMode);
                }

                @Override
                public void onPlaybackStateChanged(PlaybackStateCompat state)
                {
                    super.onPlaybackStateChanged(state);
                    if(state == null)
                        return;
                    switch (state.getState())
                    {
                        case PlaybackStateCompat.STATE_PLAYING:
                            currentState = PlaybackStateCompat.STATE_PLAYING;
                            timeHandler.post(updateTime);
                            //nowPlayingNameExpanded.setSelected(true);
                            //nowPlayingArtistAlbumExpanded.setSelected(true);
                            nowPlayingButton.setBackground(context.getDrawable(R.drawable.ic_pause));
                            nowPlayingForward.setVisibility(View.VISIBLE);
                            nowPlayingButtonExpanded.setBackground(context.getDrawable(R.drawable.ic_pause));
                            if(motionLayout.getCurrentState() == R.id.end)
                            {
                                nowPlayingArtHolder.animate().scaleX(1.35f);
                                nowPlayingArtHolder.animate().scaleY(1.3f);
                                nowPlayingArtHolder.setCardElevation(30f);
                            }
                            break;

                        case PlaybackStateCompat.STATE_PAUSED:
                            currentState = PlaybackStateCompat.STATE_PAUSED;
                            //nowPlayingNameExpanded.setSelected(false);
                            //nowPlayingArtistAlbumExpanded.setSelected(false);
                            nowPlayingButton.setBackground(context.getDrawable(R.drawable.ic_play));
                            nowPlayingButtonExpanded.setBackground(context.getDrawable(R.drawable.ic_play));
                            if(motionLayout.getCurrentState() == R.id.end)
                            {
                                nowPlayingArtHolder.animate().scaleX(1f);
                                nowPlayingArtHolder.animate().scaleY(1f);
                                nowPlayingArtHolder.setCardElevation(0f);
                            }
                            break;
                    }
                }
            };

    @Override
    public void QueueListItemClick(int position)
    {
        Bundle click = new Bundle();
        click.putInt("clickedIndex", position);
        mediaControllerCompat.getTransportControls().sendCustomAction(QUEUE_CLICK, click);
        mediaControllerCompat.getTransportControls().playFromMediaId(queueDisplay.get(position).getId(), null);
        mediaControllerCompat.getTransportControls().sendCustomAction(SET_UP_NEXT, null);
    }

    private String calculateTime(int time)
    {
        String calculated;
        int minutes = time / 1000 / 60;
        int seconds = time/ 1000 % 60;
        if(seconds > 9)
            calculated = String.format(Locale.US, "%d:%d", minutes, seconds);
        else
            calculated = String.format(Locale.US, "%d:%d%d", minutes, 0, seconds);
        return calculated;
    }

    public MediaControllerCompat getMediaController() { return mediaControllerCompat; }

    public void CollapseBottomSheet() { motionLayout.transitionToState(R.id.start); }

    public void setBottomSheetQueue()
    {
        queueAdapter = new QueueAdapter(this);
        recyclerView.setAdapter(queueAdapter);
        itemTouchHelper.attachToRecyclerView(recyclerView);
        mediaControllerCompat.getTransportControls().sendCustomAction(INITIALIZE_QUEUE_CHANGE, null);
        nowPlayingFromExpanded.setText(nowPlayingFrom);
    }

    private void setRepeat(int state)
    {
        switch(state)
        {
            case PlaybackStateCompat.REPEAT_MODE_ALL:
                repeatBackground.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.colorAccent)));
                repeatExpanded.setBackground(context.getDrawable(R.drawable.ic_repeat));
                repeatExpanded.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.white)));
                break;

            case PlaybackStateCompat.REPEAT_MODE_ONE:
                repeatBackground.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.colorAccent)));
                repeatExpanded.setBackground(context.getDrawable(R.drawable.ic_repeat_one));
                repeatExpanded.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.white)));
                break;

            case PlaybackStateCompat.REPEAT_MODE_NONE:
                repeatBackground.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.darkGray)));
                repeatExpanded.setBackground(context.getDrawable(R.drawable.ic_repeat));
                repeatExpanded.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.colorAccent)));
                break;
        }
    }

    private void setShuffle(int state)
    {
        switch(state)
        {
            case PlaybackStateCompat.SHUFFLE_MODE_ALL:
                shuffleBackground.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.colorAccent)));
                shuffleExpanded.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.white)));
                break;

            case PlaybackStateCompat.SHUFFLE_MODE_NONE:
                shuffleBackground.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.darkGray)));
                shuffleExpanded.setBackgroundTintList(ColorStateList.valueOf(context.getColor(R.color.colorAccent)));
                break;
        }
    }

    public void addNext(Songs song)
    {
        Bundle next = new Bundle();
        next.putParcelable("ADD_SONG", song);
        next.putInt("QUEUE_POSITION", QUEUE_NEXT);
        mediaControllerCompat.getTransportControls().sendCustomAction(ADD_SONG, next);
        Toast.makeText(context, "ADDED NEXT", Toast.LENGTH_SHORT).show();
    }

    public void addNext(Albums album)
    {
        this.album = album;
        content = GET_ALBUM_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(BROWSER_MEDIA_LOADER, null, addNextCallbacks);
    }

    public void addNext(Artists artist)
    {
        this.artist = artist;
        content = GET_ARTIST_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(BROWSER_MEDIA_LOADER, null, addNextCallbacks);
    }

    public void addNext(Playlists playlist)
    {
        this.playlist = playlist;
        content = GET_PLAYLIST_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(BROWSER_MEDIA_LOADER, null, addNextCallbacks);
    }

    public void addLast(Songs song)
    {
        Bundle end = new Bundle();
        end.putParcelable("ADD_SONG", song);
        end.putInt("QUEUE_POSITION", QUEUE_END);
        mediaControllerCompat.getTransportControls().sendCustomAction(ADD_SONG, end);
        Toast.makeText(context, "ADDED LAST", Toast.LENGTH_SHORT).show();
    }

    public void addLast(Albums album)
    {
        this.album = album;
        content = GET_ALBUM_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(BROWSER_MEDIA_LOADER, null, addLastCallbacks);
    }

    public void addLast(Artists artist)
    {
        this.artist = artist;
        content = GET_ARTIST_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(BROWSER_MEDIA_LOADER, null, addLastCallbacks);
    }

    public void addLast(Playlists playlist)
    {
        this.playlist = playlist;
        content = GET_PLAYLIST_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(BROWSER_MEDIA_LOADER, null, addLastCallbacks);
    }

    public void addToPlaylist(Playlists playlist, Songs song)
    {
        //GET SONGS CURRENTLY IN PLAYLIST AND ADD NEW SONG
        this.playlist = playlist;
        this.song = song;
        content = GET_PLAYLIST_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(PLAYLIST_MEDIA_LOADER, null, addToPlaylistCallbacks);
    }

    public void addToPlaylist(Playlists playlist, Albums album)
    {
        this.album = album;
        this.playlist = playlist;
        content = GET_ALBUM_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(BROWSER_MEDIA_LOADER, null, addToPlaylistCallbacks);
    }

    public void addToPlaylist(Playlists playlist, Artists artist)
    {
        this.artist = artist;
        this.playlist = playlist;
        content = GET_ARTIST_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(BROWSER_MEDIA_LOADER, null, addToPlaylistCallbacks);
    }

    public void addToPlaylist(Playlists playlist, Playlists playlistToAdd)
    {
        this.playlist = playlist;
        this.playlistToAdd = playlistToAdd;
        content = GET_PLAYLIST_SONGS;
        LoaderManager.getInstance((MainActivity) context).initLoader(BROWSER_MEDIA_LOADER, null, addToPlaylistCallbacks);
    }

    private LoaderManager.LoaderCallbacks<ArrayList> addNextCallbacks = new LoaderManager.LoaderCallbacks<ArrayList>()
    {
        @NonNull
        @Override
        public Loader<ArrayList> onCreateLoader(int id, @Nullable Bundle args)
        {
            switch(content)
            {
                case GET_ALBUM_SONGS: return new GetMedia(context, content, -1, album);
                case GET_ARTIST_SONGS: return new GetMedia(context, content, -1, artist);
                case GET_PLAYLIST_SONGS: return new GetMedia(context, content, -1, playlist);
                default: return new GetMedia(context, -1, -1);
            }
        }

        @Override
        public void onLoadFinished(@NonNull Loader<ArrayList> loader, ArrayList data)
        {
            switch(content)
            {
                case GET_ALBUM_SONGS:
                    albumSongs = data;
                    if(!albumSongs.isEmpty())
                    {
                        if(albumSongs.size() == 1)
                            addNext(albumSongs.get(0));
                        else
                        {
                            addToQueue.clear();
                            addToQueue = albumSongs;
                            Bundle next = new Bundle();
                            next.putInt("QUEUE_POSITION", QUEUE_NEXT);
                            mediaControllerCompat.getTransportControls().sendCustomAction(ADD_ALBUM_ARTIST_PLAYLIST, next);
                        }
                        Toast.makeText(context, "ADDED NEXT", Toast.LENGTH_SHORT).show();
                    }
                    break;

                case GET_ARTIST_SONGS:
                    artistSongs = data;
                    if(!artistSongs.isEmpty())
                    {
                        if(artistSongs.size() == 1)
                            addNext(artistSongs.get(0));
                        else
                        {
                            addToQueue.clear();
                            addToQueue = artistSongs;
                            Bundle next = new Bundle();
                            next.putInt("QUEUE_POSITION", QUEUE_NEXT);
                            mediaControllerCompat.getTransportControls().sendCustomAction(ADD_ALBUM_ARTIST_PLAYLIST, next);
                        }
                        Toast.makeText(context, "ADDED NEXT", Toast.LENGTH_SHORT).show();
                    }
                    break;

                case GET_PLAYLIST_SONGS:
                    playlistSongs = data;
                    if(!playlistSongs.isEmpty())
                    {
                        if(playlistSongs.size() == 1)
                            addNext(playlistSongs.get(0));
                        else
                        {
                            addToQueue.clear();
                            addToQueue = playlistSongs;
                            Bundle next = new Bundle();
                            next.putInt("QUEUE_POSITION", QUEUE_NEXT);
                            mediaControllerCompat.getTransportControls().sendCustomAction(ADD_ALBUM_ARTIST_PLAYLIST, next);
                        }
                    }
                    break;
            }
        }

        @Override
        public void onLoaderReset(@NonNull Loader<ArrayList> loader) { }
    };

    private LoaderManager.LoaderCallbacks<ArrayList> addLastCallbacks = new LoaderManager.LoaderCallbacks<ArrayList>()
    {
        @NonNull
        @Override
        public Loader<ArrayList> onCreateLoader(int id, @Nullable Bundle args)
        {
            switch(content)
            {
                case GET_ALBUM_SONGS: return new GetMedia(context, content, -1, album);
                case GET_ARTIST_SONGS: return new GetMedia(context, content, -1, artist);
                case GET_PLAYLIST_SONGS: return new GetMedia(context, content, -1, playlist);
                default: return new GetMedia(context, -1, -1);
            }
        }

        @Override
        public void onLoadFinished(@NonNull Loader<ArrayList> loader, ArrayList data)
        {
            switch(content)
            {
                case GET_ALBUM_SONGS:
                    albumSongs = data;
                    if(!albumSongs.isEmpty())
                    {
                        if(albumSongs.size() == 1)
                            addLast(albumSongs.get(0));
                        else
                        {
                            addToQueue.clear();
                            addToQueue = albumSongs;
                            Bundle end = new Bundle();
                            end.putInt("QUEUE_POSITION", QUEUE_END);
                            mediaControllerCompat.getTransportControls().sendCustomAction(ADD_ALBUM_ARTIST_PLAYLIST, end);
                            Toast.makeText(context, "ADDED LAST", Toast.LENGTH_SHORT).show();
                        }
                    }
                    break;

                case GET_ARTIST_SONGS:
                    artistSongs = data;
                    if(!artistSongs.isEmpty())
                    {
                        if(artistSongs.size() == 1)
                            addLast(artistSongs.get(0));
                        else
                        {
                            addToQueue.clear();
                            addToQueue = artistSongs;
                            Bundle end = new Bundle();
                            end.putInt("QUEUE_POSITION", QUEUE_END);
                            mediaControllerCompat.getTransportControls().sendCustomAction(ADD_ALBUM_ARTIST_PLAYLIST, end);
                            Toast.makeText(context, "ADDED LAST", Toast.LENGTH_SHORT).show();
                        }
                    }
                    break;

                case GET_PLAYLIST_SONGS:
                    playlistSongs = data;
                    if(!playlistSongs.isEmpty())
                    {
                        if(playlistSongs.size() == 1)
                            addLast(playlistSongs.get(0));
                        else
                        {
                            addToQueue.clear();
                            addToQueue = playlistSongs;
                            Bundle end = new Bundle();
                            end.putInt("QUEUE_POSITION", QUEUE_END);
                            mediaControllerCompat.getTransportControls().sendCustomAction(ADD_ALBUM_ARTIST_PLAYLIST, end);
                            Toast.makeText(context, "ADDED LAST", Toast.LENGTH_SHORT).show();
                        }
                    }
                    break;
            }
        }

        @Override
        public void onLoaderReset(@NonNull Loader<ArrayList> loader) { }
    };

    private LoaderManager.LoaderCallbacks<ArrayList> addToPlaylistCallbacks = new LoaderManager.LoaderCallbacks<ArrayList>()
    {
        @NonNull
        @Override
        public Loader<ArrayList> onCreateLoader(int id, @Nullable Bundle args)
        {
            switch(content)
            {
                case GET_ALBUM_SONGS: return new GetMedia(context, content, -1, album);
                case GET_ARTIST_SONGS: return new GetMedia(context, content, -1, artist);
                case GET_PLAYLIST_SONGS: return new GetMedia(context, content, -1, playlistToAdd);
                default: return new GetMedia(context, -1, -1);
            }
        }

        @Override
        public void onLoadFinished(@NonNull Loader<ArrayList> loader, ArrayList data)
        {
            switch(content)
            {
                case GET_ALBUM_SONGS:
                    albumSongs = data;
                    for (int i = 0; i < albumSongs.size(); i++)
                    {
                        addToPlaylist(playlist, albumSongs.get(i));
                    }
                    break;

                case GET_ARTIST_SONGS:
                    artistSongs = data;
                    for (int i = 0; i < artistSongs.size(); i++)
                    {
                        addToPlaylist(playlist, artistSongs.get(i));
                    }
                    break;

                case GET_PLAYLIST_SONGS:
                    playlistSongs = data;
                    if(loader.getId() == PLAYLIST_MEDIA_LOADER)
                    {
                        playlistSongs.add(song);

                        long playlistID = Long.parseLong(playlist.getId());
                        ContentResolver contentResolver = context.getContentResolver();
                        Uri playlistSongsUri = MediaStore.Audio.Playlists.Members.getContentUri("external", playlistID);

                        //DELETE SONGS IN PLAYLIST MEMBER TABLE
                        contentResolver.delete(playlistSongsUri, null, null);

                        //RECREATE PLAYLIST MEMBER TABLE WITH NEW SONG AND INSERT
                        ContentValues[] values = new ContentValues[playlistSongs.size()];

                        for(int i = 0; i < playlistSongs.size(); i++)
                        {
                            values[i] = new ContentValues();
                            values[i].put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, Long.valueOf(i));
                            values[i].put(MediaStore.Audio.Playlists.Members.AUDIO_ID, Long.parseLong(playlistSongs.get(i).getId()));
                            values[i].put(MediaStore.Audio.Playlists.Members.PLAYLIST_ID, playlistID);
                        }

                        contentResolver.bulkInsert(playlistSongsUri, values);

                        //Log.i(TAG, song.getTitle().toUpperCase() + " ADDED TO PLAYLIST " + playlist.getPlaylist().toUpperCase());
                    }

                    else
                    {
                        for (int i = 0; i < playlistSongs.size(); i++)
                        {
                            addToPlaylist(playlist, playlistSongs.get(i));
                        }
                    }
                    break;
            }
        }

        @Override
        public void onLoaderReset(@NonNull Loader<ArrayList> loader) { }
    };

    private void buildTransportControls()
    {
        ///////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////BOTTOM SHEET/////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////

        frameLayout = rootView.findViewById(R.id.fragment_container);

        recyclerView = rootView.findViewById(R.id.currentBottomSheetQueue);
        LinearLayoutManager layoutManager = new LinearLayoutManager(context);
        recyclerView.setLayoutManager(layoutManager);
        DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), layoutManager.getOrientation());
        recyclerView.addItemDecoration(dividerItemDecoration);

        motionLayout = rootView.findViewById(R.id.bottomSheetPlaying);
        motionLayout.setTransitionListener(new MotionLayout.TransitionListener() {
            @Override
            public void onTransitionStarted(MotionLayout motionLayout, int i, int i1)
            {
                if((motionLayout.getStartState() == R.id.end || motionLayout.getStartState() == R.id.start) && upNextIsShowing)
                {
                    upNextIsShowing = false;
                }

                else if(motionLayout.getEndState() == R.id.endQueue && !upNextIsShowing)
                {
                    upNextIsShowing = true;
                }
            }

            @Override
            public void onTransitionChange(MotionLayout motionLayout, int i, int i1, float v)
            {

            }

            @Override
            public void onTransitionCompleted(MotionLayout motionLayout, int i)
            {
                if(motionLayout.getCurrentState() == R.id.start)
                {
                    nowPlayingArtHolder.animate().scaleX(1f);
                    nowPlayingArtHolder.animate().scaleY(1f);
                    nowPlayingArtHolder.setCardElevation(0f);
                }

                else if(motionLayout.getCurrentState() == R.id.endQueue)
                {
                    upNextShuffle.setAlpha(0f);
                    upNextShuffleBackground.setAlpha(0f);
                    upNextRepeat.setAlpha(0f);
                    upNextRepeatBackground.setAlpha(0f);
                    nowPlayingArtHolder.animate().scaleX(1f);
                    nowPlayingArtHolder.animate().scaleY(1f);
                    nowPlayingArtHolder.setCardElevation(0f);
                }

                else
                {
                    if(shuffle == PlaybackStateCompat.SHUFFLE_MODE_ALL)
                    {
                        upNextShuffle.setAlpha(1f);
                        upNextShuffleBackground.setAlpha(1f);
                    }

                    if(repeat == PlaybackStateCompat.REPEAT_MODE_ALL || repeat == PlaybackStateCompat.REPEAT_MODE_ONE)
                    {
                        if(repeat == PlaybackStateCompat.REPEAT_MODE_ONE)
                            upNextRepeat.setImageResource(R.drawable.ic_repeat_one);
                        else
                            upNextRepeat.setImageResource(R.drawable.ic_repeat);
                        upNextRepeat.setAlpha(1f);
                        upNextRepeatBackground.setAlpha(1f);
                    }

                    if(currentState == PlaybackStateCompat.STATE_PLAYING)
                    {
                        nowPlayingArtHolder.animate().scaleX(1.35f);
                        nowPlayingArtHolder.animate().scaleY(1.3f);
                        nowPlayingArtHolder.setCardElevation(30f);
                    }

                    else
                    {
                        nowPlayingArtHolder.animate().scaleX(1f);
                        nowPlayingArtHolder.animate().scaleY(1f);
                        nowPlayingArtHolder.setCardElevation(0f);
                    }
                }
            }

            @Override
            public void onTransitionTrigger(MotionLayout motionLayout, int i, boolean b, float v)
            {

            }
        });

        nowPlayingArt = rootView.findViewById(R.id.currentArtBottomSheet);
        nowPlayingArtHolder = rootView.findViewById(R.id.currentArtBottomSheetHolder);
        nowPlayingName = rootView.findViewById(R.id.currentNameCollapsed);
        nowPlayingArtist = rootView.findViewById(R.id.currentArtistCollapsed);

        nowPlayingButton = rootView.findViewById(R.id.currentPlay);
        nowPlayingButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                if(!nowPlayingName.getText().equals("Not Playing"))
                {
                    if(currentState == PlaybackStateCompat.STATE_PLAYING)
                    {
                        mediaControllerCompat.getTransportControls().pause();
                    }

                    else
                    {
                        mediaControllerCompat.getTransportControls().play();
                    }
                }

                else
                {
                    getMediaController().getTransportControls().sendCustomAction(CLEAR, null);
                    Bundle queue = new Bundle();
                    queue.putInt("CURRENT_QUEUE_POSITION", 0);
                    queue.putInt("FROM", FROM_LIBRARY);
                    getMediaController().getTransportControls().sendCustomAction(SET_POSITION, queue);
                    getMediaController().getTransportControls().sendCustomAction(SET_QUEUE_LIBRARY, queue);
                    nowPlayingFrom = "Now playing from Library";
                    getMediaController().getTransportControls().sendCustomAction(PLAY_BUTTON_START, null);
                    getMediaController().getTransportControls().sendCustomAction(SET_UP_NEXT, null);
                    setBottomSheetQueue();
                }
            }
        });

        nowPlayingForward = rootView.findViewById(R.id.currentNext);
        nowPlayingForward.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                mediaControllerCompat.getTransportControls().skipToNext();
            }
        });

        nowPlayingNameExpanded = rootView.findViewById(R.id.currentNameBottomSheet);
        nowPlayingElapsedExpanded = rootView.findViewById(R.id.currentTimeBottomSheet);
        nowPlayingDurationExpanded = rootView.findViewById(R.id.currentDurationBottomSheet);
        nowPlayingFromExpanded = rootView.findViewById(R.id.playingFrom);

        nowPlayingArtistAlbumExpanded = rootView.findViewById(R.id.currentArtistAlbumBottomSheet);
        nowPlayingArtistAlbumExpanded.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                mediaControllerCompat.sendCommand(GET_ARTIST_ALBUM, null, resultReceiver);
            }
        });

        nowPlayingSlideDown = rootView.findViewById(R.id.slideDownBottomSheet);

        nowPlayingButtonExpanded = rootView.findViewById(R.id.currentPlayBottomSheet);
        nowPlayingButtonExpanded.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                if(!nowPlayingName.getText().equals("Not Playing"))
                {
                    if(currentState == PlaybackStateCompat.STATE_PLAYING)
                    {
                        mediaControllerCompat.getTransportControls().pause();
                        nowPlayingArtHolder.setCardElevation(0f);
                    }

                    else
                    {
                        mediaControllerCompat.getTransportControls().play();
                        nowPlayingArtHolder.setCardElevation(30f);
                    }
                }

                else
                {
                    getMediaController().getTransportControls().sendCustomAction(CLEAR, null);
                    Bundle queue = new Bundle();
                    queue.putInt("CURRENT_QUEUE_POSITION", 0);
                    queue.putInt("FROM", FROM_LIBRARY);
                    getMediaController().getTransportControls().sendCustomAction(SET_POSITION, queue);
                    getMediaController().getTransportControls().sendCustomAction(SET_QUEUE_LIBRARY, queue);
                    nowPlayingFrom = "Now playing from Library";
                    getMediaController().getTransportControls().sendCustomAction(PLAY_BUTTON_START, null);
                    getMediaController().getTransportControls().sendCustomAction(SET_UP_NEXT, null);
                    setBottomSheetQueue();
                }
            }
        });

        nowPlayingBackExpanded = rootView.findViewById(R.id.currentBackBottomSheet);
        nowPlayingBackExpanded.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                mediaControllerCompat.getTransportControls().skipToPrevious();
            }
        });

        nowPlayingForwardExpanded = rootView.findViewById(R.id.currentForwardBottomSheet);
        nowPlayingForwardExpanded.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                mediaControllerCompat.getTransportControls().skipToNext();
            }
        });

        progressBar = rootView.findViewById(R.id.collapsedSeekBar);
        seekBar = rootView.findViewById(R.id.currentSeekBottomSheet);
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
            {
                if(fromUser)
                {
                    mediaControllerCompat.getTransportControls().seekTo((long) progress);
                    seekBar.setProgress(progress);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar)
            {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar)
            {

            }
        });

        audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        volumeBar = rootView.findViewById(R.id.volumeControlSeek);
        volumeBar.setMax(audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC));
        volumeBar.setProgress(audioManager.getStreamVolume(AudioManager.STREAM_MUSIC));
        volumeBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int volume, boolean fromUser)
            {
                audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar)
            {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar)
            {

            }
        });

        shuffleExpanded = rootView.findViewById(R.id.currentBottomSheetShuffle);
        shuffleBackground = rootView.findViewById(R.id.shuffleBackground);
        shuffleExpanded.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                if(shuffle == PlaybackStateCompat.SHUFFLE_MODE_NONE)
                {
                    mediaControllerCompat.getTransportControls().setShuffleMode(PlaybackStateCompat.SHUFFLE_MODE_ALL);
                }

                else if(shuffle == PlaybackStateCompat.SHUFFLE_MODE_ALL)
                {
                    mediaControllerCompat.getTransportControls().setShuffleMode(PlaybackStateCompat.SHUFFLE_MODE_NONE);
                }

                //setBottomSheetQueue();
            }
        });

        repeatExpanded = rootView.findViewById(R.id.currentBottomSheetRepeat);
        repeatBackground = rootView.findViewById(R.id.repeatBackground);
        repeatExpanded.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                if(repeat == PlaybackStateCompat.REPEAT_MODE_NONE)
                {
                    mediaControllerCompat.getTransportControls().setRepeatMode(PlaybackStateCompat.REPEAT_MODE_ALL);
                }

                else if(repeat == PlaybackStateCompat.REPEAT_MODE_ALL)
                {
                    mediaControllerCompat.getTransportControls().setRepeatMode(PlaybackStateCompat.REPEAT_MODE_ONE);
                }

                else if(repeat == PlaybackStateCompat.REPEAT_MODE_ONE)
                {
                    mediaControllerCompat.getTransportControls().setRepeatMode(PlaybackStateCompat.REPEAT_MODE_NONE);
                }
            }
        });

        upNextQueue = rootView.findViewById(R.id.upNextButton);
        upNextBackground = rootView.findViewById(R.id.upNextBackground);
        upNextShuffle = rootView.findViewById(R.id.upNextShuffle);
        upNextShuffleBackground = rootView.findViewById(R.id.upNextShuffleBackground);
        upNextRepeat = rootView.findViewById(R.id.upNextRepeat);
        upNextRepeatBackground = rootView.findViewById(R.id.upNextRepeatBackground);

        //////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////
        /////////////////////////////////////BOTTOM SHEET/////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////
    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/App.java
package com.rookieandroid.rookiemusicplayer;

import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Handler;
import android.transition.ChangeBounds;
import android.transition.ChangeClipBounds;
import android.transition.ChangeTransform;
import android.transition.TransitionSet;
import android.widget.ImageView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;

import com.rookieandroid.rookiemusicplayer.adapters.QueueAdapter;
import com.rookieandroid.rookiemusicplayer.helpers.MediaBrowserHelperMotion;

import java.util.ArrayList;

public class App extends Application
{
    private final String TAG = App.class.getSimpleName();

    public static final String CHANNEL_1 = "channel_1";
    public static MediaBrowserHelperMotion mediaBrowserHelper;

    //CURRENTLY PLAYING
    public static Handler playingHandler = new Handler();
    public ImageView currentSong;
    public ImageView previousSong;
    public static int currentlyPlaying = 1;
    public static int currentState;
    public static int repeat;
    public static int shuffle;

    //LOADERS
    public static final int READ_STORAGE_LOADER = 0;
    public static final int PLAYLIST_MEDIA_LOADER = 1;
    public static final int BROWSER_MEDIA_LOADER = 2;
    public static final int DIALOG_MEDIA_LOADER = 3;
    public static final int ALBUM_MEDIA_LOADER = 4;
    public static final int LIBRARY_MEDIA_LOADER = 5;
    public static final int ARTIST_MEDIA_LOADER = 6;

    //GET MEDIA
    public static final int GET_ALBUM_SONGS = 1;
    public static final int GET_ARTIST_SONGS = 2;
    public static final int GET_PLAYLIST_SONGS = 3;
    public static final int GET_RECENT_SONGS = 4;
    public static final int GET_ARTIST_ALBUMS = 5;

    //LIBRARY
    public static ArrayList<Songs> songs = new ArrayList<>();
    public static ArrayList<Albums> albums = new ArrayList<>();
    public static ArrayList<AlbumsSections> albumsSections = new ArrayList<>();
    public static ArrayList<Artists> artists = new ArrayList<>();
    public static ArrayList<Playlists> playlists = new ArrayList<>();

    //QUEUE CONTENTS
    public static ArrayList<Songs> librarySongs = new ArrayList<>();
    public static ArrayList<Songs> artistSongs = new ArrayList<>();
    public static ArrayList<Songs> albumSongs = new ArrayList<>();
    public static ArrayList<Songs> playlistSongs = new ArrayList<>();
    public static ArrayList<Songs> searchSong = new ArrayList<>();
    public static ArrayList<Songs> addToQueue = new ArrayList<>();

    //SAVING AND RESTORING UI STATE
    public static ArrayList<Songs> savedSongs;
    public static ArrayList<SavedStateDetails> savedState;

    //UP NEXT
    public static ArrayList<Songs> queueDisplay = new ArrayList<>();
    public static QueueAdapter queueAdapter;
    public static String nowPlayingFrom;
    public static ArrayList<Songs> deleteFromQueue = new ArrayList<>();

    //FAST SCROLLING
    public static ArrayList<Integer> sectionsSongs = new ArrayList<>();
    public static ArrayList<String> lettersSongs = new ArrayList<>();
    public static ArrayList<Integer> sectionsArtists = new ArrayList<>();
    public static ArrayList<String> lettersArtists = new ArrayList<>();
    public static ArrayList<Integer> sectionsAlbums = new ArrayList<>();
    public static ArrayList<String> lettersAlbums = new ArrayList<>();

    //CONSTANTS
    public static final int SKIP_NEXT = 1;
    public static final int SKIP_PREVIOUS = 2;
    public static final int QUEUE_NEXT = 3;
    public static final int QUEUE_END = 4;
    public static final int FROM_LIBRARY = 5;
    public static final int FROM_ARTIST = 6;
    public static final int FROM_ALBUM = 7;
    public static final int FROM_SEARCH = 8;
    public static final int FROM_PLAYLIST = 9;
    public static final int TO_ARTIST = 10;
    public static final int TO_ALBUM = 11;
    public static final int ARTIST_DETAIL = 12;
    public static final int SEARCH = 13;
    public static final int RECENTLY_ADDED = 14;

    //COMMANDS AND RESULT CODES
    public static final String GET_CURRENT_POSITION = "1";
    public static final int RECEIVE_CURRENT_POSITION = 1;
    public static final String GET_ARTIST_ALBUM = "2";
    public static final int RECEIVE_ARTIST_ALBUM = 2;
    public static final String GET_QUEUE_POSITION = "3";
    public static final int RECEIVE_QUEUE_POSITION = 3;
    public static final String GET_PLAYBACKSTATE = "4";
    public static final int RECEIVE_PLAYBACKSTATE = 4;

    //CUSTOM ACTIONS
    public static final String ADD_SONG = "1";
    public static final String ADD_ALBUM_ARTIST_PLAYLIST = "2";
    public static final String SET_POSITION = "3";
    public static final String CLEAR = "4";
    public static final String ACTIVITY_RESTORE = "5";
    public static final String SET_QUEUE_LIBRARY = "6";
    public static final String SET_QUEUE_LIBRARY_SONGS = "7";
    public static final String SET_QUEUE_ARTIST = "8";
    public static final String SET_QUEUE_ALBUM = "9";
    public static final String SET_QUEUE_SEARCH = "10";
    public static final String SET_QUEUE_PLAYLIST = "11";
    public static final String SET_UP_NEXT = "12";
    public static final String QUEUE_CLICK = "13";
    public static final String SHUFFLE_QUEUE = "14";
    public static final String SAVE_QUEUE = "15";
    public static final String RESTORE_SAVED_QUEUE = "16";
    public static final String INITIALIZE_QUEUE_CHANGE = "17";
    public static final String SET_ELAPSED_TIME = "18";
    public static final String SET_FROM = "19";
    public static final String PLAY_BUTTON_START = "20";
    public static final String MEDIA_DELETED = "21";

    @Override
    public void onCreate()
    {
        super.onCreate();
        createNotificationChannels();
    }

    private void createNotificationChannels()
    {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
        {
            NotificationChannel channel_1 = new NotificationChannel(CHANNEL_1, "Now Playing", NotificationManager.IMPORTANCE_LOW);
            NotificationManager notificationManager = getSystemService(NotificationManager.class);
            if(notificationManager != null)
                notificationManager.createNotificationChannel(channel_1);
        }
    }

    public static int calculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight)
    {
        final int width = options.outWidth;
        final int height = options.outHeight;
        int sampleSize = 1;

        if (height > reqHeight || width > reqWidth)
        {
            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / sampleSize) >= reqHeight && (halfWidth / sampleSize) >= reqWidth)
                sampleSize *= 2;
        }

        return sampleSize;
    }

    public static class DetailsTransition extends TransitionSet
    {
        public DetailsTransition()
        {
            setOrdering(ORDERING_TOGETHER);
            addTransition(new ChangeBounds())
                    .addTransition(new ChangeTransform())
                    .addTransition(new ChangeClipBounds());
                    //.addTransition(new ChangeImageTransform());
        }
    }

   public static ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback()
   {
        @Override
        public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder)
        {
            int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            return makeMovementFlags(dragFlags, swipeFlags);
        }

        @Override
        public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target)
        {
            return queueAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        }

        @Override
        public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction)
        {
            queueAdapter.onItemDismiss(viewHolder.getAdapterPosition());
        }

        @Override
        public boolean isLongPressDragEnabled()
        {
            return super.isLongPressDragEnabled();
        }

        @Override
        public boolean isItemViewSwipeEnabled()
        {
            return super.isItemViewSwipeEnabled();
        }
    });
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/adapters/QueueAdapter.java
package com.rookieandroid.rookiemusicplayer.adapters;

import android.content.Context;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.rookieandroid.rookiemusicplayer.R;

import static com.rookieandroid.rookiemusicplayer.App.queueDisplay;

public class QueueAdapter extends RecyclerView.Adapter<QueueAdapter.QueueViewHolder>
{
    private static String TAG = QueueAdapter.class.getSimpleName();
    private QueueAdapter.ListItemClickListener listener;
    private QueueChange queueChange;
    private Context context;

    public interface ListItemClickListener
    {
        void QueueListItemClick(int position);
    }

    //Keeps the queue updated on drags and swipes
    public interface QueueChange
    {
        void updateQueueOrder(int from, int to);
        void updateQueueDismiss(int index);
    }

    public QueueAdapter(QueueAdapter.ListItemClickListener listener)
    {
        this.listener = listener;
        setHasStableIds(true);
    }

    public void initializeQueueChange(QueueChange queueChange) { this.queueChange = queueChange; }

    @NonNull
    @Override
    public QueueAdapter.QueueViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
    {
        context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.queue_list, parent, false);
        QueueAdapter.QueueViewHolder viewHolder = new QueueAdapter.QueueViewHolder(view);
        return viewHolder;
    }

    @Override
    public long getItemId(int position) { return Integer.parseInt(queueDisplay.get(position).getId()); }

    @Override
    public int getItemViewType(int position) { return Integer.parseInt(queueDisplay.get(position).getId()); }

    @Override
    public void onBindViewHolder(@NonNull QueueAdapter.QueueViewHolder holder, int position) { holder.bind(position); }

    @Override
    public int getItemCount() { return queueDisplay.size(); }

    public boolean onItemMove(int fromPosition, int toPosition)
    {
        queueChange.updateQueueOrder(fromPosition, toPosition);
        return true;
    }

    public void onItemDismiss(int position) { queueChange.updateQueueDismiss(position); }

    class QueueViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
    {

        private ImageView albumArt;
        private TextView songName;
        private TextView songArtist;
        private ImageView order;

        public QueueViewHolder(@NonNull View itemView)
        {
            super(itemView);
            albumArt = itemView.findViewById(R.id.songArt);
            songName = itemView.findViewById(R.id.songName);
            songArtist = itemView.findViewById(R.id.songArtist);
            order = itemView.findViewById(R.id.order);
            itemView.setOnClickListener(this);
        }

        public void bind(int position)
        {
            Uri uri = Uri.parse(queueDisplay.get(position).getArt());
            Glide.with(context).load(uri).placeholder(R.drawable.noalbumart).fallback(R.drawable.noalbumart).error(R.drawable.noalbumart).into(albumArt);

            songName.setText(queueDisplay.get(position).getTitle());
            songArtist.setText(queueDisplay.get(position).getArtist());
        }

        @Override
        public void onClick(View view)
        {
            int clickedPosition = getAdapterPosition();
            listener.QueueListItemClick(clickedPosition);
        }

    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/adapters/PlaylistDialogAdapter.java
package com.rookieandroid.rookiemusicplayer.adapters;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import com.rookieandroid.rookiemusicplayer.Playlists;
import com.rookieandroid.rookiemusicplayer.R;

import java.util.ArrayList;

public class PlaylistDialogAdapter extends RecyclerView.Adapter<PlaylistDialogAdapter.PlaylistDialogViewHolder>
{
    private ArrayList<Playlists> playlists;
    private ListItemClickListener listener;
    private Context context;
    public interface ListItemClickListener
    {
        void onListItemClick(Playlists playlist);
    }

    public PlaylistDialogAdapter(ArrayList<Playlists> playlists, ListItemClickListener listener)
    {
        this.playlists = playlists;
        this.listener = listener;
    }

    @NonNull
    @Override
    public PlaylistDialogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
    {
        context = parent.getContext();
        LayoutInflater inflater = LayoutInflater.from(context);
        View view = inflater.inflate(R.layout.playlists_list, parent, false);
        PlaylistDialogViewHolder viewHolder = new PlaylistDialogViewHolder(view);
        return viewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull PlaylistDialogViewHolder holder, int position)
    {
        holder.bind(position);
    }



    @Override
    public int getItemCount() { return playlists.size(); }

    class PlaylistDialogViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
    {
        TextView playlistName;

        public PlaylistDialogViewHolder(@NonNull View itemView)
        {
            super(itemView);
            playlistName = itemView.findViewById(R.id.playlist);
            itemView.setOnClickListener(this);
        }

        public void bind(int position)
        {
            playlistName.setText(playlists.get(position).getPlaylist());
        }

        @Override
        public void onClick(View view)
        {
            int clickedPosition = getAdapterPosition();
            listener.onListItemClick(playlists.get(clickedPosition));
        }

    }
}
<file_sep>/README.md
# RookMusicPlayer
<img src="screenshots/rookscreenshot1.png" width="275"> <img src="screenshots/rookscreenshot2.png" width="275"> <img src="screenshots/rookscreenshot3.png" width="275">
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/architecture/StateDatabase.java
package com.rookieandroid.rookiemusicplayer.architecture;

import android.content.Context;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteDatabase;

import com.rookieandroid.rookiemusicplayer.Songs;

@Database(entities = {Songs.class, SavedDetails.class}, version = 1, exportSchema = false)
public abstract class StateDatabase extends RoomDatabase
{
    private static StateDatabase instance;

    public abstract SavedQueueDao savedQueueDao();
    public abstract SavedDetailsDao savedDetailsDao();

    public static synchronized StateDatabase getInstance(Context context)
    {
        if(instance == null)
        {
            instance = Room.databaseBuilder(context.getApplicationContext(), StateDatabase.class, "state_database")
                    .fallbackToDestructiveMigration().addCallback(roomCallBack).build();
        }

        return instance;
    }

    private static RoomDatabase.Callback roomCallBack = new RoomDatabase.Callback()
    {
        @Override
        public void onCreate(@NonNull SupportSQLiteDatabase db)
        {
            super.onCreate(db);
        }

        @Override
        public void onOpen(@NonNull SupportSQLiteDatabase db)
        {
            super.onOpen(db);
        }
    };
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/helpers/GoToDialog.java
package com.rookieandroid.rookiemusicplayer.helpers;

import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.InsetDrawable;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.appcompat.app.AlertDialog;

import com.rookieandroid.rookiemusicplayer.R;
import com.rookieandroid.rookiemusicplayer.Songs;

import static com.rookieandroid.rookiemusicplayer.App.TO_ALBUM;
import static com.rookieandroid.rookiemusicplayer.App.TO_ARTIST;

public class GoToDialog extends AlertDialog
{
    private Context context;
    private Songs artist_album;
    private TextView artistName;
    private TextView albumName;
    private RelativeLayout goArtist;
    private RelativeLayout goAlbum;
    private GoTo goTo;

    public interface GoTo
    {
        void goTo(Songs artist_album, int to);
    }

    public GoToDialog(Context context, Songs artist_album)
    {
        super(context);
        this.context = context;
        this.artist_album = artist_album;
        goTo = (GoTo) context;
    }

    public void OpenDialog()
    {
        final AlertDialog alertDialog = new AlertDialog.Builder(context).create();
        final View alertLayout = getLayoutInflater().inflate(R.layout.dialog_goto,null);

        goArtist = alertLayout.findViewById(R.id.goToArtist);
        artistName = alertLayout.findViewById(R.id.artist);
        artistName.setText(artist_album.getArtist());

        goAlbum = alertLayout.findViewById(R.id.goToAlbum);
        albumName = alertLayout.findViewById(R.id.album);
        albumName.setText(artist_album.getAlbum());

        setClickListeners(alertDialog);

        alertDialog.setView(alertLayout);
        new Dialog(context);
        alertDialog.show();
        ColorDrawable back = new ColorDrawable(Color.TRANSPARENT);
        InsetDrawable inset = new InsetDrawable(back, 20);
        alertDialog.getWindow().setBackgroundDrawable(inset);
        alertDialog.getWindow().setElevation(20.0f);
    }

    private void setClickListeners(AlertDialog alertDialog)
    {
        goArtist.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                goTo.goTo(artist_album, TO_ARTIST);
                alertDialog.dismiss();
            }
        });

        goAlbum.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                goTo.goTo(artist_album, TO_ALBUM);
                alertDialog.dismiss();
            }
        });
    }

}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/helpers/SectionIndexFixer.java
package com.rookieandroid.rookiemusicplayer.helpers;

import java.util.ArrayList;

public class SectionIndexFixer
{
    public SectionIndexFixer(){}

    public void fixIndex(ArrayList<String> letters, ArrayList<Integer> sections)
    {
        if(!letters.contains("#"))
        {
            letters.add(0, "!");
            sections.add(0, -1);
        }
        if(!letters.contains("A"))
        {
            letters.add(1, "!");
            sections.add(1, -1);
        }
        if(!letters.contains("B"))
        {
            letters.add(2, "!");
            sections.add(2, -1);
        }
        if(!letters.contains("C"))
        {
            letters.add(3, "!");
            sections.add(3, -1);
        }
        if(!letters.contains("D"))
        {
            letters.add(4, "!");
            sections.add(4, -1);
        }
        if(!letters.contains("E"))
        {
            letters.add(5, "!");
            sections.add(5, -1);
        }
        if(!letters.contains("F"))
        {
            letters.add(6, "!");
            sections.add(6, -1);
        }
        if(!letters.contains("G"))
        {
            letters.add(7, "!");
            sections.add(7, -1);
        }
        if(!letters.contains("H"))
        {
            letters.add(8, "!");
            sections.add(8, -1);
        }
        if(!letters.contains("I"))
        {
            letters.add(9, "!");
            sections.add(9, -1);
        }
        if(!letters.contains("J"))
        {
            letters.add(10, "!");
            sections.add(10, -1);
        }
        if(!letters.contains("K"))
        {
            letters.add(11, "!");
            sections.add(11, -1);
        }
        if(!letters.contains("L"))
        {
            letters.add(12, "!");
            sections.add(12, -1);
        }
        if(!letters.contains("M"))
        {
            letters.add(13, "!");
            sections.add(13, -1);
        }
        if(!letters.contains("N"))
        {
            letters.add(14, "!");
            sections.add(14, -1);
        }
        if(!letters.contains("O"))
        {
            letters.add(15, "!");
            sections.add(15, -1);
        }
        if(!letters.contains("P"))
        {
            letters.add(16, "!");
            sections.add(16, -1);
        }
        if(!letters.contains("Q"))
        {
            letters.add(17, "!");
            sections.add(17, -1);
        }
        if(!letters.contains("R"))
        {
            letters.add(18, "!");
            sections.add(18, -1);
        }
        if(!letters.contains("S"))
        {
            letters.add(19, "!");
            sections.add(19, -1);
        }
        if(!letters.contains("T"))
        {
            letters.add(20, "!");
            sections.add(20, -1);
        }
        if(!letters.contains("U"))
        {
            letters.add(21, "!");
            sections.add(21, -1);
        }
        if(!letters.contains("V"))
        {
            letters.add(22, "!");
            sections.add(22, -1);
        }
        if(!letters.contains("W"))
        {
            letters.add(23, "!");
            sections.add(23, -1);
        }
        if(!letters.contains("X"))
        {
            letters.add(24, "!");
            sections.add(24, -1);
        }
        if(!letters.contains("Y"))
        {
            letters.add(25, "!");
            sections.add(25, -1);
        }
        if(!letters.contains("Z"))
        {
            letters.add(26, "!");
            sections.add(26, -1);
        }
    }
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/helpers/MediaControlDialog.java
package com.rookieandroid.rookiemusicplayer.helpers;

import android.app.Dialog;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.InsetDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;
import com.rookieandroid.rookiemusicplayer.Albums;
import com.rookieandroid.rookiemusicplayer.Artists;
import com.rookieandroid.rookiemusicplayer.MainActivity;
import com.rookieandroid.rookiemusicplayer.Playlists;
import com.rookieandroid.rookiemusicplayer.R;
import com.rookieandroid.rookiemusicplayer.Songs;
import com.rookieandroid.rookiemusicplayer.adapters.AlbumDetailsAdapter;
import com.rookieandroid.rookiemusicplayer.adapters.AlbumsAdapter;
import com.rookieandroid.rookiemusicplayer.adapters.ArtistsAdapter;
import com.rookieandroid.rookiemusicplayer.adapters.PlaylistDialogAdapter;
import com.rookieandroid.rookiemusicplayer.adapters.PlaylistsAdapter;
import com.rookieandroid.rookiemusicplayer.adapters.SongsAdapter;
import com.google.android.material.textfield.TextInputEditText;

import java.util.ArrayList;

import static com.rookieandroid.rookiemusicplayer.App.DIALOG_MEDIA_LOADER;
import static com.rookieandroid.rookiemusicplayer.App.FROM_ALBUM;
import static com.rookieandroid.rookiemusicplayer.App.FROM_ARTIST;
import static com.rookieandroid.rookiemusicplayer.App.FROM_LIBRARY;
import static com.rookieandroid.rookiemusicplayer.App.FROM_PLAYLIST;
import static com.rookieandroid.rookiemusicplayer.App.GET_ALBUM_SONGS;
import static com.rookieandroid.rookiemusicplayer.App.GET_ARTIST_SONGS;
import static com.rookieandroid.rookiemusicplayer.App.mediaBrowserHelper;
import static com.rookieandroid.rookiemusicplayer.App.playlists;

public class MediaControlDialog extends AlertDialog implements PlaylistDialogAdapter.ListItemClickListener
{
    private final String TAG = MediaControlDialog.class.getSimpleName();

    private static final int SONG = 1;
    private static final int ALBUM = 2;
    private static final int ARTIST = 3;
    private static final int PLAYLIST = 4;
    private static final int PLAYLIST_SONG = 5;

    ContentResolver contentResolver;
    private int removePosition;
    private ArrayList<Songs> albumSongs;
    private ArrayList<Songs> artistSongs;

    private Songs song;
    private Songs playlistSong;
    private Albums album;
    private Artists artist;
    private Playlists playlist;
    private ArrayList<Songs> librarySongs;
    private SongsAdapter librarySongsAdapter;
    private ArrayList<Albums> libraryAlbums;
    private AlbumsAdapter libraryAlbumsAdapter;
    private AlbumDetailsAdapter albumSongsAdapter;
    private ArrayList<Artists> libraryArtists;
    private ArtistsAdapter libraryArtistsAdapter;
    private ArrayList<Playlists> currentPlaylists;
    private PlaylistsAdapter playlistsAdapter;
    private ArrayList<Songs> playlistSongs;
    private SongsAdapter playlistSongsAdapter;
    private AlertDialog alertDialog;
    private AlertDialog alertDialogPlaylist;
    private AlertDialog alertDialogNewPlaylist;
    private int media;
    private int code;
    private int ID;
    private Context context;
    private MediaControlDialog.UpdateLibrary updateLibrary;
    private MediaControlDialog.UpdatePlaylist updatePlaylist;

    //MEDIA CONTROL
    private ImageView mediaArt;
    private TextView mediaName;
    private TextView mediaArtist;
    private TextView mediaType;
    private TextView deleteText;
    private RelativeLayout delete;
    private RelativeLayout playlistAction;
    private RelativeLayout next;
    private RelativeLayout last;

    //PLAYLIST CONTROL
    private RecyclerView recyclerView;
    private Button cancelPlaylist;
    private Button createNewPlaylist;

    //NEW PLAYLIST CONTROL
    private TextInputEditText newPlaylistName;
    private TextInputEditText newPlaylistDescription;
    private Button newPlaylistCancel;
    private Button newPlaylistCreate;

    public interface UpdateLibrary
    {
        void updateSongsLibrary(Songs song);
        void updateAlbumsLibrary(Albums album, ArrayList<Songs> albumSongs, int from);
        void updateArtistsLibrary(Artists artist, ArrayList<Songs> artistSongs);
    }

    public interface UpdatePlaylist
    {
        void updatePlaylistCount();
    }

    //DIALOG FOR SONG
    public MediaControlDialog(Context context, Songs song, ArrayList<Songs> librarySongs, SongsAdapter librarySongsAdapter, MediaControlDialog.UpdateLibrary updateLibrary, int code)
    {
        super(context);
        this.song = song;
        this.librarySongs = librarySongs;
        this.librarySongsAdapter = librarySongsAdapter;
        this.context = context;
        this.updateLibrary = updateLibrary;
        this.code = code;
        media = SONG;
        contentResolver = context.getContentResolver();
    }

    //DIALOG FOR ALBUM SONG
    public MediaControlDialog(Context context, Songs song, ArrayList<Songs> librarySongs, AlbumDetailsAdapter albumSongsAdapter, MediaControlDialog.UpdateLibrary updateLibrary, int code)
    {
        super(context);
        this.song = song;
        this.librarySongs = librarySongs;
        this.albumSongsAdapter = albumSongsAdapter;
        this.context = context;
        this.updateLibrary = updateLibrary;
        this.code = code;
        media = SONG;
        contentResolver = context.getContentResolver();
    }

    //DIALOG FOR ALBUM
    public MediaControlDialog(Context context, Albums album, ArrayList<Albums> libraryAlbums, AlbumsAdapter libraryAlbumsAdapter, MediaControlDialog.UpdateLibrary updateLibrary, int code)
    {
        super(context);
        this.album = album;
        this.libraryAlbums = libraryAlbums;
        this.libraryAlbumsAdapter = libraryAlbumsAdapter;
        this.context = context;
        this.updateLibrary = updateLibrary;
        this.code = code;
        media = ALBUM;
        contentResolver = context.getContentResolver();
    }

    //DIALOG FOR ARTIST
    public MediaControlDialog(Context context, Artists artist, ArrayList<Artists> libraryArtists, ArtistsAdapter libraryArtistsAdapter, MediaControlDialog.UpdateLibrary updateLibrary, int code)
    {
        super(context);
        this.artist = artist;
        this.libraryArtists = libraryArtists;
        this.libraryArtistsAdapter = libraryArtistsAdapter;
        this.context = context;
        this.updateLibrary = updateLibrary;
        this.code = code;
        media = ARTIST;
        contentResolver = context.getContentResolver();
    }

    //DIALOG FOR PLAYLIST
    public MediaControlDialog(Context context, Playlists playlist, ArrayList<Playlists> currentPlaylists, PlaylistsAdapter playlistsAdapter, int code)
    {
        super(context);
        this.playlist = playlist;
        this.context = context;
        this.currentPlaylists = currentPlaylists;
        this.playlistsAdapter = playlistsAdapter;
        this.code = code;
        media = PLAYLIST;
        contentResolver = context.getContentResolver();
    }

    //DIALOG FOR PLAYLIST SONG
    public MediaControlDialog(Context context, Songs playlistSong, Playlists playlist, ArrayList<Songs> playlistSongs, SongsAdapter playlistSongsAdapter, MediaControlDialog.UpdatePlaylist updatePlaylist, int code)
    {
        super(context);
        this.playlistSong = playlistSong;
        this.playlist = playlist;
        this.context = context;
        this.playlistSongs = playlistSongs;
        this.playlistSongsAdapter = playlistSongsAdapter;
        this.code = code;
        this.updatePlaylist = updatePlaylist;
        media = PLAYLIST_SONG;
        contentResolver = context.getContentResolver();
    }

    public void OpenDialog()
    {
        alertDialog = new AlertDialog.Builder(context).create();
        final View alertLayout = getLayoutInflater().inflate(R.layout.dialog_media,null);

        mediaArt = alertLayout.findViewById(R.id.mediaArt);
        mediaName = alertLayout.findViewById(R.id.mediaName);
        mediaArtist = alertLayout.findViewById(R.id.mediaArtist);
        mediaType = alertLayout.findViewById(R.id.mediaType);
        delete = alertLayout.findViewById(R.id.mediaDelete);
        playlistAction = alertLayout.findViewById(R.id.mediaPlaylist);
        deleteText = alertLayout.findViewById(R.id.mediaDeleteText);
        next = alertLayout.findViewById(R.id.mediaPlayNext);
        last = alertLayout.findViewById(R.id.mediaPlayLast);
        setDialogClickListeners(alertDialog);

        switch (media)
        {
            case SONG:
                    setSongArt(song);
                    deleteText.setText(context.getString(R.string.deleteSongFromLibrary));
                    mediaName.setText(song.getTitle());
                    mediaArtist.setText(song.getArtist());
                    mediaType.setText(song.getAlbum());
                    break;

            case ALBUM:
                    setAlbumArt(album);
                    deleteText.setText(context.getString(R.string.deleteAlbumFromLibrary));
                    mediaName.setText(album.getAlbum());
                    mediaArtist.setText(album.getArtist());
                    mediaType.setVisibility(View.GONE);
                    break;

            case ARTIST:
                    deleteText.setText(context.getString(R.string.deleteArtistFromLibrary));
                    mediaArt.setImageDrawable(context.getDrawable(R.drawable.ic_noartistart));
                    mediaName.setText(artist.getArtist());
                    mediaArtist.setVisibility(View.GONE);
                    mediaType.setVisibility(View.GONE);
                    break;

            case PLAYLIST:
                    setPlaylistArt();
                    if(currentPlaylists == null && playlistsAdapter == null)
                        delete.setVisibility(View.GONE);
                    else
                        deleteText.setText(context.getString(R.string.deletePlaylist));
                    mediaName.setText(playlist.getPlaylist());
                    mediaArtist.setVisibility(View.GONE);
                    mediaType.setVisibility(View.GONE);
                    break;

            case PLAYLIST_SONG:
                setSongArt(playlistSong);
                deleteText.setText(context.getString(R.string.deleteSongFromPlaylist));
                mediaName.setText(playlistSong.getTitle());
                mediaArtist.setText(playlistSong.getArtist());
                mediaType.setText(playlistSong.getAlbum());
                break;
        }

        /*switch (code)
        {
            case FROM_LIBRARY:
                //Log.i(TAG, "DIALOG FROM LIBRARY");
                break;

            case FROM_ARTIST:
                //Log.i(TAG, "DIALOG FROM ARTIST");
                break;

            case FROM_ALBUM:
                //Log.i(TAG, "DIALOG FROM ALBUM");
                break;

            case FROM_PLAYLIST:
                //Log.i(TAG, "DIALOG FROM PLAYLIST");
                break;
        }*/

        alertDialog.setView(alertLayout);
        new Dialog(context);
        alertDialog.show();
        ColorDrawable back = new ColorDrawable(Color.TRANSPARENT);
        InsetDrawable inset = new InsetDrawable(back, 20);
        alertDialog.getWindow().setBackgroundDrawable(inset);
        alertDialog.getWindow().setElevation(20.0f);
    }

    private void OpenPlaylistDialog()
    {
        alertDialogPlaylist = new AlertDialog.Builder(context).create();
        final View alertLayoutPlaylist = getLayoutInflater().inflate(R.layout.dialog_playlist,null);

        cancelPlaylist = alertLayoutPlaylist.findViewById(R.id.cancelPlaylist);
        createNewPlaylist = alertLayoutPlaylist.findViewById(R.id.createPlaylist);
        setPlaylistDialogClickListeners(alertDialogPlaylist);

        recyclerView = alertLayoutPlaylist.findViewById(R.id.createdPlaylists);
        LinearLayoutManager layoutManager = new LinearLayoutManager(context);
        PlaylistDialogAdapter dialogAdapter = new PlaylistDialogAdapter(playlists, this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(dialogAdapter);

        alertDialogPlaylist.setView(alertLayoutPlaylist);
        new Dialog(context);
        alertDialogPlaylist.show();
        ColorDrawable back = new ColorDrawable(Color.TRANSPARENT);
        InsetDrawable inset = new InsetDrawable(back, 100);

        alertDialogPlaylist.getWindow().setBackgroundDrawable(inset);
        alertDialogPlaylist.getWindow().setElevation(20.0f);
    }

    public void OpenNewPlaylistDialog()
    {
        alertDialogNewPlaylist = new AlertDialog.Builder(context).create();
        final View alertLayoutNewPlaylist = getLayoutInflater().inflate(R.layout.dialog_new_playlist,null);

        newPlaylistCancel = alertLayoutNewPlaylist.findViewById(R.id.cancelNewPlaylist);
        newPlaylistCreate = alertLayoutNewPlaylist.findViewById(R.id.createNewPlaylist);
        newPlaylistName = alertLayoutNewPlaylist.findViewById(R.id.newPlaylistName);
        newPlaylistDescription = alertLayoutNewPlaylist.findViewById(R.id.newPlaylistDescription);
        setNewPlaylistDialogClickListeners(alertDialogNewPlaylist);

        alertDialogNewPlaylist.setView(alertLayoutNewPlaylist);
        new Dialog(context);
        alertDialogNewPlaylist.show();
        ColorDrawable back = new ColorDrawable(Color.TRANSPARENT);
        InsetDrawable inset = new InsetDrawable(back, 100);
        alertDialogNewPlaylist.getWindow().setBackgroundDrawable(inset);
        alertDialogNewPlaylist.getWindow().setElevation(20.0f);
    }

    private void setSongArt(Songs song)
    {
        Uri uri = Uri.parse(song.getArt());
        Glide.with(context).load(uri).placeholder(R.drawable.noalbumart).fallback(R.drawable.noalbumart).error(R.drawable.noalbumart).into(mediaArt);
    }

    private void setAlbumArt(Albums album)
    {
        Uri uri = Uri.parse(album.getArt());
        Glide.with(context).load(uri).placeholder(R.drawable.noalbumart).fallback(R.drawable.noalbumart).error(R.drawable.noalbumart).into(mediaArt);
    }

    private void setPlaylistArt()
    {
        Glide.with(context).load(R.drawable.playlistart).into(mediaArt);
    }

    @Override
    public void onListItemClick(Playlists playlist)
    {
        switch (media)
        {
            case SONG:
                mediaBrowserHelper.addToPlaylist(playlist, song);
                Toast.makeText(context, song.getTitle().toUpperCase() + " ADDED TO " + playlist.getPlaylist().toUpperCase(), Toast.LENGTH_SHORT).show();
                break;

            case ARTIST:
                mediaBrowserHelper.addToPlaylist(playlist, artist);
                Toast.makeText(context, artist.getArtist().toUpperCase() + " ADDED TO " + playlist.getPlaylist().toUpperCase(), Toast.LENGTH_SHORT).show();
                //Log.i(TAG, artist.getArtist().toUpperCase() + " ADDED TO " + playlist.getPlaylist().toUpperCase());
                break;

            case ALBUM:
                mediaBrowserHelper.addToPlaylist(playlist, album);
                Toast.makeText(context, album.getAlbum().toUpperCase() + " ADDED TO " + playlist.getPlaylist().toUpperCase(), Toast.LENGTH_SHORT).show();
                //Log.i(TAG, album.getAlbum().toUpperCase() + " ADDED TO " + playlist.getPlaylist().toUpperCase());
                break;

            case PLAYLIST:
                mediaBrowserHelper.addToPlaylist(playlist, this.playlist);
                Toast.makeText(context, this.playlist.getPlaylist().toUpperCase() + " ADDED TO " + playlist.getPlaylist().toUpperCase(), Toast.LENGTH_SHORT).show();
                //Log.i(TAG, this.playlist.getPlaylist().toUpperCase() + " ADDED TO " + playlist.getPlaylist().toUpperCase());
                break;
        }
        alertDialogPlaylist.dismiss();
    }

    private void setDialogClickListeners(AlertDialog alertDialog)
    {
        delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                switch (media)
                {
                    case SONG:
                        alertDialog.dismiss();
                        /*if(code == FROM_ALBUM || code == FROM_ARTIST)
                        {
                            updateLibrary.updateSongsLibrary(song);
                        }*/
                        updateLibrary.updateSongsLibrary(song);
                        removePosition = librarySongs.indexOf(song);
                        librarySongs.remove(song);
                        if(code == FROM_LIBRARY || code == FROM_ARTIST)
                            librarySongsAdapter.notifyItemRemoved(removePosition);
                        else if(code == FROM_ALBUM)
                            albumSongsAdapter.notifyItemRemoved(removePosition);

                        /*long songId = Long.parseLong(song.getId());
                        Uri songUri = ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, songId);
                        String selectionSong = MediaStore.Audio.Media._ID + "=?";
                        String[] selectionArgsSong = {song.getId()};
                        contentResolver.delete(songUri, selectionSong, selectionArgsSong);*/

                        //Toast.makeText(context, song.getTitle() + " DELETED", Toast.LENGTH_SHORT).show();
                        //Log.i(TAG, song.getTitle().toUpperCase() + " DELETED FROM LIBRARY");
                        break;

                    case ALBUM:
                        alertDialog.dismiss();
                        LoaderManager.getInstance((MainActivity) context).initLoader(DIALOG_MEDIA_LOADER, null, songsCallbacks);
                        break;

                    case ARTIST:
                        alertDialog.dismiss();
                        LoaderManager.getInstance((MainActivity) context).initLoader(DIALOG_MEDIA_LOADER, null, songsCallbacks);
                        break;

                    case PLAYLIST:
                        alertDialog.dismiss();
                        removePosition = currentPlaylists.indexOf(playlist);
                        currentPlaylists.remove(playlist);
                        playlistsAdapter.notifyItemRemoved(removePosition);
                        playlists.remove(playlist);

                        Uri playlistUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
                        String selectionPlaylist = MediaStore.Audio.Playlists.NAME + "=?";
                        String[] selectionArgPlaylist = {playlist.getPlaylist() + "SPLITHERE" + playlist.getDescription()};
                        contentResolver.delete(playlistUri, selectionPlaylist, selectionArgPlaylist);

                        Uri playlistSongsUri = MediaStore.Audio.Playlists.Members.getContentUri("external", Long.parseLong(playlist.getId()));
                        contentResolver.delete(playlistSongsUri, null, null);

                        Toast.makeText(context, playlist.getPlaylist() + " DELETED", Toast.LENGTH_SHORT).show();
                        //Log.i(TAG, playlist.getPlaylist().toUpperCase() + " DELETED FROM PLAYLISTS");
                        break;

                    case PLAYLIST_SONG:
                        alertDialog.dismiss();
                        removePosition = playlistSongs.indexOf(playlistSong);
                        playlistSongs.remove(playlistSong);
                        playlistSongsAdapter.notifyItemRemoved(removePosition);

                        Uri playlistSongUri = MediaStore.Audio.Playlists.Members.getContentUri("external", Long.parseLong(playlist.getId()));
                        String selectionPlaylistSong = MediaStore.Audio.Playlists.Members.AUDIO_ID + "=?";
                        String[] selectionArgPlaylistSong = {playlistSong.getId()};
                        contentResolver.delete(playlistSongUri, selectionPlaylistSong, selectionArgPlaylistSong);
                        updatePlaylist.updatePlaylistCount();

                        Toast.makeText(context, playlistSong.getTitle() + " DELETED", Toast.LENGTH_SHORT).show();
                        //Log.i(TAG, playlistSong.getTitle().toUpperCase() + " DELETED FROM PLAYLIST");
                        break;
                }
            }
        });

        playlistAction.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                alertDialog.dismiss();
                OpenPlaylistDialog();
            }
        });

        next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                switch (media)
                {
                    case SONG: mediaBrowserHelper.addNext(song); alertDialog.dismiss(); break;
                    case PLAYLIST_SONG: mediaBrowserHelper.addNext(playlistSong); alertDialog.dismiss(); break;
                    case ALBUM: mediaBrowserHelper.addNext(album); alertDialog.dismiss(); break;
                    case ARTIST: mediaBrowserHelper.addNext(artist); alertDialog.dismiss(); break;
                    case PLAYLIST: mediaBrowserHelper.addNext(playlist); alertDialog.dismiss(); break;
                }
            }
        });

        last.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                switch (media)
                {
                    case SONG: mediaBrowserHelper.addLast(song); alertDialog.dismiss(); break;
                    case PLAYLIST_SONG: mediaBrowserHelper.addLast(playlistSong); alertDialog.dismiss(); break;
                    case ALBUM: mediaBrowserHelper.addLast(album); alertDialog.dismiss(); break;
                    case ARTIST: mediaBrowserHelper.addLast(artist); alertDialog.dismiss(); break;
                    case PLAYLIST: mediaBrowserHelper.addLast(playlist); alertDialog.dismiss(); break;
                }
            }
        });
    }

    private void setPlaylistDialogClickListeners(AlertDialog alertDialog)
    {
        cancelPlaylist.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                alertDialog.dismiss();
            }
        });

        createNewPlaylist.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                alertDialog.dismiss();
                OpenNewPlaylistDialog();
            }
        });
    }

    private void setNewPlaylistDialogClickListeners(AlertDialog alertDialog)
    {
        newPlaylistCancel.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                alertDialog.dismiss();
            }
        });

        newPlaylistCreate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view)
            {
                String newName = newPlaylistName.getText().toString();
                String newDescription = newPlaylistDescription.getText().toString();
                if(!TextUtils.isEmpty(newName))
                {
                    ContentResolver contentResolver = context.getContentResolver();
                    Uri playlistUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;

                    ContentValues values = new ContentValues();

                    String split = "SPLITHERE";
                    values.put(MediaStore.Audio.Playlists.NAME, newName + split + newDescription);
                    Uri newPlaylistUri = contentResolver.insert(playlistUri, values);
                    if(newPlaylistUri != null)
                    {
                        String[] projection = {MediaStore.Audio.Playlists.NAME ,MediaStore.Audio.Playlists._ID};
                        Cursor cursor = contentResolver.query(newPlaylistUri ,projection, null, null, null);
                        if(cursor != null && cursor.moveToFirst())
                        {
                            int playlistDetails = cursor.getColumnIndex(MediaStore.Audio.Playlists.NAME);
                            int id = cursor.getColumnIndex(MediaStore.Audio.Playlists._ID);

                            String NameDescription = cursor.getString(playlistDetails);
                            String[] parts = NameDescription.split(split);
                            String playlistName = parts[0];
                            String playlistDescription = parts[1];
                            String playlistID = cursor.getString(id);

                            //Log.i(TAG, "PLAYLIST " + playlistName.toUpperCase() + " CREATED WITH ID " + playlistID);
                            playlists.add(new Playlists(playlistName, playlistID, playlistDescription));
                            cursor.close();
                        }
                    }

                    alertDialog.dismiss();
                    if(playlist != null)
                        OpenPlaylistDialog();
                }
                else
                    newPlaylistName.setError("MUST INPUT NAME");
            }
        });
    }

    private LoaderManager.LoaderCallbacks<ArrayList> songsCallbacks = new LoaderManager.LoaderCallbacks<ArrayList>()
    {
        @NonNull
        @Override
        public Loader<ArrayList> onCreateLoader(int id, @Nullable Bundle args)
        {
            if(media == ALBUM)
                return new GetMedia(context, GET_ALBUM_SONGS, -1, album);
            return new GetMedia(context, GET_ARTIST_SONGS, -1, artist);
        }

        @Override
        public void onLoadFinished(@NonNull Loader<ArrayList> loader, ArrayList data)
        {
            if(media == ALBUM)
            {
                albumSongs = data;
                updateLibrary.updateAlbumsLibrary(album, albumSongs, code);
                if(code == FROM_LIBRARY || code == FROM_ARTIST)
                {
                    removePosition = libraryAlbums.indexOf(album);
                    libraryAlbums.remove(album);
                    libraryAlbumsAdapter.notifyItemRemoved(removePosition);
                }
            }

            else
            {
                artistSongs = data;
                updateLibrary.updateArtistsLibrary(artist, artistSongs);
                removePosition = libraryArtists.indexOf(artist);
                libraryArtists.remove(artist);
                libraryArtistsAdapter.notifyItemRemoved(removePosition);
            }
        }

        @Override
        public void onLoaderReset(@NonNull Loader<ArrayList> loader) {}
    };
}
<file_sep>/app/src/main/java/com/rookieandroid/rookiemusicplayer/architecture/StateViewModel.java
package com.rookieandroid.rookiemusicplayer.architecture;

import android.app.Application;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;

import com.rookieandroid.rookiemusicplayer.SavedStateDetails;
import com.rookieandroid.rookiemusicplayer.Songs;

import java.util.List;

public class StateViewModel extends AndroidViewModel
{
    private StateRepository stateRepository;
    private LiveData<List<Songs>> savedQueue;
    private LiveData<List<SavedStateDetails>> savedStateDetails;


    public StateViewModel(@NonNull Application application)
    {
        super(application);
        stateRepository = new StateRepository(application);
        savedQueue = stateRepository.getSavedQueue();
        savedStateDetails = stateRepository.getSavedStateDetails();
    }

    public void insertAll(List<Songs> savedQueue)
    {
        stateRepository.insertAll(savedQueue);
    }

    public void insert(SavedDetails savedDetails)
    {
        stateRepository.insert(savedDetails);
    }

    public void update(SavedDetails savedDetails)
    {
        stateRepository.update(savedDetails);
    }

    public void delete(SavedDetails savedDetails)
    {
        stateRepository.delete(savedDetails);
    }

    public void deleteSavedQueue()
    {
        stateRepository.deleteSavedQueue();
    }

    public void deleteSavedDetails()
    {
        stateRepository.deleteSavedDetails();
    }

    public LiveData<List<Songs>> getSavedQueue()
    {
        return savedQueue;
    }

    public LiveData<List<SavedStateDetails>> getSavedStateDetails()
    {
        return savedStateDetails;
    }
}
