From c4d63a80e158a497cb83a584629abe010d24040c Mon Sep 17 00:00:00 2001 From: dakhnod Date: Tue, 31 Dec 2019 03:14:20 +0100 Subject: [PATCH] added menu handling and error status --- app/src/main/AndroidManifest.xml | 3 + .../devices/qhybrid/ConfigActivity.java | 2 +- .../devices/qhybrid/HRConfigActivity.java | 191 ++++++++++++ .../devices/qhybrid/QHybridCoordinator.java | 4 +- .../devices/qhybrid/adapter/WatchAdapter.java | 3 + .../adapter/fossil/FossilWatchAdapter.java | 2 +- .../fossil_hr/FossilHRWatchAdapter.java | 101 ++++++- .../fossil/file/FileCloseRequest.java | 3 +- .../requests/fossil/file/FileGetRequest.java | 3 +- .../fossil/file/FileLookupRequest.java | 3 +- .../requests/fossil/file/FilePutRequest.java | 5 +- .../fossil/file/FileVerifyRequest.java | 3 +- .../VerifyPrivateKeyRequest.java | 6 +- .../ButtonConfigurationPutRequest.java | 29 +- .../ConfigurationPutRequest.java | 58 ++++ .../file/FileEncryptedGetRequest.java | 4 +- .../file/FileEncryptedPutRequest.java | 271 ++++++++++++++++++ .../fossil_hr/file/FilePutRawRequest.java | 4 +- .../requests/fossil_hr/file/ResultCode.java | 39 +++ .../requests/fossil_hr/image/Image.java | 32 +++ .../fossil_hr/image/ImagesPutRequest.java | 2 +- .../requests/misfit/DownloadFileRequest.java | 4 +- .../layout/activity_qhybrid_hr_settings.xml | 40 +++ 23 files changed, 756 insertions(+), 56 deletions(-) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HRConfigActivity.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/configuration/ConfigurationPutRequest.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/FileEncryptedPutRequest.java create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/file/ResultCode.java create mode 100644 app/src/main/res/layout/activity_qhybrid_hr_settings.xml 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 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 @@ + + + + + +