diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/AccessibilityScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/AccessibilityScreen.kt index 9fc12c634..f5f38a45d 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/AccessibilityScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/AccessibilityScreen.kt @@ -42,6 +42,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign @@ -200,7 +201,9 @@ fun AccessibilityScreen( .padding( horizontal = SPadding, vertical = SPadding, - ).testTag("mainAccessibilityIntroductionScreenReaderIntroductionIos"), + ).semantics { + stateDescription = "link" + }.testTag("mainAccessibilityIntroductionScreenReaderIntroductionIos"), text = stringResource(R.string.main_accessibility_introduction_screen_reader_introduction_ios), ) DynamicText( @@ -209,7 +212,9 @@ fun AccessibilityScreen( .padding( horizontal = SPadding, vertical = SPadding, - ).testTag("mainAccessibilityIntroductionScreenReaderIntroductionAndroid"), + ).semantics { + stateDescription = "link" + }.testTag("mainAccessibilityIntroductionScreenReaderIntroductionAndroid"), text = stringResource(R.string.main_accessibility_introduction_screen_reader_introduction_android), ) Text( @@ -249,7 +254,9 @@ fun AccessibilityScreen( .padding( horizontal = SPadding, vertical = SPadding, - ).testTag("mainAccessibilityIntroductionScreenMagnificationScreenToolsIos"), + ).semantics { + stateDescription = "link" + }.testTag("mainAccessibilityIntroductionScreenMagnificationScreenToolsIos"), text = stringResource( R.string.main_accessibility_introduction_screen_magnification_screen_tools_ios, @@ -261,7 +268,9 @@ fun AccessibilityScreen( .padding( horizontal = SPadding, vertical = SPadding, - ).testTag("mainAccessibilityIntroductionScreenMagnificationScreenToolsAndroid"), + ).semantics { + stateDescription = "link" + }.testTag("mainAccessibilityIntroductionScreenMagnificationScreenToolsAndroid"), text = stringResource( R.string.main_accessibility_introduction_screen_magnification_screen_tools_android, @@ -282,7 +291,9 @@ fun AccessibilityScreen( .padding( horizontal = SPadding, vertical = SPadding, - ).testTag("mainAccessibilityIntroductionScreenMagnificationToolsIos"), + ).semantics { + stateDescription = "link" + }.testTag("mainAccessibilityIntroductionScreenMagnificationToolsIos"), text = stringResource(R.string.main_accessibility_introduction_screen_magnification_tools_ios), ) DynamicText( @@ -291,7 +302,9 @@ fun AccessibilityScreen( .padding( horizontal = SPadding, vertical = SPadding, - ).testTag("mainAccessibilityIntroductionScreenMagnificationToolsAndroid"), + ).semantics { + stateDescription = "link" + }.testTag("mainAccessibilityIntroductionScreenMagnificationToolsAndroid"), text = stringResource(R.string.main_accessibility_introduction_screen_magnification_tools_android), ) InvisibleElement(modifier = modifier) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/DiagnosticsScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/DiagnosticsScreen.kt index 74ebedcc2..e1f25d5cb 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/DiagnosticsScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/DiagnosticsScreen.kt @@ -650,58 +650,53 @@ fun DiagnosticsScreen( }.testTag("mainDiagnosticsRestartConfirmationDialog"), onDismissRequest = dismissRestartConfirmationDialog, ) { - Surface( + Column( modifier = modifier .padding(SPadding) .wrapContentHeight() .wrapContentWidth() - .verticalScroll(rememberScrollState()), + .verticalScroll(rememberScrollState()) + .semantics { + testTagsAsResourceId = true + }.testTag("diagnosticsActivateLoggingContainer"), ) { - Column( - modifier = - modifier - .semantics { - testTagsAsResourceId = true - }.testTag("diagnosticsActivateLoggingContainer"), - ) { - HrefMessageDialog( - text1 = R.string.main_diagnostics_restart_message, - text2 = R.string.main_diagnostics_restart_message_restart_now, - linkText = R.string.read_more_here, - linkUrl = R.string.main_diagnostics_restart_message_href, - newLineBeforeLink = true, - newLineBeforeText2 = true, - ) + HrefMessageDialog( + text1 = R.string.main_diagnostics_restart_message, + text2 = R.string.main_diagnostics_restart_message_restart_now, + linkText = R.string.read_more_here, + linkUrl = R.string.main_diagnostics_restart_message_href, + newLineBeforeLink = true, + newLineBeforeText2 = true, + ) - CancelAndOkButtonRow( - okButtonTestTag = "hrefMessageDialogOkButton", - cancelButtonTestTag = "hrefMessageDialogCancelButton", - cancelButtonClick = dismissRestartConfirmationDialog, - okButtonClick = { - enableOneTimeLogGeneration = true - diagnosticsViewModel.dataStore.setIsLogFileGenerationEnabled( - true, - ) - closeRestartConfirmationDialog() - sendAccessibilityEvent( - context, - getAccessibilityEventType(), - settingValueChanged, - ) - sharedSettingsViewModel.recreateActivity(true) - }, - cancelButtonTitle = R.string.cancel_button, - okButtonTitle = R.string.ok_button, - cancelButtonContentDescription = - stringResource( - id = R.string.cancel_button, - ).lowercase(), - okButtonContentDescription = stringResource(id = R.string.ok_button).lowercase(), - showCancelButton = true, - ) - InvisibleElement(modifier = modifier) - } + CancelAndOkButtonRow( + okButtonTestTag = "hrefMessageDialogOkButton", + cancelButtonTestTag = "hrefMessageDialogCancelButton", + cancelButtonClick = dismissRestartConfirmationDialog, + okButtonClick = { + enableOneTimeLogGeneration = true + diagnosticsViewModel.dataStore.setIsLogFileGenerationEnabled( + true, + ) + closeRestartConfirmationDialog() + sendAccessibilityEvent( + context, + getAccessibilityEventType(), + settingValueChanged, + ) + sharedSettingsViewModel.recreateActivity(true) + }, + cancelButtonTitle = R.string.cancel_button, + okButtonTitle = R.string.ok_button, + cancelButtonContentDescription = + stringResource( + id = R.string.cancel_button, + ).lowercase(), + okButtonContentDescription = stringResource(id = R.string.ok_button).lowercase(), + showCancelButton = true, + ) + InvisibleElement(modifier = modifier) } } } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptionServicesSettingsScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptionServicesSettingsScreen.kt index eb20c032f..e16ecff18 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptionServicesSettingsScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptionServicesSettingsScreen.kt @@ -72,6 +72,7 @@ import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction @@ -183,6 +184,8 @@ fun EncryptionServicesSettingsScreen( val fetchUrlLabel = stringResource(R.string.main_settings_crypto_fetch_url) val postUrlLabel = stringResource(R.string.main_settings_crypto_post_url) + val panelDescription = stringResource(R.string.panel) + var uuidText by rememberSaveable(stateSaver = textFieldValueSaver) { mutableStateOf( TextFieldValue( @@ -476,6 +479,7 @@ fun EncryptionServicesSettingsScreen( ).semantics { contentDescription = "$serverLabel: ${selectedCdoc2Conf.name}" + stateDescription = panelDescription }, ) } else { diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/HomeScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/HomeScreen.kt index 0b9779250..96820ff8a 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/HomeScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/HomeScreen.kt @@ -56,8 +56,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.isTraversalGroup import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.semantics.traversalIndex import androidx.compose.ui.tooling.preview.Preview @@ -80,6 +82,7 @@ import ee.ria.DigiDoc.ui.theme.Dimensions.XSPadding import ee.ria.DigiDoc.ui.theme.Dimensions.iconSizeM import ee.ria.DigiDoc.ui.theme.RIADigiDocTheme import ee.ria.DigiDoc.utils.Route +import ee.ria.DigiDoc.utils.extensions.notAccessible import ee.ria.DigiDoc.viewmodel.HomeViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedMenuViewModel import kotlinx.coroutines.CoroutineScope @@ -166,7 +169,9 @@ fun HomeScreen( }, sharedMenuViewModel = sharedMenuViewModel, leftIcon = R.drawable.ic_m3_menu_48dp_wght400, + leftIconContentDescription = R.string.main_home_menu_button, title = null, + isPanel = true, onLeftButtonClick = { isMainMenuBottomSheetVisible.value = true isSettingsMenuBottomSheetVisible.value = false @@ -252,10 +257,13 @@ fun HomeScreen( modifier = modifier .padding(end = XSPadding) - .width(iconSizeM), + .width(iconSizeM) + .semantics { + stateDescription = "logo" + }, ) Text( - modifier = modifier, + modifier = modifier.notAccessible(), style = MaterialTheme.typography.displayMedium, color = MaterialTheme.colorScheme.onSurface, text = stringResource(id = R.string.digidoc), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/InfoScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/InfoScreen.kt index 6787924fd..84c0a144e 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/InfoScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/InfoScreen.kt @@ -71,6 +71,7 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -199,6 +200,7 @@ fun InfoScreen( .aspectRatio(cofundedLogoWidthPx.toFloat() / cofundedLogo.height) .semantics { contentDescription = logoDescription + stateDescription = "logo" role = Role.Image }.drawBehind { drawRect(logoSurfaceColor) @@ -215,23 +217,29 @@ fun InfoScreen( modifier = Modifier.weight(1f), horizontalAlignment = Alignment.Start, ) { - Text( - text = stringResource(id = R.string.main_about_ria_digidoc_title), - style = MaterialTheme.typography.titleLarge, - ) - Text( - text = - String.format( - stringResource(id = R.string.main_about_version_title), - BuildConfig.VERSION_NAME + '.' + BuildConfig.VERSION_CODE, - ), - style = MaterialTheme.typography.bodyMedium, - ) + Column( + modifier = modifier.semantics(mergeDescendants = true) {}, + ) { + Text( + text = stringResource(id = R.string.main_about_ria_digidoc_title), + style = MaterialTheme.typography.titleLarge, + ) + Text( + text = + String.format( + stringResource(id = R.string.main_about_version_title), + BuildConfig.VERSION_NAME + '.' + BuildConfig.VERSION_CODE, + ), + style = MaterialTheme.typography.bodyMedium, + ) + } + Text( modifier = Modifier.padding(vertical = SPadding), text = stringResource(id = R.string.main_about_info_title), style = MaterialTheme.typography.labelMedium, ) + val helpButtonContentDescriptionText = if (isEstonianLanguageUsed.value) { stringResource(id = R.string.main_home_menu_help) + @@ -256,6 +264,7 @@ fun InfoScreen( ).clip(buttonRoundCornerShape) .semantics { contentDescription = helpButtonContentDescriptionText + stateDescription = "link" testTagsAsResourceId = true }.testTag("mainInfoHelpButton"), colors = diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/InitScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/InitScreen.kt index 98b4ea057..1d34420f6 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/InitScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/InitScreen.kt @@ -41,6 +41,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.tooling.preview.Preview import androidx.navigation.NavHostController @@ -87,7 +88,9 @@ fun InitScreen( top = LPadding, bottom = zeroPadding, end = XLPadding, - ), + ).semantics { + stateDescription = "logo" + }, ) Text( text = stringResource(id = R.string.digidoc_all_caps), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ProxyServicesSettingsScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ProxyServicesSettingsScreen.kt index 49066bb80..8ad25ad03 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ProxyServicesSettingsScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/ProxyServicesSettingsScreen.kt @@ -35,8 +35,6 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton import androidx.compose.material3.Scaffold @@ -47,7 +45,6 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -56,16 +53,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTagsAsResourceId -import androidx.compose.ui.semantics.traversalIndex import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction @@ -87,14 +79,11 @@ import ee.ria.DigiDoc.ui.theme.Dimensions.SPadding import ee.ria.DigiDoc.ui.theme.Dimensions.XSBorder import ee.ria.DigiDoc.ui.theme.Dimensions.XSPadding import ee.ria.DigiDoc.ui.theme.buttonRoundedCornerShape -import ee.ria.DigiDoc.utils.accessibility.AccessibilityUtil.Companion.isTalkBackEnabled import ee.ria.DigiDoc.utils.extensions.notAccessible import ee.ria.DigiDoc.utils.snackbar.SnackBarManager.showMessage import ee.ria.DigiDoc.viewmodel.shared.SharedMenuViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedSettingsViewModel import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @OptIn(ExperimentalLayoutApi::class, ExperimentalComposeUiApi::class) @@ -105,10 +94,6 @@ fun ProxyServicesSettingsScreen( sharedMenuViewModel: SharedMenuViewModel, navController: NavHostController, ) { - val context = LocalContext.current - val focusManager = LocalFocusManager.current - val scope = rememberCoroutineScope() - val hostFocusRequester = remember { FocusRequester() } val portFocusRequester = remember { FocusRequester() } val usernameFocusRequester = remember { FocusRequester() } @@ -116,8 +101,6 @@ fun ProxyServicesSettingsScreen( val isSettingsMenuBottomSheetVisible = rememberSaveable { mutableStateOf(false) } - var passwordVisible by rememberSaveable { mutableStateOf(false) } - val setProxySetting = sharedSettingsViewModel.dataStore::setProxySetting val setProxyHost = sharedSettingsViewModel.dataStore::setProxyHost val setProxyPort = sharedSettingsViewModel.dataStore::setProxyPort @@ -185,7 +168,6 @@ fun ProxyServicesSettingsScreen( val systemProxyText = stringResource(R.string.main_settings_proxy_use_system) val manualProxyText = stringResource(R.string.main_settings_proxy_manual) val proxyCheckConnectionText = stringResource(R.string.main_settings_proxy_check_connection) - val clearButtonText = stringResource(R.string.clear_text) val buttonName = stringResource(id = R.string.button_name) LaunchedEffect(sharedSettingsViewModel.errorState) { @@ -481,83 +463,29 @@ fun ProxyServicesSettingsScreen( removeIconTestTag = "proxyServicesUsernameRemoveIconButton", ) - Row( + PrimaryTextField( modifier = - modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically, - ) { - PrimaryTextField( - modifier = - Modifier - .focusRequester(passwordFocusRequester) - .weight(1f) - .padding(vertical = XSPadding), - value = proxyPassword, - onValueChange = { - proxyPassword = it - setProxyPassword(it.text) - }, - singleLine = true, - label = stringResource(R.string.main_settings_proxy_password), - enabled = settingsProxyChoice.value == ProxySetting.MANUAL_PROXY.name, - isPasswordText = !passwordVisible, - trailingIcon = { - val image = - if (passwordVisible) { - ImageVector.vectorResource(id = R.drawable.ic_visibility) - } else { - ImageVector.vectorResource(id = R.drawable.ic_visibility_off) - } - val description = - if (passwordVisible) { - stringResource( - id = R.string.hide_password, - ) - } else { - stringResource(id = R.string.show_password) - } - IconButton( - modifier = - modifier - .semantics { traversalIndex = 9f } - .testTag("proxyServicesPasswordVisibleButton"), - onClick = { passwordVisible = !passwordVisible }, - ) { - Icon(imageVector = image, description) - } - }, - keyboardOptions = - KeyboardOptions.Default.copy( - imeAction = ImeAction.Done, - keyboardType = KeyboardType.Password, - ), - testTag = "proxyServicesPasswordTextField", - ) - - if (isTalkBackEnabled(context) && proxyPassword.text.isNotEmpty()) { - IconButton(onClick = { - proxyPassword = TextFieldValue("") - scope.launch(Main) { - passwordFocusRequester.requestFocus() - focusManager.clearFocus() - delay(200) - passwordFocusRequester.requestFocus() - } - }) { - Icon( - modifier = - modifier - .semantics { - testTagsAsResourceId = true - }.testTag("proxyServicesPasswordRemoveIconButton"), - imageVector = ImageVector.vectorResource(R.drawable.ic_icon_remove), - contentDescription = "$clearButtonText $buttonName", - ) - } - } - } + Modifier + .focusRequester(passwordFocusRequester) + .padding(vertical = XSPadding), + value = proxyPassword, + onValueChange = { + proxyPassword = it + setProxyPassword(it.text) + }, + singleLine = true, + label = stringResource(R.string.main_settings_proxy_password), + enabled = settingsProxyChoice.value == ProxySetting.MANUAL_PROXY.name, + isPasswordText = true, + keyboardOptions = + KeyboardOptions.Default.copy( + imeAction = ImeAction.Done, + keyboardType = KeyboardType.Password, + ), + testTag = "proxyServicesPasswordTextField", + removeIconTestTag = "proxyServicesPasswordRemoveIconButton", + showIconTestTag = "proxyServicesPasswordVisibleButton", + ) } } } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/SignatureMethodScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/SignatureMethodScreen.kt index eb9ce0328..d764ccf0b 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/SignatureMethodScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/SignatureMethodScreen.kt @@ -42,6 +42,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi @@ -58,6 +59,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController import ee.ria.DigiDoc.R import ee.ria.DigiDoc.domain.model.methods.SigningMethod +import ee.ria.DigiDoc.ui.component.menu.SettingsMenuBottomSheet import ee.ria.DigiDoc.ui.component.shared.StatusSnackbarHost import ee.ria.DigiDoc.ui.component.shared.TopBar import ee.ria.DigiDoc.ui.component.signing.SignatureAddRadioItem @@ -87,6 +89,8 @@ fun SignatureMethodScreen( val signingMethodText = stringResource(id = R.string.signature_method) val signingMethodSelectedText = stringResource(id = R.string.signature_method_selected) + val isSettingsMenuBottomSheetVisible = rememberSaveable { mutableStateOf(false) } + Scaffold( snackbarHost = { StatusSnackbarHost() }, topBar = { @@ -97,9 +101,17 @@ fun SignatureMethodScreen( onLeftButtonClick = { navController.navigateUp() }, + onRightSecondaryButtonClick = { + isSettingsMenuBottomSheetVisible.value = true + }, ) }, ) { paddingValues -> + SettingsMenuBottomSheet( + navController = navController, + isBottomSheetVisible = isSettingsMenuBottomSheetVisible, + ) + Column( modifier = modifier diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/DecryptPasswordDialog.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/DecryptPasswordDialog.kt index f67c5602d..8245258d5 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/DecryptPasswordDialog.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/DecryptPasswordDialog.kt @@ -110,7 +110,7 @@ fun DecryptPasswordDialog( .notAccessible(), textAlign = TextAlign.Start, style = MaterialTheme.typography.bodyLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant + color = MaterialTheme.colorScheme.onSurfaceVariant, ) Text( diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/RecipientComponent.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/RecipientComponent.kt index b59ef74b9..3fcfeae1c 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/RecipientComponent.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/RecipientComponent.kt @@ -54,6 +54,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import ee.ria.DigiDoc.R import ee.ria.DigiDoc.cryptolib.Addressee @@ -86,6 +87,7 @@ fun RecipientComponent( ) { val recipientText = stringResource(R.string.crypto_recipient_title) val buttonName = stringResource(id = R.string.button_name) + val panelDescription = stringResource(R.string.panel) if (showRecipientsLoadingIndicator) { Box( @@ -255,6 +257,7 @@ fun RecipientComponent( modifier = modifier .semantics { + stateDescription = panelDescription testTagsAsResourceId = true }.testTag("recipientComponentMoreOptionsIconButton"), imageVector = ImageVector.vectorResource(R.drawable.ic_more_vert), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/info/InfoComponent.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/info/InfoComponent.kt index d420ab651..e31923da9 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/info/InfoComponent.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/info/InfoComponent.kt @@ -45,6 +45,7 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -85,7 +86,8 @@ fun InfoComponent( modifier = modifier .weight(1f) - .fillMaxWidth(), + .fillMaxWidth() + .semantics(mergeDescendants = true) {}, horizontalAlignment = Alignment.Start, ) { Text( @@ -138,6 +140,7 @@ fun InfoComponent( .size(iconSizeXXS) .semantics { testTagsAsResourceId = true + stateDescription = "link" }.testTag("mainAboutComponentLicenseUrlButton"), imageVector = ImageVector.vectorResource(id = R.drawable.ic_m3_open_in_new_48dp_wght400), contentDescription = openButtonContentDescription, diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/myeid/pinandcertificate/MyEidPinScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/myeid/pinandcertificate/MyEidPinScreen.kt index e35a7dafa..77664e951 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/myeid/pinandcertificate/MyEidPinScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/myeid/pinandcertificate/MyEidPinScreen.kt @@ -643,6 +643,7 @@ fun MyEidPinScreen( isError = !isCurrentPinValid, keyboardImeAction = ImeAction.Next, removeIconTestTag = "myEidCurrentPinRemoveButton", + showIconTestTag = "myEidCurrentPinShowButton", onDone = { if (isCurrentPinValid) { showCurrentPinField.value = false @@ -705,6 +706,7 @@ fun MyEidPinScreen( isError = !isNewPinValid, keyboardImeAction = ImeAction.Next, removeIconTestTag = "myEidNewPinRemoveButton", + showIconTestTag = "myEidNewPinShowButton", onDone = { if (isNewPinValid) { showCurrentPinField.value = false @@ -795,6 +797,7 @@ fun MyEidPinScreen( pinCodeTextEdited = null, isError = !isNewRepeatedPinValid, removeIconTestTag = "myEidNewPinRepeatedRemoveButton", + showIconTestTag = "myEidNewPinRepeatedShowButton", ) Text( modifier = diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/settings/advanced/signingservices/MobileIdAndSmartIdServicesComponent.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/settings/advanced/signingservices/MobileIdAndSmartIdServicesComponent.kt index 5da2cb32b..16f400d22 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/settings/advanced/signingservices/MobileIdAndSmartIdServicesComponent.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/settings/advanced/signingservices/MobileIdAndSmartIdServicesComponent.kt @@ -23,7 +23,6 @@ package ee.ria.DigiDoc.ui.component.settings.advanced.signingservices import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.Row @@ -33,8 +32,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton import androidx.compose.material3.Text @@ -43,7 +40,6 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment @@ -51,17 +47,12 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource -import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.testTagsAsResourceId -import androidx.compose.ui.semantics.traversalIndex import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -77,12 +68,8 @@ import ee.ria.DigiDoc.ui.theme.Dimensions.SPadding import ee.ria.DigiDoc.ui.theme.Dimensions.XSBorder import ee.ria.DigiDoc.ui.theme.Dimensions.XSPadding import ee.ria.DigiDoc.ui.theme.buttonRoundedCornerShape -import ee.ria.DigiDoc.utils.accessibility.AccessibilityUtil.Companion.isTalkBackEnabled import ee.ria.DigiDoc.utils.extensions.notAccessible import ee.ria.DigiDoc.viewmodel.shared.SharedSettingsViewModel -import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch @OptIn(ExperimentalLayoutApi::class, ExperimentalComposeUiApi::class) @Composable @@ -91,13 +78,9 @@ fun MobileIdAndSmartIdServicesComponent( sharedSettingsViewModel: SharedSettingsViewModel, ) { val context = LocalContext.current - val focusManager = LocalFocusManager.current - val scope = rememberCoroutineScope() val focusRequester = remember { FocusRequester() } - var passwordVisible by rememberSaveable { mutableStateOf(false) } - val getSettingsUUID = sharedSettingsViewModel.dataStore::getSettingsUUID val getUuidSetting = sharedSettingsViewModel.dataStore::getUuidSetting val setSettingsUuid = sharedSettingsViewModel.dataStore::setSettingsUUID @@ -118,9 +101,6 @@ fun MobileIdAndSmartIdServicesComponent( val useManualAccessText = stringResource(R.string.main_settings_siva_default_manual_access_title) val accessToMobileAndSmartIdServicesText = stringResource(R.string.main_settings_uuid_title) - val clearButtonText = stringResource(R.string.clear_text) - val buttonName = stringResource(id = R.string.button_name) - // Reset RPUUID when the user navigates away from this screen and has set default choice DisposableEffect(Unit) { onDispose { @@ -246,84 +226,27 @@ fun MobileIdAndSmartIdServicesComponent( } if (settingsUuidChoice.value == UUIDSetting.MANUAL.name) { -// Spacer(modifier = modifier.height(LPadding)) - - Row( - modifier = - modifier - .fillMaxWidth(), - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically, - ) { - PrimaryTextField( - modifier = - Modifier - .padding(vertical = LPadding) - .weight(1f), - value = settingsUuid, - onValueChange = { - settingsUuid = it - setSettingsUuid(it.text) - }, - singleLine = true, - enabled = settingsUuidChoice.value == UUIDSetting.MANUAL.name, - label = accessToMobileAndSmartIdServicesText, - isPasswordText = !passwordVisible, - keyboardOptions = - KeyboardOptions.Default.copy( - imeAction = ImeAction.Done, - keyboardType = KeyboardType.Password, - ), - trailingIcon = { - val image = - if (passwordVisible) { - ImageVector.vectorResource(id = R.drawable.ic_visibility) - } else { - ImageVector.vectorResource(id = R.drawable.ic_visibility_off) - } - val description = - if (passwordVisible) { - stringResource( - id = R.string.hide_password, - ) - } else { - stringResource(id = R.string.show_password) - } - IconButton( - modifier = - modifier - .semantics { traversalIndex = 9f } - .testTag("mobileIdAndSmartIdServicesComponentPasswordVisibleButton"), - onClick = { passwordVisible = !passwordVisible }, - ) { - Icon(imageVector = image, description) - } - }, - testTag = "mobileIdAndSmartIdServicesComponentTextField", - ) - - if (isTalkBackEnabled(context) && settingsUuid.text.isNotEmpty()) { - IconButton(onClick = { - settingsUuid = TextFieldValue("") - scope.launch(Main) { - focusRequester.requestFocus() - focusManager.clearFocus() - delay(200) - focusRequester.requestFocus() - } - }) { - Icon( - modifier = - modifier - .semantics { - testTagsAsResourceId = true - }.testTag("mobileIdAndSmartIdServicesComponentRemoveIconButton"), - imageVector = ImageVector.vectorResource(R.drawable.ic_icon_remove), - contentDescription = "$clearButtonText $buttonName", - ) - } - } - } + PrimaryTextField( + modifier = Modifier.padding(vertical = LPadding), + focusRequester = focusRequester, + value = settingsUuid, + onValueChange = { + settingsUuid = it + setSettingsUuid(it.text) + }, + singleLine = true, + enabled = settingsUuidChoice.value == UUIDSetting.MANUAL.name, + label = accessToMobileAndSmartIdServicesText, + isPasswordText = true, + keyboardOptions = + KeyboardOptions.Default.copy( + imeAction = ImeAction.Done, + keyboardType = KeyboardType.Password, + ), + testTag = "mobileIdAndSmartIdServicesComponentTextField", + removeIconTestTag = "mobileIdAndSmartIdServicesComponentRemoveIconButton", + showIconTestTag = "mobileIdAndSmartIdServicesComponentPasswordVisibleButton", + ) } } } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/ContainerNameView.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/ContainerNameView.kt index 46e32f47a..dfa0a203d 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/ContainerNameView.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/ContainerNameView.kt @@ -57,6 +57,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -96,6 +97,7 @@ fun ContainerNameView( val containerTitleText = stringResource(R.string.container_title) val buttonName = stringResource(id = R.string.button_name) + val panelDescription = stringResource(R.string.panel) Card( modifier = @@ -184,6 +186,7 @@ fun ContainerNameView( modifier = modifier .semantics { + stateDescription = panelDescription testTagsAsResourceId = true }.testTag("containerNameMoreOptionsIcon"), imageVector = ImageVector.vectorResource(R.drawable.ic_more_vert), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/CryptoDataFileItem.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/CryptoDataFileItem.kt index e1e14ef68..2325af840 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/CryptoDataFileItem.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/CryptoDataFileItem.kt @@ -46,12 +46,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -75,8 +75,8 @@ fun CryptoDataFileItem( onClick: (File) -> Unit, onDataFileMoreOptionsActionButtonClick: (File) -> Unit, ) { - val context = LocalContext.current val fileDescription = stringResource(R.string.file) + val panelDescription = stringResource(R.string.panel) val buttonName = stringResource(id = R.string.button_name) @@ -161,6 +161,7 @@ fun CryptoDataFileItem( modifier = modifier .semantics { + stateDescription = panelDescription testTagsAsResourceId = true }.testTag("dataFileItemMoreOptionsIconButton"), imageVector = ImageVector.vectorResource(R.drawable.ic_more_vert), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/DataFileItem.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/DataFileItem.kt index 715c80408..2a8da6881 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/DataFileItem.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/DataFileItem.kt @@ -51,6 +51,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight @@ -76,6 +77,7 @@ fun DataFileItem( val fileDescription = stringResource(R.string.file) val buttonName = stringResource(id = R.string.button_name) + val panelDescription = stringResource(R.string.panel) Column { dataFiles.forEachIndexed { index, dataFile -> @@ -160,6 +162,7 @@ fun DataFileItem( modifier = modifier .semantics { + stateDescription = panelDescription testTagsAsResourceId = true }.testTag("dataFileItemMoreOptionsIconButton"), imageVector = ImageVector.vectorResource(R.drawable.ic_more_vert), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/DynamicText.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/DynamicText.kt index 5bab277d9..1c1260040 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/DynamicText.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/DynamicText.kt @@ -91,7 +91,7 @@ fun DynamicText( modifier .fillMaxWidth() .then(pressIndicator) - .semantics(mergeDescendants = true) { + .semantics { testTagsAsResourceId = true }.testTag("dynamicTextMessage") .let { diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/PrimaryTextField.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/PrimaryTextField.kt index fbbee55ab..a1065e6bd 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/PrimaryTextField.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/PrimaryTextField.kt @@ -40,6 +40,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -54,10 +55,8 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.semantics.LiveRegionMode import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.isTraversalGroup -import androidx.compose.ui.semantics.liveRegion import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.semantics.traversalIndex @@ -104,6 +103,7 @@ fun PrimaryTextField( onDone: (() -> Unit)? = null, testTag: String = "", removeIconTestTag: String = "", + showIconTestTag: String = "", descriptionTestTag: String = "", errorTestTag: String = "", ) { @@ -113,10 +113,14 @@ fun PrimaryTextField( var editingStarted by remember { mutableStateOf(false) } + var passwordVisible by rememberSaveable { mutableStateOf(false) } + val keyboardController = LocalSoftwareKeyboardController.current val clearButtonText = stringResource(R.string.clear_text) val buttonName = stringResource(R.string.button_name) + val showPasswordText = stringResource(R.string.show_password) + val hidePasswordText = stringResource(R.string.hide_password) LaunchedEffect(errorText) { if (errorText.isNotEmpty()) { @@ -124,10 +128,11 @@ fun PrimaryTextField( } } - Column(modifier = + Column( + modifier = modifier.semantics { isTraversalGroup = true - } + }, ) { Row( modifier = Modifier.fillMaxWidth(), @@ -148,7 +153,7 @@ fun PrimaryTextField( contentDescription = if (readDigitByDigit && value.text.isNotEmpty() && value.text.all { it.isDigit() }) { value.text.split("").joinToString(" ") - } else if (isPasswordText) { + } else if (isPasswordText && !passwordVisible) { "" } else { if (description.isNotEmpty()) { @@ -188,6 +193,34 @@ fun PrimaryTextField( trailingIcon = { if (trailingIcon != null) { trailingIcon() + } else if (isPasswordText) { + IconButton( + modifier = + Modifier + .semantics { testTagsAsResourceId = true } + .then( + if (showIconTestTag.isNotEmpty()) { + Modifier.testTag(showIconTestTag) + } else { + Modifier + }, + ), + enabled = enabled, + onClick = { passwordVisible = !passwordVisible }, + ) { + Icon( + imageVector = + ImageVector.vectorResource( + if (passwordVisible) { + R.drawable.ic_visibility + } else { + R.drawable.ic_visibility_off + }, + ), + contentDescription = + if (passwordVisible) hidePasswordText else showPasswordText, + ) + } } else if (!readOnly && !isTalkBackEnabled(context) && value.text.isNotEmpty()) { IconButton(onClick = { onValueChange(TextFieldValue("")) @@ -200,7 +233,11 @@ fun PrimaryTextField( } }, visualTransformation = - if (!isPasswordText) VisualTransformation.None else PasswordVisualTransformation(), + if (!isPasswordText || passwordVisible) { + VisualTransformation.None + } else { + PasswordVisualTransformation() + }, colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = MaterialTheme.colorScheme.primary, @@ -275,7 +312,6 @@ fun PrimaryTextField( .fillMaxWidth() .semantics { contentDescription = errorText - liveRegion = LiveRegionMode.Polite }.testTag(errorTestTag), text = errorText, color = MaterialTheme.colorScheme.error, diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/Recipient.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/Recipient.kt index e2f23af1a..c326b2c7b 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/Recipient.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/Recipient.kt @@ -54,6 +54,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.tooling.preview.Preview import ee.ria.DigiDoc.R @@ -84,6 +85,7 @@ fun Recipient( val context = LocalContext.current val recipientText = stringResource(id = R.string.crypto_recipient_title) val buttonName = stringResource(id = R.string.button_name) + val panelDescription = stringResource(R.string.panel) val isPasswordRecipient = recipient.certType == CertType.PasswordType val nameText = @@ -194,6 +196,7 @@ fun Recipient( modifier = modifier .semantics { + stateDescription = panelDescription testTagsAsResourceId = true }.testTag("recipientItemMoreOptionsIconButton"), imageVector = ImageVector.vectorResource(R.drawable.ic_more_vert), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/SecurePinTextField.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/SecurePinTextField.kt index f566883e0..d875365c0 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/SecurePinTextField.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/SecurePinTextField.kt @@ -36,23 +36,33 @@ import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.geometry.Rect import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.platform.LocalSoftwareKeyboardController +import androidx.compose.ui.platform.LocalTextToolbar +import androidx.compose.ui.platform.TextToolbar +import androidx.compose.ui.platform.TextToolbarStatus import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.LiveRegionMode import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.password import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.input.ImeAction @@ -78,6 +88,7 @@ fun SecurePinTextField( keyboardImeAction: ImeAction = ImeAction.Done, onDone: (() -> Unit)? = null, removeIconTestTag: String = "", + showIconTestTag: String = "", errorTestTag: String = "", ) { val context = LocalContext.current @@ -86,127 +97,171 @@ fun SecurePinTextField( val keyboardController = LocalSoftwareKeyboardController.current + var shown by rememberSaveable { mutableStateOf(false) } + + val displayText = + remember(pin.value, shown) { + if (shown) shownPinText(pin.value) else "*".repeat(pin.value.size) + } + val clearButtonText = stringResource(R.string.clear_text) val buttonName = stringResource(R.string.button_name) + val showPasswordText = stringResource(R.string.show_password) + val hidePasswordText = stringResource(R.string.hide_password) - Column(modifier = modifier) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically, - ) { - OutlinedTextField( - modifier = - Modifier - .focusRequester(focusRequester) - .weight(1f) - .fillMaxWidth() - .semantics { - testTagsAsResourceId = true - }.testTag("pinTextField"), - label = { - Text(text = label) - }, - value = "*".repeat(pin.value.size), - singleLine = true, - onValueChange = { newValue -> - val digitsOnly = newValue.filter { it.isDigit() } - if (digitsOnly.isEmpty()) { - if (pin.value.isNotEmpty()) { - pin.value = pin.value.dropLast(1).toByteArray() - } - } else { - pin.value += digitsOnly.last().code.toByte() - } - pinCodeTextEdited?.value = true - }, - trailingIcon = { - if (!isTalkBackEnabled(context) && pin.value.isNotEmpty()) { - IconButton(onClick = { - pin.value = byteArrayOf() - scope.launch(Main) { - focusRequester.requestFocus() - focusManager.clearFocus() - delay(200) - focusRequester.requestFocus() + CompositionLocalProvider(LocalTextToolbar provides NoFloatingToolbar) { + Column(modifier = modifier) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically, + ) { + OutlinedTextField( + modifier = + Modifier + .focusRequester(focusRequester) + .weight(1f) + .fillMaxWidth() + .semantics { + if (!shown) { + password() + } + testTagsAsResourceId = true + }.testTag("pinTextField"), + label = { + Text(text = label) + }, + value = displayText, + singleLine = true, + onValueChange = { newValue -> + val digits = newValue.filter { it.isDigit() } + val previous = pin.value + val next = + if (shown) { + // Shown text is the PIN itself, so it is authoritative + ByteArray(digits.length) { digits[it].code.toByte() } + } else if (digits.isEmpty()) { + // Masked, so an edit leaving no digit can only be a deletion + if (previous.isNotEmpty()) { + previous.copyOf(previous.size - 1) + } else { + previous + } + } else { + previous + digits.last().code.toByte() } - }) { + pin.value = next + pinCodeTextEdited?.value = true + }, + trailingIcon = { + IconButton(onClick = { shown = !shown }) { Icon( modifier = Modifier .size(iconSizeXXS) .semantics { testTagsAsResourceId = true } - .testTag(removeIconTestTag), - imageVector = ImageVector.vectorResource(R.drawable.ic_icon_remove), - contentDescription = "$clearButtonText $buttonName", + .then( + if (showIconTestTag.isNotEmpty()) { + Modifier.testTag(showIconTestTag) + } else { + Modifier + }, + ), + imageVector = + ImageVector.vectorResource( + if (shown) { + R.drawable.ic_visibility + } else { + R.drawable.ic_visibility_off + }, + ), + contentDescription = if (shown) hidePasswordText else showPasswordText, ) } - } - }, - colors = - OutlinedTextFieldDefaults.colors( - focusedBorderColor = MaterialTheme.colorScheme.primary, - unfocusedBorderColor = MaterialTheme.colorScheme.primary, - ), - keyboardOptions = - KeyboardOptions.Default.copy( - imeAction = keyboardImeAction, - keyboardType = KeyboardType.NumberPassword, - ), - keyboardActions = - KeyboardActions( - onDone = { - keyboardController?.hide() - onDone?.invoke() - }, - ), - isError = isError, - ) + }, + colors = + OutlinedTextFieldDefaults.colors( + focusedBorderColor = MaterialTheme.colorScheme.primary, + unfocusedBorderColor = MaterialTheme.colorScheme.primary, + ), + keyboardOptions = + KeyboardOptions.Default.copy( + imeAction = keyboardImeAction, + keyboardType = KeyboardType.NumberPassword, + ), + keyboardActions = + KeyboardActions( + onDone = { + keyboardController?.hide() + onDone?.invoke() + }, + ), + isError = isError, + ) - if (isTalkBackEnabled(context) && pin.value.isNotEmpty()) { - IconButton(onClick = { - pin.value = byteArrayOf() - pinCodeTextEdited?.value = true - scope.launch(Main) { - focusRequester.requestFocus() - focusManager.clearFocus() - delay(200) - focusRequester.requestFocus() + if (isTalkBackEnabled(context) && pin.value.isNotEmpty()) { + IconButton(onClick = { + pin.value = byteArrayOf() + pinCodeTextEdited?.value = true + scope.launch(Main) { + focusRequester.requestFocus() + focusManager.clearFocus() + delay(200) + focusRequester.requestFocus() + } + }) { + Icon( + modifier = + Modifier + .semantics { testTagsAsResourceId = true } + .then( + if (removeIconTestTag.isNotEmpty()) { + Modifier.testTag(removeIconTestTag) + } else { + Modifier + }, + ), + imageVector = ImageVector.vectorResource(R.drawable.ic_icon_remove), + contentDescription = "$clearButtonText $buttonName", + ) } - }) { - Icon( - modifier = - Modifier - .semantics { testTagsAsResourceId = true } - .then( - if (removeIconTestTag.isNotEmpty()) { - Modifier.testTag(removeIconTestTag) - } else { - Modifier - }, - ), - imageVector = ImageVector.vectorResource(R.drawable.ic_icon_remove), - contentDescription = "$clearButtonText $buttonName", - ) } } - } - if (errorText.isNotEmpty()) { - Text( - modifier = - Modifier - .padding(top = XSPadding) - .padding(bottom = MSPadding) - .fillMaxWidth() - .semantics { - contentDescription = errorText - liveRegion = LiveRegionMode.Polite - }.testTag(errorTestTag), - text = errorText, - color = MaterialTheme.colorScheme.error, - style = MaterialTheme.typography.bodySmall, - ) + if (errorText.isNotEmpty()) { + Text( + modifier = + Modifier + .padding(top = XSPadding) + .padding(bottom = MSPadding) + .fillMaxWidth() + .semantics { + contentDescription = errorText + liveRegion = LiveRegionMode.Polite + }.testTag(errorTestTag), + text = errorText, + color = MaterialTheme.colorScheme.error, + style = MaterialTheme.typography.bodySmall, + ) + } } } } + +private fun shownPinText(pin: ByteArray): String = String(CharArray(pin.size) { pin[it].toInt().toChar() }) + +// Dont show Copy, Paste options +private val NoFloatingToolbar = + object : TextToolbar { + override val status: TextToolbarStatus = TextToolbarStatus.Hidden + + override fun hide() = Unit + + override fun showMenu( + rect: Rect, + onCopyRequested: (() -> Unit)?, + onPasteRequested: (() -> Unit)?, + onCutRequested: (() -> Unit)?, + onSelectAllRequested: (() -> Unit)?, + ) = Unit + } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/TopBar.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/TopBar.kt index 9403c1710..eb8945358 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/TopBar.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/TopBar.kt @@ -58,6 +58,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.heading import androidx.compose.ui.semantics.isTraversalGroup +import androidx.compose.ui.semantics.popup import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.core.net.toUri @@ -94,12 +95,12 @@ fun TopBar( onExtraButtonClick: () -> Unit = {}, showExtraButton: Boolean = false, extraButtonItemCount: Int = 0, + isPanel: Boolean = false, sharedMenuViewModel: SharedMenuViewModel, ) { val context = LocalContext.current var onRightPrimaryButtonClick = onRightPrimaryButtonClick if (onRightPrimaryButtonClick == null) { - @Suppress("AssignedValueIsNeverRead") onRightPrimaryButtonClick = { val browserIntent = Intent( @@ -115,6 +116,8 @@ fun TopBar( val focusManager = LocalFocusManager.current var headingTextLoaded by remember { mutableStateOf(false) } + val panelDescription = stringResource(R.string.panel) + val isEstonianLanguageUsed = remember { mutableStateOf(false) } val isTtsInitialized by sharedMenuViewModel.isTtsInitialized.asFlow().collectAsState(false) @@ -142,7 +145,14 @@ fun TopBar( navigationIcon = { if (showNavigationIcon) { IconButton( - modifier = modifier.testTag("toolBarLeftButton"), + modifier = + modifier + .testTag("toolBarLeftButton") + .semantics { + if (isPanel) { + popup() + } + }, onClick = { // Add debounce to prevent rapid navigation clicks debounceJob?.cancel() @@ -154,7 +164,12 @@ fun TopBar( ) { Icon( imageVector = ImageVector.vectorResource(id = leftIcon), - contentDescription = stringResource(id = leftIconContentDescription), + contentDescription = + if (isPanel) { + "${stringResource(id = leftIconContentDescription)}, $panelDescription" + } else { + stringResource(id = leftIconContentDescription) + }, tint = MaterialTheme.colorScheme.onSurface, modifier = modifier @@ -273,7 +288,11 @@ fun TopBar( ) } IconButton( - modifier = modifier.testTag("toolBarRightSecondaryButton"), + modifier = + modifier + .semantics { + popup() + }.testTag("toolBarRightSecondaryButton"), onClick = { // Add debounce to prevent rapid navigation clicks debounceJob?.cancel() @@ -285,7 +304,12 @@ fun TopBar( ) { Icon( imageVector = ImageVector.vectorResource(id = rightSecondaryIcon), - contentDescription = stringResource(id = rightSecondaryIconContentDescription), + contentDescription = + if (isPanel) { + "${stringResource(id = rightSecondaryIconContentDescription)}, $panelDescription" + } else { + stringResource(id = rightSecondaryIconContentDescription) + }, tint = MaterialTheme.colorScheme.onSurface, modifier = modifier diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/NFCView.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/NFCView.kt index 692ff8788..c94d2d253 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/NFCView.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/NFCView.kt @@ -895,7 +895,7 @@ fun NFCView( } else { ImeAction.Done }, - keyboardType = KeyboardType.Number, + keyboardType = KeyboardType.NumberPassword, ), isError = canNumberTextEdited.value && @@ -950,6 +950,7 @@ fun NFCView( ), errorText = pinCodeErrorText, removeIconTestTag = "nfcPinRemoveButton", + showIconTestTag = "nfcPinShowButton", errorTestTag = "nfcPinError", ) } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SignatureComponent.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SignatureComponent.kt index 4b553213e..73998ad18 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SignatureComponent.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SignatureComponent.kt @@ -54,6 +54,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.text.style.TextOverflow import ee.ria.DigiDoc.R @@ -86,10 +87,10 @@ fun SignatureComponent( onClick: (SignatureInterface) -> Unit, onClickMore: (SignatureInterface) -> Unit, ) { - val context = LocalContext.current val signatureText = stringResource(R.string.signature_details_signer_details_title) val buttonName = stringResource(id = R.string.button_name) + val panelDescription = stringResource(R.string.panel) if (showSignaturesLoadingIndicator) { Box( @@ -266,6 +267,7 @@ fun SignatureComponent( modifier = modifier .semantics { + stateDescription = panelDescription testTagsAsResourceId = true }.testTag("signatureComponentMoreOptionsIconButton"), imageVector = ImageVector.vectorResource(R.drawable.ic_more_vert), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SigningNavigation.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SigningNavigation.kt index 0c239671f..76296ddc4 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SigningNavigation.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/SigningNavigation.kt @@ -115,7 +115,6 @@ import ee.ria.DigiDoc.ui.component.signing.bottombar.SigningBottomBar import ee.ria.DigiDoc.ui.component.signing.bottomsheet.ContainerBottomSheet import ee.ria.DigiDoc.ui.component.signing.bottomsheet.DataFileBottomSheet import ee.ria.DigiDoc.ui.component.signing.bottomsheet.SignatureBottomSheet -import ee.ria.DigiDoc.ui.component.signing.bottomsheet.SignedContainerBottomSheet import ee.ria.DigiDoc.ui.theme.Dimensions.MPadding import ee.ria.DigiDoc.ui.theme.Dimensions.SPadding import ee.ria.DigiDoc.ui.theme.Dimensions.XSPadding @@ -297,7 +296,6 @@ fun SigningNavigation( val nestedFile = rememberSaveable { mutableStateOf(null) } val showContainerBottomSheet = rememberSaveable { mutableStateOf(false) } - val showSignedContainerBottomSheet = rememberSaveable { mutableStateOf(false) } val showDataFileBottomSheet = rememberSaveable { mutableStateOf(false) } val showSignatureBottomSheet = rememberSaveable { mutableStateOf(false) } @@ -1244,21 +1242,6 @@ fun SigningNavigation( onSignatureRemove = { actionSignature = it }, ) - SignedContainerBottomSheet( - modifier = modifier, - showSheet = showSignedContainerBottomSheet, - signedContainer = signedContainer, - isNestedContainer = isNestedContainer, - isXadesContainer = isXadesContainer, - isCadesContainer = isCadesContainer, - signingViewModel = signingViewModel, - navController = navController, - onEncryptClick = onEncryptActionClick, - onExtendSignatureClick = { - // TODO: Implement extend signature click - }, - ) - if (showLoadingScreen.value) { LoadingScreen(modifier = modifier) } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/certificate/SignatureDataItem.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/certificate/SignatureDataItem.kt index 0dbb8666f..a963ad8b5 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/certificate/SignatureDataItem.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/certificate/SignatureDataItem.kt @@ -110,9 +110,9 @@ fun SignatureDataItem( .indication(interactionSource, LocalIndication.current) .semantics(mergeDescendants = true) { if (isLink) { - this.contentDescription = "$contentDescriptionText, $linkName" + this.contentDescription = "$contentDescriptionText $linkName" } else if (isWithCertificate) { - this.contentDescription = "$contentDescriptionText, $buttonName" + this.contentDescription = "$contentDescriptionText $buttonName" } else { this.contentDescription = contentDescriptionText } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/certificate/SignerDetailItem.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/certificate/SignerDetailItem.kt index 59acdaa1b..b35085d70 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/certificate/SignerDetailItem.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/certificate/SignerDetailItem.kt @@ -56,17 +56,42 @@ data class SignerDetailItem( ocspIssuerName: String?, tsSubjectName: String?, ocspSubjectName: String?, - ): List = - listOf( + ): List { + val signersCertificate = + if (signature.isDigitalSeal) { + signature.signedBy + } else if (isTimestamp) { + NameUtil.formatName(signature.signedBy).uppercase() + } else { + NameUtil.formatName(signature.signedBy) + } + val signatureMethod = signature.signatureMethod + val containerFormat = + (sharedContainerViewModel.currentContainer() as? SignedContainer)?.containerMimetype() ?: "" + val signatureFormat = signature.profile + val signedFileCount = + sharedContainerViewModel.signedContainer.value + ?.rawContainer() + ?.dataFiles() + ?.size + .toString() + val timestampTime = DateUtil.getFormattedDateTime(signature.timeStampTime, false) + val timestampTimeUtc = DateUtil.getFormattedDateTime(signature.timeStampTime, true) + val hashValue = signature.messageImprint.hexString() + val ocspProducedAt = DateUtil.getFormattedDateTime(signature.ocspProducedAt, false) + val ocspProducedAtUtc = DateUtil.getFormattedDateTime(signature.ocspProducedAt, true) + val signersMobileTime = DateUtil.getFormattedDateTime(signature.claimedSigningTime, true) + + return listOf( SignerDetailItem( icon = 0, label = R.string.signer_certificate_issuer_label, value = signerIssuerName, contentDescription = - if (value != null) { + if (signerIssuerName != null) { "${stringResource( id = R.string.signer_certificate_issuer_label, - )} $value" + )} $signerIssuerName" } else { "" }, @@ -74,20 +99,13 @@ data class SignerDetailItem( ), SignerDetailItem( label = R.string.signers_certificate_label, - value = - if (signature.isDigitalSeal) { - signature.signedBy - } else if (isTimestamp) { - NameUtil.formatName(signature.signedBy).uppercase() - } else { - NameUtil.formatName(signature.signedBy) - }, + value = signersCertificate, certificate = signature.signingCertificateDer.x509Certificate(), contentDescription = - if (value != null) { + if (signersCertificate != null) { "${stringResource( id = R.string.signers_certificate_label, - )}, $value" + )}, $signersCertificate" } else { "" }, @@ -98,12 +116,12 @@ data class SignerDetailItem( icon = R.drawable.ic_m3_open_in_new_48dp_wght400, isLink = true, label = R.string.signature_method_label, - value = signature.signatureMethod, + value = signatureMethod, contentDescription = - if (value != null) { + if (signatureMethod != null) { "${stringResource( id = R.string.signature_method_label, - )}, $value" + )}, link $signatureMethod" } else { "" }, @@ -112,12 +130,12 @@ data class SignerDetailItem( SignerDetailItem( icon = 0, label = R.string.container_format_label, - value = (sharedContainerViewModel.currentContainer() as? SignedContainer)?.containerMimetype() ?: "", + value = containerFormat, contentDescription = - if (value != null) { + if (containerFormat != null) { "${stringResource( id = R.string.container_format_label, - )}, $value" + )}, $containerFormat" } else { "" }, @@ -126,12 +144,12 @@ data class SignerDetailItem( SignerDetailItem( icon = 0, label = R.string.signature_format_label, - value = signature.profile, + value = signatureFormat, contentDescription = - if (value != null) { + if (signatureFormat != null) { "${stringResource( id = R.string.signature_format_label, - )}, $value" + )}, $signatureFormat" } else { "" }, @@ -140,17 +158,12 @@ data class SignerDetailItem( SignerDetailItem( icon = 0, label = R.string.signed_file_count_label, - value = - sharedContainerViewModel.signedContainer.value - ?.rawContainer() - ?.dataFiles() - ?.size - .toString(), + value = signedFileCount, contentDescription = - if (value != null) { + if (signedFileCount != null) { "${stringResource( id = R.string.signed_file_count_label, - )}, $value" + )}, $signedFileCount" } else { "" }, @@ -159,12 +172,12 @@ data class SignerDetailItem( SignerDetailItem( icon = 0, label = R.string.signature_timestamp_label, - value = DateUtil.getFormattedDateTime(signature.timeStampTime, false), + value = timestampTime, contentDescription = - if (value != null) { + if (timestampTime != null) { "${stringResource( id = R.string.signature_timestamp_label, - )}, $value" + )}, $timestampTime" } else { "" }, @@ -173,12 +186,12 @@ data class SignerDetailItem( SignerDetailItem( icon = 0, label = R.string.signature_timestamp_utc_label, - value = DateUtil.getFormattedDateTime(signature.timeStampTime, true), + value = timestampTimeUtc, contentDescription = - if (value != null) { + if (timestampTimeUtc != null) { "${stringResource( id = R.string.signature_timestamp_utc_label, - )}, $value" + )}, $timestampTimeUtc" } else { "" }, @@ -187,12 +200,12 @@ data class SignerDetailItem( SignerDetailItem( icon = 0, label = R.string.hash_value_of_signature_label, - value = signature.messageImprint.hexString(), + value = hashValue, contentDescription = - if (value != null) { + if (hashValue != null) { "${stringResource( id = R.string.hash_value_of_signature_label, - )}, $value" + )}, $hashValue" } else { "" }, @@ -203,10 +216,10 @@ data class SignerDetailItem( label = R.string.ts_certificate_issuer_label, value = tsIssuerName, contentDescription = - if (value != null) { + if (tsIssuerName != null) { "${stringResource( id = R.string.ts_certificate_issuer_label, - )}, $value" + )}, $tsIssuerName" } else { "" }, @@ -217,10 +230,10 @@ data class SignerDetailItem( value = tsSubjectName, certificate = signature.timeStampCertificateDer.x509Certificate(), contentDescription = - if (value != null) { + if (tsSubjectName != null) { "${stringResource( id = R.string.ts_certificate_label, - )}, $value" + )}, $tsSubjectName" } else { "" }, @@ -231,10 +244,10 @@ data class SignerDetailItem( label = R.string.ocsp_certificate_issuer_label, value = ocspIssuerName, contentDescription = - if (value != null) { + if (ocspIssuerName != null) { "${stringResource( id = R.string.ocsp_certificate_issuer_label, - )}, $value" + )}, $ocspIssuerName" } else { "" }, @@ -245,10 +258,10 @@ data class SignerDetailItem( value = ocspSubjectName, certificate = signature.ocspCertificateDer.x509Certificate(), contentDescription = - if (value != null) { + if (ocspSubjectName != null) { "${stringResource( id = R.string.ocsp_certificate_label, - )}, $value" + )}, $ocspSubjectName" } else { "" }, @@ -257,12 +270,12 @@ data class SignerDetailItem( SignerDetailItem( icon = 0, label = R.string.ocsp_time_label, - value = DateUtil.getFormattedDateTime(signature.ocspProducedAt, false), + value = ocspProducedAt, contentDescription = - if (value != null) { + if (ocspProducedAt != null) { "${stringResource( id = R.string.ocsp_time_label, - )}, $value" + )}, $ocspProducedAt" } else { "" }, @@ -271,12 +284,12 @@ data class SignerDetailItem( SignerDetailItem( icon = 0, label = R.string.ocsp_time_utc_label, - value = DateUtil.getFormattedDateTime(signature.ocspProducedAt, true), + value = ocspProducedAtUtc, contentDescription = - if (value != null) { + if (ocspProducedAtUtc != null) { "${stringResource( id = R.string.ocsp_time_utc_label, - )}, $value" + )}, $ocspProducedAtUtc" } else { "" }, @@ -285,16 +298,17 @@ data class SignerDetailItem( SignerDetailItem( icon = 0, label = R.string.signers_mobile_time_label, - value = DateUtil.getFormattedDateTime(signature.claimedSigningTime, true), + value = signersMobileTime, contentDescription = - if (value != null) { + if (signersMobileTime != null) { "${stringResource( id = R.string.signers_mobile_time_label, - )}, $value" + )}, $signersMobileTime" } else { "" }, testTag = "signatureDetailSignersMobileTimeUTC", ), ) + } } diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 67b3467ec..8e6a73039 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -408,6 +408,7 @@ Kontrollkoodi laadimine Konteineri failide laadimine Konteineri failid laaditud + Paneel Allkiri @@ -489,7 +490,7 @@ Seade väärtus muudetud Seade väärtuse muutmine katkestatud Sulge menüü - Peida parooli + Peida parool Näita parooli Hele diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1b0f26212..8166581fa 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -408,6 +408,7 @@ Control code loading Container files loading Container files loaded + Panel Signature diff --git a/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainer.kt b/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainer.kt index e0176e1c1..807cd3dbf 100644 --- a/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainer.kt +++ b/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/CryptoContainer.kt @@ -55,7 +55,6 @@ import ee.ria.cdoc.ILogger import ee.ria.cdoc.Lock import ee.ria.cdoc.NetworkBackend import ee.ria.cdoc.Recipient -import ee.ria.cdoc.Recipient.parseLabel import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.withContext import org.apache.commons.io.FilenameUtils diff --git a/utils-lib/src/main/kotlin/ee/ria/DigiDoc/utilsLib/extensions/FileExtensions.kt b/utils-lib/src/main/kotlin/ee/ria/DigiDoc/utilsLib/extensions/FileExtensions.kt index 414f7c03c..a260c97a1 100644 --- a/utils-lib/src/main/kotlin/ee/ria/DigiDoc/utilsLib/extensions/FileExtensions.kt +++ b/utils-lib/src/main/kotlin/ee/ria/DigiDoc/utilsLib/extensions/FileExtensions.kt @@ -22,7 +22,6 @@ package ee.ria.DigiDoc.utilsLib.extensions import android.content.Context -import android.util.Log import android.webkit.MimeTypeMap import com.tom_roush.pdfbox.android.PDFBoxResourceLoader import com.tom_roush.pdfbox.pdmodel.PDDocument @@ -171,4 +170,4 @@ private fun checkIsZipFile(file: File) { .Builder() .setFile(file) .get() -} \ No newline at end of file +}