com.smithdtyler.prettygoodmusicplayer.NowPlaying.java Source code

Java tutorial

Introduction

Here is the source code for com.smithdtyler.prettygoodmusicplayer.NowPlaying.java

Source

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

package com.smithdtyler.prettygoodmusicplayer;

import java.util.Locale;

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.support.v4.app.TaskStackBuilder;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

import com.smithdtyler.prettygoodmusicplayer.MusicPlaybackService.PlaybackState;

public class NowPlaying extends Activity {

    private static final String TAG = "Now Playing";
    static final String KICKOFF_SONG = "KICKOFF_SONG";

    // State information
    private String desiredArtistName;
    private String desiredArtistAbsPath;
    private String desiredAlbumName;
    private String[] desiredSongAbsFileNames;
    private int desiredAbsSongFileNamesPosition;
    private boolean startPlayingRequired = true;
    private boolean userDraggingProgress = false;

    // Messaging and service stuff
    boolean mIsBound;
    private Messenger mService;
    final Messenger mMessenger = new Messenger(new IncomingHandler(this));
    private ServiceConnection mConnection = new NowPlayingServiceConnection(this);
    private String currentTheme;
    private String currentSize;
    private boolean currentFullScreen;
    private int desiredSongProgress;
    private BroadcastReceiver exitReceiver;

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

        Intent originIntent = getIntent();
        if (originIntent.getBooleanExtra("From_Notification", false)) {

            String artistName = originIntent.getStringExtra(ArtistList.ARTIST_NAME);
            String artistAbsPath = originIntent.getStringExtra(ArtistList.ARTIST_ABS_PATH_NAME);
            if (artistName != null && artistAbsPath != null) {
                Log.i(TAG, "Now Playing was launched from a notification, setting up its back stack");
                // Reference: https://developer.android.com/reference/android/app/TaskStackBuilder.html
                TaskStackBuilder tsb = TaskStackBuilder.create(this);
                Intent intent = new Intent(this, ArtistList.class);
                tsb.addNextIntent(intent);

                intent = new Intent(this, AlbumList.class);
                intent.putExtra(ArtistList.ARTIST_NAME, artistName);
                intent.putExtra(ArtistList.ARTIST_ABS_PATH_NAME, artistAbsPath);
                tsb.addNextIntent(intent);

                String albumName = originIntent.getStringExtra(AlbumList.ALBUM_NAME);
                if (albumName != null) {
                    intent = new Intent(this, SongList.class);
                    intent.putExtra(AlbumList.ALBUM_NAME, albumName);
                    intent.putExtra(ArtistList.ARTIST_NAME, artistName);
                    intent.putExtra(ArtistList.ARTIST_ABS_PATH_NAME, artistAbsPath);
                    tsb.addNextIntent(intent);
                }
                intent = new Intent(this, NowPlaying.class);
                tsb.addNextIntent(intent);
                tsb.startActivities();
            }

        }

        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        String theme = sharedPref.getString("pref_theme", getString(R.string.light));
        String size = sharedPref.getString("pref_text_size", getString(R.string.medium));
        Log.i(TAG, "got configured theme " + theme);
        Log.i(TAG, "got configured size " + size);

        // These settings were fixed in english for a while, so check for old style settings as well as language specific ones.
        if (theme.equalsIgnoreCase(getString(R.string.dark)) || theme.equalsIgnoreCase("dark")) {
            Log.i(TAG, "setting theme to " + theme);
            if (size.equalsIgnoreCase(getString(R.string.small)) || size.equalsIgnoreCase("small")) {
                setTheme(R.style.PGMPDarkSmall);
            } else if (size.equalsIgnoreCase(getString(R.string.medium)) || size.equalsIgnoreCase("medium")) {
                setTheme(R.style.PGMPDarkMedium);
            } else {
                setTheme(R.style.PGMPDarkLarge);
            }
        } else if (theme.equalsIgnoreCase(getString(R.string.light)) || theme.equalsIgnoreCase("light")) {
            Log.i(TAG, "setting theme to " + theme);
            if (size.equalsIgnoreCase(getString(R.string.small)) || size.equalsIgnoreCase("small")) {
                setTheme(R.style.PGMPLightSmall);
            } else if (size.equalsIgnoreCase(getString(R.string.medium)) || size.equalsIgnoreCase("medium")) {
                setTheme(R.style.PGMPLightMedium);
            } else {
                setTheme(R.style.PGMPLightLarge);
            }
        }

