GloryFit: Add screen timeout setting

This commit is contained in:
José Rebelo
2025-12-31 19:30:44 +00:00
parent a87ddbb66a
commit 921e1f8254
7 changed files with 168 additions and 20 deletions
@@ -196,6 +196,7 @@ abstract class GloryFitCoordinator : AbstractBLEDeviceCoordinator() {
val display = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.DISPLAY)
display.add(R.xml.devicesettings_liftwrist_display_noshed)
display.add(R.xml.devicesettings_screen_timeout)
val health = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.HEALTH)
health.add(R.xml.devicesettings_heartrate_automatic_enable)
@@ -16,6 +16,7 @@
along with this program. If not, see <https://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.devices.gloryfit
import androidx.preference.ListPreference
import androidx.preference.Preference
import kotlinx.parcelize.Parcelize
import nodomain.freeyourgadget.gadgetbridge.R
@@ -62,6 +63,19 @@ class GloryFitSettingsCustomizer : DeviceSpecificSettingsCustomizer {
dndStart?.isVisible = false
val dndEnd = handler.findPreference<Preference>(DeviceSettingsPreferenceConst.PREF_INACTIVITY_DND_END)
dndEnd?.isVisible = false
handler.findPreference<ListPreference>(DeviceSettingsPreferenceConst.PREF_SCREEN_TIMEOUT)?.let {
it.entries = arrayOf(
handler.context.getString(R.string.seconds_5),
handler.context.getString(R.string.seconds_10),
handler.context.getString(R.string.seconds_15),
)
it.entryValues = arrayOf(
"5",
"10",
"15",
)
}
}
override fun getPreferenceKeysWithSummary(): Set<String> {
@@ -59,7 +59,7 @@ import java.util.Calendar
import java.util.GregorianCalendar
import java.util.Locale
import java.util.UUID
import kotlin.math.roundToInt
import nodomain.freeyourgadget.gadgetbridge.util.kotlin.coerceIn
class GloryFitSupport() : AbstractBTLESingleDeviceSupport(LOG) {
init {
@@ -944,10 +944,10 @@ class GloryFitSupport() : AbstractBTLESingleDeviceSupport(LOG) {
buf.put(CMD_USER_INFO)
buf.putShort(activityUser.heightCm.coerceIn(91, 241).toShort())
buf.putShort(activityUser.weightKg.coerceIn(20, 255).toShort())
buf.put(0x05) // ?
buf.put(devicePrefs.screenTimeout.coerceIn(5, 15, 5).toByte())
buf.put(0x00) // ?
buf.put(0x00) // ?
buf.putShort(((activityUser.stepsGoal / 1000.0).roundToInt() * 1000).coerceIn(1000, 30000).toShort())
buf.putShort(activityUser.stepsGoal.coerceIn(1000, 30000, 1000).toShort())
buf.put(if (raiseHandToActivateDisplay) 0x01 else 0x00)
buf.put(heartRateAlertHigh)
buf.put(0x00) // ?
@@ -970,7 +970,7 @@ class GloryFitSupport() : AbstractBTLESingleDeviceSupport(LOG) {
fun setGoalSteps(builder: TransactionBuilder) {
val activityUser = ActivityUser()
val stepsCoerced = ((activityUser.stepsGoal / 1000.0).roundToInt() * 1000).coerceIn(1000, 30000).toShort()
val stepsCoerced = activityUser.stepsGoal.coerceIn(1000, 30000, 1000).toShort()
LOG.debug("Setting steps goal to {}", stepsCoerced)
@@ -988,7 +988,7 @@ class GloryFitSupport() : AbstractBTLESingleDeviceSupport(LOG) {
fun setGoalCalories(builder: TransactionBuilder) {
val activityUser = ActivityUser()
val caloriesCoerced = ((activityUser.caloriesBurntGoal / 50.0).roundToInt() * 50).coerceIn(50, 1000).toShort()
val caloriesCoerced = activityUser.caloriesBurntGoal.coerceIn(50, 1000, 50).toShort()
LOG.debug("Setting calories goal to {}", caloriesCoerced)
@@ -1004,8 +1004,7 @@ class GloryFitSupport() : AbstractBTLESingleDeviceSupport(LOG) {
fun setGoalDistance(builder: TransactionBuilder) {
val activityUser = ActivityUser()
val distanceCoerced =
((activityUser.distanceGoalMeters / 1000f).roundToInt() * 1000).coerceIn(1000, 20000).toShort()
val distanceCoerced = activityUser.distanceGoalMeters.coerceIn(1000, 20000, 1000).toShort()
LOG.debug("Setting distance goal to {}", distanceCoerced)
@@ -1037,7 +1036,7 @@ class GloryFitSupport() : AbstractBTLESingleDeviceSupport(LOG) {
val buf = ByteBuffer.allocate(12).order(ByteOrder.BIG_ENDIAN)
buf.put(CMD_SEDENTARY_REMINDER)
buf.put(if (enabled) 0x01 else 0x00)
buf.put(((duration / 5.0).roundToInt() * 5).coerceIn(30, 180).toByte())
buf.put(duration.coerceIn(30, 180, 5).toByte())
buf.put(0x02) // ?
buf.put(0x03) // ?
buf.put(0x01) // ?
@@ -0,0 +1,7 @@
package nodomain.freeyourgadget.gadgetbridge.util.kotlin
import kotlin.math.roundToInt
fun Int.coerceIn(min: Int, max: Int, step: Int): Int {
return ((this / step.toDouble()).roundToInt() * step).coerceIn(min, max)
}
@@ -156,4 +156,8 @@ public class DevicePrefs extends Prefs {
public int getHeartRateLowThreshold() {
return getInt(DeviceSettingsPreferenceConst.PREF_HEARTRATE_ALERT_LOW_THRESHOLD, 0);
}
public int getScreenTimeout() {
return getInt(PREF_SCREEN_TIMEOUT, 5);
}
}
@@ -1,32 +1,20 @@
package nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.messages;
import static org.junit.Assert.*;
import androidx.annotation.NonNull;
import org.junit.Assert;
import org.junit.Test;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.activities.DebugActivity;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.model.weather.WeatherMapper;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.GarminSupport;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FieldDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.FitLocalMessageBuilder;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordData;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.RecordDefinition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherAqi;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.fit.fieldDefinitions.FieldDefinitionWeatherCondition;
import nodomain.freeyourgadget.gadgetbridge.service.devices.garmin.messages.MessageWriter;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
@@ -0,0 +1,135 @@
package nodomain.freeyourgadget.gadgetbridge.util.kotlin
import org.junit.Assert.assertEquals
import org.junit.Test
class IntExtensionsTest {
@Test
fun testCoerceInWithStep_valueWithinRange() {
// Value already on a step boundary within range
assertEquals(50, 50.coerceIn(0, 100, 10))
// Value within range but not on step boundary - should round to nearest step
assertEquals(50, 52.coerceIn(0, 100, 10))
assertEquals(50, 48.coerceIn(0, 100, 10))
}
@Test
fun testCoerceInWithStep_valueBelowMin() {
// Value below minimum should be coerced to minimum (respecting step)
assertEquals(0, (-10).coerceIn(0, 100, 10))
assertEquals(10, 5.coerceIn(10, 100, 10))
}
@Test
fun testCoerceInWithStep_valueAboveMax() {
// Value above maximum should be coerced to maximum
assertEquals(100, 150.coerceIn(0, 100, 10))
assertEquals(100, 105.coerceIn(0, 100, 10))
}
@Test
fun testCoerceInWithStep_roundingToNearestStep() {
// Test rounding behavior: rounds to nearest step
assertEquals(10, 6.coerceIn(0, 100, 10))
assertEquals(10, 7.coerceIn(0, 100, 10))
assertEquals(10, 8.coerceIn(0, 100, 10))
assertEquals(10, 9.coerceIn(0, 100, 10))
assertEquals(10, 10.coerceIn(0, 100, 10))
assertEquals(10, 11.coerceIn(0, 100, 10))
assertEquals(10, 12.coerceIn(0, 100, 10))
assertEquals(10, 13.coerceIn(0, 100, 10))
assertEquals(10, 14.coerceIn(0, 100, 10))
// > 15 should round to 20
assertEquals(20, 15.coerceIn(0, 100, 10))
assertEquals(20, 16.coerceIn(0, 100, 10))
assertEquals(20, 17.coerceIn(0, 100, 10))
assertEquals(20, 18.coerceIn(0, 100, 10))
assertEquals(20, 19.coerceIn(0, 100, 10))
}
@Test
fun testCoerceInWithStep_differentStepSizes() {
// Test with step size of 5
assertEquals(15, 14.coerceIn(0, 100, 5))
assertEquals(15, 16.coerceIn(0, 100, 5))
// Test with step size of 25
assertEquals(25, 24.coerceIn(0, 100, 25))
assertEquals(25, 30.coerceIn(0, 100, 25))
// Test with step size of 1 (should behave like normal coerceIn)
assertEquals(42, 42.coerceIn(0, 100, 1))
assertEquals(10, 10.coerceIn(0, 100, 1))
assertEquals(15, 15.coerceIn(0, 100, 1))
assertEquals(0, (-5).coerceIn(0, 100, 1))
assertEquals(100, 105.coerceIn(0, 100, 1))
}
@Test
fun testCoerceInWithStep_negativeRanges() {
// Test with negative min and max
assertEquals(-50, (-52).coerceIn(-100, 0, 10))
assertEquals(-50, (-48).coerceIn(-100, 0, 10))
assertEquals(-100, (-150).coerceIn(-100, 0, 10))
assertEquals(0, 50.coerceIn(-100, 0, 10))
}
@Test
fun testCoerceInWithStep_rangeSpanningZero() {
// Test with range spanning negative to positive
assertEquals(0, 2.coerceIn(-50, 50, 10))
assertEquals(10, 12.coerceIn(-50, 50, 10))
assertEquals(-10, (-12).coerceIn(-50, 50, 10))
}
@Test
fun testCoerceInWithStep_edgeCases() {
// Value exactly at minimum
assertEquals(0, 0.coerceIn(0, 100, 10))
// Value exactly at maximum
assertEquals(100, 100.coerceIn(0, 100, 10))
// Single value range (min == max)
assertEquals(50, 50.coerceIn(50, 50, 10))
assertEquals(50, 45.coerceIn(50, 50, 10))
assertEquals(50, 55.coerceIn(50, 50, 10))
}
@Test
fun testCoerceInWithStep_largeStepSize() {
// Step size larger than range
assertEquals(0, 25.coerceIn(0, 50, 100))
assertEquals(0, 49.coerceIn(0, 50, 100))
assertEquals(50, 51.coerceIn(0, 50, 100))
assertEquals(50, 99.coerceIn(0, 50, 100))
assertEquals(50, 101.coerceIn(0, 50, 100))
assertEquals(50, 150.coerceIn(0, 50, 100))
assertEquals(50, 200.coerceIn(0, 50, 100))
assertEquals(50, 250.coerceIn(0, 50, 100))
assertEquals(0, (-10).coerceIn(0, 50, 100))
}
@Test
fun testCoerceInWithStep_smallValues() {
// Test with small ranges and steps
assertEquals(0, 0.coerceIn(0, 10, 2))
assertEquals(2, 1.coerceIn(0, 10, 2))
assertEquals(2, 2.coerceIn(0, 10, 2))
assertEquals(4, 3.coerceIn(0, 10, 2))
assertEquals(4, 4.coerceIn(0, 10, 2))
assertEquals(6, 5.coerceIn(0, 10, 2))
assertEquals(6, 6.coerceIn(0, 10, 2))
assertEquals(8, 7.coerceIn(0, 10, 2))
assertEquals(8, 8.coerceIn(0, 10, 2))
}
@Test
fun testCoerceInWithStep_maxNotOnStepBoundary() {
// When max is not on a step boundary
// e.g., 0, 10, 20, 30, 40, 50... but max is 45
assertEquals(40, 42.coerceIn(0, 45, 10))
assertEquals(45, 48.coerceIn(0, 45, 10)) // Rounded to 50, coerced to 45
}
}