From 5064ec3ac45d26957c4f1110a3473cdb181c9ed2 Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Thu, 15 Dec 2016 15:51:27 +0100 Subject: [PATCH 01/12] Fix spelling --- .../newpipe/extractor/stream_info/StreamExtractor.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/extractor/stream_info/StreamExtractor.java b/app/src/main/java/org/schabi/newpipe/extractor/stream_info/StreamExtractor.java index 66c500358..0f4eb7502 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/stream_info/StreamExtractor.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/stream_info/StreamExtractor.java @@ -37,14 +37,14 @@ public abstract class StreamExtractor { private UrlIdHandler urlIdHandler; private StreamPreviewInfoCollector previewInfoCollector; - public class ExctractorInitException extends ExtractionException { - public ExctractorInitException(String message) { + public class ExtractorInitException extends ExtractionException { + public ExtractorInitException(String message) { super(message); } - public ExctractorInitException(Throwable cause) { + public ExtractorInitException(Throwable cause) { super(cause); } - public ExctractorInitException(String message, Throwable cause) { + public ExtractorInitException(String message, Throwable cause) { super(message, cause); } } From 14623456ffebc9b81dc35c2b0944853685b537b5 Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Thu, 15 Dec 2016 15:42:44 +0100 Subject: [PATCH 02/12] Correct typos --- .../org/schabi/newpipe/extractor/stream_info/AudioStream.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/extractor/stream_info/AudioStream.java b/app/src/main/java/org/schabi/newpipe/extractor/stream_info/AudioStream.java index c345b3fa8..98eb17620 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/stream_info/AudioStream.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/stream_info/AudioStream.java @@ -31,14 +31,14 @@ public class AudioStream { this.bandwidth = bandwidth; this.sampling_rate = samplingRate; } - // reveals wether two streams are the same, but have diferent urls + // reveals whether two streams are the same, but have different urls public boolean equalStats(AudioStream cmp) { return format == cmp.format && bandwidth == cmp.bandwidth && sampling_rate == cmp.sampling_rate; } - // revelas wether two streams are equal + // reveals whether two streams are equal public boolean equals(AudioStream cmp) { return cmp != null && equalStats(cmp) && url == cmp.url; From 05cbc7891df19ffed20ca9b3760c14d38a4425b5 Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Tue, 27 Dec 2016 13:26:48 +0100 Subject: [PATCH 03/12] Typos corrected in StreamInfo --- .../newpipe/extractor/stream_info/StreamInfo.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/extractor/stream_info/StreamInfo.java b/app/src/main/java/org/schabi/newpipe/extractor/stream_info/StreamInfo.java index 23b90b083..5a1c0b740 100644 --- a/app/src/main/java/org/schabi/newpipe/extractor/stream_info/StreamInfo.java +++ b/app/src/main/java/org/schabi/newpipe/extractor/stream_info/StreamInfo.java @@ -86,8 +86,8 @@ public class StreamInfo extends AbstractStreamInfo { private static StreamInfo extractImportantData( StreamInfo streamInfo, StreamExtractor extractor) throws ExtractionException, IOException { - /* ---- importand data, withoug the video can't be displayed goes here: ---- */ - // if one of these is not available an exception is ment to be thrown directly into the frontend. + /* ---- important data, withoug the video can't be displayed goes here: ---- */ + // if one of these is not available an exception is meant to be thrown directly into the frontend. UrlIdHandler uiconv = extractor.getUrlIdHandler(); @@ -134,7 +134,7 @@ public class StreamInfo extends AbstractStreamInfo { streamInfo.audio_streams = new Vector<>(); } //todo: make this quick and dirty solution a real fallback - // same as the quick and dirty aboth + // same as the quick and dirty above try { streamInfo.audio_streams.addAll( DashMpdParser.getAudioStreams(streamInfo.dashMpdUrl)); @@ -173,9 +173,9 @@ public class StreamInfo extends AbstractStreamInfo { private static StreamInfo extractOptionalData( StreamInfo streamInfo, StreamExtractor extractor) { /* ---- optional data goes here: ---- */ - // If one of these failes, the frontend neets to handle that they are not available. - // Exceptions are therfore not thrown into the frontend, but stored into the error List, - // so the frontend can afterwads check where errors happend. + // If one of these fails, the frontend needs to handle that they are not available. + // Exceptions are therefore not thrown into the frontend, but stored into the error List, + // so the frontend can afterwards check where errors happened. try { streamInfo.thumbnail_url = extractor.getThumbnailUrl(); @@ -290,4 +290,4 @@ public class StreamInfo extends AbstractStreamInfo { public int start_position = 0; public List errors = new Vector<>(); -} \ No newline at end of file +} From 7c7129f9a1d0700de8cc15f271eb5697b7bf3da6 Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Tue, 27 Dec 2016 13:31:34 +0100 Subject: [PATCH 04/12] Notifications: Set customs views in builder --- .../java/org/schabi/newpipe/player/BackgroundPlayer.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 8ec525e77..8cc8871c6 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -339,13 +339,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare //Make notification appear on lockscreen noteBuilder.setVisibility(Notification.VISIBILITY_PUBLIC); - + noteBuilder.setCustomContentView(view); + noteBuilder.setCustomBigContentView(expandedView); note = noteBuilder.build(); - note.contentView = view; - - if (android.os.Build.VERSION.SDK_INT > 16) { - note.bigContentView = expandedView; - } return note; } From a7d734c20c2b7e98c2973f491e6a777ab0ff6f65 Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Tue, 27 Dec 2016 13:32:03 +0100 Subject: [PATCH 05/12] Ignore vim's temporary files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2b3c40d66..a68965416 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /.idea /*.iml gradle.properties +*~ From 5a127c26e6dfb8f07da6a24f349d5fda4d06617b Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Tue, 27 Dec 2016 14:37:18 +0100 Subject: [PATCH 06/12] Improve notification building/updateing * Use custom notification builder which has methods to set the artist and title and also a method to set the playing state * Update builder instead of view -> resovles deprecated warnings --- .../newpipe/player/BackgroundPlayer.java | 160 ++++++++++++------ 1 file changed, 106 insertions(+), 54 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 8cc8871c6..2824647ec 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -123,7 +123,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare private NotificationManager noteMgr; private WifiManager.WifiLock wifiLock; private Bitmap videoThumbnail; - private NotificationCompat.Builder noteBuilder; + private NoteBuilder noteBuilder; private Notification note; public PlayerThread(String src, String title, BackgroundPlayer owner) { @@ -211,24 +211,16 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare String action = intent.getAction(); //Log.i(TAG, "received broadcast action:"+action); if(action.equals(ACTION_PLAYPAUSE)) { - if(mediaPlayer.isPlaying()) { + boolean isPlaying = mediaPlayer.isPlaying(); + if(isPlaying) { mediaPlayer.pause(); - note.contentView.setImageViewResource(R.id.notificationPlayPause, R.drawable.ic_play_circle_filled_white_24dp); - if(android.os.Build.VERSION.SDK_INT >=16){ - note.bigContentView.setImageViewResource(R.id.notificationPlayPause, R.drawable.ic_play_circle_filled_white_24dp); - } - noteMgr.notify(noteID, note); - } - else { + } else { //reacquire CPU lock after auto-releasing it on pause mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); mediaPlayer.start(); - note.contentView.setImageViewResource(R.id.notificationPlayPause, R.drawable.ic_pause_white_24dp); - if(android.os.Build.VERSION.SDK_INT >=16){ - note.bigContentView.setImageViewResource(R.id.notificationPlayPause, R.drawable.ic_pause_white_24dp); - } - noteMgr.notify(noteID, note); } + noteBuilder.setIsPlaying(isPlaying); + noteMgr.notify(noteID, noteBuilder.build()); } else if(action.equals(ACTION_REWIND)) { mediaPlayer.seekTo(0); @@ -275,7 +267,11 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare private Notification buildNotification() { Notification note; Resources res = getApplicationContext().getResources(); - noteBuilder = new NotificationCompat.Builder(owner); + + /* + NotificationCompat.Action pauseButton = new NotificationCompat.Action.Builder + (R.drawable.ic_pause_white_24dp, "Pause", playPI).build(); + */ PendingIntent playPI = PendingIntent.getBroadcast(owner, noteID, new Intent(ACTION_PLAYPAUSE), PendingIntent.FLAG_UPDATE_CURRENT); @@ -283,10 +279,6 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare new Intent(ACTION_STOP), PendingIntent.FLAG_UPDATE_CURRENT); PendingIntent rewindPI = PendingIntent.getBroadcast(owner, noteID, new Intent(ACTION_REWIND), PendingIntent.FLAG_UPDATE_CURRENT); - /* - NotificationCompat.Action pauseButton = new NotificationCompat.Action.Builder - (R.drawable.ic_pause_white_24dp, "Pause", playPI).build(); - */ //build intent to return to video, on tapping notification Intent openDetailViewIntent = new Intent(getApplicationContext(), @@ -296,54 +288,114 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare openDetailViewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent openDetailView = PendingIntent.getActivity(owner, noteID, openDetailViewIntent, PendingIntent.FLAG_UPDATE_CURRENT); - + noteBuilder = new NoteBuilder(owner, playPI, stopPI, rewindPI, openDetailView); noteBuilder + .setTitle(title) + .setArtist(channelName) .setOngoing(true) .setDeleteIntent(stopPI) //doesn't fit with Notification.MediaStyle //.setProgress(vidLength, 0, false) .setSmallIcon(R.drawable.ic_play_circle_filled_white_24dp) - .setTicker( - String.format(res.getString( - R.string.background_player_time_text), title)) .setContentIntent(PendingIntent.getActivity(getApplicationContext(), noteID, openDetailViewIntent, PendingIntent.FLAG_UPDATE_CURRENT)) - .setContentIntent(openDetailView); + .setContentIntent(openDetailView) + .setCategory(Notification.CATEGORY_TRANSPORT) + //Make notification appear on lockscreen + .setVisibility(Notification.VISIBILITY_PUBLIC); - - RemoteViews view = - new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification); - view.setImageViewBitmap(R.id.notificationCover, videoThumbnail); - view.setTextViewText(R.id.notificationSongName, title); - view.setTextViewText(R.id.notificationArtist, channelName); - view.setOnClickPendingIntent(R.id.notificationStop, stopPI); - view.setOnClickPendingIntent(R.id.notificationPlayPause, playPI); - view.setOnClickPendingIntent(R.id.notificationRewind, rewindPI); - view.setOnClickPendingIntent(R.id.notificationContent, openDetailView); - - //possibly found the expandedView problem, - //but can't test it as I don't have a 5.0 device. -medavox - RemoteViews expandedView = - new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification_expanded); - expandedView.setImageViewBitmap(R.id.notificationCover, videoThumbnail); - expandedView.setTextViewText(R.id.notificationSongName, title); - expandedView.setTextViewText(R.id.notificationArtist, channelName); - expandedView.setOnClickPendingIntent(R.id.notificationStop, stopPI); - expandedView.setOnClickPendingIntent(R.id.notificationPlayPause, playPI); - expandedView.setOnClickPendingIntent(R.id.notificationRewind, rewindPI); - expandedView.setOnClickPendingIntent(R.id.notificationContent, openDetailView); - - - noteBuilder.setCategory(Notification.CATEGORY_TRANSPORT); - - //Make notification appear on lockscreen - noteBuilder.setVisibility(Notification.VISIBILITY_PUBLIC); - noteBuilder.setCustomContentView(view); - noteBuilder.setCustomBigContentView(expandedView); note = noteBuilder.build(); return note; } + + + /** + * Notification builder which works like the real builder but uses a custom view. + */ + class NoteBuilder extends NotificationCompat.Builder { + + /** + * @param context + * @inheritDoc + */ + public NoteBuilder(Context context, PendingIntent playPI, PendingIntent stopPI, + PendingIntent rewindPI, PendingIntent openDetailView) { + super(context); + setCustomContentView(createCustomContentView(playPI, stopPI, rewindPI, openDetailView)); + setCustomBigContentView(createCustomBigContentView(playPI, stopPI, rewindPI, openDetailView)); + } + + private RemoteViews createCustomBigContentView(PendingIntent playPI, + PendingIntent stopPI, + PendingIntent rewindPI, + PendingIntent openDetailView) { + //possibly found the expandedView problem, + //but can't test it as I don't have a 5.0 device. -medavox + RemoteViews expandedView = + new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification_expanded); + expandedView.setImageViewBitmap(R.id.notificationCover, videoThumbnail); + expandedView.setOnClickPendingIntent(R.id.notificationStop, stopPI); + expandedView.setOnClickPendingIntent(R.id.notificationPlayPause, playPI); + expandedView.setOnClickPendingIntent(R.id.notificationRewind, rewindPI); + expandedView.setOnClickPendingIntent(R.id.notificationContent, openDetailView); + return expandedView; + } + + private RemoteViews createCustomContentView(PendingIntent playPI, PendingIntent stopPI, + PendingIntent rewindPI, + PendingIntent openDetailView) { + RemoteViews view = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification); + view.setImageViewBitmap(R.id.notificationCover, videoThumbnail); + view.setOnClickPendingIntent(R.id.notificationStop, stopPI); + view.setOnClickPendingIntent(R.id.notificationPlayPause, playPI); + view.setOnClickPendingIntent(R.id.notificationRewind, rewindPI); + view.setOnClickPendingIntent(R.id.notificationContent, openDetailView); + return view; + } + + NoteBuilder setTitle(String title) { + setContentTitle(title); + getContentView().setTextViewText(R.id.notificationSongName, title); + getBigContentView().setTextViewText(R.id.notificationSongName, title); + setTicker(String.format(getBaseContext().getString( + R.string.background_player_time_text), title)); + return this; + } + + NoteBuilder setArtist(String artist) { + setSubText(artist); + getContentView().setTextViewText(R.id.notificationArtist, artist); + getBigContentView().setTextViewText(R.id.notificationArtist, artist); + return this; + } + + @Override + public android.support.v4.app.NotificationCompat.Builder setProgress(int max, int progress, boolean indeterminate) { + // TODO: implement + return super.setProgress(max, progress, indeterminate); + } + + @Override + public Notification build() { + + return super.build(); + } + + public void setIsPlaying(boolean isPlaying) { + RemoteViews views = getContentView(), bigViews = getBigContentView(); + int imageSrc; + if(isPlaying) { + imageSrc = R.drawable.ic_play_circle_filled_white_24dp; + } else { + imageSrc = R.drawable.ic_pause_white_24dp; + } + views.setImageViewResource(R.id.notificationPlayPause, imageSrc); + bigViews.setImageViewResource(R.id.notificationPlayPause, imageSrc); + + } + + } } } From 8021848b03530cbbf980e0182f9b4a7d302fed8d Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Tue, 27 Dec 2016 14:41:30 +0100 Subject: [PATCH 07/12] Remove note field completly Always use noteBuilder directly to generate a notification --- .../org/schabi/newpipe/player/BackgroundPlayer.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 2824647ec..0eec36b1b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -124,7 +124,6 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare private WifiManager.WifiLock wifiLock; private Bitmap videoThumbnail; private NoteBuilder noteBuilder; - private Notification note; public PlayerThread(String src, String title, BackgroundPlayer owner) { this.source = src; @@ -183,9 +182,8 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare filter.addAction(ACTION_REWIND); registerReceiver(broadcastReceiver, filter); - note = buildNotification(); - - startForeground(noteID, note); + initNotificationBuilder(); + startForeground(noteID, noteBuilder.build()); //currently decommissioned progressbar looping update code - works, but doesn't fit inside //Notification.MediaStyle Notification layout. @@ -264,7 +262,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare } } - private Notification buildNotification() { + private void initNotificationBuilder() { Notification note; Resources res = getApplicationContext().getResources(); @@ -304,10 +302,6 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare .setCategory(Notification.CATEGORY_TRANSPORT) //Make notification appear on lockscreen .setVisibility(Notification.VISIBILITY_PUBLIC); - - note = noteBuilder.build(); - - return note; } From 9494f3a299d9cdcbae47bb9cee3d63719cb7e565 Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Tue, 27 Dec 2016 14:49:59 +0100 Subject: [PATCH 08/12] Add progress bar to expanded notification --- .../newpipe/player/BackgroundPlayer.java | 25 +++++++++++++------ .../layout/player_notification_expanded.xml | 6 +++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 0eec36b1b..b5fa60610 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -349,6 +349,11 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare return view; } + /** + * Set the title of the stream + * @param title the title of the stream + * @return this builder for chaining + */ NoteBuilder setTitle(String title) { setContentTitle(title); getContentView().setTextViewText(R.id.notificationSongName, title); @@ -358,6 +363,11 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare return this; } + /** + * Set the artist of the stream + * @param artist the artist of the stream + * @return this builder for chaining + */ NoteBuilder setArtist(String artist) { setSubText(artist); getContentView().setTextViewText(R.id.notificationArtist, artist); @@ -367,16 +377,15 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare @Override public android.support.v4.app.NotificationCompat.Builder setProgress(int max, int progress, boolean indeterminate) { - // TODO: implement - return super.setProgress(max, progress, indeterminate); - } - - @Override - public Notification build() { - - return super.build(); + super.setProgress(max, progress, indeterminate); + getBigContentView().setProgressBar(R.id.playbackProgress, max, progress, indeterminate); + return this; } + /** + * Set the isPlaying state + * @param isPlaying the is playing state + */ public void setIsPlaying(boolean isPlaying) { RemoteViews views = getContentView(), bigViews = getBigContentView(); int imageSrc; diff --git a/app/src/main/res/layout/player_notification_expanded.xml b/app/src/main/res/layout/player_notification_expanded.xml index 5a8b3a9d3..8a0da2af3 100644 --- a/app/src/main/res/layout/player_notification_expanded.xml +++ b/app/src/main/res/layout/player_notification_expanded.xml @@ -40,6 +40,12 @@ android:ellipsize="marquee" android:singleLine="true" android:text="artist" /> + + Date: Tue, 27 Dec 2016 15:52:02 +0100 Subject: [PATCH 09/12] Add PlaybackState broadcast messages They can be used to retrieve the current playback * Duration * Played time * If the media player is playing --- .../newpipe/player/BackgroundPlayer.java | 241 +++++++++++++++--- 1 file changed, 209 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index b5fa60610..c5273b16a 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -14,6 +14,8 @@ import android.media.AudioManager; import android.media.MediaPlayer; import android.net.wifi.WifiManager; import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; import android.os.PowerManager; import android.support.v7.app.NotificationCompat; import android.util.Log; @@ -27,6 +29,7 @@ import org.schabi.newpipe.detail.VideoItemDetailActivity; import org.schabi.newpipe.detail.VideoItemDetailFragment; import java.io.IOException; +import java.util.Arrays; /** * 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.*/ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPreparedListener*/ { - private static final String TAG = BackgroundPlayer.class.toString(); - private static final String ACTION_STOP = TAG + ".STOP"; - private static final String ACTION_PLAYPAUSE = TAG + ".PLAYPAUSE"; - private static final String ACTION_REWIND = TAG + ".REWIND"; + private static final String TAG = "BackgroundPlayer"; + private static final String CLASSNAME = "org.schabi.newpipe.player.BackgroundPlayer"; + private static final String ACTION_STOP = CLASSNAME + ".STOP"; + 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 public static final String TITLE = "title"; @@ -114,6 +120,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare isRunning = false; } + private class PlayerThread extends Thread { MediaPlayer mediaPlayer; private String source; @@ -124,6 +131,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare private WifiManager.WifiLock wifiLock; private Bitmap videoThumbnail; private NoteBuilder noteBuilder; + private volatile boolean donePlaying = false; public PlayerThread(String src, String title, BackgroundPlayer owner) { this.source = src; @@ -133,6 +141,43 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare 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 public void run() { 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_STOP); filter.addAction(ACTION_REWIND); + filter.addAction(ACTION_PLAYBACK_STATE); registerReceiver(broadcastReceiver, filter); 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 //Notification.MediaStyle Notification layout. noteMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); - /* + //update every 2s or 4 times in the video, whichever is shorter - int sleepTime = Math.min(2000, (int)((double)vidLength/4)); - while(mediaPlayer.isPlaying()) { - noteBuilder.setProgress(vidLength, mediaPlayer.getCurrentPosition(), false); - noteMgr.notify(noteID, noteBuilder.build()); + int vidLength = mediaPlayer.getDuration(); + int sleepTime = Math.min(2000, (int)(vidLength / 4)); + while(!isDonePlaying()) { + broadcastState(); try { - Thread.sleep(sleepTime); + synchronized (this) { + wait(sleepTime); + } } catch (InterruptedException e) { - Log.d(TAG, "sleep failure"); + Log.e(TAG, "sleep failure", e); } - }*/ + } } /**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) { String action = intent.getAction(); //Log.i(TAG, "received broadcast action:"+action); - if(action.equals(ACTION_PLAYPAUSE)) { - boolean isPlaying = mediaPlayer.isPlaying(); - if(isPlaying) { - mediaPlayer.pause(); - } else { - //reacquire CPU lock after auto-releasing it on pause - mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); - mediaPlayer.start(); + switch (action) { + case ACTION_PLAYPAUSE: { + boolean isPlaying = mediaPlayer.isPlaying(); + if(isPlaying) { + mediaPlayer.pause(); + } else { + //reacquire CPU lock after auto-releasing it on pause + mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); + mediaPlayer.start(); + } + noteBuilder.setIsPlaying(isPlaying); + noteMgr.notify(noteID, noteBuilder.build()); + break; } - noteBuilder.setIsPlaying(isPlaying); - noteMgr.notify(noteID, noteBuilder.build()); - } - else if(action.equals(ACTION_REWIND)) { - mediaPlayer.seekTo(0); + case ACTION_REWIND: + mediaPlayer.seekTo(0); + synchronized (PlayerThread.this) { + PlayerThread.this.notifyAll(); + } // noteMgr.notify(noteID, note); - } - else if(action.equals(ACTION_STOP)) { - //this auto-releases CPU lock - mediaPlayer.stop(); - afterPlayCleanup(); + break; + case ACTION_STOP: + //this auto-releases CPU lock + mediaPlayer.stop(); + 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 public void onCompletion(MediaPlayer mp) { + setDonePlaying(); afterPlayCleanup(); + } } @@ -390,9 +460,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare RemoteViews views = getContentView(), bigViews = getBigContentView(); int imageSrc; if(isPlaying) { - imageSrc = R.drawable.ic_play_circle_filled_white_24dp; - } else { imageSrc = R.drawable.ic_pause_white_24dp; + } else { + imageSrc = R.drawable.ic_play_circle_filled_white_24dp; } views.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 CREATOR = new Creator() { + @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; + } + } } From f22b5157f5fd4223c8c61453df1d628d7314a849 Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Tue, 27 Dec 2016 15:58:17 +0100 Subject: [PATCH 10/12] Notify thread on play/pause --- .../java/org/schabi/newpipe/player/BackgroundPlayer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index c5273b16a..21193ac55 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -266,8 +266,9 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); mediaPlayer.start(); } - noteBuilder.setIsPlaying(isPlaying); - noteMgr.notify(noteID, noteBuilder.build()); + synchronized (PlayerThread.this) { + PlayerThread.this.notifyAll(); + } break; } case ACTION_REWIND: @@ -275,7 +276,6 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare synchronized (PlayerThread.this) { PlayerThread.this.notifyAll(); } -// noteMgr.notify(noteID, note); break; case ACTION_STOP: //this auto-releases CPU lock From ac0dff7aa1c4ccde14753618ea05e48a34a0fa10 Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Tue, 27 Dec 2016 16:17:59 +0100 Subject: [PATCH 11/12] Make shure thread quits before service --- .../newpipe/player/BackgroundPlayer.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java index 21193ac55..eba1d57f2 100644 --- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java +++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java @@ -161,18 +161,20 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare } } - private PlaybackState getPlaybackState() { + private synchronized 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 + Log.w(TAG, this + ": Got illegal state exception while creating playback state", e); return PlaybackState.UNPREPARED; } } private void broadcastState() { PlaybackState state = getPlaybackState(); + if(state == null) return; Intent intent = new Intent(ACTION_PLAYBACK_STATE); intent.putExtra(EXTRA_PLAYBACK_STATE, state); sendBroadcast(intent); @@ -284,9 +286,6 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare 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()); @@ -301,6 +300,8 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare }; private void afterPlayCleanup() { + // Notify thread to stop + setDonePlaying(); //remove progress bar //noteBuilder.setProgress(0, 0, false); @@ -314,7 +315,12 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare wifiLock.release(); //remove foreground status of service; make BackgroundPlayer killable stopForeground(true); - + try { + // Wait for thread to stop + PlayerThread.this.join(); + } catch (InterruptedException e) { + Log.e(TAG, "unable to join player thread", e); + } stopSelf(); } @@ -326,9 +332,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare @Override public void onCompletion(MediaPlayer mp) { - setDonePlaying(); afterPlayCleanup(); - } } From 83541a0d5d56838aacf5056e9dac0cebfaea2e0d Mon Sep 17 00:00:00 2001 From: Coffeemakr Date: Wed, 28 Dec 2016 08:15:12 +0100 Subject: [PATCH 12/12] Add 8dp margin on the right side of the progress bar --- app/src/main/res/layout/player_notification_expanded.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/player_notification_expanded.xml b/app/src/main/res/layout/player_notification_expanded.xml index 8a0da2af3..0fb6a8eb2 100644 --- a/app/src/main/res/layout/player_notification_expanded.xml +++ b/app/src/main/res/layout/player_notification_expanded.xml @@ -20,7 +20,7 @@ android:layout_above="@+id/notificationButtons" android:layout_toRightOf="@+id/notificationCover" android:gravity="center_vertical" - android:orientation="vertical" > + android:orientation="vertical"> + style="@style/Widget.AppCompat.ProgressBar.Horizontal" + android:layout_marginRight="8dp" />