mirror of
https://codeberg.org/Freeyourgadget/Gadgetbridge
synced 2024-11-25 03:16:51 +01:00
Merge branch 'fossil-q-hybrid' of github.com:dakhnod/Gadgetbridge into q-hybrid-hr
This commit is contained in:
commit
d344bfa6da
@ -16,13 +16,17 @@ import org.slf4j.LoggerFactory;
|
||||
import java.util.Objects;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.makibeshr3.MakibesHR3Constants;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.XTimePreference;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.XTimePreferenceFragment;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_DATEFORMAT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SCREEN_ORIENTATION;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_TIMEFORMAT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_WEARLOCATION;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_ACTIVATE_DISPLAY_ON_LIFT;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISCONNECT_NOTIFICATION;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DISCONNECT_NOTIFICATION_END;
|
||||
@ -370,25 +374,12 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
|
||||
});
|
||||
}
|
||||
|
||||
EditTextPreference mibandTimeOffset = findPreference(MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS);
|
||||
if (mibandTimeOffset != null) {
|
||||
mibandTimeOffset.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() {
|
||||
@Override
|
||||
public void onBindEditText(@NonNull EditText editText) {
|
||||
editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
|
||||
}
|
||||
});
|
||||
}
|
||||
setInputTypeFor(HuamiConst.PREF_BUTTON_ACTION_BROADCAST_DELAY, InputType.TYPE_CLASS_NUMBER);
|
||||
setInputTypeFor(HuamiConst.PREF_BUTTON_ACTION_PRESS_MAX_INTERVAL, InputType.TYPE_CLASS_NUMBER);
|
||||
setInputTypeFor(HuamiConst.PREF_BUTTON_ACTION_PRESS_COUNT, InputType.TYPE_CLASS_NUMBER);
|
||||
setInputTypeFor(MiBandConst.PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS, InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED);
|
||||
setInputTypeFor(MakibesHR3Constants.PREF_FIND_PHONE_DURATION, InputType.TYPE_CLASS_NUMBER);
|
||||
|
||||
EditTextPreference findPhoneDuration = findPreference(MakibesHR3Constants.PREF_FIND_PHONE_DURATION);
|
||||
if (findPhoneDuration != null) {
|
||||
findPhoneDuration.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() {
|
||||
@Override
|
||||
public void onBindEditText(@NonNull EditText editText) {
|
||||
editText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static DeviceSpecificSettingsFragment newInstance(String settingsFileSuffix, @NonNull int[] supportedSettings) {
|
||||
@ -431,4 +422,16 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void setInputTypeFor(final String preferenceKey, final int editTypeFlags) {
|
||||
EditTextPreference textPreference = findPreference(preferenceKey);
|
||||
if (textPreference != null) {
|
||||
textPreference.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() {
|
||||
@Override
|
||||
public void onBindEditText(@NonNull EditText editText) {
|
||||
editText.setInputType(editTypeFlags);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,13 @@ public class HuamiConst {
|
||||
public static final String PREF_EXPOSE_HR_THIRDPARTY = "expose_hr_thirdparty";
|
||||
public static final String PREF_USE_CUSTOM_FONT = "use_custom_font";
|
||||
|
||||
public static final String PREF_BUTTON_ACTION_ENABLE = "button_action_enable";
|
||||
public static final String PREF_BUTTON_ACTION_VIBRATE = "button_action_vibrate";
|
||||
public static final String PREF_BUTTON_ACTION_PRESS_COUNT = "button_action_press_count";
|
||||
public static final String PREF_BUTTON_ACTION_PRESS_MAX_INTERVAL = "button_action_press_max_interval";
|
||||
public static final String PREF_BUTTON_ACTION_BROADCAST_DELAY = "button_action_broadcast_delay";
|
||||
public static final String PREF_BUTTON_ACTION_BROADCAST = "button_action_broadcast";
|
||||
|
||||
public static int toActivityKind(int rawType) {
|
||||
switch (rawType) {
|
||||
case TYPE_DEEP_SLEEP:
|
||||
|
@ -86,6 +86,7 @@ public class AmazfitBipCoordinator extends HuamiCoordinator {
|
||||
R.xml.devicesettings_liftwrist_display,
|
||||
R.xml.devicesettings_disconnectnotification,
|
||||
R.xml.devicesettings_expose_hr_thirdparty,
|
||||
R.xml.devicesettings_buttonactions,
|
||||
R.xml.devicesettings_pairingkey
|
||||
};
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ public class MiBand2Coordinator extends HuamiCoordinator {
|
||||
R.xml.devicesettings_liftwrist_display,
|
||||
R.xml.devicesettings_rotatewrist_cycleinfo,
|
||||
R.xml.devicesettings_expose_hr_thirdparty,
|
||||
R.xml.devicesettings_buttonactions,
|
||||
R.xml.devicesettings_pairingkey
|
||||
};
|
||||
}
|
||||
|
@ -27,12 +27,6 @@ public final class MiBandConst {
|
||||
public static final String PREF_MIBAND_ALARMS = "mi_alarms";
|
||||
public static final String PREF_MIBAND_DONT_ACK_TRANSFER = "mi_dont_ack_transfer";
|
||||
public static final String PREF_MIBAND_RESERVE_ALARM_FOR_CALENDAR = "mi_reserve_alarm_calendar";
|
||||
public static final String PREF_MIBAND_BUTTON_ACTION_ENABLE = "mi2_enable_button_action";
|
||||
public static final String PREF_MIBAND_BUTTON_ACTION_VIBRATE = "mi2_button_action_vibrate";
|
||||
public static final String PREF_MIBAND_BUTTON_PRESS_COUNT = "mi_button_press_count";
|
||||
public static final String PREF_MIBAND_BUTTON_PRESS_MAX_DELAY = "mi_button_press_count_max_delay";
|
||||
public static final String PREF_MIBAND_BUTTON_ACTION_DELAY = "mi_button_press_count_match_delay";
|
||||
public static final String PREF_MIBAND_BUTTON_PRESS_BROADCAST = "mi_button_press_broadcast";
|
||||
public static final String PREF_MIBAND_USE_HR_FOR_SLEEP_DETECTION = "mi_hr_sleep_detection";
|
||||
public static final String PREF_MIBAND_DEVICE_TIME_OFFSET_HOURS = "device_time_offset_hours";
|
||||
public static final String PREF_MI2_DATEFORMAT = "mi2_dateformat";
|
||||
|
@ -46,6 +46,10 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
@ -54,6 +58,9 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.buttonconfig.ConfigPayload;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
public class ConfigActivity extends AbstractGBActivity {
|
||||
@ -67,7 +74,7 @@ public class ConfigActivity extends AbstractGBActivity {
|
||||
|
||||
SharedPreferences prefs;
|
||||
|
||||
TextView timeOffsetView;
|
||||
TextView timeOffsetView, timezoneOffsetView;
|
||||
|
||||
GBDevice device;
|
||||
|
||||
@ -129,6 +136,51 @@ public class ConfigActivity extends AbstractGBActivity {
|
||||
});
|
||||
updateTimeOffset();
|
||||
|
||||
|
||||
timezoneOffsetView = findViewById(R.id.timezoneOffset);
|
||||
timezoneOffsetView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int timeOffset = prefs.getInt("QHYBRID_TIMEZONE_OFFSET", 0);
|
||||
LinearLayout layout2 = new LinearLayout(ConfigActivity.this);
|
||||
layout2.setOrientation(LinearLayout.HORIZONTAL);
|
||||
|
||||
final NumberPicker hourPicker = new NumberPicker(ConfigActivity.this);
|
||||
hourPicker.setMinValue(0);
|
||||
hourPicker.setMaxValue(23);
|
||||
hourPicker.setValue(timeOffset / 60);
|
||||
|
||||
final NumberPicker minPicker = new NumberPicker(ConfigActivity.this);
|
||||
minPicker.setMinValue(0);
|
||||
minPicker.setMaxValue(59);
|
||||
minPicker.setValue(timeOffset % 60);
|
||||
|
||||
layout2.addView(hourPicker);
|
||||
TextView tw = new TextView(ConfigActivity.this);
|
||||
tw.setText(":");
|
||||
layout2.addView(tw);
|
||||
layout2.addView(minPicker);
|
||||
|
||||
layout2.setGravity(Gravity.CENTER);
|
||||
|
||||
new AlertDialog.Builder(ConfigActivity.this)
|
||||
.setTitle("offset timezone by")
|
||||
.setView(layout2)
|
||||
.setPositiveButton("ok", new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
prefs.edit().putInt("QHYBRID_TIMEZONE_OFFSET", hourPicker.getValue() * 60 + minPicker.getValue()).apply();
|
||||
updateTimezoneOffset();
|
||||
LocalBroadcastManager.getInstance(ConfigActivity.this).sendBroadcast(new Intent(QHybridSupport.QHYBRID_COMMAND_UPDATE_TIMEZONE));
|
||||
Toast.makeText(ConfigActivity.this, "change might take some seconds...", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("cancel", null)
|
||||
.show();
|
||||
}
|
||||
});
|
||||
updateTimezoneOffset();
|
||||
|
||||
setTitle(R.string.preferences_qhybrid_settings);
|
||||
|
||||
ListView appList = findViewById(R.id.qhybrid_appList);
|
||||
@ -256,6 +308,17 @@ public class ConfigActivity extends AbstractGBActivity {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private void updateTimezoneOffset() {
|
||||
int timeOffset = prefs.getInt("QHYBRID_TIMEZONE_OFFSET", 0);
|
||||
DecimalFormat format = new DecimalFormat("00");
|
||||
timezoneOffsetView.setText(
|
||||
format.format(timeOffset / 60) + ":" +
|
||||
format.format(timeOffset % 60)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private void setSettingsEnabled(boolean enables) {
|
||||
findViewById(R.id.settingsLayout).setAlpha(enables ? 1f : 0.2f);
|
||||
}
|
||||
@ -305,7 +368,7 @@ public class ConfigActivity extends AbstractGBActivity {
|
||||
activityHandCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean checked) {
|
||||
if(!device.getDeviceInfo(QHybridSupport.ITEM_STEP_GOAL).getDetails().equals("1000000")){
|
||||
if (!device.getDeviceInfo(QHybridSupport.ITEM_STEP_GOAL).getDetails().equals("1000000")) {
|
||||
new AlertDialog.Builder(ConfigActivity.this)
|
||||
.setMessage("Please set the step count to a million to activate that.")
|
||||
.setPositiveButton("ok", null)
|
||||
@ -326,10 +389,68 @@ public class ConfigActivity extends AbstractGBActivity {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
GB.toast("nah.", Toast.LENGTH_SHORT, GB.INFO);
|
||||
((CheckBox)v).setChecked(false);
|
||||
((CheckBox) v).setChecked(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
final String buttonJson = device.getDeviceInfo(FossilWatchAdapter.ITEM_BUTTONS).getDetails();
|
||||
if (buttonJson != null && !buttonJson.isEmpty()) {
|
||||
try {
|
||||
final JSONArray buttonConfig = new JSONArray(buttonJson);
|
||||
LinearLayout buttonLayout = findViewById(R.id.buttonConfigLayout);
|
||||
buttonLayout.removeAllViews();
|
||||
findViewById(R.id.buttonOverwriteButtons).setVisibility(View.GONE);
|
||||
final ConfigPayload[] payloads = ConfigPayload.values();
|
||||
final String[] names = new String[payloads.length];
|
||||
for (int i = 0; i < payloads.length; i++)
|
||||
names[i] = payloads[i].getDescription();
|
||||
for (int i = 0; i < buttonConfig.length(); i++) {
|
||||
final int currentIndex = i;
|
||||
String configName = buttonConfig.getString(i);
|
||||
TextView buttonTextView = new TextView(ConfigActivity.this);
|
||||
buttonTextView.setTextColor(Color.WHITE);
|
||||
buttonTextView.setTextSize(20);
|
||||
try {
|
||||
ConfigPayload payload = ConfigPayload.valueOf(configName);
|
||||
buttonTextView.setText("Button " + (i + 1) + ": " + payload.getDescription());
|
||||
} catch (IllegalArgumentException e) {
|
||||
buttonTextView.setText("Button " + (i + 1) + ": Unknown");
|
||||
}
|
||||
|
||||
buttonTextView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
AlertDialog dialog = new AlertDialog.Builder(ConfigActivity.this)
|
||||
.setItems(names, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.cancel();
|
||||
ConfigPayload selected = payloads[which];
|
||||
|
||||
try {
|
||||
buttonConfig.put(currentIndex, selected.toString());
|
||||
device.addDeviceInfo(new GenericItem(FossilWatchAdapter.ITEM_BUTTONS, buttonConfig.toString()));
|
||||
updateSettings();
|
||||
LocalBroadcastManager.getInstance(ConfigActivity.this).sendBroadcast(new Intent(QHybridSupport.QHYBRID_COMMAND_OVERWRITE_BUTTONS));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
})
|
||||
.setTitle("Button " + (currentIndex + 1))
|
||||
.create();
|
||||
dialog.show();
|
||||
}
|
||||
});
|
||||
|
||||
buttonLayout.addView(buttonTextView);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
GB.toast("error parsing button config", Toast.LENGTH_LONG, GB.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -92,12 +92,16 @@ public class QHybridCoordinator extends AbstractDeviceCoordinator {
|
||||
}
|
||||
|
||||
public boolean supportsAlarmConfiguration() {
|
||||
GBDevice connectedDevice = GBApplication.app().getDeviceManager().getSelectedDevice();
|
||||
if(connectedDevice == null || connectedDevice.getType() != DeviceType.FOSSILQHYBRID){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlarmSlotCount() {
|
||||
return 0;
|
||||
return this.supportsAlarmConfiguration() ? 5 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1090,7 +1090,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
|
||||
return;
|
||||
}
|
||||
|
||||
String requiredButtonPressMessage = prefs.getString(MiBandConst.PREF_MIBAND_BUTTON_PRESS_BROADCAST,
|
||||
String requiredButtonPressMessage = prefs.getString(HuamiConst.PREF_BUTTON_ACTION_BROADCAST,
|
||||
this.getContext().getString(R.string.mi2_prefs_button_press_broadcast_default_value));
|
||||
|
||||
Intent in = new Intent();
|
||||
@ -1098,7 +1098,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
|
||||
in.putExtra("button_id", currentButtonActionId);
|
||||
LOG.info("Sending " + requiredButtonPressMessage + " with button_id " + currentButtonActionId);
|
||||
this.getContext().getApplicationContext().sendBroadcast(in);
|
||||
if (prefs.getBoolean(MiBandConst.PREF_MIBAND_BUTTON_ACTION_VIBRATE, false)) {
|
||||
if (prefs.getBoolean(HuamiConst.PREF_BUTTON_ACTION_VIBRATE, false)) {
|
||||
performPreferredNotification(null, null, null, HuamiService.ALERT_LEVEL_VIBRATE_ONLY, null);
|
||||
}
|
||||
|
||||
@ -1215,18 +1215,17 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
|
||||
}
|
||||
}
|
||||
|
||||
public void handleButtonEvent() {
|
||||
///logMessageContent(value);
|
||||
private void handleButtonEvent() {
|
||||
|
||||
// If disabled we return from function immediately
|
||||
Prefs prefs = GBApplication.getPrefs();
|
||||
if (!prefs.getBoolean(MiBandConst.PREF_MIBAND_BUTTON_ACTION_ENABLE, false)) {
|
||||
Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()));
|
||||
if (!prefs.getBoolean(HuamiConst.PREF_BUTTON_ACTION_ENABLE, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int buttonPressMaxDelay = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_PRESS_MAX_DELAY, 2000);
|
||||
int buttonActionDelay = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_ACTION_DELAY, 0);
|
||||
int requiredButtonPressCount = prefs.getInt(MiBandConst.PREF_MIBAND_BUTTON_PRESS_COUNT, 0);
|
||||
int buttonPressMaxDelay = prefs.getInt(HuamiConst.PREF_BUTTON_ACTION_PRESS_MAX_INTERVAL, 2000);
|
||||
int buttonActionDelay = prefs.getInt(HuamiConst.PREF_BUTTON_ACTION_BROADCAST_DELAY, 0);
|
||||
int requiredButtonPressCount = prefs.getInt(HuamiConst.PREF_BUTTON_ACTION_PRESS_COUNT, 0);
|
||||
|
||||
if (requiredButtonPressCount > 0) {
|
||||
long timeSinceLastPress = System.currentTimeMillis() - currentButtonPressTime;
|
||||
@ -1244,7 +1243,7 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport {
|
||||
currentButtonTimerActivationTime = currentButtonPressTime;
|
||||
if (buttonActionDelay > 0) {
|
||||
LOG.info("Activating timer");
|
||||
final Timer buttonActionTimer = new Timer("Mi Band Button Action Timer");
|
||||
final Timer buttonActionTimer = new Timer("Huami Button Action Timer");
|
||||
buttonActionTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -156,6 +156,12 @@ public class AmazfitBipFirmwareInfo extends HuamiFirmwareInfo {
|
||||
crcToVersion.put(61054, "8");
|
||||
crcToVersion.put(62291, "9 (old Latin)");
|
||||
crcToVersion.put(59577, "9 (Latin)");
|
||||
|
||||
// BipOS FW
|
||||
crcToVersion.put(28373, "1.1.5.02 (BipOS 0.5)");
|
||||
|
||||
// BipOS RES
|
||||
crcToVersion.put(16303, "1.1.5.02 (BipOS 0.5)");
|
||||
}
|
||||
|
||||
public AmazfitBipFirmwareInfo(byte[] bytes) {
|
||||
|
@ -79,11 +79,6 @@ public class AmazfitBipSupport extends HuamiSupport {
|
||||
onSetCallState(callSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleButtonEvent() {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AmazfitBipSupport setDisplayItems(TransactionBuilder builder) {
|
||||
if (gbDevice.getFirmwareVersion() == null) {
|
||||
|
@ -23,6 +23,7 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -32,11 +33,15 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
|
||||
import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.R;
|
||||
import nodomain.freeyourgadget.gadgetbridge.database.DBHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInfo;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationConfiguration;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.PackageConfigHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoMaster;
|
||||
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
|
||||
import nodomain.freeyourgadget.gadgetbridge.externalevents.NotificationListener;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
|
||||
@ -56,6 +61,7 @@ public class QHybridSupport extends QHybridBaseSupport {
|
||||
public static final String QHYBRID_COMMAND_SET = "qhybrid_command_set";
|
||||
public static final String QHYBRID_COMMAND_VIBRATE = "qhybrid_command_vibrate";
|
||||
public static final String QHYBRID_COMMAND_UPDATE = "qhybrid_command_update";
|
||||
public static final String QHYBRID_COMMAND_UPDATE_TIMEZONE = "qhybrid_command_update_timezone";
|
||||
public static final String QHYBRID_COMMAND_NOTIFICATION = "qhybrid_command_notification";
|
||||
public static final String QHYBRID_COMMAND_UPDATE_SETTINGS = "nodomain.freeyourgadget.gadgetbridge.Q_UPDATE_SETTINGS";
|
||||
public static final String QHYBRID_COMMAND_OVERWRITE_BUTTONS = "nodomain.freeyourgadget.gadgetbridge.Q_OVERWRITE_BUTTONS";
|
||||
@ -67,6 +73,7 @@ public class QHybridSupport extends QHybridBaseSupport {
|
||||
public static final String QHYBRID_COMMAND_NOTIFICATION_CONFIG_CHANGED = "nodomain.freeyourgadget.gadgetbridge.Q_NOTIFICATION_CONFIG_CHANGED";
|
||||
|
||||
public static final String QHYBRID_EVENT_BUTTON_PRESS = "nodomain.freeyourgadget.gadgetbridge.Q_BUTTON_PRESSED";
|
||||
public static final String QHYBRID_EVENT_MULTI_BUTTON_PRESS = "nodomain.freeyourgadget.gadgetbridge.Q_MULTI_BUTTON_PRESSED";
|
||||
|
||||
public static final String ITEM_STEP_GOAL = "STEP_GOAL";
|
||||
public static final String ITEM_STEP_COUNT = "STEP_COUNT";
|
||||
@ -76,6 +83,7 @@ public class QHybridSupport extends QHybridBaseSupport {
|
||||
public static final String ITEM_HAS_ACTIVITY_HAND = "HAS_ACTIVITY_HAND";
|
||||
public static final String ITEM_USE_ACTIVITY_HAND = "USE_ACTIVITY_HAND";
|
||||
public static final String ITEM_LAST_HEARTBEAT = "LAST_HEARTBEAT";
|
||||
public static final String ITEM_TIMEZONE_OFFSET = "STEPTIMEZONE_OFFSET_COUNT";
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(QHybridSupport.class);
|
||||
|
||||
@ -100,6 +108,7 @@ public class QHybridSupport extends QHybridBaseSupport {
|
||||
commandFilter.addAction(QHYBRID_COMMAND_SET);
|
||||
commandFilter.addAction(QHYBRID_COMMAND_VIBRATE);
|
||||
commandFilter.addAction(QHYBRID_COMMAND_UPDATE);
|
||||
commandFilter.addAction(QHYBRID_COMMAND_UPDATE_TIMEZONE);
|
||||
commandFilter.addAction(QHYBRID_COMMAND_NOTIFICATION);
|
||||
commandFilter.addAction(QHYBRID_COMMAND_UPDATE_SETTINGS);
|
||||
commandFilter.addAction(QHYBRID_COMMAND_OVERWRITE_BUTTONS);
|
||||
@ -143,6 +152,10 @@ public class QHybridSupport extends QHybridBaseSupport {
|
||||
onSetTime();
|
||||
break;
|
||||
}
|
||||
case QHYBRID_COMMAND_UPDATE_TIMEZONE:{
|
||||
loadTimezoneOffset();
|
||||
break;
|
||||
}
|
||||
case QHYBRID_COMMAND_UPDATE_SETTINGS: {
|
||||
String newSetting = intent.getStringExtra("EXTRA_SETTING");
|
||||
switch (newSetting) {
|
||||
@ -220,10 +233,26 @@ public class QHybridSupport extends QHybridBaseSupport {
|
||||
GBApplication.getContext().registerReceiver(globalCommandReceiver, globalFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
super.onSetAlarms(alarms);
|
||||
if(this.watchAdapter == null){
|
||||
GB.toast("watch not connected", Toast.LENGTH_LONG, GB.ERROR);
|
||||
return;
|
||||
}
|
||||
this.watchAdapter.onSetAlarms(alarms);
|
||||
}
|
||||
|
||||
private void loadTimeOffset() {
|
||||
timeOffset = getContext().getSharedPreferences(getContext().getPackageName(), Context.MODE_PRIVATE).getInt("QHYBRID_TIME_OFFSET", 0);
|
||||
}
|
||||
|
||||
private void loadTimezoneOffset(){
|
||||
short offset = (short) getContext().getSharedPreferences(getContext().getPackageName(), Context.MODE_PRIVATE).getInt("QHYBRID_TIMEZONE_OFFSET", 0);
|
||||
|
||||
this.watchAdapter.setTimezoneOffsetMinutes(offset);
|
||||
}
|
||||
|
||||
public long getTimeOffset(){
|
||||
return this.timeOffset;
|
||||
}
|
||||
|
@ -4,7 +4,10 @@ import android.bluetooth.BluetoothGatt;
|
||||
import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Context;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationConfiguration;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.misfit.PlayNotificationRequest;
|
||||
|
||||
@ -39,6 +42,7 @@ public abstract class WatchAdapter {
|
||||
public abstract void setVibrationStrength(short strength);
|
||||
public abstract void syncNotificationSettings();
|
||||
public abstract void onTestNewFunction();
|
||||
public abstract void setTimezoneOffsetMinutes(short offset);
|
||||
|
||||
public abstract boolean supportsFindDevice();
|
||||
public abstract boolean supportsExtendedVibration();
|
||||
@ -59,6 +63,8 @@ public abstract class WatchAdapter {
|
||||
|
||||
public abstract void onFetchActivityData();
|
||||
|
||||
public abstract void onSetAlarms(ArrayList<? extends Alarm> alarms);
|
||||
|
||||
public abstract boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic);
|
||||
public void onMtuChanged(BluetoothGatt gatt, int mtu, int status){};
|
||||
|
||||
|
@ -10,6 +10,9 @@ import android.widget.Toast;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
@ -20,14 +23,20 @@ import nodomain.freeyourgadget.gadgetbridge.GBException;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationConfiguration;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.PackageConfigHelper;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.WatchAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.buttonconfig.ConfigFileBuilder;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.buttonconfig.ConfigPayload;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.Request;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.RequestMtuRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.SetDeviceStateRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm.AlarmsGetRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm.AlarmsSetRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.button.ButtonConfigurationGetRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationGetRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest;
|
||||
@ -40,6 +49,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.mis
|
||||
import nodomain.freeyourgadget.gadgetbridge.util.GB;
|
||||
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.QHYBRID_EVENT_BUTTON_PRESS;
|
||||
import static nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport.QHYBRID_EVENT_MULTI_BUTTON_PRESS;
|
||||
|
||||
public class FossilWatchAdapter extends WatchAdapter {
|
||||
private ArrayList<Request> requestQueue = new ArrayList<>();
|
||||
@ -49,6 +59,7 @@ public class FossilWatchAdapter extends WatchAdapter {
|
||||
private int MTU = 23;
|
||||
|
||||
private String ITEM_MTU = "MTU";
|
||||
static public final String ITEM_BUTTONS = "BUTTONS";
|
||||
|
||||
private int lastButtonIndex = -1;
|
||||
|
||||
@ -69,6 +80,18 @@ public class FossilWatchAdapter extends WatchAdapter {
|
||||
|
||||
syncNotificationSettings();
|
||||
|
||||
queueWrite(new ButtonConfigurationGetRequest(this) {
|
||||
@Override
|
||||
public void onConfigurationsGet(ConfigPayload[] configs) {
|
||||
super.onConfigurationsGet(configs);
|
||||
|
||||
JSONArray buttons = new JSONArray();
|
||||
for (ConfigPayload payload : configs) buttons.put(String.valueOf(payload));
|
||||
String json = buttons.toString();
|
||||
getDeviceSupport().getDevice().addDeviceInfo(new GenericItem(ITEM_BUTTONS, json));
|
||||
}
|
||||
});
|
||||
|
||||
queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZED), false);
|
||||
}
|
||||
|
||||
@ -110,24 +133,33 @@ public class FossilWatchAdapter extends WatchAdapter {
|
||||
|
||||
@Override
|
||||
public void overwriteButtons() {
|
||||
FilePutRequest fileUploadRequets = new FilePutRequest((short) 0x0600, new byte[]{
|
||||
(byte) 0x01, (byte) 0x00, (byte) 0x00,
|
||||
(byte) 0x03,
|
||||
(byte) 0x10, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x00, (byte) 0x00,
|
||||
(byte) 0x20, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x00, (byte) 0x00,
|
||||
(byte) 0x30, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x00, (byte) 0x00,
|
||||
(byte) 0x01,
|
||||
(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x2E, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x0F, (byte) 0x00, (byte) 0x8B, (byte) 0x00, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x08, (byte) 0x01, (byte) 0x14, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0xFE, (byte) 0x08, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0xBF, (byte) 0xD5, (byte) 0x54, (byte) 0xD1,
|
||||
(byte) 0x00,
|
||||
(byte) 0x4F, (byte) 0x79, (byte) 0x97, (byte) 0x78,
|
||||
}, this){
|
||||
try {
|
||||
JSONArray buttonConfigJson = new JSONArray(getDeviceSupport().getDevice().getDeviceInfo(ITEM_BUTTONS).getDetails());
|
||||
|
||||
ConfigPayload[] payloads = new ConfigPayload[buttonConfigJson.length()];
|
||||
|
||||
for (int i = 0; i < buttonConfigJson.length(); i++) {
|
||||
try {
|
||||
payloads[i] = ConfigPayload.valueOf(buttonConfigJson.getString(i));
|
||||
} catch (IllegalArgumentException e) {
|
||||
payloads[i] = ConfigPayload.FORWARD_TO_PHONE;
|
||||
}
|
||||
}
|
||||
|
||||
ConfigFileBuilder builder = new ConfigFileBuilder(payloads);
|
||||
|
||||
FilePutRequest fileUploadRequets = new FilePutRequest((short) 0x0600, builder.build(true), this) {
|
||||
@Override
|
||||
public void onFilePut(boolean success) {
|
||||
if(success) GB.toast("successfully overwritten button settings", Toast.LENGTH_SHORT, GB.INFO);
|
||||
if (success)
|
||||
GB.toast("successfully overwritten button settings", Toast.LENGTH_SHORT, GB.INFO);
|
||||
else GB.toast("error overwriting button settings", Toast.LENGTH_SHORT, GB.INFO);
|
||||
}
|
||||
};
|
||||
queueWrite(fileUploadRequets);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -166,10 +198,11 @@ public class FossilWatchAdapter extends WatchAdapter {
|
||||
|
||||
@Override
|
||||
public void setStepGoal(int stepGoal) {
|
||||
queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.DailyStepGoalConfigItem(stepGoal), this){
|
||||
queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.DailyStepGoalConfigItem(stepGoal), this) {
|
||||
@Override
|
||||
public void onFilePut(boolean success) {
|
||||
if(success) GB.toast("successfully updated step goal", Toast.LENGTH_SHORT, GB.INFO);
|
||||
if (success)
|
||||
GB.toast("successfully updated step goal", Toast.LENGTH_SHORT, GB.INFO);
|
||||
else GB.toast("error updating step goal", Toast.LENGTH_SHORT, GB.INFO);
|
||||
}
|
||||
}, false);
|
||||
@ -181,11 +214,13 @@ public class FossilWatchAdapter extends WatchAdapter {
|
||||
|
||||
|
||||
queueWrite(
|
||||
new ConfigurationPutRequest(new ConfigurationPutRequest.ConfigItem[]{vibrationItem}, this){
|
||||
new ConfigurationPutRequest(new ConfigurationPutRequest.ConfigItem[]{vibrationItem}, this) {
|
||||
@Override
|
||||
public void onFilePut(boolean success) {
|
||||
if(success) GB.toast("successfully updated vibration strength", Toast.LENGTH_SHORT, GB.INFO);
|
||||
else GB.toast("error updating vibration strength", Toast.LENGTH_SHORT, GB.INFO);
|
||||
if (success)
|
||||
GB.toast("successfully updated vibration strength", Toast.LENGTH_SHORT, GB.INFO);
|
||||
else
|
||||
GB.toast("error updating vibration strength", Toast.LENGTH_SHORT, GB.INFO);
|
||||
}
|
||||
}, false
|
||||
);
|
||||
@ -222,7 +257,26 @@ public class FossilWatchAdapter extends WatchAdapter {
|
||||
|
||||
@Override
|
||||
public void onTestNewFunction() {
|
||||
queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.ConfigItem[0], this), false);
|
||||
queueWrite(new FilePutRequest(
|
||||
(short) 0x0600,
|
||||
new byte[]{
|
||||
(byte) 0x01, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x01, (byte) 0x24, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x30, (byte) 0x52, (byte) 0xFF, (byte) 0x26, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x09, (byte) 0x04, (byte) 0x01, (byte) 0x03, (byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0xA0, (byte) 0x00, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x09, (byte) 0x04, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x24, (byte) 0x00, (byte) 0x00, (byte) 0x24, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x50, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x1F, (byte) 0xBE, (byte) 0xB4, (byte) 0x1B
|
||||
},
|
||||
this)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimezoneOffsetMinutes(short offset) {
|
||||
queueWrite(new ConfigurationPutRequest(new ConfigurationPutRequest.TimezoneOffsetConfigItem(offset), this){
|
||||
@Override
|
||||
public void onFilePut(boolean success) {
|
||||
super.onFilePut(success);
|
||||
|
||||
if(success) GB.toast("successfully updated timezone", Toast.LENGTH_SHORT, GB.INFO);
|
||||
else GB.toast("error updating timezone", Toast.LENGTH_SHORT, GB.ERROR);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -267,6 +321,38 @@ public class FossilWatchAdapter extends WatchAdapter {
|
||||
// queueWrite(new ConfigurationGetRequest(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
// throw new RuntimeException("noope");
|
||||
ArrayList<nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm.Alarm> activeAlarms = new ArrayList<>();
|
||||
for (Alarm alarm : alarms){
|
||||
if(!alarm.getEnabled()) continue;
|
||||
if(alarm.getRepetition() == 0){
|
||||
activeAlarms.add(new nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm.Alarm(
|
||||
(byte) alarm.getMinute(),
|
||||
(byte) alarm.getHour(),
|
||||
false
|
||||
));
|
||||
continue;
|
||||
}
|
||||
int repitition = alarm.getRepetition();
|
||||
repitition = (repitition << 1) | ((repitition >> 6) & 1);
|
||||
activeAlarms.add(new nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm.Alarm(
|
||||
(byte) alarm.getMinute(),
|
||||
(byte) alarm.getHour(),
|
||||
(byte) repitition
|
||||
));
|
||||
}
|
||||
queueWrite(new AlarmsSetRequest(activeAlarms.toArray(new nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm.Alarm[0]), this){
|
||||
@Override
|
||||
public void onFilePut(boolean success) {
|
||||
super.onFilePut(success);
|
||||
if(success) GB.toast("successfully set alarms", Toast.LENGTH_SHORT, GB.INFO);
|
||||
else GB.toast("error setting alarms", Toast.LENGTH_SHORT, GB.INFO);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
|
||||
switch (characteristic.getUuid().toString()) {
|
||||
@ -335,6 +421,25 @@ public class FossilWatchAdapter extends WatchAdapter {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 5: {
|
||||
if (value.length != 4) {
|
||||
throw new RuntimeException("wrong button message");
|
||||
}
|
||||
int action = value[3];
|
||||
|
||||
String actionString = "SINGLE";
|
||||
if(action == 3) actionString = "DOUBLE";
|
||||
else if(action == 4) actionString = "LONG";
|
||||
|
||||
// lastButtonIndex = index;
|
||||
log(actionString + " button press");
|
||||
|
||||
Intent i = new Intent(QHYBRID_EVENT_MULTI_BUTTON_PRESS);
|
||||
i.putExtra("ACTION", actionString);
|
||||
getContext().sendBroadcast(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ import android.bluetooth.BluetoothGattCharacteristic;
|
||||
import android.content.Intent;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
@ -15,6 +16,7 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.NoSuchElementException;
|
||||
@ -25,6 +27,7 @@ import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEventBatteryInf
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
|
||||
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationConfiguration;
|
||||
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
|
||||
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
|
||||
@ -402,6 +405,11 @@ public class MisfitWatchAdapter extends WatchAdapter {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimezoneOffsetMinutes(short offset) {
|
||||
GB.toast("old firmware does't support timezones", Toast.LENGTH_LONG, GB.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFindDevice() {
|
||||
return supportsExtendedVibration();
|
||||
@ -443,6 +451,12 @@ public class MisfitWatchAdapter extends WatchAdapter {
|
||||
queueWrite(new ActivityPointGetRequest());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetAlarms(ArrayList<? extends Alarm> alarms) {
|
||||
GB.toast("alarms not supported with this firmware", Toast.LENGTH_LONG, GB.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void overwriteButtons() {
|
||||
uploadFileRequest = new UploadFileRequest((short) 0x0800, new byte[]{
|
||||
|
@ -0,0 +1,82 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.buttonconfig;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
public class ConfigFileBuilder {
|
||||
private ConfigPayload[] configs;
|
||||
|
||||
public ConfigFileBuilder(ConfigPayload[] configs) {
|
||||
this.configs = configs;
|
||||
}
|
||||
|
||||
public byte[] build(boolean appendChecksum) {
|
||||
int payloadSize = 0;
|
||||
for (ConfigPayload payload : this.configs) {
|
||||
payloadSize += payload.getData().length;
|
||||
}
|
||||
|
||||
int headerSize = 0;
|
||||
for (ConfigPayload payload : this.configs) {
|
||||
headerSize += payload.getHeader().length + 3; // button + version + null;
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(
|
||||
3 // version bytes
|
||||
+ 1 // header count byte
|
||||
+ headerSize
|
||||
+ 1 // payload count byte
|
||||
+ payloadSize
|
||||
+ 1 // customization count byte
|
||||
+ (appendChecksum ? 4 : 0) // checksum
|
||||
);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
buffer.put(new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x00}); // version
|
||||
buffer.put((byte) this.configs.length);
|
||||
int buttonIndex = 0x00;
|
||||
for (ConfigPayload payload : configs) {
|
||||
buffer.put((byte) (buttonIndex += 0x10));
|
||||
buffer.put((byte) 0x01);
|
||||
buffer.put(payload.getHeader());
|
||||
buffer.put((byte) 0x00);
|
||||
}
|
||||
|
||||
ArrayList<ConfigPayload> distinctPayloads = new ArrayList<>(3);
|
||||
|
||||
// distinctPayloads.add(configs[0].getData());
|
||||
|
||||
compareLoop:
|
||||
for (int payloadIndex = 0; payloadIndex < configs.length; payloadIndex++) {
|
||||
for (int compareTo = 0; compareTo < distinctPayloads.size(); compareTo++) {
|
||||
if (configs[payloadIndex].equals(distinctPayloads.get(compareTo))) {
|
||||
continue compareLoop;
|
||||
}
|
||||
}
|
||||
distinctPayloads.add(configs[payloadIndex]);
|
||||
}
|
||||
|
||||
buffer.put((byte) distinctPayloads.size());
|
||||
for (ConfigPayload payload : distinctPayloads) {
|
||||
buffer.put(payload.getData());
|
||||
}
|
||||
|
||||
buffer.put((byte) 0x00);
|
||||
|
||||
ByteBuffer buffer2 = ByteBuffer.allocate(buffer.position() + (appendChecksum ? 4 : 0));
|
||||
buffer2.order(ByteOrder.LITTLE_ENDIAN);
|
||||
buffer2.put(buffer.array(), 0, buffer.position());
|
||||
|
||||
if (!appendChecksum) return buffer2.array();
|
||||
|
||||
CRC32 crc = new CRC32();
|
||||
crc.update(buffer.array(), 0, buffer.position());
|
||||
|
||||
buffer2.putInt((int) crc.getValue());
|
||||
|
||||
return buffer2.array();
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.buttonconfig;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
|
||||
public enum ConfigPayload {
|
||||
FORWARD_TO_PHONE(
|
||||
"forward to phone",
|
||||
new byte[]{(byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x00},
|
||||
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x0C, (byte) 0x2E, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x0F, (byte) 0x00, (byte) 0x8B, (byte) 0x00, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x08, (byte) 0x01, (byte) 0x14, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0xFE, (byte) 0x08, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0xBF, (byte) 0xD5, (byte) 0x54, (byte) 0xD1,}
|
||||
),
|
||||
FORWARD_TO_PHONE_MULTI(
|
||||
"forward to phone (multifunction)",
|
||||
new byte[]{(byte) 0x01, (byte) 0x06, (byte) 0x12, (byte) 0x00},
|
||||
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x06, (byte) 0x12, (byte) 0x63, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x1D, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0xF6, (byte) 0x00, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x42, (byte) 0x02, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x43, (byte) 0x03, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x44, (byte) 0x04, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x1E, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xCD, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xB6, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xB5, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xFE, (byte) 0x08, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x7B, (byte) 0x56, (byte) 0x4E, (byte) 0x97}
|
||||
),
|
||||
STOPWATCH(
|
||||
"stopwatch",
|
||||
new byte[]{(byte) 0x02, (byte) 0x01, (byte) 0x20, (byte) 0x01},
|
||||
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x20, (byte) 0x20, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x07, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x08, (byte) 0x00, (byte) 0x92, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x0F, (byte) 0xC0, (byte) 0x5F, (byte) 0x2A}
|
||||
),
|
||||
DATE(
|
||||
"show date",
|
||||
new byte[]{(byte) 0x01, (byte) 0x01, (byte) 0x14, (byte) 0x00},
|
||||
new byte[]{(byte) 0x01 , (byte) 0x00 , (byte) 0x01 , (byte) 0x01 , (byte) 0x14 , (byte) 0x2D , (byte) 0x00 , (byte) 0x00 , (byte) 0x00 , (byte) 0x01 , (byte) 0x00 , (byte) 0x06 , (byte) 0x00 , (byte) 0x02 , (byte) 0x00 , (byte) 0x00 , (byte) 0x07 , (byte) 0x00 , (byte) 0x01 , (byte) 0x01 , (byte) 0x16 , (byte) 0x00 , (byte) 0x89 , (byte) 0x05 , (byte) 0x01 , (byte) 0x07 , (byte) 0xB0 , (byte) 0x00 , (byte) 0x00 , (byte) 0xB0 , (byte) 0x00 , (byte) 0x00 , (byte) 0xB0 , (byte) 0x00 , (byte) 0x00 , (byte) 0x08 , (byte) 0x01 , (byte) 0x50 , (byte) 0x00 , (byte) 0x01 , (byte) 0x00 , (byte) 0xD0 , (byte) 0x89 , (byte) 0xDE , (byte) 0x6E}
|
||||
),
|
||||
LAST_NOTIFICATION(
|
||||
"show last notification",
|
||||
new byte[]{(byte) 0x01, (byte) 0x01, (byte) 0x18, (byte) 0x00},
|
||||
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x18, (byte) 0x2F, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x16, (byte) 0x00, (byte) 0x89, (byte) 0x05, (byte) 0x01, (byte) 0x07, (byte) 0xB0, (byte) 0x02, (byte) 0x00, (byte) 0xB0, (byte) 0x02, (byte) 0x00, (byte) 0xB0, (byte) 0x02, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x50, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x6B, (byte) 0x9D, (byte) 0x55, (byte) 0x3A}
|
||||
),
|
||||
SECOND_TIMEZONE(
|
||||
"show second timezone",
|
||||
new byte[]{0x01, (byte) 0x01, (byte) 0x16, (byte) 0x00},
|
||||
new byte[]{0x01, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x16, (byte) 0x2F, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x08, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x07, (byte) 0x02, (byte) 0x02, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x16, (byte) 0x00, (byte) 0x89, (byte) 0x05, (byte) 0x01, (byte) 0x07, (byte) 0xB0, (byte) 0x01, (byte) 0x00, (byte) 0xB0, (byte) 0x01, (byte) 0x00, (byte) 0xB0, (byte) 0x01, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x50, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x3D, (byte) 0x07, (byte) 0x28, (byte) 0x01}
|
||||
)
|
||||
/* PLAY_PAUSE(
|
||||
"play/pause music",
|
||||
new byte[]{(byte) 0x01, (byte) 0x06, (byte) 0x12, (byte) 0x00},
|
||||
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x06, (byte) 0x12, (byte) 0x63, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x1D, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0xF6, (byte) 0x00, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x42, (byte) 0x02, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x43, (byte) 0x03, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x44, (byte) 0x04, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x1E, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xCD, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xB6, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xB5, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xFE, (byte) 0x08, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x7B, (byte) 0x56, (byte) 0x4E, (byte) 0x97}
|
||||
),
|
||||
VOLUME_UP(
|
||||
"music volume up",
|
||||
new byte[]{(byte) 0x01, (byte) 0x04, (byte) 0x12, (byte) 0x00},
|
||||
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x04, (byte) 0x12, (byte) 0x5E, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x1D, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0xF6, (byte) 0x00, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x42, (byte) 0x02, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x43, (byte) 0x03, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x48, (byte) 0x04, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x1E, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xE9, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x0B, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xE9, (byte) 0x00, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x04, (byte) 0x0A, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xFE, (byte) 0x08, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0xC6, (byte) 0xB2, (byte) 0xCB, (byte) 0xAC}
|
||||
),
|
||||
VOLUME_DOWN(
|
||||
"music volume down",
|
||||
new byte[]{(byte) 0x01, (byte) 0x05, (byte) 0x12, (byte) 0x00},
|
||||
new byte[]{(byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x05, (byte) 0x12, (byte) 0x5E, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x03, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x1D, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0xF6, (byte) 0x00, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x42, (byte) 0x02, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x43, (byte) 0x03, (byte) 0x00, (byte) 0x85, (byte) 0x01, (byte) 0x48, (byte) 0x04, (byte) 0x00, (byte) 0x08, (byte) 0x01, (byte) 0x1E, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x0D, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xEA, (byte) 0x00, (byte) 0x01, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x03, (byte) 0x0B, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0xEA, (byte) 0x00, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x01, (byte) 0x04, (byte) 0x0A, (byte) 0x00, (byte) 0x8C, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0xFE, (byte) 0x08, (byte) 0x00, (byte) 0x93, (byte) 0x00, (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0xFA, (byte) 0x18, (byte) 0x49, (byte) 0x03}
|
||||
) */
|
||||
;
|
||||
private byte[] header, data;
|
||||
|
||||
static public ConfigPayload fromId(short id) throws RuntimeException{
|
||||
for(ConfigPayload payload : ConfigPayload.values()){
|
||||
ByteBuffer buffer = ByteBuffer.wrap(payload.header);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
if(id == buffer.getShort(1)) return payload;
|
||||
}
|
||||
|
||||
throw new RuntimeException("app " + id + " not found");
|
||||
}
|
||||
|
||||
public byte[] getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public boolean equals(ConfigPayload p1, ConfigPayload p2){
|
||||
return Arrays.equals(p1.getData(), p2.getData());
|
||||
}
|
||||
|
||||
private String description;
|
||||
|
||||
ConfigPayload(String description, byte[] header, byte[] data) {
|
||||
this.description = description;
|
||||
this.header = header;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class Alarm {
|
||||
public final int WEEKDAY_SUNDAY = 0;
|
||||
public final int WEEKDAY_MONDAY = 1;
|
||||
public final int WEEKDAY_TUESDAY = 2;
|
||||
public final int WEEKDAY_THURSDAY = 3;
|
||||
public final int WEEKDAY_WEDNESDAY = 4;
|
||||
public final int WEEKDAY_FRIDAY = 5;
|
||||
public final int WEEKDAY_SATURDAY = 6;
|
||||
private byte days = 0;
|
||||
private byte minute, hour;
|
||||
private boolean repeat;
|
||||
|
||||
public Alarm(byte minute, byte hour){
|
||||
this.minute = minute;
|
||||
this.hour = hour;
|
||||
this.repeat = false;
|
||||
}
|
||||
|
||||
public Alarm(byte minute, byte hour, boolean repeat){
|
||||
this.minute = minute;
|
||||
this.hour = hour;
|
||||
this.repeat = repeat;
|
||||
}
|
||||
|
||||
public Alarm(byte minute, byte hour, byte days){
|
||||
this.minute = minute;
|
||||
this.hour = hour;
|
||||
this.repeat = true;
|
||||
this.days = days;
|
||||
}
|
||||
|
||||
public void setDayEnabled(int day, boolean enabled){
|
||||
if(enabled) this.days |= 1 << day;
|
||||
else this.days &= ~(1 << day);
|
||||
}
|
||||
|
||||
public byte[] getData(){
|
||||
byte first = (byte) 0xFF;
|
||||
if(repeat){
|
||||
first = (byte) (0x80 | this.days);
|
||||
}
|
||||
|
||||
byte second = (byte) this.minute;
|
||||
|
||||
if(repeat) second |= 0x80;
|
||||
|
||||
byte third = this.hour;
|
||||
|
||||
return new byte[]{first, second, third};
|
||||
}
|
||||
|
||||
static public Alarm fromBytes(byte[] bytes){
|
||||
if(bytes.length != 3) throw new RuntimeException("alarm bytes length must be 3");
|
||||
|
||||
byte days = bytes[0];
|
||||
|
||||
byte minutes = (byte)(bytes[1] & 0b01111111);
|
||||
boolean repeat = (bytes[1] & 0x80) == 0x80;
|
||||
|
||||
if(repeat) {
|
||||
return new Alarm(minutes, bytes[2], days);
|
||||
}
|
||||
return new Alarm(minutes, bytes[2]);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
String description = this.hour + ":" + this.minute + " ";
|
||||
if(repeat){
|
||||
String[] dayNames = new String[]{"sunday", "monday", "tuesday", "thursday", "wednesday", "friday", "saturday"};
|
||||
for(int i = WEEKDAY_SUNDAY; i <= WEEKDAY_SATURDAY; i++){
|
||||
if((days & 1 << i) != 0){
|
||||
description += dayNames[i] + " ";
|
||||
}
|
||||
}
|
||||
}else{
|
||||
description += "not repeating";
|
||||
}
|
||||
|
||||
return description;
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileGetRequest;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileLookupAndGetRequest;
|
||||
|
||||
public class AlarmsGetRequest extends FileLookupAndGetRequest {
|
||||
public AlarmsGetRequest(FossilWatchAdapter adapter) {
|
||||
super((byte) 0x0A, adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFileData(byte[] fileData) {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(fileData);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
short handle = buffer.getShort(0);
|
||||
if(handle != (short) 0x0A00) throw new RuntimeException("wrong alarm handle");
|
||||
|
||||
int length = buffer.getInt(8) / 3;
|
||||
Alarm[] alarms = new Alarm[length];
|
||||
|
||||
for (int i = 0; i < length; i++){
|
||||
buffer.position(12 + i * 3);
|
||||
byte[] alarmBytes = new byte[]{
|
||||
buffer.get(),
|
||||
buffer.get(),
|
||||
buffer.get()
|
||||
};
|
||||
alarms[i] = Alarm.fromBytes(alarmBytes);
|
||||
}
|
||||
|
||||
this.handleAlarms(alarms);
|
||||
}
|
||||
|
||||
public void handleAlarms(Alarm[] alarms){
|
||||
Alarm[] alarms2 = new Alarm[alarms.length];
|
||||
|
||||
for(int i = 0; i < alarms.length; i++){
|
||||
alarms2[i] = Alarm.fromBytes(alarms[i].getData());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.alarm;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest;
|
||||
|
||||
public class AlarmsSetRequest extends FilePutRequest {
|
||||
public AlarmsSetRequest(Alarm[] alarms, FossilWatchAdapter adapter) {
|
||||
super((short) 0x0A00, createFileFromAlarms(alarms), adapter);
|
||||
}
|
||||
|
||||
static byte[] createFileFromAlarms(Alarm[] alarms){
|
||||
ByteBuffer buffer = ByteBuffer.allocate(alarms.length * 3);
|
||||
for(Alarm alarm : alarms) buffer.put(alarm.getData());
|
||||
|
||||
return buffer.array();
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.button;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.buttonconfig.ConfigPayload;
|
||||
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileGetRequest;
|
||||
|
||||
public class ButtonConfigurationGetRequest extends FileGetRequest {
|
||||
public ButtonConfigurationGetRequest(FossilWatchAdapter adapter) {
|
||||
super((short) 0x0600, adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleFileData(byte[] fileData) {
|
||||
log("fileData");
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(fileData);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
short fileHandle = buffer.getShort(0);
|
||||
// TODO check file handle
|
||||
// if(fileData != )
|
||||
|
||||
byte count = buffer.get(15);
|
||||
|
||||
ConfigPayload[] configs = new ConfigPayload[count];
|
||||
|
||||
buffer.position(16);
|
||||
for(int i = 0; i < count; i++){
|
||||
int buttonIndex = buffer.get() >> 4;
|
||||
int entryCount = buffer.get();
|
||||
buffer.get();
|
||||
short appId = buffer.getShort();
|
||||
|
||||
buffer.position(buffer.position() + entryCount * 5 - 3);
|
||||
|
||||
try {
|
||||
configs[buttonIndex - 1] = ConfigPayload.fromId(appId);
|
||||
}catch (RuntimeException e){
|
||||
configs[buttonIndex - 1] = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.onConfigurationsGet(configs);
|
||||
}
|
||||
|
||||
public void onConfigurationsGet(ConfigPayload[] configs){}
|
||||
}
|
@ -29,6 +29,8 @@ public class ConfigurationGetRequest extends FileLookupAndGetRequest {
|
||||
device.addDeviceInfo(new GenericItem(QHybridSupport.ITEM_STEP_GOAL, String.valueOf(((ConfigurationPutRequest.DailyStepGoalConfigItem) item).getValue())));
|
||||
}else if(item instanceof ConfigurationPutRequest.CurrentStepCountConfigItem){
|
||||
device.addDeviceInfo(new GenericItem(QHybridSupport.ITEM_STEP_COUNT, String.valueOf(((ConfigurationPutRequest.CurrentStepCountConfigItem) item).getValue())));
|
||||
}else if(item instanceof ConfigurationPutRequest.TimezoneOffsetConfigItem) {
|
||||
device.addDeviceInfo(new GenericItem(QHybridSupport.ITEM_TIMEZONE_OFFSET, String.valueOf(((ConfigurationPutRequest.TimezoneOffsetConfigItem) item).getValue())));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -109,6 +109,8 @@ public class ConfigurationPutRequest extends FilePutRequest {
|
||||
switch (value.getClass().getName()) {
|
||||
case "java.lang.Byte":
|
||||
return 1;
|
||||
case "java.lang.Short":
|
||||
return 2;
|
||||
case "java.lang.Integer":
|
||||
return 4;
|
||||
case "java.lang.Long":
|
||||
@ -139,6 +141,10 @@ public class ConfigurationPutRequest extends FilePutRequest {
|
||||
buffer.putLong((Long) this.value);
|
||||
break;
|
||||
}
|
||||
case "java.lang.Short": {
|
||||
buffer.putShort((Short) this.value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return buffer.array();
|
||||
}
|
||||
@ -161,6 +167,10 @@ public class ConfigurationPutRequest extends FilePutRequest {
|
||||
this.value = (T) (Integer) buffer.getInt();
|
||||
break;
|
||||
}
|
||||
case 8:{
|
||||
this.value = (T) (Long) buffer.getLong();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -175,6 +185,12 @@ public class ConfigurationPutRequest extends FilePutRequest {
|
||||
}
|
||||
}
|
||||
|
||||
static public class TimezoneOffsetConfigItem extends GenericConfigItem<Short> {
|
||||
public TimezoneOffsetConfigItem(Short value) {
|
||||
super((short) 17, value);
|
||||
}
|
||||
}
|
||||
|
||||
static public class VibrationStrengthConfigItem extends GenericConfigItem<Byte> {
|
||||
public VibrationStrengthConfigItem(){
|
||||
this((byte) -1);
|
||||
|
@ -64,6 +64,24 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:text="second timezone offset relative to UTC" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timezoneOffset"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonOverwriteButtons"
|
||||
android:layout_width="match_parent"
|
||||
@ -77,6 +95,12 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:text="use activity hand as notification counter" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/buttonConfigLayout"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- <ProgressBar
|
||||
|
58
app/src/main/res/xml/devicesettings_buttonactions.xml
Normal file
58
app/src/main/res/xml/devicesettings_buttonactions.xml
Normal file
@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceScreen
|
||||
android:key="screen_button_actions"
|
||||
android:persistent="false"
|
||||
android:summary="@string/mi2_prefs_button_actions_summary"
|
||||
android:title="@string/mi2_prefs_button_actions">
|
||||
|
||||
<!-- workaround for missing toolbar -->
|
||||
<PreferenceCategory android:title="@string/mi2_prefs_button_action" />
|
||||
|
||||
public static final String PREF_BUTTON_ACTION_PRESS_DELAY = "button_action_press_count_delay";
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="button_action_enable"
|
||||
android:summary="@string/mi2_prefs_button_action_summary"
|
||||
android:title="@string/mi2_prefs_button_action" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:dependency="button_action_enable"
|
||||
android:key="button_action_vibrate"
|
||||
android:summary="@string/mi2_prefs_button_action_vibrate_summary"
|
||||
android:title="@string/mi2_prefs_button_action_vibrate" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="6"
|
||||
android:dependency="button_action_enable"
|
||||
android:inputType="number"
|
||||
android:key="button_action_press_count"
|
||||
android:summary="@string/mi2_prefs_button_press_count_summary"
|
||||
android:title="@string/mi2_prefs_button_press_count" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="@string/mi2_prefs_button_press_broadcast_default_value"
|
||||
android:dependency="button_action_enable"
|
||||
android:key="button_action_broadcast"
|
||||
android:summary="@string/mi2_prefs_button_press_broadcast_summary"
|
||||
android:title="@string/mi2_prefs_button_press_broadcast" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="2000"
|
||||
android:dependency="button_action_enable"
|
||||
android:inputType="number"
|
||||
android:key="button_action_press_max_interval"
|
||||
android:summary="@string/mi2_prefs_button_press_count_max_delay_summary"
|
||||
android:title="@string/mi2_prefs_button_press_count_max_delay" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="0"
|
||||
android:dependency="button_action_enable"
|
||||
android:inputType="number"
|
||||
android:key="button_action_broadcast_delay"
|
||||
android:summary="@string/mi2_prefs_button_press_count_match_delay_summary"
|
||||
android:title="@string/mi2_prefs_button_press_count_match_delay" />
|
||||
</PreferenceScreen>
|
||||
</androidx.preference.PreferenceScreen>
|
@ -22,62 +22,6 @@
|
||||
android:summary="@string/mi2_prefs_goal_notification_summary"
|
||||
android:title="@string/mi2_prefs_goal_notification" />
|
||||
|
||||
<PreferenceScreen
|
||||
android:key="mi2_button_actions_key"
|
||||
android:summary="@string/mi2_prefs_button_actions_summary"
|
||||
android:title="@string/mi2_prefs_button_actions"
|
||||
android:persistent="false">
|
||||
|
||||
<!-- workaround for missing toolbar -->
|
||||
<PreferenceCategory
|
||||
android:title="@string/mi2_prefs_button_action"
|
||||
/>
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:key="mi2_enable_button_action"
|
||||
android:summary="@string/mi2_prefs_button_action_summary"
|
||||
android:title="@string/mi2_prefs_button_action" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="false"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:key="mi2_button_action_vibrate"
|
||||
android:summary="@string/mi2_prefs_button_action_vibrate_summary"
|
||||
android:title="@string/mi2_prefs_button_action_vibrate" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="6"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:inputType="number"
|
||||
android:key="mi_button_press_count"
|
||||
android:summary="@string/mi2_prefs_button_press_count_summary"
|
||||
android:title="@string/mi2_prefs_button_press_count" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="@string/mi2_prefs_button_press_broadcast_default_value"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:key="mi_button_press_broadcast"
|
||||
android:summary="@string/mi2_prefs_button_press_broadcast_summary"
|
||||
android:title="@string/mi2_prefs_button_press_broadcast" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="2000"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:inputType="number"
|
||||
android:key="mi_button_press_count_max_delay"
|
||||
android:summary="@string/mi2_prefs_button_press_count_max_delay_summary"
|
||||
android:title="@string/mi2_prefs_button_press_count_max_delay" />
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="0"
|
||||
android:dependency="mi2_enable_button_action"
|
||||
android:inputType="number"
|
||||
android:key="mi_button_press_count_match_delay"
|
||||
android:summary="@string/mi2_prefs_button_press_count_match_delay_summary"
|
||||
android:title="@string/mi2_prefs_button_press_count_match_delay" />
|
||||
</PreferenceScreen>
|
||||
|
||||
<EditTextPreference
|
||||
android:defaultValue="0"
|
||||
android:inputType="number"
|
||||
|
Loading…
Reference in New Issue
Block a user