WIP Settings apply

This commit is contained in:
Daniele Verducci 2024-11-16 09:13:20 +01:00
parent 24ee0500b3
commit afabb0c711
8 changed files with 231 additions and 34 deletions

View File

@ -13,6 +13,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.progressindicator.LinearProgressIndicator
import com.thegrizzlylabs.sardineandroid.impl.SardineException
import it.danieleverducci.lunatracker.adapters.LunaEventRecyclerAdapter import it.danieleverducci.lunatracker.adapters.LunaEventRecyclerAdapter
import it.danieleverducci.lunatracker.entities.Logbook import it.danieleverducci.lunatracker.entities.Logbook
import it.danieleverducci.lunatracker.entities.LunaEvent import it.danieleverducci.lunatracker.entities.LunaEvent
@ -23,6 +24,8 @@ import it.danieleverducci.lunatracker.repository.LogbookRepository
import it.danieleverducci.lunatracker.repository.LogbookSavedListener import it.danieleverducci.lunatracker.repository.LogbookSavedListener
import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
import kotlinx.coroutines.Runnable import kotlinx.coroutines.Runnable
import okio.IOException
import org.json.JSONException
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
companion object { companion object {
@ -45,15 +48,6 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val localSettings = LocalSettingsRepository(this) val localSettings = LocalSettingsRepository(this)
// TEMPORARY: Save credentials (prepare for update with settings enabled)
localSettings.saveWebdavCredentials(
TemporaryHardcodedCredentials.URL,
TemporaryHardcodedCredentials.USERNAME,
TemporaryHardcodedCredentials.PASSWORD
)
// END TEMPORARY
val webDavCredentials = localSettings.loadWebdavCredentials() val webDavCredentials = localSettings.loadWebdavCredentials()
if (webDavCredentials == null) { if (webDavCredentials == null) {
TODO("Not supported ATM (TODO: apply settings)") TODO("Not supported ATM (TODO: apply settings)")
@ -206,16 +200,28 @@ class MainActivity : AppCompatActivity() {
}) })
} }
override fun onError(error: String) { override fun onIOError(error: IOException) {
runOnUiThread({ onRepoError(error) // TODO: Meaningful message
progressIndicator.visibility = View.INVISIBLE
findViewById<View>(R.id.no_connection_screen).visibility = View.VISIBLE
Log.e(TAG, "Unable to load logbook: $error . Created a new one.")
logbook = Logbook()
showLogbook()
})
} }
override fun onWebDAVError(error: SardineException) {
onRepoError(error) // TODO: Meaningful message
}
override fun onJSONError(error: JSONException) {
onRepoError(error) // TODO: Meaningful message
}
})
}
fun onRepoError(e: Exception){
runOnUiThread({
progressIndicator.visibility = View.INVISIBLE
findViewById<View>(R.id.no_connection_screen).visibility = View.VISIBLE
Log.e(TAG, "Unable to load logbook: ${e.toString()} . Created a new one.")
logbook = Logbook()
showLogbook()
}) })
} }

View File

