From d6ee8c176782c50bd94b9106a31dbc2ca4cb7216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Rebelo?= Date: Sat, 17 Jan 2026 13:56:30 +0000 Subject: [PATCH] Debug: Add devices page --- .../activities/debug/AbstractDebugFragment.kt | 27 +++++++ .../activities/debug/DeviceDebugFragment.kt | 72 +++++++++++++++++++ .../debug/DeviceListDebugFragment.kt | 41 +++++++++++ .../res/drawable/ic_devices_wearables.xml | 11 +++ app/src/main/res/values/strings.xml | 1 + .../main/res/xml/debug_preferences_device.xml | 47 ++++++++++++ .../main/res/xml/debug_preferences_main.xml | 7 ++ 7 files changed, 206 insertions(+) create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/DeviceDebugFragment.kt create mode 100644 app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/DeviceListDebugFragment.kt create mode 100644 app/src/main/res/drawable/ic_devices_wearables.xml create mode 100644 app/src/main/res/xml/debug_preferences_device.xml diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/AbstractDebugFragment.kt b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/AbstractDebugFragment.kt index ff3713a995..dbd53fb4af 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/AbstractDebugFragment.kt +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/AbstractDebugFragment.kt @@ -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) diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/DeviceDebugFragment.kt b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/DeviceDebugFragment.kt new file mode 100644 index 0000000000..a5188e4a41 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/DeviceDebugFragment.kt @@ -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.EXTRA_DEVICE)!! + } + + preferenceScreen?.title = gbDevice.aliasOrName + + findPreference(PREF_DEBUG_DEVICE_NAME)?.summary = gbDevice.name ?: "" + findPreference(PREF_DEBUG_DEVICE_ALIAS)?.summary = gbDevice.alias ?: getString(R.string.not_set) + findPreference(PREF_DEBUG_DEVICE_MAC_ADDRESS)?.summary = gbDevice.address + findPreference(PREF_DEBUG_DEVICE_TYPE)?.summary = gbDevice.type.name + + val headerDetails = findPreference(PREF_HEADER_DETAILS) + for (detail in gbDevice.deviceInfos) { + addDynamicPref(headerDetails, detail.name.replace(": *$".toRegex(), ""), detail.details) + } + + val headerCoordinator = findPreference(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" + } +} diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/DeviceListDebugFragment.kt b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/DeviceListDebugFragment.kt new file mode 100644 index 0000000000..d6210f0314 --- /dev/null +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/debug/DeviceListDebugFragment.kt @@ -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") + } + } +} diff --git a/app/src/main/res/drawable/ic_devices_wearables.xml b/app/src/main/res/drawable/ic_devices_wearables.xml new file mode 100644 index 0000000000..921237bea2 --- /dev/null +++ b/app/src/main/res/drawable/ic_devices_wearables.xml @@ -0,0 +1,11 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 59e3e26fef..003a94e221 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1200,6 +1200,7 @@ Style Keep the band\'s display always on Device name + Device alias Password Lock the band with a password when removed from the wrist Password Enabled diff --git a/app/src/main/res/xml/debug_preferences_device.xml b/app/src/main/res/xml/debug_preferences_device.xml new file mode 100644 index 0000000000..478d0ee30e --- /dev/null +++ b/app/src/main/res/xml/debug_preferences_device.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/xml/debug_preferences_main.xml b/app/src/main/res/xml/debug_preferences_main.xml index 746419c5b8..47cb6ea700 100644 --- a/app/src/main/res/xml/debug_preferences_main.xml +++ b/app/src/main/res/xml/debug_preferences_main.xml @@ -74,6 +74,13 @@ android:persistent="false" android:title="@string/menuitem_settings" /> + +