11package org .schabi .newpipe .player .mediasession ;
22
33import static org .schabi .newpipe .MainActivity .DEBUG ;
4+ import static org .schabi .newpipe .player .notification .NotificationConstants .ACTION_RECREATE_NOTIFICATION ;
45
56import android .content .Intent ;
67import android .content .SharedPreferences ;
78import android .graphics .Bitmap ;
9+ import android .os .Build ;
810import android .support .v4 .media .MediaMetadataCompat ;
911import android .support .v4 .media .session .MediaSessionCompat ;
1012import android .util .Log ;
1416import androidx .media .session .MediaButtonReceiver ;
1517
1618import com .google .android .exoplayer2 .ForwardingPlayer ;
19+ import com .google .android .exoplayer2 .Player .RepeatMode ;
1720import com .google .android .exoplayer2 .ext .mediasession .MediaSessionConnector ;
1821
1922import org .schabi .newpipe .R ;
23+ import org .schabi .newpipe .extractor .stream .StreamInfo ;
2024import org .schabi .newpipe .player .Player ;
25+ import org .schabi .newpipe .player .notification .NotificationActionData ;
26+ import org .schabi .newpipe .player .notification .NotificationConstants ;
2127import org .schabi .newpipe .player .ui .PlayerUi ;
2228import org .schabi .newpipe .player .ui .VideoPlayerUi ;
2329import org .schabi .newpipe .util .StreamTypeUtil ;
2430
31+ import java .util .List ;
32+ import java .util .Objects ;
2533import java .util .Optional ;
34+ import java .util .stream .Collectors ;
35+ import java .util .stream .IntStream ;
2636
2737public class MediaSessionPlayerUi extends PlayerUi
2838 implements SharedPreferences .OnSharedPreferenceChangeListener {
@@ -34,6 +44,10 @@ public class MediaSessionPlayerUi extends PlayerUi
3444 private final String ignoreHardwareMediaButtonsKey ;
3545 private boolean shouldIgnoreHardwareMediaButtons = false ;
3646
47+ // used to check whether any notification action changed, before sending costly updates
48+ private List <NotificationActionData > prevNotificationActions = List .of ();
49+
50+
3751 public MediaSessionPlayerUi (@ NonNull final Player player ) {
3852 super (player );
3953 ignoreHardwareMediaButtonsKey =
@@ -63,6 +77,10 @@ public void initPlayer() {
6377
6478 sessionConnector .setMetadataDeduplicationEnabled (true );
6579 sessionConnector .setMediaMetadataProvider (exoPlayer -> buildMediaMetadata ());
80+
81+ // force updating media session actions by resetting the previous ones
82+ prevNotificationActions = List .of ();
83+ updateMediaSessionActions ();
6684 }
6785
6886 @ Override
@@ -80,6 +98,7 @@ public void destroyPlayer() {
8098 mediaSession .release ();
8199 mediaSession = null ;
82100 }
101+ prevNotificationActions = List .of ();
83102 }
84103
85104 @ Override
@@ -163,4 +182,109 @@ private MediaMetadataCompat buildMediaMetadata() {
163182
164183 return builder .build ();
165184 }
185+
186+
187+ private void updateMediaSessionActions () {
188+ // On Android 13+ (or Android T or API 33+) the actions in the player notification can't be
189+ // controlled directly anymore, but are instead derived from custom media session actions.
190+ // However the system allows customizing only two of these actions, since the other three
191+ // are fixed to play-pause-buffering, previous, next.
192+
193+ if (Build .VERSION .SDK_INT < Build .VERSION_CODES .TIRAMISU ) {
194+ // Although setting media session actions on older android versions doesn't seem to
195+ // cause any trouble, it also doesn't seem to do anything, so we don't do anything to
196+ // save battery. Check out NotificationUtil.updateActions() to see what happens on
197+ // older android versions.
198+ return ;
199+ }
200+
201+ // only use the fourth and fifth actions (the settings page also shows only the last 2 on
202+ // Android 13+)
203+ final List <NotificationActionData > newNotificationActions = IntStream .of (3 , 4 )
204+ .map (i -> player .getPrefs ().getInt (
205+ player .getContext ().getString (NotificationConstants .SLOT_PREF_KEYS [i ]),
206+ NotificationConstants .SLOT_DEFAULTS [i ]))
207+ .mapToObj (action -> NotificationActionData
208+ .fromNotificationActionEnum (player , action ))
209+ .filter (Objects ::nonNull )
210+ .collect (Collectors .toList ());
211+
212+ // avoid costly notification actions update, if nothing changed from last time
213+ if (!newNotificationActions .equals (prevNotificationActions )) {
214+ prevNotificationActions = newNotificationActions ;
215+ sessionConnector .setCustomActionProviders (
216+ newNotificationActions .stream ()
217+ .map (data -> new SessionConnectorActionProvider (data , context ))
218+ .toArray (SessionConnectorActionProvider []::new ));
219+ }
220+ }
221+
222+ @ Override
223+ public void onBlocked () {
224+ super .onBlocked ();
225+ updateMediaSessionActions ();
226+ }
227+
228+ @ Override
229+ public void onPlaying () {
230+ super .onPlaying ();
231+ updateMediaSessionActions ();
232+ }
233+
234+ @ Override
235+ public void onBuffering () {
236+ super .onBuffering ();
237+ updateMediaSessionActions ();
238+ }
239+
240+ @ Override
241+ public void onPaused () {
242+ super .onPaused ();
243+ updateMediaSessionActions ();
244+ }
245+
246+ @ Override
247+ public void onPausedSeek () {
248+ super .onPausedSeek ();
249+ updateMediaSessionActions ();
250+ }
251+
252+ @ Override
253+ public void onCompleted () {
254+ super .onCompleted ();
255+ updateMediaSessionActions ();
256+ }
257+
258+ @ Override
259+ public void onRepeatModeChanged (@ RepeatMode final int repeatMode ) {
260+ super .onRepeatModeChanged (repeatMode );
261+ updateMediaSessionActions ();
262+ }
263+
264+ @ Override
265+ public void onShuffleModeEnabledChanged (final boolean shuffleModeEnabled ) {
266+ super .onShuffleModeEnabledChanged (shuffleModeEnabled );
267+ updateMediaSessionActions ();
268+ }
269+
270+ @ Override
271+ public void onBroadcastReceived (final Intent intent ) {
272+ super .onBroadcastReceived (intent );
273+ if (ACTION_RECREATE_NOTIFICATION .equals (intent .getAction ())) {
274+ // the notification actions changed
275+ updateMediaSessionActions ();
276+ }
277+ }
278+
279+ @ Override
280+ public void onMetadataChanged (@ NonNull final StreamInfo info ) {
281+ super .onMetadataChanged (info );
282+ updateMediaSessionActions ();
283+ }
284+
285+ @ Override
286+ public void onPlayQueueEdited () {
287+ super .onPlayQueueEdited ();
288+ updateMediaSessionActions ();
289+ }
166290}
0 commit comments