feat: Allow changing base url dynamically

This commit is contained in:
Peter Vacho 2024-12-23 23:45:36 +01:00
parent 87e9af0bb0
commit 0cf94a4736
Signed by: school
GPG key ID: 8CFC3837052871B4
9 changed files with 196 additions and 9 deletions

View file

@ -0,0 +1,49 @@
package com.p_vacho.neat_calendar
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageButton
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import com.p_vacho.neat_calendar.api.RetrofitClient
class ServerSettingsBarFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for the app bar
return inflater.inflate(R.layout.fragment_server_settings_bar, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val settingsIcon = view.findViewById<ImageButton>(R.id.settingsIcon)
settingsIcon?.setOnClickListener {
showSettingDialog()
}
}
private fun showSettingDialog() {
val dialogView = layoutInflater.inflate(R.layout.dialog_server_settings, null)
val baseUrlInput = dialogView.findViewById<EditText>(R.id.baseUrlInput)
// Pre-fill current base URL
baseUrlInput.setText(RetrofitClient.getBaseUrl())
AlertDialog.Builder(requireContext())
.setTitle(R.string.server_settings)
.setView(dialogView)
.setPositiveButton(R.string.save) { _, _ ->
val newBaseUrl = baseUrlInput.text.toString().trim()
RetrofitClient.updateBaseUrl(newBaseUrl)
}
.setNegativeButton(R.string.cancel, null)
.show()
}
}

View file

@ -13,11 +13,11 @@ import retrofit2.HttpException
import java.io.IOException
object RetrofitClient {
private const val BASE_URL = "http://10.0.2.2:8000"
private const val DEFAULT_BASE_URL = "http://10.0.2.2:8000"
private lateinit var retrofitWithReachability: Retrofit
private lateinit var retrofitWithoutReachability: Retrofit
private lateinit var appContext: Context
val authService: AuthService by lazy {
retrofitWithReachability.create(AuthService::class.java)
@ -31,6 +31,9 @@ object RetrofitClient {
* Initializes the RetrofitClient with context and token manager
*/
fun initialize(context: Context) {
appContext = context
val baseUrl = getBaseUrl()
val apiReachabilityInterceptor = ApiReachabilityInterceptor(context)
val authInterceptor = AuthInterceptor(context)
@ -46,18 +49,36 @@ object RetrofitClient {
.build()
retrofitWithReachability = Retrofit.Builder()
.baseUrl(BASE_URL)
.baseUrl(baseUrl)
.client(okHttpClientWithReachability)
.addConverterFactory(GsonConverterFactory.create())
.build()
retrofitWithoutReachability = Retrofit.Builder()
.baseUrl(BASE_URL)
.baseUrl(baseUrl)
.client(okHttpClientWithoutReachability)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
/**
* Get the base api url that's currently in-use.
*/
fun getBaseUrl(): String {
val sharedPreferences = appContext.getSharedPreferences("app_settings", Context.MODE_PRIVATE)
return sharedPreferences.getString("base_url", DEFAULT_BASE_URL) ?: DEFAULT_BASE_URL
}
/**
* Update the base api url that the client should use.
*
* Note: This change will persist throughout restarts.
*/
fun updateBaseUrl(newBaseUrl: String) {
val sharedPreferences = appContext.getSharedPreferences("app_settings", Context.MODE_PRIVATE)
sharedPreferences.edit().putString("base_url", newBaseUrl).apply()
initialize(appContext) // Reinitialize with updated URL
}
/**
* Checks whether the API is reachable by making a ping request using the client

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M300,240q-25,0 -42.5,17.5T240,300q0,25 17.5,42.5T300,360q25,0 42.5,-17.5T360,300q0,-25 -17.5,-42.5T300,240ZM300,640q-25,0 -42.5,17.5T240,700q0,25 17.5,42.5T300,760q25,0 42.5,-17.5T360,700q0,-25 -17.5,-42.5T300,640ZM160,120h640q17,0 28.5,11.5T840,160v280q0,17 -11.5,28.5T800,480L160,480q-17,0 -28.5,-11.5T120,440v-280q0,-17 11.5,-28.5T160,120ZM200,200v200h560v-200L200,200ZM160,520h640q17,0 28.5,11.5T840,560v280q0,17 -11.5,28.5T800,880L160,880q-17,0 -28.5,-11.5T120,840v-280q0,-17 11.5,-28.5T160,520ZM200,600v200h560v-200L200,600ZM200,200v200,-200ZM200,600v200,-200Z"
android:fillColor="#e8eaed"/>
</vector>

View file

@ -8,6 +8,16 @@
android:layout_height="match_parent"
tools:context=".ApiUnreachableActivity">
<!-- Include Server Settings Bar -->
<fragment
android:id="@+id/serverSettingsBarFragment"
android:name="com.p_vacho.neat_calendar.ServerSettingsBarFragment"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/messageView"
android:layout_width="wrap_content"
@ -17,7 +27,7 @@
android:textAppearance="?android:attr/textAppearanceLarge"
android:gravity="center"
android:padding="16dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintTop_toBottomOf="@id/serverSettingsBarFragment"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />

View file

@ -6,15 +6,25 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="48dp"
tools:context=".LoginActivity">
<!-- Include Server Settings Bar -->
<fragment
android:id="@+id/serverSettingsBarFragment"
android:name="com.p_vacho.neat_calendar.ServerSettingsBarFragment"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- Username Field -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/usernameInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="@+id/serverSettingsBarFragment"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:hint="@string/username">

View file

@ -5,15 +5,25 @@
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="48dp"
tools:context=".RegisterActivity">
<!-- Include Server Settings Bar -->
<fragment
android:id="@+id/serverSettingsBarFragment"
android:name="com.p_vacho.neat_calendar.ServerSettingsBarFragment"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<!-- Username Field -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/usernameInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="24dp"
app:layout_constraintTop_toBottomOf="@+id/serverSettingsBarFragment"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:hint="@string/username">

View file

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/baseUrlLabel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/base_url"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/baseUrlLabel"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/baseUrlInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/base_url_hint"
android:inputType="textUri" />
</com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:padding="8dp"
tools:context=".ServerSettingsBarFragment">
<!-- Title Text -->
<com.google.android.material.textview.MaterialTextView
android:id="@+id/appBarTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/app_name"
android:textColor="@android:color/white"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/settingsIcon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="center_vertical" />
<!-- Server Settings Icon -->
<ImageButton
android:id="@+id/settingsIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/server_settings"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_server"
app:tint="@android:color/white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:padding="8dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -11,4 +11,9 @@
<string name="retry_attempts_message">Retry failed. Attempts: %1$d</string>
<string name="error_type">Type: %1$s</string>
<string name="error_message">Message: %1$s</string>
<string name="server_settings">Server Settings</string>
<string name="base_url">API Base URL</string>
<string name="base_url_hint">https://myapiurl.com</string>
<string name="save">Save</string>
<string name="cancel">Cancel</string>
</resources>