Android Open Source - EnklawaPlayer Player Service






From Project

Back to project page EnklawaPlayer.

License

The source code is released under:

GNU General Public License

If you think the Android project EnklawaPlayer 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

package macbury.pod.services;
/*  w  ww .  j ava  2 s.co  m*/
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.RemoteControlClient;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.Toast;

import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.ion.Ion;

import macbury.pod.R;
import macbury.pod.db.models.EnqueueEpisode;
import macbury.pod.db.models.Episode;
import macbury.pod.managers.App;
import macbury.pod.managers.player.PlayerManager;
import macbury.pod.managers.player.PlayerManagerListener;
import macbury.pod.managers.player.sources.AbstractMediaSource;
import macbury.pod.managers.player.sources.EpisodeMediaSource;
import macbury.pod.managers.player.sources.RadioMediaSource;
import macbury.pod.receivers.MediaButtonReceiver;

public class PlayerService extends Service implements PlayerManagerListener, AudioManager.OnAudioFocusChangeListener {
  private static final String AVRCP_ACTION_PLAYER_STATUS_CHANGED  = "com.android.music.playstatechanged";
  private static final String AVRCP_ACTION_META_CHANGED           = "com.android.music.metachanged";
  private static final String TAG                                 = "PlayerService";
  private static final int NOTIFICATION_PLAYED_ID                 = 689;
  private static final String WIFI_LOCK_TAG                       = "EnklawaPlayerService";
  private RemoteControlClient remoteControlClient;
  private App app;
  private PlayerManager playerManager;
  private final IBinder playerManagerBinder = new PlayerBinder();
  private Bitmap currentBitmapArt;
  private WifiManager.WifiLock wifiLock;
  private static boolean running;
  private AudioManager audioManager;
  private ComponentName mediaButtonEventReceiver;
  private boolean havePlayedBeforeLostFocus;

  public PlayerService() {
  }

