diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettings.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettings.java new file mode 100644 index 000000000..fc56b13fd --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettings.java @@ -0,0 +1,184 @@ +/* Copyright (C) 2024 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.activities.devicesettings; + +import android.os.Parcel; +import android.os.Parcelable; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.XmlRes; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * A class that contains the device-specific settings screens for a device. All the integers in this + * class correspond to xml resources for preferences. + *

+ * This class contains 2 types of screens: + * - Root screens - the ones that are displayed in the first page of the device settings activity. These can be + * normal preference screens / preferences, or dummy root screens (see {@link DeviceSpecificSettingsScreen}. + * - Sub-screens - a screen that is opened when one of the {@link DeviceSpecificSettingsScreen} is clicked. + *

+ * There can be an arbitrary number of nested sub-screens, as long as they are all mapped by key in the + * subScreens map. + *

+ * See the XiaomiCoordinator and ZeppOsCoordinator for example usages. + */ +public class DeviceSpecificSettings implements Parcelable { + private final List rootScreens = new ArrayList<>(); + private final Map> subScreens = new LinkedHashMap<>(); + + public DeviceSpecificSettings() { + } + + public DeviceSpecificSettings(final int[] rootScreens) { + for (final int setting : rootScreens) { + this.rootScreens.add(setting); + } + } + + public void addRootScreen(@XmlRes final int screen) { + if (!rootScreens.contains(screen)) { + rootScreens.add(screen); + } + } + + public List addRootScreen(final DeviceSpecificSettingsScreen screen) { + if (!rootScreens.contains(screen.getXml())) { + rootScreens.add(screen.getXml()); + } + + return addSubScreen(screen, screen.getXml()); + } + + public List addRootScreen(final DeviceSpecificSettingsScreen screen, final int... subScreens) { + final List subScreenScreens = addRootScreen(screen); + for (final int subScreen : subScreens) { + subScreenScreens.add(subScreen); + } + return subScreenScreens; + } + + public void addRootScreen(final int index, @XmlRes final int screen) { + rootScreens.add(index, screen); + } + + public List addSubScreen(final DeviceSpecificSettingsScreen rootScreen, final int... screens) { + return addSubScreen(rootScreen.getKey(), screens); + } + + private List addSubScreen(final String key, final int... screens) { + if (!subScreens.containsKey(key)) { + subScreens.put(key, new ArrayList<>()); + } + + final List subscreenPages = Objects.requireNonNull(subScreens.get(key)); + + for (final int screen : screens) { + if (!subscreenPages.contains(screen)) { + subscreenPages.add(screen); + } + } + + return subscreenPages; + } + + public void mergeFrom(final DeviceSpecificSettings deviceSpecificSettings) { + for (final Integer rootScreen : deviceSpecificSettings.rootScreens) { + addRootScreen(rootScreen); + } + for (final Map.Entry> e : deviceSpecificSettings.subScreens.entrySet()) { + if (!subScreens.containsKey(e.getKey())) { + subScreens.put(e.getKey(), new ArrayList<>()); + } + + for (final int screen : e.getValue()) { + Objects.requireNonNull(subScreens.get(e.getKey())).add(screen); + } + } + } + + public List getRootScreens() { + return rootScreens; + } + + @Nullable + public List getScreen(@NonNull final String key) { + return subScreens.get(key); + } + + public List getAllScreens() { + final List allScreens = new ArrayList<>(rootScreens); + for (final List screens : subScreens.values()) { + allScreens.addAll(screens); + } + return allScreens; + } + + public static final Creator CREATOR = new Creator() { + @Override + public DeviceSpecificSettings createFromParcel(final Parcel in) { + final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings(); + final int numRootScreens = in.readInt(); + for (int i = 0; i < numRootScreens; i++) { + deviceSpecificSettings.addRootScreen(in.readInt()); + } + final int numSubScreens = in.readInt(); + for (int i = 0; i < numSubScreens; i++) { + final String key = in.readString(); + final int numScreens = in.readInt(); + final int[] screens = new int[numScreens]; + for (int j = 0; j < numScreens; j++) { + screens[j] = in.readInt(); + } + deviceSpecificSettings.addSubScreen(key, screens); + } + return deviceSpecificSettings; + } + + @Override + public DeviceSpecificSettings[] newArray(final int size) { + return new DeviceSpecificSettings[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull final Parcel dest, final int flags) { + dest.writeInt(rootScreens.size()); + for (final Integer rootScreen : rootScreens) { + dest.writeInt(rootScreen); + } + dest.writeInt(subScreens.size()); + for (final Map.Entry> e : subScreens.entrySet()) { + dest.writeString(e.getKey()); + dest.writeInt(e.getValue().size()); + for (final Integer s : e.getValue()) { + dest.writeInt(s); + } + } + } +} 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 3cc59ceaf..2b7386c20 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 @@ -25,19 +25,19 @@ import android.media.AudioManager; import android.os.Bundle; import android.text.InputType; -import androidx.annotation.NonNull; import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.preference.ListPreference; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; @@ -59,7 +59,6 @@ import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; -import nodomain.freeyourgadget.gadgetbridge.model.DeviceType; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.*; @@ -96,10 +95,15 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i private GBDevice device; - private void setSettingsFileSuffix(String settingsFileSuffix, @NonNull int[] supportedSettings) { + private void setSettingsFileSuffix(String settingsFileSuffix) { Bundle args = new Bundle(); args.putString("settingsFileSuffix", settingsFileSuffix); - args.putIntArray("supportedSettings", supportedSettings); + setArguments(args); + } + + private void setDeviceSpecificSettings(final DeviceSpecificSettings deviceSpecificSettings) { + final Bundle args = getArguments() != null ? getArguments() : new Bundle(); + args.putParcelable("deviceSpecificSettings", deviceSpecificSettings); setArguments(args); } @@ -122,12 +126,11 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i return; } String settingsFileSuffix = arguments.getString("settingsFileSuffix", null); - int[] supportedSettings = arguments.getIntArray("supportedSettings"); - String[] supportedLanguages = arguments.getStringArray("supportedLanguages"); + DeviceSpecificSettings deviceSpecificSettings = arguments.getParcelable("deviceSpecificSettings"); this.deviceSpecificSettingsCustomizer = arguments.getParcelable("deviceSpecificSettingsCustomizer"); this.device = arguments.getParcelable("device"); - if (settingsFileSuffix == null || supportedSettings == null) { + if (settingsFileSuffix == null || deviceSpecificSettings == null) { return; } @@ -136,7 +139,7 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i if (rootKey == null) { // we are the main preference screen boolean first = true; - for (int setting : supportedSettings) { + for (int setting : deviceSpecificSettings.getRootScreens()) { if (first) { setPreferencesFromResource(setting, null); first = false; @@ -145,16 +148,47 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i } } } else { - // Now, this is ugly: search all the xml files for the rootKey - for (int setting : supportedSettings) { - try { - setPreferencesFromResource(setting, rootKey); - } catch (Exception ignore) { - continue; + // First attempt to find a known screen for this key + final List screenSettings = deviceSpecificSettings.getScreen(rootKey); + if (screenSettings != null) { + boolean first = true; + for (int setting : screenSettings) { + if (first) { + // Use the root key here to set the root screen, so that the actionbar title gets updated + setPreferencesFromResource(setting, rootKey); + first = false; + } else { + addPreferencesFromResource(setting); + } + } + } else { + // Now, this is ugly: search all the xml files for the rootKey + // This means that this device is using the deprecated getSupportedDeviceSpecificSettings, + // or that we're on a sub-screen + final List allScreens = deviceSpecificSettings.getAllScreens(); + for (int setting : allScreens) { + try { + setPreferencesFromResource(setting, rootKey); + } catch (Exception ignore) { + continue; + } + break; } - break; } } + + // Since all root preference screens are empty, clicking them will not do anything + // add on-click listeners + for (final DeviceSpecificSettingsScreen value : DeviceSpecificSettingsScreen.values()) { + final PreferenceScreen prefScreen = findPreference(value.getKey()); + if (prefScreen != null) { + prefScreen.setOnPreferenceClickListener(p -> { + onNavigateToScreen(prefScreen); + return true; + }); + } + } + setChangeListener(); } @@ -1023,53 +1057,56 @@ public class DeviceSpecificSettingsFragment extends AbstractPreferenceFragment i } } - static private void addElementsToList(ArrayList list, int[]... elements){ - for(int[] array : elements){ - for(int item : array){ - list.add(item); - } - } - } - static DeviceSpecificSettingsFragment newInstance(GBDevice device, DeviceSettingsActivity.MENU_ENTRY_POINTS applicationSpecificSettings) { final DeviceCoordinator coordinator = device.getDeviceCoordinator(); - ArrayList supportedSettings = new ArrayList<>(); - String[] supportedLanguages = null; + + final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings(); if (applicationSpecificSettings.equals(DeviceSettingsActivity.MENU_ENTRY_POINTS.AUTH_SETTINGS)) { //auth settings screen - supportedSettings.add(R.xml.devicesettings_pairingkey_explanation); - addElementsToList(supportedSettings, coordinator.getSupportedDeviceSpecificAuthenticationSettings()); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_pairingkey_explanation); + for (final int s : coordinator.getSupportedDeviceSpecificAuthenticationSettings()) { + deviceSpecificSettings.addRootScreen(s); + } } else { //device/application settings if (coordinator.getSupportedLanguageSettings(device) != null) { - supportedSettings.add(R.xml.devicesettings_language_generic); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_language_generic); + } + DeviceSpecificSettings coordinatorDeviceSettings = coordinator.getDeviceSpecificSettings(device); + if (coordinatorDeviceSettings != null) { + deviceSpecificSettings.mergeFrom(coordinatorDeviceSettings); } - addElementsToList(supportedSettings, coordinator.getSupportedDeviceSpecificSettings(device)); final int[] supportedAuthSettings = coordinator.getSupportedDeviceSpecificAuthenticationSettings(); if (supportedAuthSettings != null && supportedAuthSettings.length > 0) { - supportedSettings.add(R.xml.devicesettings_header_authentication); - addElementsToList(supportedSettings, supportedAuthSettings); + deviceSpecificSettings.addRootScreen( + DeviceSpecificSettingsScreen.AUTHENTICATION, + supportedAuthSettings + ); } - addElementsToList( - supportedSettings, - coordinator.getSupportedDeviceSpecificConnectionSettings(), - coordinator.getSupportedDeviceSpecificApplicationSettings()); + deviceSpecificSettings.addRootScreen( + DeviceSpecificSettingsScreen.CONNECTION, + coordinator.getSupportedDeviceSpecificConnectionSettings() + ); + if (coordinator.supportsActivityTracking()) { - supportedSettings.addAll(Arrays.asList( - R.xml.devicesettings_activity_info_header, + deviceSpecificSettings.addRootScreen( + DeviceSpecificSettingsScreen.ACTIVITY_INFO, R.xml.devicesettings_chartstabs, R.xml.devicesettings_device_card_activity_card_preferences - )); + ); } - supportedSettings.add(R.xml.devicesettings_settings_third_party_apps); - } - int[] supportedSettingsInts = ArrayUtils.toPrimitive(supportedSettings.toArray(new Integer[0])); + deviceSpecificSettings.addRootScreen( + DeviceSpecificSettingsScreen.DEVELOPER, + R.xml.devicesettings_settings_third_party_apps + ); + } final DeviceSpecificSettingsCustomizer deviceSpecificSettingsCustomizer = coordinator.getDeviceSpecificSettingsCustomizer(device); final String settingsFileSuffix = device.getAddress(); final DeviceSpecificSettingsFragment fragment = new DeviceSpecificSettingsFragment(); - fragment.setSettingsFileSuffix(settingsFileSuffix, supportedSettingsInts); + fragment.setSettingsFileSuffix(settingsFileSuffix); + fragment.setDeviceSpecificSettings(deviceSpecificSettings); fragment.setDeviceSpecificSettingsCustomizer(deviceSpecificSettingsCustomizer); fragment.setDevice(device); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsScreen.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsScreen.java new file mode 100644 index 000000000..006cb8215 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsScreen.java @@ -0,0 +1,51 @@ +/* Copyright (C) 2024 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.activities.devicesettings; + +import androidx.annotation.XmlRes; + +import nodomain.freeyourgadget.gadgetbridge.R; + +public enum DeviceSpecificSettingsScreen { + ACTIVITY_INFO("pref_screen_activity_info", R.xml.devicesettings_root_activity_info), + AUTHENTICATION("pref_screen_authentication", R.xml.devicesettings_root_authentication), + CALENDAR("pref_screen_calendar", R.xml.devicesettings_root_calendar), + CONNECTION("pref_screen_connection", R.xml.devicesettings_root_connection), + DEVELOPER("pref_screen_developer", R.xml.devicesettings_root_developer), + DISPLAY("pref_screen_display", R.xml.devicesettings_root_display), + NOTIFICATIONS("pref_screen_notifications", R.xml.devicesettings_root_notifications), + DATE_TIME("pref_screen_date_time", R.xml.devicesettings_root_date_time), + WORKOUT("pref_screen_workout", R.xml.devicesettings_root_workout), + ; + + private final String key; + @XmlRes + private final int xml; + + DeviceSpecificSettingsScreen(final String key, final int xml) { + this.key = key; + this.xml = xml; + } + + public String getKey() { + return key; + } + + public int getXml() { + return xml; + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java index c959a079d..b0f012e13 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/AbstractDeviceCoordinator.java @@ -51,6 +51,7 @@ import de.greenrobot.dao.query.QueryBuilder; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.capabilities.HeartRateCapability; import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl; @@ -590,13 +591,18 @@ public abstract class AbstractDeviceCoordinator implements DeviceCoordinator { } @Override - public int[] getSupportedDeviceSpecificApplicationSettings() { + public int[] getSupportedDeviceSpecificSettings(GBDevice device) { return new int[0]; } @Override - public int[] getSupportedDeviceSpecificSettings(GBDevice device) { - return new int[0]; + public DeviceSpecificSettings getDeviceSpecificSettings(GBDevice device) { + final int[] settings = getSupportedDeviceSpecificSettings(device); + if (settings == null || settings.length == 0) { + return null; + } + + return new DeviceSpecificSettings(settings); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java index 16f9ba243..3a4b3df7e 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/DeviceCoordinator.java @@ -36,6 +36,7 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; import nodomain.freeyourgadget.gadgetbridge.GBException; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.capabilities.HeartRateCapability; import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl; @@ -561,13 +562,6 @@ public interface DeviceCoordinator { */ int[] getSupportedDeviceSpecificConnectionSettings(); - /** - * Returns device specific settings related to the application itself - * charts settings and so on - * @return int[] - */ - int[] getSupportedDeviceSpecificApplicationSettings(); - /** * Returns device specific settings related to the Auth key * @return int[] @@ -576,9 +570,19 @@ public interface DeviceCoordinator { /** * Indicates which device specific settings the device supports (not per device type or family, but unique per device). + * + * @deprecated use getDeviceSpecificSettings */ + @Deprecated int[] getSupportedDeviceSpecificSettings(GBDevice device); + /** + * Returns the device-specific settings supported by this specific device. See + * {@link DeviceSpecificSettings} for more information + */ + @Nullable + DeviceSpecificSettings getDeviceSpecificSettings(GBDevice device); + /** * Returns the {@link DeviceSpecificSettingsCustomizer}, allowing for the customization of the devices specific settings screen. */ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/zeppos/ZeppOsCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/zeppos/ZeppOsCoordinator.java index 663f8b6c9..d65836cbd 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/zeppos/ZeppOsCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/zeppos/ZeppOsCoordinator.java @@ -22,8 +22,6 @@ import android.net.Uri; import androidx.annotation.NonNull; -import org.apache.commons.lang3.ArrayUtils; - import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -37,6 +35,8 @@ import java.util.regex.Pattern; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsUtils; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.capabilities.HeartRateCapability; import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl; @@ -350,150 +350,142 @@ public abstract class ZeppOsCoordinator extends HuamiCoordinator { * by {@link ZeppOsSettingsCustomizer}. */ @Override - public int[] getSupportedDeviceSpecificSettings(final GBDevice device) { - final List settings = new ArrayList<>(); + public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) { + final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings(); // // Apps // TODO: These should go somewhere else // - settings.add(R.xml.devicesettings_header_apps); if (ZeppOsLoyaltyCardService.isSupported(getPrefs(device))) { - settings.add(R.xml.devicesettings_loyalty_cards); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_loyalty_cards); } // // Time // - settings.add(R.xml.devicesettings_header_time); - //settings.add(R.xml.devicesettings_timeformat); - settings.add(R.xml.devicesettings_dateformat_2); + final List dateTime = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DATE_TIME); + //dateTime.add(R.xml.devicesettings_timeformat); + dateTime.add(R.xml.devicesettings_dateformat_2); if (getWorldClocksSlotCount() > 0) { - settings.add(R.xml.devicesettings_world_clocks); + dateTime.add(R.xml.devicesettings_world_clocks); } // // Display // - settings.add(R.xml.devicesettings_header_display); - settings.add(R.xml.devicesettings_huami2021_displayitems); - settings.add(R.xml.devicesettings_huami2021_shortcuts); + final List display = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DISPLAY); + display.add(R.xml.devicesettings_huami2021_displayitems); + display.add(R.xml.devicesettings_huami2021_shortcuts); if (supportsControlCenter()) { - settings.add(R.xml.devicesettings_huami2021_control_center); + display.add(R.xml.devicesettings_huami2021_control_center); } if (supportsShortcutCards(device)) { - settings.add(R.xml.devicesettings_huami2021_shortcut_cards); + display.add(R.xml.devicesettings_huami2021_shortcut_cards); } - settings.add(R.xml.devicesettings_nightmode); - settings.add(R.xml.devicesettings_sleep_mode); - settings.add(R.xml.devicesettings_liftwrist_display_sensitivity_with_smart); - settings.add(R.xml.devicesettings_password); - settings.add(R.xml.devicesettings_huami2021_watchface); - settings.add(R.xml.devicesettings_always_on_display); - settings.add(R.xml.devicesettings_screen_timeout); + display.add(R.xml.devicesettings_nightmode); + display.add(R.xml.devicesettings_sleep_mode); + display.add(R.xml.devicesettings_liftwrist_display_sensitivity_with_smart); + display.add(R.xml.devicesettings_password); + display.add(R.xml.devicesettings_huami2021_watchface); + display.add(R.xml.devicesettings_always_on_display); + display.add(R.xml.devicesettings_screen_timeout); if (supportsAutoBrightness(device)) { - settings.add(R.xml.devicesettings_screen_brightness_withauto); + display.add(R.xml.devicesettings_screen_brightness_withauto); } else { - settings.add(R.xml.devicesettings_screen_brightness); + display.add(R.xml.devicesettings_screen_brightness); } // // Health // - settings.add(R.xml.devicesettings_header_health); - settings.add(R.xml.devicesettings_heartrate_sleep_alert_activity_stress_spo2); - settings.add(R.xml.devicesettings_inactivity_dnd_no_threshold); - settings.add(R.xml.devicesettings_goal_notification); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_heartrate_sleep_alert_activity_stress_spo2); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_inactivity_dnd_no_threshold); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_goal_notification); // // Workout // - settings.add(R.xml.devicesettings_header_workout); + final List workout = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.WORKOUT); if (hasGps(device)) { - settings.add(R.xml.devicesettings_gps_agps); + workout.add(R.xml.devicesettings_gps_agps); } else { // If the device has GPS, it doesn't report workout start/end to the phone - settings.add(R.xml.devicesettings_workout_start_on_phone); - settings.add(R.xml.devicesettings_workout_send_gps_to_band); + workout.add(R.xml.devicesettings_workout_start_on_phone); + workout.add(R.xml.devicesettings_workout_send_gps_to_band); } - settings.add(R.xml.devicesettings_workout_keep_screen_on); - settings.add(R.xml.devicesettings_workout_detection); + workout.add(R.xml.devicesettings_workout_keep_screen_on); + workout.add(R.xml.devicesettings_workout_detection); // // Notifications // - settings.add(R.xml.devicesettings_header_notifications); + final List notifications = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.NOTIFICATIONS); if (supportsBluetoothPhoneCalls(device)) { - settings.add(R.xml.devicesettings_phone_calls_watch_pair); + notifications.add(R.xml.devicesettings_phone_calls_watch_pair); } else { - settings.add(R.xml.devicesettings_display_caller); + notifications.add(R.xml.devicesettings_display_caller); } - settings.add(R.xml.devicesettings_sound_and_vibration); - settings.add(R.xml.devicesettings_vibrationpatterns); - settings.add(R.xml.devicesettings_donotdisturb_withauto_and_always); - settings.add(R.xml.devicesettings_send_app_notifications); - settings.add(R.xml.devicesettings_screen_on_on_notifications); - settings.add(R.xml.devicesettings_autoremove_notifications); - settings.add(R.xml.devicesettings_canned_reply_16); - settings.add(R.xml.devicesettings_transliteration); + notifications.add(R.xml.devicesettings_sound_and_vibration); + notifications.add(R.xml.devicesettings_vibrationpatterns); + notifications.add(R.xml.devicesettings_donotdisturb_withauto_and_always); + notifications.add(R.xml.devicesettings_send_app_notifications); + notifications.add(R.xml.devicesettings_screen_on_on_notifications); + notifications.add(R.xml.devicesettings_autoremove_notifications); + notifications.add(R.xml.devicesettings_canned_reply_16); + notifications.add(R.xml.devicesettings_transliteration); // // Calendar // - settings.add(R.xml.devicesettings_header_calendar); - settings.add(R.xml.devicesettings_sync_calendar); + deviceSpecificSettings.addRootScreen( + DeviceSpecificSettingsScreen.CALENDAR, + R.xml.devicesettings_sync_calendar + ); // // Other // - settings.add(R.xml.devicesettings_header_other); if (getContactsSlotCount(device) > 0) { - settings.add(R.xml.devicesettings_contacts); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_contacts); } - settings.add(R.xml.devicesettings_offline_voice); - settings.add(R.xml.devicesettings_device_actions_without_not_wear); - settings.add(R.xml.devicesettings_phone_silent_mode); - settings.add(R.xml.devicesettings_buttonactions_upper_long); - settings.add(R.xml.devicesettings_buttonactions_lower_short); - settings.add(R.xml.devicesettings_weardirection); - settings.add(R.xml.devicesettings_camera_remote); - settings.add(R.xml.devicesettings_morning_updates); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_offline_voice); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_device_actions_without_not_wear); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_phone_silent_mode); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_buttonactions_upper_long); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_buttonactions_lower_short); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_weardirection); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_camera_remote); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_morning_updates); // // Connection // - settings.add(R.xml.devicesettings_header_connection); - settings.add(R.xml.devicesettings_expose_hr_thirdparty); - settings.add(R.xml.devicesettings_bt_connected_advertisement); - settings.add(R.xml.devicesettings_high_mtu); + final List connection = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.CONNECTION); + connection.add(R.xml.devicesettings_expose_hr_thirdparty); + connection.add(R.xml.devicesettings_bt_connected_advertisement); + connection.add(R.xml.devicesettings_high_mtu); // // Developer // - settings.add(R.xml.devicesettings_header_developer); + final List developer = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DEVELOPER); if (ZeppOsLogsService.isSupported(getPrefs(device))) { - settings.add(R.xml.devicesettings_app_logs_start_stop); + developer.add(R.xml.devicesettings_app_logs_start_stop); } if (supportsAlexa(device)) { - settings.add(R.xml.devicesettings_huami2021_alexa); + developer.add(R.xml.devicesettings_huami2021_alexa); } if (supportsWifiHotspot(device)) { - settings.add(R.xml.devicesettings_wifi_hotspot); + developer.add(R.xml.devicesettings_wifi_hotspot); } if (supportsFtpServer(device)) { - settings.add(R.xml.devicesettings_ftp_server); + developer.add(R.xml.devicesettings_ftp_server); } - settings.add(R.xml.devicesettings_keep_activity_data_on_device); - settings.add(R.xml.devicesettings_huami2021_fetch_operation_time_unit); + developer.add(R.xml.devicesettings_keep_activity_data_on_device); + developer.add(R.xml.devicesettings_huami2021_fetch_operation_time_unit); - return ArrayUtils.toPrimitive(settings.toArray(new Integer[0])); - } - - @Override - public int[] getSupportedDeviceSpecificAuthenticationSettings() { - return new int[]{ - R.xml.devicesettings_pairingkey - }; + return deviceSpecificSettings; } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java index 5a00e5fa4..48d7dcd79 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java @@ -269,6 +269,7 @@ public class QHybridCoordinator extends AbstractBLEDeviceCoordinator { if (getFirmwareVersion() != null && getFirmwareVersion().smallerThan(new Version("2.20"))) { supportedSettings = ArrayUtils.insert(1, supportedSettings, R.xml.devicesettings_fossilhybridhr_pre_fw220); } + supportedSettings = ArrayUtils.add(supportedSettings, R.xml.devicesettings_custom_deviceicon); return supportedSettings; } @@ -285,13 +286,6 @@ public class QHybridCoordinator extends AbstractBLEDeviceCoordinator { }; } - @Override - public int[] getSupportedDeviceSpecificApplicationSettings() { - return new int[]{ - R.xml.devicesettings_custom_deviceicon, - }; - } - private boolean isHybridHR() { List devices = GBApplication.app().getDeviceManager().getSelectedDevices(); for(GBDevice device : devices){ diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java index 9d88b119e..72e31c488 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/test/TestDeviceCoordinator.java @@ -464,13 +464,6 @@ public class TestDeviceCoordinator extends AbstractDeviceCoordinator { return super.getSupportedDeviceSpecificConnectionSettings(); } - @Override - public int[] getSupportedDeviceSpecificApplicationSettings() { - return new int[]{ - R.xml.devicesettings_custom_deviceicon, - }; - } - @Override public int[] getSupportedDeviceSpecificSettings(final GBDevice device) { final List settings = new ArrayList<>(); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java index bd0145e6e..d26e59b14 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/xiaomi/XiaomiCoordinator.java @@ -38,6 +38,8 @@ import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.appmanager.AppManagerActivity; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsScreen; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettings; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.capabilities.HeartRateCapability; import nodomain.freeyourgadget.gadgetbridge.capabilities.password.PasswordCapabilityImpl; @@ -370,117 +372,120 @@ public abstract class XiaomiCoordinator extends AbstractBLEDeviceCoordinator { } @Override - public int[] getSupportedDeviceSpecificSettings(final GBDevice device) { - final List settings = new ArrayList<>(); + public DeviceSpecificSettings getDeviceSpecificSettings(final GBDevice device) { + final DeviceSpecificSettings deviceSpecificSettings = new DeviceSpecificSettings(); if (supports(device, FEAT_WEAR_MODE)) { // TODO we should be able to get this from the band - right now it must be changed // at least once from the band itself - settings.add(R.xml.devicesettings_wearmode); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_wearmode); } // // Time // - settings.add(R.xml.devicesettings_header_time); - settings.add(R.xml.devicesettings_timeformat); + final List dateTime = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DATE_TIME); + dateTime.add(R.xml.devicesettings_timeformat); if (getWorldClocksSlotCount() > 0) { - settings.add(R.xml.devicesettings_world_clocks); + dateTime.add(R.xml.devicesettings_world_clocks); } // // Display // - settings.add(R.xml.devicesettings_header_display); + final List display = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DISPLAY); if (supports(device, FEAT_DISPLAY_ITEMS)) { - settings.add(R.xml.devicesettings_xiaomi_displayitems); + display.add(R.xml.devicesettings_xiaomi_displayitems); } if (this.supportsWidgets(device)) { - settings.add(R.xml.devicesettings_widgets); + display.add(R.xml.devicesettings_widgets); } if (supports(device, FEAT_PASSWORD)) { - settings.add(R.xml.devicesettings_password); + display.add(R.xml.devicesettings_password); } // // Health // - settings.add(R.xml.devicesettings_header_health); if (supportsStressMeasurement() && supports(device, FEAT_STRESS) && supportsSpo2() && supports(device, FEAT_SPO2)) { - settings.add(R.xml.devicesettings_heartrate_sleep_alert_activity_stress_spo2); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_heartrate_sleep_alert_activity_stress_spo2); } else if (supportsStressMeasurement() && supports(device, FEAT_STRESS)) { - settings.add(R.xml.devicesettings_heartrate_sleep_alert_activity_stress); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_heartrate_sleep_alert_activity_stress); } else { - settings.add(R.xml.devicesettings_heartrate_sleep_activity); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_heartrate_sleep_activity); } if (supports(device, FEAT_INACTIVITY)) { - settings.add(R.xml.devicesettings_inactivity_dnd_no_threshold); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_inactivity_dnd_no_threshold); } if (supports(device, FEAT_SLEEP_MODE_SCHEDULE)) { - settings.add(R.xml.devicesettings_sleep_mode_schedule); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_sleep_mode_schedule); } if (supports(device, FEAT_GOAL_NOTIFICATION)) { - settings.add(R.xml.devicesettings_goal_notification); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_goal_notification); } if (supports(device, FEAT_GOAL_SECONDARY)) { - settings.add(R.xml.devicesettings_goal_secondary); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_goal_secondary); } if (supports(device, FEAT_VITALITY_SCORE)) { - settings.add(R.xml.devicesettings_vitality_score); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_vitality_score); } // // Workout // - settings.add(R.xml.devicesettings_header_workout); - settings.add(R.xml.devicesettings_workout_start_on_phone); - settings.add(R.xml.devicesettings_workout_send_gps_to_band); + final List workout = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.WORKOUT); + workout.add(R.xml.devicesettings_workout_start_on_phone); + workout.add(R.xml.devicesettings_workout_send_gps_to_band); // // Notifications // - settings.add(R.xml.devicesettings_header_notifications); + final List notifications = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.NOTIFICATIONS); // TODO not implemented settings.add(R.xml.devicesettings_vibrationpatterns); // TODO not implemented settings.add(R.xml.devicesettings_donotdisturb_withauto_and_always); - settings.add(R.xml.devicesettings_send_app_notifications); + notifications.add(R.xml.devicesettings_send_app_notifications); if (supports(device, FEAT_SCREEN_ON_ON_NOTIFICATIONS)) { - settings.add(R.xml.devicesettings_screen_on_on_notifications); + notifications.add(R.xml.devicesettings_screen_on_on_notifications); } - settings.add(R.xml.devicesettings_autoremove_notifications); + notifications.add(R.xml.devicesettings_autoremove_notifications); if (getCannedRepliesSlotCount(device) > 0) { - settings.add(R.xml.devicesettings_canned_dismisscall_16); + notifications.add(R.xml.devicesettings_canned_dismisscall_16); } // // Calendar // if (supportsCalendarEvents()) { - settings.add(R.xml.devicesettings_header_calendar); - settings.add(R.xml.devicesettings_sync_calendar); + deviceSpecificSettings.addRootScreen( + DeviceSpecificSettingsScreen.CALENDAR, + R.xml.devicesettings_header_calendar, + R.xml.devicesettings_sync_calendar + ); } // // Other // - settings.add(R.xml.devicesettings_header_other); if (getContactsSlotCount(device) > 0) { - settings.add(R.xml.devicesettings_contacts); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_contacts); } if (supports(device, FEAT_CAMERA_REMOTE)) { - settings.add(R.xml.devicesettings_camera_remote); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_camera_remote); } if (supports(device, FEAT_DEVICE_ACTIONS)) { - settings.add(R.xml.devicesettings_device_actions); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_device_actions); } - settings.add(R.xml.devicesettings_phone_silent_mode); + deviceSpecificSettings.addRootScreen(R.xml.devicesettings_phone_silent_mode); // // Developer // - settings.add(R.xml.devicesettings_header_developer); - settings.add(R.xml.devicesettings_keep_activity_data_on_device); + deviceSpecificSettings.addRootScreen( + DeviceSpecificSettingsScreen.DEVELOPER, + R.xml.devicesettings_keep_activity_data_on_device + ); - return ArrayUtils.toPrimitive(settings.toArray(new Integer[0])); + return deviceSpecificSettings; } @Override diff --git a/app/src/main/res/xml/devicesettings_root_activity_info.xml b/app/src/main/res/xml/devicesettings_root_activity_info.xml new file mode 100644 index 000000000..fe5d7c307 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_activity_info.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_root_authentication.xml b/app/src/main/res/xml/devicesettings_root_authentication.xml new file mode 100644 index 000000000..8e561282e --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_authentication.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_root_calendar.xml b/app/src/main/res/xml/devicesettings_root_calendar.xml new file mode 100644 index 000000000..aaca622cd --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_calendar.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_root_connection.xml b/app/src/main/res/xml/devicesettings_root_connection.xml new file mode 100644 index 000000000..a5880830f --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_connection.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_root_date_time.xml b/app/src/main/res/xml/devicesettings_root_date_time.xml new file mode 100644 index 000000000..7a9f53258 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_date_time.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_root_developer.xml b/app/src/main/res/xml/devicesettings_root_developer.xml new file mode 100644 index 000000000..307e157bf --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_developer.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_root_display.xml b/app/src/main/res/xml/devicesettings_root_display.xml new file mode 100644 index 000000000..0246a6c87 --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_display.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_root_notifications.xml b/app/src/main/res/xml/devicesettings_root_notifications.xml new file mode 100644 index 000000000..e68a77eca --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_notifications.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/xml/devicesettings_root_workout.xml b/app/src/main/res/xml/devicesettings_root_workout.xml new file mode 100644 index 000000000..457c2404a --- /dev/null +++ b/app/src/main/res/xml/devicesettings_root_workout.xml @@ -0,0 +1,9 @@ + + + + +