diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/ConstantTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/ConstantTest.kt index c87250030..dfefc7d65 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/ConstantTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/ConstantTest.kt @@ -50,8 +50,6 @@ class ConstantTest { assertEquals("signer_detail_route", Constant.Routes.SIGNER_DETAIL_SCREEN) assertEquals("certificate_detail_route", Constant.Routes.CERTIFICATE_DETAIL_SCREEN) assertEquals("recipient_detail_route", Constant.Routes.RECIPIENT_DETAIL_SCREEN) - assertEquals("recent_documents_route", Constant.Routes.RECENT_DOCUMENTS_SCREEN) - assertEquals("recent_documents_from_encrypt_route", Constant.Routes.RECENT_DOCUMENTS_FROM_ENCRYPT_SCREEN) assertEquals("settings_route", Constant.Routes.SETTINGS_SCREEN) assertEquals("settings_language_chooser_route", Constant.Routes.SETTINGS_LANGUAGE_CHOOSER_SCREEN) assertEquals("settings_theme_chooser_route", Constant.Routes.SETTINGS_THEME_CHOOSER_SCREEN) diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/RouteTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/RouteTest.kt index 8eeb954ac..5a7cc4e65 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/RouteTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/RouteTest.kt @@ -40,9 +40,6 @@ import ee.ria.DigiDoc.utils.Constant.Routes.MYEID_IDENTIFICATION_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.MYEID_PIN_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.MYEID_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.PROXY_SERVICES_SCREEN -import ee.ria.DigiDoc.utils.Constant.Routes.RECENT_DOCUMENTS_FROM_ENCRYPT_SCREEN -import ee.ria.DigiDoc.utils.Constant.Routes.RECENT_DOCUMENTS_SCREEN -import ee.ria.DigiDoc.utils.Constant.Routes.RECENT_DOCUMENTS_SCREEN_FROM_SIGNING_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.RECIPIENT_DETAIL_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.ROOT_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.SETTINGS_LANGUAGE_CHOOSER_SCREEN @@ -79,9 +76,6 @@ class RouteTest { assertEquals(SIGNER_DETAIL_SCREEN, Route.SignerDetail.route) assertEquals(CERTIFICATE_DETAIL_SCREEN, Route.CertificateDetail.route) assertEquals(RECIPIENT_DETAIL_SCREEN, Route.RecipientDetail.route) - assertEquals(RECENT_DOCUMENTS_SCREEN, Route.RecentDocuments.route) - assertEquals(RECENT_DOCUMENTS_SCREEN_FROM_SIGNING_SCREEN, Route.RecentDocumentsFromSigning.route) - assertEquals(RECENT_DOCUMENTS_FROM_ENCRYPT_SCREEN, Route.RecentDocumentsFromEncrypt.route) assertEquals(SETTINGS_SCREEN, Route.Settings.route) assertEquals(SETTINGS_LANGUAGE_CHOOSER_SCREEN, Route.SettingsLanguageChooser.route) assertEquals(SETTINGS_THEME_CHOOSER_SCREEN, Route.SettingsThemeChooser.route) diff --git a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt b/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt deleted file mode 100644 index 991d1cf77..000000000 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName") - -package ee.ria.DigiDoc.viewmodel - -import android.content.ContentResolver -import android.content.Context -import androidx.arch.core.executor.testing.InstantTaskExecutorRule -import androidx.test.platform.app.InstrumentationRegistry -import com.google.gson.Gson -import ee.ria.DigiDoc.common.Constant.ASICE_MIMETYPE -import ee.ria.DigiDoc.common.Constant.DEFAULT_MIME_TYPE -import ee.ria.DigiDoc.common.testfiles.asset.AssetFile -import ee.ria.DigiDoc.common.testfiles.file.TestFileUtil.Companion.createZipWithTextFile -import ee.ria.DigiDoc.configuration.ConfigurationProperty -import ee.ria.DigiDoc.configuration.ConfigurationSignatureVerifierImpl -import ee.ria.DigiDoc.configuration.loader.ConfigurationLoader -import ee.ria.DigiDoc.configuration.loader.ConfigurationLoaderImpl -import ee.ria.DigiDoc.configuration.properties.ConfigurationPropertiesImpl -import ee.ria.DigiDoc.configuration.repository.CentralConfigurationRepositoryImpl -import ee.ria.DigiDoc.configuration.repository.ConfigurationRepository -import ee.ria.DigiDoc.configuration.repository.ConfigurationRepositoryImpl -import ee.ria.DigiDoc.configuration.service.CentralConfigurationServiceImpl -import ee.ria.DigiDoc.cryptolib.CDOC2Settings -import ee.ria.DigiDoc.domain.repository.siva.SivaRepository -import ee.ria.DigiDoc.libdigidoclib.SignedContainer -import ee.ria.DigiDoc.libdigidoclib.init.Initialization -import ee.ria.DigiDoc.libdigidoclib.init.LibdigidocLibraryLoader -import ee.ria.DigiDoc.utilsLib.container.ContainerUtil -import ee.ria.DigiDoc.utilsLib.mimetype.MimeTypeCache -import ee.ria.DigiDoc.utilsLib.mimetype.MimeTypeResolver -import ee.ria.DigiDoc.utilsLib.mimetype.MimeTypeResolverImpl -import ee.ria.DigiDoc.viewmodel.shared.SharedContainerViewModel -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.test.runTest -import org.junit.Assert.assertEquals -import org.junit.Assert.assertFalse -import org.junit.Assert.assertTrue -import org.junit.Assert.fail -import org.junit.Before -import org.junit.BeforeClass -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.mock -import org.mockito.Mockito.`when` -import org.mockito.MockitoAnnotations -import org.mockito.junit.MockitoJUnitRunner -import org.mockito.kotlin.anyOrNull -import org.mockito.kotlin.verify -import java.io.File - -@RunWith(MockitoJUnitRunner::class) -class RecentDocumentsViewModelTest { - @get:Rule - val instantExecutorRule = InstantTaskExecutorRule() - - @Mock - private lateinit var sivaRepository: SivaRepository - - @Mock - private lateinit var mimeTypeCache: MimeTypeCache - - private lateinit var mimeTypeResolver: MimeTypeResolver - - private lateinit var cdoc2Settings: CDOC2Settings - - private lateinit var container: File - private lateinit var signedContainer: SignedContainer - - private lateinit var sharedContainerViewModel: SharedContainerViewModel - - companion object { - private var context: Context = InstrumentationRegistry.getInstrumentation().targetContext - private lateinit var configurationLoader: ConfigurationLoader - private lateinit var configurationRepository: ConfigurationRepository - - @JvmStatic - @BeforeClass - fun setupOnce() { - runBlocking { - try { - configurationLoader = - ConfigurationLoaderImpl( - Gson(), - CentralConfigurationRepositoryImpl( - CentralConfigurationServiceImpl(context, ConfigurationProperty()), - ), - ConfigurationProperty(), - ConfigurationPropertiesImpl(), - ConfigurationSignatureVerifierImpl(), - ) - configurationRepository = ConfigurationRepositoryImpl(context, configurationLoader) - LibdigidocLibraryLoader().init(context) - Initialization(configurationRepository).init(context) - } catch (_: Exception) { - } - } - } - } - - private lateinit var viewModel: RecentDocumentsViewModel - - @Before - fun setup() { - MockitoAnnotations.openMocks(this) - mimeTypeResolver = MimeTypeResolverImpl(mimeTypeCache) - cdoc2Settings = CDOC2Settings(context, configurationRepository) - viewModel = RecentDocumentsViewModel(context, sivaRepository, mimeTypeResolver, cdoc2Settings) - - container = - AssetFile.getResourceFileAsFile( - context, - "example.asice", - ee.ria.DigiDoc.common.R.raw.example, - ) - - signedContainer = - runBlocking { - SignedContainer.openOrCreate(context, container, listOf(container), true) - } - - sharedContainerViewModel = - SharedContainerViewModel( - context, - mock(ContentResolver::class.java), - ) - } - - @Test - fun recentDocumentsViewModel_openSignatureDocument_success() = - runBlocking { - val container = - AssetFile.getResourceFileAsFile( - context, - "example.asice", - ee.ria.DigiDoc.common.R.raw.example, - ) - - `when`(sivaRepository.isTimestampedContainer(anyOrNull())).thenReturn(false) - - val result = viewModel.openSignatureDocument(container, true) - - assertEquals(result.getDataFiles().size, 1) - } - - @Test - fun recentDocumentsViewModel_openSignatureDocument_successWithTimestampedContainer(): Unit = - runBlocking { - val tempFile = File.createTempFile("testFile", "asics", ContainerUtil.signatureContainersDir(context)) - tempFile.writeText("test content") - tempFile.deleteOnExit() - - `when`(sivaRepository.isTimestampedContainer(anyOrNull())).thenReturn(true) - - viewModel.openSignatureDocument(tempFile, true) - - verify(sivaRepository).getTimestampedContainer(anyOrNull(), anyOrNull(), anyOrNull()) - } - - @Test - fun recentDocumentsViewModel_getRecentDocumentList_success() { - ContainerUtil.signatureContainersDir(context).delete() - val tempFile = File.createTempFile("testFile", ".asice", ContainerUtil.signatureContainersDir(context)) - tempFile.deleteOnExit() - val list = viewModel.getRecentDocumentList() - - assertTrue(list.isNotEmpty()) - } - - @Test - fun recentDocumentsViewModel_handleSendToSigningViewWithSiva_sendToSigningViewWithSivaFalseWithOtherMimetype() = - runTest { - val mimeType = "some/other-mime" - val mockFile = createZipWithTextFile("application/zip") - - viewModel.handleDocument(mockFile, mimeType, true, sharedContainerViewModel) - - val sendToSiva = viewModel.sendToSigningViewWithSiva.value - if (sendToSiva != null) { - assertFalse(sendToSiva) - } else { - fail("sendToSigningViewWithSiva cannot be null") - } - } - - @Test - fun recentDocumentsViewModel_handleSendToSigningViewWithSiva_sendToSigningViewWithSivaTrue() = - runTest { - viewModel.handleSendToSigningViewWithSiva(true) - - val sendToSiva = viewModel.sendToSigningViewWithSiva.value - - if (sendToSiva != null) { - assertTrue(sendToSiva) - } else { - fail("sendToSigningViewWithSiva cannot be null") - } - } - - @Test - fun recentDocumentsViewModel_handleSendToSigningViewWithSiva_sendToSigningViewWithSivaFalse() = - runTest { - viewModel.handleSendToSigningViewWithSiva(false) - - val sendToSiva = viewModel.sendToSigningViewWithSiva.value - - if (sendToSiva != null) { - assertFalse(sendToSiva) - } else { - fail("sendToSigningViewWithSiva cannot be null") - } - } - - @Test - fun recentDocumentsViewModel_getMimetype_success() = - runTest { - `when`(mimeTypeCache.getMimeType(anyOrNull())).thenReturn(ASICE_MIMETYPE) - - val mimetype = viewModel.getMimetype(container) - - assertEquals(ASICE_MIMETYPE, mimetype) - } - - @Test - fun recentDocumentsViewModel_getMimetype_defaultMimeType() = - runTest { - `when`(mimeTypeCache.getMimeType(anyOrNull())).thenReturn("") - - val mimetype = viewModel.getMimetype(container) - - assertEquals(DEFAULT_MIME_TYPE, mimetype) - } - - @Test - fun recentDocumentsViewModel_getMimetype_successGettingFileMimetype() = - runTest { - `when`(mimeTypeCache.getMimeType(anyOrNull())).thenReturn("text/plain") - - val file = File.createTempFile("temp", ".txt") - - val mimetype = viewModel.getMimetype(file) - - assertEquals("text/plain", mimetype) - } -} diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt b/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt index 78ea19658..634e4ba20 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt @@ -52,6 +52,7 @@ import ee.ria.DigiDoc.utils.locale.LocaleUtilImpl import ee.ria.DigiDoc.utils.secure.SecureUtil import ee.ria.DigiDoc.utilsLib.R.string.main_diagnostics_logging_key import ee.ria.DigiDoc.utilsLib.R.string.main_diagnostics_logging_running_key +import ee.ria.DigiDoc.utilsLib.container.ContainerUtil import ee.ria.DigiDoc.utilsLib.file.FileUtil.getExternalFileUris import ee.ria.DigiDoc.utilsLib.file.FileUtil.getLogsDirectory import ee.ria.DigiDoc.utilsLib.locale.LocaleUtil.getLocale @@ -167,6 +168,11 @@ class MainActivity : fileTypeSetup.initializeApplicationFileTypesAssociation(componentClassName) librarySetup.setupLibraries(applicationContext, isLoggingEnabled) + if (savedInstanceState == null) { + ContainerUtil.removeSignatureContainersDir(applicationContext) + ContainerUtil.removeCryptoContainersDir(applicationContext) + } + isAppReady = true setContent { diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt b/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt index bdeb3cfcc..217d47a66 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt @@ -50,7 +50,6 @@ import ee.ria.DigiDoc.fragment.MyEidFragment import ee.ria.DigiDoc.fragment.MyEidIdentificationFragment import ee.ria.DigiDoc.fragment.MyEidPinFragment import ee.ria.DigiDoc.fragment.ProxyServicesSettingsFragment -import ee.ria.DigiDoc.fragment.RecentDocumentsFragment import ee.ria.DigiDoc.fragment.RootFragment import ee.ria.DigiDoc.fragment.SignatureInputFragment import ee.ria.DigiDoc.fragment.SignatureMethodFragment @@ -186,6 +185,16 @@ fun RIADigiDocAppScreen( sharedRecipientViewModel = sharedRecipientViewModel, ) } + composable(route = Route.Encrypted.route) { + EncryptFragment( + modifier = Modifier.safeDrawingPadding(), + navController = navController, + sharedMenuViewModel = sharedMenuViewModel, + sharedContainerViewModel = sharedContainerViewModel, + sharedRecipientViewModel = sharedRecipientViewModel, + withEncryption = true, + ) + } composable(route = Route.DecryptScreen.route) { DecryptFragment( modifier = Modifier.safeDrawingPadding(), @@ -217,33 +226,6 @@ fun RIADigiDocAppScreen( sharedSettingsViewModel = sharedSettingsViewModel, ) } - composable(route = Route.RecentDocuments.route) { - RecentDocumentsFragment( - modifier = Modifier.safeDrawingPadding(), - navController = navController, - sharedMenuViewModel = sharedMenuViewModel, - sharedContainerViewModel = sharedContainerViewModel, - fileOpeningMethod = FileOpeningMethod.ALL, - ) - } - composable(route = Route.RecentDocumentsFromSigning.route) { - RecentDocumentsFragment( - modifier = Modifier.safeDrawingPadding(), - navController = navController, - sharedMenuViewModel = sharedMenuViewModel, - sharedContainerViewModel = sharedContainerViewModel, - fileOpeningMethod = FileOpeningMethod.SIGNING, - ) - } - composable(route = Route.RecentDocumentsFromEncrypt.route) { - RecentDocumentsFragment( - modifier = Modifier.safeDrawingPadding(), - navController = navController, - sharedMenuViewModel = sharedMenuViewModel, - sharedContainerViewModel = sharedContainerViewModel, - fileOpeningMethod = FileOpeningMethod.CRYPTO, - ) - } composable(route = Route.Settings.route) { AdvancedSettingsFragment( modifier = Modifier.safeDrawingPadding(), diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/EncryptFragment.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/EncryptFragment.kt index a1aeced1e..c2283509f 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/EncryptFragment.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/EncryptFragment.kt @@ -50,6 +50,7 @@ fun EncryptFragment( sharedMenuViewModel: SharedMenuViewModel, sharedContainerViewModel: SharedContainerViewModel, sharedRecipientViewModel: SharedRecipientViewModel, + withEncryption: Boolean = false, ) { Surface( modifier = @@ -67,6 +68,7 @@ fun EncryptFragment( sharedMenuViewModel = sharedMenuViewModel, sharedContainerViewModel = sharedContainerViewModel, sharedRecipientViewModel = sharedRecipientViewModel, + withEncryption = withEncryption, ) } } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/RecentDocumentsFragment.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/RecentDocumentsFragment.kt deleted file mode 100644 index 0a94e4202..000000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/RecentDocumentsFragment.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName", "FunctionName") - -package ee.ria.DigiDoc.fragment - -import android.content.res.Configuration -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.testTagsAsResourceId -import androidx.compose.ui.tooling.preview.Preview -import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController -import ee.ria.DigiDoc.common.model.FileOpeningMethod -import ee.ria.DigiDoc.fragment.screen.RecentDocumentsScreen -import ee.ria.DigiDoc.ui.theme.RIADigiDocTheme -import ee.ria.DigiDoc.viewmodel.shared.SharedContainerViewModel -import ee.ria.DigiDoc.viewmodel.shared.SharedMenuViewModel - -@OptIn(ExperimentalComposeUiApi::class) -@Composable -fun RecentDocumentsFragment( - modifier: Modifier = Modifier, - navController: NavHostController, - sharedMenuViewModel: SharedMenuViewModel, - sharedContainerViewModel: SharedContainerViewModel, - fileOpeningMethod: FileOpeningMethod, -) { - Surface( - modifier = - modifier - .fillMaxSize() - .background(MaterialTheme.colorScheme.background) - .semantics { - testTagsAsResourceId = true - }.testTag("recentDocumentsFragment"), - color = MaterialTheme.colorScheme.background, - ) { - RecentDocumentsScreen( - modifier = modifier, - navController = navController, - sharedMenuViewModel = sharedMenuViewModel, - sharedContainerViewModel = sharedContainerViewModel, - fileOpeningMethod = fileOpeningMethod, - ) - } -} - -@Preview(showBackground = true) -@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -fun RecentDocumentsFragmentPreview() { - RIADigiDocTheme { - RecentDocumentsFragment( - navController = rememberNavController(), - sharedMenuViewModel = hiltViewModel(), - sharedContainerViewModel = hiltViewModel(), - fileOpeningMethod = FileOpeningMethod.ALL, - ) - } -} diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/AdvancedSettingsScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/AdvancedSettingsScreen.kt index 3c808fda4..7a090b720 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/AdvancedSettingsScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/AdvancedSettingsScreen.kt @@ -42,9 +42,9 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptRecipientScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptRecipientScreen.kt index cc4983633..994fe0742 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptRecipientScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptRecipientScreen.kt @@ -214,7 +214,7 @@ fun EncryptRecipientScreen( encryptRecipientViewModel.handleIsContainerEncrypted(false) containerEncryptedSuccess.value = false - navController.navigate(Route.Encrypt.route) { + navController.navigate(Route.Encrypted.route) { popUpTo(Route.Home.route) { inclusive = false } @@ -588,7 +588,7 @@ fun EncryptRecipientScreen( ) } - if (showLoading.value == true) { + if (showLoading.value) { LoadingScreen(modifier = modifier) } diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptScreen.kt index bf3013029..bdf9c519a 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptScreen.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/EncryptScreen.kt @@ -41,6 +41,7 @@ fun EncryptScreen( sharedMenuViewModel: SharedMenuViewModel, sharedContainerViewModel: SharedContainerViewModel, sharedRecipientViewModel: SharedRecipientViewModel, + withEncryption: Boolean = false, ) { EncryptNavigation( modifier = modifier, @@ -48,6 +49,7 @@ fun EncryptScreen( sharedMenuViewModel = sharedMenuViewModel, sharedContainerViewModel = sharedContainerViewModel, sharedRecipientViewModel = sharedRecipientViewModel, + withEncryption = withEncryption, ) } 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..c2f9bc597 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 @@ -71,7 +71,6 @@ import ee.ria.DigiDoc.R import ee.ria.DigiDoc.ui.component.home.ActionButton import ee.ria.DigiDoc.ui.component.main.CrashDialog import ee.ria.DigiDoc.ui.component.menu.MainMenuBottomSheet -import ee.ria.DigiDoc.ui.component.menu.OpenMenuBottomSheet import ee.ria.DigiDoc.ui.component.menu.SettingsMenuBottomSheet import ee.ria.DigiDoc.ui.component.shared.StatusSnackbarHost import ee.ria.DigiDoc.ui.component.shared.TopBar @@ -100,9 +99,6 @@ fun HomeScreen( val isMainMenuBottomSheetVisible = rememberSaveable { mutableStateOf(false) } val isSettingsMenuBottomSheetVisible = rememberSaveable { mutableStateOf(false) } - val isOpenMenuBottomSheetVisible = rememberSaveable { mutableStateOf(false) } - val isOpenMenuBottomFromSigning = rememberSaveable { mutableStateOf(false) } - val isOpenMenuBottomFromEncrypt = rememberSaveable { mutableStateOf(false) } val openMenuAddFileNavigateTo = remember { mutableStateOf(Route.SigningFileChoosing.route) } @@ -188,27 +184,6 @@ fun HomeScreen( isBottomSheetVisible = isSettingsMenuBottomSheetVisible, ) - OpenMenuBottomSheet( - isBottomSheetVisible = isOpenMenuBottomSheetVisible, - firstButtonClick = { - isOpenMenuBottomSheetVisible.value = false - navController.navigate( - openMenuAddFileNavigateTo.value, - ) - }, - secondButtonClick = { - isOpenMenuBottomSheetVisible.value = false - val recentDocumentsRoute = - when { - isOpenMenuBottomFromSigning.value -> Route.RecentDocumentsFromSigning.route - isOpenMenuBottomFromEncrypt.value -> Route.RecentDocumentsFromEncrypt.route - else -> Route.RecentDocuments.route - } - - navController.navigate(recentDocumentsRoute) - }, - ) - Surface( color = MaterialTheme.colorScheme.surface, modifier = @@ -296,9 +271,9 @@ fun HomeScreen( stringResource(id = R.string.main_home_open_document_description), onClickItem = { openMenuAddFileNavigateTo.value = Route.AllFilesChoosing.route - isOpenMenuBottomSheetVisible.value = true - isOpenMenuBottomFromSigning.value = false - isOpenMenuBottomFromEncrypt.value = false + navController.navigate( + openMenuAddFileNavigateTo.value, + ) }, testTag = "homeOpenDocumentButton", ) @@ -312,9 +287,9 @@ fun HomeScreen( stringResource(id = R.string.main_home_signature_description), onClickItem = { openMenuAddFileNavigateTo.value = Route.SigningFileChoosing.route - isOpenMenuBottomSheetVisible.value = true - isOpenMenuBottomFromSigning.value = true - isOpenMenuBottomFromEncrypt.value = false + navController.navigate( + openMenuAddFileNavigateTo.value, + ) }, testTag = "homeSignatureButton", ) @@ -328,9 +303,9 @@ fun HomeScreen( stringResource(id = R.string.main_home_crypto_description), onClickItem = { openMenuAddFileNavigateTo.value = Route.CryptoFileChoosing.route - isOpenMenuBottomSheetVisible.value = true - isOpenMenuBottomFromSigning.value = false - isOpenMenuBottomFromEncrypt.value = true + navController.navigate( + openMenuAddFileNavigateTo.value, + ) }, testTag = "homeCryptoButton", ) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/RecentDocumentsScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/RecentDocumentsScreen.kt deleted file mode 100644 index ae74c9f54..000000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/RecentDocumentsScreen.kt +++ /dev/null @@ -1,581 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName", "FunctionName") - -package ee.ria.DigiDoc.fragment.screen - -import android.content.res.Configuration -import androidx.compose.foundation.focusable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items -import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.SearchBar -import androidx.compose.material3.SearchBarDefaults -import androidx.compose.material3.SearchBarDefaults.inputFieldColors -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -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.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.focusProperties -import androidx.compose.ui.focus.focusTarget -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.heading -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.testTagsAsResourceId -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel -import androidx.lifecycle.asFlow -import androidx.navigation.NavHostController -import androidx.navigation.compose.rememberNavController -import ee.ria.DigiDoc.R -import ee.ria.DigiDoc.common.Constant.DEFAULT_CONTAINER_EXTENSION -import ee.ria.DigiDoc.common.Constant.SEND_SIVA_CONTAINER_NOTIFICATION_MIMETYPES -import ee.ria.DigiDoc.common.model.FileOpeningMethod -import ee.ria.DigiDoc.cryptolib.CryptoContainer -import ee.ria.DigiDoc.libdigidoclib.SignedContainer -import ee.ria.DigiDoc.ui.component.menu.SettingsMenuBottomSheet -import ee.ria.DigiDoc.ui.component.shared.Document -import ee.ria.DigiDoc.ui.component.shared.InvisibleElement -import ee.ria.DigiDoc.ui.component.shared.LoadingScreen -import ee.ria.DigiDoc.ui.component.shared.MessageDialog -import ee.ria.DigiDoc.ui.component.shared.PreventResize -import ee.ria.DigiDoc.ui.component.shared.StatusSnackbarHost -import ee.ria.DigiDoc.ui.component.shared.TopBar -import ee.ria.DigiDoc.ui.component.shared.dialog.SivaConfirmationDialog -import ee.ria.DigiDoc.ui.theme.Dimensions.SPadding -import ee.ria.DigiDoc.ui.theme.Dimensions.XSPadding -import ee.ria.DigiDoc.ui.theme.Dimensions.dividerHeight -import ee.ria.DigiDoc.ui.theme.Dimensions.iconSizeXXS -import ee.ria.DigiDoc.ui.theme.Dimensions.invisibleElementHeight -import ee.ria.DigiDoc.ui.theme.Dimensions.zeroPadding -import ee.ria.DigiDoc.ui.theme.RIADigiDocTheme -import ee.ria.DigiDoc.utils.Route -import ee.ria.DigiDoc.utils.accessibility.AccessibilityUtil.Companion.getAccessibilityEventType -import ee.ria.DigiDoc.utils.accessibility.AccessibilityUtil.Companion.sendAccessibilityEvent -import ee.ria.DigiDoc.utils.extensions.reachedBottom -import ee.ria.DigiDoc.utils.snackbar.SnackBarManager.showMessage -import ee.ria.DigiDoc.utilsLib.extensions.isCades -import ee.ria.DigiDoc.utilsLib.extensions.isXades -import ee.ria.DigiDoc.viewmodel.RecentDocumentsViewModel -import ee.ria.DigiDoc.viewmodel.shared.SharedContainerViewModel -import ee.ria.DigiDoc.viewmodel.shared.SharedMenuViewModel -import kotlinx.coroutines.Dispatchers.IO -import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import java.io.File - -@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class) -@Composable -fun RecentDocumentsScreen( - modifier: Modifier = Modifier, - navController: NavHostController, - sharedMenuViewModel: SharedMenuViewModel, - sharedContainerViewModel: SharedContainerViewModel, - recentDocumentsViewModel: RecentDocumentsViewModel = hiltViewModel(), - fileOpeningMethod: FileOpeningMethod, -) { - val logTag = "RecentDocumentsScreen" - - val context = LocalContext.current - - val scope = rememberCoroutineScope() - - val showLoading = rememberSaveable { mutableStateOf(false) } - val showSivaDialog = rememberSaveable { mutableStateOf(false) } - val selectedDocument = rememberSaveable { mutableStateOf(null) } - val isSettingsMenuBottomSheetVisible = rememberSaveable { mutableStateOf(false) } - - val handleResult: (Boolean) -> Unit = { confirmed -> - showLoading.value = true - val document = selectedDocument.value - - if (document == null) { - showLoading.value = false - } else { - scope.launch(IO) { - try { - val documentMimeType = recentDocumentsViewModel.getMimetype(document) - - recentDocumentsViewModel.handleDocument( - document, - documentMimeType ?: DEFAULT_CONTAINER_EXTENSION, - confirmed, - sharedContainerViewModel, - ) - } catch (ex: Exception) { - recentDocumentsViewModel.handleError(logTag, ex) - } finally { - showLoading.value = false - } - } - } - } - - val recentDocumentList = - rememberSaveable { - mutableStateOf( - recentDocumentsViewModel.getRecentDocumentList(fileOpeningMethod), - ) - } - var actionDocument by rememberSaveable { mutableStateOf(null) } - val openRemoveDocumentDialog = rememberSaveable { mutableStateOf(false) } - - val documentRemoved = stringResource(id = R.string.document_removed) - val documentRemovalCancelled = stringResource(id = R.string.document_removal_cancelled) - - val removeDocumentDialogMessage = - stringResource(id = R.string.recent_documents_remove_confirmation_message) - val removeDocumentCancelButtonContentDescription = - stringResource(id = R.string.recent_documents_cancel_container_removal_button) - val removeDocumentOkButtonContentDescription = - stringResource(id = R.string.recent_documents_confirm_container_removal_button) - val closeDocumentDialog = { - openRemoveDocumentDialog.value = false - } - val dismissRemoveDocumentDialog = { - closeDocumentDialog() - sendAccessibilityEvent(context, getAccessibilityEventType(), documentRemovalCancelled) - } - - val listState = rememberLazyListState() - var expanded by rememberSaveable { mutableStateOf(false) } - val searchText by recentDocumentsViewModel.searchText.collectAsState() - val documentList by recentDocumentsViewModel.documentList.collectAsState() - - val dismissSearch = { - expanded = false - recentDocumentsViewModel.onSearchTextChange("") - } - - LaunchedEffect(recentDocumentsViewModel.sendToSigningViewWithSiva) { - recentDocumentsViewModel.sendToSigningViewWithSiva.asFlow().collect { openSigningView -> - if (openSigningView) { - recentDocumentsViewModel.handleSendToSigningViewWithSiva(false) - navController.navigate(Route.Signing.route) - } - } - } - - LaunchedEffect(recentDocumentsViewModel.errorState) { - recentDocumentsViewModel.errorState.asFlow().collect { error -> - error?.let { - showMessage(context, error) - } - } - } - - Scaffold( - modifier = - modifier - .semantics { - testTagsAsResourceId = true - }.testTag("recentDocumentsScreen"), - snackbarHost = { StatusSnackbarHost() }, - topBar = { - if (!expanded) { - TopBar( - modifier = modifier, - sharedMenuViewModel = sharedMenuViewModel, - title = null, - onLeftButtonClick = { - navController.navigateUp() - }, - onRightSecondaryButtonClick = { - isSettingsMenuBottomSheetVisible.value = true - }, - ) - } - }, - ) { paddingValues -> - SettingsMenuBottomSheet( - navController = navController, - isBottomSheetVisible = isSettingsMenuBottomSheetVisible, - ) - - Column( - modifier = - modifier - .padding(paddingValues) - .fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - if (!expanded) { - Text( - text = stringResource(id = R.string.recent_documents_title), - maxLines = 2, - modifier = - modifier - .fillMaxWidth() - .padding(SPadding) - .semantics { heading() } - .focusable(enabled = true) - .focusTarget() - .focusProperties { canFocus = true }, - textAlign = TextAlign.Start, - style = MaterialTheme.typography.headlineSmall, - ) - } - val searchBarPadding = - if (!expanded) { - SPadding - } else { - zeroPadding - } - SearchBar( - modifier = - modifier - .padding(horizontal = searchBarPadding), - inputField = { - SearchBarDefaults.InputField( - modifier = - modifier - .fillMaxWidth() - .wrapContentHeight(), - query = searchText, - onQueryChange = recentDocumentsViewModel::onSearchTextChange, - onSearch = recentDocumentsViewModel::onSearchTextChange, - expanded = expanded, - enabled = true, - placeholder = { - PreventResize { - Text(stringResource(id = R.string.recent_documents_search)) - } - }, - leadingIcon = { - Icon( - modifier = - modifier - .size(iconSizeXXS), - imageVector = ImageVector.vectorResource(R.drawable.ic_m3_search_48dp_wght400), - contentDescription = null, - ) - }, - trailingIcon = { - IconButton( - modifier = - modifier - .padding(end = XSPadding) - .size(iconSizeXXS) - .testTag("searchCancelButton"), - onClick = dismissSearch, - content = { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.ic_m3_close_48dp_wght400), - contentDescription = - stringResource( - id = R.string.recent_documents_search_cancel, - ), - ) - }, - ) - }, - onExpandedChange = { expanded = it }, - colors = inputFieldColors(), - interactionSource = null, - ) - }, - expanded = expanded, - onExpandedChange = { expanded = it }, - ) { - LazyColumn( - state = listState, - modifier = modifier.testTag("lazyColumnScrollView"), - ) { - if (documentList.isNotEmpty()) { - item { - HorizontalDivider( - modifier = - modifier - .fillMaxWidth() - .padding(SPadding) - .height(dividerHeight), - ) - } - items(documentList) { document -> - Document( - name = document.name, - onItemClick = { - scope.launch(IO) { - selectedDocument.value = document - if (( - SEND_SIVA_CONTAINER_NOTIFICATION_MIMETYPES.contains( - recentDocumentsViewModel.getMimetype(document), - ) || - document.isCades(context) - ) && - !document.isXades(context) - ) { - showSivaDialog.value = true - } else { - showSivaDialog.value = false - - val container = - recentDocumentsViewModel.openDocument( - document, - true, - ) - - if (container is SignedContainer) { - sharedContainerViewModel.setSignedContainer( - container, - ) - - withContext(Main) { - navController.navigate( - Route.Signing.route, - ) - } - } else if (container is CryptoContainer) { - sharedContainerViewModel.setCryptoContainer( - container, - ) - - withContext(Main) { - navController.navigate( - Route.Encrypt.route, - ) - } - } - } - } - }, - onRemoveButtonClick = { - actionDocument = document - openRemoveDocumentDialog.value = true - }, - ) - HorizontalDivider( - modifier = - modifier - .fillMaxWidth() - .padding(SPadding) - .height(dividerHeight), - ) - } - } else { - item { - Box( - modifier = - modifier - .padding(SPadding), - contentAlignment = Alignment.Center, - ) { - Text( - modifier = - modifier - .testTag("recentDocumentsListEmpty"), - text = stringResource(id = R.string.recent_documents_search_empty), - style = MaterialTheme.typography.bodyLarge, - ) - } - } - } - - item { - Spacer( - modifier = modifier.height(invisibleElementHeight), - ) - if (listState.reachedBottom()) { - InvisibleElement(modifier = modifier) - } - } - } - } - if (!expanded) { - LazyColumn( - state = listState, - modifier = modifier.testTag("lazyColumnScrollView"), - ) { - if (recentDocumentList.value.isNotEmpty()) { - item { - HorizontalDivider( - modifier = - modifier - .fillMaxWidth() - .padding(SPadding) - .height(dividerHeight), - ) - } - items(recentDocumentList.value) { document -> - Document( - name = document.name, - onItemClick = { - scope.launch(IO) { - selectedDocument.value = document - if (( - SEND_SIVA_CONTAINER_NOTIFICATION_MIMETYPES.contains( - recentDocumentsViewModel.getMimetype(document), - ) || - document.isCades(context) - ) && - !document.isXades(context) - ) { - showSivaDialog.value = true - } else { - showSivaDialog.value = false - - val container = - recentDocumentsViewModel.openDocument( - document, - true, - ) - - if (container is SignedContainer) { - sharedContainerViewModel.setSignedContainer( - container, - ) - - withContext(Main) { - navController.navigate( - Route.Signing.route, - ) - } - } else if (container is CryptoContainer) { - sharedContainerViewModel.setCryptoContainer( - container, - ) - - withContext(Main) { - navController.navigate( - Route.Encrypt.route, - ) - } - } - } - } - }, - onRemoveButtonClick = { - actionDocument = document - openRemoveDocumentDialog.value = true - }, - ) - HorizontalDivider( - modifier = - modifier - .fillMaxWidth() - .padding(SPadding) - .height(dividerHeight), - ) - } - } else { - item { - Box( - modifier = - modifier - .padding(SPadding), - contentAlignment = Alignment.Center, - ) { - Text( - modifier = - modifier - .testTag("recentDocumentsListEmpty"), - text = stringResource(id = R.string.recent_documents_empty_message), - style = MaterialTheme.typography.bodyLarge, - ) - } - } - } - - item { - Spacer( - modifier = modifier.height(invisibleElementHeight), - ) - if (listState.reachedBottom()) { - InvisibleElement(modifier = modifier) - } - } - } - } - } - if (openRemoveDocumentDialog.value) { - MessageDialog( - modifier = modifier, - title = stringResource(R.string.document_remove_button), - message = removeDocumentDialogMessage, - showIcons = false, - dismissButtonText = stringResource(R.string.cancel_button), - confirmButtonText = stringResource(R.string.remove_title), - dismissButtonContentDescription = removeDocumentCancelButtonContentDescription, - confirmButtonContentDescription = removeDocumentOkButtonContentDescription, - onDismissRequest = dismissRemoveDocumentDialog, - onDismissButton = dismissRemoveDocumentDialog, - onConfirmButton = { - actionDocument?.delete() - recentDocumentList.value = recentDocumentsViewModel.getRecentDocumentList(fileOpeningMethod) - dismissSearch() - closeDocumentDialog() - sendAccessibilityEvent(context, getAccessibilityEventType(), documentRemoved) - }, - ) - } - - SivaConfirmationDialog( - showDialog = showSivaDialog, - modifier = modifier, - onResult = handleResult, - ) - - if (showLoading.value) { - LoadingScreen(modifier = modifier) - } - } -} - -@Preview(showBackground = true) -@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -fun RecentDocumentsScreenPreview() { - RIADigiDocTheme { - RecentDocumentsScreen( - sharedMenuViewModel = hiltViewModel(), - sharedContainerViewModel = hiltViewModel(), - navController = rememberNavController(), - fileOpeningMethod = FileOpeningMethod.ALL, - ) - } -} diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/EncryptNavigation.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/EncryptNavigation.kt index d01e1aa6c..82a7a217e 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/EncryptNavigation.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/EncryptNavigation.kt @@ -23,6 +23,7 @@ package ee.ria.DigiDoc.ui.component.crypto import android.app.Activity import android.content.ActivityNotFoundException +import android.content.Context import android.content.Intent import android.content.res.Configuration import android.widget.Toast @@ -117,6 +118,7 @@ import ee.ria.DigiDoc.utils.accessibility.AccessibilityUtil.Companion.sendAccess import ee.ria.DigiDoc.utils.extensions.reachedBottom import ee.ria.DigiDoc.utils.snackbar.SnackBarManager.showMessage import ee.ria.DigiDoc.utils.snackbar.SnackbarType +import ee.ria.DigiDoc.utilsLib.container.ContainerUtil import ee.ria.DigiDoc.utilsLib.container.ContainerUtil.createContainerAction import ee.ria.DigiDoc.utilsLib.container.ContainerUtil.removeExtensionFromContainerFilename import ee.ria.DigiDoc.utilsLib.extensions.isContainer @@ -149,6 +151,7 @@ fun EncryptNavigation( signingViewModel: SigningViewModel = hiltViewModel(), encryptViewModel: EncryptViewModel = hiltViewModel(), encryptRecipientViewModel: EncryptRecipientViewModel = hiltViewModel(), + withEncryption: Boolean = false, ) { val cryptoContainer by sharedContainerViewModel.cryptoContainer.asFlow().collectAsState(null) val shouldResetContainer by encryptViewModel.shouldResetCryptoContainer.asFlow().collectAsState(false) @@ -162,6 +165,14 @@ fun EncryptNavigation( val clickedRecipient = remember { mutableStateOf(null) } val isNestedContainer = sharedContainerViewModel.isNestedContainer(cryptoContainer) + val isSaveContainerShown = rememberSaveable { mutableStateOf(false) } + val isWithEncryptionHandled = rememberSaveable { mutableStateOf(false) } + val isEditContainerButtonShown = + !isNestedContainer && + !encryptViewModel.isEncryptedContainer(cryptoContainer) && + !encryptViewModel.isDecryptedContainer(cryptoContainer) + val isSignButtonShown = + !isNestedContainer && encryptViewModel.isEncryptedContainer(cryptoContainer) val containerEncryptedSuccess = remember { mutableStateOf(false) } val containerEncryptedSuccessText = stringResource(id = R.string.crypto_create_success) @@ -189,7 +200,7 @@ fun EncryptNavigation( stringResource(id = R.string.document_remove_last_confirmation_message) } val closeContainerMessage = stringResource(id = R.string.crypto_close_container_message) - val removeContainerMessage = stringResource(id = R.string.remove_container) + val confirmCloseContainerMessage = stringResource(id = R.string.crypto_close_container_title) val saveContainerMessage = stringResource(id = R.string.container_save) val dismissRemoveFileDialog = { closeRemoveFileDialog() @@ -401,7 +412,16 @@ fun EncryptNavigation( try { fileToSave.value?.let { file -> sharedContainerViewModel.saveContainerFile(file, result) - showMessage(context, R.string.file_saved, SnackbarType.SUCCESS) + val isContainerFile = file.absolutePath == cryptoContainer?.file?.absolutePath + showMessage( + context, + if (isContainerFile) { + R.string.crypto_saved_container_success + } else { + R.string.file_saved + }, + SnackbarType.SUCCESS, + ) isSaved = true } ?: showMessage(context, R.string.file_saved_error) } catch (_: Exception) { @@ -412,10 +432,11 @@ fun EncryptNavigation( } BackHandler { - if (!isNestedContainer && encryptViewModel.isEncryptedContainer(cryptoContainer)) { + if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, @@ -436,6 +457,13 @@ fun EncryptNavigation( sharedContainerViewModel.setCryptoContainer(sharedContainerViewModel.currentContainer() as? CryptoContainer) } + LaunchedEffect(withEncryption) { + if (withEncryption && !isWithEncryptionHandled.value) { + isSaveContainerShown.value = true + isWithEncryptionHandled.value = true + } + } + LaunchedEffect(encryptRecipientViewModel.isContainerEncrypted) { encryptRecipientViewModel.isContainerEncrypted.asFlow().collect { isContainerEncrypted -> if (isContainerEncrypted) { @@ -453,6 +481,7 @@ fun EncryptNavigation( delay(500) encryptionButtonEnabled.value = true + isSaveContainerShown.value = true } } } @@ -490,14 +519,17 @@ fun EncryptNavigation( LaunchedEffect(isSaved) { if (isSaved) { + isSaveContainerShown.value = false if (showContainerCloseConfirmationDialog.value) { showContainerCloseConfirmationDialog.value = false handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, ) } + @Suppress("AssignedValueIsNeverRead") isSaved = false } } @@ -505,7 +537,7 @@ fun EncryptNavigation( LaunchedEffect(sharedContainerViewModel.decryptNFCStatus) { sharedContainerViewModel.decryptNFCStatus.asFlow().collect { status -> status?.let { - if (status == true) { + if (status) { withContext(Main) { containerDecryptedSuccess.value = true sendAccessibilityEvent( @@ -525,7 +557,7 @@ fun EncryptNavigation( LaunchedEffect(sharedContainerViewModel.decryptIDCardStatus) { sharedContainerViewModel.decryptIDCardStatus.asFlow().collect { status -> status?.let { - if (status == true) { + if (status) { withContext(Main) { containerDecryptedSuccess.value = true sendAccessibilityEvent( @@ -572,10 +604,11 @@ fun EncryptNavigation( }, leftIconContentDescription = R.string.crypto_close_container_title, onLeftButtonClick = { - if (!isNestedContainer && encryptViewModel.isEncryptedContainer(cryptoContainer)) { + if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, @@ -651,18 +684,20 @@ fun EncryptNavigation( verticalArrangement = Arrangement.Top, horizontalAlignment = Alignment.Start, ) { - if (containerEncryptedSuccess.value == true) { + if (containerEncryptedSuccess.value) { showMessage(containerEncryptedSuccessText, SnackbarType.SUCCESS) containerEncryptedSuccess.value = false + isSaveContainerShown.value = true } - if (containerDecryptedSuccess.value == true) { + if (containerDecryptedSuccess.value) { showMessage(containerDecryptedSuccessText, SnackbarType.SUCCESS) containerDecryptedSuccess.value = false } if (encryptViewModel.isEmptyFileInContainer(cryptoContainer) && - !encryptViewModel.isEncryptedContainer(cryptoContainer) + !encryptViewModel.isEncryptedContainer(cryptoContainer) && + !encryptViewModel.isDecryptedContainer(cryptoContainer) ) { showMessage(emptyFileInContainerText) } @@ -751,7 +786,10 @@ fun EncryptNavigation( } }, onMoreOptionsActionButtonClick = { - showContainerBottomSheet.value = true + showContainerBottomSheet.value = + isSaveContainerShown.value || + isEditContainerButtonShown || + isSignButtonShown }, ) } @@ -881,9 +919,11 @@ fun EncryptNavigation( subtitle = stringResource(id = R.string.crypto_containter_update_name), editValue = containerName, onEditValueChange = { + @Suppress("AssignedValueIsNeverRead") containerName = it }, onClearValueClick = { + @Suppress("AssignedValueIsNeverRead") containerName = TextFieldValue("") }, cancelButtonClick = dismissEditContainerNameDialog, @@ -940,6 +980,7 @@ fun EncryptNavigation( cryptoContainer?.file?.delete() sharedContainerViewModel.resetCryptoContainer() handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, @@ -1037,28 +1078,19 @@ fun EncryptNavigation( openRemoveFileDialog = openRemoveFileDialog, onBackButtonClick = { handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, ) }, ) - EncryptContainerBottomSheet( modifier = modifier, showSheet = showContainerBottomSheet, - isEditContainerButtonShown = - !isNestedContainer && - !encryptViewModel.isEncryptedContainer(cryptoContainer) && - !encryptViewModel.isDecryptedContainer(cryptoContainer), + isEditContainerButtonShown = isEditContainerButtonShown, openEditContainerNameDialog = openEditContainerNameDialog, - isSaveButtonShown = ( - encryptViewModel.isEncryptedContainer(cryptoContainer) || - ( - encryptViewModel.isDecryptedContainer(cryptoContainer) && - cryptoContainer?.hasRecipients() == true - ) - ), + isSaveButtonShown = isSaveContainerShown.value, cryptoContainer = cryptoContainer, saveFileLauncher = saveFileLauncher, saveFile = saveFile, @@ -1082,38 +1114,50 @@ fun EncryptNavigation( } if (showContainerCloseConfirmationDialog.value) { + val dismissIcon = + if (isSaveContainerShown.value) { + R.drawable.ic_m3_download_48dp_wght400 + } else { + R.drawable.ic_m3_cancel_48dp_wght400 + } + val dismissButtonText = + if (isSaveContainerShown.value) { + stringResource(R.string.save) + } else { + stringResource(R.string.cancel_button) + } MessageDialog( modifier = modifier, title = stringResource(R.string.crypto_close_container_title), message = closeContainerMessage, showIcons = true, - dismissIcon = R.drawable.ic_m3_download_48dp_wght400, + dismissIcon = dismissIcon, confirmIcon = R.drawable.ic_m3_delete_48dp_wght400, - dismissButtonText = stringResource(R.string.save), - confirmButtonText = stringResource(R.string.remove_title), + dismissButtonText = dismissButtonText, + confirmButtonText = stringResource(R.string.close_button), dismissButtonContentDescription = saveContainerMessage, - confirmButtonContentDescription = removeContainerMessage, + confirmButtonContentDescription = confirmCloseContainerMessage, onDismissRequest = { showContainerCloseConfirmationDialog.value = false }, onDismissButton = { - val file = cryptoContainer?.file - if (file != null) { - saveFile( - file, - cryptoContainer?.containerMimetype(), - saveFileLauncher, - ) + if (isSaveContainerShown.value) { + val file = cryptoContainer?.file + if (file != null) { + saveFile( + file, + cryptoContainer?.containerMimetype(), + saveFileLauncher, + ) + } + } else { + showContainerCloseConfirmationDialog.value = false } }, onConfirmButton = { showContainerCloseConfirmationDialog.value = false - val containerFile = cryptoContainer?.file - if (containerFile?.exists() == true) { - containerFile.delete() - } sharedContainerViewModel.resetCryptoContainer() - handleBackButtonClick(navController, encryptViewModel, sharedContainerViewModel) + handleBackButtonClick(context, navController, encryptViewModel, sharedContainerViewModel) }, ) } @@ -1122,6 +1166,7 @@ fun EncryptNavigation( } private fun handleBackButtonClick( + context: Context, navController: NavHostController, encryptViewModel: EncryptViewModel, sharedContainerViewModel: SharedContainerViewModel, @@ -1130,8 +1175,7 @@ private fun handleBackButtonClick( sharedContainerViewModel.resetIsSivaConfirmed() if (sharedContainerViewModel.nestedContainers.size > 1) { sharedContainerViewModel.removeLastContainer() - val currentContainer = sharedContainerViewModel.currentContainer() - when (currentContainer) { + when (val currentContainer = sharedContainerViewModel.currentContainer()) { is SignedContainer -> { sharedContainerViewModel.resetCryptoContainer() sharedContainerViewModel.setSignedContainer(currentContainer) @@ -1143,6 +1187,7 @@ private fun handleBackButtonClick( } } } else { + ContainerUtil.removeCryptoContainersDir(context) sharedContainerViewModel.clearContainers() encryptViewModel.handleBackButton() navController.navigateUp() diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/OpenMenuBottomSheet.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/OpenMenuBottomSheet.kt deleted file mode 100644 index e14465ef9..000000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/OpenMenuBottomSheet.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName", "FunctionName") - -package ee.ria.DigiDoc.ui.component.menu - -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ModalBottomSheet -import androidx.compose.material3.rememberModalBottomSheetState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.testTagsAsResourceId -import ee.ria.DigiDoc.R -import ee.ria.DigiDoc.ui.theme.Dimensions.SPadding - -// Menu component for the bottom sheet (Open document, add more files, sign document and encrypt document) -@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) -@Composable -fun OpenMenuBottomSheet( - modifier: Modifier = Modifier, - isBottomSheetVisible: MutableState = mutableStateOf(false), - @StringRes firstButtonStringRes: Int = R.string.main_menu_add_file, - @StringRes secondButtonStringRes: Int = R.string.main_menu_recent_documents, - @StringRes firstButtonStringResContentDescription: Int = R.string.main_menu_add_file_accessibility, - @StringRes secondButtonStringResContentDescription: Int = R.string.main_menu_recent_documents_accessibility, - @DrawableRes firstButtonIcon: Int = R.drawable.ic_m3_attach_file_48dp_wght400, - @DrawableRes secondButtonIcon: Int = R.drawable.ic_m3_folder_48dp_wght400, - firstButtonClick: () -> Unit = {}, - secondButtonClick: () -> Unit = {}, - testTag: String = "menuOpenBottomSheet", - firstButtonTestTag: String = "menuOpenAddFileButton", - secondButtonTestTag: String = "menuOpenRecentDocumentsButton", -) { - if (isBottomSheetVisible.value) { - ModalBottomSheet( - modifier = modifier, - containerColor = MaterialTheme.colorScheme.surfaceContainer, - contentColor = MaterialTheme.colorScheme.onSurface, - onDismissRequest = { isBottomSheetVisible.value = false }, - sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true), - ) { - Column( - modifier = - Modifier - .semantics { - testTagsAsResourceId = true - }.testTag(testTag) - .fillMaxWidth() - .padding(SPadding), - ) { - TwoButtonMenu( - modifier = modifier, - firstButtonStringRes = firstButtonStringRes, - secondButtonStringRes = secondButtonStringRes, - firstButtonStringResContentDescription = firstButtonStringResContentDescription, - secondButtonStringResContentDescription = secondButtonStringResContentDescription, - firstButtonIcon = firstButtonIcon, - secondButtonIcon = secondButtonIcon, - firstButtonClick = firstButtonClick, - secondButtonClick = secondButtonClick, - firstButtonTestTag = firstButtonTestTag, - secondButtonTestTag = secondButtonTestTag, - ) - } - } - } -} diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/TwoButtonMenu.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/TwoButtonMenu.kt deleted file mode 100644 index 42aaac05f..000000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/TwoButtonMenu.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName", "FunctionName") - -package ee.ria.DigiDoc.ui.component.menu - -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.runtime.Composable -import androidx.compose.ui.ExperimentalComposeUiApi -import androidx.compose.ui.Modifier - -@OptIn(ExperimentalComposeUiApi::class, ExperimentalMaterial3Api::class) -@Composable -fun TwoButtonMenu( - modifier: Modifier = Modifier, - @StringRes firstButtonStringRes: Int, - @StringRes secondButtonStringRes: Int, - @StringRes firstButtonStringResContentDescription: Int, - @StringRes secondButtonStringResContentDescription: Int, - @DrawableRes firstButtonIcon: Int, - @DrawableRes secondButtonIcon: Int, - firstButtonClick: () -> Unit = {}, - secondButtonClick: () -> Unit = {}, - firstButtonTestTag: String, - secondButtonTestTag: String, -) { - MenuButton( - modifier = modifier, - buttonStringRes = firstButtonStringRes, - buttonIcon = firstButtonIcon, - buttonStringResContentDescription = firstButtonStringResContentDescription, - buttonClick = firstButtonClick, - testTag = firstButtonTestTag, - ) - MenuButton( - modifier = modifier, - buttonStringRes = secondButtonStringRes, - buttonIcon = secondButtonIcon, - buttonStringResContentDescription = secondButtonStringResContentDescription, - buttonClick = secondButtonClick, - testTag = secondButtonTestTag, - ) -} diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/Document.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/Document.kt deleted file mode 100644 index 198324995..000000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/Document.kt +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName", "FunctionName") - -package ee.ria.DigiDoc.ui.component.shared - -import android.content.res.Configuration -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.focusable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.layout.wrapContentSize -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.graphics.vector.ImageVector -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.tooling.preview.Preview -import androidx.constraintlayout.compose.ConstraintLayout -import ee.ria.DigiDoc.R -import ee.ria.DigiDoc.ui.theme.Dimensions.SPadding -import ee.ria.DigiDoc.ui.theme.Dimensions.XSPadding -import ee.ria.DigiDoc.ui.theme.Dimensions.iconSizeXXS -import ee.ria.DigiDoc.ui.theme.RIADigiDocTheme -import ee.ria.DigiDoc.utils.accessibility.AccessibilityUtil.Companion.formatNumbers - -@Composable -fun Document( - modifier: Modifier = Modifier, - name: String, - onItemClick: () -> Unit = {}, - onRemoveButtonClick: () -> Unit = {}, -) { - val documentTitle = stringResource(id = R.string.document) - val textColor = MaterialTheme.colorScheme.inverseSurface.toArgb() - ConstraintLayout( - modifier = - modifier - .padding(vertical = XSPadding) - .wrapContentHeight() - .fillMaxWidth() - .semantics { - this.contentDescription = "$documentTitle ${formatNumbers(name).lowercase()}" - }.focusable(true) - .clickable(onClick = onItemClick) - .testTag("recentDocumentsItem"), - ) { - val ( - folderIcon, - documentText, - removeIcon, - ) = createRefs() - Icon( - modifier = - modifier - .padding(start = SPadding, end = XSPadding) - .size(iconSizeXXS) - .constrainAs(folderIcon) { - start.linkTo(parent.start) - top.linkTo(parent.top) - bottom.linkTo(parent.bottom) - }, - imageVector = ImageVector.vectorResource(R.drawable.ic_m3_folder_48dp_wght400), - contentDescription = null, - ) - MiddleEllipsizeMultilineText( - modifier = - modifier - .wrapContentSize() - .padding(end = iconSizeXXS * 2 + XSPadding * 2 + SPadding * 2) - .constrainAs(documentText) { - start.linkTo(folderIcon.end) - top.linkTo(parent.top) - bottom.linkTo(parent.bottom) - }.focusable(false) - .testTag("recentDocumentsItemName"), - text = name, - maxLines = 4, - textColor = textColor, - ) - IconButton( - modifier = - modifier - .padding(end = SPadding) - .size(iconSizeXXS) - .constrainAs(removeIcon) { - end.linkTo(parent.end) - top.linkTo(parent.top) - bottom.linkTo(parent.bottom) - }.testTag("recentDocumentsItemRemoveButton"), - onClick = onRemoveButtonClick, - content = { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.ic_m3_delete_48dp_wght400), - contentDescription = "${ - stringResource( - id = R.string.recent_documents_remove_button, - ) - } ${formatNumbers(name).lowercase()}", - ) - }, - ) - } -} - -@Preview(showBackground = true) -@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) -@Composable -fun DocumentPreview() { - RIADigiDocTheme { - Surface( - modifier = Modifier.background(MaterialTheme.colorScheme.background), - color = MaterialTheme.colorScheme.background, - ) { - Column { - Document( - name = "test-container.asice", - ) - Document( - name = "some-" + "very-".repeat(30) + "long-document-name-document.bdoc", - ) - Document( - name = "some-" + "very-".repeat(40) + "long-document-name-document.ddoc", - ) - } - } - } -} 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 c01dc659f..85ab0aa83 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 @@ -130,6 +130,7 @@ import ee.ria.DigiDoc.utils.extensions.reachedBottom import ee.ria.DigiDoc.utils.libdigidoc.SignatureStatusUtil import ee.ria.DigiDoc.utils.snackbar.SnackBarManager.showMessage import ee.ria.DigiDoc.utils.snackbar.SnackbarType +import ee.ria.DigiDoc.utilsLib.container.ContainerUtil import ee.ria.DigiDoc.utilsLib.container.ContainerUtil.createContainerAction import ee.ria.DigiDoc.utilsLib.container.ContainerUtil.removeExtensionFromContainerFilename import ee.ria.DigiDoc.utilsLib.extensions.isContainer @@ -182,6 +183,9 @@ fun SigningNavigation( val isCadesContainer = signedContainer?.isCades() == true val isDdocContainer = signedContainer?.isDdoc() == true var isEmptyFileInContainer by remember { mutableStateOf(false) } + val isSaveContainerShown = rememberSaveable { mutableStateOf(false) } + val isEditContainerButtonShown = signingViewModel.isBottomContainerButtonShown(signedContainer, isNestedContainer) + val isEncryptButtonShown = signingViewModel.isEncryptButtonShown(signedContainer, isNestedContainer) var validSignaturesCount by remember { mutableIntStateOf(0) } var unknownSignaturesCount by remember { mutableIntStateOf(0) } @@ -224,7 +228,7 @@ fun SigningNavigation( stringResource(id = R.string.document_remove_last_confirmation_message) } val closeContainerMessage = stringResource(id = R.string.signing_close_container_message) - val removeContainerMessage = stringResource(id = R.string.remove_container) + val confirmCloseContainerMessage = stringResource(id = R.string.signing_close_container_title) val saveContainerMessage = stringResource(id = R.string.container_save) val dismissRemoveFileDialog = { closeRemoveFileDialog() @@ -453,7 +457,7 @@ fun SigningNavigation( } ?: run { signedContainer?.getContainerFile()?.let { sharedContainerViewModel.saveContainerFile(it, result) - showMessage(context, R.string.file_saved, SnackbarType.SUCCESS) + showMessage(context, R.string.signature_saved_container_success, SnackbarType.SUCCESS) isSaved = true } ?: showMessage(context, R.string.file_saved_error) } @@ -464,10 +468,11 @@ fun SigningNavigation( } BackHandler { - if (!isNestedContainer) { + if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( + context, navController, signingViewModel, sharedContainerViewModel, @@ -526,7 +531,7 @@ fun SigningNavigation( LaunchedEffect(sharedContainerViewModel.signedNFCStatus) { sharedContainerViewModel.signedNFCStatus.asFlow().collect { status -> status?.let { - if (status == true) { + if (status) { signatures = signedContainer?.getSignatures() ?: emptyList() withContext(Main) { signatureAddedSuccess.value = true @@ -543,7 +548,7 @@ fun SigningNavigation( LaunchedEffect(sharedContainerViewModel.signedIDCardStatus) { sharedContainerViewModel.signedIDCardStatus.asFlow().collect { status -> status?.let { - if (status == true) { + if (status) { signatures = signedContainer?.getSignatures() ?: emptyList() withContext(Main) { signatureAddedSuccess.value = true @@ -623,6 +628,7 @@ fun SigningNavigation( val announcementText = when { unknownSignaturesCount == 0 && invalidSignaturesCount == 0 -> { + @Suppress("AssignedValueIsNeverRead") validSignaturesCount = signatures.size "$containerHasText, ${validSignaturesText.lowercase()}" } @@ -635,6 +641,7 @@ fun SigningNavigation( } delay(1000) + @Suppress("AssignedValueIsNeverRead") isSignaturesCountLoaded = true sendAccessibilityEvent( context, @@ -655,14 +662,17 @@ fun SigningNavigation( LaunchedEffect(isSaved) { if (isSaved) { + isSaveContainerShown.value = false if (showContainerCloseConfirmationDialog.value) { showContainerCloseConfirmationDialog.value = false handleBackButtonClick( + context, navController, signingViewModel, sharedContainerViewModel, ) } + @Suppress("AssignedValueIsNeverRead") isSaved = false } } @@ -710,10 +720,11 @@ fun SigningNavigation( }, leftIconContentDescription = R.string.signing_close_container_title, onLeftButtonClick = { - if (!isNestedContainer) { + if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( + context, navController, signingViewModel, sharedContainerViewModel, @@ -805,6 +816,7 @@ fun SigningNavigation( ) } } + isSaveContainerShown.value = true showMessage(signatureAddedSuccessText, SnackbarType.SUCCESS) signatureAddedSuccess.value = false } @@ -849,11 +861,7 @@ fun SigningNavigation( isXadesContainer, isCadesContainer, ), - showRightActionButton = - signingViewModel.isEncryptButtonShown( - signedContainer, - isNestedContainer, - ), + showRightActionButton = isEncryptButtonShown, leftActionButtonName = R.string.signature_update_signature_add, rightActionButtonName = R.string.encrypt_button, leftActionButtonContentDescription = R.string.signature_update_signature_add, @@ -878,7 +886,10 @@ fun SigningNavigation( }, onRightActionButtonClick = onEncryptActionClick, onMoreOptionsActionButtonClick = { - showContainerBottomSheet.value = true + showContainerBottomSheet.value = + isSaveContainerShown.value || + isEditContainerButtonShown || + isEncryptButtonShown }, ) } @@ -963,10 +974,10 @@ fun SigningNavigation( timestamps, showSignaturesLoadingIndicator.value, signaturesLoading, - true, - false, - onSignatureMoreClick, - onSignatureMoreClick, + showNameAsAllCaps = true, + isDdocValid = false, + onClick = onSignatureMoreClick, + onClickMore = onSignatureMoreClick, ) } } @@ -1040,9 +1051,11 @@ fun SigningNavigation( subtitle = stringResource(id = R.string.signature_update_name_update_name), editValue = containerName, onEditValueChange = { + @Suppress("AssignedValueIsNeverRead") containerName = it }, onClearValueClick = { + @Suppress("AssignedValueIsNeverRead") containerName = TextFieldValue("") }, cancelButtonClick = dismissEditContainerNameDialog, @@ -1053,6 +1066,7 @@ fun SigningNavigation( ) } openEditContainerNameDialog.value = false + isSaveContainerShown.value = true sendAccessibilityEvent( context, getAccessibilityEventType(), @@ -1099,7 +1113,12 @@ fun SigningNavigation( signedContainer?.getContainerFile()?.delete() sharedContainerViewModel.resetSignedContainer() sharedContainerViewModel.resetContainerNotifications() - handleBackButtonClick(navController, signingViewModel, sharedContainerViewModel) + handleBackButtonClick( + context, + navController, + signingViewModel, + sharedContainerViewModel, + ) } else { scope.launch(IO) { try { @@ -1114,6 +1133,7 @@ fun SigningNavigation( } } } + isSaveContainerShown.value = true closeRemoveFileDialog() sendAccessibilityEvent(context, getAccessibilityEventType(), fileRemoved) }, @@ -1158,6 +1178,7 @@ fun SigningNavigation( actionSignature, ) } + isSaveContainerShown.value = true closeSignatureDialog() sendAccessibilityEvent(context, getAccessibilityEventType(), signatureRemoved) }, @@ -1193,27 +1214,20 @@ fun SigningNavigation( openRemoveFileDialog = openRemoveFileDialog, onBackButtonClick = { handleBackButtonClick( + context, navController, signingViewModel, sharedContainerViewModel, ) }, ) - ContainerBottomSheet( modifier = modifier, showSheet = showContainerBottomSheet, - isEditContainerButtonShown = - signingViewModel.isBottomContainerButtonShown( - signedContainer, - isNestedContainer, - ), + isSaveButtonShown = isSaveContainerShown.value, + isEditContainerButtonShown = isEditContainerButtonShown, openEditContainerNameDialog = openEditContainerNameDialog, - isEncryptButtonShown = - signingViewModel.isBottomContainerButtonShown( - signedContainer, - isNestedContainer, - ), + isEncryptButtonShown = isEncryptButtonShown, signedContainer = signedContainer, onEncryptClick = onEncryptActionClick, saveFileLauncher = saveFileLauncher, @@ -1256,39 +1270,51 @@ fun SigningNavigation( } if (showContainerCloseConfirmationDialog.value) { + val dismissIcon = + if (isSaveContainerShown.value) { + R.drawable.ic_m3_download_48dp_wght400 + } else { + R.drawable.ic_m3_cancel_48dp_wght400 + } + val dismissButtonText = + if (isSaveContainerShown.value) { + stringResource(R.string.save) + } else { + stringResource(R.string.cancel_button) + } MessageDialog( modifier = modifier, title = stringResource(R.string.signing_close_container_title), message = closeContainerMessage, showIcons = true, - dismissIcon = R.drawable.ic_m3_download_48dp_wght400, + dismissIcon = dismissIcon, confirmIcon = R.drawable.ic_m3_delete_48dp_wght400, - dismissButtonText = stringResource(R.string.save), - confirmButtonText = stringResource(R.string.remove_title), + dismissButtonText = dismissButtonText, + confirmButtonText = stringResource(R.string.close_button), dismissButtonContentDescription = saveContainerMessage, - confirmButtonContentDescription = removeContainerMessage, + confirmButtonContentDescription = confirmCloseContainerMessage, onDismissRequest = { showContainerCloseConfirmationDialog.value = false }, onDismissButton = { - val file = signedContainer?.getContainerFile() - if (file != null) { - saveFile( - file, - signedContainer?.containerMimetype(), - saveFileLauncher, - ) + if (isSaveContainerShown.value) { + val file = signedContainer?.getContainerFile() + if (file != null) { + saveFile( + file, + signedContainer?.containerMimetype(), + saveFileLauncher, + ) + } + } else { + showContainerCloseConfirmationDialog.value = false } }, onConfirmButton = { showContainerCloseConfirmationDialog.value = false - val containerFile = signedContainer?.getContainerFile() - if (containerFile?.exists() == true) { - containerFile.delete() - } sharedContainerViewModel.resetSignedContainer() sharedContainerViewModel.resetContainerNotifications() - handleBackButtonClick(navController, signingViewModel, sharedContainerViewModel) + handleBackButtonClick(context, navController, signingViewModel, sharedContainerViewModel) }, ) } @@ -1318,6 +1344,7 @@ private suspend fun createContainerForSignedPDF( } private fun handleBackButtonClick( + context: Context, navController: NavHostController, signingViewModel: SigningViewModel, sharedContainerViewModel: SharedContainerViewModel, @@ -1326,8 +1353,7 @@ private fun handleBackButtonClick( sharedContainerViewModel.resetIsSivaConfirmed() if (sharedContainerViewModel.nestedContainers.size > 1) { sharedContainerViewModel.removeLastContainer() - val currentContainer = sharedContainerViewModel.currentContainer() - when (currentContainer) { + when (val currentContainer = sharedContainerViewModel.currentContainer()) { is SignedContainer -> { sharedContainerViewModel.resetCryptoContainer() sharedContainerViewModel.setSignedContainer(currentContainer) @@ -1339,6 +1365,7 @@ private fun handleBackButtonClick( } } } else { + ContainerUtil.removeSignatureContainersDir(context) sharedContainerViewModel.clearContainers() signingViewModel.handleBackButton() navController.navigateUp() diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/bottomsheet/ContainerBottomSheet.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/bottomsheet/ContainerBottomSheet.kt index 0f03a55da..d02ab4e1a 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/bottomsheet/ContainerBottomSheet.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/signing/bottomsheet/ContainerBottomSheet.kt @@ -37,6 +37,7 @@ import java.io.File fun ContainerBottomSheet( modifier: Modifier, showSheet: MutableState, + isSaveButtonShown: Boolean = false, isEditContainerButtonShown: Boolean = true, openEditContainerNameDialog: MutableState, isEncryptButtonShown: Boolean = true, @@ -66,6 +67,7 @@ fun ContainerBottomSheet( openEditContainerNameDialog.value = true }, BottomSheetButton( + showButton = isSaveButtonShown, icon = R.drawable.ic_m3_download_48dp_wght400, text = stringResource(R.string.container_save), contentDescription = "${stringResource( diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/utils/Constant.kt b/app/src/main/kotlin/ee/ria/DigiDoc/utils/Constant.kt index adeb7d00a..0d69d794e 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/utils/Constant.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/utils/Constant.kt @@ -40,6 +40,7 @@ object Constant { const val CRYPTO_FILE_CHOOSING_SCREEN = "crypto_file_choosing_route" const val SIGNING_SCREEN = "signing_route" const val ENCRYPT_SCREEN = "encrypt_route" + const val ENCRYPTED_SCREEN = "encrypted_route" const val DECRYPT_SCREEN = "decrypt_route" const val ACCESSIBILITY_SCREEN = "accessibility_route" const val INFO_SCREEN = "info_route" @@ -47,9 +48,6 @@ object Constant { const val SIGNER_DETAIL_SCREEN = "signer_detail_route" const val CERTIFICATE_DETAIL_SCREEN = "certificate_detail_route" const val RECIPIENT_DETAIL_SCREEN = "recipient_detail_route" - const val RECENT_DOCUMENTS_SCREEN = "recent_documents_route" - const val RECENT_DOCUMENTS_SCREEN_FROM_SIGNING_SCREEN = "recent_documents_route_from_signing_screen" - const val RECENT_DOCUMENTS_FROM_ENCRYPT_SCREEN = "recent_documents_from_encrypt_route" const val SETTINGS_SCREEN = "settings_route" const val SETTINGS_LANGUAGE_CHOOSER_SCREEN = "settings_language_chooser_route" const val SETTINGS_THEME_CHOOSER_SCREEN = "settings_theme_chooser_route" diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.kt b/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.kt index b4f70b448..795b99861 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.kt @@ -30,6 +30,7 @@ import ee.ria.DigiDoc.utils.Constant.Routes.CRYPTO_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.DECRYPT_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.DIAGNOSTICS_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.EID_SCREEN +import ee.ria.DigiDoc.utils.Constant.Routes.ENCRYPTED_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.ENCRYPTION_SERVICES_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.ENCRYPT_RECIPIENT_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.ENCRYPT_SCREEN @@ -40,9 +41,6 @@ import ee.ria.DigiDoc.utils.Constant.Routes.MYEID_IDENTIFICATION_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.MYEID_PIN_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.MYEID_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.PROXY_SERVICES_SCREEN -import ee.ria.DigiDoc.utils.Constant.Routes.RECENT_DOCUMENTS_FROM_ENCRYPT_SCREEN -import ee.ria.DigiDoc.utils.Constant.Routes.RECENT_DOCUMENTS_SCREEN -import ee.ria.DigiDoc.utils.Constant.Routes.RECENT_DOCUMENTS_SCREEN_FROM_SIGNING_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.RECIPIENT_DETAIL_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.ROOT_SCREEN import ee.ria.DigiDoc.utils.Constant.Routes.SETTINGS_LANGUAGE_CHOOSER_SCREEN @@ -81,6 +79,8 @@ sealed class Route( data object Encrypt : Route(ENCRYPT_SCREEN) + data object Encrypted : Route(ENCRYPTED_SCREEN) + data object DecryptScreen : Route(DECRYPT_SCREEN) data object Accessibility : Route(ACCESSIBILITY_SCREEN) @@ -95,12 +95,6 @@ sealed class Route( data object RecipientDetail : Route(RECIPIENT_DETAIL_SCREEN) - data object RecentDocuments : Route(RECENT_DOCUMENTS_SCREEN) - - data object RecentDocumentsFromSigning : Route(RECENT_DOCUMENTS_SCREEN_FROM_SIGNING_SCREEN) - - data object RecentDocumentsFromEncrypt : Route(RECENT_DOCUMENTS_FROM_ENCRYPT_SCREEN) - data object Settings : Route(SETTINGS_SCREEN) data object SettingsLanguageChooser : Route(SETTINGS_LANGUAGE_CHOOSER_SCREEN) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptViewModel.kt b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptViewModel.kt index 32a3891f9..e92b17450 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptViewModel.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptViewModel.kt @@ -35,8 +35,6 @@ import ee.ria.DigiDoc.common.Constant.CDOC1_EXTENSION import ee.ria.DigiDoc.cryptolib.CDOC2Settings import ee.ria.DigiDoc.cryptolib.CryptoContainer import ee.ria.DigiDoc.domain.repository.fileopening.FileOpeningRepository -import ee.ria.DigiDoc.domain.repository.siva.SivaRepository -import ee.ria.DigiDoc.libdigidoclib.SignedContainer import ee.ria.DigiDoc.utilsLib.container.ContainerUtil import ee.ria.DigiDoc.utilsLib.container.ContainerUtil.createContainerAction import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog @@ -52,7 +50,6 @@ import javax.inject.Inject class EncryptViewModel @Inject constructor( - private val sivaRepository: SivaRepository, private val mimeTypeResolver: MimeTypeResolver, private val contentResolver: ContentResolver, private val fileOpeningRepository: FileOpeningRepository, @@ -92,6 +89,15 @@ class EncryptViewModel ) && isDataFilesInContainer(cryptoContainer) + fun isSaveButtonShown(cryptoContainer: CryptoContainer?): Boolean = + ( + isEncryptedContainer(cryptoContainer) || + ( + isDecryptedContainer(cryptoContainer) && + cryptoContainer?.hasRecipients() == true + ) + ) + fun isSignButtonShown( cryptoContainer: CryptoContainer?, isNestedContainer: Boolean, @@ -150,20 +156,6 @@ class EncryptViewModel fun getMimetype(file: File): String? = mimeTypeResolver.mimeType(file) - suspend fun getTimestampedContainer( - context: Context, - signedContainer: SignedContainer, - isSivaConfirmed: Boolean, - ): SignedContainer { - if (sivaRepository.isTimestampedContainer(signedContainer) && - !signedContainer.isXades() - ) { - return sivaRepository.getTimestampedContainer(context, signedContainer, isSivaConfirmed) - } - - return signedContainer - } - @Throws(Exception::class) suspend fun openSignedContainer( context: Context, diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModel.kt b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModel.kt deleted file mode 100644 index 3216f8f9e..000000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModel.kt +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2017 - 2026 Riigi Infosüsteemi Amet - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -@file:Suppress("PackageName") - -package ee.ria.DigiDoc.viewmodel - -import android.content.Context -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext -import ee.ria.DigiDoc.R -import ee.ria.DigiDoc.common.Constant.ASICS_MIMETYPE -import ee.ria.DigiDoc.common.Constant.DDOC_MIMETYPE -import ee.ria.DigiDoc.common.container.Container -import ee.ria.DigiDoc.common.exception.NoInternetConnectionException -import ee.ria.DigiDoc.common.model.FileOpeningMethod -import ee.ria.DigiDoc.cryptolib.CDOC2Settings -import ee.ria.DigiDoc.cryptolib.CryptoContainer -import ee.ria.DigiDoc.domain.repository.siva.SivaRepository -import ee.ria.DigiDoc.libdigidoclib.SignedContainer -import ee.ria.DigiDoc.utilsLib.container.ContainerUtil -import ee.ria.DigiDoc.utilsLib.extensions.isCades -import ee.ria.DigiDoc.utilsLib.extensions.isCryptoContainer -import ee.ria.DigiDoc.utilsLib.logging.LoggingUtil.Companion.errorLog -import ee.ria.DigiDoc.utilsLib.mimetype.MimeTypeResolver -import ee.ria.DigiDoc.viewmodel.shared.SharedContainerViewModel -import kotlinx.coroutines.Dispatchers.Main -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.withContext -import java.io.File -import java.io.IOException -import javax.inject.Inject - -@HiltViewModel -class RecentDocumentsViewModel - @Inject - constructor( - @param:ApplicationContext private val context: Context, - private val sivaRepository: SivaRepository, - private val mimeTypeResolver: MimeTypeResolver, - private val cdoc2Settings: CDOC2Settings, - ) : ViewModel() { - private val logTag = "RecentDocumentsViewModel" - private val _sendToSigningViewWithSiva = MutableLiveData(false) - val sendToSigningViewWithSiva: LiveData = _sendToSigningViewWithSiva - - private val _errorState = MutableLiveData(null) - val errorState: LiveData = _errorState - private val _searchText = MutableStateFlow("") - val searchText = _searchText.asStateFlow() - - private val _documentList = MutableStateFlow(getRecentDocumentList()) - val documentList = filterDocuments() - - private fun filterDocuments() = - searchText - .combine(_documentList) { text, documents -> - documents.filter { document -> - document.name.uppercase().contains(text.trim().uppercase()) - } - }.stateIn( - scope = viewModelScope, - // It will allow the StateFlow survive 5 seconds before it been canceled - started = SharingStarted.WhileSubscribed(5000), - initialValue = _documentList.value, - ) - - suspend fun openDocument( - document: File, - isSivaConfirmed: Boolean, - ): Container? { - return try { - if (!document.isCryptoContainer()) { - openSignatureDocument(document, isSivaConfirmed) - } else { - openCryptoDocument(document) - } - } catch (e: Exception) { - errorLog(logTag, "Unable to open container", e) - _errorState.postValue(R.string.container_load_error) - return null - } - } - - @Throws(Exception::class) - suspend fun openSignatureDocument( - document: File, - isSivaConfirmed: Boolean, - ): SignedContainer { - val signedContainer = SignedContainer.openOrCreate(context, document, listOf(document), isSivaConfirmed) - if (sivaRepository.isTimestampedContainer(signedContainer) && - !signedContainer.isXades() - ) { - return sivaRepository.getTimestampedContainer(context, signedContainer, isSivaConfirmed) - } - - return SignedContainer.openOrCreate(context, document, listOf(document), isSivaConfirmed) - } - - @Throws(Exception::class) - suspend fun openCryptoDocument(document: File): CryptoContainer = - CryptoContainer.openOrCreate(context, document, listOf(document), cdoc2Settings) - - fun getRecentDocumentList(fileOpeningMethod: FileOpeningMethod = FileOpeningMethod.ALL): List = - ContainerUtil.findRecentContainerFiles(context, fileOpeningMethod) - - suspend fun handleDocument( - document: File, - mimeType: String, - confirmed: Boolean, - sharedContainerViewModel: SharedContainerViewModel, - ) { - val isCades = document.isCades(context) - val isAsicsOrConfirmedDdoc = mimeType == ASICS_MIMETYPE || (mimeType == DDOC_MIMETYPE && confirmed) - - if (isAsicsOrConfirmedDdoc || isCades) { - val signedContainer = openSignatureDocument(document, confirmed) - sharedContainerViewModel.setSignedContainer(signedContainer) - - handleSendToSigningViewWithSiva(true) - } - } - - fun handleSendToSigningViewWithSiva(sendToSigningView: Boolean) { - _sendToSigningViewWithSiva.postValue(sendToSigningView) - } - - suspend fun handleError( - logTag: String, - ex: Exception, - ) { - errorLog(logTag, "Unable to open container from recent documents", ex) - - var errorMessage: Int? = R.string.error_general_client - - withContext(Main) { - val exceptionMessage = ex.message ?: "" - if (ex is IOException && - exceptionMessage.isNotEmpty() && - exceptionMessage.contains("Online validation disabled") - ) { - errorMessage = null - return@withContext - } - - if (ex is NoInternetConnectionException) { - errorMessage = R.string.no_internet_connection - } - - _errorState.postValue(errorMessage) - } - } - - fun getMimetype(file: File): String? = mimeTypeResolver.mimeType(file) - - fun onSearchTextChange(text: String) { - _searchText.value = text - _documentList.value = getRecentDocumentList() - } - } diff --git a/app/src/main/res/drawable/ic_m3_cancel_48dp_wght400.xml b/app/src/main/res/drawable/ic_m3_cancel_48dp_wght400.xml new file mode 100644 index 000000000..1228cc1a4 --- /dev/null +++ b/app/src/main/res/drawable/ic_m3_cancel_48dp_wght400.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index e2ac9e0d7..cd9b614f1 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -134,18 +134,6 @@ Oled sulgemas ümbrikut. Palun vali, mida ümbrikuga edasi teha: Ümbriku teavitused - - Dokument - Otsi ümbrikku… - Katkesta ümbriku otsing - Hiljutisi ümbrikke ei leitud - Hiljutised ümbrikud - Ümbrikud puuduvad - Eemalda ümbrik - Soovid eemaldada ümbriku? - Katkesta ümbriku eemaldamine - Kinnita ümbriku eemaldamine - Seda tüüpi allkirjastatud dokument edastatakse digitaalallkirjade kehtivuse kontrollimiseks valideerimisteenusele SiVa. Digitaalallkirjade kehtivuse kontrollimisel edastatud andmete kohta loe lähemalt veebilehelt siit. @@ -192,6 +180,8 @@ Ümbriku dekrüpteerimine Dekrüpteerimise meetod + Krüptokonteineri salvestamine õnnestus + Ümbriku allkirjastamine Ümbriku nimi @@ -205,6 +195,8 @@ Allkiri lisatud Lisa allkiri + Allkirjastatud konteineri salvestamine õnnestus + Allkirjastamise viisiks on mobiil-ID, vahekaart %1$d / %2$d Allkirjastamise viisiks on Smart-ID, vahekaart %1$d / %2$d Allkirjastamise viisiks on ID-kaart, vahekaart %1$d / %2$d diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2ce3541fc..57f25e4a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -134,18 +134,6 @@ You are about to close the container. Please choose what to do with the container: Container notifications - - Document - Search container… - Cancel search container - No recent containers found - Recent containers - No recent containers - Remove container - Remove container? - Cancel container removal - Confirm container removal - This type of signed document will be transmitted to the Digital Signature Validation Service SiVa to verify the validity of the digital signature. Read more information about transmitted data to Digital Signature Validation Service on the website here. @@ -192,6 +180,8 @@ Container decryption Decryption method + Crypto container saved successfully + Container signing Container name @@ -205,6 +195,8 @@ Signature added Add signature + Signed container saved successfully + Signing option Mobile-ID, tab %1$d of %2$d Signing option Smart-ID, tab %1$d of %2$d Signing option ID-card, tab %1$d of %2$d diff --git a/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/repository/RecipientRepositoryImpl.kt b/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/repository/RecipientRepositoryImpl.kt index cbe205f23..6cffd8261 100644 --- a/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/repository/RecipientRepositoryImpl.kt +++ b/crypto-lib/src/main/kotlin/ee/ria/DigiDoc/cryptolib/repository/RecipientRepositoryImpl.kt @@ -98,7 +98,10 @@ class RecipientRepositoryImpl query: String, ): Pair, Int> { val configurationProvider = configurationRepository.getConfiguration() - + var exception: Exception = + CryptoException( + "No addressee found", + ) val ldapFilter = LdapFilter(query) if (ldapFilter.isPersonalCode(query)) { val ldapPersonUrls = configurationProvider?.ldapPersonUrls @@ -116,12 +119,19 @@ class RecipientRepositoryImpl count += countSearch } catch (e: NoInternetConnectionException) { errorLog(logTag, "Unable to connect to LDAP url: $ldapPersonUrl", e) - throw e + exception = e } catch (ce: CryptoException) { errorLog(logTag, "Unable to get certificates from LDAP url: $ldapPersonUrl", ce) - throw CryptoException("Unable to get certificates from LDAP url: $ldapPersonUrl", ce) + exception = + CryptoException( + "Unable to get certificates from LDAP url: $ldapPersonUrl", + ce, + ) } } + if (addressees.isEmpty()) { + throw exception + } return Pair(addressees, count) } else { val ldapCorpUrl = configurationProvider?.ldapCorpUrl?.split("://")[1] diff --git a/utils-lib/src/main/kotlin/ee/ria/DigiDoc/utilsLib/container/ContainerUtil.kt b/utils-lib/src/main/kotlin/ee/ria/DigiDoc/utilsLib/container/ContainerUtil.kt index 08d1ef4de..bc72af118 100644 --- a/utils-lib/src/main/kotlin/ee/ria/DigiDoc/utilsLib/container/ContainerUtil.kt +++ b/utils-lib/src/main/kotlin/ee/ria/DigiDoc/utilsLib/container/ContainerUtil.kt @@ -282,6 +282,36 @@ object ContainerUtil { return dir } + fun removeSignatureContainersDir(context: Context): Boolean { + val dir = File(context.filesDir, DIR_SIGNATURE_CONTAINERS) + return deleteDir(dir) + } + + fun removeCryptoContainersDir(context: Context): Boolean { + val dir = File(context.filesDir, DIR_CRYPTO_CONTAINERS) + return deleteDir(dir) + } + + private fun deleteDir(dir: File): Boolean { + if (!dir.exists()) { + debugLog(LOG_TAG, "Directory does not exist: ${dir.path}") + return true + } + + return try { + dir.deleteRecursively().also { success -> + if (success) { + debugLog(LOG_TAG, "Directory removed: ${dir.path}") + } else { + debugLog(LOG_TAG, "Failed to remove directory: ${dir.path}") + } + } + } catch (e: Exception) { + debugLog(LOG_TAG, "Error removing directory ${dir.path}: ${e.message}", e) + false + } + } + fun removeExtensionFromContainerFilename(filename: String): String = FilenameUtils.removeExtension(filename) fun addExtensionToContainerFilename(filename: String): String {