  @Override
  public void onCreate() {
    super.onCreate();
    this.audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
    this.wifiLock     = ((WifiManager) getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL, WIFI_LOCK_TAG);

    this.app           = App.current();
    this.playerManager = new PlayerManager(getApplicationContext());
    playerManager.addListener(this);

    running = true;
    app.broadcasts.playerStatusChanged();

    if (audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
      Toast.makeText(this, R.string.media_player_error_audio_focus_not_granted, Toast.LENGTH_LONG).show();
      stopSelf();
      return;
    }

    setupRemoteControlClient();

    registerReceiver(headsetDisconnected, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
    registerReceiver(audioBecomingNoisy, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
  }

  private void setupRemoteControlClient() {
    Intent mediaButtonIntent         = new Intent(Intent.ACTION_MEDIA_BUTTON);
    this.mediaButtonEventReceiver    = new ComponentName(getPackageName(), MediaButtonReceiver.class.getName());
    mediaButtonIntent.setComponent(mediaButtonEventReceiver);

    PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent, 0);
    remoteControlClient              = new RemoteControlClient(mediaPendingIntent);
    remoteControlClient.setTransportControlFlags(RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE);

    audioManager.registerMediaButtonEventReceiver(mediaButtonEventReceiver);
    audioManager.registerRemoteControlClient(remoteControlClient);
  }

  private void refreshRemoteControlClient(int playstate) {
    if (playerManager.isRunning()) {
      remoteControlClient.setPlaybackState(playstate);
      RemoteControlClient.MetadataEditor editor = remoteControlClient.editMetadata(false);
      AbstractMediaSource ams                   = playerManager.getCurrentMediaSource();

      editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, ams.getTitle());
      editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, ams.getSummary());
      //editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, ams.getDuration());
      //editor.putLong(MediaMetadataRetriever.METADATA_KEY_LOCATION, ams.getPosition());
      editor.apply();

      bluetoothNotifyChange(ams, AVRCP_ACTION_PLAYER_STATUS_CHANGED);
      bluetoothNotifyChange(ams, AVRCP_ACTION_META_CHANGED);
    }
  }

  private void bluetoothNotifyChange(AbstractMediaSource ams, String whatChanged) {
    if (ams != null) {
      Intent i = new Intent(whatChanged);
      i.putExtra("id", 1);
      i.putExtra("artist", "");
      i.putExtra("album", ams.getSummary());
      i.putExtra("track", ams.getTitle());
      i.putExtra("playing", playerManager.isPlaying());
      i.putExtra("ListSize", App.current().db.queue.count());
      i.putExtra("duration", ams.getDuration());
      i.putExtra("position", ams.getPosition());
      sendBroadcast(i);
    }
  }

  @Override
  public void onDestroy() {
    playerManager.removeListener(this);
    playerManager.destroy();
    running = false;
    if (wifiLock.isHeld())
      wifiLock.release();
    unregisterReceiver(headsetDisconnected);
    unregisterReceiver(audioBecomingNoisy);
    audioManager.unregisterMediaButtonEventReceiver(mediaButtonEventReceiver);
    audioManager.abandonAudioFocus(this);
    super.onDestroy();
  }

  @Override
  public IBinder onBind(Intent intent) {
    return playerManagerBinder;
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    if (app.intents.haveKeyCode(intent)) {
      int keycode = app.intents.getKeyCode(intent);
      handleKeycode(keycode);
    } else if (app.intents.havePauseExtra(intent)) {
      playerManager.pause();
    } else if (app.intents.haveCancelExtra(intent)) {
      playerManager.cancel();
    } else if (app.intents.haveRadioExtra(intent)) {
      RadioMediaSource rms = new RadioMediaSource();
      if (!playerManager.isRunning(rms)) {
        playerManager.pause();
        playerManager.set(rms);
      }
    } else if (app.intents.haveEpisode(intent)) {
      Episode episode               = app.db.episodes.find(app.intents.getEpisodeId(intent));
      EnqueueEpisode enqueueEpisode = app.db.queue.createFromEpisode(episode);
      Log.i(TAG, "Recived episode to play:" + episode.name);
      if (playerManager.isRunning(enqueueEpisode)) {
        playerManager.start();
      } else {
        app.db.queue.moveToBegining(enqueueEpisode);
        playerManager.restart();
      }
    }
    return super.onStartCommand(intent, flags, startId);
  }

  private void handleKeycode(int keycode) {
    switch (keycode) {
      case KeyEvent.KEYCODE_HEADSETHOOK:
      case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
        if (playerManager.isPlaying()) {
          playerManager.pause();
        } else if (playerManager.isPaused()) {
          playerManager.start();
        }
      break;

      case KeyEvent.KEYCODE_MEDIA_PLAY:
        playerManager.start();
      break;

      case KeyEvent.KEYCODE_MEDIA_PAUSE:
        playerManager.pause();
      break;

      default:
        Toast.makeText(this, getString(R.string.unknown_media_key), Toast.LENGTH_SHORT).show();
      break;
    }
  }

  @Override
  public void onInitialize(PlayerManager manager, AbstractMediaSource mediaSource) {
    refreshRemoteControlClient(RemoteControlClient.PLAYSTATE_BUFFERING);
    Ion.with(this).load(mediaSource.getPreviewArtUri().toString()).asBitmap().setCallback(new FutureCallback<Bitmap>() {
      @Override
      public void onCompleted(Exception e, Bitmap result) {
        PlayerService.this.currentBitmapArt = result;
        updateNotification();
      }
    });
    updateNotification();
  }

  private void updateNotification() {
    EpisodeMediaSource ems = (EpisodeMediaSource) playerManager.getCurrentMediaSource();
    startForeground(NOTIFICATION_PLAYED_ID, App.current().notifications.playEpisode(currentBitmapArt, ems.getEnqueueEpisode()));
  }

  @Override
  public void onFinishAll(PlayerManager manager) {
    Log.i(TAG, "On finish all");
    stopForeground(true);
    stopSelf();
  }

  @Override
  public void onPlay(PlayerManager manager, AbstractMediaSource mediaSource) {
    if (!wifiLock.isHeld())
      wifiLock.acquire();
    refreshRemoteControlClient(RemoteControlClient.PLAYSTATE_PLAYING);
    updateNotification();
  }

  @Override
  public void onPause(PlayerManager manager, AbstractMediaSource mediaSource) {
    refreshRemoteControlClient(RemoteControlClient.PLAYSTATE_PAUSED);
    updateNotification();
  }

  @Override
  public void onFinish(PlayerManager manager, AbstractMediaSource mediaSource) {
    updateNotification();
    wifiLock.release();
  }

  @Override
  public void onBufferMedia(PlayerManager manager, AbstractMediaSource mediaSource) {

  }

  @Override
  public void onMediaUpdate(PlayerManager playerManager, AbstractMediaSource currentMediaSource) {

  }

  @Override
  public void onMediaError(PlayerManager playerManager, int extra) {
    switch (extra) {
      case MediaPlayer.MEDIA_ERROR_MALFORMED:
      case MediaPlayer.MEDIA_ERROR_IO:
        Toast.makeText(this, R.string.media_player_error_io, Toast.LENGTH_LONG).show();
      break;

      case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:
        Toast.makeText(this, R.string.media_player_error_unsupported, Toast.LENGTH_LONG).show();
      break;

      case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
        Toast.makeText(this, R.string.media_player_error_timeout, Toast.LENGTH_LONG).show();
      break;

      default:
        Toast.makeText(this, R.string.media_player_error_unknown, Toast.LENGTH_LONG).show();
      break;
    }
  }

  @Override
  public void onAudioFocusChange(int focusChange) {
    if  (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
      playerManager.lowerVolume();
    } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
      playerManager.normalVolume();
      if (havePlayedBeforeLostFocus) {
        playerManager.play();
        havePlayedBeforeLostFocus = false;
      }
    } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
      havePlayedBeforeLostFocus = playerManager.isPlaying();
      playerManager.pause();
    } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
      playerManager.pauseAndExit();
    }
  }

  public class PlayerBinder extends Binder {
    public PlayerManager getPlayerManager() {
      return PlayerService.this.playerManager;
    }

    public AbstractMediaSource getCurrentMediaSource() {
      return playerManager.getCurrentMediaSource();
    }

    public void addListener(PlayerManagerListener listener) {
      playerManager.addListener(listener);
    }

    public void removeListener(PlayerManagerListener listener) {
      playerManager.removeListener(listener);
    }

    public boolean isPlaying() {
      return playerManager.isPlaying();
    }
  }

  public static boolean isRunning() {
    return running;
  }

  private BroadcastReceiver headsetDisconnected = new BroadcastReceiver() {
    private static final String TAG = "headsetDisconnected";
    private static final int UNPLUGGED = 0;
    @Override
    public void onReceive(Context context, Intent intent) {
      if (intent.getAction() == Intent.ACTION_HEADSET_PLUG) {
        int state = intent.getIntExtra("state", -1);
        if (state != -1) {
          Log.d(TAG, "Headset plug event. State is " + state);
          if (state == UNPLUGGED) {
            Log.d(TAG, "Headset was unplugged during playback.");
            playerManager.pause();
          }
        } else {
          Log.e(TAG, "Received invalid ACTION_HEADSET_PLUG intent");
        }
      }
    }
  };

  private BroadcastReceiver audioBecomingNoisy = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
      Log.d(TAG, "Pausing playback because audio is becoming noisy");
      playerManager.pause();
    }
  };

}




