mirror of
https://github.com/AntennaPod/AntennaPod.git
synced 2026-08-18 02:53:10 +00:00
Update play button on wearos instantly (#8577)
### Description Update play button on wearos instantly by pushing events from the phone to the watch. Also extend documentation around wearos communication ### 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
6c69e0db15
commit
cf30423ee9
@ -2,3 +2,16 @@
|
||||
|
||||
A companion Wear OS app for AntennaPod. Can browse episodes from the phone and remote-control the phone to start playback.
|
||||
Communicates with the phone via the WearOS DataLayer API using paths and serialization defined in `:net:sync:wear-interface`.
|
||||
|
||||
## Communication with Phone
|
||||
|
||||
`WearDataListenerService` extends `WearableListenerService` and receives all incoming messages from the phone.
|
||||
Most requests are watch-initiated requests (polling regularly), rarely the phone pushes events.
|
||||
|
||||
`WearDataListenerService` decodes received payloads via `WearSerializer`.
|
||||
`WearDataRepository` is a singleton holding `StateFlow`s for each data type.
|
||||
Composables and ViewModels collect from these flows to stay up to date.
|
||||
Updated exclusively by `WearDataListenerService`.
|
||||
|
||||
`WearMessageSender.send(context, path)` sends a message to the first connected phone.
|
||||
Use this to trigger actions (playback control) or to request data (the phone then replies with the data on the same path).
|
||||
|
||||
@ -5,3 +5,9 @@ The main application module that integrates all features and hosts app-specific
|
||||
|
||||
The miniplayer (the collapsed player bar at the bottom of the screen) is implemented in `ExternalPlayerFragment`.
|
||||
It is hosted in `MainActivity` as a bottom sheet. `MainActivity` controls its visibility via `setPlayerVisible()` based on playback state.
|
||||
|
||||
## Wear OS Communication (play flavor only)
|
||||
|
||||
`WearListenerService` is a `WearableListenerService` that handles `DataLayer` messages from connected watches.
|
||||
It responds to watch-initiated requests.
|
||||
The service is kept alive by the Android framework while at least one watch is connected.
|
||||
|
||||
@ -2,7 +2,9 @@ package de.danoeh.antennapod;
|
||||
|
||||
import android.util.Log;
|
||||
import com.google.android.gms.wearable.MessageEvent;
|
||||
import com.google.android.gms.wearable.Node;
|
||||
import com.google.android.gms.wearable.Wearable;
|
||||
import de.danoeh.antennapod.event.PlayerStatusEvent;
|
||||
import de.danoeh.antennapod.playback.service.PlaybackService;
|
||||
import de.danoeh.antennapod.storage.preferences.PlaybackPreferences;
|
||||
import io.reactivex.rxjava3.core.Completable;
|
||||
@ -20,6 +22,9 @@ import de.danoeh.antennapod.net.sync.wearinterface.WearDataPaths;
|
||||
import de.danoeh.antennapod.net.sync.wearinterface.WearSerializer;
|
||||
import de.danoeh.antennapod.playback.service.PlaybackServiceStarter;
|
||||
import de.danoeh.antennapod.storage.database.DBReader;
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -27,6 +32,34 @@ public class WearListenerService extends WearableListenerService {
|
||||
private static final String TAG = "WearListenerService";
|
||||
private static final int MAX_ITEMS = 100;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.BACKGROUND)
|
||||
public void onPlayerStatusEvent(PlayerStatusEvent event) {
|
||||
FeedMedia media = DBReader.getFeedMedia(PlaybackPreferences.getCurrentlyPlayingFeedMediaId());
|
||||
if (media == null || media.getItem() == null) {
|
||||
return;
|
||||
}
|
||||
boolean isPlaying = PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PLAYING;
|
||||
byte[] payload = WearSerializer.nowPlayingToBytes(media.getItem(), isPlaying);
|
||||
Wearable.getNodeClient(this).getConnectedNodes()
|
||||
.addOnSuccessListener(nodes -> {
|
||||
for (Node node : nodes) {
|
||||
Wearable.getMessageClient(this).sendMessage(node.getId(), WearDataPaths.NOW_PLAYING, payload);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(MessageEvent event) {
|
||||
String path = event.getPath();
|
||||
|
||||
@ -2,3 +2,19 @@
|
||||
|
||||
This module contains the shared data paths and serialization logic for communication between the phone app and the Wear OS companion app.
|
||||
`WearDataPaths` defines message path constants; `WearSerializer` handles JSON encoding. Both `:app` and `:app-wearos` depend on this module.
|
||||
|
||||
## Communication
|
||||
|
||||
Most requests are watch-initiated: the watch sends a message to a path (e.g. `WearDataPaths.QUEUE`), the phone responds with data on the same path.
|
||||
Rarely, the phone proactively sends a message (playback state changes).
|
||||
Both directions use `Wearable.getMessageClient().sendMessage(nodeId, path, payload)`.
|
||||
The actual communication is located in `:app` and `:app-wearos`, the `:net:sync:wear-interface` defines the common protocol.
|
||||
|
||||
## Path constants (`WearDataPaths`)
|
||||
|
||||
- `NOW_PLAYING`: current episode + playing state; pushed by phone on player status changes and returned on watch request
|
||||
- `QUEUE`, `DOWNLOADS`, `EPISODES`, `SUBSCRIPTIONS`: episode/feed lists
|
||||
- `PLAY_PREFIX + itemId`: watch sends to start playback of an episode on the phone
|
||||
- `PAUSE`: watch sends to pause playback on the phone
|
||||
- `FEED_EPISODES_PREFIX + feedId`: watch requests episodes for a specific feed
|
||||
- `OPEN_ON_PHONE_PREFIX + itemId`: watch requests the phone to open an episode in the main app
|
||||
|
||||
Reference in New Issue
Block a user