Garmin: Hand calibration activity (WIP)

This commit is contained in:
José Rebelo 2024-09-06 00:09:42 +01:00
parent e51b55a38a
commit b8e46f085d
26 changed files with 437 additions and 202 deletions

View File

@ -814,7 +814,7 @@
android:exported="true"
android:parentActivityName=".devices.qhybrid.HRConfigActivity" />
<activity
android:name=".devices.qhybrid.CalibrationActivity"
android:name=".activities.calibration.HandCalibrationActivity"
android:label="@string/qhybrid_title_calibration"
android:parentActivityName=".devices.qhybrid.HRConfigActivity" />
<activity

View File

@ -0,0 +1,5 @@
package nodomain.freeyourgadget.gadgetbridge;
public class GenericCommands {
public static final int GENERIC_CMD_HAND_CALIBRATION = 1;
}

View File

@ -0,0 +1,161 @@
/* 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 <https://www.gnu.org/licenses/>. */
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);
}
}

View File

@ -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);
}
}

View File

@ -633,13 +633,10 @@ public class GBDeviceAdapterv2 extends ListAdapter<GBDevice, GBDeviceAdapterv2.V
);
holder.calibrateDevice.setVisibility(device.isInitialized() && (coordinator.getCalibrationActivity() != null) ? View.VISIBLE : View.GONE);
holder.calibrateDevice.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent startIntent = new Intent(context, coordinator.getCalibrationActivity());
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
}
holder.calibrateDevice.setOnClickListener(v -> {
Intent startIntent = new Intent(context, coordinator.getCalibrationActivity());
startIntent.putExtra(GBDevice.EXTRA_DEVICE, device);
context.startActivity(startIntent);
});
holder.fmFrequencyBox.setVisibility(View.GONE);

View File

@ -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);
}

View File

@ -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<? extends Activity> getCalibrationActivity() {
return HandCalibrationActivity.class;
}
}

View File

@ -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<? extends Activity> getCalibrationActivity() {
return HandCalibrationActivity.class;
}
}

View File

@ -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<? extends Activity> getCalibrationActivity() {
// untested!
return HandCalibrationActivity.class;
}
}

View File

@ -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 <https://www.gnu.org/licenses/>. */
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<GBDevice> 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<HAND>(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));
}
}
}

View File

@ -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<? extends Activity> 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<Integer> health = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.HEALTH);
health.add(R.xml.devicesettings_fossilhybridhr_workout_detection);

View File

@ -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<? extends Activity> getCalibrationActivity() {
// TODO getCalibrationActivity
return super.getCalibrationActivity();
return supports(getTestDevice(), TestFeature.CALIBRATION) ? HandCalibrationActivity.class : null;
}
@Override

View File

@ -28,6 +28,7 @@ public enum TestFeature {
APP_LIST_FETCHING,
APP_REORDERING,
APPS_MANAGEMENT,
CALIBRATION,
BATTERIES_MULTIPLE,
CACHED_APP_MANAGEMENT,
CALENDAR_EVENTS,

View File

@ -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);
}
}

View File

@ -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

View File

@ -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) {}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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)
)
)
));
}
}

View File

@ -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;

View File

@ -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";

View File

@ -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
}
}

View File

@ -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;
}

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
@ -15,13 +16,14 @@
android:text="@string/qhybrid_calibration_align_hint" />
<Spinner
android:id="@+id/hand_calibration_hand_spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/qhybrid_calibration_hand_spinner" />
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:baselineAligned="false"
android:orientation="horizontal"
android:weightSum="2">
@ -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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/qhybrid_calibration_counterclockwise"/>
android:text="@string/qhybrid_calibration_counterclockwise" />
<Button
android:id="@+id/hand_calibration_counter_clockwise_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/qhybrid_calibration_counter_clockwise_1"
android:text="@string/qhybrid_calibration_1_step" />
<Button
android:id="@+id/hand_calibration_counter_clockwise_10"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/qhybrid_calibration_counter_clockwise_10"
android:text="@string/qhybrid_calibration_10_steps" />
<Button
android:id="@+id/hand_calibration_counter_clockwise_100"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/qhybrid_calibration_counter_clockwise_100"
android:text="@string/qhybrid_calibration_100_steps" />
</LinearLayout>
@ -61,30 +63,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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/qhybrid_calibration_clockwise"/>
android:text="@string/qhybrid_calibration_clockwise" />
<Button
android:id="@+id/hand_calibration_clockwise_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/qhybrid_calibration_clockwise_1"
android:text="@string/qhybrid_calibration_1_step" />
<Button
android:id="@+id/hand_calibration_clockwise_10"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/qhybrid_calibration_clockwise_10"
android:text="@string/qhybrid_calibration_10_steps" />
<Button
android:id="@+id/hand_calibration_clockwise_100"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/qhybrid_calibration_clockwise_100"
android:text="@string/qhybrid_calibration_100_steps" />
</LinearLayout>
@ -93,4 +95,15 @@
</LinearLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_hand_calibration_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="16dp"
android:contentDescription="@string/save_configuration"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:srcCompat="@drawable/ic_save" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -441,7 +441,7 @@
android:focusable="true"
android:padding="3dp"
android:scaleType="fitXY"
card_view:srcCompat="@drawable/ic_activity_unknown"
card_view:srcCompat="@drawable/ic_sensor_calibration"
card_view:tint="@color/secondarytext" />
<LinearLayout

View File

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:title="@string/qhybrid_title_calibration"
android:icon="@drawable/ic_sensor_calibration"
android:summary="@string/qhybrid_summary_calibration">
<intent
android:targetPackage="@string/applicationId"
android:targetClass="nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.CalibrationActivity" />
</Preference>
</androidx.preference.PreferenceScreen>