2024-01-10 18:54:00 +01:00
|
|
|
/* Copyright (C) 2023-2024 José Rebelo
|
2023-07-09 16:16:37 +02:00
|
|
|
|
|
|
|
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
|
2024-01-10 18:54:00 +01:00
|
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
2023-07-09 16:16:37 +02:00
|
|
|
package nodomain.freeyourgadget.gadgetbridge.util;
|
|
|
|
|
|
|
|
import android.content.ComponentName;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.media.AudioManager;
|
|
|
|
import android.media.MediaMetadata;
|
|
|
|
import android.media.session.MediaController;
|
|
|
|
import android.media.session.MediaSessionManager;
|
|
|
|
import android.media.session.PlaybackState;
|
|
|
|
|
2024-01-28 21:26:31 +01:00
|
|
|
import androidx.annotation.Nullable;
|
|
|
|
|
2023-07-09 16:16:37 +02:00
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.externalevents.NotificationListener;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
|
|
|
|
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
|
|
|
|
|
|
|
|
public class MediaManager {
|
|
|
|
private static final Logger LOG = LoggerFactory.getLogger(MediaManager.class);
|
|
|
|
|
|
|
|
private final Context context;
|
|
|
|
|
|
|
|
private MusicSpec bufferMusicSpec = null;
|
|
|
|
private MusicStateSpec bufferMusicStateSpec = null;
|
|
|
|
|
|
|
|
public MediaManager(final Context context) {
|
|
|
|
this.context = context;
|
|
|
|
}
|
|
|
|
|
|
|
|
public MusicSpec getBufferMusicSpec() {
|
|
|
|
return bufferMusicSpec;
|
|
|
|
}
|
|
|
|
|
|
|
|
public MusicStateSpec getBufferMusicStateSpec() {
|
|
|
|
return bufferMusicStateSpec;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the spec changed, so the device should be updated.
|
|
|
|
*/
|
|
|
|
public boolean onSetMusicState(final MusicStateSpec stateSpec) {
|
|
|
|
if (stateSpec != null && !stateSpec.equals(bufferMusicStateSpec)) {
|
|
|
|
bufferMusicStateSpec = stateSpec;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if the spec changed, so the device should be updated.
|
|
|
|
*/
|
|
|
|
public boolean onSetMusicInfo(MusicSpec musicSpec) {
|
|
|
|
if (musicSpec != null && !musicSpec.equals(bufferMusicSpec)) {
|
|
|
|
bufferMusicSpec = musicSpec;
|
|
|
|
if (bufferMusicStateSpec != null) {
|
|
|
|
bufferMusicStateSpec.state = 0;
|
|
|
|
bufferMusicStateSpec.position = 0;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void refresh() {
|
|
|
|
LOG.info("Refreshing media state");
|
|
|
|
|
|
|
|
final MediaSessionManager mediaSessionManager =
|
|
|
|
(MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
|
|
|
|
|
|
|
|
try {
|
|
|
|
final List<MediaController> controllers = mediaSessionManager.getActiveSessions(
|
|
|
|
new ComponentName(context, NotificationListener.class)
|
|
|
|
);
|
|
|
|
if (controllers.isEmpty()) {
|
|
|
|
LOG.debug("No media controller available");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final MediaController controller = controllers.get(0);
|
|
|
|
|
2023-07-09 18:39:21 +02:00
|
|
|
bufferMusicSpec = extractMusicSpec(controller.getMetadata());
|
|
|
|
bufferMusicStateSpec = extractMusicStateSpec(controller.getPlaybackState());
|
|
|
|
} catch (final SecurityException e) {
|
|
|
|
LOG.warn("No permission to get media sessions - did not grant notification access?", e);
|
|
|
|
} catch (final Exception e) {
|
|
|
|
LOG.error("Failed to get media info", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-28 21:26:31 +01:00
|
|
|
@Nullable
|
2023-07-09 18:39:21 +02:00
|
|
|
public static MusicSpec extractMusicSpec(final MediaMetadata d) {
|
2024-01-28 21:26:31 +01:00
|
|
|
if (d == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-07-09 18:39:21 +02:00
|
|
|
final MusicSpec musicSpec = new MusicSpec();
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (d.containsKey(MediaMetadata.METADATA_KEY_ARTIST))
|
|
|
|
musicSpec.artist = d.getString(MediaMetadata.METADATA_KEY_ARTIST);
|
|
|
|
if (d.containsKey(MediaMetadata.METADATA_KEY_ALBUM))
|
|
|
|
musicSpec.album = d.getString(MediaMetadata.METADATA_KEY_ALBUM);
|
|
|
|
if (d.containsKey(MediaMetadata.METADATA_KEY_TITLE))
|
|
|
|
musicSpec.track = d.getString(MediaMetadata.METADATA_KEY_TITLE);
|
|
|
|
if (d.containsKey(MediaMetadata.METADATA_KEY_DURATION))
|
|
|
|
musicSpec.duration = (int) d.getLong(MediaMetadata.METADATA_KEY_DURATION) / 1000;
|
|
|
|
if (d.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS))
|
|
|
|
musicSpec.trackCount = (int) d.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS);
|
|
|
|
if (d.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER))
|
|
|
|
musicSpec.trackNr = (int) d.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
|
|
|
|
} catch (final Exception e) {
|
|
|
|
LOG.error("Failed to extract music spec", e);
|
|
|
|
}
|
|
|
|
|
|
|
|
return musicSpec;
|
|
|
|
}
|
|
|
|
|
2024-01-28 21:26:31 +01:00
|
|
|
@Nullable
|
2023-07-09 18:39:21 +02:00
|
|
|
public static MusicStateSpec extractMusicStateSpec(final PlaybackState s) {
|
2024-01-28 21:26:31 +01:00
|
|
|
if (s == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2023-07-09 18:39:21 +02:00
|
|
|
final MusicStateSpec stateSpec = new MusicStateSpec();
|
|
|
|
|
|
|
|
try {
|
|
|
|
stateSpec.position = (int) (s.getPosition() / 1000);
|
|
|
|
stateSpec.playRate = Math.round(100 * s.getPlaybackSpeed());
|
|
|
|
stateSpec.repeat = MusicStateSpec.STATE_UNKNOWN;
|
|
|
|
stateSpec.shuffle = MusicStateSpec.STATE_UNKNOWN;
|
|
|
|
switch (s.getState()) {
|
2023-07-09 16:16:37 +02:00
|
|
|
case PlaybackState.STATE_PLAYING:
|
|
|
|
stateSpec.state = MusicStateSpec.STATE_PLAYING;
|
|
|
|
break;
|
|
|
|
case PlaybackState.STATE_STOPPED:
|
|
|
|
stateSpec.state = MusicStateSpec.STATE_STOPPED;
|
|
|
|
break;
|
2023-07-09 18:39:21 +02:00
|
|
|
case PlaybackState.STATE_PAUSED:
|
|
|
|
stateSpec.state = MusicStateSpec.STATE_PAUSED;
|
|
|
|
break;
|
2023-07-09 16:16:37 +02:00
|
|
|
default:
|
|
|
|
stateSpec.state = MusicStateSpec.STATE_UNKNOWN;
|
2023-07-09 18:39:21 +02:00
|
|
|
break;
|
2023-07-09 16:16:37 +02:00
|
|
|
}
|
2023-07-09 18:39:21 +02:00
|
|
|
} catch (final Exception e) {
|
|
|
|
LOG.error("Failed to extract music state spec", e);
|
2023-07-09 16:16:37 +02:00
|
|
|
}
|
2023-07-09 18:39:21 +02:00
|
|
|
|
|
|
|
return stateSpec;
|
2023-07-09 16:16:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public int getPhoneVolume() {
|
|
|
|
return getPhoneVolume(context);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static int getPhoneVolume(final Context context) {
|
|
|
|
final AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
|
|
|
|
|
|
|
final int volumeLevel = audioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
|
|
|
|
final int volumeMax = audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
|
|
|
|
return Math.round(100 * (volumeLevel / (float) volumeMax));
|
|
|
|
}
|
|
|
|
}
|