Fossil/Skagen Hybrids: Fix some NPE crashes

This commit is contained in:
José Rebelo
2025-08-01 20:19:29 +01:00
parent cf56c7b9aa
commit 4dcf7d852e
4 changed files with 83 additions and 51 deletions
@@ -46,6 +46,7 @@ import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.AbstractGBActivity;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceType;
import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
import nodomain.freeyourgadget.gadgetbridge.service.devices.qhybrid.QHybridSupport;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -69,16 +70,16 @@ public class AppsManagementActivity extends AbstractGBActivity {
private void refreshInstalledApps() {
try {
List<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
boolean deviceFound = false;
for(GBDevice device : devices){
if (
device.getType() == DeviceType.FOSSILQHYBRID &&
device.isConnected() &&
device.getModel() != null &&
(device.getModel().startsWith("DN") || device.getModel().startsWith("IV")) &&
device.getState() == GBDevice.State.INITIALIZED
) {
String installedAppsJson = device.getDeviceInfo("INSTALLED_APPS").getDetails();
if (installedAppsJson == null || installedAppsJson.isEmpty()) {
ItemWithDetails installedAppsJson = device.getDeviceInfo("INSTALLED_APPS");
if (installedAppsJson == null || installedAppsJson.getDetails().isEmpty()) {
throw new RuntimeException("can't get installed apps");
}
JSONArray apps = new JSONArray(installedAppsJson);
@@ -98,7 +99,7 @@ public class AppsManagementActivity extends AbstractGBActivity {
throw new RuntimeException("Device not connected");
}
class AppsListAdapter extends ArrayAdapter<String> {
static class AppsListAdapter extends ArrayAdapter<String> {
public AppsListAdapter(@NonNull Context context, @NonNull String[] objects) {
super(context, 0, objects);
}
@@ -248,18 +248,8 @@ public class QHybridConfigActivity extends AbstractGBActivity {
}
}
};
picker.handsListener = new TimePicker.OnHandsSetListener() {
@Override
public void onHandsSet(NotificationConfiguration config) {
setHands(config);
}
};
picker.vibrationListener = new TimePicker.OnVibrationSetListener() {
@Override
public void onVibrationSet(NotificationConfiguration config) {
vibrate(config);
}
};
picker.handsListener = QHybridConfigActivity.this::setHands;
picker.vibrationListener = QHybridConfigActivity.this::vibrate;
setControl(true, picker.getSettings());
break;
}
@@ -341,6 +331,11 @@ public class QHybridConfigActivity extends AbstractGBActivity {
);
}
@NonNull
private String getDeviceInfoDetails(final String name) {
ItemWithDetails deviceInfo = device.getDeviceInfo(name);
return deviceInfo != null ? deviceInfo.getDetails() : "";
}
private void setSettingsEnabled(boolean enables) {
findViewById(R.id.settingsLayout).setAlpha(enables ? 1f : 0.2f);
@@ -352,7 +347,7 @@ public class QHybridConfigActivity extends AbstractGBActivity {
public void run() {
EditText et = findViewById(R.id.stepGoalEt);
et.setOnEditorActionListener(null);
final String text = device.getDeviceInfo(QHybridSupport.ITEM_STEP_GOAL).getDetails();
final String text = getDeviceInfoDetails(QHybridSupport.ITEM_STEP_GOAL);
et.setText(text);
et.setSelection(text.length());
et.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@@ -367,14 +362,19 @@ public class QHybridConfigActivity extends AbstractGBActivity {
LocalBroadcastManager.getInstance(QHybridConfigActivity.this).sendBroadcast(intent);
updateSettings();
}
((InputMethodManager) getApplicationContext().getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
final View currentFocus = getCurrentFocus();
if (currentFocus != null) {
((InputMethodManager) getApplicationContext().getSystemService(Activity.INPUT_METHOD_SERVICE)).hideSoftInputFromWindow(currentFocus.getWindowToken(), 0);
}
}
return true;
}
});
if ("true".equals(device.getDeviceInfo(QHybridSupport.ITEM_EXTENDED_VIBRATION_SUPPORT).getDetails())) {
final int strengthProgress = (int) (Math.log(Double.parseDouble(device.getDeviceInfo(QHybridSupport.ITEM_VIBRATION_STRENGTH).getDetails()) / 25) / Math.log(2));
final String extendedVibrationSupport = getDeviceInfoDetails(QHybridSupport.ITEM_EXTENDED_VIBRATION_SUPPORT);
final String vibrationStrength = getDeviceInfoDetails(QHybridSupport.ITEM_VIBRATION_STRENGTH);
if ("true".equals(extendedVibrationSupport) && !vibrationStrength.isEmpty()) {
final int strengthProgress = (int) (Math.log(Double.parseDouble(vibrationStrength) / 25) / Math.log(2));
setSettingsEnabled(true);
SeekBar seekBar = findViewById(R.id.vibrationStrengthBar);
@@ -384,14 +384,14 @@ public class QHybridConfigActivity extends AbstractGBActivity {
findViewById(R.id.vibrationStrengthLayout).setAlpha(0.5f);
}
CheckBox activityHandCheckbox = findViewById(R.id.checkBoxUserActivityHand);
if (device.getDeviceInfo(QHybridSupport.ITEM_HAS_ACTIVITY_HAND).getDetails().equals("true")) {
if (device.getDeviceInfo(QHybridSupport.ITEM_USE_ACTIVITY_HAND).getDetails().equals("true")) {
if (getDeviceInfoDetails(QHybridSupport.ITEM_HAS_ACTIVITY_HAND).equals("true")) {
if (getDeviceInfoDetails(QHybridSupport.ITEM_USE_ACTIVITY_HAND).equals("true")) {
activityHandCheckbox.setChecked(true);
}
activityHandCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean checked) {
if (!device.getDeviceInfo(QHybridSupport.ITEM_STEP_GOAL).getDetails().equals("1000000")) {
public void onCheckedChanged(@NonNull CompoundButton buttonView, boolean checked) {
if (!getDeviceInfoDetails(QHybridSupport.ITEM_STEP_GOAL).equals("1000000")) {
new MaterialAlertDialogBuilder(QHybridConfigActivity.this)
.setMessage(getString(R.string.qhybrid_prompt_million_steps))
.setPositiveButton("ok", null)
@@ -565,7 +565,7 @@ public class QHybridConfigActivity extends AbstractGBActivity {
@Override
public void run() {
setSettingsEnabled(false);
((TextView) findViewById(R.id.settingsErrorText)).setVisibility(View.VISIBLE);
findViewById(R.id.settingsErrorText).setVisibility(View.VISIBLE);
((TextView) findViewById(R.id.settingsErrorText)).setText(error);
}
});
@@ -32,6 +32,7 @@ import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;
@@ -60,6 +61,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NavigationInfoSpec;
@@ -214,6 +216,10 @@ public class QHybridSupport extends QHybridBaseSupport {
}
case QHYBRID_COMMAND_MOVE: {
MoveHandsRequest.MovementConfiguration movement = new MoveHandsRequest.MovementConfiguration(true);
if (extras == null) {
logger.error("Got QHYBRID_COMMAND_MOVE without extras");
break;
}
if(extras.containsKey("EXTRA_DISTANCE_HOUR")) movement.setHourDegrees(extras.getShort("EXTRA_DISTANCE_HOUR"));
if(extras.containsKey("EXTRA_DISTANCE_MINUTE")) movement.setMinuteDegrees(extras.getShort("EXTRA_DISTANCE_MINUTE"));
if(extras.containsKey("EXTRA_DISTANCE_SUB")) movement.setSubDegrees(extras.getShort("EXTRA_DISTANCE_SUB"));
@@ -247,17 +253,36 @@ public class QHybridSupport extends QHybridBaseSupport {
}
case QHYBRID_COMMAND_UPDATE_SETTINGS: {
String newSetting = intent.getStringExtra("EXTRA_SETTING");
if (newSetting == null) {
logger.error("newSetting is null");
break;
}
switch (newSetting) {
case ITEM_VIBRATION_STRENGTH: {
watchAdapter.setVibrationStrength(Short.parseShort(gbDevice.getDeviceInfo(ITEM_VIBRATION_STRENGTH).getDetails()));
final ItemWithDetails itemVibrationStrength = gbDevice.getDeviceInfo(ITEM_VIBRATION_STRENGTH);
if (itemVibrationStrength == null) {
logger.error("itemVibrationStrength is null");
break;
}
watchAdapter.setVibrationStrength(Short.parseShort(itemVibrationStrength.getDetails()));
break;
}
case ITEM_STEP_GOAL: {
watchAdapter.setStepGoal(Integer.parseInt(gbDevice.getDeviceInfo(ITEM_STEP_GOAL).getDetails()));
final ItemWithDetails itemStepGoal = gbDevice.getDeviceInfo(ITEM_STEP_GOAL);
if (itemStepGoal == null) {
logger.error("itemStepGoal is null");
break;
}
watchAdapter.setStepGoal(Integer.parseInt(itemStepGoal.getDetails()));
break;
}
case ITEM_USE_ACTIVITY_HAND: {
QHybridSupport.this.useActivityHand = gbDevice.getDeviceInfo(ITEM_USE_ACTIVITY_HAND).getDetails().equals("true");
final ItemWithDetails itemUseActivityHand = gbDevice.getDeviceInfo(ITEM_USE_ACTIVITY_HAND);
if (itemUseActivityHand == null) {
logger.error("itemUseActivityHand is null");
break;
}
QHybridSupport.this.useActivityHand = itemUseActivityHand.getDetails().equals("true");
GBApplication.getPrefs().getPreferences().edit().putBoolean("QHYBRID_USE_ACTIVITY_HAND", useActivityHand).apply();
break;
}
@@ -317,14 +342,20 @@ public class QHybridSupport extends QHybridBaseSupport {
globalCommandReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (watchAdapter == null) {
if (watchAdapter == null || intent.getAction() == null) {
return;
}
final Bundle extras = intent.getExtras();
if (extras == null) {
logger.error("Got {} without extras", intent.getAction());
return;
}
switch (intent.getAction()) {
case QHYBRID_ACTION_SET_ACTIVITY_HAND: {
try {
String extra = String.valueOf(intent.getExtras().get("EXTRA_PROGRESS"));
String extra = String.valueOf(extras.get("EXTRA_PROGRESS"));
float progress = Float.parseFloat(extra);
watchAdapter.setActivityHand(progress);
@@ -341,8 +372,8 @@ public class QHybridSupport extends QHybridBaseSupport {
break;
}
case QHYBRID_COMMAND_SET_MENU_MESSAGE: {
String message = String.valueOf(intent.getExtras().get("EXTRA_MESSAGE"));
boolean finished = Boolean.valueOf(String.valueOf(intent.getExtras().get("EXTRA_FINISHED")));
String message = String.valueOf(extras.get("EXTRA_MESSAGE"));
boolean finished = Boolean.parseBoolean(String.valueOf(extras.get("EXTRA_FINISHED")));
watchAdapter.setCommuteMenuMessage(message, finished);
@@ -351,13 +382,13 @@ public class QHybridSupport extends QHybridBaseSupport {
case QHYBRID_COMMAND_SET_WIDGET_CONTENT: {
HashMap<String, String> widgetValues = new HashMap<>();
for(String key : intent.getExtras().keySet()){
for(String key : extras.keySet()){
if(key.matches("^EXTRA_WIDGET_ID_.*$")){
widgetValues.put(key.substring(16), String.valueOf(intent.getExtras().get(key)));
widgetValues.put(key.substring(16), String.valueOf(extras.get(key)));
}
}
boolean render = intent.getBooleanExtra("EXTRA_RENDER", true);
if(widgetValues.size() > 0){
if (!widgetValues.isEmpty()){
Iterator<String> valuesIterator = widgetValues.keySet().iterator();
valuesIterator.next();
@@ -370,8 +401,8 @@ public class QHybridSupport extends QHybridBaseSupport {
String id = valuesIterator.next();
watchAdapter.setWidgetContent(id, widgetValues.get(id), render);
}else {
String id = String.valueOf(intent.getExtras().get("EXTRA_WIDGET_ID"));
String content = String.valueOf(intent.getExtras().get("EXTRA_CONTENT"));
String id = String.valueOf(extras.get("EXTRA_WIDGET_ID"));
String content = String.valueOf(extras.get("EXTRA_CONTENT"));
watchAdapter.setWidgetContent(id, content, render);
}
break;
@@ -382,11 +413,11 @@ public class QHybridSupport extends QHybridBaseSupport {
break;
}
case QHYBRID_COMMAND_PUSH_CONFIG:{
handleConfigSetIntent(intent);
handleConfigSetIntent(extras);
break;
}
case QHYBRID_COMMAND_SWITCH_WATCHFACE:{
handleSwitchWatchfaceIntent(intent);
handleSwitchWatchfaceIntent(extras);
break;
}
case QHYBRID_COMMAND_SET_MENU_STRUCTURE:{
@@ -399,14 +430,14 @@ public class QHybridSupport extends QHybridBaseSupport {
ContextCompat.registerReceiver(GBApplication.getContext(), globalCommandReceiver, globalFilter, ContextCompat.RECEIVER_EXPORTED);
}
private void handleConfigSetIntent(Intent intent) {
String configJson = intent.getExtras().getString("EXTRA_CONFIG_JSON", "{}");
private void handleConfigSetIntent(Bundle extras) {
String configJson = extras.getString("EXTRA_CONFIG_JSON", "{}");
watchAdapter.pushConfigJson(configJson);
}
private void handleSwitchWatchfaceIntent(Intent intent) {
String watchfaceName = intent.getExtras().getString("WATCHFACE_NAME", "");
if (watchfaceName != "") {
private void handleSwitchWatchfaceIntent(Bundle extras) {
String watchfaceName = extras.getString("WATCHFACE_NAME", "");
if (!StringUtils.isBlank(watchfaceName)) {
((FossilHRWatchAdapter) watchAdapter).activateWatchface(watchfaceName);
}
}
@@ -451,8 +482,7 @@ public class QHybridSupport extends QHybridBaseSupport {
if(handleObject instanceof String){
handleObject = FileHandle.fromName((String)handleObject);
}
if(!(handleObject instanceof FileHandle)) return;
FileHandle handle = (FileHandle) handleObject;
if(!(handleObject instanceof FileHandle handle)) return;
watchAdapter.uploadFileGenerateHeader(handle, filePath, intent.getBooleanExtra("EXTRA_ENCRYPTED", false));
}
@@ -655,7 +685,7 @@ public class QHybridSupport extends QHybridBaseSupport {
for (String notificationPackage : NotificationListener.notificationStack) {
for (NotificationConfiguration notificationConfiguration : configs.keySet()) {
if (configs.get(notificationConfiguration)) continue;
if (Boolean.TRUE.equals(configs.get(notificationConfiguration))) continue;
if (notificationConfiguration.getPackageName().equals(notificationPackage)) {
notificationProgress += 0.25;
configs.put(notificationConfiguration, true);
@@ -727,7 +757,7 @@ public class QHybridSupport extends QHybridBaseSupport {
if (file.exists()) {
throw new Exception("file " + file.getPath() + " exists");
}
logger.debug("Writing file " + file.getPath());
logger.debug("Writing file {}", file.getPath());
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(request.file);
}
@@ -764,7 +794,7 @@ public class QHybridSupport extends QHybridBaseSupport {
Matcher matcher = Pattern
.compile("(?<=[A-Z]{2}[0-9]\\.[0-9]\\.)[0-9]+\\.[0-9]+")
.matcher(firmwareVersion);
if(matcher.find()){
if (matcher.find()){
gbDevice.setFirmwareVersion2(matcher.group());
}
@@ -122,6 +122,7 @@ import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.GenericItem;
import nodomain.freeyourgadget.gadgetbridge.model.ItemWithDetails;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicStateSpec;
import nodomain.freeyourgadget.gadgetbridge.model.NavigationInfoSpec;
@@ -2202,8 +2203,8 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter {
public void onSetNavigationInfo(NavigationInfoSpec navigationInfoSpec) {
SharedPreferences prefs = getDeviceSpecificPreferences();
String installedAppsJson = getDeviceSupport().getDevice().getDeviceInfo("INSTALLED_APPS").getDetails();
if (installedAppsJson == null || !installedAppsJson.contains("navigationApp")) {
ItemWithDetails installedAppsJson = getDeviceSupport().getDevice().getDeviceInfo("INSTALLED_APPS");
if (installedAppsJson == null || !installedAppsJson.getDetails().contains("navigationApp")) {
if (!notifiedAboutMissingNavigationApp) {
notifiedAboutMissingNavigationApp = true;
NotificationCompat.Builder ncomp = new NotificationCompat.Builder(getContext(), NOTIFICATION_CHANNEL_ID)