Allow for device settings sub-screens (#3620)

Reviewed-on: https://codeberg.org/Freeyourgadget/Gadgetbridge/pulls/3620
Co-authored-by: José Rebelo <joserebelo@outlook.com>
Co-committed-by: José Rebelo <joserebelo@outlook.com>
This commit is contained in:
José Rebelo 2024-03-16 17:21:42 +00:00 committed by José Rebelo
parent fcc930749e
commit 9db60f16d1
18 changed files with 527 additions and 180 deletions

View File

@ -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 <https://www.gnu.org/licenses/>. */
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.
* <p>
* 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.
* <p>
* There can be an arbitrary number of nested sub-screens, as long as they are all mapped by key in the
* subScreens map.
* <p>
* See the XiaomiCoordinator and ZeppOsCoordinator for example usages.
*/
public class DeviceSpecificSettings implements Parcelable {
private final List<Integer> rootScreens = new ArrayList<>();
private final Map<String, List<Integer>> 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<Integer> addRootScreen(final DeviceSpecificSettingsScreen screen) {
if (!rootScreens.contains(screen.getXml())) {
rootScreens.add(screen.getXml());
}
return addSubScreen(screen, screen.getXml());
}
public List<Integer> addRootScreen(final DeviceSpecificSettingsScreen screen, final int... subScreens) {
final List<Integer> 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<Integer> addSubScreen(final DeviceSpecificSettingsScreen rootScreen, final int... screens) {
return addSubScreen(rootScreen.getKey(), screens);
}
private List<Integer> addSubScreen(final String key, final int... screens) {
if (!subScreens.containsKey(key)) {
subScreens.put(key, new ArrayList<>());
}
final List<Integer> 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<String, List<Integer>> 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<Integer> getRootScreens() {
return rootScreens;
}
@Nullable
public List<Integer> getScreen(@NonNull final String key) {
return subScreens.get(key);
}
public List<Integer> getAllScreens() {
final List<Integer> allScreens = new ArrayList<>(rootScreens);
for (final List<Integer> screens : subScreens.values()) {
allScreens.addAll(screens);
}
return allScreens;
}
public static final Creator<DeviceSpecificSettings> CREATOR = new Creator<DeviceSpecificSettings>() {
@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<String, List<Integer>> e : subScreens.entrySet()) {
dest.writeString(e.getKey());
dest.writeInt(e.getValue().size());
for (final Integer s : e.getValue()) {
dest.writeInt(s);
}
}
}
}

View File

@ -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<Integer> 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<Integer> 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<Integer> 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<Integer> 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);

View File

@ -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 <https://www.gnu.org/licenses/>. */
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;
}
}

View File

@ -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

View File

@ -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.
*/

View File

@ -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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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

View File

@ -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<GBDevice> devices = GBApplication.app().getDeviceManager().getSelectedDevices();
for(GBDevice device : devices){

View File

@ -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<Integer> settings = new ArrayList<>();

View File

@ -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<Integer> 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<Integer> 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<Integer> 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<Integer> 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<Integer> 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

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_activity_unknown_small"
android:key="pref_screen_activity_info"
android:persistent="false"
android:title="@string/activity_info">
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_vpn_key"
android:key="pref_screen_authentication"
android:persistent="false"
android:title="@string/pref_header_authentication">
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_calendar_today"
android:key="pref_screen_calendar"
android:persistent="false"
android:title="@string/pref_header_calendar">
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_mtu"
android:key="pref_screen_connection"
android:persistent="false"
android:title="@string/pref_header_connection">
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_timer"
android:key="pref_screen_date_time"
android:persistent="false"
android:title="@string/dateformat_date_time">
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_developer_mode"
android:key="pref_screen_developer"
android:persistent="false"
android:title="@string/pref_title_developer_settings">
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_watch"
android:key="pref_screen_display"
android:persistent="false"
android:title="@string/pref_header_display">
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_notifications"
android:key="pref_screen_notifications"
android:persistent="false"
android:title="@string/pref_header_notifications">
</PreferenceScreen>
</androidx.preference.PreferenceScreen>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceScreen
android:icon="@drawable/ic_activity_unknown_small"
android:key="pref_screen_workout"
android:persistent="false"
android:title="@string/menuitem_workout">
</PreferenceScreen>
</androidx.preference.PreferenceScreen>