diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 02b3265a0..4f97a7201 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -40,9 +40,14 @@
+
+
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 5eacb5d05..e4f02f578 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayer.java
@@ -48,6 +48,7 @@ import org.schabi.newpipe.R;
import org.schabi.newpipe.extractor.MediaFormat;
import org.schabi.newpipe.extractor.stream.AudioStream;
import org.schabi.newpipe.extractor.stream.StreamInfo;
+import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.util.ListHelper;
import org.schabi.newpipe.util.ThemeHelper;
@@ -80,13 +81,6 @@ public final class BackgroundPlayer extends Service {
// Service-Activity Binder
//////////////////////////////////////////////////////////////////////////*/
- public interface PlayerEventListener {
- void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters);
- void onProgressUpdate(int currentProgress, int duration, int bufferPercent);
- void onMetadataUpdate(StreamInfo info);
- void onServiceStopped();
- }
-
private PlayerEventListener activityListener;
private IBinder mBinder;
@@ -149,7 +143,9 @@ public final class BackgroundPlayer extends Service {
public void openControl(final Context context) {
final Intent intent = new Intent(context, BackgroundPlayerActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
context.startActivity(intent);
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java
index 62abe81b5..bfd066885 100644
--- a/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/player/BackgroundPlayerActivity.java
@@ -1,535 +1,46 @@
package org.schabi.newpipe.player;
-import android.content.ComponentName;
import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Build;
-import android.os.Bundle;
import android.os.IBinder;
-import android.provider.Settings;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.Toolbar;
-import android.support.v7.widget.helper.ItemTouchHelper;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ImageButton;
-import android.widget.PopupMenu;
-import android.widget.ProgressBar;
-import android.widget.SeekBar;
-import android.widget.TextView;
-
-import com.google.android.exoplayer2.PlaybackParameters;
-import com.google.android.exoplayer2.Player;
import org.schabi.newpipe.R;
-import org.schabi.newpipe.extractor.stream.StreamInfo;
-import org.schabi.newpipe.playlist.PlayQueueItem;
-import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
-import org.schabi.newpipe.playlist.PlayQueueItemHolder;
-import org.schabi.newpipe.util.Localization;
-import org.schabi.newpipe.util.NavigationHelper;
-import org.schabi.newpipe.util.ThemeHelper;
-public class BackgroundPlayerActivity extends AppCompatActivity
- implements BackgroundPlayer.PlayerEventListener, SeekBar.OnSeekBarChangeListener, View.OnClickListener {
+public final class BackgroundPlayerActivity extends ServicePlayerActivity {
private static final String TAG = "BGPlayerActivity";
- private boolean serviceBound;
- private ServiceConnection serviceConnection;
-
- private BackgroundPlayer.BasePlayerImpl player;
-
- private boolean seeking;
-
- ////////////////////////////////////////////////////////////////////////////
- // Views
- ////////////////////////////////////////////////////////////////////////////
-
- private static final int RECYCLER_ITEM_POPUP_MENU_GROUP_ID = 47;
- private static final int PLAYBACK_SPEED_POPUP_MENU_GROUP_ID = 61;
- private static final int PLAYBACK_PITCH_POPUP_MENU_GROUP_ID = 97;
-
- private View rootView;
-
- private RecyclerView itemsList;
- private ItemTouchHelper itemTouchHelper;
-
- private TextView metadataTitle;
- private TextView metadataArtist;
-
- private SeekBar progressSeekBar;
- private TextView progressCurrentTime;
- private TextView progressEndTime;
-
- private ImageButton repeatButton;
- private ImageButton backwardButton;
- private ImageButton playPauseButton;
- private ImageButton forwardButton;
- private ImageButton shuffleButton;
- private ProgressBar progressBar;
-
- private TextView playbackSpeedButton;
- private PopupMenu playbackSpeedPopupMenu;
- private TextView playbackPitchButton;
- private PopupMenu playbackPitchPopupMenu;
-
- ////////////////////////////////////////////////////////////////////////////
- // Activity Lifecycle
- ////////////////////////////////////////////////////////////////////////////
-
@Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- ThemeHelper.setTheme(this);
- setContentView(R.layout.activity_background_player);
- rootView = findViewById(R.id.main_content);
-
- final Toolbar toolbar = rootView.findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
- if (getSupportActionBar() != null) {
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setTitle(R.string.title_activity_background_player);
- }
-
- serviceConnection = backgroundPlayerConnection();
- bind();
+ public String getTag() {
+ return TAG;
}
@Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.menu_play_queue, menu);
- return true;
+ public String getSupportActionTitle() {
+ return getResources().getString(R.string.title_activity_background_player);
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
- case R.id.action_history:
- NavigationHelper.openHistory(this);
- return true;
- case R.id.action_settings:
- NavigationHelper.openSettings(this);
- return true;
- case R.id.action_system_audio:
- startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
- return true;
- }
- return super.onOptionsItemSelected(item);
+ public BasePlayer playerFrom(IBinder binder) {
+ final BackgroundPlayer.LocalBinder mLocalBinder = (BackgroundPlayer.LocalBinder) binder;
+ return mLocalBinder.getBackgroundPlayerInstance();
}
@Override
- protected void onDestroy() {
- super.onDestroy();
- unbind();
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Service Connection
- ////////////////////////////////////////////////////////////////////////////
-
- private void bind() {
- final Intent mIntent = new Intent(this, BackgroundPlayer.class);
- final boolean success = bindService(mIntent, serviceConnection, BIND_AUTO_CREATE);
- if (!success) {
- unbindService(serviceConnection);
- }
- serviceBound = success;
- }
-
- private void unbind() {
- if(serviceBound) {
- unbindService(serviceConnection);
- serviceBound = false;
- stopPlayerListener();
- player = null;
- finish();
- }
- }
-
- private ServiceConnection backgroundPlayerConnection() {
- return new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- Log.d(TAG, "Background player service is disconnected");
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- Log.d(TAG, "Background player service is connected");
- final BackgroundPlayer.LocalBinder mLocalBinder = (BackgroundPlayer.LocalBinder) service;
- player = mLocalBinder.getBackgroundPlayerInstance();
- if (player == null || player.playQueue == null || player.playQueueAdapter == null || player.simpleExoPlayer == null) {
- unbind();
- } else {
- buildComponents();
- startPlayerListener();
- }
- }
- };
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Component Building
- ////////////////////////////////////////////////////////////////////////////
-
- private void buildComponents() {
- buildQueue();
- buildMetadata();
- buildSeekBar();
- buildControls();
- }
-
- private void buildQueue() {
- itemsList = findViewById(R.id.play_queue);
- itemsList.setLayoutManager(new LinearLayoutManager(this));
- itemsList.setAdapter(player.playQueueAdapter);
- itemsList.setClickable(true);
- itemsList.setLongClickable(true);
-
- itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
- itemTouchHelper.attachToRecyclerView(itemsList);
-
- player.playQueueAdapter.setSelectedListener(getOnSelectedListener());
- }
-
- private void buildMetadata() {
- metadataTitle = rootView.findViewById(R.id.song_name);
- metadataArtist = rootView.findViewById(R.id.artist_name);
- }
-
- private void buildSeekBar() {
- progressCurrentTime = rootView.findViewById(R.id.current_time);
- progressSeekBar = rootView.findViewById(R.id.seek_bar);
- progressEndTime = rootView.findViewById(R.id.end_time);
-
- progressSeekBar.setOnSeekBarChangeListener(this);
- }
-
- private void buildControls() {
- repeatButton = rootView.findViewById(R.id.control_repeat);
- backwardButton = rootView.findViewById(R.id.control_backward);
- playPauseButton = rootView.findViewById(R.id.control_play_pause);
- forwardButton = rootView.findViewById(R.id.control_forward);
- shuffleButton = rootView.findViewById(R.id.control_shuffle);
- playbackSpeedButton = rootView.findViewById(R.id.control_playback_speed);
- playbackPitchButton = rootView.findViewById(R.id.control_playback_pitch);
- progressBar = rootView.findViewById(R.id.control_progress_bar);
-
- repeatButton.setOnClickListener(this);
- backwardButton.setOnClickListener(this);
- playPauseButton.setOnClickListener(this);
- forwardButton.setOnClickListener(this);
- shuffleButton.setOnClickListener(this);
- playbackSpeedButton.setOnClickListener(this);
- playbackPitchButton.setOnClickListener(this);
-
- playbackSpeedPopupMenu = new PopupMenu(this, playbackSpeedButton);
- playbackPitchPopupMenu = new PopupMenu(this, playbackPitchButton);
- buildPlaybackSpeedMenu();
- buildPlaybackPitchMenu();
- }
-
- private void buildPlaybackSpeedMenu() {
- if (playbackSpeedPopupMenu == null) return;
-
- playbackSpeedPopupMenu.getMenu().removeGroup(PLAYBACK_SPEED_POPUP_MENU_GROUP_ID);
- for (int i = 0; i < BasePlayer.PLAYBACK_SPEEDS.length; i++) {
- final float playbackSpeed = BasePlayer.PLAYBACK_SPEEDS[i];
- final String formattedSpeed = player.formatSpeed(playbackSpeed);
- final MenuItem item = playbackSpeedPopupMenu.getMenu().add(PLAYBACK_SPEED_POPUP_MENU_GROUP_ID, i, Menu.NONE, formattedSpeed);
- item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem menuItem) {
- player.setPlaybackSpeed(playbackSpeed);
- return true;
- }
- });
- }
- }
-
- private void buildPlaybackPitchMenu() {
- if (playbackPitchPopupMenu == null) return;
-
- playbackPitchPopupMenu.getMenu().removeGroup(PLAYBACK_PITCH_POPUP_MENU_GROUP_ID);
- for (int i = 0; i < BasePlayer.PLAYBACK_PITCHES.length; i++) {
- final float playbackPitch = BasePlayer.PLAYBACK_PITCHES[i];
- final String formattedPitch = player.formatPitch(playbackPitch);
- final MenuItem item = playbackPitchPopupMenu.getMenu().add(PLAYBACK_PITCH_POPUP_MENU_GROUP_ID, i, Menu.NONE, formattedPitch);
- item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem menuItem) {
- player.setPlaybackPitch(playbackPitch);
- return true;
- }
- });
- }
- }
-
- private void buildItemPopupMenu(final PlayQueueItem item, final View view) {
- final PopupMenu menu = new PopupMenu(this, view);
- final MenuItem remove = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 0, Menu.NONE, R.string.play_queue_remove);
- remove.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem menuItem) {
- final int index = player.playQueue.indexOf(item);
- if (index != -1) player.playQueue.remove(index);
- return true;
- }
- });
-
- final MenuItem detail = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1, Menu.NONE, R.string.play_queue_stream_detail);
- detail.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem menuItem) {
- onOpenDetail(item.getServiceId(), item.getUrl(), item.getTitle());
- return true;
- }
- });
-
- menu.show();
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Component Helpers
- ////////////////////////////////////////////////////////////////////////////
-
- private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
- return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
- @Override
- public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
- if (source.getItemViewType() != target.getItemViewType()) {
- return false;
- }
-
- final int sourceIndex = source.getLayoutPosition();
- final int targetIndex = target.getLayoutPosition();
- player.playQueue.move(sourceIndex, targetIndex);
- return true;
- }
-
- @Override
- public boolean isLongPressDragEnabled() {
- return false;
- }
-
- @Override
- public boolean isItemViewSwipeEnabled() {
- return false;
- }
-
- @Override
- public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {}
- };
- }
-
- private PlayQueueItemBuilder.OnSelectedListener getOnSelectedListener() {
- return new PlayQueueItemBuilder.OnSelectedListener() {
- @Override
- public void selected(PlayQueueItem item, View view) {
- final int index = player.playQueue.indexOf(item);
- if (index == -1) return;
-
- if (player.playQueue.getIndex() == index) {
- player.onRestart();
- } else {
- player.playQueue.setIndex(index);
- }
- }
-
- @Override
- public void held(PlayQueueItem item, View view) {
- final int index = player.playQueue.indexOf(item);
- if (index != -1) buildItemPopupMenu(item, view);
- }
-
- @Override
- public void onStartDrag(PlayQueueItemHolder viewHolder) {
- if (itemTouchHelper != null) itemTouchHelper.startDrag(viewHolder);
- }
- };
- }
-
- private void onOpenDetail(int serviceId, String videoUrl, String videoTitle) {
- NavigationHelper.openVideoDetail(this, serviceId, videoUrl, videoTitle);
- }
-
- private void scrollToSelected() {
- itemsList.smoothScrollToPosition(player.playQueue.getIndex());
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Component On-Click Listener
- ////////////////////////////////////////////////////////////////////////////
-
- @Override
- public void onClick(View view) {
- if (view.getId() == repeatButton.getId()) {
- player.onRepeatClicked();
-
- } else if (view.getId() == backwardButton.getId()) {
- player.onPlayPrevious();
- scrollToSelected();
-
- } else if (view.getId() == playPauseButton.getId()) {
- player.onVideoPlayPause();
- scrollToSelected();
-
- } else if (view.getId() == forwardButton.getId()) {
- player.onPlayNext();
-
- } else if (view.getId() == shuffleButton.getId()) {
- player.onShuffleClicked();
-
- } else if (view.getId() == playbackSpeedButton.getId()) {
- playbackSpeedPopupMenu.show();
-
- } else if (view.getId() == playbackPitchButton.getId()) {
- playbackPitchPopupMenu.show();
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Seekbar Listener
- ////////////////////////////////////////////////////////////////////////////
-
- @Override
- public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
- if (fromUser) progressCurrentTime.setText(Localization.getDurationString(progress / 1000));
+ public Intent getBindIntent() {
+ return new Intent(this, BackgroundPlayer.class);
}
@Override
- public void onStartTrackingTouch(SeekBar seekBar) {
- seeking = true;
- }
-
- @Override
- public void onStopTrackingTouch(SeekBar seekBar) {
- player.simpleExoPlayer.seekTo(seekBar.getProgress());
- seeking = false;
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Binding Service Listener
- ////////////////////////////////////////////////////////////////////////////
-
- private void startPlayerListener() {
- if (player != null) {
- player.setActivityListener(this);
- }
- }
-
- private void stopPlayerListener() {
- if (player != null) {
- player.removeActivityListener(this);
+ public void startPlayerListener() {
+ if (player != null && player instanceof BackgroundPlayer.BasePlayerImpl) {
+ ((BackgroundPlayer.BasePlayerImpl) player).setActivityListener(this);
}
}
@Override
- public void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters) {
- onStateChanged(state);
- onPlayModeChanged(repeatMode, shuffled);
- onPlaybackParameterChanged(parameters);
- scrollToSelected();
- }
-
- @Override
- public void onProgressUpdate(int currentProgress, int duration, int bufferPercent) {
- // Set buffer progress
- progressSeekBar.setSecondaryProgress((int) (progressSeekBar.getMax() * ((float) bufferPercent / 100)));
-
- // Set Duration
- progressSeekBar.setMax(duration);
- progressEndTime.setText(Localization.getDurationString(duration / 1000));
-
- // Set current time if not seeking
- if (!seeking) {
- progressSeekBar.setProgress(currentProgress);
- progressCurrentTime.setText(Localization.getDurationString(currentProgress / 1000));
- }
- }
-
- @Override
- public void onMetadataUpdate(StreamInfo info) {
- if (info != null) {
- metadataTitle.setText(info.name);
- metadataArtist.setText(info.uploader_name);
- scrollToSelected();
- }
- }
-
- @Override
- public void onServiceStopped() {
- unbind();
- }
-
- ////////////////////////////////////////////////////////////////////////////
- // Binding Service Helper
- ////////////////////////////////////////////////////////////////////////////
-
- private void onStateChanged(final int state) {
- switch (state) {
- case BasePlayer.STATE_PAUSED:
- playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
- break;
- case BasePlayer.STATE_PLAYING:
- playPauseButton.setImageResource(R.drawable.ic_pause_white);
- break;
- case BasePlayer.STATE_COMPLETED:
- playPauseButton.setImageResource(R.drawable.ic_replay_white);
- break;
- default:
- break;
- }
-
- switch (state) {
- case BasePlayer.STATE_PAUSED:
- case BasePlayer.STATE_PLAYING:
- case BasePlayer.STATE_COMPLETED:
- playPauseButton.setClickable(true);
- playPauseButton.setVisibility(View.VISIBLE);
- progressBar.setVisibility(View.GONE);
- break;
- default:
- playPauseButton.setClickable(false);
- playPauseButton.setVisibility(View.INVISIBLE);
- progressBar.setVisibility(View.VISIBLE);
- break;
- }
- }
-
- private void onPlayModeChanged(final int repeatMode, final boolean shuffled) {
- switch (repeatMode) {
- case Player.REPEAT_MODE_OFF:
- repeatButton.setImageResource(R.drawable.exo_controls_repeat_off);
- break;
- case Player.REPEAT_MODE_ONE:
- repeatButton.setImageResource(R.drawable.exo_controls_repeat_one);
- break;
- case Player.REPEAT_MODE_ALL:
- repeatButton.setImageResource(R.drawable.exo_controls_repeat_all);
- break;
- }
-
- final int shuffleAlpha = shuffled ? 255 : 77;
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- shuffleButton.setImageAlpha(shuffleAlpha);
- } else {
- shuffleButton.setAlpha(shuffleAlpha);
- }
- }
-
- private void onPlaybackParameterChanged(final PlaybackParameters parameters) {
- if (parameters != null) {
- playbackSpeedButton.setText(player.formatSpeed(parameters.speed));
- playbackPitchButton.setText(player.formatPitch(parameters.pitch));
+ public void stopPlayerListener() {
+ if (player != null && player instanceof BackgroundPlayer.BasePlayerImpl) {
+ ((BackgroundPlayer.BasePlayerImpl) player).removeActivityListener(this);
}
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
index 270b7e5b8..6adf0f8e3 100644
--- a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
+++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayer.java
@@ -30,11 +30,13 @@ import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.PixelFormat;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -49,6 +51,7 @@ import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
+import com.google.android.exoplayer2.PlaybackParameters;
import com.google.android.exoplayer2.Player;
import org.schabi.newpipe.BuildConfig;
@@ -56,14 +59,15 @@ import org.schabi.newpipe.MainActivity;
import org.schabi.newpipe.R;
import org.schabi.newpipe.ReCaptchaActivity;
import org.schabi.newpipe.extractor.NewPipe;
-import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
import org.schabi.newpipe.extractor.services.youtube.YoutubeStreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfo;
+import org.schabi.newpipe.player.event.PlayerEventListener;
import org.schabi.newpipe.player.old.PlayVideoActivity;
import org.schabi.newpipe.player.playback.MediaSourceManager;
+import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.playlist.SinglePlayQueue;
import org.schabi.newpipe.report.ErrorActivity;
import org.schabi.newpipe.report.UserAction;
@@ -120,6 +124,19 @@ public final class PopupVideoPlayer extends Service {
private VideoPlayerImpl playerImpl;
private Disposable currentWorker;
+ /*//////////////////////////////////////////////////////////////////////////
+ // Service-Activity Binder
+ //////////////////////////////////////////////////////////////////////////*/
+
+ private PlayerEventListener activityListener;
+ private IBinder mBinder;
+
+ class LocalBinder extends Binder {
+ VideoPlayerImpl getPopupPlayerInstance() {
+ return PopupVideoPlayer.this.playerImpl;
+ }
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// Service LifeCycle
//////////////////////////////////////////////////////////////////////////*/
@@ -131,6 +148,8 @@ public final class PopupVideoPlayer extends Service {
playerImpl = new VideoPlayerImpl();
ThemeHelper.setTheme(this);
+
+ mBinder = new LocalBinder();
}
@Override
@@ -191,7 +210,7 @@ public final class PopupVideoPlayer extends Service {
@Override
public IBinder onBind(Intent intent) {
- return null;
+ return mBinder;
}
/*//////////////////////////////////////////////////////////////////////////
@@ -284,19 +303,16 @@ public final class PopupVideoPlayer extends Service {
public void onVideoClose() {
if (DEBUG) Log.d(TAG, "onVideoClose() called");
+ playerImpl.stopActivityBinding();
stopSelf();
}
- public void onOpenDetail(Context context, String videoUrl, String videoTitle) {
- if (DEBUG) Log.d(TAG, "onOpenDetail() called with: context = [" + context + "], videoUrl = [" + videoUrl + "]");
- Intent i = new Intent(context, MainActivity.class);
- i.putExtra(Constants.KEY_SERVICE_ID, 0);
- i.putExtra(Constants.KEY_URL, videoUrl);
- i.putExtra(Constants.KEY_TITLE, videoTitle);
- i.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM);
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- context.startActivity(i);
+ public void openControl(final Context context) {
+ final Intent intent = new Intent(context, PopupVideoPlayerActivity.class);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+ context.startActivity(intent);
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
@@ -376,7 +392,7 @@ public final class PopupVideoPlayer extends Service {
///////////////////////////////////////////////////////////////////////////
- private class VideoPlayerImpl extends VideoPlayer {
+ protected class VideoPlayerImpl extends VideoPlayer {
private TextView resizingIndicator;
VideoPlayerImpl() {
@@ -465,6 +481,61 @@ public final class PopupVideoPlayer extends Service {
hideControls(100, 0);
}
}
+
+ @Override
+ public void onShuffleClicked() {
+ super.onShuffleClicked();
+ updatePlayback();
+ }
+
+ @Override
+ public void onUpdateProgress(int currentProgress, int duration, int bufferPercent) {
+ super.onUpdateProgress(currentProgress, duration, bufferPercent);
+ updateProgress(currentProgress, duration, bufferPercent);
+ }
+
+ /*//////////////////////////////////////////////////////////////////////////
+ // Activity Event Listener
+ //////////////////////////////////////////////////////////////////////////*/
+
+ public void setActivityListener(PlayerEventListener listener) {
+ activityListener = listener;
+ updateMetadata();
+ updatePlayback();
+ triggerProgressUpdate();
+ }
+
+ public void removeActivityListener(PlayerEventListener listener) {
+ if (activityListener == listener) {
+ activityListener = null;
+ }
+ }
+
+ private void updateMetadata() {
+ if (activityListener != null && currentInfo != null) {
+ activityListener.onMetadataUpdate(currentInfo);
+ }
+ }
+
+ private void updatePlayback() {
+ if (activityListener != null && simpleExoPlayer != null && playQueue != null) {
+ activityListener.onPlaybackUpdate(currentState, simpleExoPlayer.getRepeatMode(), playQueue.isShuffled(), simpleExoPlayer.getPlaybackParameters());
+ }
+ }
+
+ private void updateProgress(int currentProgress, int duration, int bufferPercent) {
+ if (activityListener != null) {
+ activityListener.onProgressUpdate(currentProgress, duration, bufferPercent);
+ }
+ }
+
+ private void stopActivityBinding() {
+ if (activityListener != null) {
+ activityListener.onServiceStopped();
+ activityListener = null;
+ }
+ }
+
/*//////////////////////////////////////////////////////////////////////////
// ExoPlayer Video Listener
//////////////////////////////////////////////////////////////////////////*/
@@ -474,15 +545,29 @@ public final class PopupVideoPlayer extends Service {
super.onRepeatModeChanged(i);
setRepeatModeRemote(notRemoteView, i);
updateNotification(-1);
+ updatePlayback();
+ }
+
+ @Override
+ public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
+ super.onPlaybackParametersChanged(playbackParameters);
+ updatePlayback();
}
/*//////////////////////////////////////////////////////////////////////////
// Playback Listener
//////////////////////////////////////////////////////////////////////////*/
+ @Override
+ public void sync(@NonNull PlayQueueItem item, @Nullable StreamInfo info) {
+ super.sync(item, info);
+ updateMetadata();
+ }
+
@Override
public void shutdown() {
super.shutdown();
+ stopActivityBinding();
stopSelf();
}
@@ -512,17 +597,24 @@ public final class PopupVideoPlayer extends Service {
onVideoPlayPause();
break;
case ACTION_OPEN_DETAIL:
- onOpenDetail(PopupVideoPlayer.this, getVideoUrl(), getVideoTitle());
+ openControl(PopupVideoPlayer.this);
break;
case ACTION_REPEAT:
onRepeatClicked();
break;
}
}
+
/*//////////////////////////////////////////////////////////////////////////
// States
//////////////////////////////////////////////////////////////////////////*/
+ @Override
+ public void changeState(int state) {
+ super.changeState(state);
+ updatePlayback();
+ }
+
@Override
public void onBlocked() {
super.onBlocked();
diff --git a/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayerActivity.java
new file mode 100644
index 000000000..80375c6fb
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayerActivity.java
@@ -0,0 +1,46 @@
+package org.schabi.newpipe.player;
+
+import android.content.Intent;
+import android.os.IBinder;
+
+import org.schabi.newpipe.R;
+
+public final class PopupVideoPlayerActivity extends ServicePlayerActivity {
+
+ private static final String TAG = "PopupVideoPlayerActivity";
+
+ @Override
+ public String getTag() {
+ return TAG;
+ }
+
+ @Override
+ public String getSupportActionTitle() {
+ return getResources().getString(R.string.title_activity_popup_player);
+ }
+
+ @Override
+ public BasePlayer playerFrom(IBinder binder) {
+ final PopupVideoPlayer.LocalBinder mLocalBinder = (PopupVideoPlayer.LocalBinder) binder;
+ return mLocalBinder.getPopupPlayerInstance();
+ }
+
+ @Override
+ public Intent getBindIntent() {
+ return new Intent(this, PopupVideoPlayer.class);
+ }
+
+ @Override
+ public void startPlayerListener() {
+ if (player != null && player instanceof PopupVideoPlayer.VideoPlayerImpl) {
+ ((PopupVideoPlayer.VideoPlayerImpl) player).setActivityListener(this);
+ }
+ }
+
+ @Override
+ public void stopPlayerListener() {
+ if (player != null && player instanceof PopupVideoPlayer.VideoPlayerImpl) {
+ ((PopupVideoPlayer.VideoPlayerImpl) player).removeActivityListener(this);
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
new file mode 100644
index 000000000..e7ce1d2a4
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java
@@ -0,0 +1,536 @@
+package org.schabi.newpipe.player;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.Toolbar;
+import android.support.v7.widget.helper.ItemTouchHelper;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.PopupMenu;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import com.google.android.exoplayer2.PlaybackParameters;
+import com.google.android.exoplayer2.Player;
+
+import org.schabi.newpipe.R;
+import org.schabi.newpipe.extractor.stream.StreamInfo;
+import org.schabi.newpipe.player.event.PlayerEventListener;
+import org.schabi.newpipe.playlist.PlayQueueItem;
+import org.schabi.newpipe.playlist.PlayQueueItemBuilder;
+import org.schabi.newpipe.playlist.PlayQueueItemHolder;
+import org.schabi.newpipe.util.Localization;
+import org.schabi.newpipe.util.NavigationHelper;
+import org.schabi.newpipe.util.ThemeHelper;
+
+public abstract class ServicePlayerActivity extends AppCompatActivity
+ implements PlayerEventListener, SeekBar.OnSeekBarChangeListener, View.OnClickListener {
+
+ private boolean serviceBound;
+ private ServiceConnection serviceConnection;
+
+ protected BasePlayer player;
+
+ private boolean seeking;
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Views
+ ////////////////////////////////////////////////////////////////////////////
+
+ private static final int RECYCLER_ITEM_POPUP_MENU_GROUP_ID = 47;
+ private static final int PLAYBACK_SPEED_POPUP_MENU_GROUP_ID = 61;
+ private static final int PLAYBACK_PITCH_POPUP_MENU_GROUP_ID = 97;
+
+ private View rootView;
+
+ private RecyclerView itemsList;
+ private ItemTouchHelper itemTouchHelper;
+
+ private TextView metadataTitle;
+ private TextView metadataArtist;
+
+ private SeekBar progressSeekBar;
+ private TextView progressCurrentTime;
+ private TextView progressEndTime;
+
+ private ImageButton repeatButton;
+ private ImageButton backwardButton;
+ private ImageButton playPauseButton;
+ private ImageButton forwardButton;
+ private ImageButton shuffleButton;
+ private ProgressBar progressBar;
+
+ private TextView playbackSpeedButton;
+ private PopupMenu playbackSpeedPopupMenu;
+ private TextView playbackPitchButton;
+ private PopupMenu playbackPitchPopupMenu;
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Abstracts
+ ////////////////////////////////////////////////////////////////////////////
+
+ public abstract String getTag();
+
+ public abstract String getSupportActionTitle();
+
+ public abstract Intent getBindIntent();
+
+ public abstract void startPlayerListener();
+
+ public abstract void stopPlayerListener();
+
+ public abstract BasePlayer playerFrom(final IBinder binder);
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Activity Lifecycle
+ ////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ ThemeHelper.setTheme(this);
+ setContentView(R.layout.activity_player_queue_control);
+ rootView = findViewById(R.id.main_content);
+
+ final Toolbar toolbar = rootView.findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setTitle(getSupportActionTitle());
+ }
+
+ serviceConnection = getServiceConnection();
+ bind();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_play_queue, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ case R.id.action_history:
+ NavigationHelper.openHistory(this);
+ return true;
+ case R.id.action_settings:
+ NavigationHelper.openSettings(this);
+ return true;
+ case R.id.action_system_audio:
+ startActivity(new Intent(Settings.ACTION_SOUND_SETTINGS));
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ unbind();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Service Connection
+ ////////////////////////////////////////////////////////////////////////////
+
+ private void bind() {
+ final boolean success = bindService(getBindIntent(), serviceConnection, BIND_AUTO_CREATE);
+ if (!success) {
+ unbindService(serviceConnection);
+ }
+ serviceBound = success;
+ }
+
+ private void unbind() {
+ if(serviceBound) {
+ unbindService(serviceConnection);
+ serviceBound = false;
+ stopPlayerListener();
+ player = null;
+ finish();
+ }
+ }
+
+ private ServiceConnection getServiceConnection() {
+ return new ServiceConnection() {
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Log.d(getTag(), "Player service is disconnected");
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Log.d(getTag(), "Player service is connected");
+ player = playerFrom(service);
+ if (player == null || player.playQueue == null || player.playQueueAdapter == null || player.simpleExoPlayer == null) {
+ unbind();
+ } else {
+ buildComponents();
+ startPlayerListener();
+ }
+ }
+ };
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Component Building
+ ////////////////////////////////////////////////////////////////////////////
+
+ private void buildComponents() {
+ buildQueue();
+ buildMetadata();
+ buildSeekBar();
+ buildControls();
+ }
+
+ private void buildQueue() {
+ itemsList = findViewById(R.id.play_queue);
+ itemsList.setLayoutManager(new LinearLayoutManager(this));
+ itemsList.setAdapter(player.playQueueAdapter);
+ itemsList.setClickable(true);
+ itemsList.setLongClickable(true);
+
+ itemTouchHelper = new ItemTouchHelper(getItemTouchCallback());
+ itemTouchHelper.attachToRecyclerView(itemsList);
+
+ player.playQueueAdapter.setSelectedListener(getOnSelectedListener());
+ }
+
+ private void buildMetadata() {
+ metadataTitle = rootView.findViewById(R.id.song_name);
+ metadataArtist = rootView.findViewById(R.id.artist_name);
+ }
+
+ private void buildSeekBar() {
+ progressCurrentTime = rootView.findViewById(R.id.current_time);
+ progressSeekBar = rootView.findViewById(R.id.seek_bar);
+ progressEndTime = rootView.findViewById(R.id.end_time);
+
+ progressSeekBar.setOnSeekBarChangeListener(this);
+ }
+
+ private void buildControls() {
+ repeatButton = rootView.findViewById(R.id.control_repeat);
+ backwardButton = rootView.findViewById(R.id.control_backward);
+ playPauseButton = rootView.findViewById(R.id.control_play_pause);
+ forwardButton = rootView.findViewById(R.id.control_forward);
+ shuffleButton = rootView.findViewById(R.id.control_shuffle);
+ playbackSpeedButton = rootView.findViewById(R.id.control_playback_speed);
+ playbackPitchButton = rootView.findViewById(R.id.control_playback_pitch);
+ progressBar = rootView.findViewById(R.id.control_progress_bar);
+
+ repeatButton.setOnClickListener(this);
+ backwardButton.setOnClickListener(this);
+ playPauseButton.setOnClickListener(this);
+ forwardButton.setOnClickListener(this);
+ shuffleButton.setOnClickListener(this);
+ playbackSpeedButton.setOnClickListener(this);
+ playbackPitchButton.setOnClickListener(this);
+
+ playbackSpeedPopupMenu = new PopupMenu(this, playbackSpeedButton);
+ playbackPitchPopupMenu = new PopupMenu(this, playbackPitchButton);
+ buildPlaybackSpeedMenu();
+ buildPlaybackPitchMenu();
+ }
+
+ private void buildPlaybackSpeedMenu() {
+ if (playbackSpeedPopupMenu == null) return;
+
+ playbackSpeedPopupMenu.getMenu().removeGroup(PLAYBACK_SPEED_POPUP_MENU_GROUP_ID);
+ for (int i = 0; i < BasePlayer.PLAYBACK_SPEEDS.length; i++) {
+ final float playbackSpeed = BasePlayer.PLAYBACK_SPEEDS[i];
+ final String formattedSpeed = player.formatSpeed(playbackSpeed);
+ final MenuItem item = playbackSpeedPopupMenu.getMenu().add(PLAYBACK_SPEED_POPUP_MENU_GROUP_ID, i, Menu.NONE, formattedSpeed);
+ item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ player.setPlaybackSpeed(playbackSpeed);
+ return true;
+ }
+ });
+ }
+ }
+
+ private void buildPlaybackPitchMenu() {
+ if (playbackPitchPopupMenu == null) return;
+
+ playbackPitchPopupMenu.getMenu().removeGroup(PLAYBACK_PITCH_POPUP_MENU_GROUP_ID);
+ for (int i = 0; i < BasePlayer.PLAYBACK_PITCHES.length; i++) {
+ final float playbackPitch = BasePlayer.PLAYBACK_PITCHES[i];
+ final String formattedPitch = player.formatPitch(playbackPitch);
+ final MenuItem item = playbackPitchPopupMenu.getMenu().add(PLAYBACK_PITCH_POPUP_MENU_GROUP_ID, i, Menu.NONE, formattedPitch);
+ item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ player.setPlaybackPitch(playbackPitch);
+ return true;
+ }
+ });
+ }
+ }
+
+ private void buildItemPopupMenu(final PlayQueueItem item, final View view) {
+ final PopupMenu menu = new PopupMenu(this, view);
+ final MenuItem remove = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 0, Menu.NONE, R.string.play_queue_remove);
+ remove.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ final int index = player.playQueue.indexOf(item);
+ if (index != -1) player.playQueue.remove(index);
+ return true;
+ }
+ });
+
+ final MenuItem detail = menu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1, Menu.NONE, R.string.play_queue_stream_detail);
+ detail.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem menuItem) {
+ onOpenDetail(item.getServiceId(), item.getUrl(), item.getTitle());
+ return true;
+ }
+ });
+
+ menu.show();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Component Helpers
+ ////////////////////////////////////////////////////////////////////////////
+
+ private ItemTouchHelper.SimpleCallback getItemTouchCallback() {
+ return new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
+ @Override
+ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
+ if (source.getItemViewType() != target.getItemViewType()) {
+ return false;
+ }
+
+ final int sourceIndex = source.getLayoutPosition();
+ final int targetIndex = target.getLayoutPosition();
+ player.playQueue.move(sourceIndex, targetIndex);
+ return true;
+ }
+
+ @Override
+ public boolean isLongPressDragEnabled() {
+ return false;
+ }
+
+ @Override
+ public boolean isItemViewSwipeEnabled() {
+ return false;
+ }
+
+ @Override
+ public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {}
+ };
+ }
+
+ private PlayQueueItemBuilder.OnSelectedListener getOnSelectedListener() {
+ return new PlayQueueItemBuilder.OnSelectedListener() {
+ @Override
+ public void selected(PlayQueueItem item, View view) {
+ final int index = player.playQueue.indexOf(item);
+ if (index == -1) return;
+
+ if (player.playQueue.getIndex() == index) {
+ player.onRestart();
+ } else {
+ player.playQueue.setIndex(index);
+ }
+ }
+
+ @Override
+ public void held(PlayQueueItem item, View view) {
+ final int index = player.playQueue.indexOf(item);
+ if (index != -1) buildItemPopupMenu(item, view);
+ }
+
+ @Override
+ public void onStartDrag(PlayQueueItemHolder viewHolder) {
+ if (itemTouchHelper != null) itemTouchHelper.startDrag(viewHolder);
+ }
+ };
+ }
+
+ private void onOpenDetail(int serviceId, String videoUrl, String videoTitle) {
+ NavigationHelper.openVideoDetail(this, serviceId, videoUrl, videoTitle);
+ }
+
+ private void scrollToSelected() {
+ itemsList.smoothScrollToPosition(player.playQueue.getIndex());
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Component On-Click Listener
+ ////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onClick(View view) {
+ if (view.getId() == repeatButton.getId()) {
+ player.onRepeatClicked();
+
+ } else if (view.getId() == backwardButton.getId()) {
+ player.onPlayPrevious();
+ scrollToSelected();
+
+ } else if (view.getId() == playPauseButton.getId()) {
+ player.onVideoPlayPause();
+ scrollToSelected();
+
+ } else if (view.getId() == forwardButton.getId()) {
+ player.onPlayNext();
+
+ } else if (view.getId() == shuffleButton.getId()) {
+ player.onShuffleClicked();
+
+ } else if (view.getId() == playbackSpeedButton.getId()) {
+ playbackSpeedPopupMenu.show();
+
+ } else if (view.getId() == playbackPitchButton.getId()) {
+ playbackPitchPopupMenu.show();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Seekbar Listener
+ ////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if (fromUser) progressCurrentTime.setText(Localization.getDurationString(progress / 1000));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ seeking = true;
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ player.simpleExoPlayer.seekTo(seekBar.getProgress());
+ seeking = false;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Binding Service Listener
+ ////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters) {
+ onStateChanged(state);
+ onPlayModeChanged(repeatMode, shuffled);
+ onPlaybackParameterChanged(parameters);
+ scrollToSelected();
+ }
+
+ @Override
+ public void onProgressUpdate(int currentProgress, int duration, int bufferPercent) {
+ // Set buffer progress
+ progressSeekBar.setSecondaryProgress((int) (progressSeekBar.getMax() * ((float) bufferPercent / 100)));
+
+ // Set Duration
+ progressSeekBar.setMax(duration);
+ progressEndTime.setText(Localization.getDurationString(duration / 1000));
+
+ // Set current time if not seeking
+ if (!seeking) {
+ progressSeekBar.setProgress(currentProgress);
+ progressCurrentTime.setText(Localization.getDurationString(currentProgress / 1000));
+ }
+ }
+
+ @Override
+ public void onMetadataUpdate(StreamInfo info) {
+ if (info != null) {
+ metadataTitle.setText(info.name);
+ metadataArtist.setText(info.uploader_name);
+ scrollToSelected();
+ }
+ }
+
+ @Override
+ public void onServiceStopped() {
+ unbind();
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Binding Service Helper
+ ////////////////////////////////////////////////////////////////////////////
+
+ private void onStateChanged(final int state) {
+ switch (state) {
+ case BasePlayer.STATE_PAUSED:
+ playPauseButton.setImageResource(R.drawable.ic_play_arrow_white);
+ break;
+ case BasePlayer.STATE_PLAYING:
+ playPauseButton.setImageResource(R.drawable.ic_pause_white);
+ break;
+ case BasePlayer.STATE_COMPLETED:
+ playPauseButton.setImageResource(R.drawable.ic_replay_white);
+ break;
+ default:
+ break;
+ }
+
+ switch (state) {
+ case BasePlayer.STATE_PAUSED:
+ case BasePlayer.STATE_PLAYING:
+ case BasePlayer.STATE_COMPLETED:
+ playPauseButton.setClickable(true);
+ playPauseButton.setVisibility(View.VISIBLE);
+ progressBar.setVisibility(View.GONE);
+ break;
+ default:
+ playPauseButton.setClickable(false);
+ playPauseButton.setVisibility(View.INVISIBLE);
+ progressBar.setVisibility(View.VISIBLE);
+ break;
+ }
+ }
+
+ private void onPlayModeChanged(final int repeatMode, final boolean shuffled) {
+ switch (repeatMode) {
+ case Player.REPEAT_MODE_OFF:
+ repeatButton.setImageResource(R.drawable.exo_controls_repeat_off);
+ break;
+ case Player.REPEAT_MODE_ONE:
+ repeatButton.setImageResource(R.drawable.exo_controls_repeat_one);
+ break;
+ case Player.REPEAT_MODE_ALL:
+ repeatButton.setImageResource(R.drawable.exo_controls_repeat_all);
+ break;
+ }
+
+ final int shuffleAlpha = shuffled ? 255 : 77;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ shuffleButton.setImageAlpha(shuffleAlpha);
+ } else {
+ shuffleButton.setAlpha(shuffleAlpha);
+ }
+ }
+
+ private void onPlaybackParameterChanged(final PlaybackParameters parameters) {
+ if (parameters != null) {
+ playbackSpeedButton.setText(player.formatSpeed(parameters.speed));
+ playbackPitchButton.setText(player.formatPitch(parameters.pitch));
+ }
+ }
+}
diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java
new file mode 100644
index 000000000..3a7b29954
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java
@@ -0,0 +1,13 @@
+package org.schabi.newpipe.player.event;
+
+
+import com.google.android.exoplayer2.PlaybackParameters;
+
+import org.schabi.newpipe.extractor.stream.StreamInfo;
+
+public interface PlayerEventListener {
+ void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters);
+ void onProgressUpdate(int currentProgress, int duration, int bufferPercent);
+ void onMetadataUpdate(StreamInfo info);
+ void onServiceStopped();
+}
diff --git a/app/src/main/res/layout/activity_background_player.xml b/app/src/main/res/layout/activity_player_queue_control.xml
similarity index 100%
rename from app/src/main/res/layout/activity_background_player.xml
rename to app/src/main/res/layout/activity_player_queue_control.xml
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9054397a6..8295fd5a8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -295,6 +295,7 @@
Background Player
+ Popup Player
Remove
Details
Audio Settings