From b8e46f085d2ecd92e9dbac1d24267723d0a24dec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Fri, 6 Sep 2024 00:09:42 +0100 Subject: [PATCH] Garmin: Hand calibration activity (WIP) --- app/src/main/AndroidManifest.xml | 2 +- .../gadgetbridge/GenericCommands.java | 5 + .../calibration/HandCalibrationActivity.java | 161 ++++++++++++++++++ .../calibration/HandCalibrationHandler.java | 53 ++++++ .../adapter/GBDeviceAdapterv2.java | 11 +- .../gadgetbridge/devices/EventHandler.java | 2 + .../vivomove/GarminVivomoveHrCoordinator.java | 11 ++ .../GarminVivomoveStyleCoordinator.java | 11 ++ .../GarminVivomoveTrendCoordinator.java | 12 ++ .../devices/qhybrid/CalibrationActivity.java | 158 ----------------- .../devices/qhybrid/QHybridCoordinator.java | 7 +- .../devices/test/TestDeviceCoordinator.java | 4 +- .../devices/test/TestFeature.java | 1 + .../gadgetbridge/impl/GBDeviceService.java | 8 + .../gadgetbridge/model/DeviceService.java | 3 + .../service/AbstractDeviceSupport.java | 2 + .../service/DeviceCommunicationService.java | 5 + .../service/ServiceDeviceSupport.java | 8 + .../service/devices/garmin/GarminSupport.java | 69 +++++++- .../devices/garmin/ProtocolBufferHandler.java | 17 ++ .../devices/qhybrid/QHybridSupport.java | 4 +- .../garmin/gdi_hand_calibration_service.proto | 27 +++ .../main/proto/garmin/gdi_smart_proto.proto | 2 + ...tion.xml => activity_hand_calibration.xml} | 41 +++-- app/src/main/res/layout/device_itemv2.xml | 2 +- ...icesettings_fossilhybridhr_calibration.xml | 13 -- 26 files changed, 437 insertions(+), 202 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/GenericCommands.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/calibration/HandCalibrationActivity.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/calibration/HandCalibrationHandler.java delete mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/CalibrationActivity.java create mode 100644 app/src/main/proto/garmin/gdi_hand_calibration_service.proto rename app/src/main/res/layout/{activity_qhybrid_calibration.xml => activity_hand_calibration.xml} (68%) delete mode 100644 app/src/main/res/xml/devicesettings_fossilhybridhr_calibration.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e792e65b9..869dc3998 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -814,7 +814,7 @@ android:exported="true" android:parentActivityName=".devices.qhybrid.HRConfigActivity" /> . */ +package nodomain.freeyourgadget.gadgetbridge.activities.calibration; + +import android.os.Bundle; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.Spinner; +import android.widget.Toast; + +import androidx.annotation.NonNull; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; + +import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.util.GB; + +public class HandCalibrationActivity extends AbstractGBActivity { + private GBDevice device; + private boolean shouldSave; + + enum HAND { + MINUTE, + HOUR, + SUB; + + public String getDisplayName() { + return name().substring(0, 1).toUpperCase() + name().substring(1).toLowerCase(); + } + + public String getVariableName() { + return name(); + } + + @NonNull + @Override + public String toString() { + return getDisplayName(); + } + } + + enum MOVE_BUTTON { + CLOCKWISE_ONE(R.id.hand_calibration_clockwise_1, 1), + CLOCKWISE_TEN(R.id.hand_calibration_clockwise_10, 10), + CLOCKWISE_HUNDRED(R.id.hand_calibration_clockwise_100, 100), + + COUNTER_CLOCKWISE_ONE(R.id.hand_calibration_counter_clockwise_1, -1), + COUNTER_CLOCKWISE_TEN(R.id.hand_calibration_counter_clockwise_10, -10), + COUNTER_CLOCKWISE_HUNDRED(R.id.hand_calibration_counter_clockwise_100, -100), + ; + + private final int layoutId; + private final int distance; + + MOVE_BUTTON(final int layoutId, final int distance) { + this.layoutId = layoutId; + this.distance = distance; + } + } + + HAND selectedHand = HAND.MINUTE; + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + device = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE); + if (device == null || !device.isInitialized()) { + GB.toast(getString(R.string.watch_not_connected), Toast.LENGTH_SHORT, GB.INFO); + finish(); + } + + setContentView(R.layout.activity_hand_calibration); + + final FloatingActionButton fab = findViewById(R.id.fab_hand_calibration_save); + fab.setOnClickListener(view -> { + // TODO save + shouldSave = true; + finish(); + }); + + // TODO start calibration + + initViews(); + } + + private void initViews() { + final Spinner handSpinner = findViewById(R.id.hand_calibration_hand_spinner); + handSpinner.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, HAND.values())); + handSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(final AdapterView parent, final View view, final int position, final long id) { + selectedHand = (HAND) parent.getSelectedItem(); + } + + @Override + public void onNothingSelected(final AdapterView parent) { + + } + }); + + for (final MOVE_BUTTON buttonDeclaration : MOVE_BUTTON.values()) { + final Button button = findViewById(buttonDeclaration.layoutId); + + button.setOnClickListener(v -> { + final short step = (short) buttonDeclaration.distance; + + // TODO move + }); + } + } + + @Override + protected void onPause() { + super.onPause(); + finish(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + if (!shouldSave) { + // If we're supposed to save, we already did it in the fab + // TODO abort + //GBApplication.deviceService(device).onGenericCommand(); + } + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + final int itemId = item.getItemId(); + if (itemId == android.R.id.home) { + // back button + // TODO abort + //GBApplication.deviceService(device).onGenericCommand(); + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/calibration/HandCalibrationHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/calibration/HandCalibrationHandler.java new file mode 100644 index 000000000..a2038d3cf --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/calibration/HandCalibrationHandler.java @@ -0,0 +1,53 @@ +package nodomain.freeyourgadget.gadgetbridge.activities.calibration; + +import android.os.Bundle; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class HandCalibrationHandler { + private static final Logger LOG = LoggerFactory.getLogger(HandCalibrationHandler.class); + + public static final String ACTION_CALIBRATION_START = "qhybrid_command_save_calibration1"; + public static final String ACTION_CALIBRATION_END = "qhybrid_command_save_calibration2"; + public static final String ACTION_CALIBRATION_MOVE = "qhybrid_command_save_calibration3"; + + private final Callback mCallback; + + public HandCalibrationHandler(final Callback callback) { + this.mCallback = callback; + } + + public void onGenericCommand(final Bundle bundle) { + final String action = bundle.getString("asd"); + if (action == null) { + LOG.warn("Got null action"); + return; + } + + switch (action) { + case ACTION_CALIBRATION_START: { + mCallback.onHandCalibrationStart(); + break; + } + case ACTION_CALIBRATION_END: { + //mCallback.onHandCalibrationEnd(); + break; + } + case ACTION_CALIBRATION_MOVE: { + //mCallback.onHandCalibrationMove(); + break; + } + } + } + + public void register() { + + } + + public interface Callback { + void onHandCalibrationStart(); + void onHandCalibrationEnd(boolean save); + void onHandCalibrationMove(int hand, int direction, int step); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java index ac1ba0ff4..ffed258cb 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/adapter/GBDeviceAdapterv2.java @@ -633,13 +633,10 @@ public class GBDeviceAdapterv2 extends ListAdapter { + Intent startIntent = new Intent(context, coordinator.getCalibrationActivity()); + startIntent.putExtra(GBDevice.EXTRA_DEVICE, device); + context.startActivity(startIntent); }); holder.fmFrequencyBox.setVisibility(View.GONE); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java index e11671d44..c63fbea12 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/EventHandler.java @@ -154,4 +154,6 @@ public interface EventHandler { void onSleepAsAndroidAction(String action, Bundle extras); void onCameraStatusChange(GBDeviceEventCameraRemote.Event event, String filename); + + void onGenericCommand(int type, Bundle data); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveHrCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveHrCoordinator.java index 49b12e38c..8f184a844 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveHrCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveHrCoordinator.java @@ -1,9 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.devices.garmin.watches.vivomove; +import android.app.Activity; + +import androidx.annotation.Nullable; + import java.util.regex.Pattern; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminCoordinator; +import nodomain.freeyourgadget.gadgetbridge.activities.calibration.HandCalibrationActivity; public class GarminVivomoveHrCoordinator extends GarminCoordinator { @Override @@ -15,4 +20,10 @@ public class GarminVivomoveHrCoordinator extends GarminCoordinator { public int getDeviceNameResource() { return R.string.devicetype_garmin_vivomove_hr; } + + @Nullable + @Override + public Class getCalibrationActivity() { + return HandCalibrationActivity.class; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveStyleCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveStyleCoordinator.java index 1dcb41b1f..16f901ce9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveStyleCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveStyleCoordinator.java @@ -1,8 +1,13 @@ package nodomain.freeyourgadget.gadgetbridge.devices.garmin.watches.vivomove; +import android.app.Activity; + +import androidx.annotation.Nullable; + import java.util.regex.Pattern; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.calibration.HandCalibrationActivity; import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminCoordinator; public class GarminVivomoveStyleCoordinator extends GarminCoordinator { @@ -15,4 +20,10 @@ public class GarminVivomoveStyleCoordinator extends GarminCoordinator { public int getDeviceNameResource() { return R.string.devicetype_garmin_vivomove_style; } + + @Nullable + @Override + public Class getCalibrationActivity() { + return HandCalibrationActivity.class; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveTrendCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveTrendCoordinator.java index 4325598c4..ce0cac1a1 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveTrendCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/garmin/watches/vivomove/GarminVivomoveTrendCoordinator.java @@ -1,9 +1,14 @@ package nodomain.freeyourgadget.gadgetbridge.devices.garmin.watches.vivomove; +import android.app.Activity; + +import androidx.annotation.Nullable; + import java.util.regex.Pattern; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.devices.garmin.GarminCoordinator; +import nodomain.freeyourgadget.gadgetbridge.activities.calibration.HandCalibrationActivity; public class GarminVivomoveTrendCoordinator extends GarminCoordinator { @Override @@ -15,4 +20,11 @@ public class GarminVivomoveTrendCoordinator extends GarminCoordinator { public int getDeviceNameResource() { return R.string.devicetype_garmin_vivomove_trend; } + + @Nullable + @Override + public Class getCalibrationActivity() { + // untested! + return HandCalibrationActivity.class; + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/CalibrationActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/CalibrationActivity.java deleted file mode 100644 index b0e4157ec..000000000 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/CalibrationActivity.java +++ /dev/null @@ -1,158 +0,0 @@ -/* Copyright (C) 2020-2024 Daniel Dakhno, José Rebelo - - This file is part of Gadgetbridge. - - Gadgetbridge is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published - by the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Gadgetbridge is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . */ -package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid; - -import android.content.Intent; -import android.os.Bundle; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.Button; -import android.widget.Spinner; -import android.widget.SpinnerAdapter; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - -import java.util.List; - -import nodomain.freeyourgadget.gadgetbridge.GBApplication; -import nodomain.freeyourgadget.gadgetbridge.R; -import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity; -import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; -import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport; - -public class CalibrationActivity extends AbstractGBActivity { - enum HAND{ - MINUTE, - HOUR, - SUB; - - public String getDisplayName(){ - return name().substring(0, 1).toUpperCase() + name().substring(1).toLowerCase(); - } - - public String getVariableName(){ - return name(); - } - - - @NonNull - @Override - public String toString() { - return getDisplayName(); - } - } - - enum MOVE_BUTTON{ - CLOCKWISE_ONE(R.id.qhybrid_calibration_clockwise_1, 1), - CLOCKWISE_TEN(R.id.qhybrid_calibration_clockwise_10, 10), - CLOCKWISE_HUNRED(R.id.qhybrid_calibration_clockwise_100, 100), - - COUNTER_CLOCKWISE_ONE(R.id.qhybrid_calibration_counter_clockwise_1, -1), - COUNTER_CLOCKWISE_TEN(R.id.qhybrid_calibration_counter_clockwise_10, -10), - COUNTER_CLOCKWISE_HUNRED(R.id.qhybrid_calibration_counter_clockwise_100, -100), - ; - - int layoutId; - int distance; - - MOVE_BUTTON(int layoutId, int distance) { - this.layoutId = layoutId; - this.distance = distance; - } - } - - HAND selectedHand = HAND.MINUTE; - LocalBroadcastManager localBroadcastManager; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_qhybrid_calibration); - - List devices = GBApplication.app().getDeviceManager().getSelectedDevices(); - boolean atLeastOneConnected = false; - for(GBDevice device : devices){ - if(device.getType() == DeviceType.FOSSILQHYBRID){ - atLeastOneConnected = true; - break; - } - } - - if(!atLeastOneConnected){ - Toast.makeText(this, R.string.watch_not_connected, Toast.LENGTH_LONG).show(); - finish(); - return; - } - - localBroadcastManager = LocalBroadcastManager.getInstance(this); - - localBroadcastManager.sendBroadcast( - new Intent(QHybridSupport.QHYBRID_COMMAND_CONTROL) - ); - - initViews(); - } - - private void initViews(){ - Spinner handSpinner = findViewById(R.id.qhybrid_calibration_hand_spinner); - handSpinner.setAdapter(new ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, HAND.values())); - handSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - selectedHand = (HAND) parent.getSelectedItem(); - } - - @Override - public void onNothingSelected(AdapterView parent) { - - } - }); - - for(final MOVE_BUTTON buttonDeclaration : MOVE_BUTTON.values()) { - final Button button = findViewById(buttonDeclaration.layoutId); - - button.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(QHybridSupport.QHYBRID_COMMAND_MOVE); - intent.putExtra("EXTRA_DISTANCE_" + selectedHand.getVariableName(), (short) buttonDeclaration.distance); - - localBroadcastManager.sendBroadcast(intent); - } - }); - } - } - - @Override - protected void onPause() { - super.onPause(); - finish(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (localBroadcastManager != null) { - localBroadcastManager.sendBroadcast(new Intent(QHybridSupport.QHYBRID_COMMAND_SAVE_CALIBRATION)); - localBroadcastManager.sendBroadcast(new Intent(QHybridSupport.QHYBRID_COMMAND_UNCONTROL)); - } - } -} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java index cba18d037..d99d50fef 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java @@ -39,6 +39,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity; +import nodomain.freeyourgadget.gadgetbridge.activities.calibration.HandCalibrationActivity; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen; @@ -183,6 +184,11 @@ public class QHybridCoordinator extends AbstractBLEDeviceCoordinator { return isHybridHR() ? HybridHRWatchfaceDesignerActivity.class : null; } + @Override + public Class getCalibrationActivity() { + return isHybridHR() ? HandCalibrationActivity.class : null; + } + /** * Returns the directory containing the watch app cache. * @throws IOException when the external files directory cannot be accessed @@ -253,7 +259,6 @@ public class QHybridCoordinator extends AbstractBLEDeviceCoordinator { generic.add(R.xml.devicesettings_fossilhybridhr_pre_fw220); } // Settings applicable to all firmware versions - generic.add(R.xml.devicesettings_fossilhybridhr_calibration); generic.add(R.xml.devicesettings_fossilhybridhr_navigation); final List health = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.HEALTH); health.add(R.xml.devicesettings_fossilhybridhr_workout_detection); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java index e8106c691..e8a984687 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java @@ -38,6 +38,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity; +import nodomain.freeyourgadget.gadgetbridge.activities.calibration.HandCalibrationActivity; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.capabilities.HeartRateCapability; import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl; @@ -540,8 +541,7 @@ public class TestDeviceCoordinator extends AbstractDeviceCoordinator { @Nullable @Override public Class getCalibrationActivity() { - // TODO getCalibrationActivity - return super.getCalibrationActivity(); + return supports(getTestDevice(), TestFeature.CALIBRATION) ? HandCalibrationActivity.class : null; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestFeature.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestFeature.java index ed5a485f4..fd7835239 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestFeature.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestFeature.java @@ -28,6 +28,7 @@ public enum TestFeature { APP_LIST_FETCHING, APP_REORDERING, APPS_MANAGEMENT, + CALIBRATION, BATTERIES_MULTIPLE, CACHED_APP_MANAGEMENT, CALENDAR_EVENTS, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java index bf5b5280f..320c31134 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/impl/GBDeviceService.java @@ -568,4 +568,12 @@ public class GBDeviceService implements DeviceService { intent.putExtra(EXTRA_CAMERA_FILENAME, filename); invokeService(intent); } + + @Override + public void onGenericCommand(final int type, final Bundle data) { + Intent intent = createIntent().setAction(ACTION_GENERIC_COMMAND); + intent.putExtra(EXTRA_GENERIC_COMMAND_TYPE, type); + intent.putExtra(EXTRA_GENERIC_COMMAND_DATA, data); + invokeService(intent); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java index 79c40d8f5..3421303ad 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/model/DeviceService.java @@ -80,6 +80,7 @@ public interface DeviceService extends EventHandler { String ACTION_SET_LED_COLOR = PREFIX + ".action.set_led_color"; String ACTION_POWER_OFF = PREFIX + ".action.power_off"; String ACTION_CAMERA_STATUS_CHANGE = PREFIX + ".action.camera_status_change"; + String ACTION_GENERIC_COMMAND = PREFIX + ".action.generic_command"; String ACTION_SLEEP_AS_ANDROID = ".action.sleep_as_android"; String EXTRA_SLEEP_AS_ANDROID_ACTION = "sleepasandroid_action"; @@ -147,6 +148,8 @@ public interface DeviceService extends EventHandler { String EXTRA_RESET_FLAGS = "reset_flags"; String EXTRA_CAMERA_EVENT = "event"; String EXTRA_CAMERA_FILENAME = "filename"; + String EXTRA_GENERIC_COMMAND_TYPE = "type"; + String EXTRA_GENERIC_COMMAND_DATA = "data"; /** * Use EXTRA_REALTIME_SAMPLE instead diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java index 0a432a70e..8eceaaa0d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/AbstractDeviceSupport.java @@ -1230,4 +1230,6 @@ public abstract class AbstractDeviceSupport implements DeviceSupport { @Override public void onCameraStatusChange(GBDeviceEventCameraRemote.Event event, String filename) {} + + public void onGenericCommand(int type, Bundle data) {} } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index a309d3ed4..30c3d0eb9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -1127,6 +1127,11 @@ public class DeviceCommunicationService extends Service implements SharedPrefere } deviceSupport.onCameraStatusChange(event, filename); break; + case ACTION_GENERIC_COMMAND: + final int genericCommandType = intentCopy.getIntExtra(EXTRA_GENERIC_COMMAND_TYPE, 0); + final Bundle genericCommandData = intentCopy.getBundleExtra(EXTRA_GENERIC_COMMAND_DATA); + deviceSupport.onGenericCommand(genericCommandType, genericCommandData); + break; } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java index ad01d2cb8..73b2d0520 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/ServiceDeviceSupport.java @@ -524,4 +524,12 @@ public class ServiceDeviceSupport implements DeviceSupport { } delegate.onCameraStatusChange(event, filename); } + + @Override + public void onGenericCommand(int type, Bundle data) { + if (checkBusy("generic command " + type)) { + return; + } + delegate.onGenericCommand(type, data); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java index cab947e13..9b319ed79 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/GarminSupport.java @@ -32,6 +32,7 @@ import java.util.stream.Collectors; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.calibration.HandCalibrationHandler; import nodomain.freeyourgadget.gadgetbridge.database.DBHandler; import nodomain.freeyourgadget.gadgetbridge.deviceevents.GBDeviceEvent; import nodomain.freeyourgadget.gadgetbridge.devices.PendingFileProvider; @@ -52,6 +53,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiCore; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiDeviceStatus; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiFindMyWatch; +import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiHandCalibrationService; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiSettingsService; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiSmartProto; import nodomain.freeyourgadget.gadgetbridge.service.btle.AbstractBTLEDeviceSupport; @@ -87,7 +89,8 @@ import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.Dev import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SEND_APP_NOTIFICATIONS; -public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommunicator.Callback { +public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommunicator.Callback, + HandCalibrationHandler.Callback { private static final Logger LOG = LoggerFactory.getLogger(GarminSupport.class); private final ProtocolBufferHandler protocolBufferHandler; private final NotificationsHandler notificationsHandler; @@ -310,7 +313,9 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni super.evaluateGBDeviceEvent(deviceEvent); } - /** @noinspection BooleanMethodIsAlwaysInverted*/ + /** + * @noinspection BooleanMethodIsAlwaysInverted + */ private boolean getKeepActivityDataOnDevice() { return getDevicePrefs().getBoolean("keep_activity_data_on_device", false); } @@ -826,9 +831,26 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni } } + int i = 0; + @Override public void onTestNewFunction() { - parseAllFitFilesFromStorage(); + switch (i) { + case 0: + onHandCalibrationStart(); + break; + case 1: + onHandCalibrationMove(1, 1, 1); + break; + case 2: + onHandCalibrationMove(1, 2, 10); + break; + case 3: + onHandCalibrationEnd(false); + break; + } + + i++; } boolean parsingFitFilesFromStorage = false; @@ -906,4 +928,45 @@ public class GarminSupport extends AbstractBTLEDeviceSupport implements ICommuni } }); } + + @Override + public void onHandCalibrationStart() { + sendOutgoingMessage("onHandCalibrationStart", protocolBufferHandler.prepareProtobufRequest(GdiSmartProto.Smart.newBuilder() + .setHandCalibrationService( + GdiHandCalibrationService.HandCalibrationService.newBuilder() + .setStartRequest( + GdiHandCalibrationService.HandCalibrationService.StartRequest.newBuilder() + .setCommand(1) + ) + ) + )); + } + + @Override + public void onHandCalibrationEnd(final boolean save) { + sendOutgoingMessage("onHandCalibrationEnd", protocolBufferHandler.prepareProtobufRequest(GdiSmartProto.Smart.newBuilder() + .setHandCalibrationService( + GdiHandCalibrationService.HandCalibrationService.newBuilder() + .setStartRequest( + GdiHandCalibrationService.HandCalibrationService.StartRequest.newBuilder() + .setCommand(save ? 3 : 2) + ) + ) + )); + } + + @Override + public void onHandCalibrationMove(final int hand, final int direction, final int step) { + sendOutgoingMessage("onHandCalibrationMove", protocolBufferHandler.prepareProtobufRequest(GdiSmartProto.Smart.newBuilder() + .setHandCalibrationService( + GdiHandCalibrationService.HandCalibrationService.newBuilder() + .setMoveRequest( + GdiHandCalibrationService.HandCalibrationService.MoveRequest.newBuilder() + .setHand(hand) + .setDirection(direction) + .setUnk3(step) + ) + ) + )); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/ProtocolBufferHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/ProtocolBufferHandler.java index 14c55d958..33defe259 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/ProtocolBufferHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/garmin/ProtocolBufferHandler.java @@ -31,6 +31,7 @@ import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiCore; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiDataTransferService; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiDeviceStatus; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiFindMyWatch; +import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiHandCalibrationService; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiHttpService; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiSettingsService; import nodomain.freeyourgadget.gadgetbridge.proto.garmin.GdiSmartProto; @@ -124,6 +125,18 @@ public class ProtocolBufferHandler implements MessageHandler { processed = true; processProtobufFindMyWatchResponse(smart.getFindMyWatchService()); } + if (smart.hasHandCalibrationService()) { + processed = true; + + final GdiHandCalibrationService.HandCalibrationService handCalibrationService = smart.getHandCalibrationService(); + if (handCalibrationService.hasStartResponse()) { + LOG.debug("Got hand calibration start response, status={}", handCalibrationService.getStartResponse().getStatus()); + } else if (handCalibrationService.hasMoveResponse()) { + LOG.debug("Got hand calibration move response, status={}", handCalibrationService.getMoveResponse().getStatus()); + } else { + LOG.warn("Got unknown hand calibration response {}", smart); + } + } if (smart.hasSettingsService()) { processed = true; processProtobufSettingsService(smart.getSettingsService()); @@ -439,6 +452,10 @@ public class ProtocolBufferHandler implements MessageHandler { return processed; } + public ProtobufMessage prepareProtobufRequest(GdiSmartProto.Smart.Builder protobufPayloadBuilder) { + return prepareProtobufRequest(protobufPayloadBuilder.build()); + } + public ProtobufMessage prepareProtobufRequest(GdiSmartProto.Smart protobufPayload) { if (null == protobufPayload) return null; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java index 19fbb40b7..f517003c3 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java @@ -83,8 +83,8 @@ public class QHybridSupport extends QHybridBaseSupport { public static final String QHYBRID_COMMAND_CONTROL = "qhybrid_command_control"; public static final String QHYBRID_COMMAND_UNCONTROL = "qhybrid_command_uncontrol"; public static final String QHYBRID_COMMAND_SET = "qhybrid_command_set"; - public static final String QHYBRID_COMMAND_MOVE = "qhybrid_command_move"; - public static final String QHYBRID_COMMAND_SAVE_CALIBRATION = "qhybrid_command_save_calibration"; + public static final String QHYBRID_COMMAND_MOVE = "qhybrid_command_move"; // TODO deprecated + public static final String QHYBRID_COMMAND_SAVE_CALIBRATION = "qhybrid_command_save_calibration"; // TODO deprecated 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"; diff --git a/app/src/main/proto/garmin/gdi_hand_calibration_service.proto b/app/src/main/proto/garmin/gdi_hand_calibration_service.proto new file mode 100644 index 000000000..feb93b9cf --- /dev/null +++ b/app/src/main/proto/garmin/gdi_hand_calibration_service.proto @@ -0,0 +1,27 @@ +syntax = "proto2"; + +package garmin_vivomovehr; + +option java_package = "nodomain.freeyourgadget.gadgetbridge.proto.garmin"; + +message HandCalibrationService { + optional StartRequest startRequest = 1; + optional StartResponse startResponse = 2; + optional MoveRequest moveRequest = 3; + optional MoveResponse moveResponse = 4; + + message StartRequest { + optional uint32 command = 1; // 1 start 2 abort 3 save + } + message StartResponse { + optional uint32 status = 1; // 1 + } + message MoveRequest { + optional uint32 hand = 1; // 1 hour 2 minute + optional uint32 direction = 2; // 1 + 2 - + optional uint32 unk3 = 3; // 1 - step? + } + message MoveResponse { + optional uint32 status = 1; // 1 + } +} diff --git a/app/src/main/proto/garmin/gdi_smart_proto.proto b/app/src/main/proto/garmin/gdi_smart_proto.proto index 218592db3..b2c4cdaa1 100644 --- a/app/src/main/proto/garmin/gdi_smart_proto.proto +++ b/app/src/main/proto/garmin/gdi_smart_proto.proto @@ -11,6 +11,7 @@ import "garmin/gdi_http_service.proto"; import "garmin/gdi_data_transfer_service.proto"; import "garmin/gdi_sms_notification.proto"; import "garmin/gdi_calendar_service.proto"; +import "garmin/gdi_hand_calibration_service.proto"; import "garmin/gdi_settings_service.proto"; message Smart { @@ -21,6 +22,7 @@ message Smart { optional FindMyWatchService find_my_watch_service = 12; optional CoreService core_service = 13; optional SmsNotificationService sms_notification_service = 16; + optional HandCalibrationService hand_calibration_service = 33; optional SettingsService settings_service = 42; } diff --git a/app/src/main/res/layout/activity_qhybrid_calibration.xml b/app/src/main/res/layout/activity_hand_calibration.xml similarity index 68% rename from app/src/main/res/layout/activity_qhybrid_calibration.xml rename to app/src/main/res/layout/activity_hand_calibration.xml index 93fa46da9..9dcd35edd 100644 --- a/app/src/main/res/layout/activity_qhybrid_calibration.xml +++ b/app/src/main/res/layout/activity_hand_calibration.xml @@ -1,5 +1,6 @@ @@ -15,13 +16,14 @@ android:text="@string/qhybrid_calibration_align_hint" /> + android:layout_height="wrap_content" /> @@ -29,30 +31,30 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" - android:orientation="vertical" - android:gravity="center_horizontal"> + android:gravity="center_horizontal" + android:orientation="vertical"> + android:text="@string/qhybrid_calibration_counterclockwise" />