feat: show clear error when p12 certificate import fails

The import previously swallowed all exceptions silently. Now the upload
config dialog shows a specific message for: wrong password, expired
certificate, invalid file, and unknown failures.
This commit is contained in:
atsunatsu committed 2026-09-25 23:50:48 +08:00
1 parent bc5a8e4bba
commit d8d0825f54
6 files changed
+38 -3

No files matched your search

@@ -203,6 +203,10 @@
<string name="prefs_lotw_upload_password">证书密码</string> <string name="prefs_lotw_upload_password">证书密码</string>
<string name="prefs_lotw_upload_import">选择 .p12 文件</string> <string name="prefs_lotw_upload_import">选择 .p12 文件</string>
<string name="prefs_lotw_upload_import_confirm">导入</string> <string name="prefs_lotw_upload_import_confirm">导入</string>
<string name="prefs_lotw_upload_error_password">证书密码错误,请检查 TQSL 导出 .p12 时设置的密码</string>
<string name="prefs_lotw_upload_error_expired">证书已过期或尚未生效</string>
<string name="prefs_lotw_upload_error_invalid">不是有效的 LoTW 证书文件</string>
<string name="prefs_lotw_upload_error_unknown">导入失败,请重试</string>
<string name="prefs_lotw_upload_cert_info">%1$s · DXCC %2$d · 有效期至 %3$s</string> <string name="prefs_lotw_upload_cert_info">%1$s · DXCC %2$d · 有效期至 %3$s</string>
<string name="prefs_lotw_upload_remove">移除证书</string> <string name="prefs_lotw_upload_remove">移除证书</string>
<string name="prefs_lotw_upload_station_title">台址</string> <string name="prefs_lotw_upload_station_title">台址</string>
@@ -234,6 +234,10 @@
<string name="prefs_lotw_upload_password">Certificate password</string> <string name="prefs_lotw_upload_password">Certificate password</string>
<string name="prefs_lotw_upload_import">Choose .p12 file</string> <string name="prefs_lotw_upload_import">Choose .p12 file</string>
<string name="prefs_lotw_upload_import_confirm">Import</string> <string name="prefs_lotw_upload_import_confirm">Import</string>
<string name="prefs_lotw_upload_error_password">Incorrect certificate password. Check the password you set when exporting the .p12 from TQSL.</string>
<string name="prefs_lotw_upload_error_expired">Certificate is expired or not yet valid.</string>
<string name="prefs_lotw_upload_error_invalid">Not a valid LoTW certificate file.</string>
<string name="prefs_lotw_upload_error_unknown">Import failed. Try again.</string>
<string name="prefs_lotw_upload_cert_info">%1$s · DXCC %2$d · expires %3$s</string> <string name="prefs_lotw_upload_cert_info">%1$s · DXCC %2$d · expires %3$s</string>
<string name="prefs_lotw_upload_remove">Remove certificate</string> <string name="prefs_lotw_upload_remove">Remove certificate</string>
<string name="prefs_lotw_upload_station_title">Station location</string> <string name="prefs_lotw_upload_station_title">Station location</string>
@@ -78,6 +78,7 @@ fun LoTWUploadConfigDialog(
certificate: LoTWCertificate?, certificate: LoTWCertificate?,
station: LoTWStation?, station: LoTWStation?,
busy: Boolean, busy: Boolean,
error: LoTWUploadError?,
onDismiss: () -> Unit, onDismiss: () -> Unit,
onImport: (ByteArray, CharArray) -> Unit, onImport: (ByteArray, CharArray) -> Unit,
onRemove: () -> Unit, onRemove: () -> Unit,
@@ -122,6 +123,20 @@ fun LoTWUploadConfigDialog(
isEnabled = !busy, isEnabled = !busy,
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
error?.let {
Text(
text = stringResource(
when (it) {
LoTWUploadError.PASSWORD -> R.string.prefs_lotw_upload_error_password
LoTWUploadError.EXPIRED -> R.string.prefs_lotw_upload_error_expired
LoTWUploadError.INVALID_FILE -> R.string.prefs_lotw_upload_error_invalid
LoTWUploadError.UNKNOWN -> R.string.prefs_lotw_upload_error_unknown
}
),
color = MaterialTheme.colorScheme.error,
fontSize = 12.sp
)
}
selectedFile?.let { uri -> selectedFile?.let { uri ->
Text( Text(
text = displayName(uri), text = displayName(uri),
@@ -273,6 +273,7 @@ private fun SettingsScreen(uiState: SettingsState, onAction: (SettingsAction) ->
certificate = uiState.lotwCertificate, certificate = uiState.lotwCertificate,
station = uiState.lotwStation, station = uiState.lotwStation,
busy = uiState.lotwUploadBusy, busy = uiState.lotwUploadBusy,
error = uiState.lotwUploadError,
onDismiss = { dialogs.lotwUpload = false }, onDismiss = { dialogs.lotwUpload = false },
onImport = { bytes, password -> onAction(SettingsAction.ImportLoTWCertificate(bytes, password)) }, onImport = { bytes, password -> onAction(SettingsAction.ImportLoTWCertificate(bytes, password)) },
onRemove = { onAction(SettingsAction.RemoveLoTWCertificate) }, onRemove = { onAction(SettingsAction.RemoveLoTWCertificate) },
@@ -75,6 +75,8 @@ data class SettingsState(
/** LoTW upload station location (null when unset). */ /** LoTW upload station location (null when unset). */
val lotwStation: com.rtbishop.look4sat.core.domain.repository.LoTWStation? = null, val lotwStation: com.rtbishop.look4sat.core.domain.repository.LoTWStation? = null,
val lotwUploadBusy: Boolean = false, val lotwUploadBusy: Boolean = false,
/** Last certificate import outcome; shown inside the upload config dialog. */
val lotwUploadError: LoTWUploadError? = null,
/** 指南针校准精度等级 (校准对话框进度条). */ /** 指南针校准精度等级 (校准对话框进度条). */
val compassAccuracy: CompassAccuracy = CompassAccuracy.UNRELIABLE, val compassAccuracy: CompassAccuracy = CompassAccuracy.UNRELIABLE,
/** 校正后航向(度, 含磁偏角+手动偏置), 校准对话框实时显示. */ /** 校正后航向(度, 含磁偏角+手动偏置), 校准对话框实时显示. */
@@ -91,6 +93,8 @@ sealed interface LoTWError {
data class Network(val detail: String) : LoTWError data class Network(val detail: String) : LoTWError
} }
enum class LoTWUploadError { PASSWORD, INVALID_FILE, EXPIRED, UNKNOWN }
sealed interface SettingsAction { sealed interface SettingsAction {
// Position // Position
data object SetGpsPosition : SettingsAction data object SetGpsPosition : SettingsAction
@@ -354,12 +354,19 @@ class SettingsViewModel(
private fun importLoTWCertificate(bytes: ByteArray, password: CharArray) { private fun importLoTWCertificate(bytes: ByteArray, password: CharArray) {
viewModelScope.launch { viewModelScope.launch {
_uiState.update { it.copy(lotwUploadBusy = true) } _uiState.update { it.copy(lotwUploadBusy = true, lotwUploadError = null) }
try { try {
val cert = lotwUploadRepository.importCertificate(bytes, password) val cert = lotwUploadRepository.importCertificate(bytes, password)
_uiState.update { it.copy(lotwCertificate = cert, lotwUploadBusy = false) } _uiState.update { it.copy(lotwCertificate = cert, lotwUploadBusy = false, lotwUploadError = null) }
} catch (e: com.rtbishop.look4sat.core.domain.repository.LoTWOperationException) {
val error = when (e.reason) {
com.rtbishop.look4sat.core.domain.repository.LoTWProblem.CERTIFICATE_PASSWORD -> LoTWUploadError.PASSWORD
com.rtbishop.look4sat.core.domain.repository.LoTWProblem.CERTIFICATE_EXPIRED -> LoTWUploadError.EXPIRED
else -> LoTWUploadError.INVALID_FILE
}
_uiState.update { it.copy(lotwUploadBusy = false, lotwUploadError = error) }
} catch (_: Exception) { } catch (_: Exception) {
_uiState.update { it.copy(lotwUploadBusy = false) } _uiState.update { it.copy(lotwUploadBusy = false, lotwUploadError = LoTWUploadError.UNKNOWN) }
} }
} }
} }