mirror of
https://github.com/TeamNewPipe/NewPipe.git
synced 2024-11-26 04:52:29 +01:00
-Added media session implementation for all players.
-Extracted version numbers in gradle dependencies. -Updated ExoPlayer to 2.7.1. -Updated RxJava to 2.1.10, RxAndroid to 2.0.2 and RxBinding to 2.1.1. -Removed deprecated implementation of media buttons.
This commit is contained in:
parent
5a05cb96be
commit
bc7188c8a8
@ -49,6 +49,11 @@ android {
|
|||||||
|
|
||||||
ext {
|
ext {
|
||||||
supportLibVersion = '27.1.0'
|
supportLibVersion = '27.1.0'
|
||||||
|
exoPlayerLibVersion = '2.7.1'
|
||||||
|
roomDbLibVersion = '1.0.0'
|
||||||
|
leakCanaryVersion = '1.5.4'
|
||||||
|
okHttpVersion = '1.5.0'
|
||||||
|
icepickVersion = '3.2.0'
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
|
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2') {
|
||||||
@ -73,27 +78,28 @@ dependencies {
|
|||||||
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
implementation 'de.hdodenhof:circleimageview:2.2.0'
|
||||||
implementation 'com.github.nirhart:ParallaxScroll:dd53d1f9d1'
|
implementation 'com.github.nirhart:ParallaxScroll:dd53d1f9d1'
|
||||||
implementation 'com.nononsenseapps:filepicker:4.2.1'
|
implementation 'com.nononsenseapps:filepicker:4.2.1'
|
||||||
implementation 'com.google.android.exoplayer:exoplayer:2.7.1'
|
implementation "com.google.android.exoplayer:exoplayer:$exoPlayerLibVersion"
|
||||||
|
implementation "com.google.android.exoplayer:extension-mediasession:$exoPlayerLibVersion"
|
||||||
|
|
||||||
debugImplementation 'com.facebook.stetho:stetho:1.5.0'
|
debugImplementation "com.facebook.stetho:stetho:$okHttpVersion"
|
||||||
debugImplementation 'com.facebook.stetho:stetho-urlconnection:1.5.0'
|
debugImplementation "com.facebook.stetho:stetho-urlconnection:$okHttpVersion"
|
||||||
debugImplementation 'com.android.support:multidex:1.0.3'
|
debugImplementation 'com.android.support:multidex:1.0.3'
|
||||||
|
|
||||||
implementation 'io.reactivex.rxjava2:rxjava:2.1.7'
|
implementation 'io.reactivex.rxjava2:rxjava:2.1.10'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
|
||||||
implementation 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
|
implementation 'com.jakewharton.rxbinding2:rxbinding:2.1.1'
|
||||||
|
|
||||||
implementation 'android.arch.persistence.room:runtime:1.0.0'
|
implementation "android.arch.persistence.room:runtime:$roomDbLibVersion"
|
||||||
implementation 'android.arch.persistence.room:rxjava2:1.0.0'
|
implementation "android.arch.persistence.room:rxjava2:$roomDbLibVersion"
|
||||||
annotationProcessor 'android.arch.persistence.room:compiler:1.0.0'
|
annotationProcessor "android.arch.persistence.room:compiler:$roomDbLibVersion"
|
||||||
|
|
||||||
implementation 'frankiesardo:icepick:3.2.0'
|
implementation "frankiesardo:icepick:$icepickVersion"
|
||||||
annotationProcessor 'frankiesardo:icepick-processor:3.2.0'
|
annotationProcessor "frankiesardo:icepick-processor:$icepickVersion"
|
||||||
|
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.5.4'
|
debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakCanaryVersion"
|
||||||
betaImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
|
betaImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"
|
||||||
releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.4'
|
releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakCanaryVersion"
|
||||||
|
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
|
implementation 'com.squareup.okhttp3:okhttp:3.9.1'
|
||||||
debugImplementation 'com.facebook.stetho:stetho-okhttp3:1.5.0'
|
debugImplementation "com.facebook.stetho:stetho-okhttp3:$okHttpVersion"
|
||||||
}
|
}
|
||||||
|
@ -43,12 +43,6 @@
|
|||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:label="@string/title_activity_background_player"/>
|
android:label="@string/title_activity_background_player"/>
|
||||||
|
|
||||||
<receiver android:name="org.schabi.newpipe.player.BackgroundPlayer$MediaButtonReceiver">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
|
||||||
</intent-filter>
|
|
||||||
</receiver>
|
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".player.PopupVideoPlayerActivity"
|
android:name=".player.PopupVideoPlayerActivity"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
|
@ -22,8 +22,6 @@ package org.schabi.newpipe.player;
|
|||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
@ -35,7 +33,6 @@ import android.support.annotation.NonNull;
|
|||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.RemoteViews;
|
import android.widget.RemoteViews;
|
||||||
|
|
||||||
@ -81,8 +78,6 @@ public final class BackgroundPlayer extends Service {
|
|||||||
private BasePlayerImpl basePlayerImpl;
|
private BasePlayerImpl basePlayerImpl;
|
||||||
private LockManager lockManager;
|
private LockManager lockManager;
|
||||||
|
|
||||||
private ComponentName mReceiverComponent;
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Service-Activity Binder
|
// Service-Activity Binder
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
@ -119,9 +114,6 @@ public final class BackgroundPlayer extends Service {
|
|||||||
|
|
||||||
mBinder = new PlayerServiceBinder(basePlayerImpl);
|
mBinder = new PlayerServiceBinder(basePlayerImpl);
|
||||||
shouldUpdateOnProgress = true;
|
shouldUpdateOnProgress = true;
|
||||||
|
|
||||||
mReceiverComponent = new ComponentName(this, MediaButtonReceiver.class);
|
|
||||||
basePlayerImpl.getAudioReactor().registerMediaButtonEventReceiver(mReceiverComponent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -152,7 +144,6 @@ public final class BackgroundPlayer extends Service {
|
|||||||
lockManager.releaseWifiAndCpu();
|
lockManager.releaseWifiAndCpu();
|
||||||
}
|
}
|
||||||
if (basePlayerImpl != null) {
|
if (basePlayerImpl != null) {
|
||||||
basePlayerImpl.getAudioReactor().unregisterMediaButtonEventReceiver(mReceiverComponent);
|
|
||||||
basePlayerImpl.stopActivityBinding();
|
basePlayerImpl.stopActivityBinding();
|
||||||
basePlayerImpl.destroy();
|
basePlayerImpl.destroy();
|
||||||
}
|
}
|
||||||
@ -573,49 +564,4 @@ public final class BackgroundPlayer extends Service {
|
|||||||
lockManager.releaseWifiAndCpu();
|
lockManager.releaseWifiAndCpu();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MediaButtonReceiver extends BroadcastReceiver {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (!Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) return;
|
|
||||||
final KeyEvent event = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
|
|
||||||
if (event.getAction() != KeyEvent.ACTION_UP) return;
|
|
||||||
final int keycode = event.getKeyCode();
|
|
||||||
|
|
||||||
final PendingIntent pendingIntent;
|
|
||||||
switch (keycode) {
|
|
||||||
case KeyEvent.KEYCODE_MEDIA_NEXT:
|
|
||||||
pendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
|
|
||||||
new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
break;
|
|
||||||
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
|
|
||||||
pendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
|
|
||||||
new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
break;
|
|
||||||
case KeyEvent.KEYCODE_HEADSETHOOK:
|
|
||||||
case KeyEvent.KEYCODE_MEDIA_PAUSE:
|
|
||||||
case KeyEvent.KEYCODE_MEDIA_PLAY:
|
|
||||||
pendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
|
|
||||||
new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
break;
|
|
||||||
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
|
|
||||||
pendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
|
|
||||||
new Intent(ACTION_FAST_FORWARD), PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
break;
|
|
||||||
case KeyEvent.KEYCODE_MEDIA_REWIND:
|
|
||||||
pendingIntent = PendingIntent.getBroadcast(context, NOTIFICATION_ID,
|
|
||||||
new Intent(ACTION_FAST_REWIND), PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
pendingIntent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pendingIntent == null) return;
|
|
||||||
try {
|
|
||||||
pendingIntent.send();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "Error Sending intent MediaButtonReceiver", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -61,8 +61,10 @@ import org.schabi.newpipe.extractor.stream.StreamType;
|
|||||||
import org.schabi.newpipe.history.HistoryRecordManager;
|
import org.schabi.newpipe.history.HistoryRecordManager;
|
||||||
import org.schabi.newpipe.player.helper.AudioReactor;
|
import org.schabi.newpipe.player.helper.AudioReactor;
|
||||||
import org.schabi.newpipe.player.helper.LoadController;
|
import org.schabi.newpipe.player.helper.LoadController;
|
||||||
|
import org.schabi.newpipe.player.helper.MediaSessionManager;
|
||||||
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
import org.schabi.newpipe.player.helper.PlayerDataSource;
|
||||||
import org.schabi.newpipe.player.helper.PlayerHelper;
|
import org.schabi.newpipe.player.helper.PlayerHelper;
|
||||||
|
import org.schabi.newpipe.player.playback.BasePlayerMediaSession;
|
||||||
import org.schabi.newpipe.player.playback.CustomTrackSelector;
|
import org.schabi.newpipe.player.playback.CustomTrackSelector;
|
||||||
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
import org.schabi.newpipe.player.playback.MediaSourceManager;
|
||||||
import org.schabi.newpipe.player.playback.PlaybackListener;
|
import org.schabi.newpipe.player.playback.PlaybackListener;
|
||||||
@ -148,6 +150,7 @@ public abstract class BasePlayer implements
|
|||||||
|
|
||||||
protected SimpleExoPlayer simpleExoPlayer;
|
protected SimpleExoPlayer simpleExoPlayer;
|
||||||
protected AudioReactor audioReactor;
|
protected AudioReactor audioReactor;
|
||||||
|
protected MediaSessionManager mediaSessionManager;
|
||||||
|
|
||||||
private boolean isPrepared = false;
|
private boolean isPrepared = false;
|
||||||
private boolean isSynchronizing = false;
|
private boolean isSynchronizing = false;
|
||||||
@ -195,11 +198,13 @@ public abstract class BasePlayer implements
|
|||||||
final LoadControl loadControl = new LoadController(context);
|
final LoadControl loadControl = new LoadController(context);
|
||||||
final RenderersFactory renderFactory = new DefaultRenderersFactory(context);
|
final RenderersFactory renderFactory = new DefaultRenderersFactory(context);
|
||||||
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(renderFactory, trackSelector, loadControl);
|
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(renderFactory, trackSelector, loadControl);
|
||||||
audioReactor = new AudioReactor(context, simpleExoPlayer);
|
|
||||||
|
|
||||||
simpleExoPlayer.addListener(this);
|
simpleExoPlayer.addListener(this);
|
||||||
simpleExoPlayer.setPlayWhenReady(true);
|
simpleExoPlayer.setPlayWhenReady(true);
|
||||||
simpleExoPlayer.setSeekParameters(PlayerHelper.getSeekParameters(context));
|
simpleExoPlayer.setSeekParameters(PlayerHelper.getSeekParameters(context));
|
||||||
|
|
||||||
|
audioReactor = new AudioReactor(context, simpleExoPlayer);
|
||||||
|
mediaSessionManager = new MediaSessionManager(context, simpleExoPlayer,
|
||||||
|
new BasePlayerMediaSession(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initListeners() {}
|
public void initListeners() {}
|
||||||
@ -262,8 +267,8 @@ public abstract class BasePlayer implements
|
|||||||
}
|
}
|
||||||
if (isProgressLoopRunning()) stopProgressLoop();
|
if (isProgressLoopRunning()) stopProgressLoop();
|
||||||
if (playQueue != null) playQueue.dispose();
|
if (playQueue != null) playQueue.dispose();
|
||||||
|
if (audioReactor != null) audioReactor.dispose();
|
||||||
if (playbackManager != null) playbackManager.dispose();
|
if (playbackManager != null) playbackManager.dispose();
|
||||||
if (audioReactor != null) audioReactor.abandonAudioFocus();
|
|
||||||
if (databaseUpdateReactor != null) databaseUpdateReactor.dispose();
|
if (databaseUpdateReactor != null) databaseUpdateReactor.dispose();
|
||||||
|
|
||||||
if (playQueueAdapter != null) {
|
if (playQueueAdapter != null) {
|
||||||
@ -279,6 +284,7 @@ public abstract class BasePlayer implements
|
|||||||
|
|
||||||
trackSelector = null;
|
trackSelector = null;
|
||||||
simpleExoPlayer = null;
|
simpleExoPlayer = null;
|
||||||
|
mediaSessionManager = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -3,7 +3,6 @@ package org.schabi.newpipe.player.helper;
|
|||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.media.AudioFocusRequest;
|
import android.media.AudioFocusRequest;
|
||||||
@ -18,18 +17,14 @@ import com.google.android.exoplayer2.SimpleExoPlayer;
|
|||||||
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
import com.google.android.exoplayer2.audio.AudioRendererEventListener;
|
||||||
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
import com.google.android.exoplayer2.decoder.DecoderCounters;
|
||||||
|
|
||||||
public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, AudioRendererEventListener {
|
public class AudioReactor implements AudioManager.OnAudioFocusChangeListener,
|
||||||
|
AudioRendererEventListener {
|
||||||
|
|
||||||
private static final String TAG = "AudioFocusReactor";
|
private static final String TAG = "AudioFocusReactor";
|
||||||
|
|
||||||
private static final boolean SHOULD_BUILD_FOCUS_REQUEST =
|
private static final boolean SHOULD_BUILD_FOCUS_REQUEST =
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O;
|
||||||
|
|
||||||
private static final boolean CAN_USE_MEDIA_BUTTONS =
|
|
||||||
Build.VERSION.SDK_INT <= Build.VERSION_CODES.O_MR1;
|
|
||||||
private static final String MEDIA_BUTTON_DEPRECATED_ERROR =
|
|
||||||
"registerMediaButtonEventReceiver has been deprecated and maybe not supported anymore.";
|
|
||||||
|
|
||||||
private static final int DUCK_DURATION = 1500;
|
private static final int DUCK_DURATION = 1500;
|
||||||
private static final float DUCK_AUDIO_TO = .2f;
|
private static final float DUCK_AUDIO_TO = .2f;
|
||||||
|
|
||||||
@ -42,7 +37,8 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, Au
|
|||||||
|
|
||||||
private final AudioFocusRequest request;
|
private final AudioFocusRequest request;
|
||||||
|
|
||||||
public AudioReactor(@NonNull final Context context, @NonNull final SimpleExoPlayer player) {
|
public AudioReactor(@NonNull final Context context,
|
||||||
|
@NonNull final SimpleExoPlayer player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||||
@ -59,6 +55,11 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, Au
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void dispose() {
|
||||||
|
abandonAudioFocus();
|
||||||
|
player.removeAudioDebugListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// Audio Manager
|
// Audio Manager
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
@ -91,22 +92,6 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, Au
|
|||||||
audioManager.setStreamVolume(STREAM_TYPE, volume, 0);
|
audioManager.setStreamVolume(STREAM_TYPE, volume, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerMediaButtonEventReceiver(ComponentName componentName) {
|
|
||||||
if (CAN_USE_MEDIA_BUTTONS) {
|
|
||||||
audioManager.registerMediaButtonEventReceiver(componentName);
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, MEDIA_BUTTON_DEPRECATED_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void unregisterMediaButtonEventReceiver(ComponentName componentName) {
|
|
||||||
if (CAN_USE_MEDIA_BUTTONS) {
|
|
||||||
audioManager.unregisterMediaButtonEventReceiver(componentName);
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, MEDIA_BUTTON_DEPRECATED_ERROR);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*//////////////////////////////////////////////////////////////////////////
|
/*//////////////////////////////////////////////////////////////////////////
|
||||||
// AudioFocus
|
// AudioFocus
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
//////////////////////////////////////////////////////////////////////////*/
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package org.schabi.newpipe.player.helper;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.player.mediasession.DummyPlaybackPreparer;
|
||||||
|
import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
|
||||||
|
import org.schabi.newpipe.player.mediasession.PlayQueueNavigator;
|
||||||
|
import org.schabi.newpipe.player.mediasession.PlayQueuePlaybackController;
|
||||||
|
|
||||||
|
public class MediaSessionManager {
|
||||||
|
private static final String TAG = "MediaSessionManager";
|
||||||
|
|
||||||
|
private final MediaSessionCompat mediaSession;
|
||||||
|
private final MediaSessionConnector sessionConnector;
|
||||||
|
|
||||||
|
public MediaSessionManager(@NonNull final Context context,
|
||||||
|
@NonNull final Player player,
|
||||||
|
@NonNull final MediaSessionCallback callback) {
|
||||||
|
this.mediaSession = new MediaSessionCompat(context, TAG);
|
||||||
|
this.sessionConnector = new MediaSessionConnector(mediaSession,
|
||||||
|
new PlayQueuePlaybackController(callback));
|
||||||
|
this.sessionConnector.setQueueNavigator(new PlayQueueNavigator(mediaSession, callback));
|
||||||
|
this.sessionConnector.setPlayer(player, new DummyPlaybackPreparer());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaSessionCompat getMediaSession() {
|
||||||
|
return mediaSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MediaSessionConnector getSessionConnector() {
|
||||||
|
return sessionConnector;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package org.schabi.newpipe.player.mediasession;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
||||||
|
|
||||||
|
public class DummyPlaybackPreparer implements MediaSessionConnector.PlaybackPreparer {
|
||||||
|
@Override
|
||||||
|
public long getSupportedPrepareActions() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepare() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepareFromMediaId(String mediaId, Bundle extras) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepareFromSearch(String query, Bundle extras) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPrepareFromUri(Uri uri, Bundle extras) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getCommands() {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,17 @@
|
|||||||
|
package org.schabi.newpipe.player.mediasession;
|
||||||
|
|
||||||
|
import android.support.v4.media.MediaDescriptionCompat;
|
||||||
|
|
||||||
|
public interface MediaSessionCallback {
|
||||||
|
void onSkipToPrevious();
|
||||||
|
void onSkipToNext();
|
||||||
|
void onSkipToIndex(final int index);
|
||||||
|
|
||||||
|
int getCurrentPlayingIndex();
|
||||||
|
int getQueueSize();
|
||||||
|
MediaDescriptionCompat getQueueMetadata(final int index);
|
||||||
|
|
||||||
|
void onPlay();
|
||||||
|
void onPause();
|
||||||
|
void onSetShuffle(final boolean isShuffled);
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package org.schabi.newpipe.player.mediasession;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.ResultReceiver;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.media.session.MediaSessionCompat;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
|
||||||
|
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
|
||||||
|
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM;
|
||||||
|
|
||||||
|
|
||||||
|
public class PlayQueueNavigator implements MediaSessionConnector.QueueNavigator {
|
||||||
|
public static final int DEFAULT_MAX_QUEUE_SIZE = 10;
|
||||||
|
|
||||||
|
private final MediaSessionCompat mediaSession;
|
||||||
|
private final MediaSessionCallback callback;
|
||||||
|
private final int maxQueueSize;
|
||||||
|
|
||||||
|
private long activeQueueItemId;
|
||||||
|
|
||||||
|
public PlayQueueNavigator(@NonNull final MediaSessionCompat mediaSession,
|
||||||
|
@NonNull final MediaSessionCallback callback) {
|
||||||
|
this.mediaSession = mediaSession;
|
||||||
|
this.callback = callback;
|
||||||
|
this.maxQueueSize = DEFAULT_MAX_QUEUE_SIZE;
|
||||||
|
|
||||||
|
this.activeQueueItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSupportedQueueNavigatorActions(@Nullable Player player) {
|
||||||
|
return ACTION_SKIP_TO_NEXT | ACTION_SKIP_TO_PREVIOUS | ACTION_SKIP_TO_QUEUE_ITEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTimelineChanged(Player player) {
|
||||||
|
publishFloatingQueueWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCurrentWindowIndexChanged(Player player) {
|
||||||
|
if (activeQueueItemId == MediaSessionCompat.QueueItem.UNKNOWN_ID
|
||||||
|
|| player.getCurrentTimeline().getWindowCount() > maxQueueSize) {
|
||||||
|
publishFloatingQueueWindow();
|
||||||
|
} else if (!player.getCurrentTimeline().isEmpty()) {
|
||||||
|
activeQueueItemId = player.getCurrentWindowIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getActiveQueueItemId(@Nullable Player player) {
|
||||||
|
return callback.getCurrentPlayingIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToPrevious(Player player) {
|
||||||
|
callback.onSkipToPrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToQueueItem(Player player, long id) {
|
||||||
|
callback.onSkipToIndex((int) id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToNext(Player player) {
|
||||||
|
callback.onSkipToNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void publishFloatingQueueWindow() {
|
||||||
|
if (callback.getQueueSize() == 0) {
|
||||||
|
mediaSession.setQueue(Collections.<MediaSessionCompat.QueueItem>emptyList());
|
||||||
|
activeQueueItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Yes this is almost a copypasta, got a problem with that? =\
|
||||||
|
int windowCount = callback.getQueueSize();
|
||||||
|
int currentWindowIndex = callback.getCurrentPlayingIndex();
|
||||||
|
int queueSize = Math.min(maxQueueSize, windowCount);
|
||||||
|
int startIndex = Util.constrainValue(currentWindowIndex - ((queueSize - 1) / 2), 0,
|
||||||
|
windowCount - queueSize);
|
||||||
|
|
||||||
|
List<MediaSessionCompat.QueueItem> queue = new ArrayList<>();
|
||||||
|
for (int i = startIndex; i < startIndex + queueSize; i++) {
|
||||||
|
queue.add(new MediaSessionCompat.QueueItem(callback.getQueueMetadata(i), i));
|
||||||
|
}
|
||||||
|
mediaSession.setQueue(queue);
|
||||||
|
activeQueueItemId = currentWindowIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getCommands() {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCommand(Player player, String command, Bundle extras, ResultReceiver cb) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package org.schabi.newpipe.player.mediasession;
|
||||||
|
|
||||||
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
|
import com.google.android.exoplayer2.ext.mediasession.DefaultPlaybackController;
|
||||||
|
|
||||||
|
public class PlayQueuePlaybackController extends DefaultPlaybackController {
|
||||||
|
private final MediaSessionCallback callback;
|
||||||
|
|
||||||
|
public PlayQueuePlaybackController(final MediaSessionCallback callback) {
|
||||||
|
super();
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlay(Player player) {
|
||||||
|
callback.onPlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause(Player player) {
|
||||||
|
callback.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetShuffleMode(Player player, int shuffleMode) {
|
||||||
|
callback.onSetShuffle(shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_ALL
|
||||||
|
|| shuffleMode == PlaybackStateCompat.SHUFFLE_MODE_GROUP);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,77 @@
|
|||||||
|
package org.schabi.newpipe.player.playback;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.v4.media.MediaDescriptionCompat;
|
||||||
|
|
||||||
|
import org.schabi.newpipe.player.BasePlayer;
|
||||||
|
import org.schabi.newpipe.player.mediasession.MediaSessionCallback;
|
||||||
|
import org.schabi.newpipe.playlist.PlayQueueItem;
|
||||||
|
|
||||||
|
public class BasePlayerMediaSession implements MediaSessionCallback {
|
||||||
|
private BasePlayer player;
|
||||||
|
|
||||||
|
public BasePlayerMediaSession(final BasePlayer player) {
|
||||||
|
this.player = player;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToPrevious() {
|
||||||
|
player.onPlayPrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToNext() {
|
||||||
|
player.onPlayNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToIndex(int index) {
|
||||||
|
if (player.getPlayQueue() == null) return;
|
||||||
|
player.onSelected(player.getPlayQueue().getItem(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCurrentPlayingIndex() {
|
||||||
|
if (player.getPlayQueue() == null) return -1;
|
||||||
|
return player.getPlayQueue().getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getQueueSize() {
|
||||||
|
if (player.getPlayQueue() == null) return -1;
|
||||||
|
return player.getPlayQueue().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MediaDescriptionCompat getQueueMetadata(int index) {
|
||||||
|
if (player.getPlayQueue() == null || player.getPlayQueue().getItem(index) == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final PlayQueueItem item = player.getPlayQueue().getItem(index);
|
||||||
|
MediaDescriptionCompat.Builder descriptionBuilder = new MediaDescriptionCompat.Builder()
|
||||||
|
.setMediaId(String.valueOf(index))
|
||||||
|
.setTitle(item.getTitle())
|
||||||
|
.setSubtitle(item.getUploader());
|
||||||
|
|
||||||
|
final Uri thumbnailUri = Uri.parse(item.getThumbnailUrl());
|
||||||
|
if (thumbnailUri != null) descriptionBuilder.setIconUri(thumbnailUri);
|
||||||
|
|
||||||
|
return descriptionBuilder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlay() {
|
||||||
|
if (!player.isPlaying()) player.onVideoPlayPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
if (player.isPlaying()) player.onVideoPlayPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetShuffle(boolean isShuffled) {
|
||||||
|
player.onShuffleModeEnabledChanged(isShuffled);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user