mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-01-10 17:11:56 +01:00
Playback state tracking improvements
This commit is contained in:
parent
d637b9263c
commit
83dba19c60
@ -0,0 +1,124 @@
|
||||
/* Copyright (C) 2024 Aleksandr Ivanov
|
||||
|
||||
This file is part of Gadgetbridge.
|
||||
|
||||
Gadgetbridge is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Gadgetbridge is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
||||
|
||||
package nodomain.freeyourgadget.gadgetbridge.externalevents;
|
||||
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.PlaybackState;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.MediaManager;
|
||||
|
||||
public class MediaStateReceiver extends MediaController.Callback {
|
||||
private long lastPosition = 0;
|
||||
private long lastUpdateTime = 0;
|
||||
|
||||
private MediaController mMediaController;
|
||||
|
||||
public MediaStateReceiver(MediaController mediaController) {
|
||||
mMediaController = mediaController;
|
||||
}
|
||||
|
||||
public boolean isPlaybackActive() {
|
||||
// https://developer.android.com/reference/android/media/session/PlaybackState#isActive()
|
||||
|
||||
switch (mMediaController.getPlaybackState().getState()) {
|
||||
case PlaybackState.STATE_BUFFERING:
|
||||
case PlaybackState.STATE_CONNECTING:
|
||||
case PlaybackState.STATE_FAST_FORWARDING:
|
||||
case PlaybackState.STATE_PLAYING:
|
||||
case PlaybackState.STATE_REWINDING:
|
||||
case PlaybackState.STATE_SKIPPING_TO_NEXT:
|
||||
case PlaybackState.STATE_SKIPPING_TO_PREVIOUS:
|
||||
case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void startReceiving() {
|
||||
setMetadata(mMediaController.getMetadata());
|
||||
setPlaybackState(mMediaController.getPlaybackState());
|
||||
mMediaController.registerCallback(this);
|
||||
}
|
||||
|
||||
public void stopReceiving() {
|
||||
mMediaController.unregisterCallback(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMetadataChanged(@Nullable MediaMetadata metadata) {
|
||||
super.onMetadataChanged(metadata);
|
||||
setMetadata(metadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(@Nullable PlaybackState state) {
|
||||
super.onPlaybackStateChanged(state);
|
||||
setPlaybackState(state);
|
||||
}
|
||||
|
||||
private void setMetadata(@Nullable MediaMetadata metadata) {
|
||||
final MusicSpec musicSpec = MediaManager.extractMusicSpec(metadata);
|
||||
|
||||
if (musicSpec != null) {
|
||||
GBApplication.deviceService().onSetMusicInfo(musicSpec);
|
||||
}
|
||||
}
|
||||
|
||||
private void setPlaybackState(@Nullable PlaybackState state) {
|
||||
if (state.getState() == PlaybackState.STATE_PLAYING && !doStateUpdate(state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final MusicStateSpec stateSpec = MediaManager.extractMusicStateSpec(state);
|
||||
|
||||
if (stateSpec != null) {
|
||||
GBApplication.deviceService().onSetMusicState(stateSpec);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doStateUpdate(PlaybackState state)
|
||||
{
|
||||
// To prevent spamming device with state updates
|
||||
|
||||
float speed = state.getPlaybackSpeed();
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
long currentPosition = state.getPosition();
|
||||
|
||||
long timeDiff = (long) (speed * (currentTime - lastUpdateTime));
|
||||
long positionDiff = currentPosition - lastPosition;
|
||||
|
||||
long epsilon = (long) Math.abs(speed * 50);
|
||||
|
||||
if (Math.abs(timeDiff - positionDiff) > epsilon)
|
||||
{
|
||||
lastUpdateTime = currentTime;
|
||||
lastPosition = currentPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -36,7 +36,6 @@ import android.graphics.drawable.Drawable;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSession;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
@ -75,14 +74,11 @@ import nodomain.freeyourgadget.gadgetbridge.externalevents.notifications.GoogleM
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.AppNotificationType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.DeviceCommunicationService;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.MediaManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.NotificationUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.PebbleUtils;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
@ -126,9 +122,7 @@ public class NotificationListener extends NotificationListenerService {
|
||||
private long activeCallPostTime;
|
||||
private int mLastCallCommand = CallSpec.CALL_UNDEFINED;
|
||||
|
||||
private final Handler mHandler = new Handler();
|
||||
private Runnable mSetMusicInfoRunnable = null;
|
||||
private Runnable mSetMusicStateRunnable = null;
|
||||
private MediaStateReceiver mMediaStateReceiver = null;
|
||||
|
||||
private GoogleMapsNotificationHandler googleMapsNotificationHandler = new GoogleMapsNotificationHandler();
|
||||
|
||||
@ -245,6 +239,11 @@ public class NotificationListener extends NotificationListenerService {
|
||||
LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
|
||||
notificationStack.clear();
|
||||
notificationsActive.clear();
|
||||
|
||||
if (mMediaStateReceiver != null) {
|
||||
mMediaStateReceiver.stopReceiving();
|
||||
}
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@ -669,7 +668,7 @@ public class NotificationListener extends NotificationListenerService {
|
||||
|
||||
private boolean handleMediaSessionNotification(final StatusBarNotification sbn) {
|
||||
final MediaSession.Token token = sbn.getNotification().extras.getParcelable(Notification.EXTRA_MEDIA_SESSION);
|
||||
return token != null && handleMediaSessionNotification(token);
|
||||
return handleMediaSessionNotification(token);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -680,38 +679,29 @@ public class NotificationListener extends NotificationListenerService {
|
||||
*/
|
||||
public boolean handleMediaSessionNotification(MediaSession.Token mediaSession) {
|
||||
try {
|
||||
final MediaController c = new MediaController(getApplicationContext(), mediaSession);
|
||||
if (c.getMetadata() == null) {
|
||||
if (mediaSession == null)
|
||||
{
|
||||
if (mMediaStateReceiver != null) {
|
||||
mMediaStateReceiver.stopReceiving();
|
||||
mMediaStateReceiver = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
final MusicStateSpec stateSpec = MediaManager.extractMusicStateSpec(c.getPlaybackState());
|
||||
final MusicSpec musicSpec = MediaManager.extractMusicSpec(c.getMetadata());
|
||||
MediaController mediaController = new MediaController(getApplicationContext(), mediaSession);
|
||||
MediaStateReceiver mediaStateReceiver = new MediaStateReceiver(mediaController);
|
||||
|
||||
// finally, tell the device about it
|
||||
if (mSetMusicInfoRunnable != null) {
|
||||
mHandler.removeCallbacks(mSetMusicInfoRunnable);
|
||||
if (mMediaStateReceiver != null && mediaStateReceiver.isPlaybackActive())
|
||||
{
|
||||
mMediaStateReceiver.stopReceiving();
|
||||
mMediaStateReceiver = null;
|
||||
}
|
||||
mSetMusicInfoRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSetMusicInfo(musicSpec);
|
||||
}
|
||||
};
|
||||
mHandler.postDelayed(mSetMusicInfoRunnable, 100);
|
||||
|
||||
if (stateSpec != null) {
|
||||
if (mSetMusicStateRunnable != null) {
|
||||
mHandler.removeCallbacks(mSetMusicStateRunnable);
|
||||
if (mMediaStateReceiver == null) {
|
||||
mMediaStateReceiver = mediaStateReceiver;
|
||||
mMediaStateReceiver.startReceiving();
|
||||
}
|
||||
mSetMusicStateRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
GBApplication.deviceService().onSetMusicState(stateSpec);
|
||||
}
|
||||
};
|
||||
}
|
||||
mHandler.postDelayed(mSetMusicStateRunnable, 100);
|
||||
|
||||
return true;
|
||||
} catch (final NullPointerException | SecurityException e) {
|
||||
|
Loading…
Reference in New Issue
Block a user