diff --git a/app/build.gradle b/app/build.gradle
index 8bcd1b4c56..16c45fc0a0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -117,6 +117,10 @@ android {
vectorDrawables.useSupportLibrary = true
buildConfigField "String", "GIT_HASH_SHORT", "\"${getGitHashShort()}\""
buildConfigField "boolean", "INTERNET_ACCESS", "false"
+
+ manifestPlaceholders = [
+ appAuthRedirectScheme: "${applicationId}"
+ ]
}
signingConfigs {
@@ -286,6 +290,9 @@ dependencies {
implementation libs.androidsvg
implementation libs.jsoup
+ // AppAuth is used for OAuth/PKCE/OIDC functionality, like required by the Endurain integration
+ implementation 'net.openid:appauth:0.11.1'
+
// Bouncy Castle is included directly in GB, to avoid pulling the entire dependency
// It's included in the org.bouncycastle.shaded package, to fix conflicts with roboelectric
//implementation 'org.bouncycastle:bcpkix-jdk18on:1.76'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4d9923078d..eece14c607 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -259,6 +259,10 @@
android:name=".activities.preferences.HealthConnectPreferencesActivity"
android:label="@string/healthconnect_settings"
android:parentActivityName=".activities.SettingsActivity" />
+
. */
+package nodomain.freeyourgadget.gadgetbridge.activities
+
+import android.os.Bundle
+import android.widget.LinearLayout
+import android.widget.Toast
+import androidx.appcompat.app.AlertDialog
+import androidx.core.content.edit
+import androidx.core.net.toUri
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import androidx.preference.PreferenceFragmentCompat
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.google.android.material.textfield.TextInputEditText
+import com.google.android.material.textfield.TextInputLayout
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import nodomain.freeyourgadget.gadgetbridge.GBApplication
+import nodomain.freeyourgadget.gadgetbridge.R
+import nodomain.freeyourgadget.gadgetbridge.util.GB
+import nodomain.freeyourgadget.gadgetbridge.util.InternetUtils
+
+class EndurainPreferencesActivity : AbstractSettingsActivityV2() {
+ override fun newFragment(): PreferenceFragmentCompat {
+ return EndurainPreferencesFragment()
+ }
+
+ companion object {
+ class EndurainPreferencesFragment : AbstractPreferenceFragment() {
+ override fun onCreatePreferences(
+ savedInstanceState: Bundle?,
+ rootKey: String?
+ ) {
+ setPreferencesFromResource(R.xml.endurain_preferences, rootKey)
+ val prefs = GBApplication.getPrefs().preferences
+
+ // Hide network required warning when network is available
+ val networkWarning = findPreference("pref_key_network_required")
+ networkWarning?.isVisible = !GBApplication.hasInternetAccess()
+
+ // Wire up the login button
+ val loginPref = findPreference("pref_key_log_in")
+ loginPref?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
+ val currentServer = prefs.getString("endurain_server", "https://")
+ val input = TextInputEditText(requireContext()).apply {
+ setText(currentServer)
+ layoutParams = LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT
+ )
+ }
+
+ val layout = TextInputLayout(requireContext()).apply {
+ hint = "Example: https://endurain.example.com"
+ addView(input)
+ }
+
+ val dialog = MaterialAlertDialogBuilder(requireContext())
+ .setTitle("Endurain server")
+ .setView(layout)
+ .setNegativeButton(R.string.Cancel, null)
+ .setPositiveButton(R.string.ok, null) // ← no listener yet
+ .create()
+
+ dialog.setOnShowListener {
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
+ val newUrl = input.text.toString().toUri()
+ val newServer =
+ if (newUrl.scheme == null || newUrl.host == null) {
+ null
+ } else {
+ "${newUrl.scheme}://${newUrl.host}"
+ }
+
+ prefs.edit {
+ putString("endurain_server", newServer)
+ }
+
+ dialog.dismiss()
+ lifecycleScope.launch(Dispatchers.IO) {
+ doLogin()
+ }
+ }
+ }
+
+ dialog.show()
+
+ true
+ }
+
+ // TODO: Wire up the logoff button
+ val logoffPref = findPreference("pref_key_log_out")
+ logoffPref?.isVisible = false
+
+ // TODO: Fill the status preference text
+ val statusPref = findPreference("pref_key_status")
+ statusPref?.summary = "Not logged in, integration is disabled"
+ }
+
+ private fun doLogin() {
+ val prefs = GBApplication.getPrefs().preferences
+ val server = prefs.getString("endurain_server", "").orEmpty()
+ if (server.isBlank()) return
+
+ val settingsUri = "${server}/api/v1/public/server_settings".toUri()
+ val serverSettings = InternetUtils.doJsonRequest(settingsUri)
+
+ if (serverSettings == null) {
+ GB.toast("Endurain server is offline", Toast.LENGTH_LONG, GB.ERROR)
+ return
+ }
+
+ val localLogin = serverSettings.optBoolean("local_login_enabled")
+ val ssoLogin = serverSettings.optBoolean("sso_enabled")
+
+ GB.toast("Endurain local=$localLogin sso=$ssoLogin", Toast.LENGTH_LONG, GB.INFO)
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
index 0b1b54ac30..852230a75f 100644
--- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
+++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/activities/SettingsActivity.java
@@ -414,6 +414,15 @@ public class SettingsActivity extends AbstractSettingsActivityV2 {
});
}
+ pref = findPreference("pref_category_endurain");
+ if (pref != null) {
+ pref.setOnPreferenceClickListener(preference -> {
+ Intent enableIntent = new Intent(requireContext(), EndurainPreferencesActivity.class);
+ startActivity(enableIntent);
+ return true;
+ });
+ }
+
pref = findPreference("pref_category_notifications");
if (pref != null) {
pref.setOnPreferenceClickListener(preference -> {
diff --git a/app/src/main/res/xml/endurain_preferences.xml b/app/src/main/res/xml/endurain_preferences.xml
new file mode 100644
index 0000000000..fdea266e12
--- /dev/null
+++ b/app/src/main/res/xml/endurain_preferences.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e9e450f91a..ff1effc028 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -387,6 +387,13 @@
android:key="pref_category_internethelper"
android:title="@string/prefs_internet_helper_title"
android:summary="@string/prefs_internet_helper_summary" />
+
+
+