Android Open Source - PlayerHater Player Hater Service






From Project

Back to project page PlayerHater.

License

The source code is released under:

Apache License

If you think the Android project PlayerHater listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*******************************************************************************
 * Copyright 2013, 2014 Chris Rhoden, Rebecca Nesson, Public Radio Exchange
 *//w  ww  .j  a  v a 2s  . c  o m
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package org.prx.playerhater.service;

import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.media.RemoteControlClient;
import android.os.IBinder;
import android.view.KeyEvent;

import org.prx.playerhater.BroadcastReceiver;
import org.prx.playerhater.PlayerHater;
import org.prx.playerhater.PlayerHaterPlugin;
import org.prx.playerhater.Song;
import org.prx.playerhater.ipc.ClientPlugin;
import org.prx.playerhater.ipc.IPlayerHaterClient;
import org.prx.playerhater.ipc.PlayerHaterClient;
import org.prx.playerhater.ipc.PlayerHaterServer;
import org.prx.playerhater.mediaplayer.PlaylistSupportingPlayer;
import org.prx.playerhater.plugins.BackgroundedPlugin;
import org.prx.playerhater.plugins.PluginCollection;
import org.prx.playerhater.service.PlayerStateWatcher.PlayerHaterStateListener;
import org.prx.playerhater.songs.SongHost;
import org.prx.playerhater.util.Config;
import org.prx.playerhater.util.IPlayerHater;
import org.prx.playerhater.util.Log;
import org.prx.playerhater.wrappers.ServicePlayerHater;

