1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-11-26 03:46:49 +01:00

Fossil Hybrid: file handle can be passed to service as string

This commit is contained in:
Daniel Dakhno 2021-01-30 21:10:25 +01:00
parent 251aee8c74
commit ec77345632
8 changed files with 175 additions and 47 deletions

View File

@ -16,6 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
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

View File

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

View File

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

View File

@ -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<String, Bitmap> appIconCache = new HashMap<>();
String lastPostedApp = null;
List<ApplicationInformation> 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<ApplicationInformation> 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<ButtonConfiguration> 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) {

View File

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

View File

@ -28,6 +28,10 @@ public class ButtonConfiguration {
this.action = action;
}
public String getAction() {
return action;
}
public JSONObject toJsonObject(){
try {
return new JSONObject()

View File

@ -10,7 +10,9 @@
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/qhybrid_file_types" />
android:id="@+id/qhybrid_file_types"
android:alpha="0.2"
android:clickable="false"/>
<LinearLayout
android:layout_width="match_parent"
@ -24,6 +26,12 @@
android:text="encrypted file"
android:id="@+id/qhybrid_switch_encrypted_file" />
<Switch
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="generate file header"
android:id="@+id/sqhybrid_switch_generate_file_header" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -16,6 +16,7 @@
android:key="button_1_function_long"
android:summary="%s"
android:title="@string/pref_title_upper_button_function_long" />
<!-- supported FW does not support double click
<ListPreference
android:defaultValue="weatherApp"
android:entries="@array/pref_hybridhr_buttonfunctions"
@ -23,6 +24,7 @@
android:key="button_1_function_double"
android:summary="%s"
android:title="@string/pref_title_upper_button_function_double" />
-->
<ListPreference
android:defaultValue="commuteApp"
@ -31,6 +33,7 @@
android:key="button_2_function_short"
android:summary="%s"
android:title="@string/pref_title_middle_button_function_short" />
<!-- supported FW does not support double click
<ListPreference
android:defaultValue="commuteApp"
android:entries="@array/pref_hybridhr_buttonfunctions"
@ -38,6 +41,7 @@
android:key="button_2_function_double"
android:summary="%s"
android:title="@string/pref_title_middle_button_function_double" />
-->
<ListPreference
android:defaultValue="musicApp"
@ -53,6 +57,7 @@
android:key="button_3_function_long"
android:summary="%s"
android:title="@string/pref_title_lower_button_function_long" />
<!-- supported FW does not support double click
<ListPreference
android:defaultValue="musicApp"
android:entries="@array/pref_hybridhr_buttonfunctions"
@ -60,6 +65,7 @@
android:key="button_3_function_double"
android:summary="%s"
android:title="@string/pref_title_lower_button_function_double" />
-->
<SwitchPreference
android:defaultValue="false"