From 25bb16b0c3d6b84f941142b17b13b9d1bc98d67b Mon Sep 17 00:00:00 2001 From: Andreas Shimokawa Date: Thu, 14 Nov 2019 12:33:36 +0100 Subject: [PATCH 01/14] Make button action setting per-device, enable for amazfip bip TODO: - preference migration - support vibration on bip --- .../DeviceSpecificSettingsFragment.java | 41 +++++++------ .../devices/huami/HuamiConst.java | 7 +++ .../amazfitbip/AmazfitBipCoordinator.java | 1 + .../huami/miband2/MiBand2Coordinator.java | 1 + .../devices/miband/MiBandConst.java | 6 -- .../service/devices/huami/HuamiSupport.java | 19 +++--- .../huami/amazfitbip/AmazfitBipSupport.java | 5 -- .../res/xml/devicesettings_buttonactions.xml | 58 +++++++++++++++++++ app/src/main/res/xml/miband_preferences.xml | 56 ------------------ 9 files changed, 98 insertions(+), 96 deletions(-) create mode 100644 app/src/main/res/xml/devicesettings_buttonactions.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java index 90d75ef58..f5c04e7cd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java @@ -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); + } + }); + } + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java index 891a14c0e..ada18bb2d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java @@ -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: diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java index 7de49a73c..1b28bb5e6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/amazfitbip/AmazfitBipCoordinator.java @@ -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 }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java index b087dbe1d..f39fd61f7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband2/MiBand2Coordinator.java @@ -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 }; } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java index 7e2ab22a7..ff7b69883 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/MiBandConst.java @@ -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"; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java index 92c0e49c9..eca222cbb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java @@ -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() { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java index e152872c0..2fa1329c2 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipSupport.java @@ -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) { diff --git a/app/src/main/res/xml/devicesettings_buttonactions.xml b/app/src/main/res/xml/devicesettings_buttonactions.xml new file mode 100644 index 000000000..14083cd18 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_buttonactions.xml @@ -0,0 +1,58 @@ + + + + + + + + public static final String PREF_BUTTON_ACTION_PRESS_DELAY = "button_action_press_count_delay"; + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/miband_preferences.xml b/app/src/main/res/xml/miband_preferences.xml index 13dca15b9..9cda47158 100644 --- a/app/src/main/res/xml/miband_preferences.xml +++ b/app/src/main/res/xml/miband_preferences.xml @@ -22,62 +22,6 @@ android:summary="@string/mi2_prefs_goal_notification_summary" android:title="@string/mi2_prefs_goal_notification" /> - - - - - - - - - - - - - - - - - - Date: Sat, 16 Nov 2019 21:47:14 +0100 Subject: [PATCH 02/14] Amazfit Bip: Whitelist BipOS 0.5 firmware/res (non-latin) --- .../devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java index 9b446278d..6ac8d83be 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/amazfitbip/AmazfitBipFirmwareInfo.java @@ -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) { From e04cd474dce8658bd723ebb1e3ff5fed3bfc3271 Mon Sep 17 00:00:00 2001 From: Daniel Dakhno Date: Sun, 17 Nov 2019 04:55:12 +0100 Subject: [PATCH 04/14] user can now assign a few more functions to buttons --- .../devices/qhybrid/ConfigActivity.java | 68 ++++++++++++++- .../adapter/fossil/FossilWatchAdapter.java | 76 ++++++++++++----- .../buttonconfig/ConfigFileBuilder.java | 82 +++++++++++++++++++ .../qhybrid/buttonconfig/ConfigPayload.java | 58 +++++++++++++ .../ButtonConfigurationGetRequest.java | 47 +++++++++++ .../res/layout/activity_qhybrid_settings.xml | 6 ++ 6 files changed, 313 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/buttonconfig/ConfigFileBuilder.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/buttonconfig/ConfigPayload.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/buttons/ButtonConfigurationGetRequest.java diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java index 6ff7eed29..7a5a21f48 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/ConfigActivity.java @@ -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 { @@ -305,7 +312,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 +333,67 @@ 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); + try { + ConfigPayload payload = ConfigPayload.valueOf(configName); + buttonTextView.setTextColor(Color.WHITE); + buttonTextView.setTextSize(20); + 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(); + } + } + }) + .create(); + dialog.show(); + } + }); + + buttonLayout.addView(buttonTextView); + } + } catch (JSONException e) { + e.printStackTrace(); + GB.toast("error parsing button config", Toast.LENGTH_LONG, GB.ERROR); + } + } } }); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java index 69a5d8817..7f5588f4d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil/FossilWatchAdapter.java @@ -7,6 +7,10 @@ import android.os.Build; import android.util.Log; import android.widget.Toast; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; @@ -21,10 +25,13 @@ 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.buttons.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; @@ -46,6 +53,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; @@ -64,6 +72,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(payload.toString()); + String json = buttons.toString(); + getDeviceSupport().getDevice().addDeviceInfo(new GenericItem(ITEM_BUTTONS, json)); + } + }); + queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZED), false); } @@ -105,24 +125,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){ - @Override - public void onFilePut(boolean success) { - 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); + 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; + } } - }; - queueWrite(fileUploadRequets); + + 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); + else GB.toast("error overwriting button settings", Toast.LENGTH_SHORT, GB.INFO); + } + }; + queueWrite(fileUploadRequets); + } catch (JSONException e) { + e.printStackTrace(); + } } @Override @@ -161,10 +190,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); @@ -176,11 +206,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 ); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/buttonconfig/ConfigFileBuilder.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/buttonconfig/ConfigFileBuilder.java new file mode 100644 index 000000000..af28bc65a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/buttonconfig/ConfigFileBuilder.java @@ -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 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(); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/buttonconfig/ConfigPayload.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/buttonconfig/ConfigPayload.java new file mode 100644 index 000000000..45fd450a8 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/buttonconfig/ConfigPayload.java @@ -0,0 +1,58 @@ +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,} + ), + 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} + ); + 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; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/buttons/ButtonConfigurationGetRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/buttons/ButtonConfigurationGetRequest.java new file mode 100644 index 000000000..ecb58222a --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/buttons/ButtonConfigurationGetRequest.java @@ -0,0 +1,47 @@ +package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.buttons; + +import android.util.SparseArray; + +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]; + + for(int i = 0; i < count; i++){ + byte buttonIndex = (byte) (buffer.get(16 + i * 7) >> 4); + short appId = buffer.getShort(19 + i * 7); + + try { + configs[buttonIndex - 1] = ConfigPayload.fromId(appId); + }catch (RuntimeException e){ + configs[buttonIndex - 1] = null; + } + } + + this.onConfigurationsGet(configs); + } + + public void onConfigurationsGet(ConfigPayload[] configs){} +} diff --git a/app/src/main/res/layout/activity_qhybrid_settings.xml b/app/src/main/res/layout/activity_qhybrid_settings.xml index 2007dbfd5..bd9a45223 100644 --- a/app/src/main/res/layout/activity_qhybrid_settings.xml +++ b/app/src/main/res/layout/activity_qhybrid_settings.xml @@ -77,6 +77,12 @@ android:layout_height="wrap_content" android:text="use activity hand as notification counter" /> + +