mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2024-12-23 16:40:32 +00:00
-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.
This commit is contained in:
parent
9685456ee4
commit
87fca5cffe
@ -40,9 +40,14 @@
|
||||
|
||||
<activity
|
||||
android:name=".player.BackgroundPlayerActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:launchMode="singleTask"
|
||||
android:label="@string/title_activity_background_player"/>
|
||||
|
||||
<activity
|
||||
android:name=".player.PopupVideoPlayerActivity"
|
||||
android:launchMode="singleTask"
|
||||
android:label="@string/title_activity_popup_player"/>
|
||||
|
||||
<service
|
||||
android:name=".player.PopupVideoPlayer"
|
||||
android:exported="false"/>
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
@ -295,6 +295,7 @@
|
||||
|
||||
<!-- Play Queue -->
|
||||
<string name="title_activity_background_player">Background Player</string>
|
||||
<string name="title_activity_popup_player">Popup Player</string>
|
||||
<string name="play_queue_remove">Remove</string>
|
||||
<string name="play_queue_stream_detail">Details</string>
|
||||
<string name="play_queue_audio_settings">Audio Settings</string>
|
||||
|
Loading…
Reference in New Issue
Block a user