Skip to content

Commit c6024a0

Browse files
committed
minimal port to media3
1 parent 790bf54 commit c6024a0

5 files changed

Lines changed: 309 additions & 496 deletions

File tree

app/build.gradle

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,13 +79,15 @@ dependencies {
7979
implementation project(':ServerProxy')
8080
implementation fileTree(include: ['*.jar'], dir: 'libs')
8181
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
82-
implementation 'androidx.media:media:1.7.0'
82+
implementation 'androidx.media3:media3-session:1.9.2'
83+
implementation 'androidx.media3:media3-common:1.9.2'
8384
implementation 'androidx.appcompat:appcompat:1.7.1'
8485
implementation 'androidx.mediarouter:mediarouter:1.8.1'
8586
implementation 'androidx.recyclerview:recyclerview:1.4.0'
8687
implementation 'androidx.activity:activity:1.10.1'
8788
implementation 'com.google.android.material:material:1.13.0'
88-
googleImplementation 'com.google.android.gms:play-services-cast:22.1.0'
89+
90+
googleImplementation 'com.google.android.gms:play-services-cast:22.1.0'
8991
implementation 'com.sothree.slidinguppanel:library:3.4.0'
9092
implementation 'de.hdodenhof:circleimageview:3.1.0'
9193
implementation 'com.shehabic.droppy:Droppy:0.6.0@aar'

app/src/main/java/github/paroj/dsub2000/service/AutoMediaBrowserService.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@
2525
import androidx.annotation.Nullable;
2626
import android.support.v4.media.MediaBrowserCompat;
2727
import androidx.media.MediaBrowserServiceCompat;
28+
import androidx.media3.session.MediaSession;
29+
import androidx.annotation.OptIn;
30+
import androidx.media3.common.util.UnstableApi;
2831
import android.support.v4.media.MediaDescriptionCompat;
32+
import android.support.v4.media.session.MediaSessionCompat;
2933
import android.util.Log;
3034

3135
import java.io.IOException;
@@ -662,7 +666,15 @@ public void run() {
662666
}, 100);
663667
} else {
664668
RemoteControlClientLP remoteControlClient = (RemoteControlClientLP) downloadService.getRemoteControlClient();
665-
setSessionToken(remoteControlClient.getMediaSession().getSessionToken());
669+
MediaSession session = remoteControlClient.getMediaSession();
670+
if (session != null) {
671+
setSessionToken(getSessionToken(session));
672+
}
666673
}
667674
}
675+
676+
@OptIn(markerClass = UnstableApi.class)
677+
private MediaSessionCompat.Token getSessionToken(MediaSession session) {
678+
return MediaSessionCompat.Token.fromToken(session.getPlatformToken());
679+
}
668680
}

app/src/main/java/github/paroj/dsub2000/util/Notifications.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727
import android.os.Build;
2828
import android.os.Handler;
2929
import androidx.core.app.NotificationCompat;
30+
import androidx.annotation.OptIn;
31+
import androidx.media3.common.util.UnstableApi;
32+
import android.support.v4.media.session.MediaSessionCompat;
3033

