Feat: Make delete password optional for web app integration and translate messages.
This commit is contained in:
@@ -438,38 +438,53 @@ class ShoppingListsViewModel(private val noteshopRepository: NoteshopRepository,
|
||||
viewModelScope.launch {
|
||||
val webAppPrefs = getApplication<Application>().getSharedPreferences("webapp_prefs", Context.MODE_PRIVATE)
|
||||
val url = webAppPrefs.getString("webapp_url", null)
|
||||
val keyPass = webAppPrefs.getString("key_pass", null)
|
||||
|
||||
if (url.isNullOrBlank() || keyPass.isNullOrBlank()) {
|
||||
Toast.makeText(getApplication(), "Web App credentials not set up", Toast.LENGTH_SHORT).show()
|
||||
if (url.isNullOrBlank()) {
|
||||
Toast.makeText(getApplication(), de.lxtools.noteshop.R.string.webapp_url_not_set_up, Toast.LENGTH_SHORT).show()
|
||||
return@launch
|
||||
}
|
||||
|
||||
val fileEncryptor = FileEncryptor()
|
||||
val keyManager = KeyManager(getApplication(), false)
|
||||
val secretKey = keyManager.derivePbeKey(keyPass.toCharArray())
|
||||
val keyPass = webAppPrefs.getString("key_pass", null)
|
||||
val username: String?
|
||||
val password: String?
|
||||
|
||||
try {
|
||||
val usernameEncrypted = webAppPrefs.getString("username_encrypted", null)
|
||||
val passwordEncrypted = webAppPrefs.getString("password_encrypted", null)
|
||||
if (keyPass.isNullOrBlank()) {
|
||||
// Load unencrypted credentials
|
||||
username = webAppPrefs.getString("username", null)
|
||||
password = webAppPrefs.getString("password", null)
|
||||
} else {
|
||||
// Load encrypted credentials
|
||||
val fileEncryptor = FileEncryptor()
|
||||
val keyManager = KeyManager(getApplication(), false)
|
||||
val secretKey = keyManager.derivePbeKey(keyPass.toCharArray())
|
||||
|
||||
if (usernameEncrypted == null || passwordEncrypted == null) {
|
||||
Toast.makeText(getApplication(), "Web App credentials corrupted", Toast.LENGTH_SHORT).show()
|
||||
try {
|
||||
val usernameEncrypted = webAppPrefs.getString("username_encrypted", null)
|
||||
val passwordEncrypted = webAppPrefs.getString("password_encrypted", null)
|
||||
|
||||
if (usernameEncrypted == null || passwordEncrypted == null) {
|
||||
Toast.makeText(getApplication(), de.lxtools.noteshop.R.string.webapp_credentials_corrupted, Toast.LENGTH_SHORT).show()
|
||||
return@launch
|
||||
}
|
||||
|
||||
val decryptedUsernameBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(usernameEncrypted), secretKey)
|
||||
username = String(decryptedUsernameBytes, Charsets.UTF_8)
|
||||
|
||||
val decryptedPasswordBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(passwordEncrypted), secretKey)
|
||||
password = String(decryptedPasswordBytes, Charsets.UTF_8)
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(getApplication(), getApplication<Application>().getString(de.lxtools.noteshop.R.string.failed_to_decrypt_credentials_generic, e.message), Toast.LENGTH_LONG).show()
|
||||
return@launch
|
||||
}
|
||||
|
||||
val decryptedUsernameBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(usernameEncrypted), secretKey)
|
||||
val username = String(decryptedUsernameBytes, Charsets.UTF_8)
|
||||
|
||||
val decryptedPasswordBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(passwordEncrypted), secretKey)
|
||||
val password = String(decryptedPasswordBytes, Charsets.UTF_8)
|
||||
|
||||
noteshopRepository.importItemsFromWebApp(listId, url, username, password)
|
||||
Toast.makeText(getApplication(), de.lxtools.noteshop.R.string.import_successful, Toast.LENGTH_SHORT).show()
|
||||
|
||||
} catch (e: Exception) {
|
||||
Toast.makeText(getApplication(), "Failed to import items: ${e.message}", Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
if (username.isNullOrBlank() || password.isNullOrBlank()) {
|
||||
Toast.makeText(getApplication(), de.lxtools.noteshop.R.string.webapp_credentials_not_set_up, Toast.LENGTH_SHORT).show()
|
||||
return@launch
|
||||
}
|
||||
|
||||
noteshopRepository.importItemsFromWebApp(listId, url, username, password)
|
||||
Toast.makeText(getApplication(), de.lxtools.noteshop.R.string.import_successful, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -477,35 +492,49 @@ class ShoppingListsViewModel(private val noteshopRepository: NoteshopRepository,
|
||||
viewModelScope.launch {
|
||||
val webAppPrefs = getApplication<Application>().getSharedPreferences("webapp_prefs", Context.MODE_PRIVATE)
|
||||
val url = webAppPrefs.getString("webapp_url", null)
|
||||
val keyPass = webAppPrefs.getString("key_pass", null)
|
||||
|
||||
if (url.isNullOrBlank() || keyPass.isNullOrBlank()) {
|
||||
if (url.isNullOrBlank()) {
|
||||
return@launch // Silently fail if not configured
|
||||
}
|
||||
|
||||
val fileEncryptor = FileEncryptor()
|
||||
val keyManager = KeyManager(getApplication(), false)
|
||||
val secretKey = keyManager.derivePbeKey(keyPass.toCharArray())
|
||||
val keyPass = webAppPrefs.getString("key_pass", null)
|
||||
val username: String?
|
||||
val password: String?
|
||||
|
||||
try {
|
||||
val usernameEncrypted = webAppPrefs.getString("username_encrypted", null)
|
||||
val passwordEncrypted = webAppPrefs.getString("password_encrypted", null)
|
||||
if (keyPass.isNullOrBlank()) {
|
||||
// Load unencrypted credentials
|
||||
username = webAppPrefs.getString("username", null)
|
||||
password = webAppPrefs.getString("password", null)
|
||||
} else {
|
||||
// Load encrypted credentials
|
||||
val fileEncryptor = FileEncryptor()
|
||||
val keyManager = KeyManager(getApplication(), false)
|
||||
val secretKey = keyManager.derivePbeKey(keyPass.toCharArray())
|
||||
|
||||
if (usernameEncrypted == null || passwordEncrypted == null) {
|
||||
return@launch // Silently fail
|
||||
try {
|
||||
val usernameEncrypted = webAppPrefs.getString("username_encrypted", null)
|
||||
val passwordEncrypted = webAppPrefs.getString("password_encrypted", null)
|
||||
|
||||
if (usernameEncrypted == null || passwordEncrypted == null) {
|
||||
return@launch // Silently fail
|
||||
}
|
||||
|
||||
val decryptedUsernameBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(usernameEncrypted), secretKey)
|
||||
username = String(decryptedUsernameBytes, Charsets.UTF_8)
|
||||
|
||||
val decryptedPasswordBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(passwordEncrypted), secretKey)
|
||||
password = String(decryptedPasswordBytes, Charsets.UTF_8)
|
||||
} catch (e: Exception) {
|
||||
Log.e("ShoppingListsViewModel", "Failed to decrypt credentials for marking item", e)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val decryptedUsernameBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(usernameEncrypted), secretKey)
|
||||
val username = String(decryptedUsernameBytes, Charsets.UTF_8)
|
||||
|
||||
val decryptedPasswordBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(passwordEncrypted), secretKey)
|
||||
val password = String(decryptedPasswordBytes, Charsets.UTF_8)
|
||||
|
||||
noteshopRepository.markItemInWebApp(url, username, password, itemName)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("ShoppingListsViewModel", "Failed to mark item in web app", e)
|
||||
}
|
||||
|
||||
if (username.isNullOrBlank() || password.isNullOrBlank()) {
|
||||
return@launch // Silently fail if not configured
|
||||
}
|
||||
|
||||
noteshopRepository.markItemInWebApp(url, username, password, itemName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,33 +542,51 @@ class ShoppingListsViewModel(private val noteshopRepository: NoteshopRepository,
|
||||
viewModelScope.launch {
|
||||
val webAppPrefs = getApplication<Application>().getSharedPreferences("webapp_prefs", Context.MODE_PRIVATE)
|
||||
val url = webAppPrefs.getString("webapp_url", null)
|
||||
val keyPass = webAppPrefs.getString("key_pass", null) // This is the delete password
|
||||
val deletePass = webAppPrefs.getString("key_pass", "") ?: ""
|
||||
|
||||
if (url.isNullOrBlank() || keyPass.isNullOrBlank()) {
|
||||
if (url.isNullOrBlank()) {
|
||||
// Silently fail if not configured
|
||||
return@launch
|
||||
}
|
||||
|
||||
val fileEncryptor = FileEncryptor()
|
||||
val keyManager = KeyManager(getApplication(), false)
|
||||
val secretKey = keyManager.derivePbeKey(keyPass.toCharArray())
|
||||
val keyPass = webAppPrefs.getString("key_pass", null)
|
||||
val username: String?
|
||||
val password: String?
|
||||
|
||||
try {
|
||||
val usernameEncrypted = webAppPrefs.getString("username_encrypted", null)
|
||||
val passwordEncrypted = webAppPrefs.getString("password_encrypted", null)
|
||||
if (keyPass.isNullOrBlank()) {
|
||||
// Load unencrypted credentials
|
||||
username = webAppPrefs.getString("username", null)
|
||||
password = webAppPrefs.getString("password", null)
|
||||
} else {
|
||||
// Load encrypted credentials
|
||||
val fileEncryptor = FileEncryptor()
|
||||
val keyManager = KeyManager(getApplication(), false)
|
||||
val secretKey = keyManager.derivePbeKey(keyPass.toCharArray())
|
||||
|
||||
if (usernameEncrypted == null || passwordEncrypted == null) {
|
||||
return@launch // Silently fail
|
||||
try {
|
||||
val usernameEncrypted = webAppPrefs.getString("username_encrypted", null)
|
||||
val passwordEncrypted = webAppPrefs.getString("password_encrypted", null)
|
||||
|
||||
if (usernameEncrypted == null || passwordEncrypted == null) {
|
||||
return@launch // Silently fail
|
||||
}
|
||||
|
||||
val decryptedUsernameBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(usernameEncrypted), secretKey)
|
||||
username = String(decryptedUsernameBytes, Charsets.UTF_8)
|
||||
|
||||
val decryptedPasswordBytes = fileEncryptor.decrypt(java.util.Base64.getDecoder().decode(passwordEncrypted), secretKey)
|
||||
password = String(decryptedPasswordBytes, Charsets.UTF_8)
|
||||
} catch (e: Exception) {
|
||||
Log.e("ShoppingListsViewModel", "Failed to decrypt credentials for deleting items", e)
|
||||
return@launch
|
||||
}
|
||||
|
||||
val username = String(java.util.Base64.getDecoder().decode(usernameEncrypted), Charsets.UTF_8)
|
||||
val password = String(java.util.Base64.getDecoder().decode(passwordEncrypted), Charsets.UTF_8)
|
||||
|
||||
noteshopRepository.deleteMarkedItemsInWebApp(url, username, password, keyPass)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.e("ShoppingListsViewModel", "Failed to delete marked items in web app", e)
|
||||
}
|
||||
|
||||
if (username.isNullOrBlank() || password.isNullOrBlank()) {
|
||||
return@launch // Silently fail if not configured
|
||||
}
|
||||
|
||||
noteshopRepository.deleteMarkedItemsInWebApp(url, username, password, deletePass)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ fun WebAppIntegrationScreen(
|
||||
OutlinedTextField(
|
||||
value = viewModel.deletePassword,
|
||||
onValueChange = viewModel::onDeletePasswordChange,
|
||||
label = { Text(stringResource(R.string.delete_password_label)) },
|
||||
label = { Text(stringResource(R.string.delete_password_label) + " (optional)") },
|
||||
visualTransformation = if (viewModel.showDeletePassword) VisualTransformation.None else PasswordVisualTransformation(),
|
||||
trailingIcon = {
|
||||
val image = if (viewModel.showDeletePassword)
|
||||
|
||||
@@ -81,7 +81,12 @@ class WebAppIntegrationViewModel(private val repository: NoteshopRepository, app
|
||||
viewModelScope.launch {
|
||||
webAppUrl = sharedPrefs.getString("webapp_url", "") ?: ""
|
||||
val keyPass = sharedPrefs.getString("key_pass", null)
|
||||
if (keyPass != null) {
|
||||
if (keyPass.isNullOrBlank()) {
|
||||
// Load unencrypted credentials
|
||||
username = sharedPrefs.getString("username", "") ?: ""
|
||||
password = sharedPrefs.getString("password", "") ?: ""
|
||||
} else {
|
||||
// Load encrypted credentials
|
||||
deletePassword = keyPass
|
||||
val secretKey = keyManager.derivePbeKey(keyPass.toCharArray())
|
||||
|
||||
@@ -103,24 +108,34 @@ class WebAppIntegrationViewModel(private val repository: NoteshopRepository, app
|
||||
|
||||
fun saveAndTestConnection() {
|
||||
viewModelScope.launch {
|
||||
if (deletePassword.isBlank()) {
|
||||
Toast.makeText(getApplication(), getApplication<Application>().getString(de.lxtools.noteshop.R.string.deletion_password_cannot_be_empty), Toast.LENGTH_SHORT).show()
|
||||
return@launch
|
||||
}
|
||||
|
||||
val (success, message) = repository.testWebAppConnection(webAppUrl, username, password)
|
||||
if (success) {
|
||||
val secretKey = keyManager.derivePbeKey(deletePassword.toCharArray())
|
||||
if (deletePassword.isNotBlank()) {
|
||||
val secretKey = keyManager.derivePbeKey(deletePassword.toCharArray())
|
||||
|
||||
val encryptedUsername = java.util.Base64.getEncoder().encodeToString(fileEncryptor.encrypt(username.toByteArray(), secretKey))
|
||||
val encryptedPassword = java.util.Base64.getEncoder().encodeToString(fileEncryptor.encrypt(password.toByteArray(), secretKey))
|
||||
val encryptedUsername = java.util.Base64.getEncoder().encodeToString(fileEncryptor.encrypt(username.toByteArray(), secretKey))
|
||||
val encryptedPassword = java.util.Base64.getEncoder().encodeToString(fileEncryptor.encrypt(password.toByteArray(), secretKey))
|
||||
|
||||
sharedPrefs.edit().apply {
|
||||
putString("webapp_url", webAppUrl)
|
||||
putString("username_encrypted", encryptedUsername)
|
||||
putString("password_encrypted", encryptedPassword)
|
||||
putString("key_pass", deletePassword) // Storing the key password directly, assuming it's the delete password
|
||||
apply()
|
||||
sharedPrefs.edit().apply {
|
||||
putString("webapp_url", webAppUrl)
|
||||
putString("username_encrypted", encryptedUsername)
|
||||
putString("password_encrypted", encryptedPassword)
|
||||
putString("key_pass", deletePassword)
|
||||
remove("username") // Remove plain text credentials if they exist
|
||||
remove("password")
|
||||
apply()
|
||||
}
|
||||
} else {
|
||||
// Store credentials unencrypted
|
||||
sharedPrefs.edit().apply {
|
||||
putString("webapp_url", webAppUrl)
|
||||
putString("username", username)
|
||||
putString("password", password)
|
||||
remove("username_encrypted")
|
||||
remove("password_encrypted")
|
||||
remove("key_pass")
|
||||
apply()
|
||||
}
|
||||
}
|
||||
Toast.makeText(getApplication(), de.lxtools.noteshop.R.string.connection_successful, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
@@ -141,6 +156,8 @@ class WebAppIntegrationViewModel(private val repository: NoteshopRepository, app
|
||||
remove("username_encrypted")
|
||||
remove("password_encrypted")
|
||||
remove("key_pass")
|
||||
remove("username")
|
||||
remove("password")
|
||||
apply()
|
||||
}
|
||||
Toast.makeText(getApplication(), getApplication<Application>().getString(de.lxtools.noteshop.R.string.web_app_integration_reset_successful), Toast.LENGTH_SHORT).show()
|
||||
|
||||
@@ -302,6 +302,11 @@
|
||||
<string name="web_app_integration_reset_successful">Web-App-Integration erfolgreich zurückgesetzt</string>
|
||||
<string name="failed_to_decrypt_credentials">Fehler beim Entschlüsseln der Zugangsdaten</string>
|
||||
<string name="deletion_password_cannot_be_empty">Löschpasswort darf nicht leer sein</string>
|
||||
<string name="webapp_url_not_set_up">Web-App-URL nicht eingerichtet</string>
|
||||
<string name="webapp_credentials_not_set_up">Web-App-Anmeldeinformationen nicht eingerichtet</string>
|
||||
<string name="webapp_credentials_corrupted">Web-App-Anmeldeinformationen beschädigt</string>
|
||||
<string name="failed_to_decrypt_credentials_generic">Anmeldeinformationen konnten nicht entschlüsselt werden: %1$s</string>
|
||||
<string name="failed_to_import_items">Elemente konnten nicht importiert werden: %1$s</string>
|
||||
|
||||
<string name="tour_page_1_title">Willkommen bei Noteshop!</string>
|
||||
<string name="tour_page_1_text">Verwalte deine Notizen, Einkaufslisten und Rezepte an einem Ort.</string>
|
||||
|
||||
@@ -302,6 +302,11 @@
|
||||
<string name="web_app_integration_reset_successful">Web App Integration reset successfully</string>
|
||||
<string name="failed_to_decrypt_credentials">Failed to decrypt credentials</string>
|
||||
<string name="deletion_password_cannot_be_empty">Deletion password cannot be empty</string>
|
||||
<string name="webapp_url_not_set_up">Web App URL not set up</string>
|
||||
<string name="webapp_credentials_not_set_up">Web App credentials not set up</string>
|
||||
<string name="webapp_credentials_corrupted">Web App credentials corrupted</string>
|
||||
<string name="failed_to_decrypt_credentials_generic">Failed to decrypt credentials: %1$s</string>
|
||||
<string name="failed_to_import_items">Failed to import items: %1$s</string>
|
||||
|
||||
<string name="tour_page_1_title">Welcome to Noteshop!</string>
|
||||
<string name="tour_page_1_text">Manage your notes, shopping lists, and recipes all in one place.</string>
|
||||
|
||||
Reference in New Issue
Block a user