@ -2,23 +2,158 @@ package it.danieleverducci.lunatracker
import android.os.Bundle import android.os.Bundle
import android.os.PersistableBundle import android.os.PersistableBundle
import android.util.Log
import android.view.View import android.view.View
import android.widget.RadioButton
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.thegrizzlylabs.sardineandroid.impl.SardineException
import it.danieleverducci.lunatracker.entities.Logbook
import it.danieleverducci.lunatracker.repository.FileLogbookRepository
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
import it.danieleverducci.lunatracker.repository.LogbookLoadedListener
import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
import okio.IOException
import org.json.JSONException
class SettingsActivity : AppCompatActivity() { class SettingsActivity : AppCompatActivity() {
protected lateinit var settingsRepository: LocalSettingsRepository
protected lateinit var radioDataLocal: RadioButton
protected lateinit var radioDataWebDAV: RadioButton
protected lateinit var textViewWebDAVUrl: TextView
protected lateinit var textViewWebDAVUser: TextView
protected lateinit var textViewWebDAVPass: TextView
protected lateinit var progressIndicator: LinearProgressIndicator
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_settings) setContentView(R.layout.activity_settings)
radioDataLocal = findViewById(R.id.settings_data_local)
radioDataWebDAV = findViewById(R.id.settings_data_webdav)
textViewWebDAVUrl = findViewById(R.id.settings_data_webdav_url)
textViewWebDAVUser = findViewById(R.id.settings_data_webdav_user)
textViewWebDAVPass = findViewById(R.id.settings_data_webdav_pass)
progressIndicator = findViewById(R.id.progress_indicator)
findViewById<View>(R.id.settings_save).setOnClickListener({ findViewById<View>(R.id.settings_save).setOnClickListener({
Toast.makeText(this, "TODO", Toast.LENGTH_SHORT).show() validateAndSave()
}) })
findViewById<View>(R.id.settings_cancel).setOnClickListener({ findViewById<View>(R.id.settings_cancel).setOnClickListener({
finish() finish()
}) })
settingsRepository = LocalSettingsRepository(this)
loadSettings()
}
fun loadSettings() {
val dataRepo = settingsRepository.loadDataRepository()
val webDavCredentials = settingsRepository.loadWebdavCredentials()
when (dataRepo) {
LocalSettingsRepository.DATA_REPO.LOCAL_FILE -> radioDataLocal.isChecked = true
LocalSettingsRepository.DATA_REPO.WEBDAV -> radioDataWebDAV.isChecked = true
}
if (webDavCredentials != null) {
textViewWebDAVUrl.setText(webDavCredentials[0])
textViewWebDAVUser.setText(webDavCredentials[1])
textViewWebDAVPass.setText(webDavCredentials[2])
}
}
fun validateAndSave() {
if (radioDataLocal.isChecked) {
// No validation required, just save
saveSettings()
return
}
// Try to connect to WebDAV and check if the save file already exists
val logbookRepo = WebDAVLogbookRepository(
textViewWebDAVUrl.text.toString(),
textViewWebDAVUser.text.toString(),
textViewWebDAVPass.text.toString()
)
progressIndicator.visibility = View.VISIBLE
logbookRepo.loadLogbook(this, object: LogbookLoadedListener {
override fun onLogbookLoaded(logbook: Logbook) {
progressIndicator.visibility = View.INVISIBLE
// Save file does exist. Settings valid. Save.
saveSettings()
}
override fun onIOError(error: IOException) {
// Unable to reach network
runOnUiThread({
Toast.makeText(this@SettingsActivity, getString(R.string.settings_network_error) + error.toString(), Toast.LENGTH_SHORT).show()
})
}
override fun onWebDAVError(error: SardineException) {
// Save file does not exist, upload the local one
runOnUiThread({
progressIndicator.visibility = View.INVISIBLE
if (error.toString().contains("401")) {
Toast.makeText(this@SettingsActivity, getString(R.string.settings_webdav_error_denied), Toast.LENGTH_SHORT).show()
} else if (error.toString().contains("404")) {
// Connection successful, but no existing save. Upload the local one.
val fileLogbookRepo = FileLogbookRepository()
fileLogbookRepo.loadLogbook(this@SettingsActivity, object: LogbookLoadedListener {
override fun onLogbookLoaded(logbook: Logbook) {
TODO("Not yet implemented")
}
override fun onIOError(error: IOException) {
TODO("Not yet implemented")
}
override fun onWebDAVError(error: SardineException) {
TODO("Not yet implemented")
}
override fun onJSONError(error: JSONException) {
TODO("Not yet implemented")
}
override fun onError(error: Exception) {
TODO("Not yet implemented")
}
})
} else {
Toast.makeText(this@SettingsActivity, getString(R.string.settings_webdav_error_generic) + error.toString(), Toast.LENGTH_SHORT).show()
}
})
}
override fun onJSONError(error: JSONException) {
progressIndicator.visibility = View.INVISIBLE
// Save file exists, but is corrupted
Toast.makeText(this@SettingsActivity, getString(R.string.settings_json_error) + error.toString(), Toast.LENGTH_SHORT).show()
}
override fun onError(error: Exception) {
progressIndicator.visibility = View.INVISIBLE
Toast.makeText(this@SettingsActivity, getString(R.string.settings_generic_error) + error.toString(), Toast.LENGTH_SHORT).show()
}
})
}
fun saveSettings() {
settingsRepository.saveDataRepository(
if (radioDataWebDAV.isChecked) LocalSettingsRepository.DATA_REPO.WEBDAV
else LocalSettingsRepository.DATA_REPO.LOCAL_FILE
)
settingsRepository.saveWebdavCredentials(
textViewWebDAVUrl.text.toString(),
textViewWebDAVUser.text.toString(),
textViewWebDAVPass.text.toString()
)
finish()
} }
} }