3134
import android.util.Log;
3235
import android.view.KeyEvent;
@@ -79,9 +82,11 @@ public static void showPlayingNotification(final Context context, final Download
7982

8083
if (usingMediaStyleNotification) {
8184
RemoteControlClientLP remoteControlClient = (RemoteControlClientLP) downloadService.getRemoteControlClient();
85+
androidx.media3.session.MediaSession session = remoteControlClient.getMediaSession();
86+
MediaSessionCompat.Token token = getSessionToken(session);
8287

8388
androidx.media.app.NotificationCompat.MediaStyle mediaStyle = new androidx.media.app.NotificationCompat.MediaStyle()
84-
.setMediaSession(remoteControlClient.getMediaSession().getSessionToken());
89+
.setMediaSession(token);
8590

8691
if (isSingle) {
8792
mediaStyle.setShowActionsInCompactView(1);
@@ -634,4 +639,12 @@ private static void stopForeground(DownloadService downloadService, boolean remo
634639
downloadService.stopForeground(removeNotification);
635640
downloadService.setIsForeground(false);
636641
}
642+
643+
@OptIn(markerClass = UnstableApi.class)
644+
private static MediaSessionCompat.Token getSessionToken(androidx.media3.session.MediaSession session) {
645+
if (session != null) {
646+
return MediaSessionCompat.Token.fromToken(session.getPlatformToken());
647+
}
648+
return null;
649+
}
637650
}
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
This file is part of Subsonic.
3+
Subsonic is free software: you can redistribute it and/or modify
4+
it under the terms of the GNU General Public License as published by
5+
the Free Software Foundation, either version 3 of the License, or
6+
(at your option) any later version.
7+
Subsonic is distributed in the hope that it will be useful,
8+
but WITHOUT ANY WARRANTY; without even the implied warranty of
9+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10+
GNU General Public License for more details.
11+
You should have received a copy of the GNU General Public License
12+
along with Subsonic. If not, see <http://www.gnu.org/licenses/>.
13+
Copyright 2014 (C) Scott Jackson
14+
*/
15+
16+
package github.paroj.dsub2000.util.compat;
17+
18+
import android.os.Handler;
19+
import android.os.Looper;
20+
import androidx.annotation.NonNull;
21+
import androidx.annotation.OptIn;
22+
import androidx.media3.common.Player;
23+
import androidx.media3.common.SimpleBasePlayer;
24+
import androidx.media3.common.util.UnstableApi;
25+
import github.paroj.dsub2000.service.DownloadService;
26+
import github.paroj.dsub2000.service.DownloadFile;
27+
import com.google.common.util.concurrent.Futures;
28+
import com.google.common.util.concurrent.ListenableFuture;
29+
import java.util.Collections;
30+
import java.util.List;
31+
32+
@OptIn(markerClass = UnstableApi.class)
33+
public class DSubPlayer extends SimpleBasePlayer {
34+
private final DownloadService downloadService;
35+
private State state;
36+
37+
public DSubPlayer(Looper looper, DownloadService downloadService) {
38+
super(looper);
39+
this.downloadService = downloadService;
40+
state = new State.Builder()
41+
.setAvailableCommands(new Player.Commands.Builder().addAllCommands().build())
42+
.setPlaylist(Collections.emptyList())
43+
.build();
44+
}
45+
46+
@Override
47+
protected State getState() {
48+
return state;
49+
}
50+
51+
public void setInternalState(State newState) {
52+
if (Looper.myLooper() == getApplicationLooper()) {
53+
state = newState;
54+
invalidateState();
55+
} else {
56+
new Handler(getApplicationLooper()).post(() -> setInternalState(newState));
57+
}
58+
}
59+
60+
public interface UpdateAction {
61+
void update(State.Builder builder);
62+
}
63+
64+
public void updateState(UpdateAction action) {
65+
if (Looper.myLooper() == getApplicationLooper()) {
66+
State.Builder builder = state.buildUpon();
67+
action.update(builder);
68+
setInternalState(builder.build());
69+
} else {
70+
new Handler(getApplicationLooper()).post(() -> updateState(action));
71+
}
72+
}
73+
74+
public State.Builder getStateBuilder() {
75+
return state.buildUpon();
76+
}
77+
78+
@NonNull
79+
@Override
80+
protected ListenableFuture<?> handleSetPlayWhenReady(boolean playWhenReady) {
81+
if (playWhenReady) {
82+
downloadService.start();
83+
} else {
84+
downloadService.pause();
85+
}
86+
return Futures.immediateVoidFuture();
87+
}
88+
89+
@NonNull
90+
@Override
91+
protected ListenableFuture<?> handleSeek(int mediaItemIndex, long positionMs, int seekCommand) {
92+
if (mediaItemIndex != state.currentMediaItemIndex) {
93+
List<DownloadFile> queue = downloadService.getSongs();
94+
if (queue != null && mediaItemIndex >= 0 && mediaItemIndex < queue.size()) {
95+
downloadService.play(queue.get(mediaItemIndex));
96+
}
97+
} else {
98+
downloadService.seekTo((int) positionMs);
99+
}
100+
return Futures.immediateVoidFuture();
101+
}
102+
}

0 commit comments

Comments
 (0)