diff --git a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt index 59962c9..4c03fe9 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/MainActivity.kt @@ -13,6 +13,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.progressindicator.LinearProgressIndicator +import com.thegrizzlylabs.sardineandroid.impl.SardineException import it.danieleverducci.lunatracker.adapters.LunaEventRecyclerAdapter import it.danieleverducci.lunatracker.entities.Logbook 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.WebDAVLogbookRepository import kotlinx.coroutines.Runnable +import okio.IOException +import org.json.JSONException class MainActivity : AppCompatActivity() { companion object { @@ -45,15 +48,6 @@ class MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) 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() if (webDavCredentials == null) { TODO("Not supported ATM (TODO: apply settings)") @@ -206,16 +200,28 @@ class MainActivity : AppCompatActivity() { }) } - override fun onError(error: String) { - runOnUiThread({ - progressIndicator.visibility = View.INVISIBLE - findViewById(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 onIOError(error: IOException) { + onRepoError(error) // TODO: Meaningful message } + + 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(R.id.no_connection_screen).visibility = View.VISIBLE + + Log.e(TAG, "Unable to load logbook: ${e.toString()} . Created a new one.") + logbook = Logbook() + showLogbook() }) } diff --git a/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt b/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt index a5ba166..5c8fb3e 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/SettingsActivity.kt @@ -2,23 +2,158 @@ package it.danieleverducci.lunatracker import android.os.Bundle import android.os.PersistableBundle +import android.util.Log import android.view.View +import android.widget.RadioButton +import android.widget.TextView 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.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() { + 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?) { super.onCreate(savedInstanceState) 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(R.id.settings_save).setOnClickListener({ - Toast.makeText(this, "TODO", Toast.LENGTH_SHORT).show() + validateAndSave() }) findViewById(R.id.settings_cancel).setOnClickListener({ 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() } } \ No newline at end of file diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt index f09e650..169fd53 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/FileLogbookRepository.kt @@ -27,7 +27,7 @@ class FileLogbookRepository: LogbookRepository { } } catch (e: FileNotFoundException) { Log.d(TAG, "No logbook file found") - listener.onError(e.toString()) + listener.onIOError(e) } listener.onLogbookLoaded(logbook) } diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt index 9b41de0..98125cf 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/LocalSettingsRepository.kt @@ -8,10 +8,12 @@ class LocalSettingsRepository(val context: Context) { companion object { val SHARED_PREFS_FILE_NAME = "lunasettings" val SHARED_PREFS_BB_CONTENT = "bbcontent" + val SHARED_PREFS_DATA_REPO = "webdav_url" val SHARED_PREFS_DAV_URL = "webdav_url" val SHARED_PREFS_DAV_USER = "webdav_user" val SHARED_PREFS_DAV_PASS = "webdav_password" } + enum class DATA_REPO {LOCAL_FILE, WEBDAV} val sharedPreferences: SharedPreferences init { @@ -26,6 +28,27 @@ class LocalSettingsRepository(val context: Context) { 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) { val spe = sharedPreferences.edit() spe.putString(SHARED_PREFS_DAV_URL, url) diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/LogbookRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/LogbookRepository.kt index f729903..b5a65bc 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/LogbookRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/LogbookRepository.kt @@ -1,7 +1,10 @@ package it.danieleverducci.lunatracker.repository import android.content.Context +import com.thegrizzlylabs.sardineandroid.impl.SardineException import it.danieleverducci.lunatracker.entities.Logbook +import okio.IOException +import org.json.JSONException interface LogbookRepository { fun loadLogbook(context: Context, listener: LogbookLoadedListener) @@ -10,7 +13,10 @@ interface LogbookRepository { interface LogbookLoadedListener { 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 { diff --git a/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt b/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt index e115cc3..d70be06 100644 --- a/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt +++ b/app/src/main/java/it/danieleverducci/lunatracker/repository/WebDAVLogbookRepository.kt @@ -1,15 +1,17 @@ package it.danieleverducci.lunatracker.repository import android.content.Context +import android.util.Log import com.thegrizzlylabs.sardineandroid.impl.OkHttpSardine import com.thegrizzlylabs.sardineandroid.impl.SardineException -import it.danieleverducci.lunatracker.TemporaryHardcodedCredentials import it.danieleverducci.lunatracker.entities.Logbook import it.danieleverducci.lunatracker.entities.LunaEvent import kotlinx.coroutines.Runnable import org.json.JSONArray +import org.json.JSONException import java.io.BufferedReader import java.io.IOException +import java.net.SocketTimeoutException import kotlin.io.bufferedReader 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) } listener.onLogbookLoaded(logbook) + } catch (e: SardineException) { + Log.e(TAG, e.toString()) + listener.onWebDAVError(e) } 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() } @@ -71,6 +85,6 @@ class WebDAVLogbookRepository(val webDavURL: String, val username: String, val p } private fun getUrl(): String { - return "${TemporaryHardcodedCredentials.URL}/$FILE_NAME" + return "$webDavURL/$FILE_NAME" } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 7984823..1da01c8 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -1,7 +1,8 @@ + android:layout_height="match_parent" + xmlns:app="http://schemas.android.com/apk/res-auto"> + android:hint="@string/settings_storage_dav_url_hint" + android:inputType="textUri"/> + android:layout_marginLeft="30dp" + android:inputType="textEmailAddress"/> + android:layout_marginLeft="30dp" + android:inputType="textPassword"/> + +