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" /> + + +