diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java index 242743746..e95578d5f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsFragment.java @@ -860,6 +860,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp supportedSettings = ArrayUtils.addAll(supportedSettings, R.xml.devicesettings_chartstabs); supportedSettings = ArrayUtils.addAll(supportedSettings, R.xml.devicesettings_device_card_activity_card_preferences); } + supportedSettings = ArrayUtils.add(supportedSettings, R.xml.devicesettings_settings_third_party_apps); } else if (applicationSpecificSettings.equals(DeviceSettingsActivity.MENU_ENTRY_POINTS.AUTH_SETTINGS)) { //auth settings screen supportedSettings = ArrayUtils.insert(0, supportedSettings, coordinator.getSupportedDeviceSpecificAuthenticationSettings()); supportedSettings = ArrayUtils.addAll(supportedSettings, R.xml.devicesettings_pairingkey_explanation); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/DeviceSettingsReceiver.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/DeviceSettingsReceiver.java new file mode 100644 index 000000000..ec913e8ac --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/externalevents/DeviceSettingsReceiver.java @@ -0,0 +1,110 @@ +/* Copyright (C) 2022 José Rebelo + + This file is part of Gadgetbridge. + + Gadgetbridge is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Gadgetbridge is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . */ +package nodomain.freeyourgadget.gadgetbridge.externalevents; + +import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Set; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; + +public class DeviceSettingsReceiver extends BroadcastReceiver { + private static final Logger LOG = LoggerFactory.getLogger(DeviceSettingsReceiver.class); + + public static final String COMMAND = "nodomain.freeyourgadget.gadgetbridge.action.SET_DEVICE_SETTING"; + + private static final String MAC_ADDR_PATTERN = "^([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$"; + + @Override + @SuppressLint("ApplySharedPref") + // use commit to ensure it's already applied when we call the device + public void onReceive(final Context context, final Intent intent) { + if (!COMMAND.equals(intent.getAction())) { + LOG.warn("Unexpected action {}", intent.getAction()); + } + + final String deviceAddress = intent.getStringExtra("device"); + + if (deviceAddress == null) { + LOG.warn("Missing device address"); + return; + } + + if (!deviceAddress.matches(MAC_ADDR_PATTERN)) { + LOG.warn("Device address '{}' does not match '{}'", deviceAddress, MAC_ADDR_PATTERN); + return; + } + + final GBDevice targetDevice = GBApplication.app() + .getDeviceManager() + .getDeviceByAddress(deviceAddress); + + if (targetDevice == null) { + LOG.warn("Unknown device {}", deviceAddress); + return; + } + + final SharedPreferences prefs = GBApplication.getDeviceSpecificSharedPrefs(targetDevice.getAddress()); + + if (!prefs.getBoolean("third_party_apps_set_settings", false)) { + LOG.warn("Setting device settings from 3rd party apps not allowed for {}", deviceAddress); + return; + } + + final String key = intent.getStringExtra("key"); + final Object value = intent.getExtras().get("value"); + + if (key == null) { + LOG.warn("No key specified"); + return; + } + + LOG.info("Setting '{}' to '{}'", key, value); + + final SharedPreferences.Editor editor = prefs.edit(); + + if (value == null) { + editor.remove(key); + } else if (value instanceof Integer) { + editor.putInt(key, (Integer) value); + } else if (value instanceof Boolean) { + editor.putBoolean(key, (Boolean) value); + } else if (value instanceof String) { + editor.putString(key, (String) value); + } else if (value instanceof Float) { + editor.putFloat(key, (Float) value); + } else if (value instanceof Long) { + editor.putLong(key, (Long) value); + } else if (value instanceof Set) { + editor.putStringSet(key, (Set) value); + } else { + LOG.warn("Unknown preference value type {} for {}", value.getClass(), key); + } + + editor.commit(); + + GBApplication.deviceService().onSendConfiguration(key); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java index 0a11ece79..20697775e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/DeviceCommunicationService.java @@ -61,6 +61,7 @@ import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothConnectRecei import nodomain.freeyourgadget.gadgetbridge.externalevents.BluetoothPairingRequestReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.CMWeatherReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.CalendarReceiver; +import nodomain.freeyourgadget.gadgetbridge.externalevents.DeviceSettingsReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.LineageOsWeatherReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.MusicPlaybackReceiver; import nodomain.freeyourgadget.gadgetbridge.externalevents.OmniJawsObserver; @@ -327,6 +328,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere private LineageOsWeatherReceiver mLineageOsWeatherReceiver = null; private TinyWeatherForecastGermanyReceiver mTinyWeatherForecastGermanyReceiver = null; private OmniJawsObserver mOmniJawsObserver = null; + private final DeviceSettingsReceiver deviceSettingsReceiver = new DeviceSettingsReceiver(); private final String[] mMusicActions = { "com.android.music.metachanged", @@ -488,6 +490,10 @@ public class DeviceCommunicationService extends Service implements SharedPrefere IntentFilter bluetoothCommandFilter = new IntentFilter(); bluetoothCommandFilter.addAction(COMMAND_BLUETOOTH_CONNECT); registerReceiver(bluetoothCommandReceiver, bluetoothCommandFilter); + + final IntentFilter deviceSettingsIntentFilter = new IntentFilter(); + deviceSettingsIntentFilter.addAction(DeviceSettingsReceiver.COMMAND); + registerReceiver(deviceSettingsReceiver, deviceSettingsIntentFilter); } private DeviceSupportFactory getDeviceSupportFactory() { @@ -1279,6 +1285,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere GB.removeNotification(GB.NOTIFICATION_ID, this); // need to do this because the updated notification won't be cancelled when service stops unregisterReceiver(bluetoothCommandReceiver); + unregisterReceiver(deviceSettingsReceiver); } @Override diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 478c14977..30a4a7185 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -726,6 +726,8 @@ Will keep activity data on the Mi Band even after synchronization. Useful if GB is used together with other apps. Use low-latency mode for firmware flashing This might help on devices where firmware flashing fails. + Allow 3rd party apps to change settings + Allow other installed 3rd party apps to set device settings through intents. Steps history Current steps/min Total steps diff --git a/app/src/main/res/xml/devicesettings_settings_third_party_apps.xml b/app/src/main/res/xml/devicesettings_settings_third_party_apps.xml new file mode 100644 index 000000000..899e6045a --- /dev/null +++ b/app/src/main/res/xml/devicesettings_settings_third_party_apps.xml @@ -0,0 +1,9 @@ + + + +