diff --git a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
index 1b7c08465..5184aecaa 100644
--- a/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
+++ b/GBDaoGenerator/src/nodomain/freeyourgadget/gadgetbridge/daogen/GBDaoGenerator.java
@@ -77,6 +77,7 @@ public class GBDaoGenerator {
addLefunActivitySample(schema, user, device);
addLefunBiometricSample(schema,user,device);
addLefunSleepSample(schema, user, device);
+ addSonySWR12Sample(schema, user, device);
addHybridHRActivitySample(schema, user, device);
addCalendarSyncState(schema, device);
@@ -407,6 +408,17 @@ public class GBDaoGenerator {
return activitySample;
}
+ private static Entity addSonySWR12Sample(Schema schema, Entity user, Entity device) {
+ Entity activitySample = addEntity(schema, "SonySWR12Sample");
+ activitySample.implementsSerializable();
+ addCommonActivitySampleProperties("AbstractActivitySample", activitySample, user, device);
+ addHeartRateProperties(activitySample);
+ activitySample.addIntProperty(SAMPLE_STEPS).notNull().codeBeforeGetterAndSetter(OVERRIDE);
+ activitySample.addIntProperty(SAMPLE_RAW_KIND).notNull().codeBeforeGetterAndSetter(OVERRIDE);
+ activitySample.addIntProperty(SAMPLE_RAW_INTENSITY).notNull().codeBeforeGetterAndSetter(OVERRIDE);
+ return activitySample;
+ }
+
private static Entity addLefunActivitySample(Schema schema, Entity user, Entity device) {
Entity activitySample = addEntity(schema, "LefunActivitySample");
activitySample.implementsSerializable();
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5e61bf86c..f0479d146 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -567,5 +567,8 @@
+
\ No newline at end of file
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
index 277404ee8..39f3c99ff 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
@@ -59,6 +59,7 @@ import nodomain.freeyourgadget.gadgetbridge.database.PeriodicExporter;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPreferencesActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.ConfigActivity;
+import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12PrefActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.zetime.ZeTimePreferenceActivity;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils;
@@ -137,6 +138,15 @@ public class SettingsActivity extends AbstractSettingsActivity {
}
});
+ pref = findPreference("pref_key_sonyswr12");
+ pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
+ public boolean onPreferenceClick(Preference preference) {
+ Intent intent = new Intent(SettingsActivity.this, SonySWR12PrefActivity.class);
+ startActivity(intent);
+ return true;
+ }
+ });
+
pref = findPreference("pref_key_blacklist");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sonyswr12/SonySWR12DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sonyswr12/SonySWR12DeviceCoordinator.java
new file mode 100644
index 000000000..67e936d7a
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sonyswr12/SonySWR12DeviceCoordinator.java
@@ -0,0 +1,122 @@
+package nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12;
+
+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.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 SonySWR12DeviceCoordinator extends AbstractDeviceCoordinator {
+
+ @Override
+ public DeviceType getDeviceType() {
+ return DeviceType.SONY_SWR12;
+ }
+
+ @NonNull
+ @Override
+ public DeviceType getSupportedType(GBDeviceCandidate candidate) {
+ String name = candidate.getDevice().getName();
+ if (!name.isEmpty() && name.toLowerCase().contains("swr12"))
+ return getDeviceType();
+ return DeviceType.UNKNOWN;
+ }
+
+ @Override
+ public String getManufacturer() {
+ return "Sony";
+ }
+
+ @Override
+ protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException {
+
+ }
+
+ @Nullable
+ @Override
+ public Class extends Activity> getPairingActivity() {
+ return null;
+ }
+
+ @Override
+ public boolean supportsActivityDataFetching() {
+ return true;
+ }
+
+ @Override
+ public boolean supportsActivityTracking() {
+ return true;
+ }
+
+ @Override
+ public SampleProvider extends ActivitySample> getSampleProvider(GBDevice device, DaoSession session) {
+ return new SonySWR12SampleProvider(device, session);
+ }
+
+ @Override
+ public InstallHandler findInstallHandler(Uri uri, Context context) {
+ return null;
+ }
+
+ @Override
+ public boolean supportsScreenshots() {
+ return false;
+ }
+
+ @Override
+ public int getAlarmSlotCount() {
+ return 5;
+ }
+
+ @Override
+ public boolean supportsSmartWakeup(GBDevice device) {
+ return true;
+ }
+
+ @Override
+ public boolean supportsHeartRateMeasurement(GBDevice device) {
+ return true;
+ }
+
+ @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 true;
+ }
+
+ @Override
+ public boolean supportsWeather() {
+ return false;
+ }
+
+ @Override
+ public boolean supportsFindDevice() {
+ return false;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sonyswr12/SonySWR12PrefActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sonyswr12/SonySWR12PrefActivity.java
new file mode 100644
index 000000000..4611a88ed
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sonyswr12/SonySWR12PrefActivity.java
@@ -0,0 +1,98 @@
+package nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.CompoundButton;
+
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.widget.AppCompatSpinner;
+import androidx.appcompat.widget.SwitchCompat;
+
+import java.util.Arrays;
+import java.util.List;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12Constants;
+
+public class SonySWR12PrefActivity extends AbstractGBActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_sonyswr12_settings);
+ setTitle(getString(R.string.sonyswr12_settings_title));
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ setVibrationSetting();
+ setStaminaSetting();
+ setAlarmIntervalSetting();
+ GBDevice device = GBApplication.app().getDeviceManager().getSelectedDevice();
+ int disablerVisibility = (device != null
+ && device.isConnected()
+ && device.getType() == DeviceType.SONY_SWR12) ? View.GONE : View.VISIBLE;
+ findViewById(R.id.settingsDisabler).setVisibility(disablerVisibility);
+ }
+
+ @Override
+ public boolean onSupportNavigateUp() {
+ onBackPressed();
+ return true;
+ }
+
+ private void setVibrationSetting() {
+ boolean isLow = GBApplication.getPrefs().getBoolean(SonySWR12Constants.VIBRATION_PREFERENCE, false);
+ SwitchCompat switchVibration = ((SwitchCompat) findViewById(R.id.lowVibration));
+ switchVibration.setChecked(isLow);
+ switchVibration.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ GBApplication.getPrefs().getPreferences().edit()
+ .putBoolean(SonySWR12Constants.VIBRATION_PREFERENCE, isChecked).apply();
+ GBApplication.deviceService().onSendConfiguration(SonySWR12Constants.VIBRATION_PREFERENCE);
+ }
+ });
+ }
+
+ private void setStaminaSetting() {
+ boolean isOn = GBApplication.getPrefs().getBoolean(SonySWR12Constants.STAMINA_PREFERENCE, false);
+ SwitchCompat switchStamina = ((SwitchCompat) findViewById(R.id.staminaOn));
+ switchStamina.setChecked(isOn);
+ switchStamina.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ GBApplication.getPrefs().getPreferences().edit()
+ .putBoolean(SonySWR12Constants.STAMINA_PREFERENCE, isChecked).apply();
+ GBApplication.deviceService().onSendConfiguration(SonySWR12Constants.STAMINA_PREFERENCE);
+ }
+ });
+ }
+
+ private void setAlarmIntervalSetting() {
+ String interval = GBApplication.getPrefs().getString(SonySWR12Constants.SMART_ALARM_INTERVAL_PREFERENCE, "0");
+ List intervalsArray = Arrays.asList(GBApplication.getContext().getResources().getStringArray(R.array.sonyswr12_smart_alarm_intervals));
+ int position = intervalsArray.indexOf(interval);
+ final AppCompatSpinner spinner = ((AppCompatSpinner) findViewById(R.id.smartAlarmSpinner));
+ spinner.setSelection(position);
+ spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view, int position, long id) {
+ String interval = (String) spinner.getItemAtPosition(position);
+ GBApplication.getPrefs().getPreferences().edit()
+ .putString(SonySWR12Constants.SMART_ALARM_INTERVAL_PREFERENCE, interval).apply();
+ GBApplication.deviceService().onSendConfiguration(SonySWR12Constants.SMART_ALARM_INTERVAL_PREFERENCE);
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView> parent) {
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sonyswr12/SonySWR12SampleProvider.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sonyswr12/SonySWR12SampleProvider.java
new file mode 100644
index 000000000..1f0bacf62
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sonyswr12/SonySWR12SampleProvider.java
@@ -0,0 +1,83 @@
+package nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import de.greenrobot.dao.AbstractDao;
+import de.greenrobot.dao.Property;
+import nodomain.freeyourgadget.gadgetbridge.devices.AbstractSampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
+import nodomain.freeyourgadget.gadgetbridge.entities.SonySWR12Sample;
+import nodomain.freeyourgadget.gadgetbridge.entities.SonySWR12SampleDao;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12Constants;
+
+public class SonySWR12SampleProvider extends AbstractSampleProvider {
+ public SonySWR12SampleProvider(GBDevice device, DaoSession session) {
+ super(device, session);
+ }
+
+ @Override
+ public AbstractDao getSampleDao() {
+ return getSession().getSonySWR12SampleDao();
+ }
+
+ @Nullable
+ @Override
+ protected Property getRawKindSampleProperty() {
+ return SonySWR12SampleDao.Properties.RawKind;
+ }
+
+ @NonNull
+ @Override
+ protected Property getTimestampSampleProperty() {
+ return SonySWR12SampleDao.Properties.Timestamp;
+ }
+
+ @NonNull
+ @Override
+ protected Property getDeviceIdentifierSampleProperty() {
+ return SonySWR12SampleDao.Properties.DeviceId;
+ }
+
+ @Override
+ public int normalizeType(int rawType) {
+ switch (rawType) {
+ case SonySWR12Constants.TYPE_ACTIVITY:
+ return ActivityKind.TYPE_ACTIVITY;
+ case SonySWR12Constants.TYPE_LIGHT:
+ return ActivityKind.TYPE_LIGHT_SLEEP;
+ case SonySWR12Constants.TYPE_DEEP:
+ return ActivityKind.TYPE_DEEP_SLEEP;
+ case SonySWR12Constants.TYPE_NOT_WORN:
+ return ActivityKind.TYPE_NOT_WORN;
+ }
+ return ActivityKind.TYPE_UNKNOWN;
+ }
+
+ @Override
+ public int toRawActivityKind(int activityKind) {
+ switch (activityKind) {
+ case ActivityKind.TYPE_ACTIVITY:
+ return SonySWR12Constants.TYPE_ACTIVITY;
+ case ActivityKind.TYPE_LIGHT_SLEEP:
+ return SonySWR12Constants.TYPE_LIGHT;
+ case ActivityKind.TYPE_DEEP_SLEEP:
+ return SonySWR12Constants.TYPE_DEEP;
+ case ActivityKind.TYPE_NOT_WORN:
+ return SonySWR12Constants.TYPE_NOT_WORN;
+ }
+ return SonySWR12Constants.TYPE_ACTIVITY;
+ }
+
+ @Override
+ public float normalizeIntensity(int rawIntensity) {
+ return rawIntensity;
+ }
+
+ @Override
+ public SonySWR12Sample createActivitySample() {
+ return new SonySWR12Sample();
+ }
+}
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 02607887f..ef48af631 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceType.java
@@ -76,6 +76,7 @@ public enum DeviceType {
LEFUN(210, R.drawable.ic_device_h30_h10, R.drawable.ic_device_h30_h10_disabled, R.string.devicetype_lefun),
ITAG(250, R.drawable.ic_device_itag, R.drawable.ic_device_itag_disabled, R.string.devicetype_itag),
VIBRATISSIMO(300, R.drawable.ic_device_lovetoy, R.drawable.ic_device_lovetoy_disabled, R.string.devicetype_vibratissimo),
+ SONY_SWR12(310, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_sonyswr12),
TEST(1000, R.drawable.ic_device_default, R.drawable.ic_device_default_disabled, R.string.devicetype_test);
private final int key;
@DrawableRes
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 44b1b3ef4..e39f5fdcf 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceSupportFactory.java
@@ -62,6 +62,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.pebble.PebbleSupport
import nodomain.freeyourgadget.gadgetbridge.service.devices.pinetime.PineTimeJFSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.roidmi.RoidmiSupport;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12DeviceSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.tlw64.TLW64Support;
import nodomain.freeyourgadget.gadgetbridge.service.devices.vibratissimo.VibratissimoSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.watch9.Watch9DeviceSupport;
@@ -264,6 +265,9 @@ public class DeviceSupportFactory {
case LEFUN:
deviceSupport = new ServiceDeviceSupport(new LefunDeviceSupport(), EnumSet.of(ServiceDeviceSupport.Flags.BUSY_CHECKING));
break;
+ case SONY_SWR12:
+ deviceSupport = new ServiceDeviceSupport(new SonySWR12DeviceSupport(), 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/sonyswr12/SonySWR12Constants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12Constants.java
new file mode 100644
index 000000000..1c01e4811
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12Constants.java
@@ -0,0 +1,22 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12;
+
+import java.util.UUID;
+
+public class SonySWR12Constants {
+ //accessory host service
+ public static final String BASE_UUID_AHS = "0000%s-37CB-11E3-8682-0002A5D5C51B";
+ public static final UUID UUID_SERVICE_AHS = UUID.fromString(String.format(BASE_UUID_AHS, "0200"));
+ public static final UUID UUID_CHARACTERISTIC_ALARM = UUID.fromString(String.format(BASE_UUID_AHS, "0204"));
+ public static final UUID UUID_CHARACTERISTIC_EVENT = UUID.fromString(String.format(BASE_UUID_AHS, "0205"));
+ public static final UUID UUID_CHARACTERISTIC_TIME = UUID.fromString(String.format(BASE_UUID_AHS, "020B"));
+ public static final UUID UUID_CHARACTERISTIC_CONTROL_POINT = UUID.fromString(String.format(BASE_UUID_AHS, "0208"));
+
+ public static final String VIBRATION_PREFERENCE = "vibration_preference";
+ public static final String STAMINA_PREFERENCE = "stamina_preference";
+ public static final String SMART_ALARM_INTERVAL_PREFERENCE = "smart_alarm_interval_preference";
+
+ public static final int TYPE_ACTIVITY = 0;
+ public static final int TYPE_LIGHT = 1;
+ public static final int TYPE_DEEP = 2;
+ public static final int TYPE_NOT_WORN = 3;
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12DeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12DeviceSupport.java
new file mode 100644
index 000000000..6e47e6ba1
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12DeviceSupport.java
@@ -0,0 +1,351 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12;
+
+import android.bluetooth.BluetoothGatt;
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.content.Intent;
+import android.net.Uri;
+import android.widget.Toast;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.UUID;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
+import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
+import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
+import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
+import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
+import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
+import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
+import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.GattService;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.IntentListener;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfo;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.profiles.battery.BatteryInfoProfile;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity.EventBase;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity.EventFactory;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.alarm.BandAlarm;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.alarm.BandAlarms;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.control.CommandCode;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.control.ControlPointLowVibration;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.control.ControlPointWithValue;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.time.BandTime;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
+
+// done:
+// - time sync
+// - alarms (also smart)
+// - fetching activity(walking, sleep)
+// - stamina mode
+// - vibration intensity
+// - realtime heart rate
+// todo options:
+// - "get moving"
+// - get notified: -call, -notification, -notification from, -do not disturb
+// - media control: media/find phone(tap once for play pause, tap twice for next, tap triple for previous)
+
+public class SonySWR12DeviceSupport extends AbstractBTLEDeviceSupport {
+ private static final Logger LOG = LoggerFactory.getLogger(SonySWR12DeviceSupport.class);
+ private SonySWR12HandlerThread processor = null;
+
+ private final BatteryInfoProfile batteryInfoProfile;
+ private final IntentListener mListener = new IntentListener() {
+ @Override
+ public void notify(Intent intent) {
+ if (intent.getAction().equals(BatteryInfoProfile.ACTION_BATTERY_INFO)) {
+ BatteryInfo info = intent.getParcelableExtra(BatteryInfoProfile.EXTRA_BATTERY_INFO);
+ GBDeviceEventBatteryInfo gbInfo = new GBDeviceEventBatteryInfo();
+ gbInfo.level = (short) info.getPercentCharged();
+ handleGBDeviceEvent(gbInfo);
+ }
+ }
+ };
+
+ public SonySWR12DeviceSupport() {
+ super(LOG);
+ addSupportedService(GattService.UUID_SERVICE_BATTERY_SERVICE);
+ addSupportedService(SonySWR12Constants.UUID_SERVICE_AHS);
+ batteryInfoProfile = new BatteryInfoProfile<>(this);
+ batteryInfoProfile.addListener(mListener);
+ addSupportedProfile(batteryInfoProfile);
+ }
+
+ @Override
+ protected TransactionBuilder initializeDevice(TransactionBuilder builder) {
+ initialize();
+ setTime(builder);
+ batteryInfoProfile.requestBatteryInfo(builder);
+ return builder;
+ }
+
+ private SonySWR12HandlerThread getProcessor() {
+ if (processor == null) {
+ processor = new SonySWR12HandlerThread(getDevice(), getContext());
+ processor.start();
+ }
+ return processor;
+ }
+
+ private void initialize() {
+ if (gbDevice.getState() != GBDevice.State.INITIALIZED) {
+ gbDevice.setFirmwareVersion("N/A");
+ gbDevice.setFirmwareVersion2("N/A");
+ gbDevice.setState(GBDevice.State.INITIALIZED);
+ gbDevice.sendDeviceUpdateIntent(getContext());
+ }
+ }
+
+ @Override
+ public boolean useAutoConnect() {
+ return false;
+ }
+
+ @Override
+ public void onNotification(NotificationSpec notificationSpec) {
+
+ }
+
+ @Override
+ public void onDeleteNotification(int id) {
+
+ }
+
+ @Override
+ public void onSetTime() {
+ try {
+ TransactionBuilder builder = performInitialized("setTime");
+ setTime(builder);
+ builder.queue(getQueue());
+ } catch (Exception e) {
+ GB.toast(getContext(), "Error setting time: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
+ }
+ }
+
+ private void setTime(TransactionBuilder builder) {
+ BluetoothGattCharacteristic timeCharacteristic = getCharacteristic(SonySWR12Constants.UUID_CHARACTERISTIC_TIME);
+ builder.write(timeCharacteristic, new BandTime(Calendar.getInstance()).toByteArray());
+ }
+
+ @Override
+ public void onSetAlarms(ArrayList extends Alarm> alarms) {
+ try {
+ BluetoothGattCharacteristic alarmCharacteristic = getCharacteristic(SonySWR12Constants.UUID_CHARACTERISTIC_ALARM);
+ TransactionBuilder builder = performInitialized("alarm");
+ int prefInterval = Integer.valueOf(GBApplication.getPrefs().getString(SonySWR12Constants.SMART_ALARM_INTERVAL_PREFERENCE, "0"));
+ ArrayList bandAlarmList = new ArrayList<>();
+ for (Alarm alarm : alarms) {
+ BandAlarm bandAlarm = BandAlarm.fromAppAlarm(alarm, bandAlarmList.size(), alarm.getSmartWakeup() ? prefInterval : 0);
+ if (bandAlarm != null)
+ bandAlarmList.add(bandAlarm);
+ }
+ builder.write(alarmCharacteristic, new BandAlarms(bandAlarmList).toByteArray());
+ builder.queue(getQueue());
+ } catch (Exception e) {
+ GB.toast(getContext(), "Error setting alarms: " + e.getLocalizedMessage(), Toast.LENGTH_LONG, GB.ERROR);
+ }
+ }
+
+ @Override
+ public boolean onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
+ return super.onCharacteristicRead(gatt, characteristic, status);
+ }
+
+ @Override
+ public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
+ if (super.onCharacteristicChanged(gatt, characteristic))
+ return true;
+ UUID uuid = characteristic.getUuid();
+ if (uuid.equals(SonySWR12Constants.UUID_CHARACTERISTIC_EVENT)) {
+ try {
+ EventBase event = EventFactory.readEventFromByteArray(characteristic.getValue());
+ getProcessor().process(event);
+ } catch (Exception e) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void onSetCallState(CallSpec callSpec) {
+
+ }
+
+ @Override
+ public void onSetCannedMessages(CannedMessagesSpec cannedMessagesSpec) {
+
+ }
+
+ @Override
+ public void onSetMusicState(MusicStateSpec stateSpec) {
+
+ }
+
+ @Override
+ public void onSetMusicInfo(MusicSpec musicSpec) {
+
+ }
+
+ @Override
+ public void onEnableRealtimeSteps(boolean enable) {
+ //doesn't support realtime steps
+ //supports only realtime heart rate
+ }
+
+ @Override
+ public void onInstallApp(Uri uri) {
+
+ }
+
+ @Override
+ public void onAppInfoReq() {
+
+ }
+
+ @Override
+ public void onAppStart(UUID uuid, boolean start) {
+
+ }
+
+ @Override
+ public void onAppDelete(UUID uuid) {
+
+ }
+
+ @Override
+ public void onAppConfiguration(UUID appUuid, String config, Integer id) {
+
+ }
+
+ @Override
+ public void onAppReorder(UUID[] uuids) {
+
+ }
+
+ @Override
+ public void onFetchRecordedData(int dataTypes) {
+ try {
+ TransactionBuilder builder = performInitialized("fetchActivity");
+ builder.notify(getCharacteristic(SonySWR12Constants.UUID_CHARACTERISTIC_EVENT), true);
+ ControlPointWithValue flushControl = new ControlPointWithValue(CommandCode.FLUSH_ACTIVITY, 0);
+ builder.write(getCharacteristic(SonySWR12Constants.UUID_CHARACTERISTIC_CONTROL_POINT), flushControl.toByteArray());
+ builder.queue(getQueue());
+ } catch (Exception e) {
+ LOG.error("failed to fetch activity data", e);
+ }
+ }
+
+ @Override
+ public void onReset(int flags) {
+
+ }
+
+ @Override
+ public void onHeartRateTest() {
+ }
+
+ @Override
+ public void onEnableRealtimeHeartRateMeasurement(boolean enable) {
+ try {
+ TransactionBuilder builder = performInitialized("HeartRateTest");
+ builder.notify(getCharacteristic(SonySWR12Constants.UUID_CHARACTERISTIC_EVENT), enable);
+ ControlPointWithValue controlPointHeart = new ControlPointWithValue(CommandCode.HEARTRATE_REALTIME, enable ? 1 : 0);
+ builder.write(getCharacteristic(SonySWR12Constants.UUID_CHARACTERISTIC_CONTROL_POINT), controlPointHeart.toByteArray());
+ builder.queue(getQueue());
+ } catch (IOException ex) {
+ LOG.error("Unable to read heart rate from Sony device", ex);
+ }
+ }
+
+ @Override
+ public void onFindDevice(boolean start) {
+
+ }
+
+ @Override
+ public void onSetConstantVibration(int integer) {
+
+ }
+
+ @Override
+ public void onScreenshotReq() {
+
+ }
+
+ @Override
+ public void onEnableHeartRateSleepSupport(boolean enable) {
+
+ }
+
+ @Override
+ public void onSetHeartRateMeasurementInterval(int seconds) {
+
+ }
+
+ @Override
+ public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) {
+
+ }
+
+ @Override
+ public void onDeleteCalendarEvent(byte type, long id) {
+
+ }
+
+ @Override
+ public void onSendConfiguration(String config) {
+ try {
+ switch (config) {
+ case SonySWR12Constants.STAMINA_PREFERENCE: {
+ //stamina can be:
+ //disabled = 0, enabled = 1 or todo auto on low battery = 2
+ int status = GBApplication.getPrefs().getBoolean(config, false) ? 1 : 0;
+ TransactionBuilder builder = performInitialized(config);
+ ControlPointWithValue vibrationControl = new ControlPointWithValue(CommandCode.STAMINA_MODE, status);
+ builder.write(getCharacteristic(SonySWR12Constants.UUID_CHARACTERISTIC_CONTROL_POINT), vibrationControl.toByteArray());
+ builder.queue(getQueue());
+ break;
+ }
+ case SonySWR12Constants.VIBRATION_PREFERENCE: {
+ boolean isEnabled = GBApplication.getPrefs().getBoolean(config, false);
+ TransactionBuilder builder = performInitialized(config);
+ ControlPointLowVibration vibrationControl = new ControlPointLowVibration(isEnabled);
+ builder.write(getCharacteristic(SonySWR12Constants.UUID_CHARACTERISTIC_CONTROL_POINT), vibrationControl.toByteArray());
+ builder.queue(getQueue());
+ break;
+ }
+ case SonySWR12Constants.SMART_ALARM_INTERVAL_PREFERENCE: {
+ onSetAlarms(new ArrayList(DBHelper.getAlarms(gbDevice)));
+ }
+ }
+ } catch (Exception exc) {
+ LOG.error("failed to send config " + config, exc);
+ }
+ }
+
+ @Override
+ public void onReadConfiguration(String config) {
+
+ }
+
+ @Override
+ public void onTestNewFunction() {
+
+ }
+
+ @Override
+ public void onSendWeather(WeatherSpec weatherSpec) {
+
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12HandlerThread.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12HandlerThread.java
new file mode 100644
index 000000000..2ce1816bd
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12HandlerThread.java
@@ -0,0 +1,135 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHandler;
+import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
+import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12SampleProvider;
+import nodomain.freeyourgadget.gadgetbridge.entities.SonySWR12Sample;
+import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
+import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
+import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity.ActivityBase;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity.ActivitySleep;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity.ActivityWithData;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity.EventBase;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity.EventCode;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity.EventWithActivity;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity.EventWithValue;
+import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceIoThread;
+
+public class SonySWR12HandlerThread extends GBDeviceIoThread {
+ private static final Logger LOG = LoggerFactory.getLogger(SonySWR12HandlerThread.class);
+
+ public SonySWR12HandlerThread(GBDevice gbDevice, Context context) {
+ super(gbDevice, context);
+ }
+
+ public void process(EventBase event) {
+ if (event instanceof EventWithValue) {
+ if (event.getCode() == EventCode.HEART_RATE) {
+ processRealTimeHeartRate((EventWithValue) event);
+ }
+ } else if (event instanceof EventWithActivity) {
+ processWithActivity((EventWithActivity) event);
+ }
+ }
+
+ private void processRealTimeHeartRate(EventWithValue event) {
+ try {
+ DBHandler dbHandler = GBApplication.acquireDB();
+ Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId();
+ Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
+ SonySWR12SampleProvider provider = new SonySWR12SampleProvider(getDevice(), dbHandler.getDaoSession());
+ int timestamp = getTimestamp();
+ SonySWR12Sample sample = new SonySWR12Sample(timestamp, deviceId, userId, (int) event.value, ActivitySample.NOT_MEASURED, 0, 1);
+ provider.addGBActivitySample(sample);
+ GBApplication.releaseDB();
+ Intent intent = new Intent(DeviceService.ACTION_REALTIME_SAMPLES)
+ .putExtra(DeviceService.EXTRA_REALTIME_SAMPLE, sample)
+ .putExtra(DeviceService.EXTRA_TIMESTAMP, timestamp);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(intent);
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ }
+
+ private int getTimestamp() {
+ return (int) (System.currentTimeMillis() / 1000);
+ }
+
+ private void processWithActivity(EventWithActivity event) {
+ List payloadList = event.activityList;
+ for (ActivityBase activity : payloadList) {
+ switch (activity.getType()) {
+ case WALK:
+ case RUN:
+ addActivity((ActivityWithData) activity);
+ break;
+ case SLEEP:
+ addSleep((ActivitySleep) activity);
+ break;
+ }
+ }
+ }
+
+ private void addActivity(ActivityWithData activity) {
+ try {
+ DBHandler dbHandler = GBApplication.acquireDB();
+ Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId();
+ Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
+ SonySWR12SampleProvider provider = new SonySWR12SampleProvider(getDevice(), dbHandler.getDaoSession());
+ int kind = SonySWR12Constants.TYPE_ACTIVITY;
+ SonySWR12Sample sample = new SonySWR12Sample(activity.getTimeStampSec(), deviceId, userId, ActivitySample.NOT_MEASURED, activity.data, kind, 1);
+ provider.addGBActivitySample(sample);
+ GBApplication.releaseDB();
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ }
+
+ private void addSleep(ActivitySleep activity) {
+ try {
+ DBHandler dbHandler = GBApplication.acquireDB();
+ Long userId = DBHelper.getUser(dbHandler.getDaoSession()).getId();
+ Long deviceId = DBHelper.getDevice(getDevice(), dbHandler.getDaoSession()).getId();
+ SonySWR12SampleProvider provider = new SonySWR12SampleProvider(getDevice(), dbHandler.getDaoSession());
+ int kind;
+ switch (activity.sleepLevel) {
+ case LIGHT:
+ kind = SonySWR12Constants.TYPE_LIGHT;
+ break;
+ case DEEP:
+ kind = SonySWR12Constants.TYPE_DEEP;
+ break;
+ default:
+ kind = SonySWR12Constants.TYPE_ACTIVITY;
+ break;
+ }
+ if (kind == SonySWR12Constants.TYPE_LIGHT || kind == SonySWR12Constants.TYPE_DEEP) {
+ //need so much samples because sleep has exact duration
+ //so empty samples are for right representation of sleep on activity charts
+ SonySWR12Sample sample = new SonySWR12Sample(activity.getTimeStampSec(), deviceId, userId, ActivitySample.NOT_MEASURED, 0, SonySWR12Constants.TYPE_NOT_WORN, 1);
+ provider.addGBActivitySample(sample);
+ sample = new SonySWR12Sample(activity.getTimeStampSec() + 2, deviceId, userId, ActivitySample.NOT_MEASURED, 0, kind, 1);
+ provider.addGBActivitySample(sample);
+ sample = new SonySWR12Sample(activity.getTimeStampSec() + activity.durationMin * 60 - 2, deviceId, userId, ActivitySample.NOT_MEASURED, 0, kind, 1);
+ provider.addGBActivitySample(sample);
+ sample = new SonySWR12Sample(activity.getTimeStampSec() + activity.durationMin * 60, deviceId, userId, ActivitySample.NOT_MEASURED, 0, SonySWR12Constants.TYPE_NOT_WORN, 1);
+ provider.addGBActivitySample(sample);
+ }
+ GBApplication.releaseDB();
+ } catch (Exception e) {
+ LOG.error(e.getMessage(), e);
+ }
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12Util.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12Util.java
new file mode 100644
index 000000000..7f2497c4b
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/SonySWR12Util.java
@@ -0,0 +1,22 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class SonySWR12Util {
+
+ public static long secSince2013() {
+ //sony uses time on band since 2013 for some reason
+ final Calendar instance = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+ instance.set(2013, 0, 1, 0, 0, 0);
+ instance.set(14, 0);
+ return instance.getTimeInMillis()/1000;
+ }
+
+ public static String timeToString(long sec) {
+ SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss");
+ return format.format(new Date(sec * 1000));
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityBase.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityBase.java
new file mode 100644
index 000000000..1cb102fb6
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityBase.java
@@ -0,0 +1,35 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.UIntBitWriter;
+
+public abstract class ActivityBase {
+ protected final ActivityType type;
+ protected final int timeOffsetMin;
+ private final long timeStampSec;
+
+ public ActivityBase(ActivityType type, int timeOffsetMin, long timeStampSec) {
+ if (timeOffsetMin < 0 || timeOffsetMin > 1440) {
+ throw new IllegalArgumentException("activity time offset out of range: " + timeOffsetMin);
+ }
+ this.type = type;
+ this.timeOffsetMin = timeOffsetMin;
+ this.timeStampSec = timeStampSec + this.timeOffsetMin * 60;
+ }
+
+ public final int getTimeStampSec() {
+ return (int) (timeStampSec);
+ }
+
+ public final ActivityType getType() {
+ return this.type;
+ }
+
+ protected final UIntBitWriter getWriterWithTypeAndOffset() {
+ UIntBitWriter uIntBitWriter = new UIntBitWriter(32);
+ uIntBitWriter.append(4, this.type.value);
+ uIntBitWriter.append(12, this.timeOffsetMin);
+ return uIntBitWriter;
+ }
+
+ public abstract long toLong();
+}
\ No newline at end of file
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityHeartRate.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityHeartRate.java
new file mode 100644
index 000000000..611c64c33
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityHeartRate.java
@@ -0,0 +1,22 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.UIntBitWriter;
+
+public class ActivityHeartRate extends ActivityBase {
+ public final int bpm;
+
+ public ActivityHeartRate(int timeOffsetMin, int bpm, Long timeStampSec) {
+ super(ActivityType.HEART_RATE, timeOffsetMin, timeStampSec);
+ if (bpm < 0 || bpm > 65535) {
+ throw new IllegalArgumentException("bpm out of range: " + bpm);
+ }
+ this.bpm = bpm;
+ }
+
+ @Override
+ public long toLong() {
+ UIntBitWriter writerWithTypeAndOffset = this.getWriterWithTypeAndOffset();
+ writerWithTypeAndOffset.append(16, this.bpm);
+ return writerWithTypeAndOffset.getValue();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivitySleep.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivitySleep.java
new file mode 100644
index 000000000..f8afb9703
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivitySleep.java
@@ -0,0 +1,22 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.UIntBitWriter;
+
+public class ActivitySleep extends ActivityBase {
+ public final SleepLevel sleepLevel;
+ public final int durationMin;
+
+ public ActivitySleep(int timeOffsetMin, int durationMin, SleepLevel sleepLevel, Long timeStampSec) {
+ super(ActivityType.SLEEP, timeOffsetMin, timeStampSec);
+ this.durationMin = durationMin;
+ this.sleepLevel = sleepLevel;
+ }
+
+ @Override
+ public long toLong() {
+ UIntBitWriter writerWithTypeAndOffset = this.getWriterWithTypeAndOffset();
+ writerWithTypeAndOffset.append(14, this.durationMin);
+ writerWithTypeAndOffset.append(2, this.sleepLevel.value);
+ return writerWithTypeAndOffset.getValue();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityType.java
new file mode 100644
index 000000000..e9c6d92e3
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityType.java
@@ -0,0 +1,23 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+public enum ActivityType {
+ WALK(1),
+ RUN(2),
+ SLEEP(3),
+ HEART_RATE(10),
+ END(14);
+
+ final int value;
+
+ ActivityType(int value) {
+ this.value = value;
+ }
+
+ public static ActivityType fromInt(int i) {
+ for (ActivityType type : values()){
+ if (type.value == i)
+ return type;
+ }
+ throw new IllegalArgumentException("wrong activity type: " + i);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityWithData.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityWithData.java
new file mode 100644
index 000000000..37348177b
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/ActivityWithData.java
@@ -0,0 +1,22 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.UIntBitWriter;
+
+public class ActivityWithData extends ActivityBase {
+ public final int data;
+
+ public ActivityWithData(ActivityType activityType, int timeOffsetMin, int data, Long timeStampSec) {
+ super(activityType, timeOffsetMin, timeStampSec);
+ if (data < 0 || data > 65535) {
+ throw new IllegalArgumentException("data out of range: " + data);
+ }
+ this.data = data;
+ }
+
+ @Override
+ public long toLong() {
+ UIntBitWriter writerWithTypeAndOffset = this.getWriterWithTypeAndOffset();
+ writerWithTypeAndOffset.append(16, this.data);
+ return writerWithTypeAndOffset.getValue();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventBase.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventBase.java
new file mode 100644
index 000000000..1b1d31161
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventBase.java
@@ -0,0 +1,21 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayWriter;
+
+public abstract class EventBase {
+ protected final EventCode eventCode;
+
+ protected EventBase(EventCode eventCode) {
+ this.eventCode = eventCode;
+ }
+
+ public EventCode getCode() {
+ return this.eventCode;
+ }
+
+ protected ByteArrayWriter getValueWriter() {
+ ByteArrayWriter byteArrayWriter = new ByteArrayWriter();
+ byteArrayWriter.appendUint8(this.eventCode.value);
+ return byteArrayWriter;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventCode.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventCode.java
new file mode 100644
index 000000000..777d0dba6
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventCode.java
@@ -0,0 +1,21 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+public enum EventCode {
+ STEPS(3),
+ ACTIVITY_DATA(5),
+ HEART_RATE(9);
+
+ final int value;
+
+ EventCode(int value) {
+ this.value = value;
+ }
+
+ static EventCode fromInt(int i) {
+ for (EventCode code : values()){
+ if (code.value == i)
+ return code;
+ }
+ throw new RuntimeException("wrong event code: " + i);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventFactory.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventFactory.java
new file mode 100644
index 000000000..03e09e61a
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventFactory.java
@@ -0,0 +1,26 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.IntFormat;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayReader;
+
+public class EventFactory {
+
+ public static EventBase readEventFromByteArray(byte[] array) {
+ try {
+ ByteArrayReader byteArrayReader = new ByteArrayReader(array);
+ EventCode eventCode = EventCode.fromInt(byteArrayReader.readUint8());
+ switch (eventCode) {
+ case HEART_RATE: {
+ long value = byteArrayReader.readInt(IntFormat.UINT32);
+ return new EventWithValue(eventCode, value);
+ }
+ case ACTIVITY_DATA: {
+ return EventWithActivity.fromByteArray(byteArrayReader);
+ }
+ default: return null;
+ }
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventWithActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventWithActivity.java
new file mode 100644
index 000000000..e7c804ddd
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventWithActivity.java
@@ -0,0 +1,62 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.SonySWR12Util;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.IntFormat;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.UIntBitReader;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayReader;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayWriter;
+
+public class EventWithActivity extends EventBase {
+ public final long timeStampSec;
+ public final List activityList;
+
+ private EventWithActivity(long timeStampSec, List activityList) {
+ super(EventCode.ACTIVITY_DATA);
+ this.timeStampSec = timeStampSec;
+ this.activityList = activityList;
+ }
+
+ public static EventWithActivity fromByteArray(ByteArrayReader byteArrayReader) {
+ long timeOffset = byteArrayReader.readInt(IntFormat.UINT32);
+ long timeStampSec = SonySWR12Util.secSince2013() + timeOffset;
+ ArrayList activities = new ArrayList<>();
+ while (byteArrayReader.getBytesLeft() > 0) {
+ UIntBitReader uIntBitReader = new UIntBitReader(byteArrayReader.readInt(IntFormat.UINT32), 32);
+ ActivityType activityType = ActivityType.fromInt(uIntBitReader.read(4));
+ int offsetMin = uIntBitReader.read(12);
+ ActivityBase activityPayload;
+ switch (activityType) {
+ case SLEEP: {
+ SleepLevel sleepLevel = SleepLevel.fromInt(uIntBitReader.read(2));
+ int duration = uIntBitReader.read(14);
+ activityPayload = new ActivitySleep(offsetMin, duration, sleepLevel, timeStampSec);
+ break;
+ }
+ case HEART_RATE: {
+ int bpm = uIntBitReader.read(16);
+ activityPayload = new ActivityHeartRate(offsetMin, bpm, timeStampSec);
+ break;
+ }
+ default: {
+ int data = uIntBitReader.read(16);
+ activityPayload = new ActivityWithData(activityType, offsetMin, data, timeStampSec);
+ break;
+ }
+ }
+ activities.add(activityPayload);
+ }
+ return new EventWithActivity(timeStampSec, activities);
+ }
+
+ public byte[] toByteArray() {
+ ByteArrayWriter byteArrayWriter = this.getValueWriter();
+ byteArrayWriter.appendUint32(this.timeStampSec);
+ for (ActivityBase activity : activityList){
+ byteArrayWriter.appendUint32(activity.toLong());
+ }
+ return byteArrayWriter.getByteArray();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventWithValue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventWithValue.java
new file mode 100644
index 000000000..cf81e5eb0
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/EventWithValue.java
@@ -0,0 +1,18 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayWriter;
+
+public class EventWithValue extends EventBase {
+ public final long value;
+
+ public EventWithValue(EventCode eventCode, long value) {
+ super(eventCode);
+ this.value = value;
+ }
+
+ public byte[] toByteArray() {
+ ByteArrayWriter byteArrayWriter = this.getValueWriter();
+ byteArrayWriter.appendUint32(this.value);
+ return byteArrayWriter.getByteArray();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/SleepLevel.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/SleepLevel.java
new file mode 100644
index 000000000..c8565467f
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/activity/SleepLevel.java
@@ -0,0 +1,21 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.activity;
+
+public enum SleepLevel {
+ AWAKE(0),
+ LIGHT(1),
+ DEEP(2);
+
+ final int value;
+
+ SleepLevel(int value){
+ this.value = value;
+ }
+
+ public static SleepLevel fromInt(int i) {
+ for (SleepLevel level : values()){
+ if (level.value == i)
+ return level;
+ }
+ throw new RuntimeException("wrong sleep level: " + i);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/AlarmRepeat.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/AlarmRepeat.java
new file mode 100644
index 000000000..28ef12656
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/AlarmRepeat.java
@@ -0,0 +1,53 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.alarm;
+
+import java.util.Arrays;
+
+import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.UIntBitWriter;
+
+public class AlarmRepeat {
+ private final boolean[] repeat = new boolean[7];
+
+ public AlarmRepeat(Alarm alarm) {
+ super();
+ setRepeatOnDay(0, alarm.getRepetition(Alarm.ALARM_MON));
+ setRepeatOnDay(1, alarm.getRepetition(Alarm.ALARM_TUE));
+ setRepeatOnDay(2, alarm.getRepetition(Alarm.ALARM_WED));
+ setRepeatOnDay(3, alarm.getRepetition(Alarm.ALARM_THU));
+ setRepeatOnDay(4, alarm.getRepetition(Alarm.ALARM_FRI));
+ setRepeatOnDay(5, alarm.getRepetition(Alarm.ALARM_SAT));
+ setRepeatOnDay(6, alarm.getRepetition(Alarm.ALARM_SUN));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this != o) {
+ if (o == null || this.getClass() != o.getClass()) {
+ return false;
+ }
+ return Arrays.equals(this.repeat, ((AlarmRepeat) o).repeat);
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Arrays.hashCode(this.repeat);
+ }
+
+ public void setRepeatOnDay(int i, boolean b) {
+ this.repeat[i] = b;
+ }
+
+ public int toInt() {
+ UIntBitWriter uIntBitWriter = new UIntBitWriter(7);
+ uIntBitWriter.appendBoolean(this.repeat[6]);
+ uIntBitWriter.appendBoolean(this.repeat[5]);
+ uIntBitWriter.appendBoolean(this.repeat[4]);
+ uIntBitWriter.appendBoolean(this.repeat[3]);
+ uIntBitWriter.appendBoolean(this.repeat[2]);
+ uIntBitWriter.appendBoolean(this.repeat[1]);
+ uIntBitWriter.appendBoolean(this.repeat[0]);
+ return (int) uIntBitWriter.getValue();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/AlarmState.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/AlarmState.java
new file mode 100644
index 000000000..1564207f6
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/AlarmState.java
@@ -0,0 +1,13 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.alarm;
+
+public enum AlarmState {
+ TRIGGERED( 0),
+ SNOOZED(1),
+ IDLE(2);
+
+ final int value;
+
+ AlarmState(int value) {
+ this.value = value;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/BandAlarm.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/BandAlarm.java
new file mode 100644
index 000000000..306ba6d94
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/BandAlarm.java
@@ -0,0 +1,60 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.alarm;
+
+import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
+
+public class BandAlarm {
+ public static BandAlarm fromAppAlarm(Alarm alarm, int index, int interval) {
+ if (!alarm.getEnabled()) return null;
+ //smart wakeup = (0,10..60 min)/5
+ int ahsInterval = interval / 5;
+ return new BandAlarm(AlarmState.IDLE, index, ahsInterval, alarm.getHour(), alarm.getMinute(), new AlarmRepeat(alarm));
+ }
+
+ public AlarmState state;
+ public int index;
+ public int interval;
+ public int hour;
+ public int minute;
+ public AlarmRepeat repeat;
+
+ public BandAlarm(AlarmState state, int index, int interval, int hour, int minute, AlarmRepeat repeat) {
+ this.state = state;
+ this.index = index;
+ this.interval = interval;
+ this.hour = hour;
+ this.minute = minute;
+ this.repeat = repeat;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this != o) {
+ if (o == null || this.getClass() != o.getClass()) {
+ return false;
+ }
+ BandAlarm bandAlarm = (BandAlarm) o;
+ if (this.index != bandAlarm.index) {
+ return false;
+ }
+ if (this.hour != bandAlarm.hour) {
+ return false;
+ }
+ if (this.interval != bandAlarm.interval) {
+ return false;
+ }
+ if (this.minute != bandAlarm.minute) {
+ return false;
+ }
+ if (!this.repeat.equals(bandAlarm.repeat)) {
+ return false;
+ }
+ return this.state == bandAlarm.state;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return ((((this.state.hashCode() * 31 + this.index) * 31 + this.interval) * 31 + this.hour) * 31 + this.minute) * 31 + this.repeat.hashCode();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/BandAlarms.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/BandAlarms.java
new file mode 100644
index 000000000..7cd7676dc
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/alarm/BandAlarms.java
@@ -0,0 +1,35 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.alarm;
+
+import java.util.List;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.UIntBitWriter;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayWriter;
+
+public class BandAlarms {
+ public final List alarms;
+
+ public BandAlarms(List alarms) {
+ this.alarms = alarms;
+ }
+
+ public byte[] toByteArray() {
+ ByteArrayWriter byteArrayWriter = new ByteArrayWriter();
+ if (this.alarms.size() == 0) {
+ byteArrayWriter.appendUint32(1073741824L);
+ } else {
+ for (BandAlarm bandAlarm : this.alarms) {
+ UIntBitWriter uIntBitWriter = new UIntBitWriter(32);
+ uIntBitWriter.append(2, 0);
+ uIntBitWriter.append(4, bandAlarm.index);
+ uIntBitWriter.append(2, bandAlarm.state.value);
+ uIntBitWriter.append(4, bandAlarm.interval);
+ uIntBitWriter.append(6, bandAlarm.hour);
+ uIntBitWriter.append(6, bandAlarm.minute);
+ uIntBitWriter.append(1, 0);
+ uIntBitWriter.append(7, bandAlarm.repeat.toInt());
+ byteArrayWriter.appendUint32(uIntBitWriter.getValue());
+ }
+ }
+ return byteArrayWriter.getByteArray();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/CommandCode.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/CommandCode.java
new file mode 100644
index 000000000..fdd5979c3
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/CommandCode.java
@@ -0,0 +1,15 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.control;
+
+public enum CommandCode {
+ FLUSH_ACTIVITY(7),
+ HEARTRATE_REALTIME(11),
+ STAMINA_MODE(17),
+ MANUAL_ALARM(19),
+ LOW_VIBRATION(25);
+
+ public final int value;
+
+ CommandCode(int value) {
+ this.value = value;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/ControlPoint.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/ControlPoint.java
new file mode 100644
index 000000000..038a3dace
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/ControlPoint.java
@@ -0,0 +1,17 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.control;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayWriter;
+
+public abstract class ControlPoint {
+ protected final CommandCode code;
+
+ public ControlPoint(CommandCode code) {
+ this.code = code;
+ }
+
+ protected final ByteArrayWriter getValueWriter() {
+ final ByteArrayWriter byteArrayWriter = new ByteArrayWriter();
+ byteArrayWriter.appendUint8(this.code.value);
+ return byteArrayWriter;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/ControlPointLowVibration.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/ControlPointLowVibration.java
new file mode 100644
index 000000000..7addaf1ed
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/ControlPointLowVibration.java
@@ -0,0 +1,28 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.control;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.UIntBitWriter;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayWriter;
+
+public final class ControlPointLowVibration extends ControlPoint {
+ public boolean smartWakeUp;
+ public boolean incomingCall;
+ public boolean notification;
+
+ public ControlPointLowVibration(boolean isEnabled){
+ super(CommandCode.LOW_VIBRATION);
+ this.smartWakeUp = isEnabled;
+ this.incomingCall = isEnabled;
+ this.notification = isEnabled;
+ }
+
+ public final byte[] toByteArray() {
+ final UIntBitWriter uIntBitWriter = new UIntBitWriter(16);
+ uIntBitWriter.append(13, 0);
+ uIntBitWriter.appendBoolean(this.smartWakeUp);
+ uIntBitWriter.appendBoolean(this.incomingCall);
+ uIntBitWriter.appendBoolean(this.notification);
+ final ByteArrayWriter byteArrayWriter = this.getValueWriter();
+ byteArrayWriter.appendUint16((int) uIntBitWriter.getValue());
+ return byteArrayWriter.getByteArray();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/ControlPointWithValue.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/ControlPointWithValue.java
new file mode 100644
index 000000000..ed4536924
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/control/ControlPointWithValue.java
@@ -0,0 +1,21 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.control;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayWriter;
+
+public class ControlPointWithValue extends ControlPoint {
+ protected final int value;
+
+ public ControlPointWithValue(final CommandCode commandCode, final int value) {
+ super(commandCode);
+ if (value < 0 || value > 65535) {
+ throw new IllegalArgumentException("command value out of range " + value);
+ }
+ this.value = value;
+ }
+
+ public final byte[] toByteArray() {
+ final ByteArrayWriter byteArrayWriter = this.getValueWriter();
+ byteArrayWriter.appendUint16(this.value);
+ return byteArrayWriter.getByteArray();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/time/BandDaylightSavingTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/time/BandDaylightSavingTime.java
new file mode 100644
index 000000000..39ef3605f
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/time/BandDaylightSavingTime.java
@@ -0,0 +1,24 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.time;
+
+public enum BandDaylightSavingTime {
+ STANDARD_TIME(0, 0),
+ HALF_AN_HOUR_DST(2, 30),
+ DST(4, 60),
+ DOUBLE_DST( 8, 120);
+
+ final int key;
+ private final long saving;
+
+ BandDaylightSavingTime(int key, int min) {
+ this.key = key;
+ this.saving = 60000L * min;
+ }
+
+ public static BandDaylightSavingTime fromOffset(final int dstSaving) {
+ for (BandDaylightSavingTime dst: values()){
+ if (dst.saving == dstSaving)
+ return dst;
+ }
+ throw new RuntimeException("wrong dst saving: " + dstSaving);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/time/BandTime.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/time/BandTime.java
new file mode 100644
index 000000000..315b48726
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/time/BandTime.java
@@ -0,0 +1,62 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.time;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.IntFormat;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util.ByteArrayWriter;
+
+public class BandTime {
+ private final int year;
+ private final int month;
+ private final int dayOfMonth;
+ private final int hour;
+ private final int min;
+ private final int sec;
+ private final int dayOfWeek;
+ private final BandTimeZone timeZone;
+ private final BandDaylightSavingTime dst;
+
+ public BandTime(Calendar calendar) {
+ int dayOfWeek = 7;
+ if (calendar == null) {
+ throw new IllegalArgumentException("Calendar cant be null");
+ }
+ this.year = calendar.get(1);
+ if (this.year > 2099 || this.year < 2013) {
+ throw new RuntimeException("out of 2013-2099");
+ }
+ this.month = calendar.get(2) + 1;
+ this.dayOfMonth = calendar.get(5);
+ int value = calendar.get(7);
+ if (value != 1) {
+ dayOfWeek = value - 1;
+ }
+ this.dayOfWeek = dayOfWeek;
+ this.hour = calendar.get(11);
+ this.min = calendar.get(12);
+ this.sec = calendar.get(13);
+ TimeZone timeZone = calendar.getTimeZone();
+ this.timeZone = BandTimeZone.fromOffset(timeZone.getRawOffset());
+ if (timeZone.inDaylightTime(calendar.getTime())) {
+ this.dst = BandDaylightSavingTime.fromOffset(timeZone.getDSTSavings());
+ return;
+ }
+ this.dst = BandDaylightSavingTime.STANDARD_TIME;
+ }
+
+ public byte[] toByteArray() {
+ ByteArrayWriter byteArrayWriter = new ByteArrayWriter();
+ byteArrayWriter.appendUint16(this.year);
+ byteArrayWriter.appendUint8(this.month);
+ byteArrayWriter.appendUint8(this.dayOfMonth);
+ byteArrayWriter.appendUint8(this.hour);
+ byteArrayWriter.appendUint8(this.min);
+ byteArrayWriter.appendUint8(this.sec);
+ byteArrayWriter.appendUint8(this.dayOfWeek);
+ byteArrayWriter.appendValue(this.timeZone.key, IntFormat.SINT8);
+ byteArrayWriter.appendUint8(this.dst.key);
+ return byteArrayWriter.getByteArray();
+ }
+}
+
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/time/BandTimeZone.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/time/BandTimeZone.java
new file mode 100644
index 000000000..1040f5af4
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/entities/time/BandTimeZone.java
@@ -0,0 +1,61 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.entities.time;
+
+public enum BandTimeZone {
+ UTC_PLUS_06_30(26, 6, 30),
+ UTC_PLUS_07_00(28, 7, 0),
+ UTC_PLUS_08_00(32, 8, 0),
+ UTC_PLUS_08_45(35, 8, 45),
+ UTC_PLUS_09_00(36, 9, 0),
+ UTC_PLUS_09_30(38, 9, 30),
+ UTC_PLUS_10_00(40, 10, 0),
+ UTC_PLUS_10_30(42, 10, 30),
+ UTC_PLUS_11_00(44, 11, 0),
+ UTC_PLUS_11_30(46, 11, 30),
+ UTC_PLUS_12_00(48, 12, 0),
+ UTC_PLUS_12_45(51, 12, 45),
+ UTC_PLUS_13_00( 52, 13, 0),
+ UTC_PLUS_14_00(56, 14, 0),
+ UTC_MINUS_12_00(-48, -12, 0),
+ UTC_MINUS_11_00(-44, -11, 0),
+ UTC_MINUS_10_00(-40, -10, 0),
+ UTC_MINUS_09_30(-38, -9, -30),
+ UTC_MINUS_09_00(-36, -9, 0),
+ UTC_MINUS_08_00(-32, -8, 0),
+ UTC_MINUS_07_00(-28, -7, 0),
+ UTC_MINUS_06_00(-24, -6, 0),
+ UTC_MINUS_05_00(-20, -5, 0),
+ UTC_MINUS_04_30(-18, -4, -30),
+ UTC_MINUS_04_00(-16, -4, 0),
+ UTC_MINUS_03_30(-14, -3, -30),
+ UTC_MINUS_03_00(-12, -3, 0),
+ UTC_MINUS_02_00(-8, -2, 0),
+ UTC_MINUS_01_00(-4, -1, 0),
+ UTC_PLUS_00_00(0, 0, 0),
+ UTC_PLUS_01_00(4, 1, 0),
+ UTC_PLUS_02_00(8, 2, 0),
+ UTC_PLUS_03_00(12, 3, 0),
+ UTC_PLUS_03_30(14, 3, 30),
+ UTC_PLUS_04_00(16, 4, 0),
+ UTC_PLUS_04_30(18, 4, 30),
+ UTC_PLUS_05_00(20, 5, 0),
+ UTC_PLUS_05_30(22, 5, 30),
+ UTC_PLUS_05_45(23, 5, 45),
+ UTC_PLUS_06_00(24, 6, 0);
+
+ final int key;
+ private final long rawOffset;
+
+ BandTimeZone(int key, int hourOffset, int minOffset) {
+ this.key = key;
+ this.rawOffset = 3600000L * hourOffset + 60000L * minOffset;
+ }
+
+ public static BandTimeZone fromOffset(long rawOffset) {
+ for (BandTimeZone zone : values()){
+ if (zone.rawOffset == rawOffset)
+ return zone;
+ }
+ throw new RuntimeException("wrong raw offset: " + rawOffset);
+ }
+}
+
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/ByteArrayReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/ByteArrayReader.java
new file mode 100644
index 000000000..edf1edb45
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/ByteArrayReader.java
@@ -0,0 +1,53 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util;
+
+public class ByteArrayReader {
+ public final byte[] byteArray;
+ public int bytesRead;
+
+ public ByteArrayReader(byte[] array) {
+ this.bytesRead = 0;
+ if (array == null || array.length <= 0) {
+ throw new IllegalArgumentException("wrong byte array");
+ }
+ this.byteArray = array.clone();
+ }
+
+ public int getBytesLeft() {
+ return this.byteArray.length - this.bytesRead;
+ }
+
+ public long readInt(IntFormat intFormat) {
+ if (intFormat == null) {
+ throw new IllegalArgumentException("wrong intFormat");
+ }
+ int i = 0;
+ long n = 0L;
+ try {
+ while (i < intFormat.bytesCount) {
+ long n2 = this.byteArray[this.bytesRead++] & 0xFF;
+ int n3 = i + 1;
+ n += n2 << i * 8;
+ i = n3;
+ }
+ long n4 = n;
+ if (intFormat.isSigned) {
+ int n5 = intFormat.bytesCount * 8;
+ n4 = n;
+ if (((long) (1 << n5 - 1) & n) != 0x0L) {
+ n4 = ((1 << n5 - 1) - (n & (long) ((1 << n5 - 1) - 1))) * -1L;
+ }
+ }
+ return n4;
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ throw new RuntimeException("reading outside of byte array", ex.getCause());
+ }
+ }
+
+ public int readUint16() {
+ return (int) this.readInt(IntFormat.UINT16);
+ }
+
+ public int readUint8() {
+ return (int) this.readInt(IntFormat.UINT8);
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/ByteArrayWriter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/ByteArrayWriter.java
new file mode 100644
index 000000000..762f41136
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/ByteArrayWriter.java
@@ -0,0 +1,61 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util;
+
+import java.util.Arrays;
+
+public class ByteArrayWriter {
+ public byte[] byteArray;
+ private int bytesWritten;
+
+ public ByteArrayWriter() {
+ this.bytesWritten = 0;
+ }
+
+ private void addIntToValue(long n, IntFormat intFormat) {
+ for (int i = 0; i < intFormat.bytesCount; ++i) {
+ this.byteArray[this.bytesWritten++] = (byte) (n >> i * 8 & 0xFFL);
+ }
+ }
+
+ public void appendUint16(int n) {
+ this.appendValue(n, IntFormat.UINT16);
+ }
+
+ public void appendUint32(long n) {
+ this.appendValue(n, IntFormat.UINT32);
+ }
+
+ public void appendUint8(int n) {
+ this.appendValue(n, IntFormat.UINT8);
+ }
+
+ public void appendValue(long lng, IntFormat intFormat) {
+ if (intFormat == null) {
+ throw new IllegalArgumentException("wrong int format");
+ }
+ if (lng > intFormat.max || lng < intFormat.min) {
+ throw new IllegalArgumentException("wrong value for intFormat. max: " + intFormat.max + " min: " + intFormat.min + " value: " + lng);
+ }
+ this.increaseByteArray(intFormat.bytesCount);
+ long n = lng;
+ if (intFormat.isSigned) {
+ int n2 = intFormat.bytesCount * 8;
+ n = lng;
+ if (lng < 0L) {
+ n = (1 << n2 - 1) + ((long) ((1 << n2 - 1) - 1) & lng);
+ }
+ }
+ this.addIntToValue(n, intFormat);
+ }
+
+ public void increaseByteArray(int n) {
+ if (this.byteArray == null) {
+ this.byteArray = new byte[n];
+ return;
+ }
+ this.byteArray = Arrays.copyOf(this.byteArray, this.byteArray.length + n);
+ }
+
+ public byte[] getByteArray() {
+ return this.byteArray.clone();
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/IntFormat.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/IntFormat.java
new file mode 100644
index 000000000..284238753
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/IntFormat.java
@@ -0,0 +1,35 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util;
+
+public enum IntFormat {
+ UINT8(1, false),
+ SINT8(1, true),
+ UINT16( 2, false),
+ SINT16( 2, true),
+ UINT32(4, false),
+ SINT32(4, true);
+
+ final int bytesCount;
+ final boolean isSigned;
+ final long max;
+ final long min;
+
+ IntFormat(int bytesCount, boolean isSigned) {
+ this.bytesCount = bytesCount;
+ this.isSigned = isSigned;
+ int bitsCount = bytesCount * 8;
+ long max;
+ if (isSigned) {
+ max = (long) Math.pow(2.0, bitsCount - 1) - 1L;
+ } else {
+ max = (long) (Math.pow(2.0, bitsCount) - 1.0);
+ }
+ this.max = max;
+ long min;
+ if (isSigned) {
+ min = (long) (-1.0 * Math.pow(2.0, bitsCount - 1));
+ } else {
+ min = 0L;
+ }
+ this.min = min;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/UIntBitReader.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/UIntBitReader.java
new file mode 100644
index 000000000..645d24599
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/UIntBitReader.java
@@ -0,0 +1,27 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util;
+
+public class UIntBitReader {
+ private final long value;
+ private int offset;
+
+ public UIntBitReader(long value, int offset) {
+ this.value = value;
+ this.offset = offset;
+ }
+
+ public int read(int offset) {
+ this.offset -= offset;
+ if (this.offset < 0) {
+ throw new IllegalArgumentException("Read out of range");
+ }
+ return (int) ((long) ((1 << offset) - 1) & this.value >>> this.offset);
+ }
+
+ public boolean readBoolean() {
+ boolean b = true;
+ if (this.read(1) == 0) {
+ b = false;
+ }
+ return b;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/UIntBitWriter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/UIntBitWriter.java
new file mode 100644
index 000000000..21ae241f4
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/sonyswr12/util/UIntBitWriter.java
@@ -0,0 +1,37 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.sonyswr12.util;
+
+public class UIntBitWriter {
+ private long value;
+ private long offset;
+
+ public UIntBitWriter(int offset) {
+ this.value = 0L;
+ this.offset = offset;
+ }
+
+ public void append(int offset, int value) {
+ if (value < 0 || value > (1 << offset) - 1) {
+ throw new IllegalArgumentException("value is out of range: " + value);
+ }
+ this.offset -= offset;
+ if (this.offset < 0L) {
+ throw new IllegalArgumentException("Write offset out of range");
+ }
+ this.value |= (long) value << (int) this.offset;
+ }
+
+ public void appendBoolean(boolean b) {
+ if (b) {
+ this.append(1, 1);
+ return;
+ }
+ this.append(1, 0);
+ }
+
+ public long getValue() {
+ if (this.offset != 0L) {
+ throw new IllegalStateException("value is not complete yet: " + this.offset);
+ }
+ return this.value;
+ }
+}
\ No newline at end of file
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 921702b23..a30d79823 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/DeviceHelper.java
@@ -82,6 +82,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.pinetime.PineTimeJFCoordinat
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.QHybridCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi1Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.roidmi.Roidmi3Coordinator;
+import nodomain.freeyourgadget.gadgetbridge.devices.sonyswr12.SonySWR12DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.tlw64.TLW64Coordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.vibratissimo.VibratissimoCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.watch9.Watch9DeviceCoordinator;
@@ -258,6 +259,7 @@ public class DeviceHelper {
result.add(new PineTimeJFCoordinator());
result.add(new SG2Coordinator());
result.add(new LefunDeviceCoordinator());
+ result.add(new SonySWR12DeviceCoordinator());
return result;
}
diff --git a/app/src/main/res/layout/activity_sonyswr12_settings.xml b/app/src/main/res/layout/activity_sonyswr12_settings.xml
new file mode 100644
index 000000000..2c1ab8994
--- /dev/null
+++ b/app/src/main/res/layout/activity_sonyswr12_settings.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index 151c838f7..757eeaea4 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -595,4 +595,9 @@
Изкл.
Вкл.
Няма данни
+ Настройки Sony SWR12
+ Активирана ниска вибрация
+ Режимът за пестене на енергия е включен
+ Интелигентен алармен интервал в минути
+ За да промените настройките, първо трябва да се свържете с устройството!
\ No newline at end of file
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index a1d51c956..b1e63f5a3 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -874,4 +874,9 @@
Repeteix la notificació de trucada
Notificacions i trucades
Calibració del Watch X Plus
+ Sony SWR12
+ Configuració de Sony SWR12
+ Vibració baixa activada
+ Interval d\'alarma intel·ligent en minuts
+ Per canviar la configuració, primer us heu de connectar al dispositiu.
\ No newline at end of file
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index cd11fa671..792bbc5c4 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -1047,4 +1047,10 @@
Kriket
Veslovací trenažér
Fotbal
+ Sony SWR12
+ Sony SWR12 nastavení
+ Nízké vibrace povoleny
+ Režim úspory energie je zapnutý
+ Interval inteligentního alarmu v minutách
+ Chcete-li změnit nastavení, měli byste se nejprve připojit k zařízení!
\ No newline at end of file
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 65c43c032..2e0632114 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -1059,4 +1059,10 @@
Heute
Vergangenheit
Keine Aktivitäten gefunden.
+ Sony SWR12
+ Sony SWR12 Einstellungen
+ Geringe Vibration aktiviert
+ Der Energiesparmodus ist aktiviert
+ Intelligentes Alarmintervall in Minuten
+ Um Einstellungen zu ändern, sollten Sie zuerst eine Verbindung zum Gerät herstellen!
\ No newline at end of file
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 5227c67c3..c4e91c4ba 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -988,4 +988,10 @@
Ελλειπτικό μηχάνημα
Ποδηλασία εσωτερικού χώρου
Κολύμβηση (ανοιχτό νερό)
+ Sony SWR12
+ Ρυθμίσεις Sony SWR12
+ Ενεργοποιήθηκε χαμηλή δόνηση
+ Η λειτουργία εξοικονόμησης ενέργειας είναι ενεργοποιημένη
+ Έξυπνο διάστημα συναγερμού σε λίγα λεπτά
+ Για να αλλάξετε τις ρυθμίσεις πρέπει πρώτα να συνδεθείτε στη συσκευή!
\ No newline at end of file
diff --git a/app/src/main/res/values-en-rGB/strings.xml b/app/src/main/res/values-en-rGB/strings.xml
index fbd4a9a98..7b15cc575 100644
--- a/app/src/main/res/values-en-rGB/strings.xml
+++ b/app/src/main/res/values-en-rGB/strings.xml
@@ -896,4 +896,10 @@
Choose the shortcuts on the band screen
Shortcuts
Set Alias
+ Sony SWR12
+ Sony SWR12 settings
+ Low vibration enabled
+ Power saving mode on
+ Smart alarm interval in minutes
+ To change settings you should first connect to device!
\ No newline at end of file
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 49416171d..061fd5f73 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -851,4 +851,10 @@
Llamadas y Notificaciones
Calibracion Watch X Plus
Establecer Alias
+ Sony SWR12
+ Configuración de Sony SWR12
+ Baja vibración habilitada
+ El modo de ahorro de energía está activado
+ Intervalo de alarma inteligente en minutos
+ Para cambiar la configuración, primero debe conectarse al dispositivo.
\ No newline at end of file
diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml
index 0c49d70ed..c9347cca4 100644
--- a/app/src/main/res/values-et/strings.xml
+++ b/app/src/main/res/values-et/strings.xml
@@ -855,4 +855,10 @@
Nimi
Filter
Statistika
+ Sony SWR12
+ Sony SWR12 sätted
+ Madal vibratsioon on lubatud
+ Energiasäästurežiim on sisse lülitatud
+ Nutika häire intervall minutites
+ Seadete muutmiseks peaksite kõigepealt seadmega ühenduse looma!
\ No newline at end of file
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index 0da36995b..a47dcba1c 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -125,4 +125,5 @@
مکان تعیین شده برای هواشناسی (CM/LOS)
اضافه کردن برنامهها به لیست سیاه
nodomain.freeyourgadget.gadgetbridge.ButtonPressed
+ برای تغییر تنظیمات ابتدا باید به دستگاه متصل شوید!
\ No newline at end of file
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 1c8f36875..344ed368d 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -36,4 +36,9 @@
Määritä
Siirrä ylös
nodomain.freeyourgadget.gadgetbridge.ButtonPressed
+ Sony SWR12 asetukset
+ Matala tärinä käytössä
+ Virransäästötila on päällä
+ Älykäs hälytysväli minuutteina
+ Muuta asetuksia muodostamalla ensin yhteys laitteeseen!
\ No newline at end of file
diff --git a/app/src/main/res/values-fr-rCA/strings.xml b/app/src/main/res/values-fr-rCA/strings.xml
index ab07b88ba..049f76618 100644
--- a/app/src/main/res/values-fr-rCA/strings.xml
+++ b/app/src/main/res/values-fr-rCA/strings.xml
@@ -885,4 +885,10 @@
Notifications et appels
Calibrage de Watch X Plus
Système
+ Sony SWR12
+ Paramètres Sony SWR12
+ Faible vibration activée
+ Le mode d\'économie d\'énergie est activé
+ Intervalle d\'alarme intelligente en minutes
+ Pour modifier les paramètres, vous devez d\'abord vous connecter à l\'appareil!
\ No newline at end of file
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 1e2022a45..601893395 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -1057,4 +1057,10 @@ Temps de sommeil préféré en heures
Intervalle de rappel d\'hydratation (en minutes)
Le bracelet vibrera pour vous rappeler de boire de l\'eau
Rappel d\'hydratation
+ Sony SWR12
+ Paramètres Sony SWR12
+ Faible vibration activée
+ Le mode d\'économie d\'énergie est activé
+ Intervalle d\'alarme intelligente en minutes
+ Pour modifier les paramètres, vous devez d\'abord vous connecter à l\'appareil!
\ No newline at end of file
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index d5d321704..007d460e2 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -515,4 +515,9 @@
Axustes
Alipay
nodomain.freeyourgadget.gadgetbridge.ButtonPressed
+ Sony SWR12
+ Axustes Sony SWR12
+ Vibración baixa habilitada
+ Intervalo de alarma intelixente en minutos
+ Para cambiar a configuración, primeiro debes conectarte ao dispositivo.
\ No newline at end of file
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index 7e47f278e..cbc089e4c 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -1058,4 +1058,10 @@
הפרש תזכורת שתייה (בדקות)
הצמיד ירטוט כדי להזכיר לך לשתות מים
תזכורת שתייה
+ Sony SWR12
+ Sony SWR12 הגדרות
+ רטט נמוך מופעל
+ מצב חיסכון בחשמל פועל
+ מרווח אזעקה חכם בדקות
+ כדי לשנות הגדרות כדאי להתחבר תחילה למכשיר!
\ No newline at end of file
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index 7506e24bd..d83731f29 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -152,4 +152,6 @@
अपनी गतिविधि को युक्ति में रखें
सभी अलार्म बंद है
कनेक्ट नहीं.
+ मिनटों में स्मार्ट अलार्म अंतराल
+ सेटिंग्स बदलने के लिए आपको पहले डिवाइस से कनेक्ट करना चाहिए!
\ No newline at end of file
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 1220c86fd..a0bb62fe7 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -61,4 +61,7 @@
Sinkroniziraj
Doniraj
Postavke
+ Uključen je način uštede energije
+ Pametni interval alarma u minutama
+ Da biste promijenili postavke, prvo se povežite s uređajem!
\ No newline at end of file
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index c099464d0..e50fbbb41 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -495,4 +495,9 @@
Értesítések közötti legrövidebb idő
Rendszer
Feketelistás Naptárak
+ Beállítások Sony SWR12
+ Alacsony rezgés engedélyezve
+ Az energiatakarékos mód be van kapcsolva
+ Intelligens riasztási intervallum percekben
+ A beállítások módosításához először csatlakoznia kell az eszközhöz!
\ No newline at end of file
diff --git a/app/src/main/res/values-id/strings.xml b/app/src/main/res/values-id/strings.xml
index 424b97f2f..8576b444f 100644
--- a/app/src/main/res/values-id/strings.xml
+++ b/app/src/main/res/values-id/strings.xml
@@ -34,4 +34,7 @@
Manajer App
Melakukan factory reset akan menghapus seluruh data dari perangkat terkoneksi (jika didukung). Perangkat Xiaomi/Huami juga mengganti MAC address Bluetooth, sehingga akan muncul sebagai aplikasi baru di GadgetBridge.
Debug
+ Pengaturan Sony SWR12
+ Interval alarm pintar dalam beberapa menit
+ Untuk mengubah pengaturan, Anda harus menghubungkan ke perangkat terlebih dahulu!
\ No newline at end of file
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index 0c26bc3dd..f6a784e94 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -1041,4 +1041,10 @@
Nuoto (all\'aperto)
No
km
+ Sony SWR12
+ Impostazioni Sony SWR12
+ Bassa vibrazione abilitata
+ La modalità di risparmio energetico è attiva
+ Intervallo di allarme intelligente in pochi minuti
+ Per modificare le impostazioni devi prima connetterti al dispositivo!
\ No newline at end of file
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 9d8bc111f..c536cd4da 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -755,4 +755,10 @@
インドネシア語
切断通知
距離
+ Sony SWR12
+ Sony SWR12 設定
+ 低振動対応
+ 省電力モードがオンになっている
+ 分単位のスマートアラーム間隔
+ 設定を変更するには、最初にデバイスに接続する必要があります!
\ No newline at end of file
diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml
index 6d833162d..ebc6e7d9f 100644
--- a/app/src/main/res/values-ka/strings.xml
+++ b/app/src/main/res/values-ka/strings.xml
@@ -30,4 +30,7 @@
გააქტიურება
გამორტვა
კონფიგურაცია
+ Sony SWR12 პარამეტრები
+ სიგნალის ჭკვიანი ინტერვალი წუთებში
+ პარამეტრების შესაცვლელად ჯერ უნდა დაუკავშირდეთ მოწყობილობას!
\ No newline at end of file
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 099eedb83..4f98f62a7 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -609,4 +609,5 @@
올바르지 않은 주파수
87.5와 108.0 사이의 주파수를 입력하세요
nodomain.freeyourgadget.gadgetbridge.ButtonPressed
+ Sony SWR12
\ No newline at end of file
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index 1ba57eb79..fd2051d31 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -308,4 +308,10 @@
Snūstelti
Leisti didelį MTU
Padidinti perkėlimo greitį, bet gali ir neveikti kai kuriuose android įrenginiuose..
+ Sony SWR12
+ Sony SWR12 nustatymai
+ Įjungta maža vibracija
+ Įjungtas energijos taupymo režimas
+ Išmaniojo žadintuvo intervalas minutėmis
+ Norėdami pakeisti nustatymus, pirmiausia turite prisijungti prie įrenginio!
\ No newline at end of file
diff --git a/app/src/main/res/values-my/strings.xml b/app/src/main/res/values-my/strings.xml
index fa089bd6e..e106ebc8b 100644
--- a/app/src/main/res/values-my/strings.xml
+++ b/app/src/main/res/values-my/strings.xml
@@ -37,4 +37,6 @@
အခ်က္အလက္မ်ားကိုသိမ္းဆည္းမည္
မခ်ိတ္ဆက္ထားျခင္းမရွိပါ , သတိေပးခ်က္မထားရေသးပါ
nodomain.freeyourgadget.gadgetbridge.ButtonPressed
+ မိနစ်အတွင်းစမတ်နှိုးဆော်သံကြားကာလ
+ ချိန်ညှိချက်များကိုပြောင်းလဲရန်သင်ပထမ ဦး ဆုံးကိရိယာနှင့်ချိတ်ဆက်သင့်သည်!
\ No newline at end of file
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 097393a81..06f0bbb12 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -1016,4 +1016,10 @@
Drikkepåminnelseintervall (i minutter)
Grensesnittspråk
Brukt av LineageOS-værtilbyderen, andre Android-versjoner må bruke et program som «Ditt lokale vær». Mer info er å finne på Gadgetbridge-wiki-en.
+ Sony SWR12
+ Sony SWR12 innstillinger
+ Lav vibrasjon aktivert
+ Strømsparingsmodus er på
+ Smart alarmintervall på få minutter
+ For å endre innstillinger, bør du først koble til enheten!
\ No newline at end of file
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 1127072a1..8599ece1c 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -956,4 +956,10 @@
Elliptische Trainer
Binnenshuis fietsen
Zwemmen (Open water)
+ Sony SWR12
+ Sony SWR12 instellingen
+ Laag trillingsniveau ingeschakeld
+ Energiebesparende modus is ingeschakeld
+ Slimme alarminterval in minuten
+ Om instellingen te wijzigen, moet u eerst verbinding maken met het apparaat!
\ No newline at end of file
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 625208a31..f7f2d0fb1 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -1029,4 +1029,10 @@
Krykiet
Wiosłująca maszyna
Trener eliptyczny
+ Sony SWR12
+ Ustawienia Sony SWR12
+ Włączono niski poziom wibracji
+ Tryb oszczędzania energii jest włączony
+ Inteligentny interwał alarmu w minutach
+ Aby zmienić ustawienia, należy najpierw połączyć się z urządzeniem!
\ No newline at end of file
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index eedb5f51e..99fcc675a 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -1056,4 +1056,10 @@
hoje
passado distante
Links
+ Sony SWR12
+ Configurações de Sony SWR12
+ Baixa vibração habilitada
+ O modo de economia de energia está ativado
+ Intervalo de alarme inteligente em minutos
+ Para alterar as configurações, você deve primeiro se conectar ao dispositivo!
\ No newline at end of file
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 1e7a82d73..42cbd1ae8 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -1046,4 +1046,10 @@
Ritmo médio de voltas
Média de braçadas
Distância média das braçadas
+ Sony SWR12
+ Configurações de Sony SWR12
+ Baixa vibração habilitada
+ O modo de economia de energia está ativado
+ Intervalo de alarme inteligente em minutos
+ Para alterar as configurações, você deve primeiro se conectar ao dispositivo!
\ No newline at end of file
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 2edb5b68d..edeba4e2c 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -129,4 +129,8 @@
Salvare Configurație
Deconectat(ă), alarma nu este setată.
nodomain.freeyourgadget.gadgetbridge.ButtonPressed
+ Setari Sony SWR12
+ Modul de economisire a energiei este activat
+ Interval de alarmă inteligentă în minute
+ Pentru a modifica setările, ar trebui să vă conectați mai întâi la dispozitiv!
\ No newline at end of file
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 5014ccd76..1637bb9f9 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -1064,4 +1064,10 @@
Lefun
Язык интерфейса
Защита от потери
+ Sony SWR12
+ Настройки Sony SWR12
+ Слабая вибрация
+ Режима энергосбережения включен
+ Интервал умного будильника в минутах
+ Для изменения настроек необходимо сначала подключиться к устройству!
\ No newline at end of file
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index c052e3fdf..aee8c2d55 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -491,4 +491,10 @@
Pripojiť
Zap.
nodomain.freeyourgadget.gadgetbridge.ButtonPressed
+ Sony SWR12
+ Nastavenia Sony SWR12
+ Nízke vibrácie sú povolené
+ Režim úspory energie je zapnutý
+ Interval inteligentného alarmu v minútach
+ Ak chcete zmeniť nastavenie, mali by ste sa najskôr pripojiť k zariadeniu!
\ No newline at end of file
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 61daafb9c..0df2fd4ba 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -48,4 +48,9 @@
Aktivera pulsmätare
Avaktivera pulsmätare
Konfigurera
+ Inställningar Sony SWR12
+ Låg vibration aktiverad
+ Energisparläge är på
+ Smart larmintervall på några minuter
+ För att ändra inställningar bör du först ansluta till enheten!
\ No newline at end of file
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index 83f1c5db1..fcab38fe3 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -296,7 +296,7 @@
\n
\nLütfen önce .fw dosyasını, sonra .res dosyasını, ve son olarak .gps dosyasını kurun. .fw dosyasını kurduktan sonra bilekliğiniz yeniden başlatılacak.
\n
-\nNote: Bu dosyalar önceden kurulanlarla tamamen aynıysa .res ve .gps dosyalarını kurmanız gerekmez.
+\nNote: Bu dosyalar önceden kurulanlarla tamamen aynıysa .res ve .gps dosyalarını kurmanız gerekmez.
\n
\nİLERLEMENİZ DURUMUNDA RİSK SİZE AİTTİR!
Mi Band 4 aygıtınıza %s ürün yazılımını kurmak üzeresiniz.
@@ -950,7 +950,7 @@
\n
\nLütfen önce .fw dosyasını, sonra .res dosyasın kurun. .fw dosyasını kurduktan sonra bilekliğiniz yeniden başlatılacak.
\n
-\nNote: Bu dosyalar önceden kurulanlarla tamamen aynıysa .res dosyasını kurmanız gerekmez.
+\nNote: Bu dosyalar önceden kurulanlarla tamamen aynıysa .res dosyasını kurmanız gerekmez.
\n
\nİLERLEMENİZ DURUMUNDA RİSK SİZE AİTTİR!
TLW64
@@ -1088,4 +1088,9 @@
Sıvı alma hatırlatma aralığı (dakika olarak)
Bileklik su içmenizi hatırlatmak için titreyecek
Sıvı alma hatırlatıcı
+ Sony SWR12
+ Sony SWR12 Ayarları
+ Düşük titreşim etkin
+ Dakikalar içinde akıllı alarm aralığı
+ Ayarları değiştirmek için önce cihaza bağlanmalısınız!
\ No newline at end of file
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 8ebf505b5..42b697c2a 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -1052,4 +1052,10 @@
\nПримітка: вам не потрібно встановлювати .res файл, якщо він такий самий був встановлений раніше.
\n
\nДІЄТЕ НА ВЛАСНИЙ РИЗИК!
+ Sony SWR12
+ Sony SWR12 налаштування
+ Cлабка вібрація
+ Режим енергозбереження ввімкнено
+ Інтелектуальний інтервал тривоги в хвилинах
+ Щоб змінити налаштування, спочатку слід підключитися до пристрою!
\ No newline at end of file
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index a5c98d6f0..72fe6b58b 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -190,4 +190,8 @@
Giờ
Giờ và ngày
nodomain.freeyourgadget.gadgetbridge.ButtonPressed
+ Cài đặt Sony SWR12
+ Chế độ tiết kiệm pin đang bật
+ Khoảng thời gian báo thức thông minh trong vài phút
+ Để thay đổi cài đặt, trước tiên bạn nên kết nối với thiết bị!
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index ab178e865..ae2662fd1 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -1069,4 +1069,10 @@
水合提醒间隔(分钟)
当需要提醒喝水时,手环会震动
水合提醒
+ Sony SWR12
+ Sony SWR12 设置
+ 低振动启用
+ 省电模式已开启
+ 智能警报间隔(以分钟为单位)
+ 要更改设置,您应该首先连接设备!
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 1bba745d1..49edd533e 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -585,4 +585,6 @@
游泳
這是一個來自 Gadgetbridge 的測試通知
測試通知
+ Sony SWR12
+ Sony SWR12 設定
\ No newline at end of file
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index fb85f5c2e..1afcadb36 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -986,4 +986,13 @@
- 0
- 1
+
+ - 0
+ - 10
+ - 20
+ - 30
+ - 40
+ - 50
+ - 60
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 24cd7e73c..56881c8f8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -785,6 +785,7 @@
Bangle.js
TLW64
PineTime (JF Firmware)
+ Sony SWR12
Choose export location
Gadgetbridge notifications
Gadgetbridge notifications high priority
@@ -934,6 +935,11 @@
Ignore bonded devices
Enabling this option will ignore devices that have been bonded/paired already when scanning
Location must be turned on to scan for devices
+ Sony SWR12 Settings
+ Low vibration enabled
+ Power saving mode on
+ Smart alarm interval in minutes
+ To change settings you should first connect to device!
Distance
Uphill
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 7573f8eec..dfbe19375 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -581,10 +581,14 @@
-
+ android:title="@string/zetime_title_settings" />
+