diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 788b6b740..ac2381898 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -505,6 +505,9 @@
+
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 7734133a8..a27e9fa2e 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
@@ -312,7 +312,7 @@ public class ConfigActivity extends AbstractGBActivity {
});
device = GBApplication.app().getDeviceManager().getSelectedDevice();
- if (device == null || device.getType() != DeviceType.FOSSILQHYBRID) {
+ if (device == null || device.getType() != DeviceType.FOSSILQHYBRID || device.getFirmwareVersion().charAt(2) != '0') {
setSettingsError(getString(R.string.watch_not_connected));
} else {
updateSettings();
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HRConfigActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HRConfigActivity.java
new file mode 100644
index 000000000..aa5326ed6
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HRConfigActivity.java
@@ -0,0 +1,191 @@
+package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.TextureView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.w3c.dom.Text;
+
+import java.sql.Array;
+import java.util.ArrayList;
+import java.util.List;
+
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.R;
+import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
+
+public class HRConfigActivity extends AbstractGBActivity implements View.OnClickListener, DialogInterface.OnClickListener, AdapterView.OnItemClickListener {
+ private SharedPreferences sharedPreferences;
+ private ActionListAdapter actionListAdapter;
+ private ArrayList menuActions = new ArrayList<>();
+
+ static public final String CONFIG_KEY_Q_ACTIONS = "Q_ACTIONS";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_qhybrid_hr_settings);
+
+ findViewById(R.id.qhybrid_action_add).setOnClickListener(this);
+
+ sharedPreferences = GBApplication.getPrefs().getPreferences();
+
+ ListView actionListView = findViewById(R.id.qhybrid_action_list);
+ actionListAdapter = new ActionListAdapter(menuActions);
+ actionListView.setAdapter(actionListAdapter);
+ actionListView.setOnItemClickListener(this);
+
+ updateSettings();
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.qhybrid_action_add) {
+ final EditText input = new EditText(this);
+ input.setId(0);
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+ input.setLayoutParams(lp);
+
+ new AlertDialog.Builder(this)
+ .setView(input)
+ .setNegativeButton("cancel", null)
+ .setPositiveButton("ok", this)
+ .setTitle("create action")
+ .show();
+ }
+ }
+
+ private void updateSettings() {
+ JSONArray actionArray = null;
+ try {
+ actionArray = new JSONArray(sharedPreferences.getString(CONFIG_KEY_Q_ACTIONS, "[]"));
+ menuActions.clear();
+ for (int i = 0; i < actionArray.length(); i++)
+ menuActions.add(new MenuAction(actionArray.getString(i)));
+
+ actionListAdapter.notifyDataSetChanged();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ EditText actionEditText = ((AlertDialog) dialog).findViewById(0);
+
+ String action = actionEditText.getText().toString();
+ try {
+ JSONArray actionArray = new JSONArray(sharedPreferences.getString(CONFIG_KEY_Q_ACTIONS, "[]"));
+ actionArray.put(action);
+ sharedPreferences.edit().putString(CONFIG_KEY_Q_ACTIONS, actionArray.toString()).apply();
+ updateSettings();
+
+ LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(new Intent(QHybridSupport.QHYBRID_COMMAND_OVERWRITE_BUTTONS));
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, final int position, long id) {
+ final EditText input = new EditText(this);
+ input.setId(0);
+ input.setText(((TextView) view).getText());
+ LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+ input.setLayoutParams(lp);
+
+ new AlertDialog.Builder(this)
+ .setView(input)
+ .setNegativeButton("delete", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ menuActions.remove(position);
+ putActionItems(menuActions);
+ updateSettings();
+
+ LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(new Intent(QHybridSupport.QHYBRID_COMMAND_OVERWRITE_BUTTONS));
+ }
+ })
+ .setPositiveButton("ok", new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ menuActions.get(position).setAction(input.getText().toString());
+ putActionItems(menuActions);
+ updateSettings();
+
+ LocalBroadcastManager.getInstance(HRConfigActivity.this).sendBroadcast(new Intent(QHybridSupport.QHYBRID_COMMAND_OVERWRITE_BUTTONS));
+ }
+ })
+ .setTitle("edit action")
+ .show();
+ }
+
+ private void putActionItems(List actions){
+ JSONArray array = new JSONArray();
+ for (MenuAction action : actions) array.put(action.getAction());
+
+ sharedPreferences.edit().putString(CONFIG_KEY_Q_ACTIONS, array.toString()).apply();
+ }
+
+ class MenuAction {
+ private String action;
+
+ public MenuAction(String action) {
+ this.action = action;
+ }
+
+ public String getAction() {
+ return action;
+ }
+
+ public void setAction(String action) {
+ this.action = action;
+ }
+ }
+
+ class ActionListAdapter extends ArrayAdapter {
+ public ActionListAdapter(@NonNull ArrayList objects) {
+ super(HRConfigActivity.this, 0, objects);
+ }
+
+ @NonNull
+ @Override
+ public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
+ if (convertView == null) convertView = new TextView(getContext());
+ TextView view = (TextView) convertView;
+
+ view.setText(getItem(position).getAction());
+ // view.setTextColor(Color.WHITE);
+ view.setTextSize(30);
+
+ return view;
+ }
+ }
+}
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 ee2107f70..6163e1d3e 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
@@ -145,7 +145,9 @@ public class QHybridCoordinator extends AbstractDeviceCoordinator {
@Override
public Class extends Activity> getAppsManagementActivity() {
- return ConfigActivity.class;
+ GBDevice connectedDevice = GBApplication.app().getDeviceManager().getSelectedDevice();
+ boolean isHR = connectedDevice.getFirmwareVersion().charAt(2) == '1';
+ return isHR ? HRConfigActivity.class : ConfigActivity.class;
}
@Override
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java
index ff4f0ba39..665edb924 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/WatchAdapter.java
@@ -94,4 +94,7 @@ public abstract class WatchAdapter {
}
return s.substring(0, s.length() - 1) + "\n";
}
+
+ public void setCommuteMenuMessage(String message, boolean finished) {
+ }
}
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 f9f43feb8..7c8ad243c 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
@@ -86,7 +86,7 @@ public class FossilWatchAdapter extends WatchAdapter {
private int lastButtonIndex = -1;
- Logger logger = LoggerFactory.getLogger(getClass());
+ Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());
public FossilWatchAdapter(QHybridSupport deviceSupport) {
super(deviceSupport);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java
index c1b2c38fa..7b74cf0f3 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java
@@ -1,16 +1,24 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr;
-import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
+import android.content.Intent;
import android.os.Build;
import android.util.Log;
+import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+import nodomain.freeyourgadget.gadgetbridge.GBApplication;
+import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.HRConfigActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationHRConfiguration;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
@@ -18,13 +26,17 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSuppo
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
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.configuration.ConfigurationPutRequest;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.CurrentStepCountConfigItem;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.PlayNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.authentication.VerifyPrivateKeyRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.buttons.ButtonConfigurationPutRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.configuration.ConfigurationGetRequest;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.Image;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.image.ImagesPutRequest;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.menu.SetCommuteMenuMessage;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.notification.NotificationFilterPutHRRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.notification.NotificationImagePutRequest;
-import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class FossilHRWatchAdapter extends FossilWatchAdapter {
private byte[] secretKey = new byte[]{(byte) 0x60, (byte) 0x26, (byte) 0xB7, (byte) 0xFD, (byte) 0xB2, (byte) 0x6D, (byte) 0x05, (byte) 0x5E, (byte) 0xDA, (byte) 0xF7, (byte) 0x4B, (byte) 0x49, (byte) 0x98, (byte) 0x78, (byte) 0x02, (byte) 0x38};
@@ -68,38 +80,64 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
e.printStackTrace();
} // icons
- queueWrite(new NotificationFilterPutHRRequest(new NotificationHRConfiguration[]{
- new NotificationHRConfiguration("com.whatsapp", -1),
- new NotificationHRConfiguration("asdasdasdasdasd", -1),
- // new NotificationHRConfiguration("twitter", -1),
- }, this));
+ // queueWrite(new NotificationFilterPutHRRequest(new NotificationHRConfiguration[]{
+ // new NotificationHRConfiguration("com.whatsapp", -1),
+ // new NotificationHRConfiguration("asdasdasdasdasd", -1),
+ // // new NotificationHRConfiguration("twitter", -1),
+ // }, this));
- queueWrite(new PlayNotificationRequest("com.whatsapp", "WhatsAp", "wHATSaPP", this));
- queueWrite(new PlayNotificationRequest("twitterrrr", "Twitterr", "tWITTER", this));
+ // queueWrite(new PlayNotificationRequest("com.whatsapp", "WhatsAp", "wHATSaPP", this));
+ // queueWrite(new PlayNotificationRequest("twitterrrr", "Twitterr", "tWITTER", this));
syncSettings();
- queueWrite(new ButtonConfigurationPutRequest(this));
+ overwriteButtons(null);
queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZED));
}
- private void negotiateSymmetricKey(){
+ private void negotiateSymmetricKey() {
queueWrite(new VerifyPrivateKeyRequest(
this.getSecretKey(),
this
));
}
+ @Override
+ public void setTime() {
+ long millis = System.currentTimeMillis();
+ TimeZone zone = new GregorianCalendar().getTimeZone();
+
+ queueWrite(
+ new nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.configuration.ConfigurationPutRequest(
+ new ConfigurationPutRequest.TimeConfigItem(
+ (int) (millis / 1000 + getDeviceSupport().getTimeOffset() * 60),
+ (short) (millis % 1000),
+ (short) ((zone.getRawOffset() + (zone.inDaylightTime(new Date()) ? 1 : 0)) / 60000)
+ ),
+ this), false
+ );
+ }
+
+ private void setBackgroundImages(Image background, Image[] complications){
+ background.setAngle(0);
+ background.setDistance(0);
+ background.setIndexZ(0);
+
+ queueWrite(new ImagesPutRequest(new Image[]{background}, this));
+ }
+
@Override
public void onFetchActivityData() {
syncSettings();
}
- private void syncSettings(){
+ private void syncSettings() {
negotiateSymmetricKey();
queueWrite(new ConfigurationGetRequest(this));
+
+ setTime();
}
@Override
@@ -109,7 +147,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
public boolean playRawNotification(NotificationSpec notificationSpec) {
String sender = notificationSpec.sender;
- if(sender == null) sender = notificationSpec.sourceName;
+ if (sender == null) sender = notificationSpec.sourceName;
queueWrite(new PlayNotificationRequest("generic", notificationSpec.sourceName, notificationSpec.body, this));
return true;
}
@@ -138,6 +176,24 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
return watchRandomNumber;
}
+ @Override
+ public void overwriteButtons(String jsonConfigString) {
+ try {
+ JSONArray jsonArray = new JSONArray(
+ GBApplication.getPrefs().getString(HRConfigActivity.CONFIG_KEY_Q_ACTIONS, "[]")
+ );
+ String[] menuItems = new String[jsonArray.length()];
+ for(int i = 0; i < jsonArray.length(); i++) menuItems[i] = jsonArray.getString(i);
+
+ queueWrite(new ButtonConfigurationPutRequest(
+ menuItems,
+ this
+ ));
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
@Override
protected void handleBackgroundCharacteristic(BluetoothGattCharacteristic characteristic) {
super.handleBackgroundCharacteristic(characteristic);
@@ -149,12 +205,29 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
try {
JSONObject requestJson = new JSONObject(new String(value, 3, value.length - 3));
- String action = requestJson.getJSONObject("commuteApp._.config.commute_info")
+ String action = requestJson.getJSONObject("req").getJSONObject("commuteApp._.config.commute_info")
.getString("dest");
+ String startStop = requestJson.getJSONObject("req").getJSONObject("commuteApp._.config.commute_info")
+ .getString("action");
+ if(startStop.equals("stop")){
+ // overwriteButtons(null);
+ return;
+ }
+
+ queueWrite(new SetCommuteMenuMessage("Anfrage wird weitergeleitet...", false, this));
+
+ Intent menuIntent = new Intent(QHybridSupport.QHYBRID_EVENT_COMMUTE_MENU);
+ menuIntent.putExtra("EXTRA_ACTION", action);
+ getContext().sendBroadcast(menuIntent);
} catch (JSONException e) {
e.printStackTrace();
}
}
+
+ @Override
+ public void setCommuteMenuMessage(String message, boolean finished) {
+ queueWrite(new SetCommuteMenuMessage(message, finished, this));
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileCloseRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileCloseRequest.java
index 7bb3e292f..656e81bff 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileCloseRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileCloseRequest.java
@@ -24,6 +24,7 @@ import java.util.UUID;
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_hr.file.ResultCode;
public class FileCloseRequest extends FossilRequest {
private boolean isFinished = false;
@@ -64,7 +65,7 @@ public class FileCloseRequest extends FossilRequest {
byte status = buffer.get(3);
- if(status != 0) throw new RuntimeException("wrong response status");
+ if(status != 0) throw new RuntimeException("wrong response status: " + ResultCode.fromCode(status) + " (" + status + ")");
this.isFinished = true;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileGetRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileGetRequest.java
index 87c87165a..1c23c5400 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileGetRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileGetRequest.java
@@ -28,6 +28,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSuppo
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
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_hr.file.ResultCode;
public abstract class FileGetRequest extends FossilRequest {
private short handle;
@@ -75,7 +76,7 @@ public abstract class FileGetRequest extends FossilRequest {
byte status = buffer.get(3);
if(status != 0){
- throw new RuntimeException("FileGet error: " + status);
+ throw new RuntimeException("FileGet error: " + ResultCode.fromCode(status) + " (" + status + ")");
}
if(this.handle != handle){
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileLookupRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileLookupRequest.java
index c1e14dcd1..b52a135bd 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileLookupRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileLookupRequest.java
@@ -28,6 +28,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSuppo
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
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_hr.file.ResultCode;
public class FileLookupRequest extends FossilRequest {
private short handle = -1;
@@ -82,7 +83,7 @@ public class FileLookupRequest extends FossilRequest {
byte status = buffer.get(3);
if(status != 0){
- throw new RuntimeException("file lookup error: " + status);
+ throw new RuntimeException("file lookup error: " + ResultCode.fromCode(status) + " (" + status + ")");
}
if(this.handle != handle){
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FilePutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FilePutRequest.java
index aeeaaf09f..fa61e728c 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FilePutRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FilePutRequest.java
@@ -27,6 +27,7 @@ import java.util.zip.CRC32;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.ResultCode;
import nodomain.freeyourgadget.gadgetbridge.util.CRC32C;
public class FilePutRequest extends FossilRequest {
@@ -100,7 +101,7 @@ public class FilePutRequest extends FossilRequest {
byte status = value[3];
if (status != 0) {
- throw new RuntimeException("upload status: " + status);
+ throw new RuntimeException("upload status: " + ResultCode.fromCode(status) + " (" + status + ")");
}
if (handle != this.handle) {
@@ -146,7 +147,7 @@ public class FilePutRequest extends FossilRequest {
if (status != 0) {
onFilePut(false);
- throw new RuntimeException("wrong closing status: " + status);
+ throw new RuntimeException("wrong closing status: " + ResultCode.fromCode(status) + " (" + status + ")");
}
this.state = UploadState.UPLOADED;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileVerifyRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileVerifyRequest.java
index 2f4cc032f..4bbcb2dd0 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileVerifyRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil/file/FileVerifyRequest.java
@@ -22,6 +22,7 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.ResultCode;
public class FileVerifyRequest extends FossilRequest {
private boolean isFinished = false;
@@ -64,7 +65,7 @@ public class FileVerifyRequest extends FossilRequest {
byte status = buffer.get(3);
- if(status != 0) throw new RuntimeException("wrong response status");
+ if(status != 0) throw new RuntimeException("wrong response status: " + ResultCode.fromCode(status) + " (" + status + ")");
this.isFinished = true;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/VerifyPrivateKeyRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/VerifyPrivateKeyRequest.java
index ed9a1f0cc..3e3a3b628 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/VerifyPrivateKeyRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/authentication/VerifyPrivateKeyRequest.java
@@ -19,6 +19,7 @@ import javax.crypto.spec.SecretKeySpec;
import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr.FossilHRWatchAdapter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.ResultCode;
public class VerifyPrivateKeyRequest extends FossilRequest {
private final FossilHRWatchAdapter adapter;
@@ -29,7 +30,6 @@ public class VerifyPrivateKeyRequest extends FossilRequest {
this.adapter = adapter;
this.key = key;
- adapter.setPhoneRandomNumber(randomPhoneNumber);
}
@Override
@@ -62,6 +62,7 @@ public class VerifyPrivateKeyRequest extends FossilRequest {
System.arraycopy(result, 0, watchRandomNumber, 0, 8);
adapter.setWatchRandomNumber(watchRandomNumber);
+ adapter.setPhoneRandomNumber(randomPhoneNumber);
cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}));
@@ -81,7 +82,8 @@ public class VerifyPrivateKeyRequest extends FossilRequest {
throw new RuntimeException(e);
}
} else if (value[1] == 2) {
- if (value[2] != 0) throw new RuntimeException("Authentication error: " + value[2]);
+ if (value[2] != 0) throw new RuntimeException("Authentication error: " + ResultCode.fromCode(value[2]) + " (" + value[2] + ")");
+
this.isFinished = true;
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/buttons/ButtonConfigurationPutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/buttons/ButtonConfigurationPutRequest.java
index dd09a0262..043a63eeb 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/buttons/ButtonConfigurationPutRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/buttons/ButtonConfigurationPutRequest.java
@@ -11,37 +11,16 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class ButtonConfigurationPutRequest extends JsonPutRequest {
- public ButtonConfigurationPutRequest(FossilWatchAdapter adapter) {
- super((short) 0x0500, createObject(), adapter);
+ public ButtonConfigurationPutRequest(String[] menuItems, FossilWatchAdapter adapter) {
+ super((short) 0x0500, createObject(menuItems), adapter);
}
- private static JSONObject createObject() {
+ private static JSONObject createObject(String[] menuItems) {
try {
return new JSONObject()
.put("push", new JSONObject()
.put("set", new JSONObject()
- .put("commuteApp._.config.destinations", new JSONArray()
- .put("LAMP 1")
- .put("LAMP 3")
- .put("LAMP 4")
- .put("LAMP 5")
- .put("LAMP 6")
- .put("LAMP 7")
- .put("LAMP 8")
- .put("LAMP 9")
- .put("LAMP 10")
- .put("LAMP 11")
- .put("LAMP 12")
- .put("LAMP 13")
- .put("LAMP 14")
- .put("LAMP 8")
- .put("LAMP 9")
- .put("LAMP 10")
- .put("LAMP 11")
- .put("LAMP 12")
- .put("LAMP 13")
- .put("LAMP 14")
- )
+ .put("commuteApp._.config.destinations", new JSONArray(menuItems))
.put("master._.config.buttons", new JSONArray()
.put(new JSONObject()
.put("name", "commuteApp")
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationPutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationPutRequest.java
new file mode 100644
index 000000000..e9b3f2ef7
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationPutRequest.java
@@ -0,0 +1,58 @@
+/* Copyright (C) 2019 Daniel Dakhno
+
+ 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.service.devices.qhybrid.requests.fossil_hr.configuration;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr.FossilHRWatchAdapter;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.FileEncryptedPutRequest;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.configuration.ConfigurationPutRequest.ConfigItem;
+
+public class ConfigurationPutRequest extends FileEncryptedPutRequest {
+ private static HashMap> itemsById = new HashMap<>();
+
+ public ConfigurationPutRequest(ConfigItem item, FossilHRWatchAdapter adapter) {
+ super((short) 0x0800, createFileContent(new ConfigItem[]{item}), adapter);
+ }
+
+ public ConfigurationPutRequest(ConfigItem[] items, FossilHRWatchAdapter adapter) {
+ super((short) 0x0800, createFileContent(items), adapter);
+ }
+
+ private static byte[] createFileContent(ConfigItem[] items) {
+ int overallSize = 0;
+ for(ConfigItem item : items){
+ overallSize += item.getItemSize() + 3;
+ }
+ ByteBuffer buffer = ByteBuffer.allocate(overallSize);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ for(ConfigItem item : items){
+ buffer.putShort(item.getId());
+ buffer.put((byte) item.getItemSize());
+ buffer.put(item.getContent());
+ }
+
+ return buffer.array();
+ }
+}
+
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FileEncryptedGetRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FileEncryptedGetRequest.java
index fd95d8013..edba4a4f0 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FileEncryptedGetRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FileEncryptedGetRequest.java
@@ -86,7 +86,7 @@ public abstract class FileEncryptedGetRequest extends FossilRequest {
byte status = buffer.get(3);
if(status != 0){
- throw new RuntimeException("FileGet error: " + status);
+ throw new RuntimeException("FileGet error: " + ResultCode.fromCode(status) + " (" + status + ")");
}
if(this.handle != handle){
@@ -111,7 +111,7 @@ public abstract class FileEncryptedGetRequest extends FossilRequest {
int crcExpected = buffer.getInt(8);
if((int) crc.getValue() != crcExpected){
- throw new RuntimeException("handle: " + handle + " expected: " + this.handle);
+ throw new RuntimeException("crc: " + crc.getValue() + " expected: " + crcExpected);
}
this.handleFileData(this.fileData);
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FileEncryptedPutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FileEncryptedPutRequest.java
new file mode 100644
index 000000000..57b7078d1
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FileEncryptedPutRequest.java
@@ -0,0 +1,271 @@
+/* Copyright (C) 2019 Daniel Dakhno
+
+ 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.service.devices.qhybrid.requests.fossil_hr.file;
+
+import android.bluetooth.BluetoothGattCharacteristic;
+import android.widget.Toast;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.UUID;
+import java.util.zip.CRC32;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import nodomain.freeyourgadget.gadgetbridge.service.btle.TransactionBuilder;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil_hr.FossilHRWatchAdapter;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.FossilRequest;
+import nodomain.freeyourgadget.gadgetbridge.util.CRC32C;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
+
+public class FileEncryptedPutRequest extends FossilRequest {
+ public enum UploadState {INITIALIZED, UPLOADING, CLOSING, UPLOADED}
+
+ public UploadState state;
+
+ private ArrayList packets = new ArrayList<>();
+
+ private short handle;
+
+ private FossilHRWatchAdapter adapter;
+
+ private byte[] file;
+
+ private int fullCRC;
+
+ public FileEncryptedPutRequest(short handle, byte[] file, FossilHRWatchAdapter adapter) {
+ this.handle = handle;
+ this.adapter = adapter;
+
+ int fileLength = file.length + 16;
+ ByteBuffer buffer = this.createBuffer();
+ buffer.putShort(1, handle);
+ buffer.putInt(3, 0);
+ buffer.putInt(7, fileLength);
+ buffer.putInt(11, fileLength);
+
+ this.data = buffer.array();
+
+ this.file = file;
+
+ state = UploadState.INITIALIZED;
+ }
+
+ public short getHandle() {
+ return handle;
+ }
+
+ @Override
+ public void handleResponse(BluetoothGattCharacteristic characteristic) {
+ byte[] value = characteristic.getValue();
+ if (characteristic.getUuid().toString().equals("3dda0003-957f-7d4a-34a6-74696673696d")) {
+ int responseType = value[0] & 0x0F;
+ log("response: " + responseType);
+ switch (responseType) {
+ case 3: {
+ if (value.length != 5 || (value[0] & 0x0F) != 3) {
+ throw new RuntimeException("wrong answer header");
+ }
+ state = UploadState.UPLOADING;
+
+ TransactionBuilder transactionBuilder = new TransactionBuilder("file upload");
+ BluetoothGattCharacteristic uploadCharacteristic = adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0004-957f-7d4a-34a6-74696673696d"));
+
+ this.prepareFilePackets(this.file);
+
+ SecretKeySpec keySpec = new SecretKeySpec(this.adapter.getSecretKey(), "AES");
+ try {
+ Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
+
+ byte[] fileIV = new byte[16];
+
+
+ byte[] phoneRandomNumber = adapter.getPhoneRandomNumber();
+ byte[] watchRandomNumber = adapter.getWatchRandomNumber();
+
+ System.arraycopy(phoneRandomNumber, 0, fileIV, 2, 6);
+ System.arraycopy(watchRandomNumber, 0, fileIV, 9, 7);
+
+ fileIV[7]++;
+
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(fileIV));
+
+ for (byte[] packet : packets) {
+ byte[] result = cipher.doFinal(packet);
+ transactionBuilder.write(uploadCharacteristic, result);
+ }
+ }catch (Exception e){
+ GB.toast("error encrypting file", Toast.LENGTH_LONG, GB.ERROR, e);
+ }
+
+ transactionBuilder.queue(adapter.getDeviceSupport().getQueue());
+ break;
+ }
+ case 8: {
+ if (value.length == 4) return;
+ ByteBuffer buffer = ByteBuffer.wrap(value);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ short handle = buffer.getShort(1);
+ int crc = buffer.getInt(8);
+ byte status = value[3];
+
+ if (status != 0) {
+ throw new RuntimeException("upload status: " + ResultCode.fromCode(status) + " (" + status + ")");
+ }
+
+ if (handle != this.handle) {
+ throw new RuntimeException("wrong response handle");
+ }
+
+ if (crc != this.fullCRC) {
+ throw new RuntimeException("file upload exception: wrong crc");
+ }
+
+
+ ByteBuffer buffer2 = ByteBuffer.allocate(3);
+ buffer2.order(ByteOrder.LITTLE_ENDIAN);
+ buffer2.put((byte) 4);
+ buffer2.putShort(this.handle);
+
+ new TransactionBuilder("file close")
+ .write(
+ adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d")),
+ buffer2.array()
+ )
+ .queue(adapter.getDeviceSupport().getQueue());
+
+ this.state = UploadState.CLOSING;
+ break;
+ }
+ case 4: {
+ if (value.length == 9) return;
+ if (value.length != 4 || (value[0] & 0x0F) != 4) {
+ throw new RuntimeException("wrong file closing header");
+ }
+ ByteBuffer buffer = ByteBuffer.wrap(value);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ short handle = buffer.getShort(1);
+
+ if (handle != this.handle) {
+ onFilePut(false);
+ throw new RuntimeException("wrong file closing handle");
+ }
+
+ byte status = buffer.get(3);
+
+ if (status != 0) {
+ onFilePut(false);
+ throw new RuntimeException("wrong closing status: " + ResultCode.fromCode(status) + " (" + status + ")");
+ }
+
+ this.state = UploadState.UPLOADED;
+
+ onFilePut(true);
+
+ log("uploaded file");
+
+ break;
+ }
+ case 9: {
+ this.onFilePut(false);
+ throw new RuntimeException("file put timeout");
+ /*timeout = true;
+ ByteBuffer buffer2 = ByteBuffer.allocate(3);
+ buffer2.order(ByteOrder.LITTLE_ENDIAN);
+ buffer2.put((byte) 4);
+ buffer2.putShort(this.handle);
+
+ new TransactionBuilder("file close")
+ .write(
+ adapter.getDeviceSupport().getCharacteristic(UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d")),
+ buffer2.array()
+ )
+ .queue(adapter.getDeviceSupport().getQueue());
+
+ this.state = UploadState.CLOSING;
+ break;*/
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isFinished() {
+ return this.state == UploadState.UPLOADED;
+ }
+
+ private void prepareFilePackets(byte[] file) {
+ int maxPacketSize = adapter.getMTU() - 4;
+
+ ByteBuffer buffer = ByteBuffer.allocate(file.length + 12 + 4);
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+
+ buffer.putShort(handle);
+ buffer.put((byte) 2);
+ buffer.put((byte) 0);
+ buffer.putInt(0);
+ buffer.putInt(file.length);
+
+ buffer.put(file);
+
+ CRC32C crc = new CRC32C();
+
+ crc.update(file,0,file.length);
+ buffer.putInt((int) crc.getValue());
+
+ byte[] data = buffer.array();
+
+ CRC32 fullCRC = new CRC32();
+
+ fullCRC.update(data);
+ this.fullCRC = (int) fullCRC.getValue();
+
+ int packetCount = (int) Math.ceil(data.length / (float) maxPacketSize);
+
+ for (int i = 0; i < packetCount; i++) {
+ int currentPacketLength = Math.min(maxPacketSize, data.length - i * maxPacketSize);
+ byte[] packet = new byte[currentPacketLength + 1];
+ packet[0] = (byte) i;
+ System.arraycopy(data, i * maxPacketSize, packet, 1, currentPacketLength);
+
+ packets.add(packet);
+ }
+ }
+
+ public void onFilePut(boolean success) {
+ }
+
+ @Override
+ public byte[] getStartSequence() {
+ return new byte[]{0x03};
+ }
+
+ @Override
+ public int getPayloadLength() {
+ return 15;
+ }
+
+ @Override
+ public UUID getRequestUUID() {
+ return UUID.fromString("3dda0003-957f-7d4a-34a6-74696673696d");
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FilePutRawRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FilePutRawRequest.java
index abdf8ad91..99c713a5f 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FilePutRawRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FilePutRawRequest.java
@@ -87,7 +87,7 @@ public class FilePutRawRequest extends FossilRequest {
byte status = value[3];
if (status != 0) {
- throw new RuntimeException("upload status: " + status);
+ throw new RuntimeException("upload status: " + ResultCode.fromCode(status) + " (" + status + ")");
}
if (handle != this.handle) {
@@ -133,7 +133,7 @@ public class FilePutRawRequest extends FossilRequest {
if (status != 0) {
onFilePut(false);
- throw new RuntimeException("wrong closing status: " + status);
+ throw new RuntimeException("wrong closing status: " + ResultCode.fromCode(status) + " (" + status + ")");
}
this.state = UploadState.UPLOADED;
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/ResultCode.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/ResultCode.java
new file mode 100644
index 000000000..3003d8149
--- /dev/null
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/ResultCode.java
@@ -0,0 +1,39 @@
+package nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file;
+
+public enum ResultCode {
+ SUCCESS(0),
+ INVALID_OPERATION_DATA(1),
+ OPERATION_IN_PROGRESS(2),
+ MISS_PACKET(3),
+ SOCKET_BUSY(4),
+ VERIFICATION_FAIL(5),
+ OVERFLOW(6),
+ SIZE_OVER_LIMIT(7),
+ FIRMWARE_INTERNAL_ERROR(128),
+ FIRMWARE_INTERNAL_ERROR_NOT_OPEN(129),
+ FIRMWARE_INTERNAL_ERROR_ACCESS_ERROR(130),
+ FIRMWARE_INTERNAL_ERROR_NOT_FOUND(131),
+ FIRMWARE_INTERNAL_ERROR_NOT_VALID(132),
+ FIRMWARE_INTERNAL_ERROR_ALREADY_CREATE(133),
+ FIRMWARE_INTERNAL_ERROR_NOT_ENOUGH_MEMORY(134),
+ FIRMWARE_INTERNAL_ERROR_NOT_IMPLEMENTED(135),
+ FIRMWARE_INTERNAL_ERROR_NOT_SUPPORT(136),
+ FIRMWARE_INTERNAL_ERROR_SOCKET_BUSY(137),
+ FIRMWARE_INTERNAL_ERROR_SOCKET_ALREADY_OPEN(138),
+ FIRMWARE_INTERNAL_ERROR_INPUT_DATA_INVALID(139),
+ FIRMWARE_INTERNAL_NOT_AUTHENTICATE(140),
+ FIRMWARE_INTERNAL_SIZE_OVER_LIMIT(141),
+ UNKNOWN(-1);
+ int code;
+
+ ResultCode(int code) {
+ this.code = code;
+ }
+
+ public static ResultCode fromCode(int code){
+ for (ResultCode resultCode : ResultCode.values()){
+ if(resultCode.code == code) return resultCode;
+ }
+ return UNKNOWN;
+ }
+}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/image/Image.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/image/Image.java
index 0429874e5..1127748d8 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/image/Image.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/image/Image.java
@@ -37,4 +37,36 @@ public class Image {
}
return null;
}
+
+ public int getAngle() {
+ return angle;
+ }
+
+ public void setAngle(int angle) {
+ this.angle = angle;
+ }
+
+ public int getDistance() {
+ return distance;
+ }
+
+ public void setDistance(int distance) {
+ this.distance = distance;
+ }
+
+ public int getIndexZ() {
+ return indexZ;
+ }
+
+ public void setIndexZ(int indexZ) {
+ this.indexZ = indexZ;
+ }
+
+ public String getImageFile() {
+ return imageFile;
+ }
+
+ public void setImageFile(String imageFile) {
+ this.imageFile = imageFile;
+ }
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/image/ImagesPutRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/image/ImagesPutRequest.java
index bf7cdbff2..810bc9b50 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/image/ImagesPutRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/image/ImagesPutRequest.java
@@ -9,7 +9,7 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos
public class ImagesPutRequest extends JsonPutRequest {
public ImagesPutRequest(Image[] images, FossilWatchAdapter adapter) {
- super((short) 0x0501, prepareObject(images), adapter);
+ super((short) 0x0500, prepareObject(images), adapter);
}
private static JSONObject prepareObject(Image[] images){
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/misfit/DownloadFileRequest.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/misfit/DownloadFileRequest.java
index af6f78bb5..e9f6118d0 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/misfit/DownloadFileRequest.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/misfit/DownloadFileRequest.java
@@ -22,6 +22,8 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.CRC32;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.file.ResultCode;
+
public class DownloadFileRequest extends FileRequest {
ByteBuffer buffer = null;
public byte[] file = null;
@@ -69,7 +71,7 @@ public class DownloadFileRequest extends FileRequest {
this.status = buffer1.get(3);
short realHandle = buffer1.getShort(1);
if(status != 0){
- log("wrong status: " + status);
+ log("wrong status: " + ResultCode.fromCode(status) + " (" + status + ")");
}else if(realHandle != fileHandle){
log("wrong handle: " + realHandle);
completed = true;
diff --git a/app/src/main/res/layout/activity_qhybrid_hr_settings.xml b/app/src/main/res/layout/activity_qhybrid_hr_settings.xml
new file mode 100644
index 000000000..16fff6026
--- /dev/null
+++ b/app/src/main/res/layout/activity_qhybrid_hr_settings.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file