@SuppressLint("InlinedApi")
public abstract class PlayerHaterService extends Service implements
        IPlayerHater, PlayerHaterStateListener {

    private static final String SELF_STARTER = "org.prx.playerhater.service.PlayerHaterService.SELF_STARTER";

    private int mStarted = -1;

    private boolean mIsCurrentlyBound = false;
    private boolean mPausedInApp = false;

    private PlayerHaterPlugin mPlugin;
    private Config mConfig;
    private PluginCollection mPluginCollection;

    private final ServicePlayerHater mPlayerHater = new ServicePlayerHater(this);
    private final PlayerHaterServer mServer = new PlayerHaterServer(this);
    private final PlayerStateWatcher mPlayerStateWatcher = new PlayerStateWatcher(
            this);

    private ClientPlugin mClient;

    private int mTransportControlFlags = PlayerHater.DEFAULT_TRANSPORT_CONTROL_FLAGS;

    public void setClient(IPlayerHaterClient client) {
        if (mClient != null) {
            getPluginCollection().remove(mClient);
        }
        if (client != null) {
            mClient = new ClientPlugin(client);

            // If we're running remotely, set up the remote song host.
            // If this condition returns false, that indicates that
            // the two sides of the transaction are happening on the
            // same process.
            if (!(client instanceof PlayerHaterClient)) {
                SongHost.setRemote(client);
            }

            if (nowPlaying() != null) {
                mClient.onSongChanged(nowPlaying());
            }
            if (getNextSong() != null) {
                mClient.onNextSongAvailable(getNextSong());
            } else {
                mClient.onNextSongUnavailable();
            }
            switch (getState()) {
                case PlayerHater.STATE_LOADING:
                    mClient.onAudioLoading();
                    break;
                case PlayerHater.STATE_PLAYING:
                case PlayerHater.STATE_STREAMING:
                    mClient.onAudioStarted();
                    break;
                case PlayerHater.STATE_PAUSED:
                    mClient.onAudioPaused();
                    break;
                case PlayerHater.STATE_IDLE:
                    mClient.onAudioStopped();
                    break;
            }

            if (mTransportControlFlags != PlayerHater.DEFAULT_TRANSPORT_CONTROL_FLAGS) {
                mClient.onTransportControlFlagsChanged(mTransportControlFlags);
            }

            getPluginCollection().add(mClient);

        } else {
            mClient = null;
            SongHost.clear();
        }
    }

  /* The Service Life Cycle */

    @Override
    public void onCreate() {
        super.onCreate();
        BroadcastReceiver.register(getApplicationContext());
        Log.TAG = getPackageName() + "/PH/" + getClass().getSimpleName();
    }

    @Override
    public void onDestroy() {
        if (mMediaPlayer != null) {
            mMediaPlayer.release();
        }
        mMediaPlayer = null;
        getPlugin().onPlayerHaterShutdown();
        BroadcastReceiver.release(getApplicationContext());
        SongHost.clear();
        super.onDestroy();
    }

    @Override
    @SuppressWarnings("deprecation")
    public void onStart(Intent intent, int requestId) {
        onStartCommand(intent, 0, requestId);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int requestId) {
        if (isSelfStartCommand(intent)) {
            mStarted = requestId;
        } else {
            new Thread(new RemoteControlButtonTask(intent, this, requestId))
                    .start();
        }
        return START_NOT_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        setConfig(Config.fromIntent(intent));
        return mServer;
    }

    // We don't want onBind called again when the next
    // bind request comes in.
    @Override
    public boolean onUnbind(Intent intent) {
        if (mPausedInApp && getState() == PlayerHater.STATE_PAUSED) {
            stop();
        }
        setClient(null);
        mPausedInApp = false;
        return true;
    }

    @Override
    public void onRebind(Intent intent) {
    }

  /* END Life Cycle Methods */

    /*
     * Lazy Loaders for Plugins.
     */
    protected PlayerHaterPlugin getPlugin() {
        if (mPlugin == null) {
            mPlugin = new BackgroundedPlugin(getPluginCollection());
        }
        return mPlugin;
    }

    protected PluginCollection getPluginCollection() {
        if (mPluginCollection == null) {
            mPluginCollection = new PluginCollection();
        }
        return mPluginCollection;
    }

  /* Player State Methods */

    @Override
    public boolean isPlaying() {
        return (getState() & (PlayerHater.STATE_PLAYING | PlayerHater.STATE_STREAMING)) != 0;
    }

    @Override
    public boolean isLoading() {
        return (getState() & PlayerHater.STATE_LOADING) != 0;
    }

    @Override
    public int getState() {
        if (mInTransaction != PlayerHater.STATE_INVALID) {
            return mInTransaction;
        } else {
            return mLastState;
        }
    }

    @Override
    public int getDuration() {
        return peekMediaPlayer() != null ? getMediaPlayer().getDuration() : 0;
    }

    @Override
    public int getCurrentPosition() {
        return peekMediaPlayer() != null ? getMediaPlayer()
                .getCurrentPosition() : 0;
    }

  /* END Player State Methods */

  /* Generic Player Controls */

    @Override
    public boolean pause() {
        return pause(false);
    }


    public boolean pause(boolean fromApplication) {
        mPausedInApp = fromApplication;
        if (pauseAllowed()) {
            return getMediaPlayer().conditionalPause();
        }
        return false;
    }

    @Override
    public boolean stop() {
        return getMediaPlayer().conditionalStop();
    }

    @Override
    public boolean play() {
        mPausedInApp = false;
        if (playAllowed()) {
            return peekMediaPlayer() != null && getMediaPlayer().conditionalPlay();
        }
        return false;
    }

    @Override
    public boolean play(int startTime) {
        mPausedInApp = false;
        getMediaPlayer().conditionalPause();
        getMediaPlayer().seekTo(startTime);
        getMediaPlayer().conditionalPlay();
        return true;
    }

    public void duck() {
        getMediaPlayer().setVolume(0.1f, 0.1f);
    }

    public void unduck() {
        getMediaPlayer().setVolume(1.0f, 1.0f);
    }

  /* END Generic Player Controls */

  /* Decomposed Player Methods */

    @Override
    public boolean play(Song song) throws IllegalArgumentException {
        return play(song, 0);
    }

  /* END Decomposed Player Methods */

  /* Plug-In Stuff */

    @Override
    public void setTransportControlFlags(int transportControlFlags) {
        mTransportControlFlags = transportControlFlags
                | RemoteControlClient.FLAG_KEY_MEDIA_STOP;

        if ((PlayerHater.STATE_STREAMING & getState()) == 0) {
            mTransportControlFlags = mTransportControlFlags
                    | RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
        }

        getPlugin().onTransportControlFlagsChanged(mTransportControlFlags);
    }

  /* END Plug-In Stuff */

  /* Remote Controls */

    private static final int ALLOW_PLAY = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
            | RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
    private static final int ALLOW_PAUSE = RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
            | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
    private static final int ALLOW_PREV = RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS
            | RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
    private static final int ALLOW_NEXT = RemoteControlClient.FLAG_KEY_MEDIA_NEXT
            | RemoteControlClient.FLAG_KEY_MEDIA_REWIND;

    protected boolean playAllowed() {
        return (ALLOW_PLAY & mTransportControlFlags) != 0;
    }

    protected boolean pauseAllowed() {
        return (ALLOW_PAUSE & mTransportControlFlags) != 0;
    }

    protected boolean nextAllowed() {
        return (ALLOW_NEXT & mTransportControlFlags) != 0;
    }

    protected boolean previousAllowed() {
        return (ALLOW_PREV & mTransportControlFlags) != 0;
    }

    public void onRemoteControlButtonPressed(int button) {
        switch (button) {
            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                if (isPlaying()) {
                    pause();
                } else {
                    play();
                }
                break;
            case KeyEvent.KEYCODE_MEDIA_PLAY:
                play();
                break;
            case KeyEvent.KEYCODE_MEDIA_PAUSE:
                pause();
                break;
            case KeyEvent.KEYCODE_MEDIA_STOP:
                stop();
                break;
            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                skipBack();
                break;
            case KeyEvent.KEYCODE_MEDIA_NEXT:
                skip();
                break;
        }
    }

  /* END Remote Controls */

  /* Events for Subclasses */

    protected void onStopped() {
        getPlugin().onAudioStopped();
    }

    protected void onPaused() {
        getPlugin().onAudioPaused();
    }

    protected void onLoading() {
        getPlugin().onAudioLoading();
    }

    protected void onStarted() {
        getPlugin().onAudioStarted();
    }

    protected void onResumed() {
        getPlugin().onAudioResumed();
    }

    protected void onSongChanged(Song nowPlaying) {
        getPlugin().onSongChanged(nowPlaying);
    }

    protected void onSongFinished(Song nowPlaying, int reason) {
        if (nowPlaying != null) {
            getPlugin().onSongFinished(nowPlaying, reason);
        }
    }

    public abstract Song getNextSong();

    protected void onNextSongChanged(Song nextSong) {
        if (nextSong != null)
            getPlugin().onNextSongAvailable(nextSong);
        else
            getPlugin().onNextSongUnavailable();
    }

    @Override
    public void setPendingIntent(PendingIntent intent) {
        getPlugin().onPendingIntentChanged(intent);
    }

  /* END Events for Subclasses */

    // ///////////////////////
    // Config stuff

    private void setConfig(Config config) {
        if (config != null && mConfig == null) {
            mConfig = config;
            mConfig.run(getApplicationContext(), mPlayerHater,
                    getPluginCollection());
        }
    }

  /*
     * State things
   * 
   * @see
   * org.prx.playerhater.service.PlayerStateWatcher.PlayerHaterStateListener
   * #onStateChanged(int)
   */

    private int mLastState = PlayerHater.STATE_IDLE;
    private int mInTransaction = PlayerHater.STATE_INVALID;

    protected boolean startTransaction() {
        if (mInTransaction == PlayerHater.STATE_INVALID) {
            mInTransaction = mLastState;
            return true;
        } else {
            return false;
        }
    }

    protected void commitTransaction() {
        if (mInTransaction != PlayerHater.STATE_INVALID) {
            int nextState = mLastState;
            mLastState = mInTransaction;
            mInTransaction = PlayerHater.STATE_INVALID;
            onStateChanged(nextState);
        }
    }

    @Override
    public void onStateChanged(int state) {
        if (!selfStarted() && state != PlayerHater.STATE_IDLE) {
            startSelf();
        }
        if (mInTransaction == PlayerHater.STATE_INVALID) {
            switch (state) {
                case PlayerHater.STATE_IDLE:
                    onStopped();
                    break;
                case PlayerHater.STATE_LOADING:
                    onLoading();
                    break;
                case PlayerHater.STATE_PAUSED:
                    onPaused();
                    break;
                case PlayerHater.STATE_PLAYING:
                    if (mLastState == PlayerHater.STATE_PAUSED) {
                        onResumed();
                    } else {
                        onStarted();
                    }
            }
        }
        mLastState = state;
    }

    @Override
    public void onDurationChanged(int duration) {
        getPlugin().onDurationChanged(duration);
    }

    // ///////////////////////
    // For dealing with
    // MediaPlayers.

    private PlaylistSupportingPlayer mMediaPlayer;

    synchronized protected PlaylistSupportingPlayer getMediaPlayer() {
        if (mMediaPlayer == null) {
            setMediaPlayer(buildMediaPlayer());
        }
        return mMediaPlayer;
    }

    synchronized protected PlaylistSupportingPlayer peekMediaPlayer() {
        return mMediaPlayer;
    }

    synchronized protected void setMediaPlayer(
            PlaylistSupportingPlayer mediaPlayer) {
        boolean myTransaction = startTransaction();
        mPlayerStateWatcher.setMediaPlayer(mediaPlayer);
        mMediaPlayer = mediaPlayer;
        if (myTransaction) {
            commitTransaction();
        }
    }

    protected PlaylistSupportingPlayer swapMediaPlayer(
            PlaylistSupportingPlayer mediaPlayer) {
        return swapMediaPlayer(mediaPlayer, false);
    }

    protected PlaylistSupportingPlayer swapMediaPlayer(
            PlaylistSupportingPlayer mediaPlayer, boolean play) {
        boolean myTransaction = startTransaction();
        PlaylistSupportingPlayer oldPlayer = peekMediaPlayer();
        if (oldPlayer != null) {
            oldPlayer.conditionalPause();
        }
        if (play) {
            mediaPlayer.start();
        }
        setMediaPlayer(mediaPlayer);
        if (myTransaction) {
            commitTransaction();
        }
        return oldPlayer;
    }

    protected PlaylistSupportingPlayer buildMediaPlayer() {
        return new PlaylistSupportingPlayer(getApplicationContext());
    }

    /**
     * For running Remote Control button presses in the background.
     */
    private static class RemoteControlButtonTask implements Runnable {

        private final Intent mIntent;
        private final PlayerHaterService mService;
        private final int mRequestCode;

        RemoteControlButtonTask(Intent intent, PlayerHaterService service,
                                int requestCode) {
            mIntent = intent;
            mService = service;
            mRequestCode = requestCode;
        }

        @Override
        public void run() {
            int keyCode = mIntent.getIntExtra(
                    BroadcastReceiver.REMOTE_CONTROL_BUTTON, -1);
            if (keyCode != -1) {
                mService.onRemoteControlButtonPressed(keyCode);
                mService.stopSelfResult(mRequestCode);
            }
        }
    }

    private boolean selfStarted() {
        return mStarted != -1;
    }

    private void startSelf() {
        Intent intent = PlayerHater.buildServiceIntent(getApplicationContext());
        intent.putExtra(SELF_STARTER, true);
        startService(intent);
    }

    public void quit() {
        if (selfStarted()) {
            stopSelfResult(mStarted);
            mStarted = -1;
        }
    }

    private boolean isSelfStartCommand(Intent intent) {
        if (intent != null) {
            return intent.getBooleanExtra(SELF_STARTER, false);
        }
        return false;
    }

    @Override
    public int getTransportControlFlags() {
        return mTransportControlFlags;
    }
}




