Add PlaybackState broadcast messages
They can be used to retrieve the current playback * Duration * Played time * If the media player is playing
This commit is contained in:
parent
9494f3a299
commit
659d0d6115
|
@ -14,6 +14,8 @@ import android.media.AudioManager;
|
||||||
import android.media.MediaPlayer;
|
import android.media.MediaPlayer;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.support.v7.app.NotificationCompat;
|
import android.support.v7.app.NotificationCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
@ -27,6 +29,7 @@ import org.schabi.newpipe.detail.VideoItemDetailActivity;
|
||||||
import org.schabi.newpipe.detail.VideoItemDetailFragment;
|
import org.schabi.newpipe.detail.VideoItemDetailFragment;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by Adam Howard on 08/11/15.
|
* Created by Adam Howard on 08/11/15.
|
||||||
|
@ -51,10 +54,13 @@ import java.io.IOException;
|
||||||
/**Plays the audio stream of videos in the background.*/
|
/**Plays the audio stream of videos in the background.*/
|
||||||
public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPreparedListener*/ {
|
public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPreparedListener*/ {
|
||||||
|
|
||||||
private static final String TAG = BackgroundPlayer.class.toString();
|
private static final String TAG = "BackgroundPlayer";
|
||||||
private static final String ACTION_STOP = TAG + ".STOP";
|
private static final String CLASSNAME = "org.schabi.newpipe.player.BackgroundPlayer";
|
||||||
private static final String ACTION_PLAYPAUSE = TAG + ".PLAYPAUSE";
|
private static final String ACTION_STOP = CLASSNAME + ".STOP";
|
||||||
private static final String ACTION_REWIND = TAG + ".REWIND";
|
private static final String ACTION_PLAYPAUSE = CLASSNAME + ".PLAYPAUSE";
|
||||||
|
private static final String ACTION_REWIND = CLASSNAME + ".REWIND";
|
||||||
|
private static final String ACTION_PLAYBACK_STATE = CLASSNAME + ".PLAYBACK_STATE";
|
||||||
|
private static final String EXTRA_PLAYBACK_STATE = CLASSNAME + ".extras.EXTRA_PLAYBACK_STATE";
|
||||||
|
|
||||||
// Extra intent arguments
|
// Extra intent arguments
|
||||||
public static final String TITLE = "title";
|
public static final String TITLE = "title";
|
||||||
|
@ -114,6 +120,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class PlayerThread extends Thread {
|
private class PlayerThread extends Thread {
|
||||||
MediaPlayer mediaPlayer;
|
MediaPlayer mediaPlayer;
|
||||||
private String source;
|
private String source;
|
||||||
|
@ -124,6 +131,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
private WifiManager.WifiLock wifiLock;
|
private WifiManager.WifiLock wifiLock;
|
||||||
private Bitmap videoThumbnail;
|
private Bitmap videoThumbnail;
|
||||||
private NoteBuilder noteBuilder;
|
private NoteBuilder noteBuilder;
|
||||||
|
private volatile boolean donePlaying = false;
|
||||||
|
|
||||||
public PlayerThread(String src, String title, BackgroundPlayer owner) {
|
public PlayerThread(String src, String title, BackgroundPlayer owner) {
|
||||||
this.source = src;
|
this.source = src;
|
||||||
|
@ -133,6 +141,43 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDonePlaying() {
|
||||||
|
return donePlaying;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPlaying() {
|
||||||
|
try {
|
||||||
|
return mediaPlayer.isPlaying();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
Log.w(TAG, "Unable to retrieve playing state", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDonePlaying() {
|
||||||
|
donePlaying = true;
|
||||||
|
synchronized (PlayerThread.this) {
|
||||||
|
PlayerThread.this.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlaybackState getPlaybackState() {
|
||||||
|
try {
|
||||||
|
return new PlaybackState(mediaPlayer.getDuration(), mediaPlayer.getCurrentPosition(), isPlaying());
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// This isn't that nice way to handle this.
|
||||||
|
// maybe there is a better way
|
||||||
|
return PlaybackState.UNPREPARED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void broadcastState() {
|
||||||
|
PlaybackState state = getPlaybackState();
|
||||||
|
Intent intent = new Intent(ACTION_PLAYBACK_STATE);
|
||||||
|
intent.putExtra(EXTRA_PLAYBACK_STATE, state);
|
||||||
|
sendBroadcast(intent);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock
|
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock
|
||||||
|
@ -180,6 +225,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
filter.addAction(ACTION_PLAYPAUSE);
|
filter.addAction(ACTION_PLAYPAUSE);
|
||||||
filter.addAction(ACTION_STOP);
|
filter.addAction(ACTION_STOP);
|
||||||
filter.addAction(ACTION_REWIND);
|
filter.addAction(ACTION_REWIND);
|
||||||
|
filter.addAction(ACTION_PLAYBACK_STATE);
|
||||||
registerReceiver(broadcastReceiver, filter);
|
registerReceiver(broadcastReceiver, filter);
|
||||||
|
|
||||||
initNotificationBuilder();
|
initNotificationBuilder();
|
||||||
|
@ -188,18 +234,20 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
//currently decommissioned progressbar looping update code - works, but doesn't fit inside
|
//currently decommissioned progressbar looping update code - works, but doesn't fit inside
|
||||||
//Notification.MediaStyle Notification layout.
|
//Notification.MediaStyle Notification layout.
|
||||||
noteMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
noteMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
|
||||||
/*
|
|
||||||
//update every 2s or 4 times in the video, whichever is shorter
|
//update every 2s or 4 times in the video, whichever is shorter
|
||||||
int sleepTime = Math.min(2000, (int)((double)vidLength/4));
|
int vidLength = mediaPlayer.getDuration();
|
||||||
while(mediaPlayer.isPlaying()) {
|
int sleepTime = Math.min(2000, (int)(vidLength / 4));
|
||||||
noteBuilder.setProgress(vidLength, mediaPlayer.getCurrentPosition(), false);
|
while(!isDonePlaying()) {
|
||||||
noteMgr.notify(noteID, noteBuilder.build());
|
broadcastState();
|
||||||
try {
|
try {
|
||||||
Thread.sleep(sleepTime);
|
synchronized (this) {
|
||||||
|
wait(sleepTime);
|
||||||
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
Log.d(TAG, "sleep failure");
|
Log.e(TAG, "sleep failure", e);
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**Handles button presses from the notification. */
|
/**Handles button presses from the notification. */
|
||||||
|
@ -208,26 +256,46 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
String action = intent.getAction();
|
String action = intent.getAction();
|
||||||
//Log.i(TAG, "received broadcast action:"+action);
|
//Log.i(TAG, "received broadcast action:"+action);
|
||||||
if(action.equals(ACTION_PLAYPAUSE)) {
|
switch (action) {
|
||||||
boolean isPlaying = mediaPlayer.isPlaying();
|
case ACTION_PLAYPAUSE: {
|
||||||
if(isPlaying) {
|
boolean isPlaying = mediaPlayer.isPlaying();
|
||||||
mediaPlayer.pause();
|
if(isPlaying) {
|
||||||
} else {
|
mediaPlayer.pause();
|
||||||
//reacquire CPU lock after auto-releasing it on pause
|
} else {
|
||||||
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
|
//reacquire CPU lock after auto-releasing it on pause
|
||||||
mediaPlayer.start();
|
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
|
||||||
|
mediaPlayer.start();
|
||||||
|
}
|
||||||
|
noteBuilder.setIsPlaying(isPlaying);
|
||||||
|
noteMgr.notify(noteID, noteBuilder.build());
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
noteBuilder.setIsPlaying(isPlaying);
|
case ACTION_REWIND:
|
||||||
noteMgr.notify(noteID, noteBuilder.build());
|
mediaPlayer.seekTo(0);
|
||||||
}
|
synchronized (PlayerThread.this) {
|
||||||
else if(action.equals(ACTION_REWIND)) {
|
PlayerThread.this.notifyAll();
|
||||||
mediaPlayer.seekTo(0);
|
}
|
||||||
// noteMgr.notify(noteID, note);
|
// noteMgr.notify(noteID, note);
|
||||||
}
|
break;
|
||||||
else if(action.equals(ACTION_STOP)) {
|
case ACTION_STOP:
|
||||||
//this auto-releases CPU lock
|
//this auto-releases CPU lock
|
||||||
mediaPlayer.stop();
|
mediaPlayer.stop();
|
||||||
afterPlayCleanup();
|
afterPlayCleanup();
|
||||||
|
break;
|
||||||
|
case ACTION_PLAYBACK_STATE: {
|
||||||
|
PlaybackState playbackState = intent.getParcelableExtra(EXTRA_PLAYBACK_STATE);
|
||||||
|
Log.d(TAG, "playback state recieved: " + playbackState);
|
||||||
|
Log.d(TAG, "is unprepared: " + playbackState.equals(PlaybackState.UNPREPARED));
|
||||||
|
Log.d(TAG, "playing: " + playbackState.getPlayedTime());
|
||||||
|
if(!playbackState.equals(PlaybackState.UNPREPARED)) {
|
||||||
|
noteBuilder.setProgress(playbackState.getDuration(), playbackState.getPlayedTime(), false);
|
||||||
|
noteBuilder.setIsPlaying(playbackState.isPlaying());
|
||||||
|
} else {
|
||||||
|
noteBuilder.setProgress(0, 0, true);
|
||||||
|
}
|
||||||
|
noteMgr.notify(noteID, noteBuilder.build());
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -258,7 +326,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCompletion(MediaPlayer mp) {
|
public void onCompletion(MediaPlayer mp) {
|
||||||
|
setDonePlaying();
|
||||||
afterPlayCleanup();
|
afterPlayCleanup();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,9 +460,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
RemoteViews views = getContentView(), bigViews = getBigContentView();
|
RemoteViews views = getContentView(), bigViews = getBigContentView();
|
||||||
int imageSrc;
|
int imageSrc;
|
||||||
if(isPlaying) {
|
if(isPlaying) {
|
||||||
imageSrc = R.drawable.ic_play_circle_filled_white_24dp;
|
|
||||||
} else {
|
|
||||||
imageSrc = R.drawable.ic_pause_white_24dp;
|
imageSrc = R.drawable.ic_pause_white_24dp;
|
||||||
|
} else {
|
||||||
|
imageSrc = R.drawable.ic_play_circle_filled_white_24dp;
|
||||||
}
|
}
|
||||||
views.setImageViewResource(R.id.notificationPlayPause, imageSrc);
|
views.setImageViewResource(R.id.notificationPlayPause, imageSrc);
|
||||||
bigViews.setImageViewResource(R.id.notificationPlayPause, imageSrc);
|
bigViews.setImageViewResource(R.id.notificationPlayPause, imageSrc);
|
||||||
|
@ -401,4 +471,111 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the state of the player.
|
||||||
|
*/
|
||||||
|
public static class PlaybackState implements Parcelable {
|
||||||
|
|
||||||
|
private static final int INDEX_IS_PLAYING = 0;
|
||||||
|
private static final int INDEX_IS_PREPARED= 1;
|
||||||
|
private static final int INDEX_HAS_ERROR = 2;
|
||||||
|
private final int duration;
|
||||||
|
private final int played;
|
||||||
|
private final boolean[] booleanValues = new boolean[3];
|
||||||
|
|
||||||
|
static final PlaybackState UNPREPARED = new PlaybackState(false, false, false);
|
||||||
|
static final PlaybackState FAILED = new PlaybackState(false, false, true);
|
||||||
|
|
||||||
|
|
||||||
|
PlaybackState(Parcel in) {
|
||||||
|
duration = in.readInt();
|
||||||
|
played = in.readInt();
|
||||||
|
in.readBooleanArray(booleanValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlaybackState(int duration, int played, boolean isPlaying) {
|
||||||
|
this.played = played;
|
||||||
|
this.duration = duration;
|
||||||
|
this.booleanValues[INDEX_IS_PLAYING] = isPlaying;
|
||||||
|
this.booleanValues[INDEX_IS_PREPARED] = true;
|
||||||
|
this.booleanValues[INDEX_HAS_ERROR] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private PlaybackState(boolean isPlaying, boolean isPrepared, boolean hasErrors) {
|
||||||
|
this.played = 0;
|
||||||
|
this.duration = 0;
|
||||||
|
this.booleanValues[INDEX_IS_PLAYING] = isPlaying;
|
||||||
|
this.booleanValues[INDEX_IS_PREPARED] = isPrepared;
|
||||||
|
this.booleanValues[INDEX_HAS_ERROR] = hasErrors;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDuration() {
|
||||||
|
return duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPlayedTime() {
|
||||||
|
return played;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isPlaying() {
|
||||||
|
return booleanValues[INDEX_IS_PLAYING];
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isPrepared() {
|
||||||
|
return booleanValues[INDEX_IS_PREPARED];
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasErrors() {
|
||||||
|
return booleanValues[INDEX_HAS_ERROR];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(duration);
|
||||||
|
dest.writeInt(played);
|
||||||
|
dest.writeBooleanArray(booleanValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<PlaybackState> CREATOR = new Creator<PlaybackState>() {
|
||||||
|
@Override
|
||||||
|
public PlaybackState createFromParcel(Parcel in) {
|
||||||
|
return new PlaybackState(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PlaybackState[] newArray(int size) {
|
||||||
|
return new PlaybackState[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
|
PlaybackState that = (PlaybackState) o;
|
||||||
|
|
||||||
|
if (duration != that.duration) return false;
|
||||||
|
if (played != that.played) return false;
|
||||||
|
return Arrays.equals(booleanValues, that.booleanValues);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
if(this == UNPREPARED) return 1;
|
||||||
|
if(this == FAILED) return 2;
|
||||||
|
int result = duration;
|
||||||
|
result = 31 * result + played;
|
||||||
|
result = 31 * result + Arrays.hashCode(booleanValues);
|
||||||
|
return result + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue