feat: Allow changing base url dynamically
This commit is contained in:
parent
87e9af0bb0
commit
0cf94a4736
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
9
app/src/main/res/drawable/ic_server.xml
Normal file
9
app/src/main/res/drawable/ic_server.xml
Normal 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>
|
|
@ -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" />
|
||||
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
33
app/src/main/res/layout/dialog_server_settings.xml
Normal file
33
app/src/main/res/layout/dialog_server_settings.xml
Normal 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>
|
40
app/src/main/res/layout/fragment_server_settings_bar.xml
Normal file
40
app/src/main/res/layout/fragment_server_settings_bar.xml
Normal 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>
|
|
@ -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>
|
Loading…
Reference in a new issue