Add configurable buttons, separate settings screens and backup activity

- Add ButtonConfigActivity for customizing main screen buttons with
  drag-and-drop reordering and individual size options (S/M/L)
- Move storage settings to separate StorageSettingsActivity
- Move signature setting to storage settings (relevant for WebDAV sync)
- Move data backup to separate BackupActivity with export/import
- Make "more" overflow button configurable in size
- Simplify SettingsActivity to 3 navigation buttons
- Add logbook rename/delete functionality
- Improve S/M/L button contrast with visible borders
This commit is contained in:
2026-01-11 21:31:49 +01:00
parent 6a995d6561
commit 3e8af97757
28 changed files with 1979 additions and 708 deletions

View File

@@ -0,0 +1,178 @@
package it.danieleverducci.lunatracker
import android.os.Bundle
import android.view.View
import android.widget.EditText
import android.widget.RadioButton
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.progressindicator.LinearProgressIndicator
import com.thegrizzlylabs.sardineandroid.impl.SardineException
import it.danieleverducci.lunatracker.repository.FileLogbookRepository
import it.danieleverducci.lunatracker.repository.LocalSettingsRepository
import it.danieleverducci.lunatracker.repository.LogbookListObtainedListener
import it.danieleverducci.lunatracker.repository.WebDAVLogbookRepository
import okio.IOException
/**
* Activity for configuring storage settings (Local/WebDAV).
*/
class StorageSettingsActivity : AppCompatActivity() {
private lateinit var settingsRepository: LocalSettingsRepository
private lateinit var radioDataLocal: RadioButton
private lateinit var radioDataWebDAV: RadioButton
private lateinit var textViewWebDAVUrl: EditText
private lateinit var textViewWebDAVUser: EditText
private lateinit var textViewWebDAVPass: EditText
private lateinit var textViewSignature: EditText
private lateinit var progressIndicator: LinearProgressIndicator
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_storage_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)
textViewSignature = findViewById(R.id.settings_signature)
progressIndicator = findViewById(R.id.progress_indicator)
findViewById<View>(R.id.settings_save).setOnClickListener {
validateAndSave()
}
findViewById<View>(R.id.settings_cancel).setOnClickListener {
finish()
}
settingsRepository = LocalSettingsRepository(this)
loadSettings()
}
private 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])
}
textViewSignature.setText(settingsRepository.loadSignature())
}
private fun validateAndSave() {
if (radioDataLocal.isChecked) {
saveSettings()
return
}
// Try to connect to WebDAV and check if the save file already exists
val webDAVLogbookRepo = WebDAVLogbookRepository(
textViewWebDAVUrl.text.toString(),
textViewWebDAVUser.text.toString(),
textViewWebDAVPass.text.toString()
)
progressIndicator.visibility = View.VISIBLE
webDAVLogbookRepo.listLogbooks(this, object : LogbookListObtainedListener {
override fun onLogbookListObtained(logbooksNames: ArrayList<String>) {
if (logbooksNames.isEmpty()) {
copyLocalLogbooksToWebdav(webDAVLogbookRepo, object : OnCopyLocalLogbooksToWebdavFinishedListener {
override fun onCopyLocalLogbooksToWebdavFinished(errors: String?) {
runOnUiThread {
progressIndicator.visibility = View.INVISIBLE
if (errors == null) {
saveSettings()
Toast.makeText(this@StorageSettingsActivity, R.string.settings_webdav_creation_ok, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@StorageSettingsActivity, errors, Toast.LENGTH_SHORT).show()
}
}
}
})
} else {
runOnUiThread {
progressIndicator.visibility = View.INVISIBLE
saveSettings()
Toast.makeText(this@StorageSettingsActivity, R.string.settings_webdav_creation_ok, Toast.LENGTH_SHORT).show()
}
}
}
override fun onIOError(error: IOException) {
runOnUiThread {
progressIndicator.visibility = View.INVISIBLE
Toast.makeText(this@StorageSettingsActivity, getString(R.string.settings_network_error) + error.toString(), Toast.LENGTH_SHORT).show()
}
}
override fun onWebDAVError(error: SardineException) {
runOnUiThread {
progressIndicator.visibility = View.INVISIBLE
if (error.toString().contains("401")) {
Toast.makeText(this@StorageSettingsActivity, getString(R.string.settings_webdav_error_denied), Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this@StorageSettingsActivity, getString(R.string.settings_webdav_error_generic) + error.toString(), Toast.LENGTH_SHORT).show()
}
}
}
override fun onError(error: Exception) {
runOnUiThread {
progressIndicator.visibility = View.INVISIBLE
Toast.makeText(this@StorageSettingsActivity, getString(R.string.settings_generic_error) + error.toString(), Toast.LENGTH_SHORT).show()
}
}
})
}
private 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()
)
settingsRepository.saveSignature(textViewSignature.text.toString())
setResult(RESULT_OK)
finish()
}
private fun copyLocalLogbooksToWebdav(webDAVLogbookRepository: WebDAVLogbookRepository, listener: OnCopyLocalLogbooksToWebdavFinishedListener) {
Thread {
val errors = StringBuilder()
val fileLogbookRepo = FileLogbookRepository()
val logbooks = fileLogbookRepo.getAllLogbooks(this)
for (logbook in logbooks) {
val error = webDAVLogbookRepository.uploadLogbookIfNotExists(this, logbook.name)
if (error != null) {
if (errors.isNotEmpty())
errors.append("\n")
errors.append(String.format(getString(R.string.settings_webdav_upload_error), logbook.name, error))
}
}
listener.onCopyLocalLogbooksToWebdavFinished(
if (errors.isEmpty()) null else errors.toString()
)
}.start()
}
private interface OnCopyLocalLogbooksToWebdavFinishedListener {
fun onCopyLocalLogbooksToWebdavFinished(errors: String?)
}
}