Debug: Add devices page

This commit is contained in:
José Rebelo
2026-01-17 13:56:30 +00:00
parent 1ab92b49aa
commit d6ee8c1767
7 changed files with 206 additions and 0 deletions
@@ -7,6 +7,7 @@ import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceGroup
import androidx.preference.SwitchPreferenceCompat
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import nodomain.freeyourgadget.gadgetbridge.GBApplication
import nodomain.freeyourgadget.gadgetbridge.R
@@ -116,6 +117,32 @@ abstract class AbstractDebugFragment : AbstractPreferenceFragment() {
preferenceScreen?.addPreference(pref)
}
protected fun addDynamicCheckbox(
group: PreferenceGroup? = preferenceScreen,
title: String,
summary: String = "",
icon: Int = 0,
checked: Boolean,
): Preference {
val pref = SwitchPreferenceCompat(requireContext())
pref.setKey("${PREF_DYNAMIC_PREFIX}_${UUID.randomUUID()}")
pref.layoutResource = R.layout.preference_checkbox
pref.isPersistent = false
pref.isSelectable = false
pref.title = title
pref.summary = summary
if (icon != 0) {
pref.setIcon(icon)
} else if (!title.isEmpty()) {
pref.isIconSpaceReserved = false
}
pref.isChecked = checked
group?.addPreference(pref)
return pref
}
protected fun goTo(fragment: AbstractDebugFragment) {
requireActivity().supportFragmentManager.beginTransaction()
.replace(R.id.settings_container, fragment)
@@ -0,0 +1,72 @@
package nodomain.freeyourgadget.gadgetbridge.activities.debug
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import nodomain.freeyourgadget.gadgetbridge.R
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class DeviceDebugFragment : AbstractDebugFragment() {
private lateinit var gbDevice: GBDevice
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.debug_preferences_device, rootKey)
@Suppress("DEPRECATION")
gbDevice = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU) {
arguments?.getParcelable(GBDevice.EXTRA_DEVICE, GBDevice::class.java)!!
} else {
arguments?.getParcelable<GBDevice>(GBDevice.EXTRA_DEVICE)!!
}
preferenceScreen?.title = gbDevice.aliasOrName
findPreference<Preference>(PREF_DEBUG_DEVICE_NAME)?.summary = gbDevice.name ?: "<null>"
findPreference<Preference>(PREF_DEBUG_DEVICE_ALIAS)?.summary = gbDevice.alias ?: getString(R.string.not_set)
findPreference<Preference>(PREF_DEBUG_DEVICE_MAC_ADDRESS)?.summary = gbDevice.address
findPreference<Preference>(PREF_DEBUG_DEVICE_TYPE)?.summary = gbDevice.type.name
val headerDetails = findPreference<PreferenceCategory>(PREF_HEADER_DETAILS)
for (detail in gbDevice.deviceInfos) {
addDynamicPref(headerDetails, detail.name.replace(": *$".toRegex(), ""), detail.details)
}
val headerCoordinator = findPreference<PreferenceCategory>(PREF_HEADER_COORDINATOR)
val coordinator = gbDevice.deviceCoordinator
// Use reflection to find all supports* methods that return boolean
val coordinatorClass = coordinator.javaClass
val methods = coordinatorClass.methods
.filter { it.name.startsWith("supports") }
.filter { it.returnType == Boolean::class.javaPrimitiveType || it.returnType == Boolean::class.java }
.filter { it.parameterCount == 0 || (it.parameterCount == 1 && it.parameterTypes[0] == GBDevice::class.java) }
.sortedBy { it.name }
for (method in methods) {
try {
val result = if (method.parameterCount == 0) {
method.invoke(coordinator) as Boolean
} else {
method.invoke(coordinator, gbDevice) as Boolean
}
addDynamicCheckbox(headerCoordinator, method.name, checked = result)
} catch (e: Exception) {
LOG.error("Error invoking method ${method.name}", e)
addDynamicPref(headerCoordinator, method.name, "Error: ${e.message}")
}
}
}
companion object {
private val LOG: Logger = LoggerFactory.getLogger(DeviceDebugFragment::class.java)
private const val PREF_DEBUG_DEVICE_NAME = "pref_debug_device_name"
private const val PREF_DEBUG_DEVICE_ALIAS = "pref_debug_device_alias"
private const val PREF_DEBUG_DEVICE_MAC_ADDRESS = "pref_debug_device_mac_address"
private const val PREF_DEBUG_DEVICE_TYPE = "pref_debug_device_type"
private const val PREF_HEADER_DETAILS = "pref_header_details"
private const val PREF_HEADER_COORDINATOR = "pref_header_coordinator"
}
}
@@ -0,0 +1,41 @@
package nodomain.freeyourgadget.gadgetbridge.activities.debug
import android.os.Bundle
import nodomain.freeyourgadget.gadgetbridge.GBApplication
import nodomain.freeyourgadget.gadgetbridge.R
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice
class DeviceListDebugFragment : AbstractDebugFragment() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.debug_preferences_empty, rootKey)
preferenceScreen?.title = getString(R.string.bottom_nav_devices)
reloadDevices()
}
private fun reloadDevices() {
removeDynamicPrefs(preferenceScreen)
val devices = GBApplication.app().deviceManager.devices
if (!devices.isEmpty()) {
for (device in devices) {
addDynamicPref(
preferenceScreen,
device.aliasOrName,
device.address,
device.deviceCoordinator.defaultIconResource
) {
goTo(
DeviceDebugFragment().apply {
arguments = Bundle().apply {
putParcelable(GBDevice.EXTRA_DEVICE, device)
}
}
)
}
}
} else {
addDynamicPref(preferenceScreen, "", "No devices")
}
}
}
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M280,840v-720,720ZM480,240q17,0 28.5,-11.5T520,200q0,-17 -11.5,-28.5T480,160q-17,0 -28.5,11.5T440,200q0,17 11.5,28.5T480,240ZM538,920L280,920q-33,0 -56.5,-23.5T200,840v-720q0,-33 23.5,-56.5T280,40h400q33,0 56.5,23.5T760,120v240h-80v-240L280,120v720h215q7,9 15,17l16,16 12,47ZM620,920 L597,828q-36,-26 -56.5,-64.5T520,680q0,-45 20.5,-83.5T597,532l23,-92h160l23,92q36,26 56.5,64.5T880,680q0,45 -20.5,83.5T803,828l-23,92L620,920ZM700,780q42,0 71,-29t29,-71q0,-42 -29,-71t-71,-29q-42,0 -71,29t-29,71q0,42 29,71t71,29Z" />
</vector>
+1
View File
@@ -1200,6 +1200,7 @@
<string name="prefs_always_on_display_style">Style</string>
<string name="prefs_always_on_display_summary">Keep the band\'s display always on</string>
<string name="prefs_device_name">Device name</string>
<string name="prefs_device_alias">Device alias</string>
<string name="prefs_password">Password</string>
<string name="prefs_password_summary">Lock the band with a password when removed from the wrist</string>
<string name="prefs_password_enabled">Password Enabled</string>
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference
android:key="pref_debug_device_name"
android:persistent="false"
android:title="@string/prefs_device_name"
app:iconSpaceReserved="false" />
<Preference
android:key="pref_debug_device_alias"
android:persistent="false"
android:title="@string/prefs_device_alias"
app:iconSpaceReserved="false" />
<Preference
android:key="pref_debug_device_mac_address"
android:persistent="false"
android:title="@string/device_mac_address"
app:iconSpaceReserved="false" />
<Preference
android:key="pref_debug_device_type"
android:persistent="false"
android:title="@string/watchface_dialog_widget_type"
app:iconSpaceReserved="false" />
<PreferenceCategory
android:key="pref_header_details"
android:title="Details"
app:iconSpaceReserved="false">
<!-- to be filled up dynamically -->
</PreferenceCategory>
<PreferenceCategory
android:key="pref_header_coordinator"
android:title="Coordinator"
app:iconSpaceReserved="false">
<!-- to be filled up dynamically -->
</PreferenceCategory>
</androidx.preference.PreferenceScreen>
@@ -74,6 +74,13 @@
android:persistent="false"
android:title="@string/menuitem_settings" />
<Preference
android:fragment="nodomain.freeyourgadget.gadgetbridge.activities.debug.DeviceListDebugFragment"
android:icon="@drawable/ic_devices_wearables"
android:key="pref_debug_devices"
android:persistent="false"
android:title="@string/bottom_nav_devices" />
<Preference
android:fragment="nodomain.freeyourgadget.gadgetbridge.activities.debug.CompanionDebugFragment"
android:icon="@drawable/ic_devices_other"