Fossil Q: Add/fix button actions for music and volume control

This commit is contained in:
Arjan Schrijver
2026-04-05 14:18:10 +02:00
committed by Arjan Schrijver
parent 5ceef900de
commit a03bfee348
5 changed files with 105 additions and 70 deletions
@@ -55,6 +55,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.HybridHRActivitySampleProvider;
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationConfiguration;
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.PackageConfigHelper;
@@ -88,6 +89,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FirmwareFilePutRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.NotificationFilterPutRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.PlayTextNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.music.MusicControlRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.AnimationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.MoveHandsRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.ReleaseHandsControlRequest;
@@ -801,13 +803,15 @@ public class FossilWatchAdapter extends WatchAdapter {
}
protected void handleBackgroundCharacteristic(BluetoothGattCharacteristic characteristic, byte[] value) {
switch (value[1]) {
case 2: {
byte requestType = value[1];
switch (requestType) {
case 0x02: {
byte syncId = value[2];
getDeviceSupport().getDevice().addDeviceInfo(new GenericItem(QHybridSupport.ITEM_LAST_HEARTBEAT, DateFormat.getTimeInstance().format(new Date())));
break;
}
case 8: {
case 0x08: { // FORWARD_TO_PHONE
if (value.length != 12) {
throw new RuntimeException("wrong button message");
}
@@ -819,34 +823,93 @@ public class FossilWatchAdapter extends WatchAdapter {
log("Button press on button " + button);
Intent i = new Intent(QHYBRID_EVENT_BUTTON_PRESS);
i.setPackage(BuildConfig.APPLICATION_ID);
i.putExtra("BUTTON", button);
getContext().sendBroadcast(i);
LOG.debug("Sent broadcast intent: ACTION={} EXTRA=BUTTON={}", QHYBRID_EVENT_BUTTON_PRESS, button);
}
break;
}
case 5: {
case 0x05: { // FORWARD_TO_PHONE_MULTI / VOLUME_UP / VOLUME_DOWN / MUSIC_CONTROL
if (value.length != 4) {
throw new RuntimeException("wrong button message");
}
int action = value[3];
String actionString = "SINGLE";
if (action == 3) actionString = "DOUBLE";
else if (action == 4) actionString = "LONG";
String upperButtonFunction = getDeviceSpecificPreferences().getString("top_button_function", "");
String middleButtonFunction = getDeviceSpecificPreferences().getString("middle_button_function", "");
String bottomButtonFunction = getDeviceSpecificPreferences().getString("bottom_button_function", "");
boolean musicControlEnabled = upperButtonFunction.equals("MUSIC_CONTROL") || middleButtonFunction.equals("MUSIC_CONTROL") || bottomButtonFunction.equals("MUSIC_CONTROL");
MusicControlRequest.MUSIC_WATCH_REQUEST request = MusicControlRequest.MUSIC_WATCH_REQUEST.fromCommandByte((byte)action);
GBDevice currentDevice = getDeviceSupport().getDevice();
if (((QHybridCoordinator) currentDevice.getDeviceCoordinator()).isHybridHR(currentDevice)
|| request == MusicControlRequest.MUSIC_WATCH_REQUEST.MUSIC_REQUEST_LOUDER
|| request == MusicControlRequest.MUSIC_WATCH_REQUEST.MUSIC_REQUEST_QUITER
|| (musicControlEnabled && request == MusicControlRequest.MUSIC_WATCH_REQUEST.MUSIC_REQUEST_PLAY_PAUSE)
|| (musicControlEnabled && request == MusicControlRequest.MUSIC_WATCH_REQUEST.MUSIC_REQUEST_NEXT)
|| (musicControlEnabled && request == MusicControlRequest.MUSIC_WATCH_REQUEST.MUSIC_REQUEST_PREVIOUS)
) {
handleMusicRequest(value);
} else {
String actionString = "SINGLE";
if (action == 3) actionString = "DOUBLE";
else if (action == 4) actionString = "LONG";
// lastButtonIndex = index;
log(actionString + " button press");
// lastButtonIndex = index;
log(actionString + " button press");
Intent i = new Intent(QHYBRID_EVENT_MULTI_BUTTON_PRESS);
i.putExtra("ACTION", actionString);
getContext().sendBroadcast(i);
Intent i = new Intent(QHYBRID_EVENT_MULTI_BUTTON_PRESS);
i.putExtra("ACTION", actionString);
getContext().sendBroadcast(i);
LOG.debug("Sent broadcast intent: ACTION={} EXTRA=ACTION={}", QHYBRID_EVENT_MULTI_BUTTON_PRESS, actionString);
}
break;
}
}
}
private void handleMusicRequest(byte[] value) {
byte command = value[3];
LOG.info("got music command: " + command);
MusicControlRequest.MUSIC_WATCH_REQUEST request = MusicControlRequest.MUSIC_WATCH_REQUEST.fromCommandByte(command);
GBDeviceEventMusicControl deviceEventMusicControl = new GBDeviceEventMusicControl();
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PLAY;
// TODO add skipping/seeking
switch (request) {
case MUSIC_REQUEST_PLAY_PAUSE: {
queueWrite(new MusicControlRequest(MusicControlRequest.MUSIC_PHONE_REQUEST.MUSIC_REQUEST_PLAY_PAUSE));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PLAYPAUSE;
break;
}
case MUSIC_REQUEST_NEXT: {
queueWrite(new MusicControlRequest(MusicControlRequest.MUSIC_PHONE_REQUEST.MUSIC_REQUEST_NEXT));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.NEXT;
break;
}
case MUSIC_REQUEST_PREVIOUS: {
queueWrite(new MusicControlRequest(MusicControlRequest.MUSIC_PHONE_REQUEST.MUSIC_REQUEST_PREVIOUS));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PREVIOUS;
break;
}
case MUSIC_REQUEST_LOUDER: {
queueWrite(new MusicControlRequest(MusicControlRequest.MUSIC_PHONE_REQUEST.MUSIC_REQUEST_LOUDER));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEUP;
break;
}
case MUSIC_REQUEST_QUITER: {
queueWrite(new MusicControlRequest(MusicControlRequest.MUSIC_PHONE_REQUEST.MUSIC_REQUEST_QUITER));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEDOWN;
break;
}
}
getDeviceSupport().evaluateGBDeviceEvent(deviceEventMusicControl);
}
@Override
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
@@ -27,7 +27,6 @@ import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.reque
import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.UnitsConfigItem;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.VibrationStrengthConfigItem;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.music.MusicControlRequest.MUSIC_PHONE_REQUEST;
import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.music.MusicControlRequest.MUSIC_WATCH_REQUEST;
import static nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil.convertDrawableToBitmap;
import static nodomain.freeyourgadget.gadgetbridge.util.GB.NOTIFICATION_CHANNEL_ID;
import static nodomain.freeyourgadget.gadgetbridge.util.StringUtils.shortenPackageName;
@@ -103,7 +102,6 @@ import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventAppInfo;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventCallControl;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventFindPhone;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventMusicControl;
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventNotificationControl;
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.CommuteActionsActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.FossilFileReader;
@@ -130,9 +128,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NavigationInfoSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.weather.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.webview.CurrentPosition;
import nodomain.freeyourgadget.gadgetbridge.model.weather.Weather;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.file.FileHandle;
@@ -196,6 +193,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.UriHelper;
import nodomain.freeyourgadget.gadgetbridge.util.Version;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
import nodomain.freeyourgadget.gadgetbridge.webview.CurrentPosition;
public class FossilHRWatchAdapter extends FossilWatchAdapter {
public static final int MESSAGE_WHAT_VOICE_DATA_RECEIVED = 0;
@@ -1964,8 +1962,6 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
} else if (value[7] == 0x03) {
handleQuickReplyRequest(value);
}
} else if (requestType == (byte) 0x05) {
handleMusicRequest(value);
} else if (requestType == (byte) 0x01) {
int eventId = value[2];
LOG.info("got event id " + eventId);
@@ -2143,47 +2139,6 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
queueWrite(new QuickReplyConfirmationPutRequest(callId));
}
private void handleMusicRequest(byte[] value) {
byte command = value[3];
LOG.info("got music command: " + command);
MUSIC_WATCH_REQUEST request = MUSIC_WATCH_REQUEST.fromCommandByte(command);
GBDeviceEventMusicControl deviceEventMusicControl = new GBDeviceEventMusicControl();
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PLAY;
// TODO add skipping/seeking
switch (request) {
case MUSIC_REQUEST_PLAY_PAUSE: {
queueWrite(new MusicControlRequest(MUSIC_PHONE_REQUEST.MUSIC_REQUEST_PLAY_PAUSE));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PLAYPAUSE;
break;
}
case MUSIC_REQUEST_NEXT: {
queueWrite(new MusicControlRequest(MUSIC_PHONE_REQUEST.MUSIC_REQUEST_NEXT));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.NEXT;
break;
}
case MUSIC_REQUEST_PREVIOUS: {
queueWrite(new MusicControlRequest(MUSIC_PHONE_REQUEST.MUSIC_REQUEST_PREVIOUS));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.PREVIOUS;
break;
}
case MUSIC_REQUEST_LOUDER: {
queueWrite(new MusicControlRequest(MUSIC_PHONE_REQUEST.MUSIC_REQUEST_LOUDER));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEUP;
break;
}
case MUSIC_REQUEST_QUITER: {
queueWrite(new MusicControlRequest(MUSIC_PHONE_REQUEST.MUSIC_REQUEST_QUITER));
deviceEventMusicControl.event = GBDeviceEventMusicControl.Event.VOLUMEDOWN;
break;
}
}
getDeviceSupport().evaluateGBDeviceEvent(deviceEventMusicControl);
}
@Override
public void setCommuteMenuMessage(String message, boolean finished) {
queueWrite(new SetCommuteMenuMessage(message, finished, this));
@@ -27,7 +27,12 @@ public enum ConfigPayload {
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x2E, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x0F, (byte) 0x00, (byte) 0x8B, (byte) 0x00, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x08, (byte) 0x01, (byte) 0x14, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0xFE, (byte) 0x08, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0xBF, (byte) 0xD5, (byte) 0x54, (byte) 0xD1}
),
FORWARD_TO_PHONE_MULTI(
"forward to phone (multifunction)", // This is "play/pause music" in the official app
"forward to phone (multifunction)", // Identical to MUSIC_CONTROL, but handled differently in FossilWatchAdapter
new byte[]{(byte) 0x01, (byte) 0x06, (byte) 0x12, (byte) 0x00},
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x06, (byte) 0x12, (byte) 0x63, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x1D, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0xF6, (byte) 0x00, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x42, (byte) 0x02, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x43, (byte) 0x03, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x44, (byte) 0x04, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x1E, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xCD, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xB6, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xB5, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xFE, (byte) 0x08, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x7B, (byte) 0x56, (byte) 0x4E, (byte) 0x97}
),
MUSIC_CONTROL(
"control music (play/pause/prev/next", // Identical to FORWARD_TO_PHONE_MULTI, but handled differently in FossilWatchAdapter
new byte[]{(byte) 0x01, (byte) 0x06, (byte) 0x12, (byte) 0x00},
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x06, (byte) 0x12, (byte) 0x63, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x1D, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0xF6, (byte) 0x00, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x42, (byte) 0x02, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x43, (byte) 0x03, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x44, (byte) 0x04, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x1E, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xCD, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xB6, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xB5, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xFE, (byte) 0x08, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x7B, (byte) 0x56, (byte) 0x4E, (byte) 0x97}
),
+11 -9
View File
@@ -5439,15 +5439,16 @@
</string-array>
<string-array name="qhybrid_button_functions">
<item>Forward to phone</item>
<item>Forward to phone (multifunction)</item>
<item>Stopwatch</item>
<item>Date</item>
<item>Last notification</item>
<item>Second timezone</item>
<item>Volume up</item>
<item>Volume down</item>
<item>Step goal progress</item>
<item>@string/qhybrid_button_function_forward</item>
<item>@string/qhybrid_button_function_forward_multi</item>
<item>@string/qhybrid_button_function_stopwatch</item>
<item>@string/qhybrid_button_function_date</item>
<item>@string/qhybrid_button_function_last_notif</item>
<item>@string/qhybrid_button_function_2nd_tz</item>
<item>@string/qhybrid_button_function_volume_up</item>
<item>@string/qhybrid_button_function_volume_down</item>
<item>@string/qhybrid_button_function_step_goal</item>
<item>@string/qhybrid_button_function_control_music</item>
</string-array>
<string-array name="qhybrid_button_functions_values">
@@ -5461,6 +5462,7 @@
<item>VOLUME_UP</item>
<item>VOLUME_DOWN</item>
<item>STEP_GOAL_COMPLETION</item>
<item>MUSIC_CONTROL</item>
</string-array>
</resources>
+10
View File
@@ -4714,4 +4714,14 @@
<string name="pref_title_qhybrid_top_button_function">Top button function</string>
<string name="pref_title_qhybrid_middle_button_function">Middle button function</string>
<string name="pref_title_qhybrid_bottom_button_function">Bottom button function</string>
<string name="qhybrid_button_function_forward">Forward to phone</string>
<string name="qhybrid_button_function_forward_multi">Forward to phone (multifunction)</string>
<string name="qhybrid_button_function_stopwatch">Stopwatch</string>
<string name="qhybrid_button_function_date">Date</string>
<string name="qhybrid_button_function_last_notif">Last notification</string>
<string name="qhybrid_button_function_2nd_tz">Second timezone</string>
<string name="qhybrid_button_function_volume_up">Volume up</string>
<string name="qhybrid_button_function_volume_down">Volume down</string>
<string name="qhybrid_button_function_step_goal">Step goal progress</string>
<string name="qhybrid_button_function_control_music">Control music player</string>
</resources>