Java Source Code List

macbury.pod.ApplicationTest.java
macbury.pod.activities.DonateActivity.java
macbury.pod.activities.ProgramEpisodesActivity.java
macbury.pod.activities.SettingsActivity.java
macbury.pod.activities.ext.AccentFragmentActivity.java
macbury.pod.activities.main.MainActivityActionBarToggle.java
macbury.pod.activities.main.MainActivity.java
macbury.pod.activities.main.NavigationController.java
macbury.pod.activities.main.NavigationListener.java
macbury.pod.activities.player.PlayerActivity.java
macbury.pod.activities.player.RadioActivity.java
macbury.pod.adapters.EpisodesAdapterListener.java
macbury.pod.adapters.EpisodesAdapter.java
macbury.pod.adapters.ProgramsAdapter.java
macbury.pod.api.APICategory.java
macbury.pod.api.APIEpisode.java
macbury.pod.api.APIProgram.java
macbury.pod.api.APIResponse.java
macbury.pod.api.APIThread.java
macbury.pod.db.DatabaseCRUDListener.java
macbury.pod.db.models.EnqueueEpisode.java
macbury.pod.db.models.EpisodeFile.java
macbury.pod.db.models.Episode.java
macbury.pod.db.models.ForumThread.java
macbury.pod.db.models.Program.java
macbury.pod.db.scopes.AbstractScope.java
macbury.pod.db.scopes.EnqueueEpisodeScope.java
macbury.pod.db.scopes.EpisodeFilesScope.java
macbury.pod.db.scopes.EpisodesScope.java
macbury.pod.db.scopes.ProgramsScope.java
macbury.pod.db.scopes.ThreadScope.java
macbury.pod.dialogs.EpisodeAboutDialog.java
macbury.pod.dialogs.ProgramAboutDialog.java
macbury.pod.extensions.Converter.java
macbury.pod.extensions.DateDeserializer.java
macbury.pod.extensions.SleepTimer.java
macbury.pod.fragments.SettingsFragment.java
macbury.pod.fragments.main.AbstractEpisodesFragment.java
macbury.pod.fragments.main.AllProgramsFragment.java
macbury.pod.fragments.main.EnklawaBaseAbstractListFragment.java
macbury.pod.fragments.main.episodes.DownloadedEpisodesFragment.java
macbury.pod.fragments.main.episodes.NewestEpisodesFragment.java
macbury.pod.fragments.main.episodes.PlaylistFragment.java
macbury.pod.fragments.main.episodes.ProgramEpisodesFragment.java
macbury.pod.fragments.player.ExternalPlayerFragment.java
macbury.pod.fragments.player.PlayerArtworkAndInfoFragment.java
macbury.pod.fragments.player.PlayerControllerFragment.java
macbury.pod.managers.AlarmsManager.java
macbury.pod.managers.App.java
macbury.pod.managers.BroadcastsManager.java
macbury.pod.managers.DatabaseManager.java
macbury.pod.managers.IntentManager.java
macbury.pod.managers.NotificationsManager.java
macbury.pod.managers.ServiceManager.java
macbury.pod.managers.SettingsManager.java
macbury.pod.managers.StorageManager.java
macbury.pod.managers.download.DownloadEpisode.java
macbury.pod.managers.download.DownloadManagerListener.java
macbury.pod.managers.download.DownloadManager.java
macbury.pod.managers.player.PlaybackStatus.java
macbury.pod.managers.player.PlayerManagerListener.java
macbury.pod.managers.player.PlayerManager.java
macbury.pod.managers.player.sources.AbstractMediaSource.java
macbury.pod.managers.player.sources.EpisodeMediaSource.java
macbury.pod.managers.player.sources.RadioMediaSource.java
macbury.pod.navigation_drawer.NavAdapter.java
macbury.pod.navigation_drawer.NavBaseItem.java
macbury.pod.navigation_drawer.NavDivider.java
macbury.pod.navigation_drawer.items.AllProgramsNavItem.java
macbury.pod.navigation_drawer.items.DownloadedEpisodesNavItem.java
macbury.pod.navigation_drawer.items.FavoriteProgramNavItem.java
macbury.pod.navigation_drawer.items.ForumNavItem.java
macbury.pod.navigation_drawer.items.NavItemFragment.java
macbury.pod.navigation_drawer.items.NewestEpisodesNavItem.java
macbury.pod.navigation_drawer.items.PlayQueueNavItem.java
macbury.pod.receivers.BootReceiver.java
macbury.pod.receivers.MediaButtonReceiver.java
macbury.pod.services.DownloadService.java
macbury.pod.services.PlayerService.java
macbury.pod.services.SyncPodService.java
macbury.pod.tutorial.MainActivityTutorial.java
macbury.pod.views.TappableFrameLayout.java