diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsCustomizer.java index 3a8ac1323..fc948131a 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsCustomizer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsCustomizer.java @@ -20,6 +20,8 @@ import android.os.Parcelable; import androidx.preference.Preference; +import java.util.Set; + /** * A device-specific preference handler, that allows for concrete implementations to customize the preferences in * the {@link DeviceSpecificSettingsFragment}. @@ -39,4 +41,9 @@ public interface DeviceSpecificSettingsCustomizer extends Parcelable { * @param handler the {@link DeviceSpecificSettingsHandler} */ void customizeSettings(final DeviceSpecificSettingsHandler handler); + + /** + * Keys of preferences which should print its values as a summary below the preference name. + */ + Set getPreferenceKeysWithSummary(); } 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 4671a325d..cc7d588b1 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 @@ -43,7 +43,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; import nodomain.freeyourgadget.gadgetbridge.GBApplication; import nodomain.freeyourgadget.gadgetbridge.R; @@ -806,7 +808,8 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp } } - private void setInputTypeFor(final String preferenceKey, final int editTypeFlags) { + @Override + public void setInputTypeFor(final String preferenceKey, final int editTypeFlags) { EditTextPreference textPreference = findPreference(preferenceKey); if (textPreference != null) { textPreference.setOnBindEditTextListener(new EditTextPreference.OnBindEditTextListener() { @@ -818,6 +821,19 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp } } + /** + * Keys of preferences which should print its values as a summary below the preference name. + */ + protected Set getPreferenceKeysWithSummary() { + final Set keysWithSummary = new HashSet<>(); + + if (deviceSpecificSettingsCustomizer != null) { + keysWithSummary.addAll(deviceSpecificSettingsCustomizer.getPreferenceKeysWithSummary()); + } + + return keysWithSummary; + } + /** * Reload the preferences in the current screen. This is needed when the user enters or exists a PreferenceScreen, * otherwise the settings won't be reloaded by the {@link SharedPreferencesChangeHandler}, as the preferences return @@ -849,7 +865,7 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { LOG.debug("Preference changed: {}", key); - if(key == null){ + if (key == null){ LOG.warn("Preference null, ignoring"); return; } @@ -879,6 +895,11 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp LOG.warn("Unknown preference class {}, ignoring", preference.getClass()); } + if (getPreferenceKeysWithSummary().contains(key)) { + final String summary = prefs.getString(key, preference.getSummary() != null ? preference.getSummary().toString() : ""); + preference.setSummary(summary); + } + if (deviceSpecificSettingsCustomizer != null) { deviceSpecificSettingsCustomizer.onPreferenceChange(preference, DeviceSpecificSettingsFragment.this); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java index 47d4a4095..97917e837 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/devicesettings/DeviceSpecificSettingsHandler.java @@ -53,4 +53,12 @@ public interface DeviceSpecificSettingsHandler { * @param extraListener the extra listener. */ void addPreferenceHandlerFor(final String preferenceKey, Preference.OnPreferenceChangeListener extraListener); + + /** + * Sets the input type flags for an EditText preference. + * + * @param preferenceKey the preference key. + * @param editTypeFlags the edit type {@link android.text.InputType} flags. + */ + void setInputTypeFor(final String preferenceKey, final int editTypeFlags); } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java index 2ec6b9f77..1b388391c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiConst.java @@ -86,6 +86,39 @@ public class HuamiConst { public static final String PREF_DEVICE_ACTION_START_NON_WEAR_SELECTION = "events_forwarding_startnonwear_action_selection"; public static final String PREF_DEVICE_ACTION_START_NON_WEAR_BROADCAST = "prefs_events_forwarding_startnonwear_broadcast"; + /** + * The suffixes match the enum {@link nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType}. + */ + // profile + public static final String PREF_HUAMI_VIBRATION_PROFILE_PREFIX = "huami_vibration_profile_"; + public static final String PREF_HUAMI_VIBRATION_PROFILE_APP_ALERTS = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "app_alerts"; + public static final String PREF_HUAMI_VIBRATION_PROFILE_INCOMING_CALL = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "incoming_call"; + public static final String PREF_HUAMI_VIBRATION_PROFILE_INCOMING_SMS = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "incoming_sms"; + public static final String PREF_HUAMI_VIBRATION_PROFILE_GOAL_NOTIFICATION = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "goal_notification"; + public static final String PREF_HUAMI_VIBRATION_PROFILE_ALARM = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "alarm"; + public static final String PREF_HUAMI_VIBRATION_PROFILE_IDLE_ALERTS = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "idle_alerts"; + public static final String PREF_HUAMI_VIBRATION_PROFILE_EVENT_REMINDER = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "event_reminder"; + public static final String PREF_HUAMI_VIBRATION_PROFILE_FIND_BAND = PREF_HUAMI_VIBRATION_PROFILE_PREFIX + "find_band"; + // count + public static final String PREF_HUAMI_VIBRATION_COUNT_PREFIX = "huami_vibration_count_"; + public static final String PREF_HUAMI_VIBRATION_COUNT_APP_ALERTS = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "app_alerts"; + public static final String PREF_HUAMI_VIBRATION_COUNT_INCOMING_CALL = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "incoming_call"; + public static final String PREF_HUAMI_VIBRATION_COUNT_INCOMING_SMS = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "incoming_sms"; + public static final String PREF_HUAMI_VIBRATION_COUNT_GOAL_NOTIFICATION = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "goal_notification"; + public static final String PREF_HUAMI_VIBRATION_COUNT_ALARM = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "alarm"; + public static final String PREF_HUAMI_VIBRATION_COUNT_IDLE_ALERTS = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "idle_alerts"; + public static final String PREF_HUAMI_VIBRATION_COUNT_EVENT_REMINDER = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "event_reminder"; + public static final String PREF_HUAMI_VIBRATION_COUNT_FIND_BAND = PREF_HUAMI_VIBRATION_COUNT_PREFIX + "find_band"; + // try + public static final String PREF_HUAMI_VIBRATION_TRY_PREFIX = "huami_vibration_try_"; + public static final String PREF_HUAMI_VIBRATION_TRY_APP_ALERTS = PREF_HUAMI_VIBRATION_TRY_PREFIX + "app_alerts"; + public static final String PREF_HUAMI_VIBRATION_TRY_INCOMING_CALL = PREF_HUAMI_VIBRATION_TRY_PREFIX + "incoming_call"; + public static final String PREF_HUAMI_VIBRATION_TRY_INCOMING_SMS = PREF_HUAMI_VIBRATION_TRY_PREFIX + "incoming_sms"; + public static final String PREF_HUAMI_VIBRATION_TRY_GOAL_NOTIFICATION = PREF_HUAMI_VIBRATION_TRY_PREFIX + "goal_notification"; + public static final String PREF_HUAMI_VIBRATION_TRY_ALARM = PREF_HUAMI_VIBRATION_TRY_PREFIX + "alarm"; + public static final String PREF_HUAMI_VIBRATION_TRY_IDLE_ALERTS = PREF_HUAMI_VIBRATION_TRY_PREFIX + "idle_alerts"; + public static final String PREF_HUAMI_VIBRATION_TRY_EVENT_REMINDER = PREF_HUAMI_VIBRATION_TRY_PREFIX + "event_reminder"; + public static final String PREF_HUAMI_VIBRATION_TRY_FIND_BAND = PREF_HUAMI_VIBRATION_TRY_PREFIX + "find_band"; public static int toActivityKind(int rawType) { switch (rawType) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java index af54a0a2f..c19c6b18d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiCoordinator.java @@ -43,6 +43,7 @@ import nodomain.freeyourgadget.gadgetbridge.GBException; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; import nodomain.freeyourgadget.gadgetbridge.devices.AbstractDeviceCoordinator; import nodomain.freeyourgadget.gadgetbridge.devices.SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.miband.DateTimeDisplay; @@ -51,11 +52,13 @@ import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBand2SampleProvider import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandPairingActivity; import nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandService; +import nodomain.freeyourgadget.gadgetbridge.devices.miband.VibrationProfile; import nodomain.freeyourgadget.gadgetbridge.entities.AbstractActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession; import nodomain.freeyourgadget.gadgetbridge.entities.Device; import nodomain.freeyourgadget.gadgetbridge.entities.MiBandActivitySampleDao; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType; import nodomain.freeyourgadget.gadgetbridge.util.Prefs; public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { @@ -270,6 +273,17 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { return prefs.getBoolean(DeviceSettingsPreferenceConst.PREF_BT_CONNECTED_ADVERTISEMENT, false); } + public static VibrationProfile getVibrationProfile(String deviceAddress, HuamiVibrationPatternNotificationType notificationType) { + Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(deviceAddress)); + final String vibrationProfileId = prefs.getString( + HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_PREFIX + notificationType.name().toLowerCase(Locale.ROOT), + VibrationProfile.ID_MEDIUM + ); + final int vibrationProfileCount = prefs.getInt(HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX + notificationType.name().toLowerCase(Locale.ROOT), 2); + + return VibrationProfile.getProfile(vibrationProfileId, (short) vibrationProfileCount); + } + protected static Date getTimePreference(String key, String defaultValue, String deviceAddress) { Prefs prefs; @@ -353,4 +367,9 @@ public abstract class HuamiCoordinator extends AbstractDeviceCoordinator { public int getReminderSlotCount() { return 22; // At least, Mi Fit still allows more } + + @Override + public DeviceSpecificSettingsCustomizer getDeviceSpecificSettingsCustomizer(final GBDevice device) { + return new HuamiSettingsCustomizer(device); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiSettingsCustomizer.java new file mode 100644 index 000000000..fef6626d2 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/HuamiSettingsCustomizer.java @@ -0,0 +1,104 @@ +/* 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.devices.huami; + +import android.os.Parcel; +import android.text.InputType; +import android.widget.Toast; + +import androidx.preference.Preference; + +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; + +import nodomain.freeyourgadget.gadgetbridge.GBApplication; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; +import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsHandler; +import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; +import nodomain.freeyourgadget.gadgetbridge.service.devices.huami.HuamiVibrationPatternNotificationType; + +public class HuamiSettingsCustomizer implements DeviceSpecificSettingsCustomizer { + final GBDevice device; + + public HuamiSettingsCustomizer(final GBDevice device) { + this.device = device; + } + + @Override + public void onPreferenceChange(final Preference preference, final DeviceSpecificSettingsHandler handler) { + // Nothing to do here + } + + @Override + public void customizeSettings(final DeviceSpecificSettingsHandler handler) { + for (HuamiVibrationPatternNotificationType notificationType : HuamiVibrationPatternNotificationType.values()) { + final String typeKey = notificationType.name().toLowerCase(Locale.ROOT); + + handler.addPreferenceHandlerFor(HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_PREFIX + typeKey); + handler.addPreferenceHandlerFor(HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX + typeKey); + handler.setInputTypeFor(HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX + typeKey, InputType.TYPE_CLASS_NUMBER); + + final String tryPrefKey = HuamiConst.PREF_HUAMI_VIBRATION_TRY_PREFIX + typeKey; + final Preference tryPref = handler.findPreference(tryPrefKey); + if (tryPref != null) { + tryPref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(final Preference preference) { + GBApplication.deviceService().onSendConfiguration(tryPrefKey); + return true; + } + }); + } + } + } + + @Override + public Set getPreferenceKeysWithSummary() { + final Set keysWithSummary = new HashSet<>(); + + for (HuamiVibrationPatternNotificationType notificationType : HuamiVibrationPatternNotificationType.values()) { + final String typeKey = notificationType.name().toLowerCase(Locale.ROOT); + keysWithSummary.add(HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX + typeKey); + } + + return keysWithSummary; + } + + public static final Creator CREATOR = new Creator() { + @Override + public HuamiSettingsCustomizer createFromParcel(final Parcel in) { + final GBDevice device = in.readParcelable(HuamiSettingsCustomizer.class.getClassLoader()); + return new HuamiSettingsCustomizer(device); + } + + @Override + public HuamiSettingsCustomizer[] newArray(final int size) { + return new HuamiSettingsCustomizer[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(final Parcel dest, final int flags) { + dest.writeParcelable(device, 0); + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband5/MiBand5Coordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband5/MiBand5Coordinator.java index 8bf08ac85..d8bd5ac5f 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband5/MiBand5Coordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/huami/miband5/MiBand5Coordinator.java @@ -99,6 +99,7 @@ public class MiBand5Coordinator extends HuamiCoordinator { public int[] getSupportedDeviceSpecificSettings(GBDevice device) { return new int[]{ R.xml.devicesettings_miband5, + R.xml.devicesettings_miband5_vibration, R.xml.devicesettings_wearlocation, R.xml.devicesettings_custom_emoji_font, R.xml.devicesettings_timeformat, diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/VibrationProfile.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/VibrationProfile.java index d7505bcdc..70cbd9d3c 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/VibrationProfile.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/miband/VibrationProfile.java @@ -79,6 +79,10 @@ public class VibrationProfile { * @param repeat how often the sequence shall be repeated */ public VibrationProfile(String id, int[] onOffSequence, short repeat) { + if (onOffSequence.length % 2 != 0) { + throw new IllegalArgumentException("Each on duration must have a subsequent off duration"); + } + this.id = id; this.repeat = repeat; this.onOffSequence = onOffSequence; diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java index 3bb3148dc..0a8eb477d 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/sony/headphones/SonyHeadphonesSettingsCustomizer.java @@ -47,8 +47,10 @@ import androidx.preference.ListPreference; import androidx.preference.Preference; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Set; import nodomain.freeyourgadget.gadgetbridge.R; import nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSpecificSettingsCustomizer; @@ -185,6 +187,11 @@ public class SonyHeadphonesSettingsCustomizer implements DeviceSpecificSettingsC } } + @Override + public Set getPreferenceKeysWithSummary() { + return Collections.emptySet(); + } + public static final Creator CREATOR = new Creator() { @Override public SonyHeadphonesSettingsCustomizer createFromParcel(final Parcel in) { diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java index e6d692330..a2b4b40b9 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiSupport.java @@ -197,6 +197,33 @@ import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_START_NON_WEAR_SELECTION; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_WOKE_UP_BROADCAST; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_DEVICE_ACTION_WOKE_UP_SELECTION; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_ALARM; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_APP_ALERTS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_EVENT_REMINDER; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_FIND_BAND; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_GOAL_NOTIFICATION; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_IDLE_ALERTS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_INCOMING_CALL; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_INCOMING_SMS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_COUNT_PREFIX; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_ALARM; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_APP_ALERTS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_EVENT_REMINDER; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_FIND_BAND; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_GOAL_NOTIFICATION; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_IDLE_ALERTS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_INCOMING_CALL; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_INCOMING_SMS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_PROFILE_PREFIX; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_ALARM; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_APP_ALERTS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_EVENT_REMINDER; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_FIND_BAND; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_GOAL_NOTIFICATION; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_IDLE_ALERTS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_INCOMING_CALL; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_INCOMING_SMS; +import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst.PREF_HUAMI_VIBRATION_TRY_PREFIX; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.DISPLAY_ITEM_BIT_CLOCK; import static nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiService.ENDPOINT_DISPLAY_ITEMS; import static nodomain.freeyourgadget.gadgetbridge.devices.miband.MiBandConst.DEFAULT_VALUE_VIBRATION_COUNT; @@ -2465,6 +2492,32 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { case PREF_USER_GENDER: setUserInfo(builder); break; + case PREF_HUAMI_VIBRATION_PROFILE_APP_ALERTS: + case PREF_HUAMI_VIBRATION_PROFILE_INCOMING_CALL: + case PREF_HUAMI_VIBRATION_PROFILE_INCOMING_SMS: + case PREF_HUAMI_VIBRATION_PROFILE_GOAL_NOTIFICATION: + case PREF_HUAMI_VIBRATION_PROFILE_ALARM: + case PREF_HUAMI_VIBRATION_PROFILE_IDLE_ALERTS: + case PREF_HUAMI_VIBRATION_PROFILE_EVENT_REMINDER: + case PREF_HUAMI_VIBRATION_PROFILE_FIND_BAND: + case PREF_HUAMI_VIBRATION_COUNT_APP_ALERTS: + case PREF_HUAMI_VIBRATION_COUNT_INCOMING_CALL: + case PREF_HUAMI_VIBRATION_COUNT_INCOMING_SMS: + case PREF_HUAMI_VIBRATION_COUNT_GOAL_NOTIFICATION: + case PREF_HUAMI_VIBRATION_COUNT_ALARM: + case PREF_HUAMI_VIBRATION_COUNT_IDLE_ALERTS: + case PREF_HUAMI_VIBRATION_COUNT_EVENT_REMINDER: + case PREF_HUAMI_VIBRATION_COUNT_FIND_BAND: + case PREF_HUAMI_VIBRATION_TRY_APP_ALERTS: + case PREF_HUAMI_VIBRATION_TRY_INCOMING_CALL: + case PREF_HUAMI_VIBRATION_TRY_INCOMING_SMS: + case PREF_HUAMI_VIBRATION_TRY_GOAL_NOTIFICATION: + case PREF_HUAMI_VIBRATION_TRY_ALARM: + case PREF_HUAMI_VIBRATION_TRY_IDLE_ALERTS: + case PREF_HUAMI_VIBRATION_TRY_EVENT_REMINDER: + case PREF_HUAMI_VIBRATION_TRY_FIND_BAND: + setVibrationPattern(builder, config); + break; } builder.queue(getQueue()); } catch (IOException e) { @@ -2480,16 +2533,82 @@ public class HuamiSupport extends AbstractBTLEDeviceSupport { @Override public void onTestNewFunction() { //requestMTU(23); - /* try { - boolean test = false; - TransactionBuilder builder = performInitialized("test pattern"); - byte[] testpattern = new byte[] {0x20,0x00, (byte) 0x4b,0x64,0x00, (byte) 0x8d,0x01,0x73,0x00,0x38,0x01,0x64,0x00,0x64,0x00,0x64,0x00,0x67,0x00,0x64,0x00,0x37,0x01,0x7c,0x00,0x64,0x00,0x64,0x00,0x67,0x00,0x64,0x00,0x67,0x00,0x64,0x00,0x37,0x01,0x64,0x00,0x64,0x00,0x64,0x00, (byte) 0xe5,0x02}; - //byte[] testpattern = new byte[] {0x20,0x00, (byte) 0x00, 0,0,0,0}; - writeToChunked(builder,2, testpattern); + final TransactionBuilder builder = performInitialized("test pattern"); + final VibrationProfile profile = VibrationProfile.getProfile(VibrationProfile.ID_SHORT, (short) 2); + + setVibrationPattern(builder, HuamiVibrationPatternNotificationType.APP_ALERTS, true, profile); builder.queue(getQueue()); - } catch (Exception ignored) {} - */ + } catch (final Exception e) { + LOG.error("onTestNewFunction failed", e); + } + } + + private void setVibrationPattern(final TransactionBuilder builder, final String preferenceKey) { + // The preference key has one of the 3 prefixes + final String notificationTypeName = preferenceKey.replace(PREF_HUAMI_VIBRATION_COUNT_PREFIX, "") + .replace(PREF_HUAMI_VIBRATION_PROFILE_PREFIX, "") + .replace(PREF_HUAMI_VIBRATION_TRY_PREFIX, "") + .toUpperCase(Locale.ROOT); + final HuamiVibrationPatternNotificationType notificationType = HuamiVibrationPatternNotificationType.valueOf(notificationTypeName); + final boolean isTry = preferenceKey.startsWith(PREF_HUAMI_VIBRATION_TRY_PREFIX); + + final VibrationProfile vibrationProfile = HuamiCoordinator.getVibrationProfile(getDevice().getAddress(), notificationType); + + setVibrationPattern(builder, notificationType, isTry, vibrationProfile); + } + + /** + * Test or set a {@link VibrationProfile}. + * + * @param builder the {@link TransactionBuilder} + * @param notificationType the notification type + * @param test test the pattern (only vibrate the band, do not set it) + * @param profile the {@link VibrationProfile} + */ + private void setVibrationPattern(final TransactionBuilder builder, + final HuamiVibrationPatternNotificationType notificationType, + final boolean test, + final VibrationProfile profile) { + final int MAX_TOTAL_LENGTH_MS = 10_000; // 10 seconds, about as long as Mi Fit allows + int totalLengthMs = 0; + + // The on-off sequence, until the max total length is reached + final List onOff = new ArrayList<>(profile.getOnOffSequence().length); + + for (int c = 0; c < profile.getRepeat(); c++) { + for (int i = 0; i < profile.getOnOffSequence().length; i += 2) { + final short on = (short) profile.getOnOffSequence()[i]; + final short off = (short) profile.getOnOffSequence()[i + 1]; + + if (totalLengthMs + on + off > MAX_TOTAL_LENGTH_MS) { + LOG.warn("VibrationProfile {} too long, truncating to {} ms", profile.getId(), MAX_TOTAL_LENGTH_MS); + break; + } + + onOff.add(on); + onOff.add(off); + totalLengthMs += on + off; + } + } + + final ByteBuffer buf = ByteBuffer.allocate(3 + 2 * onOff.size()); + buf.order(ByteOrder.LITTLE_ENDIAN); + + buf.put((byte) 0x20); + buf.put(notificationType.getCode()); + byte flag = (byte) (onOff.size() / 2); + flag |= 0x40; + if (test) { + flag |= 0x80; + } + buf.put(flag); + + for (Short time : onOff) { + buf.putShort(time); + } + + writeToChunked(builder, 2, buf.array()); } @Override diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiVibrationPatternNotificationType.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiVibrationPatternNotificationType.java new file mode 100644 index 000000000..ba9ea0aae --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/huami/HuamiVibrationPatternNotificationType.java @@ -0,0 +1,42 @@ +/* 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.service.devices.huami; + +/** + * The notification types for which vibration patterns are customizable. If these change, the + * constants in {@link nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst} need to be updated. + */ +public enum HuamiVibrationPatternNotificationType { + APP_ALERTS(0x00), + INCOMING_CALL(0x01), + INCOMING_SMS(0x02), + GOAL_NOTIFICATION(0x04), + ALARM(0x05), + IDLE_ALERTS(0x06), + EVENT_REMINDER(0x08), + FIND_BAND(0x09); + + private final byte code; + + HuamiVibrationPatternNotificationType(final int code) { + this.code = (byte) code; + } + + public byte getCode() { + return code; + } +} diff --git a/app/src/main/res/drawable/ic_action_find_lost_device.xml b/app/src/main/res/drawable/ic_action_find_lost_device.xml index 8fdf6c723..d13a11d17 100644 --- a/app/src/main/res/drawable/ic_action_find_lost_device.xml +++ b/app/src/main/res/drawable/ic_action_find_lost_device.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> diff --git a/app/src/main/res/drawable/ic_device_set_reminders.xml b/app/src/main/res/drawable/ic_device_set_reminders.xml index 7009a6763..aa25afa2a 100644 --- a/app/src/main/res/drawable/ic_device_set_reminders.xml +++ b/app/src/main/res/drawable/ic_device_set_reminders.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/app/src/main/res/drawable/ic_phone.xml b/app/src/main/res/drawable/ic_phone.xml new file mode 100644 index 000000000..08b0fc27e --- /dev/null +++ b/app/src/main/res/drawable/ic_phone.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_seat_normal.xml b/app/src/main/res/drawable/ic_seat_normal.xml new file mode 100644 index 000000000..ca5626916 --- /dev/null +++ b/app/src/main/res/drawable/ic_seat_normal.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_star_gray.xml b/app/src/main/res/drawable/ic_star_gray.xml new file mode 100644 index 000000000..24d9e7985 --- /dev/null +++ b/app/src/main/res/drawable/ic_star_gray.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7cff8e280..f00cdc87e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -471,6 +471,11 @@ Low power warning Anti-loss warning Whole day HR measurement + Event reminder + Find device + Idle Alerts + Vibration Patterns + Configure the vibration patterns for different notifications once a minute every 5 minutes every 10 minutes diff --git a/app/src/main/res/xml/devicesettings_miband5_vibration.xml b/app/src/main/res/xml/devicesettings_miband5_vibration.xml new file mode 100644 index 000000000..a269b775d --- /dev/null +++ b/app/src/main/res/xml/devicesettings_miband5_vibration.xml @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +