From 73d67d4093cb9bd7eeb3d9c9ec6e3fe82f4daec1 Mon Sep 17 00:00:00 2001 From: Daniel Dakhno Date: Sat, 19 Aug 2023 20:48:53 +0000 Subject: [PATCH] Fossil/Skagen Hybrids: Embed menu_structure in watchface apps (#3245) This PR aims to optimize the method of synchronizing the menu_structure for the openSourceWatchface by making the menu_structure request from the watch obsolete. Instead, when a new menu_structure is sent to GB via the Intent `nodomain.freeyourgadget.gadgetbridge.Q_SET_MENU_STRUCTURE`, GB remembers that JSON. Next time a watchface is built, the JSON is embedded into that app package so the watch doesn't forget the structure. This requires a full GB rebuild to work properky. Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/3245 Co-authored-by: Daniel Dakhno Co-committed-by: Daniel Dakhno --- .../assets/fossil_hr/openSourceWatchface.bin | Bin 8734 -> 8438 bytes .../HybridHRWatchfaceDesignerActivity.java | 69 ++++++++++++++++-- .../qhybrid/HybridHRWatchfaceFactory.java | 7 ++ .../devices/qhybrid/QHybridConstants.java | 2 +- .../devices/qhybrid/QHybridSupport.java | 33 +++++++++ .../devices/qhybrid/adapter/WatchAdapter.java | 6 ++ .../fossil_hr/FossilHRWatchAdapter.java | 38 +++++++--- .../gadgetbridge/util/AndroidUtils.java | 16 ++++ .../activity_hybridhr_watchface_designer.xml | 38 +++++++++- app/src/main/res/values/strings.xml | 8 ++ external/fossil-hr-watchface | 2 +- 11 files changed, 196 insertions(+), 23 deletions(-) diff --git a/app/src/main/assets/fossil_hr/openSourceWatchface.bin b/app/src/main/assets/fossil_hr/openSourceWatchface.bin index 17987ff4ff46569cf79d4fb3dc61a8947ad1bc31..11c5bd35d3b960cca2b019de2c829fb8bd88b23d 100644 GIT binary patch delta 3659 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 H``dp2Y%w2Z diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java index ef481e5b3..e482b3371 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/HybridHRWatchfaceDesignerActivity.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -39,6 +40,7 @@ import android.view.DragEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; @@ -73,6 +75,7 @@ import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport; +import nodomain.freeyourgadget.gadgetbridge.util.AndroidUtils; import nodomain.freeyourgadget.gadgetbridge.util.BitmapUtil; import nodomain.freeyourgadget.gadgetbridge.util.DeviceHelper; import nodomain.freeyourgadget.gadgetbridge.util.GB; @@ -154,6 +157,14 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem findViewById(R.id.watchface_rotate_left).setOnClickListener(this); findViewById(R.id.watchface_rotate_right).setOnClickListener(this); findViewById(R.id.watchface_remove_image).setOnClickListener(this); + findViewById(R.id.button_watchface_open_menu_companion).setOnClickListener(this); + findViewById(R.id.button_watchface_reset_menu_structure).setOnClickListener(this); + } + + @Override + protected void onResume() { + super.onResume(); + reloadMenuStructureIndicator(); } @Override @@ -207,7 +218,8 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem @Override public void onClick(View v) { - if (v.getId() == R.id.button_edit_name) { + int buttonId = v.getId(); + if (buttonId == R.id.button_edit_name) { final EditText input = new EditText(this); input.setText(watchfaceName); input.setId(0); @@ -226,7 +238,7 @@ public class HybridHRWatchfaceDesignerActivity extends AbstractGBActivity implem }) .setTitle(R.string.watchface_dialog_title_set_name) .show(); - } else if (v.getId() == R.id.watchface_invert_colors) { + } else if (buttonId == R.id.watchface_invert_colors) { if (selectedBackgroundImage != null) { selectedBackgroundImage = BitmapUtil.invertBitmapColors(selectedBackgroundImage); for (int i=0; i widgets = new ArrayList<>(); + private JSONObject menuStructure = new JSONObject(); public HybridHRWatchfaceFactory(String name) { watchfaceName = name.replaceAll("[^-A-Za-z0-9]", ""); @@ -130,6 +131,10 @@ public class HybridHRWatchfaceFactory { } } + public void setMenuStructure(JSONObject menuStructure){ + this.menuStructure = menuStructure; + } + public void addWidgets(ArrayList widgets) { for (HybridHRWatchfaceWidget widget : widgets) { addWidget(widget); @@ -311,6 +316,8 @@ public class HybridHRWatchfaceFactory { config.put("light_up_on_notification", settings.getLightUpOnNotification()); configuration.put("config", config); + configuration.put("menu_structure", menuStructure); + return configuration.toString(); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridConstants.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridConstants.java index 24e8b1ae3..f54f40fda 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridConstants.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridConstants.java @@ -22,7 +22,7 @@ import java.util.Map; import nodomain.freeyourgadget.gadgetbridge.model.ActivityKind; public final class QHybridConstants { - public static final String HYBRIDHR_WATCHFACE_VERSION = "1.10"; + public static final String HYBRIDHR_WATCHFACE_VERSION = "1.11"; public static final int HYBRID_HR_WATCHFACE_WIDGET_SIZE = 76; public static Map KNOWN_WAPP_VERSIONS = new HashMap() { 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 bfee4c841..3c757b304 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 @@ -30,6 +30,8 @@ import android.widget.Toast; import androidx.localbroadcastmanager.content.LocalBroadcastManager; +import org.json.JSONException; +import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -99,6 +101,8 @@ public class QHybridSupport extends QHybridBaseSupport { public static final String QHYBRID_COMMAND_DOWNLOAD_FILE = "nodomain.freeyourgadget.gadgetbridge.Q_DOWNLOAD_FILE"; public static final String QHYBRID_COMMAND_UPLOAD_FILE = "nodomain.freeyourgadget.gadgetbridge.Q_UPLOAD_FILE"; + public static final String QHYBRID_COMMAND_SET_MENU_STRUCTURE = "nodomain.freeyourgadget.gadgetbridge.Q_SET_MENU_STRUCTURE"; + public static final String QHYBRID_ACTION_DOWNLOADED_FILE = "nodomain.freeyourgadget.gadgetbridge.Q_DOWNLOADED_FILE"; public static final String QHYBRID_ACTION_UPLOADED_FILE = "nodomain.freeyourgadget.gadgetbridge.Q_UPLOADED_FILE"; @@ -305,6 +309,7 @@ public class QHybridSupport extends QHybridBaseSupport { globalFilter.addAction(QHYBRID_COMMAND_UPLOAD_FILE); globalFilter.addAction(QHYBRID_COMMAND_PUSH_CONFIG); globalFilter.addAction(QHYBRID_COMMAND_SWITCH_WATCHFACE); + globalFilter.addAction(QHYBRID_COMMAND_SET_MENU_STRUCTURE); globalCommandReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -380,6 +385,10 @@ public class QHybridSupport extends QHybridBaseSupport { handleSwitchWatchfaceIntent(intent); break; } + case QHYBRID_COMMAND_SET_MENU_STRUCTURE:{ + handleSetMenuStructure(intent); + break; + } } } }; @@ -398,6 +407,30 @@ public class QHybridSupport extends QHybridBaseSupport { } } + private void handleSetMenuStructure(Intent intent){ + if(intent == null){ + logger.error("intent null"); + return; + } + String menuStructureJson = intent.getStringExtra("EXTRA_MENU_STRUCTURE_JSON"); + if(menuStructureJson == null){ + logger.error("Menu structure json null"); + return; + } + if(menuStructureJson.isEmpty()){ + logger.error("Menu structure json empty"); + return; + } + try { + JSONObject menuStructure = new JSONObject(menuStructureJson); + watchAdapter.handleSetMenuStructure(menuStructure); + GB.toast(getContext().getString(R.string.info_menu_structure_set), Toast.LENGTH_SHORT, GB.INFO); + } catch (JSONException e) { + logger.error("Menu structure json empty"); + GB.toast(getContext().getString(R.string.error_invalid_menu_structure), Toast.LENGTH_SHORT, GB.ERROR); + } + } + private boolean dangerousIntentsAllowed(){ return GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()).getBoolean(DeviceSettingsPreferenceConst.PREF_HYBRID_HR_DANGEROUS_EXTERNAL_INTENTS, true); } 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 ee4aa24b4..68cef9193 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 @@ -21,6 +21,8 @@ import android.bluetooth.BluetoothGattCharacteristic; import android.content.Context; import android.net.Uri; +import org.json.JSONObject; + import java.util.ArrayList; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationConfiguration; @@ -165,4 +167,8 @@ public abstract class WatchAdapter { public void pushConfigJson(String configJson){ } + + public void handleSetMenuStructure(JSONObject menuStructure) { + + } } 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 db2b32d35..a55f6f68d 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 @@ -1068,6 +1068,28 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { } } + @Override + public void handleSetMenuStructure(JSONObject menuStructure) { + String serialized = menuStructure.toString(); + getDeviceSpecificPreferences() + .edit() + .putString("MENU_STRUCTURE_JSON", serialized) + .apply(); + + try { + String payload = new JSONObject() + .put("push", new JSONObject() + .put("set", new JSONObject() + .put("customWatchFace._.config.menu_structure", menuStructure) + ) + ).toString(); + pushConfigJson(payload); + } catch (JSONException e) { + throw new RuntimeException(e); + } + + } + @Override public void setWidgetContent(String widgetID, String content, boolean renderOnWatch) { boolean update = false; @@ -1898,16 +1920,12 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { queueWrite(new JsonPutRequest(responseObject, this)); } } else if (request.optString("custom_menu").equals("request_config")) { - // watchface requests custom menu data to be initialized - LOG.info("Got custom_menu config request, sending intent to HR Menu Companion app..."); - Intent intent = new Intent(); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setClassName("d.d.hrmenucompanion", "d.d.hrmenucompanion.MainActivity"); - intent.putExtra("SEND_CONFIG", true); - try { - getContext().startActivity(intent); - } catch (Exception e) { - LOG.info("Couldn't send intent to Fossil-HR-Menu-Companion app, is it installed?"); + PackageManager manager = getContext().getPackageManager(); + try{ + // only show toast when companion app is installed + manager.getApplicationInfo("d.d.hrmenucompanion", 0); + GB.toast(getContext().getString(R.string.info_fossil_rebuild_watchface_custom_menu), Toast.LENGTH_SHORT, GB.INFO); + }catch (PackageManager.NameNotFoundException e){ } } else { LOG.warn("Unhandled request from watch: " + requestJson.toString()); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java index 71079c585..af101a6e6 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/util/AndroidUtils.java @@ -317,4 +317,20 @@ public class AndroidUtils { Toast.makeText(context, R.string.activity_error_share_failed, Toast.LENGTH_LONG).show(); } } + + public static void openWebsite(String url){ + Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); + GBApplication.getContext().startActivity(i); + } + + public static void openApp(String packageName) throws ClassNotFoundException { + Context context = GBApplication.getContext(); + + Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName); + if(launchIntent == null){ + throw new ClassNotFoundException("App " + packageName + " cannot be found"); + } + GBApplication.getContext().startActivity(launchIntent); + } } diff --git a/app/src/main/res/layout/activity_hybridhr_watchface_designer.xml b/app/src/main/res/layout/activity_hybridhr_watchface_designer.xml index 9d26b67e0..69d067922 100644 --- a/app/src/main/res/layout/activity_hybridhr_watchface_designer.xml +++ b/app/src/main/res/layout/activity_hybridhr_watchface_designer.xml @@ -112,21 +112,53 @@