        boolean fullScreen = sharedPref.getBoolean("pref_full_screen_now_playing", false);
        currentFullScreen = fullScreen;
        if (fullScreen) {
            requestWindowFeature(Window.FEATURE_NO_TITLE);
            getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                    WindowManager.LayoutParams.FLAG_FULLSCREEN);
        } else {
            ActionBar actionBar = getActionBar();
            actionBar.setDisplayHomeAsUpEnabled(true);
        }
        setContentView(R.layout.activity_now_playing);

        if (savedInstanceState == null) {
            doBindService(true);
            startPlayingRequired = true;
        } else {
            doBindService(false);
            startPlayingRequired = false;
        }

        // Get the message from the intent
        Intent intent = getIntent();
        if (intent.getBooleanExtra(KICKOFF_SONG, false)) {
            desiredArtistName = intent.getStringExtra(ArtistList.ARTIST_NAME);
            desiredAlbumName = intent.getStringExtra(AlbumList.ALBUM_NAME);
            desiredArtistAbsPath = intent.getStringExtra(ArtistList.ARTIST_ABS_PATH_NAME);
            desiredSongAbsFileNames = intent.getStringArrayExtra(SongList.SONG_ABS_FILE_NAME_LIST);
            desiredAbsSongFileNamesPosition = intent.getIntExtra(SongList.SONG_ABS_FILE_NAME_LIST_POSITION, 0);
            desiredSongProgress = intent.getIntExtra(MusicPlaybackService.TRACK_POSITION, 0);

            Log.d(TAG,
                    "Got song names " + desiredSongAbsFileNames + " position " + desiredAbsSongFileNamesPosition);

            TextView et = (TextView) findViewById(R.id.artistName);
            et.setText(desiredArtistName);

            et = (TextView) findViewById(R.id.albumName);
            et.setText(desiredAlbumName);
        }

        // The song name field will be set when we get our first update update from the service.

        final ImageButton pause = (ImageButton) findViewById(R.id.playPause);
        pause.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                playPause();
            }

        });

        ImageButton previous = (ImageButton) findViewById(R.id.previous);
        previous.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                previous();
            }

        });

        previous.setOnLongClickListener(new OnLongClickListener() {

            @Override
            public boolean onLongClick(View v) {
                jumpBack();
                return true;
            }
        });

        ImageButton next = (ImageButton) findViewById(R.id.next);
        next.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                next();
            }
        });

        final ImageButton shuffle = (ImageButton) findViewById(R.id.shuffle);
        shuffle.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                toggleShuffle();
            }
        });

        final ImageButton jumpback = (ImageButton) findViewById(R.id.jumpback);
        jumpback.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                jumpBack();
            }
        });

        SeekBar seekBar = (SeekBar) findViewById(R.id.songProgressBar);
        seekBar.setEnabled(true);
        seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {

            private int requestedProgress;

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser) {
                    Log.v(TAG, "drag location updated..." + progress);
                    this.requestedProgress = progress;
                    updateSongProgressLabel(progress);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                NowPlaying.this.userDraggingProgress = true;

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                Message msg = Message.obtain(null, MusicPlaybackService.MSG_SEEK_TO);
                msg.getData().putInt(MusicPlaybackService.TRACK_POSITION, requestedProgress);
                try {
                    Log.i(TAG, "Sending a request to seek!");
                    mService.send(msg);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                NowPlaying.this.userDraggingProgress = false;
            }

        });

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("com.smithdtyler.ACTION_EXIT");
        exitReceiver = new BroadcastReceiver() {

            @Override
            public void onReceive(Context context, Intent intent) {
                Log.i(TAG, "Received exit request, shutting down...");
                Intent msgIntent = new Intent(getBaseContext(), MusicPlaybackService.class);
                msgIntent.putExtra("Message", MusicPlaybackService.MSG_STOP_SERVICE);
                startService(msgIntent);
                finish();
            }

        };
        registerReceiver(exitReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(exitReceiver);
        unbindService(mConnection);
    }

    private void updateSongProgressLabel(int progress) {
        TextView progressLabel = (TextView) findViewById(R.id.songProgressLabel);
        int minutes = progress / (1000 * 60);
        int seconds = (progress % (1000 * 60)) / 1000;
        String time = String.format(Locale.getDefault(), "%d:%02d", minutes, seconds);
        progressLabel.setText(time);
    }

    // Playback control methods
    private void playPause() {
        Log.d(TAG, "Play/Pause clicked...");
        Message msg = Message.obtain(null, MusicPlaybackService.MSG_PLAYPAUSE);
        try {
            Log.i(TAG, "Sending a request to start playing!");
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private void next() {
        Log.d(TAG, "next...");
        Message msg = Message.obtain(null, MusicPlaybackService.MSG_NEXT);
        try {
            Log.i(TAG, "SEnding a request to go to next!");
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public void previous() {
        Log.d(TAG, "Previous clicked...");
        Message msg = Message.obtain(null, MusicPlaybackService.MSG_PREVIOUS);
        try {
            Log.i(TAG, "Sending a request to go to previous!");
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public void jumpBack() {
        Log.d(TAG, "JumpBack clicked...");
        Message msg = Message.obtain(null, MusicPlaybackService.MSG_JUMPBACK);
        try {
            Log.i(TAG, "Sending a request to jump back!");
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    public void toggleShuffle() {
        Log.d(TAG, "Shuffle clicked...");
        Message msg = Message.obtain(null, MusicPlaybackService.MSG_TOGGLE_SHUFFLE);
        try {
            Log.i(TAG, "Sending a request to toggle shuffle!");
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    // Service connection management
    private class NowPlayingServiceConnection implements ServiceConnection {

        private NowPlaying _nowPlaying;

        public NowPlayingServiceConnection(NowPlaying nowPlaying) {
            this._nowPlaying = nowPlaying;
        }

        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);

            // Register with the service
            try {
                Message msg = Message.obtain(null, MusicPlaybackService.MSG_REGISTER_CLIENT);
                msg.replyTo = mMessenger;
                mService.send(msg);
            } catch (RemoteException e) {
                // In this case the service has crashed before we could even do
                // anything with it
            }

            if (this._nowPlaying.startPlayingRequired) {
                if (desiredSongAbsFileNames != null) {
                    // set the playlist
                    Message msg = Message.obtain(null, MusicPlaybackService.MSG_SET_PLAYLIST);
                    msg.getData().putStringArray(SongList.SONG_ABS_FILE_NAME_LIST, desiredSongAbsFileNames);
                    msg.getData().putInt(SongList.SONG_ABS_FILE_NAME_LIST_POSITION,
                            desiredAbsSongFileNamesPosition);
                    msg.getData().putString(ArtistList.ARTIST_NAME, desiredArtistName);
                    msg.getData().putString(ArtistList.ARTIST_ABS_PATH_NAME, desiredArtistAbsPath);
                    msg.getData().putString(AlbumList.ALBUM_NAME, desiredAlbumName);
                    msg.getData().putInt(MusicPlaybackService.TRACK_POSITION, desiredSongProgress);
                    try {
                        Log.i(TAG, "Sending a playlist!");
                        mService.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }

                    // start playing!
                    msg = Message.obtain(null, MusicPlaybackService.MSG_PLAYPAUSE);
                    try {
                        Log.i(TAG, "Sending a play command!");
                        mService.send(msg);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }
            }

        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected - process crashed.
            mService = null; // TODO need to do some null checks
        }
    };

    private static class IncomingHandler extends Handler {

        private NowPlaying _activity;

        private IncomingHandler(NowPlaying nowPlaying) {
            _activity = nowPlaying;
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MusicPlaybackService.MSG_SERVICE_STATUS:
                String currentSongName = msg.getData().getString(MusicPlaybackService.PRETTY_SONG_NAME);
                TextView tv = (TextView) _activity.findViewById(R.id.songName);
                if (!tv.getText().equals(currentSongName)) {
                    tv.setText(currentSongName);
                }

                String currentAlbumName = msg.getData().getString(MusicPlaybackService.PRETTY_ALBUM_NAME);
                tv = (TextView) _activity.findViewById(R.id.albumName);
                if (!tv.getText().equals(currentAlbumName)) {
                    tv.setText(currentAlbumName);
                }

                String currentArtistName = msg.getData().getString(MusicPlaybackService.PRETTY_ARTIST_NAME);
                tv = (TextView) _activity.findViewById(R.id.artistName);
                if (!tv.getText().equals(currentArtistName)) {
                    tv.setText(currentArtistName);
                }

                boolean isShuffling = msg.getData().getBoolean(MusicPlaybackService.IS_SHUFFLING);
                ImageButton shuffle = (ImageButton) _activity.findViewById(R.id.shuffle);
                if (shuffle.isSelected() != isShuffling) {
                    shuffle.setSelected(isShuffling);
                }

                PlaybackState state = PlaybackState.values()[msg.getData()
                        .getInt(MusicPlaybackService.PLAYBACK_STATE, 0)];
                ImageButton playPause = (ImageButton) _activity.findViewById(R.id.playPause);
                if (playPause.getContentDescription().equals(_activity.getResources().getString(R.string.play))) {
                    if (state == PlaybackState.PLAYING) {
                        playPause
                                .setImageDrawable(_activity.getResources().getDrawable(R.drawable.ic_action_pause));
                        playPause.setContentDescription(_activity.getResources().getString(R.string.pause));
                    }
                } else {
                    if (state == PlaybackState.PAUSED) {
                        playPause.setImageDrawable(_activity.getResources().getDrawable(R.drawable.ic_action_play));
                        playPause.setContentDescription(_activity.getResources().getString(R.string.play));
                    }
                }
                int duration = msg.getData().getInt(MusicPlaybackService.TRACK_DURATION, -1);
                int position = msg.getData().getInt(MusicPlaybackService.TRACK_POSITION, -1);
                if (duration > 0) {
                    if (!_activity.userDraggingProgress) {
                        SeekBar seekBar = (SeekBar) _activity.findViewById(R.id.songProgressBar);
                        seekBar.setMax(duration);
                        seekBar.setProgress(position);
                        _activity.updateSongProgressLabel(position);
                    }
                }
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

    // Service Management Methods
    @SuppressLint("InlinedApi")
    void doBindService(boolean startService) {
        Log.i(TAG, "Binding to the service!");
        bindService(new Intent(this, MusicPlaybackService.class), mConnection,
                Context.BIND_IMPORTANT | Context.BIND_AUTO_CREATE);
        mIsBound = true;
        // Need to start the service so it won't be stopped when this activity is destroyed.
        // https://developer.android.com/guide/components/bound-services.html
        if (startService) {
            startService(new Intent(this, MusicPlaybackService.class));
        }
    }

    void doUnbindService() {
        Log.i(TAG, "Unbinding the service!");
        if (mIsBound) {
            // If we have received the service, and hence registered with it,
            // then now is the time to unregister.
            if (mService != null) {
                try {
                    Message msg = Message.obtain(null, MusicPlaybackService.MSG_UNREGISTER_CLIENT);
                    msg.replyTo = mMessenger;
                    mService.send(msg);
                } catch (RemoteException e) {
                    // There is nothing special we need to do if the service has
                    // crashed.
                }
            }

            // Detach our existing connection.
            unbindService(mConnection);
            mIsBound = false;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.now_playing, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            Intent intent = new Intent(NowPlaying.this, SettingsActivity.class);
            startActivity(intent);
            return true;
        }
        if (id == R.id.action_exit) {
            Intent broadcastIntent = new Intent();
            broadcastIntent.setAction("com.smithdtyler.ACTION_EXIT");
            sendBroadcast(broadcastIntent);
            Intent startMain = new Intent(Intent.ACTION_MAIN);
            startMain.addCategory(Intent.CATEGORY_HOME);
            startMain.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
            startActivity(startMain);
            finish();
            return true;
        }
        if (id == android.R.id.home) {
            onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void setAccentColors(int color) {
        View shuffleBG = findViewById(R.id.shufflebackground);
        View buttonBG = findViewById(R.id.playpausepreviousbackground);
        View spacer1 = findViewById(R.id.spacer1);
        View spacer2 = findViewById(R.id.spacer2);
        if (shuffleBG != null && buttonBG != null && spacer1 != null && spacer2 != null) {
            shuffleBG.setBackgroundColor(color);
            buttonBG.setBackgroundColor(color);
            spacer1.setBackgroundColor(color);
            spacer2.setBackgroundColor(color);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);

        String accentColor = sharedPref.getString("accent_color",
                getResources().getStringArray(R.array.accentcoloroptions)[0]);
        String customAccentColor = sharedPref.getString("custom_accent_color", "");
        String colorOptions[] = getResources().getStringArray(R.array.accentcoloroptions);
        // TODO I'm sure there's a better way to do this, I'm just not sure what it is...
        for (int i = 0; i < colorOptions.length; i++) {
            if (accentColor.equals(colorOptions[i])) {
                switch (i) {
                case 0:
                    setAccentColors(getResources().getColor(R.color.agategrey));
                    break;
                case 1:
                    setAccentColors(getResources().getColor(R.color.blackgreen));
                    break;
                case 2:
                    setAccentColors(getResources().getColor(R.color.brilliantblue));
                    break;
                case 3:
                    setAccentColors(getResources().getColor(R.color.brilliantorange));
                    break;
                case 4:
                    setAccentColors(getResources().getColor(R.color.emeraldgreen));
                    break;
                case 5:
                    setAccentColors(getResources().getColor(R.color.ivory));
                    break;
                case 6:
                    setAccentColors(getResources().getColor(R.color.skyblue));
                    break;
                case 7:
                    setAccentColors(getResources().getColor(R.color.trafficgreen));
                    break;
                case 8:
                    setAccentColors(getResources().getColor(R.color.waterblue));
                    break;
                case 9:
                    try {
                        Log.i(TAG, "custom color: " + customAccentColor);
                        if (!customAccentColor.startsWith("#")) {
                            customAccentColor = "#" + customAccentColor;
                        }
                        if (customAccentColor.toLowerCase(Locale.getDefault()).startsWith("0x")) {
                            customAccentColor = customAccentColor.substring(2);
                        }
                        int custom = Color.parseColor(customAccentColor.trim());
                        setAccentColors(custom);
                    } catch (Exception e) {
                        Log.w(TAG, "Unable to parse custom color", e);
                    }
                    break;
                }
            }
        }

        String theme = sharedPref.getString("pref_theme", getString(R.string.light));
        String size = sharedPref.getString("pref_text_size", getString(R.string.medium));
        boolean fullScreen = sharedPref.getBoolean("pref_full_screen_now_playing", false);
        Log.i(TAG, "got configured theme " + theme);
        Log.i(TAG, "Got configured size " + size);
        if (currentTheme == null) {
            currentTheme = theme;
        }

        if (currentSize == null) {
            currentSize = size;
        }
        if (!currentTheme.equals(theme) || !currentSize.equals(size) || currentFullScreen != fullScreen) {
            finish();
            startActivity(getIntent());
        }
    }

}