mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00: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:
		| @@ -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); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user
	 John Zhen Mo
					John Zhen Mo