mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge.git
synced 2025-02-04 04:54:10 +01:00
Add functionality to control the music playback from the watch. Not sure if all of this is correct. Having issues that the track title isn't updated until you issue a play, pause command from either the phone or watch.
This commit is contained in:
parent
6882127bec
commit
ed20f69cc4
@ -57,6 +57,7 @@ public class ZeTimeConstants {
|
|||||||
public static final byte CMD_GET_HEARTRATE_EXDATA = (byte) 0x61;
|
public static final byte CMD_GET_HEARTRATE_EXDATA = (byte) 0x61;
|
||||||
public static final byte CMD_PUSH_EX_MSG = (byte) 0x76;
|
public static final byte CMD_PUSH_EX_MSG = (byte) 0x76;
|
||||||
public static final byte CMD_PUSH_WEATHER_DATA = (byte) 0x77;
|
public static final byte CMD_PUSH_WEATHER_DATA = (byte) 0x77;
|
||||||
|
public static final byte CMD_MUSIC_CONTROL = (byte) 0xD0;
|
||||||
// here are the action commands
|
// here are the action commands
|
||||||
public static final byte CMD_REQUEST = (byte) 0x70;
|
public static final byte CMD_REQUEST = (byte) 0x70;
|
||||||
public static final byte CMD_SEND = (byte) 0x71;
|
public static final byte CMD_SEND = (byte) 0x71;
|
||||||
|
@ -179,7 +179,7 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGBDeviceEvent(GBDeviceEventMusicControl musicEvent) {
|
protected void handleGBDeviceEvent(GBDeviceEventMusicControl musicEvent) {
|
||||||
Context context = getContext();
|
Context context = getContext();
|
||||||
LOG.info("Got event for MUSIC_CONTROL");
|
LOG.info("Got event for MUSIC_CONTROL");
|
||||||
Intent musicIntent = new Intent(GBMusicControlReceiver.ACTION_MUSICCONTROL);
|
Intent musicIntent = new Intent(GBMusicControlReceiver.ACTION_MUSICCONTROL);
|
||||||
|
@ -21,6 +21,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
|
|||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||||
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimeConstants;
|
import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimeConstants;
|
||||||
import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimeSampleProvider;
|
import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimeSampleProvider;
|
||||||
@ -51,6 +52,7 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(ZeTimeDeviceSupport.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ZeTimeDeviceSupport.class);
|
||||||
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
|
private final GBDeviceEventBatteryInfo batteryCmd = new GBDeviceEventBatteryInfo();
|
||||||
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
private final GBDeviceEventVersionInfo versionCmd = new GBDeviceEventVersionInfo();
|
||||||
|
private final GBDeviceEventMusicControl musicCmd = new GBDeviceEventMusicControl();
|
||||||
private byte[] lastMsg;
|
private byte[] lastMsg;
|
||||||
private byte msgPart;
|
private byte msgPart;
|
||||||
private int availableSleepData;
|
private int availableSleepData;
|
||||||
@ -61,10 +63,14 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
private int progressHeartRate;
|
private int progressHeartRate;
|
||||||
private final int maxMsgLength = 20;
|
private final int maxMsgLength = 20;
|
||||||
private boolean callIncoming = false;
|
private boolean callIncoming = false;
|
||||||
|
private String songtitle = null;
|
||||||
|
public byte[] music = null;
|
||||||
|
public byte volume = 73;
|
||||||
|
|
||||||
public BluetoothGattCharacteristic notifyCharacteristic = null;
|
public BluetoothGattCharacteristic notifyCharacteristic = null;
|
||||||
public BluetoothGattCharacteristic writeCharacteristic = null;
|
public BluetoothGattCharacteristic writeCharacteristic = null;
|
||||||
public BluetoothGattCharacteristic ackCharacteristic = null;
|
public BluetoothGattCharacteristic ackCharacteristic = null;
|
||||||
|
public BluetoothGattCharacteristic replyCharacteristic = null;
|
||||||
|
|
||||||
public ZeTimeDeviceSupport(){
|
public ZeTimeDeviceSupport(){
|
||||||
super(LOG);
|
super(LOG);
|
||||||
@ -89,8 +95,10 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
notifyCharacteristic = getCharacteristic(ZeTimeConstants.UUID_NOTIFY_CHARACTERISTIC);
|
notifyCharacteristic = getCharacteristic(ZeTimeConstants.UUID_NOTIFY_CHARACTERISTIC);
|
||||||
writeCharacteristic = getCharacteristic(ZeTimeConstants.UUID_WRITE_CHARACTERISTIC);
|
writeCharacteristic = getCharacteristic(ZeTimeConstants.UUID_WRITE_CHARACTERISTIC);
|
||||||
ackCharacteristic = getCharacteristic(ZeTimeConstants.UUID_ACK_CHARACTERISTIC);
|
ackCharacteristic = getCharacteristic(ZeTimeConstants.UUID_ACK_CHARACTERISTIC);
|
||||||
|
replyCharacteristic = getCharacteristic(ZeTimeConstants.UUID_REPLY_CHARACTERISTIC);
|
||||||
|
|
||||||
builder.notify(ackCharacteristic, true);
|
builder.notify(ackCharacteristic, true);
|
||||||
|
builder.notify(notifyCharacteristic, true);
|
||||||
requestDeviceInfo(builder);
|
requestDeviceInfo(builder);
|
||||||
requestBatteryInfo(builder);
|
requestBatteryInfo(builder);
|
||||||
requestActivityInfo(builder);
|
requestActivityInfo(builder);
|
||||||
@ -152,7 +160,21 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetMusicInfo(MusicSpec musicSpec) {
|
public void onSetMusicInfo(MusicSpec musicSpec) {
|
||||||
|
songtitle = musicSpec.track;
|
||||||
|
if (music != null) {
|
||||||
|
try {
|
||||||
|
byte [] musicT = new byte[songtitle.length() + 7]; // 7 bytes for weatherdata and overhead
|
||||||
|
System.arraycopy(music, 0, musicT, 0, 6);
|
||||||
|
System.arraycopy(songtitle.getBytes(StandardCharsets.UTF_8), 0, musicT, 6, songtitle.length());
|
||||||
|
musicT[musicT.length - 1] = ZeTimeConstants.CMD_END;
|
||||||
|
musicT[2] = ZeTimeConstants.CMD_SEND;
|
||||||
|
TransactionBuilder builder = performInitialized("setMusicInfo");
|
||||||
|
sendMsgToWatch(builder, music);
|
||||||
|
performConnected(builder.getTransaction());
|
||||||
|
} catch (IOException e) {
|
||||||
|
GB.toast(getContext(), "Error setting music info: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -271,7 +293,30 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSetMusicState(MusicStateSpec stateSpec) {
|
public void onSetMusicState(MusicStateSpec stateSpec) {
|
||||||
|
if(songtitle != null) {
|
||||||
|
music = new byte[songtitle.length() + 7]; // 7 bytes for weatherdata and overhead
|
||||||
|
music[0] = ZeTimeConstants.CMD_PREAMBLE;
|
||||||
|
music[1] = ZeTimeConstants.CMD_MUSIC_CONTROL;
|
||||||
|
music[2] = ZeTimeConstants.CMD_REQUEST_RESPOND;
|
||||||
|
music[3] = (byte) ((songtitle.length() + 1) & 0xff);
|
||||||
|
music[4] = (byte) ((songtitle.length() + 1) >> 8);
|
||||||
|
if (stateSpec.state == MusicStateSpec.STATE_PLAYING) {
|
||||||
|
music[5] = 0;
|
||||||
|
} else {
|
||||||
|
music[5] = 1;
|
||||||
|
}
|
||||||
|
System.arraycopy(songtitle.getBytes(StandardCharsets.UTF_8), 0, music, 6, songtitle.length());
|
||||||
|
music[music.length - 1] = ZeTimeConstants.CMD_END;
|
||||||
|
if (music != null) {
|
||||||
|
try {
|
||||||
|
TransactionBuilder builder = performInitialized("setMusicStateInfo");
|
||||||
|
replyMsgToWatch(builder, music);
|
||||||
|
performConnected(builder.getTransaction());
|
||||||
|
} catch (IOException e) {
|
||||||
|
GB.toast(getContext(), "Error setting music state and info: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -643,10 +688,26 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
case ZeTimeConstants.CMD_GET_HEARTRATE_EXDATA:
|
case ZeTimeConstants.CMD_GET_HEARTRATE_EXDATA:
|
||||||
handleHeatRateData(data);
|
handleHeatRateData(data);
|
||||||
break;
|
break;
|
||||||
|
case ZeTimeConstants.CMD_MUSIC_CONTROL:
|
||||||
|
handleMusicControl(data);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else if (ZeTimeConstants.UUID_NOTIFY_CHARACTERISTIC.equals(characteristicUUID))
|
||||||
|
{
|
||||||
|
byte[] data = receiveCompleteMsg(characteristic.getValue());
|
||||||
|
if(isMsgFormatOK(data)) {
|
||||||
|
switch (data[1])
|
||||||
|
{
|
||||||
|
case ZeTimeConstants.CMD_MUSIC_CONTROL:
|
||||||
|
handleMusicControl(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
LOG.info("Unhandled characteristic changed: " + characteristicUUID);
|
LOG.info("Unhandled characteristic changed: " + characteristicUUID);
|
||||||
logMessageContent(characteristic.getValue());
|
logMessageContent(characteristic.getValue());
|
||||||
}
|
}
|
||||||
@ -992,4 +1053,85 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
|
|||||||
}
|
}
|
||||||
builder.write(ackCharacteristic, new byte[]{ZeTimeConstants.CMD_ACK_WRITE});
|
builder.write(ackCharacteristic, new byte[]{ZeTimeConstants.CMD_ACK_WRITE});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleMusicControl(byte[] musicControlMsg)
|
||||||
|
{
|
||||||
|
if(musicControlMsg[2] == ZeTimeConstants.CMD_SEND) {
|
||||||
|
switch (musicControlMsg[5]) {
|
||||||
|
case 0: // play current song
|
||||||
|
musicCmd.event = GBDeviceEventMusicControl.Event.PLAY;
|
||||||
|
break;
|
||||||
|
case 1: // pause current song
|
||||||
|
musicCmd.event = GBDeviceEventMusicControl.Event.PAUSE;
|
||||||
|
break;
|
||||||
|
case 2: // skip to previous song
|
||||||
|
musicCmd.event = GBDeviceEventMusicControl.Event.PREVIOUS;
|
||||||
|
break;
|
||||||
|
case 3: // skip to next song
|
||||||
|
musicCmd.event = GBDeviceEventMusicControl.Event.NEXT;
|
||||||
|
break;
|
||||||
|
case 4: // change volume
|
||||||
|
if (musicControlMsg[6] > volume) {
|
||||||
|
musicCmd.event = GBDeviceEventMusicControl.Event.VOLUMEUP;
|
||||||
|
volume += 10;
|
||||||
|
} else {
|
||||||
|
musicCmd.event = GBDeviceEventMusicControl.Event.VOLUMEDOWN;
|
||||||
|
volume -= 10;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
TransactionBuilder builder = performInitialized("replyMusicVolume");
|
||||||
|
replyMsgToWatch(builder, new byte[]{ZeTimeConstants.CMD_PREAMBLE,
|
||||||
|
ZeTimeConstants.CMD_MUSIC_CONTROL,
|
||||||
|
ZeTimeConstants.CMD_REQUEST_RESPOND,
|
||||||
|
0x02,
|
||||||
|
0x00,
|
||||||
|
0x02,
|
||||||
|
volume,
|
||||||
|
ZeTimeConstants.CMD_END});
|
||||||
|
performConnected(builder.getTransaction());
|
||||||
|
} catch (IOException e) {
|
||||||
|
GB.toast(getContext(), "Error reply the music volume: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
handleGBDeviceEvent(musicCmd);
|
||||||
|
}
|
||||||
|
if((music != null) && (musicControlMsg[5] != 4))
|
||||||
|
{
|
||||||
|
music[2] = ZeTimeConstants.CMD_REQUEST_RESPOND;
|
||||||
|
try {
|
||||||
|
TransactionBuilder builder = performInitialized("replyMusicState");
|
||||||
|
replyMsgToWatch(builder, music);
|
||||||
|
performConnected(builder.getTransaction());
|
||||||
|
} catch (IOException e) {
|
||||||
|
GB.toast(getContext(), "Error reply the music state: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void replyMsgToWatch(TransactionBuilder builder, byte[] msg)
|
||||||
|
{
|
||||||
|
if(msg.length > maxMsgLength)
|
||||||
|
{
|
||||||
|
int msgpartlength = 0;
|
||||||
|
byte[] msgpart = null;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if((msg.length - msgpartlength) < maxMsgLength)
|
||||||
|
{
|
||||||
|
msgpart = new byte[msg.length - msgpartlength];
|
||||||
|
System.arraycopy(msg, msgpartlength, msgpart, 0, msg.length - msgpartlength);
|
||||||
|
msgpartlength += (msg.length - msgpartlength);
|
||||||
|
} else {
|
||||||
|
msgpart = new byte[maxMsgLength];
|
||||||
|
System.arraycopy(msg, msgpartlength, msgpart, 0, maxMsgLength);
|
||||||
|
msgpartlength += maxMsgLength;
|
||||||
|
}
|
||||||
|
builder.write(replyCharacteristic, msgpart);
|
||||||
|
}while(msgpartlength < msg.length);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
builder.write(replyCharacteristic, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user