diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FileManagementActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FileManagementActivity.java
index 755a6bcd3..ebae3ba97 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FileManagementActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/FileManagementActivity.java
@@ -16,6 +16,7 @@
along with this program. If not, see . */
package nodomain.freeyourgadget.gadgetbridge.devices.qhybrid;
+import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -23,6 +24,7 @@ import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
+import android.widget.CompoundButton;
import android.widget.ExpandableListView;
import android.widget.Spinner;
import android.widget.Switch;
@@ -31,6 +33,9 @@ import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
import java.io.Serializable;
import java.nio.file.attribute.FileTime;
@@ -39,12 +44,16 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.file.FileHandle;
+import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class FileManagementActivity extends AbstractGBActivity implements View.OnClickListener {
private final int REQUEST_CODE_PICK_UPLOAD_FILE = 0;
private Spinner fileTypesSpinner;
private Switch encryptedFile;
+ private boolean generateFileHeader = false;
+
+ private boolean warningDisplayed = false;
BroadcastReceiver fileResultReceiver = new BroadcastReceiver() {
@Override
@@ -100,6 +109,15 @@ public class FileManagementActivity extends AbstractGBActivity implements View.O
findViewById(R.id.qhybrid_button_download_file).setOnClickListener(this);
findViewById(R.id.qhybrid_button_upload_file).setOnClickListener(this);
+
+ ((Switch) findViewById(R.id.sqhybrid_switch_generate_file_header)).setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ generateFileHeader = isChecked;
+ fileTypesSpinner.setClickable(isChecked);
+ fileTypesSpinner.setAlpha(isChecked ? 1f : 0.2f);
+ }
+ });
}
@Override
@@ -108,13 +126,41 @@ public class FileManagementActivity extends AbstractGBActivity implements View.O
if(requestCode != REQUEST_CODE_PICK_UPLOAD_FILE) return;
if(resultCode != RESULT_OK) return;
- Intent callIntent = new Intent(QHybridSupport.QHYBRID_COMMAND_UPLOAD_FILE);
- callIntent.putExtra("EXTRA_HANDLE", (FileHandle) fileTypesSpinner.getSelectedItem());
- callIntent.putExtra("EXTRA_ENCRYPTED", encryptedFile.isChecked());
- callIntent.putExtra("EXTRA_PATH", data.getData().getPath());
- // callIntent.setData(data.getData());
+ String path = data.getData().getPath();
- LocalBroadcastManager.getInstance(this).sendBroadcast(callIntent);
+ try {
+ if(!warningDisplayed) {
+ FileInputStream fis = new FileInputStream(path);
+ short fileHandle = (short) (fis.read() | (fis.read() << 8));
+ fis.close();
+
+ boolean handleFound = FileHandle.fromHandle(fileHandle) != null;
+ if (handleFound == generateFileHeader) {
+ warningDisplayed = true;
+ String text = "File seems to contain file handle. Are you sure you want to generate a potentially already existing header?";
+ if(!handleFound) text = "File does not start with a known handle. Are you sure the header is already generated?";
+ text += " Repeat to continue anyway.";
+ new AlertDialog.Builder(this)
+ .setTitle("warning")
+ .setMessage(text)
+ .setPositiveButton("ok", null)
+ .show();
+ return;
+ }
+ }
+
+ Intent callIntent = new Intent(QHybridSupport.QHYBRID_COMMAND_UPLOAD_FILE);
+ callIntent.putExtra("EXTRA_HANDLE", (FileHandle) fileTypesSpinner.getSelectedItem());
+ callIntent.putExtra("EXTRA_ENCRYPTED", encryptedFile.isChecked());
+ callIntent.putExtra("EXTRA_GENERATE_FILE_HEADER", generateFileHeader);
+ callIntent.putExtra("EXTRA_PATH", data.getData().getPath());
+ // callIntent.setData(data.getData());
+
+ LocalBroadcastManager.getInstance(this).sendBroadcast(callIntent);
+ } catch (IOException e) {
+ e.printStackTrace();
+ GB.toast("cannot open file", Toast.LENGTH_LONG, GB.ERROR);
+ }
}
@Override
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java
index 39a517c66..50deff187 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java
@@ -283,15 +283,7 @@ public class QHybridSupport extends QHybridBaseSupport {
break;
}
case QHYBRID_COMMAND_UPLOAD_FILE:{
- Object handleObject = intent.getSerializableExtra("EXTRA_HANDLE");
- if(handleObject == null)return;
- if(handleObject instanceof String){
- handleObject = FileHandle.fromName((String)handleObject);
- }
- if(!(handleObject instanceof FileHandle)) return;
- FileHandle handle = (FileHandle) handleObject;
- String filePath = intent.getStringExtra("EXTRA_PATH");
- watchAdapter.uploadFile(handle, filePath, intent.getBooleanExtra("EXTRA_ENCRYPTED", false));
+ handleFileUploadIntent(intent);
break;
}
}
@@ -369,15 +361,7 @@ public class QHybridSupport extends QHybridBaseSupport {
break;
}
case QHYBRID_COMMAND_UPLOAD_FILE:{
- Object handleObject = intent.getSerializableExtra("EXTRA_HANDLE");
- if(handleObject == null)return;
- if(handleObject instanceof String){
- handleObject = FileHandle.fromName((String)handleObject);
- }
- if(!(handleObject instanceof FileHandle)) return;
- FileHandle handle = (FileHandle) handleObject;
- String filePath = intent.getStringExtra("EXTRA_PATH");
- watchAdapter.uploadFile(handle, filePath, intent.getBooleanExtra("EXTRA_ENCRYPTED", false));
+ handleFileUploadIntent(intent);
break;
}
}
@@ -386,6 +370,23 @@ public class QHybridSupport extends QHybridBaseSupport {
GBApplication.getContext().registerReceiver(globalCommandReceiver, globalFilter);
}
+ private void handleFileUploadIntent(Intent intent){
+ boolean generateHeader = intent.getBooleanExtra("EXTRA_GENERATE_FILE_HEADER", false);
+ String filePath = intent.getStringExtra("EXTRA_PATH");
+ if(!generateHeader){
+ watchAdapter.uploadFileIncludesHeader(filePath);
+ return;
+ }
+ Object handleObject = intent.getSerializableExtra("EXTRA_HANDLE");
+ if(handleObject == null)return;
+ if(handleObject instanceof String){
+ handleObject = FileHandle.fromName((String)handleObject);
+ }
+ if(!(handleObject instanceof FileHandle)) return;
+ FileHandle handle = (FileHandle) handleObject;
+ watchAdapter.uploadFileGenerateHeader(handle, filePath, intent.getBooleanExtra("EXTRA_ENCRYPTED", false));
+ }
+
@Override
public void onSetCallState(CallSpec callSpec) {
super.onSetCallState(callSpec);
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 ee7c62908..737958119 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
@@ -145,7 +145,10 @@ public abstract class WatchAdapter {
public void downloadFile(FileHandle handle, boolean fileIsEncrypted) {
}
- public void uploadFile(FileHandle handle, String filePath, boolean fileIsEncrypted) {
+ public void uploadFileGenerateHeader(FileHandle handle, String filePath, boolean fileIsEncrypted) {
+ }
+
+ public void uploadFileIncludesHeader(String filePath){
}
public void factoryReset() {
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 cf2af3460..7c6c7e3cd 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
@@ -49,6 +49,7 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -71,6 +72,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NotificationSpec;
import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
+import nodomain.freeyourgadget.gadgetbridge.service.btle.Transaction;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.adapter.fossil.FossilWatchAdapter;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.file.FileHandle;
@@ -83,8 +85,11 @@ import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fos
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileGetRawRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FileLookupRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRawRequest;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.file.FilePutRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.PlayCallNotificationRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil.notification.PlayTextNotificationRequest;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.application.ApplicationInformation;
+import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.application.ApplicationsListRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.async.ConfirmAppStatusRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.authentication.VerifyPrivateKeyRequest;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.requests.fossil_hr.buttons.ButtonConfiguration;
@@ -141,6 +146,8 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
HashMap appIconCache = new HashMap<>();
String lastPostedApp = null;
+ List installedApplications;
+
enum CONNECTION_MODE {
NOT_INITIALIZED,
AUTHENTICATED,
@@ -157,6 +164,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
queueWrite(new RequestMtuRequest(512));
}
+ listApplications();
getDeviceInfos();
}
@@ -173,6 +181,15 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
negotiateSymmetricKey();
}
+ private void listApplications(){
+ queueWrite(new ApplicationsListRequest(this) {
+ @Override
+ public void handleApplicationsList(List applications) {
+ installedApplications = applications;
+ }
+ });
+ }
+
private void initializeAfterAuthentication(boolean authenticated) {
queueWrite(new SetDeviceStateRequest(GBDevice.State.INITIALIZING));
@@ -186,10 +203,9 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
setVibrationStrength();
syncSettings();
setTime();
- overwriteButtons(null);
}
-
+ overwriteButtons(null);
loadBackground();
loadWidgets();
// renderWidgets();
@@ -568,7 +584,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
}
@Override
- public void uploadFile(FileHandle handle, String filePath, boolean fileIsEncrypted) {
+ public void uploadFileGenerateHeader(FileHandle handle, String filePath, boolean fileIsEncrypted) {
final Intent resultIntent = new Intent(QHybridSupport.QHYBRID_ACTION_UPLOADED_FILE);
byte[] fileData;
@@ -584,7 +600,7 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
return;
}
- queueWrite(new FilePutRawRequest(handle, fileData, this) {
+ queueWrite(new FilePutRequest(handle, fileData, this) {
@Override
public void onFilePut(boolean success) {
resultIntent.putExtra("EXTRA_SUCCESS", success);
@@ -593,6 +609,38 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
});
}
+ @Override
+ public void uploadFileIncludesHeader(String filePath) {
+ final Intent resultIntent = new Intent(QHybridSupport.QHYBRID_ACTION_UPLOADED_FILE);
+ byte[] fileData;
+
+ try {
+ FileInputStream fis = new FileInputStream(filePath);
+ fileData = new byte[fis.available()];
+ fis.read(fileData);
+ fis.close();
+
+ short handleBytes = (short)(fileData[0] & 0xFF | ((fileData[1] & 0xFF) << 8));
+ FileHandle handle = FileHandle.fromHandle(handleBytes);
+
+ if(handle == null){
+ throw new RuntimeException("unknown handle");
+ }
+
+ queueWrite(new FilePutRawRequest(handle, fileData, this) {
+ @Override
+ public void onFilePut(boolean success) {
+ resultIntent.putExtra("EXTRA_SUCCESS", success);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(resultIntent);
+ }
+ });
+ } catch (Exception e) {
+ e.printStackTrace();
+ resultIntent.putExtra("EXTRA_SUCCESS", false);
+ LocalBroadcastManager.getInstance(getContext()).sendBroadcast(resultIntent);
+ }
+ }
+
@Override
public void downloadFile(final FileHandle handle, boolean fileIsEncrypted) {
if (fileIsEncrypted) {
@@ -1081,10 +1129,6 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
@Override
public void overwriteButtons(String jsonConfigString) {
- if (connectionMode == CONNECTION_MODE.NOT_AUTHENTICATED) {
- GB.toast("not available in unauthenticated mode", Toast.LENGTH_LONG, GB.ERROR);
- return;
- }
try {
JSONArray jsonArray = new JSONArray(
GBApplication.getPrefs().getString(HRConfigActivity.CONFIG_KEY_Q_ACTIONS, "[]")
@@ -1104,24 +1148,31 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
if (version.compareTo(new Version("1.0.2.19")) == -1)
singlePressEvent = "single_click";
}
+ ArrayList configs = new ArrayList<>(5);
+ configs.add(new ButtonConfiguration("top_" + singlePressEvent, prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_SHORT, "weatherApp")));
+ configs.add(new ButtonConfiguration("top_hold", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_LONG, "weatherApp")));
+ // configs.add(new ButtonConfiguration("top_double_click", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_DOUBLE, "weatherApp")));
+ configs.add(new ButtonConfiguration("middle_" + singlePressEvent, prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_SHORT, "commuteApp")));
+ // configs.add(new ButtonConfiguration("middle_hold", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_LONG, "commuteApp")));
+ // configs.add(new ButtonConfiguration("middle_double_click", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_DOUBLE, "commuteApp")));
+ configs.add(new ButtonConfiguration("bottom_" + singlePressEvent, prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_SHORT, "musicApp")));
+ configs.add(new ButtonConfiguration("bottom_hold", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_LONG, "musicApp")));
+ // configs.add(new ButtonConfiguration("bottom_double_click", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_DOUBLE, "musicApp")));
- ButtonConfiguration[] buttonConfigurations = new ButtonConfiguration[]{
- new ButtonConfiguration("top_" + singlePressEvent, prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_SHORT, "weatherApp")),
- new ButtonConfiguration("top_hold", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_LONG, "weatherApp")),
- new ButtonConfiguration("top_double_click", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_1_FUNCTION_DOUBLE, "weatherApp")),
+ // filter out all apps not installed on watch
+ outerLoop: for (ButtonConfiguration config : configs){
+ for(ApplicationInformation installedApp : installedApplications){
+ if(installedApp.getAppName().equals(config.getAction())){
+ continue outerLoop;
+ }
+ }
+ configs.remove(config);
+ }
- new ButtonConfiguration("middle_" + singlePressEvent, prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_SHORT, "commuteApp")),
- // new ButtonConfiguration("middle_hold", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_LONG, "commuteApp")),
- new ButtonConfiguration("middle_double_click", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_2_FUNCTION_DOUBLE, "commuteApp")),
-
- new ButtonConfiguration("bottom_" + singlePressEvent, prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_SHORT, "musicApp")),
- new ButtonConfiguration("bottom_hold", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_LONG, "musicApp")),
- new ButtonConfiguration("bottom_double_click", prefs.getString(DeviceSettingsPreferenceConst.PREF_BUTTON_3_FUNCTION_DOUBLE, "musicApp")),
- };
queueWrite(new ButtonConfigurationPutRequest(
menuItems,
- buttonConfigurations,
+ configs.toArray(new ButtonConfiguration[0]),
this
));
} catch (JSONException e) {
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/file/FileHandle.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/file/FileHandle.java
index d6054fecc..6ad4f3c09 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/file/FileHandle.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/file/FileHandle.java
@@ -57,6 +57,15 @@ public enum FileHandle {
return null;
}
+ public static FileHandle fromHandle(short handleBytes){
+ for(FileHandle handle : FileHandle.values()){
+ if(handle.getHandle() == handleBytes){
+ return handle;
+ }
+ }
+ return null;
+ }
+
public short getHandle(){
return (short)((handle << 8) | (subHandle));
}
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/buttons/ButtonConfiguration.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/buttons/ButtonConfiguration.java
index e320d6ebf..4dd6a31c0 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/buttons/ButtonConfiguration.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/requests/fossil_hr/buttons/ButtonConfiguration.java
@@ -28,6 +28,10 @@ public class ButtonConfiguration {
this.action = action;
}
+ public String getAction() {
+ return action;
+ }
+
public JSONObject toJsonObject(){
try {
return new JSONObject()
diff --git a/app/src/main/res/layout/activity_qhybrid_file_management.xml b/app/src/main/res/layout/activity_qhybrid_file_management.xml
index 6fd1fe1f1..2f9968e1d 100644
--- a/app/src/main/res/layout/activity_qhybrid_file_management.xml
+++ b/app/src/main/res/layout/activity_qhybrid_file_management.xml
@@ -10,7 +10,9 @@
+ android:id="@+id/qhybrid_file_types"
+ android:alpha="0.2"
+ android:clickable="false"/>
+
+
+
+
+