View File

@ -27,7 +27,7 @@ class FileLogbookRepository: LogbookRepository {
} }
} catch (e: FileNotFoundException) { } catch (e: FileNotFoundException) {
Log.d(TAG, "No logbook file found") Log.d(TAG, "No logbook file found")
listener.onError(e.toString()) listener.onIOError(e)
} }
listener.onLogbookLoaded(logbook) listener.onLogbookLoaded(logbook)
} }

View File

@ -8,10 +8,12 @@ class LocalSettingsRepository(val context: Context) {
companion object { companion object {
val SHARED_PREFS_FILE_NAME = "lunasettings" val SHARED_PREFS_FILE_NAME = "lunasettings"
val SHARED_PREFS_BB_CONTENT = "bbcontent" val SHARED_PREFS_BB_CONTENT = "bbcontent"
val SHARED_PREFS_DATA_REPO = "webdav_url"
val SHARED_PREFS_DAV_URL = "webdav_url" val SHARED_PREFS_DAV_URL = "webdav_url"
val SHARED_PREFS_DAV_USER = "webdav_user" val SHARED_PREFS_DAV_USER = "webdav_user"
val SHARED_PREFS_DAV_PASS = "webdav_password" val SHARED_PREFS_DAV_PASS = "webdav_password"
} }
enum class DATA_REPO {LOCAL_FILE, WEBDAV}
val sharedPreferences: SharedPreferences val sharedPreferences: SharedPreferences
init { init {
@ -26,6 +28,27 @@ class LocalSettingsRepository(val context: Context) {
return sharedPreferences.getInt(SHARED_PREFS_BB_CONTENT, 1) return sharedPreferences.getInt(SHARED_PREFS_BB_CONTENT, 1)
} }
fun saveDataRepository(repo: DATA_REPO) {
val spe = sharedPreferences.edit()
spe.putString(
SHARED_PREFS_DATA_REPO,
when (repo) {
DATA_REPO.WEBDAV -> "webdav"
DATA_REPO.LOCAL_FILE -> "localfile"
}
)
spe.commit()
}
fun loadDataRepository(): DATA_REPO {
val repo = sharedPreferences.getString(SHARED_PREFS_DATA_REPO, null)
return when (repo) {
"webdav" -> DATA_REPO.WEBDAV
"localfile" -> DATA_REPO.LOCAL_FILE
else -> DATA_REPO.LOCAL_FILE
}
}
fun saveWebdavCredentials(url: String, username: String, password: String) { fun saveWebdavCredentials(url: String, username: String, password: String) {
val spe = sharedPreferences.edit() val spe = sharedPreferences.edit()
spe.putString(SHARED_PREFS_DAV_URL, url) spe.putString(SHARED_PREFS_DAV_URL, url)

View File

@ -1,7 +1,10 @@
package it.danieleverducci.lunatracker.repository package it.danieleverducci.lunatracker.repository
import android.content.Context import android.content.Context
import com.thegrizzlylabs.sardineandroid.impl.SardineException
import it.danieleverducci.lunatracker.entities.Logbook import it.danieleverducci.lunatracker.entities.Logbook
import okio.IOException
import org.json.JSONException
interface LogbookRepository { interface LogbookRepository {
fun loadLogbook(context: Context, listener: LogbookLoadedListener) fun loadLogbook(context: Context, listener: LogbookLoadedListener)
@ -10,7 +13,10 @@ interface LogbookRepository {
interface LogbookLoadedListener { interface LogbookLoadedListener {
fun onLogbookLoaded(logbook: Logbook) fun onLogbookLoaded(logbook: Logbook)
fun onError(error: String) fun onIOError(error: IOException)
fun onWebDAVError(error: SardineException)
fun onJSONError(error: JSONException)
fun onError(error: Exception)
} }
interface LogbookSavedListener { interface LogbookSavedListener {

View File

@ -1,15 +1,17 @@
package it.danieleverducci.lunatracker.repository package it.danieleverducci.lunatracker.repository
import android.content.Context import android.content.Context
import android.util.Log
import com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine import com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine
import com.thegrizzlylabs.sardineandroid.impl.SardineException import com.thegrizzlylabs.sardineandroid.impl.SardineException
import it.danieleverducci.lunatracker.TemporaryHardcodedCredentials
import it.danieleverducci.lunatracker.entities.Logbook import it.danieleverducci.lunatracker.entities.Logbook
import it.danieleverducci.lunatracker.entities.LunaEvent import it.danieleverducci.lunatracker.entities.LunaEvent
import kotlinx.coroutines.Runnable import kotlinx.coroutines.Runnable
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONException
import java.io.BufferedReader import java.io.BufferedReader
import java.io.IOException import java.io.IOException
import java.net.SocketTimeoutException
import kotlin.io.bufferedReader import kotlin.io.bufferedReader
class WebDAVLogbookRepository(val webDavURL: String, val username: String, val password: String): LogbookRepository { class WebDAVLogbookRepository(val webDavURL: String, val username: String, val password: String): LogbookRepository {
@ -40,8 +42,20 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
logbook.logs.add(evt) logbook.logs.add(evt)
} }
listener.onLogbookLoaded(logbook) listener.onLogbookLoaded(logbook)
} catch (e: SardineException) {
Log.e(TAG, e.toString())
listener.onWebDAVError(e)
} catch (e: IOException) { } catch (e: IOException) {
listener.onError(e.toString()) Log.e(TAG, e.toString())
listener.onIOError(e)
} catch (e: SocketTimeoutException) {
Log.e(TAG, e.toString())
listener.onIOError(e)
} catch (e: JSONException) {
Log.e(TAG, e.toString())
listener.onJSONError(e)
} catch (e: Exception) {
listener.onError(e)
} }
}).start() }).start()
} }
@ -71,6 +85,6 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p
} }
private fun getUrl(): String { private fun getUrl(): String {
return "${TemporaryHardcodedCredentials.URL}/$FILE_NAME" return "$webDavURL/$FILE_NAME"
} }
} }

