mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2025-02-18 05:17:08 +01:00
Nothing Ear (1): Read audio status from device and various fixes:
- add support for ANC-light mode - handle multiple GBDeviceEvents - add fake fw and hw versions to make DBHelper happy - fix battery charge detection logic - extract some strings to resources
This commit is contained in:
parent
ebe2558690
commit
9ad7e210b7
@ -7,13 +7,18 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventVersionInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
|
||||
@ -26,8 +31,9 @@ public class NothingProtocol extends GBDeviceProtocol {
|
||||
|
||||
final UUID UUID_DEVICE_CTRL = UUID.fromString("aeac4a03-dff5-498f-843a-34487cf133eb");
|
||||
|
||||
private boolean isFirstExchange = true;
|
||||
|
||||
public static final byte CONTROL_DEVICE_TYPE_TWS_HEADSET = 1;
|
||||
private static final byte CONTROL_DEVICE_TYPE_TWS_HEADSET = 1;
|
||||
|
||||
private static final int CONTROL_CRC = 0x20;
|
||||
|
||||
@ -44,15 +50,25 @@ public class NothingProtocol extends GBDeviceProtocol {
|
||||
//incoming
|
||||
private static final short battery_status = (short) 0xe001;
|
||||
private static final short battery_status2 = (short) 0xc007;
|
||||
private static final short audio_mode_status = (short) 0xc01e;
|
||||
|
||||
private static final short unk_maybe_ack = (short) 0xf002;
|
||||
private static final short unk_close_case = (short) 0xe002; //sent twice when the case is closed with earphones in
|
||||
|
||||
//outgoing
|
||||
private static final short find_device = (short) 0xf002;
|
||||
private static final short in_ear_detection = (short) 0xf004;
|
||||
private static final short audio_mode = (short) 0xf00f;
|
||||
|
||||
@Override
|
||||
public GBDeviceEvent[] decodeResponse(byte[] responseData) {
|
||||
List<GBDeviceEvent> devEvts = new ArrayList<>();
|
||||
|
||||
if (isFirstExchange) {
|
||||
isFirstExchange = false;
|
||||
devEvts.add(new GBDeviceEventVersionInfo()); //TODO: this is a weird hack to make the DBHelper happy. Replace with proper firmware detection
|
||||
}
|
||||
|
||||
ByteBuffer incoming = ByteBuffer.wrap(responseData);
|
||||
incoming.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
@ -81,7 +97,11 @@ public class NothingProtocol extends GBDeviceProtocol {
|
||||
switch (getRequestCommand(command)) {
|
||||
case battery_status:
|
||||
case battery_status2:
|
||||
return handleBatteryInfo(payload);
|
||||
devEvts.add(handleBatteryInfo(payload));
|
||||
break;
|
||||
case audio_mode_status:
|
||||
devEvts.add(handleAudioModeStatus(payload));
|
||||
break;
|
||||
|
||||
case unk_maybe_ack:
|
||||
LOG.debug("received ack");
|
||||
@ -94,14 +114,14 @@ public class NothingProtocol extends GBDeviceProtocol {
|
||||
LOG.debug("Incoming message - control:" + control + " requestCommand: " + (getRequestCommand(command) & 0xffff) + "length: " + length + " dump: " + hexdump(responseData));
|
||||
|
||||
}
|
||||
return null;
|
||||
return devEvts.toArray(new GBDeviceEvent[devEvts.size()]);
|
||||
}
|
||||
|
||||
boolean isCrcNeeded(short control) {
|
||||
return (control & CONTROL_CRC) != 0;
|
||||
}
|
||||
|
||||
private byte[] encodeMessage(short control, short command, byte[] payload) {
|
||||
byte[] encodeMessage(short control, short command, byte[] payload) {
|
||||
|
||||
ByteBuffer msgBuf = ByteBuffer.allocate(8 + payload.length);
|
||||
msgBuf.order(ByteOrder.LITTLE_ENDIAN);
|
||||
@ -125,7 +145,30 @@ public class NothingProtocol extends GBDeviceProtocol {
|
||||
}
|
||||
|
||||
byte[] encodeBatteryStatusReq() {
|
||||
return encodeMessage((short) 0x120, (short) 0xc007, new byte[]{});
|
||||
return encodeMessage((short) 0x5120, battery_status2, new byte[]{});
|
||||
}
|
||||
|
||||
byte[] encodeAudioModeStatusReq() {
|
||||
return encodeMessage((short) 0x120, audio_mode_status, new byte[]{});
|
||||
}
|
||||
|
||||
//TODO: unify mapping between bytes and strings in the following two functions
|
||||
private GBDeviceEvent handleAudioModeStatus(byte[] payload) {
|
||||
SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
if (Arrays.equals(payload, new byte[]{0x01, 0x01, 0x00})) {
|
||||
editor.putString(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_AUDIOMODE, "anc").apply();
|
||||
} else if (Arrays.equals(payload, new byte[]{0x01, 0x03, 0x00})) {
|
||||
editor.putString(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_AUDIOMODE, "anc-light").apply();
|
||||
} else if (Arrays.equals(payload, new byte[]{0x01, 0x05, 0x00})) {
|
||||
editor.putString(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_AUDIOMODE, "off").apply();
|
||||
} else if (Arrays.equals(payload, new byte[]{0x01, 0x07, 0x00})) {
|
||||
editor.putString(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_AUDIOMODE, "transparency").apply();
|
||||
} else {
|
||||
LOG.warn("Unknown audio mode. Payload: " + hexdump(payload));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] encodeAudioMode(String desired) {
|
||||
@ -135,19 +178,23 @@ public class NothingProtocol extends GBDeviceProtocol {
|
||||
case "anc":
|
||||
payload[1] = 0x01;
|
||||
break;
|
||||
case "anc-light":
|
||||
payload[1] = 0x03;
|
||||
break;
|
||||
case "transparency":
|
||||
payload[1] = 0x07;
|
||||
break;
|
||||
case "off":
|
||||
default:
|
||||
}
|
||||
return encodeMessage((short) 0x120, (short) 0xf00f, payload);
|
||||
return encodeMessage((short) 0x120, audio_mode, payload);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] encodeFindDevice(boolean start) {
|
||||
byte payload = (byte) (start ? 0x01 : 0x00);
|
||||
return encodeMessage((short) 0x120, (short) 0xf002, new byte[]{payload});
|
||||
return encodeMessage((short) 0x120, find_device, new byte[]{payload});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -175,10 +222,11 @@ public class NothingProtocol extends GBDeviceProtocol {
|
||||
public byte[] encodeSetTime() {
|
||||
// This are earphones, there is no time to set here. However this method gets called soon
|
||||
// after connecting, hence we use it to perform some initializations.
|
||||
return encodeBatteryStatusReq();
|
||||
// TODO: Find a way to send more requests during the first connection
|
||||
return encodeAudioModeStatusReq();
|
||||
}
|
||||
|
||||
private GBDeviceEvent[] handleBatteryInfo(byte[] payload) {
|
||||
private GBDeviceEvent handleBatteryInfo(byte[] payload) {
|
||||
//LOG.debug("Battery payload: " + hexdump(payload));
|
||||
|
||||
/* payload:
|
||||
@ -202,14 +250,14 @@ public class NothingProtocol extends GBDeviceProtocol {
|
||||
for (int i = 0; i < numBatteries; i++) {
|
||||
evBattery.level += (short) ((payload[2 + 2 * i] & MASK_BATTERY) / numBatteries);
|
||||
if (!batteryCharging)
|
||||
batteryCharging = ((payload[2 + 2 * i]) & MASK_BATTERY_CHARGING) == 1;
|
||||
batteryCharging = ((payload[2 + 2 * i] & MASK_BATTERY_CHARGING) == MASK_BATTERY_CHARGING);
|
||||
//LOG.debug("single battery level: " + hexdump(payload, 2+2*i,1) +"-"+ ((payload[2+2*i] & 0xff))+":" + evBattery.level);
|
||||
}
|
||||
|
||||
evBattery.state = BatteryState.UNKNOWN;
|
||||
evBattery.state = batteryCharging ? BatteryState.BATTERY_CHARGING : evBattery.state;
|
||||
|
||||
return new GBDeviceEvent[]{evBattery};
|
||||
return evBattery;
|
||||
}
|
||||
|
||||
private short getRequestCommand(short command) {
|
||||
|
@ -1762,6 +1762,7 @@
|
||||
|
||||
<string-array name="nothing_ear1_audio_mode">
|
||||
<item>anc</item>
|
||||
<item>anc-light</item>
|
||||
<item>transparency</item>
|
||||
<item>off</item>
|
||||
</string-array>
|
||||
|
@ -1272,4 +1272,7 @@
|
||||
<string name="prefs_autoheartrate_sleep">Take measurements during sleep</string>
|
||||
<string name="prefs_autoheartrate_interval">Frequency of measurements</string>
|
||||
<string name="devicetype_nothingear1">Nothing Ear (1)</string>
|
||||
<string name="nothing_prefs_inear_summary">Play/pause the music depending if you wear the earbuds</string>
|
||||
<string name="nothing_prefs_inear_title">In-Ear detection</string>
|
||||
<string name="nothing_prefs_audiomode_title">Audio mode</string>
|
||||
</resources>
|
||||
|
@ -4,13 +4,13 @@
|
||||
android:defaultValue="true"
|
||||
android:icon="@drawable/ic_extension"
|
||||
android:key="pref_nothing_inear_detection"
|
||||
android:summary="Play/pause the music depending if you wear the earbuds"
|
||||
android:title="In-Ear detection" />
|
||||
android:summary="@string/nothing_prefs_inear_summary"
|
||||
android:title="@string/nothing_prefs_inear_title" />
|
||||
<ListPreference
|
||||
android:icon="@drawable/ic_extension"
|
||||
android:entryValues="@array/nothing_ear1_audio_mode"
|
||||
android:entries="@array/nothing_ear1_audio_mode"
|
||||
android:key="pref_nothing_audiomode"
|
||||
android:summary="%s"
|
||||
android:title="Audio mode" />
|
||||
android:title="@string/nothing_prefs_audiomode_title" />
|
||||
</androidx.preference.PreferenceScreen>
|
||||
|
Loading…
x
Reference in New Issue
Block a user