Garmin: Allow installing unsupported files

This commit is contained in:
José Rebelo
2025-10-25 14:02:25 +01:00
parent b09ed96de6
commit 8186271c1a
8 changed files with 78 additions and 41 deletions
@@ -266,6 +266,7 @@ public abstract class GarminCoordinator extends AbstractBLEDeviceCoordinator {
developer.add(R.xml.devicesettings_import_activity_files);
developer.add(R.xml.devicesettings_keep_activity_data_on_device);
developer.add(R.xml.devicesettings_fetch_unknown_files);
developer.add(R.xml.devicesettings_install_unsupported_files);
developer.add(R.xml.devicesettings_new_sync_protocol);
developer.add(R.xml.devicesettings_garmin_mlr);
@@ -28,8 +28,8 @@ import org.slf4j.LoggerFactory;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.install.FwAppInstallerActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.install.InstallActivity;
@@ -52,6 +52,7 @@ public class GarminFitFileInstallHandler implements InstallHandler {
protected final Context mContext;
private byte[] rawBytes;
private String filename;
private FitFile fitFile;
private FileType.FILETYPE fileType;
private FitParseException fitParseException;
@@ -62,6 +63,7 @@ public class GarminFitFileInstallHandler implements InstallHandler {
final UriHelper uriHelper;
try {
uriHelper = UriHelper.get(uri, context);
filename = uriHelper.getFileName();
} catch (final IOException e) {
LOG.error("Failed to get uri", e);
return;
@@ -162,66 +164,61 @@ public class GarminFitFileInstallHandler implements InstallHandler {
}
private boolean parseFitFile(final InstallActivity installActivity, final GarminCoordinator coordinator, final GBDevice device) {
final boolean installUnsupportedFiles = GBApplication.getDevicePrefs(device).installUnsupportedFiles();
final String name;
final int kindName;
final String kindName;
final boolean supported;
switch (fileType) {
case COURSES:
if (!coordinator.supports(device, GarminCapability.COURSE_DOWNLOAD)) {
LOG.warn("Device does not support course download");
return false;
}
final Optional<FitCourse> fitCourseOpt = fitFile.getRecords().stream()
kindName = mContext.getString(R.string.kind_gpx_route);
supported = coordinator.supports(device, GarminCapability.COURSE_DOWNLOAD);
name = fitFile.getRecords().stream()
.filter(r -> r instanceof FitCourse)
.map(r -> (FitCourse) r)
.findFirst();
if (!fitCourseOpt.isPresent()) {
LOG.error("Fit file has no course record");
return false;
}
final FitCourse fitCourse = fitCourseOpt.get();
name = String.valueOf(fitCourse.getName());
kindName = R.string.kind_gpx_route;
.findFirst()
.map(FitCourse::getName)
.orElse(filename);
break;
case WORKOUTS:
if (!coordinator.supports(device, GarminCapability.WORKOUT_DOWNLOAD)) {
LOG.warn("Device does not support workout download");
return false;
}
final Optional<FitWorkout> fitWorkoutOpt = fitFile.getRecords().stream()
kindName = mContext.getString(R.string.menuitem_workout);
supported = coordinator.supports(device, GarminCapability.WORKOUT_DOWNLOAD);
name = fitFile.getRecords().stream()
.filter(r -> r instanceof FitWorkout)
.map(r -> (FitWorkout) r)
.findFirst();
if (!fitWorkoutOpt.isPresent()) {
LOG.error("Fit file has no workout record");
return false;
}
final FitWorkout fitWorkout = fitWorkoutOpt.get();
name = String.valueOf(fitWorkout.getName());
kindName = R.string.menuitem_workout;
.findFirst()
.map(FitWorkout::getName)
.orElse(filename);
break;
default:
LOG.warn("Unsupported fit file type: {}", fileType);
kindName = mContext.getString(R.string.menuitem_unknown_app, fileType.name());
supported = false;
name = filename;
}
if (!supported) {
LOG.warn("Device does not support install of {}", fileType);
if (!installUnsupportedFiles) {
return false;
}
}
final GenericItem fwItem = new GenericItem(mContext.getString(
R.string.installhandler_firmware_name,
mContext.getString(coordinator.getDeviceNameResource()),
mContext.getString(kindName),
kindName,
name
));
fwItem.setIcon(coordinator.getDefaultIconResource());
final StringBuilder builder = new StringBuilder();
final String kindNameString = mContext.getString(kindName);
builder.append(mContext.getString(R.string.fw_upgrade_notice, kindNameString));
builder.append(mContext.getString(R.string.fw_upgrade_notice, kindName));
if (!supported) {
builder.append("\n\n");
builder.append(mContext.getString(R.string.install_unsupported_files_warning));
}
installActivity.setInfoText(builder.toString());
installActivity.setInstallItem(fwItem);
@@ -24,6 +24,7 @@ import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.GpxRouteInstallHandler;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
@@ -36,12 +37,13 @@ public class GarminGpxRouteInstallHandler extends GpxRouteInstallHandler {
}
@Override
protected boolean isCompatible(GBDevice device) {
protected boolean isCompatible(final GBDevice device) {
final DeviceCoordinator coordinator = device.getDeviceCoordinator();
if (!(coordinator instanceof GarminCoordinator garminCoordinator)) {
LOG.warn("Coordinator is not a GarminCoordinator: {}", coordinator.getClass());
return false;
}
return garminCoordinator.supports(device, COURSE_DOWNLOAD);
final boolean installUnsupportedFiles = GBApplication.getDevicePrefs(device).installUnsupportedFiles();
return garminCoordinator.supports(device, COURSE_DOWNLOAD) || installUnsupportedFiles;
}
}
@@ -25,6 +25,7 @@ import androidx.annotation.NonNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.install.FwAppInstallerActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.install.InstallActivity;
@@ -76,7 +77,9 @@ public class GarminPrgFileInstallHandler implements InstallHandler {
}
// FIXME this might not be the correct capability
if (!garminCoordinator.supports(device, GarminCapability.CONNECTIQ_APP_MANAGEMENT)) {
final boolean supported = garminCoordinator.supports(device, GarminCapability.CONNECTIQ_APP_MANAGEMENT);
final boolean installUnsupportedFiles = GBApplication.getDevicePrefs(device).installUnsupportedFiles();
if (!supported && !installUnsupportedFiles) {
installActivity.setInfoText(mContext.getString(R.string.fwapp_install_device_not_supported));
installActivity.setInstallEnabled(false);
installActivity.setCloseEnabled(true);
@@ -92,7 +95,14 @@ public class GarminPrgFileInstallHandler implements InstallHandler {
));
fwItem.setIcon(coordinator.getDefaultIconResource());
installActivity.setInfoText(mContext.getString(R.string.fw_upgrade_notice, "PRG"));
final StringBuilder builder = new StringBuilder();
builder.append(mContext.getString(R.string.fw_upgrade_notice, "PRG"));
if (!supported) {
builder.append("\n\n");
builder.append(mContext.getString(R.string.install_unsupported_files_warning));
}
installActivity.setInfoText(builder.toString());
installActivity.setInstallItem(fwItem);
if (!device.isInitialized()) {
@@ -124,6 +124,10 @@ public class DevicePrefs extends Prefs {
return getBoolean(PREF_ALLOW_HIGH_MTU, true);
}
public boolean installUnsupportedFiles() {
return getBoolean("install_unsupported_files", false);
}
public boolean getConnectionPriorityLowPower() {
return getBoolean(PREF_CONNECTION_PRIORITY_LOW_POWER, false);
}
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="#1f1f1f"
android:pathData="M441,878q-76,-8 -141.5,-41.5t-114.5,-87Q136,696 108,627T80,480q0,-157 104.5,-270T441,82v81q-119,15 -200,104.5T160,480q0,123 81,212.5T441,797v81ZM441,680v-247L337,537l-56,-57 200,-200 200,200 -57,56 -103,-103v247h-80ZM521,878v-81q44,-5 83.5,-22t72.5,-43l57,58q-45,36 -99,59T521,878ZM676,228q-33,-26 -72,-43t-83,-22v-81q60,6 114,29t99,59l-58,58ZM790,733 L733,676q26,-33 43,-72.5t22,-83.5h81q-6,60 -29.5,114T790,733ZM798,440q-5,-44 -22,-83.5T733,284l57,-57q36,45 59.5,99T879,440h-81Z" />
</vector>
+3
View File
@@ -3937,6 +3937,9 @@
<string name="pref_import_activity_files_summary">Manually import activity files from the phone storage</string>
<string name="pref_fetch_unknown_files_title">Fetch unknown files</string>
<string name="pref_fetch_unknown_files_summary">Fetch unknown activity files from the watch. They will not be processed, but will be saved in the phone.</string>
<string name="pref_install_unsupported_files_title">Install unsupported files</string>
<string name="pref_install_unsupported_files_summary">Allow upload of unsupported files to the device.</string>
<string name="install_unsupported_files_warning">This file is not supported by the device, and might cause issues.\n\nYou are DISCOURAGED from installing it!</string>
<string name="pref_refresh_on_swipe_summary">Swipe down to fetch new data from connected devices.</string>
<string name="pref_refresh_on_swipe_title">Refresh on swipe</string>
<string name="cannot_upload_watchface_too_many_watchfaces_installed">Cannot upload watchface, too many watchfaces installed</string>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<SwitchPreferenceCompat
android:defaultValue="false"
android:icon="@drawable/ic_arrow_upload_progress"
android:key="install_unsupported_files"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_install_unsupported_files_summary"
android:title="@string/pref_install_unsupported_files_title" />
</androidx.preference.PreferenceScreen>