View File

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -66,7 +67,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="30dp" android:layout_marginLeft="30dp"
android:hint="@string/settings_storage_dav_url_hint"/> android:hint="@string/settings_storage_dav_url_hint"
android:inputType="textUri"/>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -80,7 +82,8 @@
android:id="@+id/settings_data_webdav_user" android:id="@+id/settings_data_webdav_user"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="30dp"/> android:layout_marginLeft="30dp"
android:inputType="textEmailAddress"/>
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@ -94,12 +97,21 @@
android:id="@+id/settings_data_webdav_pass" android:id="@+id/settings_data_webdav_pass"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="30dp"/> android:layout_marginLeft="30dp"
android:inputType="textPassword"/>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
app:indicatorColor="@color/accent"
android:layout_margin="20dp"
android:visibility="invisible"/>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="horizontal"> android:orientation="horizontal">
<Button <Button

View File

@ -53,8 +53,9 @@
<string name="settings_storage_dav_url_hint">https://</string> <string name="settings_storage_dav_url_hint">https://</string>
<string name="settings_storage_dav_user">Username</string> <string name="settings_storage_dav_user">Username</string>
<string name="settings_storage_dav_pass">Password</string> <string name="settings_storage_dav_pass">Password</string>
<string name="settings_sync_all">Esiste già un salvataggio sul server. Vuoi sovrascriverlo?</string> <string name="settings_network_error">Impossibile raggiungere il server: </string>
<string name="settings_sync_all_yes">Carica i dati del dispositivo sul server</string> <string name="settings_webdav_error_denied">Nome utente o password sbagliati</string>
<string name="settings_sync_all_no">Sarica i dati dal server sul dispositivo</string> <string name="settings_webdav_error_generic">Si è verificato un errore tentando di accedere al server WebDAV:</string>
<string name="settings_sync_all_cancel">Annulla</string> <string name="settings_json_error">Sul server esiste un salvataggio, ma è corrotto o illeggibile. Cancellare il file </string>
<string name="settings_generic_error">Si è verificato un errore: </string>
</resources> </resources>