Java Source Code List

.AbstractPlugin.java
.AudioFocusPlugin.java
.BackgroundedPlugin.java
.BoundPlayerHater.java
.BroadcastReceiver.java
.ClientPlugin.java
.Config.java
.ExpandableNotificationPlugin.java
.HeadphoneButtonGestureHelper.java
.IPlayerHater.java
.LockScreenControlsPlugin.java
.Log.java
.MediaPlayerPool.java
.NotificationPlugin.java
.OnAudioFocusChangedListener.java
.PebblePlugin.java
.PlaybackService.java
.PlayerHaterClient.java
.PlayerHaterListenerPlugin.java
.PlayerHaterListener.java
.PlayerHaterPlugin.java
.PlayerHaterServer.java
.PlayerHaterService.java
.PlayerHater.java
.PlayerStateWatcher.java
.Player.java
.PlaylistParser.java
.PlaylistSupportingPlayer.java
.PluginCollection.java
.Receiver.java
.RemoteControlButtonReceiver.java
.RemoteSong.java
.ServerPlayerHater.java
.ServicePlayerHater.java
.SongHost.java
.SongQueue.java
.Song.java
.Songs.java
.StatelyPlayer.java
.SynchronousPlayer.java
.ThreadsafePlayerHater.java
.ThreadsafeServicePlayerHater.java
.TouchableNotificationPlugin.java
org.prx.playerhater.BroadcastReceiver.java
org.prx.playerhater.PlaybackService.java
org.prx.playerhater.PlayerHaterListener.java
org.prx.playerhater.PlayerHaterPlugin.java
org.prx.playerhater.PlayerHater.java
org.prx.playerhater.Song.java
org.prx.playerhater.broadcast.HeadphoneButtonGestureHelper.java
org.prx.playerhater.broadcast.OnAudioFocusChangedListener.java
org.prx.playerhater.broadcast.Receiver.java
org.prx.playerhater.broadcast.RemoteControlButtonReceiver.java
org.prx.playerhater.ipc.ClientPlugin.java
org.prx.playerhater.ipc.PlayerHaterClient.java
org.prx.playerhater.ipc.PlayerHaterServer.java
org.prx.playerhater.ipc.ServerPlayerHater.java
org.prx.playerhater.mediaplayer.MediaPlayerPool.java
org.prx.playerhater.mediaplayer.Player.java
org.prx.playerhater.mediaplayer.PlaylistSupportingPlayer.java
org.prx.playerhater.mediaplayer.StatelyPlayer.java
org.prx.playerhater.mediaplayer.SynchronousPlayer.java
org.prx.playerhater.plugins.AbstractPlugin.java
org.prx.playerhater.plugins.AudioFocusPlugin.java
org.prx.playerhater.plugins.BackgroundedPlugin.java
org.prx.playerhater.plugins.ExpandableNotificationPlugin.java
org.prx.playerhater.plugins.LockScreenControlsPlugin.java
org.prx.playerhater.plugins.NotificationPlugin.java
org.prx.playerhater.plugins.PebblePlugin.java
org.prx.playerhater.plugins.PlayerHaterListenerPlugin.java
org.prx.playerhater.plugins.PluginCollection.java
org.prx.playerhater.plugins.ScrubbableLockScreenControlsPlugin.java
org.prx.playerhater.plugins.TouchableNotificationPlugin.java
org.prx.playerhater.service.PlayerHaterService.java
org.prx.playerhater.service.PlayerStateWatcher.java
org.prx.playerhater.songs.RemoteSong.java
org.prx.playerhater.songs.SongHost.java
org.prx.playerhater.songs.SongQueue.java
org.prx.playerhater.songs.Songs.java
org.prx.playerhater.util.Config.java
org.prx.playerhater.util.IPlayerHater.java
org.prx.playerhater.util.Log.java
org.prx.playerhater.util.PlaylistParser.java
org.prx.playerhater.wrappers.BoundPlayerHater.java
org.prx.playerhater.wrappers.ServicePlayerHater.java
org.prx.playerhater.wrappers.ThreadsafePlayerHater.java
org.prx.playerhater.wrappers.ThreadsafeServicePlayerHater.java