From 87fca5cffe7c89e1d0f896b2cbc98a846f569d95 Mon Sep 17 00:00:00 2001 From: John Zhen M Date: Thu, 12 Oct 2017 18:22:25 -0700 Subject: [PATCH] -Enabled play queue control panel for popup video player. -Refactored background player activity into generic play queue control panel activity. -Changed control panel activities into singleTask. --- app/src/main/AndroidManifest.xml | 7 +- .../newpipe/player/BackgroundPlayer.java | 12 +- .../player/BackgroundPlayerActivity.java | 521 +---------------- .../newpipe/player/PopupVideoPlayer.java | 120 +++- .../player/PopupVideoPlayerActivity.java | 46 ++ .../newpipe/player/ServicePlayerActivity.java | 536 ++++++++++++++++++ .../player/event/PlayerEventListener.java | 13 + ....xml => activity_player_queue_control.xml} | 0 app/src/main/res/values/strings.xml | 1 + 9 files changed, 728 insertions(+), 528 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/player/PopupVideoPlayerActivity.java create mode 100644 app/src/main/java/org/schabi/newpipe/player/ServicePlayerActivity.java create mode 100644 app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java rename app/src/main/res/layout/{activity_background_player.xml => activity_player_queue_control.xml} (100%) 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