diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
index 8e9e557ee..d13f86859 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSettingsPreferenceConst.java
@@ -95,6 +95,9 @@ public class DeviceSettingsPreferenceConst {
public static final String PREF_BT_CONNECTED_ADVERTISEMENT = "bt_connected_advertisement";
public static final String PREF_TRANSLITERATION_ENABLED = "pref_transliteration_enabled";
+ public static final String PREF_NOTHING_EAR1_INEAR = "pref_nothing_inear_detection";
+ public static final String PREF_NOTHING_EAR1_AUDIOMODE = "pref_nothing_audiomode";
+
public static final String PREF_SOUNDS = "sounds";
public static final String PREF_AUTH_KEY = "authkey";
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java
index 197955fa8..effd93614 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java
@@ -104,6 +104,8 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_VIBRATION_STRENGH_PERCENTAGE;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_VIBRATION_ENABLE;
+import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_AUDIOMODE;
+import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_INEAR;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_BROADCAST;
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_FELL_SLEEP_SELECTION;
@@ -440,6 +442,9 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
addPreferenceHandlerFor(PREF_SONYSWR12_LOW_VIBRATION);
addPreferenceHandlerFor(PREF_SONYSWR12_SMART_INTERVAL);
+ addPreferenceHandlerFor(PREF_NOTHING_EAR1_INEAR);
+ addPreferenceHandlerFor(PREF_NOTHING_EAR1_AUDIOMODE);
+
String sleepTimeState = prefs.getString(PREF_SLEEP_TIME, PREF_DO_NOT_DISTURB_OFF);
boolean sleepTimeScheduled = sleepTimeState.equals(PREF_DO_NOT_DISTURB_SCHEDULED);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/nothing/Ear1Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/nothing/Ear1Coordinator.java
new file mode 100644
index 000000000..b63c97ff3
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/nothing/Ear1Coordinator.java
@@ -0,0 +1,130 @@
+package nodomain.freeyourgadget.gadgetbridge.devices.nothing;
+
+import android.app.Activity;
+import android.content.Context;
+import android.net.Uri;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import nodomain.freeyourgadget.gadgetbridge.GBException;
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator;
+import nodomain.freeyourgadget.gadgetbridge.devices.InstallHandler;
+import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
+import nodomain.freeyourgadget.gadgetbridge.entities.Device;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceCandidate;
+import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
+import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
+
+public class Ear1Coordinator extends AbstractDeviceCoordinator {
+
+ @NonNull
+ @Override
+ public DeviceType getSupportedType(GBDeviceCandidate candidate) {
+ if(candidate.getName().equals("Nothing ear (1)"))
+ return DeviceType.NOTHING_EAR1;
+ return DeviceType.UNKNOWN;
+ }
+
+ @Override
+ public DeviceType getDeviceType() {
+ return DeviceType.NOTHING_EAR1;
+ }
+
+ @Nullable
+ @Override
+ public Class extends Activity> getPairingActivity() {
+ return null;
+ }
+
+ @Override
+ public boolean supportsActivityDataFetching() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsActivityTracking() {
+ return false;
+ }
+
+ @Override
+ public SampleProvider extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
+ return null;
+ }
+
+ @Override
+ public InstallHandler findInstallHandler(Uri uri, Context context) {
+ return null;
+ }
+
+ @Override
+ public boolean supportsScreenshots() {
+ return false;
+ }
+
+ @Override
+ public int getAlarmSlotCount() {
+ return 0;
+ }
+
+ @Override
+ public boolean supportsSmartWakeup(GBDevice device) {
+ return false;
+ }
+
+ @Override
+ public boolean supportsHeartRateMeasurement(GBDevice device) {
+ return false;
+ }
+
+ @Override
+ public String getManufacturer() {
+ return "Nothing";
+ }
+
+ @Override
+ public boolean supportsAppsManagement() {
+ return false;
+ }
+
+ @Override
+ public Class extends Activity> getAppsManagementActivity() {
+ return null;
+ }
+
+ @Override
+ public boolean supportsCalendarEvents() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsRealtimeData() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsWeather() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsFindDevice() {
+ return true;
+ }
+
+ @Override
+ protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
+
+ }
+
+ @Override
+ public int[] getSupportedDeviceSpecificSettings(GBDevice device) {
+ return new int[] {
+ R.xml.devicesettings_nothing_ear1
+ };
+ }
+
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
index 7ee8b3f4f..9aa428005 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
@@ -100,6 +100,7 @@ public enum DeviceType {
WASPOS(330, R.drawable.ic_device_pebble, R.drawable.ic_device_pebble_disabled, R.string.devicetype_waspos),
UM25(350, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_um25),
DOMYOS_T540(400, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_domyos_t540),
+ NOTHING_EAR1(410, R.drawable.ic_device_nothingear, R.drawable.ic_device_nothingear_disabled, R.string.devicetype_nothingear1),
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
private final int key;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
index 1daf0e588..39572d05e 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
@@ -80,6 +80,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.miband.MiBandSupport
import nodomain.freeyourgadget.gadgetbridge.service.devices.mijia_lywsd02.MijiaLywsd02Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.miscale2.MiScale2DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.no1f1.No1F1Support;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.nothing.Ear1Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.nut.NutSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.pinetime.PineTimeJFSupport;
@@ -358,6 +359,9 @@ public class DeviceSupportFactory {
case FITPRO:
deviceSupport = new ServiceDeviceSupport(new FitProDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.THROTTLING, ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
+ case NOTHING_EAR1:
+ deviceSupport = new ServiceDeviceSupport(new Ear1Support(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
+ break;
}
if (deviceSupport != null) {
deviceSupport.setContext(gbDevice, mBtAdapter, mContext);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/Ear1Support.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/Ear1Support.java
new file mode 100644
index 000000000..a21d5420e
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/Ear1Support.java
@@ -0,0 +1,89 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.nothing;
+
+import android.net.Uri;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.service.serial.AbstractSerialDeviceSupport;
+import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
+import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
+
+public class Ear1Support extends AbstractSerialDeviceSupport {
+ private static final Logger LOG = LoggerFactory.getLogger(Ear1Support.class);
+
+
+ @Override
+ public void onSendConfiguration(String config) {
+ super.onSendConfiguration(config);
+ }
+
+ @Override
+ public void onSetAlarms(ArrayList extends Alarm> alarms) {
+
+ }
+
+ @Override
+ public void onInstallApp(Uri uri) {
+
+ }
+
+ @Override
+ public void onAppConfiguration(UUID appUuid, String config, Integer id) {
+
+ }
+
+ @Override
+ public void onHeartRateTest() {
+
+ }
+
+ @Override
+ public void onSetConstantVibration(int integer) {
+
+ }
+
+ @Override
+ public void onSetHeartRateMeasurementInterval(int seconds) {
+
+ }
+
+ @Override
+ public void onReadConfiguration(String config) {
+
+ }
+
+ @Override
+ public void onTestNewFunction() {
+ //getDeviceIOThread().write(((NothingProtocol) getDeviceProtocol()).encodeBatteryStatusReq());
+ }
+
+ @Override
+ public boolean connect() {
+ getDeviceIOThread().start();
+ return true;
+ }
+
+ @Override
+ public synchronized NothingIOThread getDeviceIOThread() {
+ return (NothingIOThread) super.getDeviceIOThread();
+ }
+
+ @Override
+ public boolean useAutoConnect() {
+ return false;
+ }
+
+ protected GBDeviceProtocol createDeviceProtocol() {
+ return new NothingProtocol(getDevice());
+ }
+
+ @Override
+ protected GBDeviceIoThread createDeviceIOThread() {
+ return new NothingIOThread(getDevice(), getContext(), (NothingProtocol) getDeviceProtocol(), Ear1Support.this, getBluetoothAdapter());
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingIOThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingIOThread.java
new file mode 100644
index 000000000..0c803be4e
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingIOThread.java
@@ -0,0 +1,45 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.nothing;
+
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.os.ParcelUuid;
+
+import androidx.annotation.NonNull;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.service.btclassic.BtClassicIoThread;
+
+import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
+
+public class NothingIOThread extends BtClassicIoThread {
+ private static final Logger LOG = LoggerFactory.getLogger(NothingIOThread.class);
+
+ private final NothingProtocol mNothingProtocol;
+
+ @NonNull
+ protected UUID getUuidToConnect(@NonNull ParcelUuid[] uuids) {
+ return mNothingProtocol.UUID_DEVICE_CTRL;
+ }
+
+ public NothingIOThread(GBDevice device, Context context, NothingProtocol deviceProtocol, Ear1Support ear1Support, BluetoothAdapter bluetoothAdapter) {
+ super(device, context, deviceProtocol, ear1Support, bluetoothAdapter);
+ mNothingProtocol = deviceProtocol;
+ }
+
+ @Override
+ protected byte[] parseIncoming(InputStream inStream) throws IOException {
+ byte[] buffer = new byte[1048576]; //HUGE read
+ int bytes = inStream.read(buffer);
+ LOG.debug("read " + bytes + " bytes. " + hexdump(buffer, 0, bytes));
+ return Arrays.copyOf(buffer, bytes);
+ }
+
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingProtocol.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingProtocol.java
new file mode 100644
index 000000000..5567adeaf
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/nothing/NothingProtocol.java
@@ -0,0 +1,235 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.nothing;
+
+import android.content.SharedPreferences;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+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.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
+import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
+
+import static nodomain.freeyourgadget.gadgetbridge.util.CheckSums.getCRC16ansi;
+import static nodomain.freeyourgadget.gadgetbridge.util.GB.hexdump;
+
+public class NothingProtocol extends GBDeviceProtocol {
+ private static final Logger LOG = LoggerFactory.getLogger(NothingProtocol.class);
+
+ final UUID UUID_DEVICE_CTRL = UUID.fromString("aeac4a03-dff5-498f-843a-34487cf133eb");
+
+
+ public static final byte CONTROL_DEVICE_TYPE_TWS_HEADSET = 1;
+
+ private static final int CONTROL_CRC = 0x20;
+
+ private static final byte MASK_RSP_CODE = 0x1f;
+ private static final short MASK_DEVICE_TYPE = 0x0F00;
+
+
+ private static final short MASK_REQUEST_CMD = (short) 0x8000;
+
+ private static final byte MASK_BATTERY = 0x7f;
+ private static final byte MASK_BATTERY_CHARGING = (byte) 0x80;
+
+
+ //incoming
+ private static final short battery_status = (short) 0xe001;
+ private static final short battery_status2 = (short) 0xc007;
+
+ 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 in_ear_detection = (short) 0xf004;
+
+ @Override
+ public GBDeviceEvent[] decodeResponse(byte[] responseData) {
+ ByteBuffer incoming = ByteBuffer.wrap(responseData);
+ incoming.order(ByteOrder.LITTLE_ENDIAN);
+
+ byte sof = incoming.get();
+ if (sof != 0x55) {
+ LOG.error("Error in message, wrong start of frame: " + hexdump(responseData));
+ return null;
+ }
+
+ short control = incoming.getShort();
+ if (!isSupportedDevice(control)) {
+ LOG.error("Unsupported device specified in message: " + hexdump(responseData));
+ return null;
+ }
+ if (!isOk(control)) {
+ LOG.error("Message is not ok: " + hexdump(responseData));
+ return null;
+ }
+ short command = incoming.getShort();
+ short length = incoming.getShort();
+ incoming.get();
+
+ byte[] payload = Arrays.copyOfRange(responseData, incoming.position(), incoming.position() + length);
+
+
+ switch (getRequestCommand(command)) {
+ case battery_status:
+ case battery_status2:
+ return handleBatteryInfo(payload);
+
+ case unk_maybe_ack:
+ LOG.debug("received ack");
+ break;
+ case unk_close_case:
+ LOG.debug("case closed");
+ break;
+
+ default:
+ LOG.debug("Incoming message - control:" + control + " requestCommand: " + (getRequestCommand(command) & 0xffff) + "length: " + length + " dump: " + hexdump(responseData));
+
+ }
+ return null;
+ }
+
+ boolean isCrcNeeded(short control) {
+ return (control & CONTROL_CRC) != 0;
+ }
+
+ private byte[] encodeMessage(short control, short command, byte[] payload) {
+
+ ByteBuffer msgBuf = ByteBuffer.allocate(8 + payload.length);
+ msgBuf.order(ByteOrder.LITTLE_ENDIAN);
+ msgBuf.put((byte) 0x55); //sof
+ msgBuf.putShort(control);
+ msgBuf.putShort(command);
+ msgBuf.putShort((short) payload.length);
+ msgBuf.put((byte) 0x00); //fsn TODO: is this always 0?
+ msgBuf.put(payload);
+
+ if (isCrcNeeded(control)) {
+ msgBuf.position(0);
+ ByteBuffer crcBuf = ByteBuffer.allocate(msgBuf.capacity() + 2);
+ crcBuf.order(ByteOrder.LITTLE_ENDIAN);
+ crcBuf.put(msgBuf);
+ crcBuf.putShort((short) getCRC16ansi(msgBuf.array()));
+ return crcBuf.array();
+ }
+
+ return msgBuf.array();
+ }
+
+ byte[] encodeBatteryStatusReq() {
+ return encodeMessage((short) 0x120, (short) 0xc007, new byte[]{});
+ }
+
+ byte[] encodeAudioMode(String desired) {
+ byte[] payload = new byte[]{0x01, 0x05, 0x00};
+
+ switch (desired) {
+ case "anc":
+ payload[1] = 0x01;
+ break;
+ case "transparency":
+ payload[1] = 0x07;
+ break;
+ case "off":
+ default:
+ }
+ return encodeMessage((short) 0x120, (short) 0xf00f, payload);
+ }
+
+ @Override
+ public byte[] encodeFindDevice(boolean start) {
+ byte payload = (byte) (start ? 0x01 : 0x00);
+ return encodeMessage((short) 0x120, (short) 0xf002, new byte[]{payload});
+ }
+
+ @Override
+ public byte[] encodeSendConfiguration(String config) {
+
+ SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(getDevice().getAddress());
+
+ switch (config) {
+ case DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_INEAR:
+ byte enabled = (byte) (prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_INEAR, true) ? 0x01 : 0x00);
+ return encodeMessage((short) 0x120, in_ear_detection, new byte[]{0x01, 0x01, enabled});
+ // response: 55 20 01 04 70 00 00 00
+ case DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_AUDIOMODE:
+ return encodeAudioMode(prefs.getString(DeviceSettingsPreferenceConst.PREF_NOTHING_EAR1_AUDIOMODE, "off"));
+ // response: 55 20 01 0F 70 00 00 00
+
+ default:
+ LOG.debug("CONFIG: " + config);
+ }
+
+ return super.encodeSendConfiguration(config);
+ }
+
+ @Override
+ 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();
+ }
+
+ private GBDeviceEvent[] handleBatteryInfo(byte[] payload) {
+ //LOG.debug("Battery payload: " + hexdump(payload));
+
+ /* payload:
+ 1st byte is number of batteries, then $number pairs follow:
+ {idx, value}
+
+ idx is 0x02 for left ear, 0x03 for right ear, 0x04 for case
+ value goes from 0-64 (equivalent of 0-100 in hexadecimal)
+
+
+ Since Gadgetbridge supports only one battery, we use an average of the levels for the
+ battery level.
+ If one of the batteries is recharging, we consider the battery as recharging.
+ */
+
+ GBDeviceEventBatteryInfo evBattery = new GBDeviceEventBatteryInfo();
+ evBattery.level = 0;
+ boolean batteryCharging = false;
+
+ int numBatteries = payload[0];
+ 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;
+ //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};
+ }
+
+ private short getRequestCommand(short command) {
+ return (short) (command | MASK_REQUEST_CMD);
+ }
+
+ private boolean isOk(short control) {
+ return (control & MASK_RSP_CODE) == 0;
+ }
+
+ private boolean isSupportedDevice(short control) {
+ return getDeviceType(control) == CONTROL_DEVICE_TYPE_TWS_HEADSET;
+ }
+
+ private byte getDeviceType(short control) {
+ return (byte) ((control & MASK_DEVICE_TYPE) >> 8);
+ }
+
+ protected NothingProtocol(GBDevice device) {
+ super(device);
+
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java
index b47599bcb..28c91189c 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/CheckSums.java
@@ -57,6 +57,24 @@ public class CheckSums {
crc &= 0xffff;
return crc;
}
+
+ public static int getCRC16ansi(byte[] seq) {
+ int crc = 0xffff;
+ int polynomial = 0xA001;
+
+ for (int i = 0; i < seq.length; i++) {
+ crc ^= seq[i] & 0xFF;
+ for (int j = 0; j < 8; j++) {
+ if ((crc & 1) != 0) {
+ crc = (crc >>> 1) ^ polynomial;
+ } else {
+ crc = crc >>> 1;
+ }
+ }
+ }
+
+ return crc & 0xFFFF;
+ }
public static int getCRC32(byte[] seq) {
CRC32 crc = new CRC32();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java
index 2ec3a4570..431df5ccf 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java
@@ -97,6 +97,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.mijia_lywsd02.MijiaLywsd02Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.miscale2.MiScale2DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.no1f1.No1F1Coordinator;
+import nodomain.freeyourgadget.gadgetbridge.devices.nothing.Ear1Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.nut.NutCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.pebble.PebbleCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeJFCoordinator;
@@ -306,6 +307,7 @@ public class DeviceHelper {
result.add(new UM25Coordinator());
result.add(new DomyosT540Cooridnator());
result.add(new FitProDeviceCoordinator());
+ result.add(new Ear1Coordinator());
return result;
}
diff --git a/app/src/main/res/drawable/ic_device_nothingear.xml b/app/src/main/res/drawable/ic_device_nothingear.xml
new file mode 100644
index 000000000..8b32ce55c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_device_nothingear.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_device_nothingear_disabled.xml b/app/src/main/res/drawable/ic_device_nothingear_disabled.xml
new file mode 100644
index 000000000..88c68f63c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_device_nothingear_disabled.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 47708b399..b3749e334 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -1775,5 +1775,11 @@
- 2
- 3
+
+
+ - anc
+ - transparency
+ - off
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 0ba88df0c..10c82c61f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1271,4 +1271,5 @@
Automatic Heart Rate measurements
Take measurements during sleep
Frequency of measurements
+ Nothing Ear (1)
diff --git a/app/src/main/res/xml/devicesettings_nothing_ear1.xml b/app/src/main/res/xml/devicesettings_nothing_ear1.xml
new file mode 100644
index 000000000..2e1a3e84b
--- /dev/null
+++ b/app/src/main/res/xml/devicesettings_nothing_ear1.xml
@@ -0,0 +1,16 @@
+
+
+
+
+