mirror of
https://github.com/AntennaPod/AntennaPod.git
synced 2026-08-18 11:05:49 +00:00
Fix state of cast device when connecting while service is stopped (#8596)
### Description When freshly binding to the service to start playback (while chromecast is already connected), the service gets started but casting does not transition to PLAYING fast enough. So when unbinding, the service gets destroyed again and releases the session, which stops casting again. So we manually start the service when connected to Chromecast. We also send the media information (in paused state), so that we do not get stuck on the AntennaPod logo. Closes #8581 ### Checklist <!-- To help us keep the issue tracker clean and work as efficient as possible, please make sure that you have done all of the following. You can tick the boxes below by placing an x inside the brackets like this: [x] --> - [x] I have read the contribution guidelines: https://github.com/AntennaPod/AntennaPod/blob/develop/CONTRIBUTING.md#submit-a-pull-request - [x] I have performed a self-review of my code, going through my changes line by line and carefully considering why this line change is necessary - [x] I have run the automated code checks using `./gradlew checkstyle lint` - [x] My code follows the style guidelines of the AntennaPod project: https://antennapod.org/contribute/develop/app/code-style - [x] I have mentioned the corresponding issue and the relevant keyword (e.g., "Closes: #xy") in the description (see https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue) - [x] If it is a core feature, I have added automated tests
This commit is contained in:
committed by
GitHub
parent
2d714739f7
commit
d4eb078a52
@ -37,16 +37,12 @@ public class MediaItemAdapter {
|
|||||||
* Create a basic media item without attached metadata.
|
* Create a basic media item without attached metadata.
|
||||||
* Should be used when initiating playback from outside the service.
|
* Should be used when initiating playback from outside the service.
|
||||||
*/
|
*/
|
||||||
public static MediaItem fromPlayableStub(Playable playable) {
|
public static MediaItem fromMediaIdStub(long mediaId) {
|
||||||
MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
|
MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
|
||||||
metadataBuilder.setIsPlayable(true);
|
metadataBuilder.setIsPlayable(true);
|
||||||
metadataBuilder.setIsBrowsable(false);
|
metadataBuilder.setIsBrowsable(false);
|
||||||
String mediaId = "0";
|
|
||||||
if (playable instanceof FeedMedia) {
|
|
||||||
mediaId = String.valueOf(((FeedMedia) playable).getId());
|
|
||||||
}
|
|
||||||
return new MediaItem.Builder()
|
return new MediaItem.Builder()
|
||||||
.setMediaId(mediaId)
|
.setMediaId(String.valueOf(mediaId))
|
||||||
.setMediaMetadata(metadataBuilder.build())
|
.setMediaMetadata(metadataBuilder.build())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,46 @@
|
|||||||
|
package de.danoeh.antennapod.playback.cast;
|
||||||
|
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.util.Log;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.mediarouter.app.MediaRouteActionProvider;
|
||||||
|
import androidx.mediarouter.app.MediaRouteButton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the currently playing episode directly after connecting to the cast device
|
||||||
|
* by starting the playback service.
|
||||||
|
*/
|
||||||
|
public class ServiceStartingMediaRouteActionProvider extends MediaRouteActionProvider {
|
||||||
|
private static final String TAG = "SrvStartMediaRouteBtn";
|
||||||
|
|
||||||
|
public ServiceStartingMediaRouteActionProvider(@NonNull Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public MediaRouteButton onCreateMediaRouteButton() {
|
||||||
|
return new ServiceStartingMediaRouteButton(getContext());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ServiceStartingMediaRouteButton extends MediaRouteButton {
|
||||||
|
ServiceStartingMediaRouteButton(@NonNull Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean performClick() {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setComponent(new ComponentName(getContext(),
|
||||||
|
"de.danoeh.antennapod.playback.service.Media3PlaybackService"));
|
||||||
|
try {
|
||||||
|
getContext().startService(intent);
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
Log.e(TAG, "Unable to start playback service", e);
|
||||||
|
}
|
||||||
|
return super.performClick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@
|
|||||||
<item
|
<item
|
||||||
android:id="@+id/media_route_menu_item"
|
android:id="@+id/media_route_menu_item"
|
||||||
android:title=""
|
android:title=""
|
||||||
app:actionProviderClass="androidx.mediarouter.app.MediaRouteActionProvider"
|
app:actionProviderClass="de.danoeh.antennapod.playback.cast.ServiceStartingMediaRouteActionProvider"
|
||||||
app:showAsAction="always" />
|
app:showAsAction="always" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package de.danoeh.antennapod.playback.service;
|
package de.danoeh.antennapod.playback.service;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.media.audiofx.LoudnessEnhancer;
|
import android.media.audiofx.LoudnessEnhancer;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -8,6 +9,7 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.OptIn;
|
import androidx.annotation.OptIn;
|
||||||
import androidx.core.util.Pair;
|
import androidx.core.util.Pair;
|
||||||
|
import androidx.media3.common.DeviceInfo;
|
||||||
import androidx.media3.common.ForwardingPlayer;
|
import androidx.media3.common.ForwardingPlayer;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
@ -183,6 +185,39 @@ public class Media3PlaybackService extends MediaLibraryService {
|
|||||||
mediaSession = new MediaLibraryService.MediaLibrarySession.Builder(this, player, sessionCallback)
|
mediaSession = new MediaLibraryService.MediaLibrarySession.Builder(this, player, sessionCallback)
|
||||||
.setSessionActivity(new MainActivityStarter(this).withOpenPlayer().getPendingIntent())
|
.setSessionActivity(new MainActivityStarter(this).withOpenPlayer().getPendingIntent())
|
||||||
.build();
|
.build();
|
||||||
|
if (isCasting()) {
|
||||||
|
keepServiceRunningWhileCasting();
|
||||||
|
loadCurrentMediaWhileCasting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCurrentMediaWhileCasting() {
|
||||||
|
long mediaId = PlaybackPreferences.getCurrentlyPlayingFeedMediaId();
|
||||||
|
if (mediaId == PlaybackPreferences.NO_MEDIA_PLAYING) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
PlaybackController.bindToMedia3Service(this, controller -> {
|
||||||
|
if (player.getCurrentMediaItem() != null || !isCasting()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
controller.setPlayWhenReady(false);
|
||||||
|
controller.setMediaItem(MediaItemAdapter.fromMediaIdStub(mediaId));
|
||||||
|
controller.prepare();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When freshly binding to the service to start playback (while chromecast is already connected),
|
||||||
|
* the service gets started but casting does not transition to PLAYING fast enough. So when unbinding,
|
||||||
|
* the service gets destroyed again and releases the session, which stops casting again.
|
||||||
|
* This method manually starts the service, to be used when connected to chromecast.
|
||||||
|
*/
|
||||||
|
private void keepServiceRunningWhileCasting() {
|
||||||
|
try {
|
||||||
|
startService(new Intent(this, Media3PlaybackService.class));
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
Log.e(TAG, "Unable to keep service running while casting", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaLibrarySessionCallback sessionCallback = new MediaLibrarySessionCallback(this) {
|
MediaLibrarySessionCallback sessionCallback = new MediaLibrarySessionCallback(this) {
|
||||||
@ -292,6 +327,14 @@ public class Media3PlaybackService extends MediaLibraryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeviceInfoChanged(@NonNull DeviceInfo deviceInfo) {
|
||||||
|
if (deviceInfo.playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE) {
|
||||||
|
keepServiceRunningWhileCasting();
|
||||||
|
loadCurrentMediaWhileCasting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) {
|
public void onMediaItemTransition(@Nullable MediaItem mediaItem, int reason) {
|
||||||
if (mediaItem == null) {
|
if (mediaItem == null) {
|
||||||
@ -425,7 +468,7 @@ public class Media3PlaybackService extends MediaLibraryService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (needsStreaming(media) && !NetworkUtils.isStreamingAllowed()
|
if (needsStreaming(media) && !NetworkUtils.isStreamingAllowed()
|
||||||
&& !allowStreamingThisTime) {
|
&& !allowStreamingThisTime && !isCasting()) {
|
||||||
showStreamingConfirmation(media);
|
showStreamingConfirmation(media);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -576,7 +619,8 @@ public class Media3PlaybackService extends MediaLibraryService {
|
|||||||
&& (player.getPlaybackState() == Player.STATE_READY
|
&& (player.getPlaybackState() == Player.STATE_READY
|
||||||
|| player.getPlaybackState() == Player.STATE_BUFFERING)
|
|| player.getPlaybackState() == Player.STATE_BUFFERING)
|
||||||
&& needsStreaming(currentPlayable)
|
&& needsStreaming(currentPlayable)
|
||||||
&& !NetworkUtils.isStreamingAllowed();
|
&& !NetworkUtils.isStreamingAllowed()
|
||||||
|
&& !isCasting();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleStreamingConfirmation() {
|
private boolean handleStreamingConfirmation() {
|
||||||
@ -639,6 +683,10 @@ public class Media3PlaybackService extends MediaLibraryService {
|
|||||||
startNextInQueue(media, false, true);
|
startNextInQueue(media, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCasting() {
|
||||||
|
return player.getDeviceInfo().playbackType == DeviceInfo.PLAYBACK_TYPE_REMOTE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the next item, and starts it if continuous playback is enabled.
|
* Loads the next item, and starts it if continuous playback is enabled.
|
||||||
*/
|
*/
|
||||||
@ -671,7 +719,7 @@ public class Media3PlaybackService extends MediaLibraryService {
|
|||||||
final FeedMedia nextMedia = pair.first;
|
final FeedMedia nextMedia = pair.first;
|
||||||
final MediaItem nextMediaItem = pair.second;
|
final MediaItem nextMediaItem = pair.second;
|
||||||
if (needsStreaming(nextMedia) && !NetworkUtils.isStreamingAllowed()
|
if (needsStreaming(nextMedia) && !NetworkUtils.isStreamingAllowed()
|
||||||
&& !allowStreamingThisTime) {
|
&& !allowStreamingThisTime && !isCasting()) {
|
||||||
showStreamingConfirmation(nextMedia);
|
showStreamingConfirmation(nextMedia);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ public class PlaybackServiceStarter {
|
|||||||
== DeviceInfo.PLAYBACK_TYPE_REMOTE) {
|
== DeviceInfo.PLAYBACK_TYPE_REMOTE) {
|
||||||
controller.play(); // Casting somehow does not play when not quickly starting the old episode
|
controller.play(); // Casting somehow does not play when not quickly starting the old episode
|
||||||
}
|
}
|
||||||
controller.setMediaItem(MediaItemAdapter.fromPlayableStub(media));
|
controller.setMediaItem(MediaItemAdapter.fromMediaIdStub(((FeedMedia) media).getId()));
|
||||||
controller.prepare();
|
controller.prepare();
|
||||||
controller.play();
|
controller.play();
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user