From bd770196c1ece3590f7a8d9fe93a3491cee457ac Mon Sep 17 00:00:00 2001 From: Boriss Melikjan Date: Fri, 16 Jan 2026 17:06:59 +0200 Subject: [PATCH 01/11] MOPPAND-1738 Crypto and Signed Container closing logic update and remove Recent Documents UI and logic. --- .../ee/ria/DigiDoc/utils/ConstantTest.kt | 2 - .../kotlin/ee/ria/DigiDoc/utils/RouteTest.kt | 6 - .../viewmodel/RecentDocumentsViewModelTest.kt | 265 -------- .../ee/ria/DigiDoc/RIADigiDocAppNavigation.kt | 28 - .../fragment/RecentDocumentsFragment.kt | 86 --- .../ria/DigiDoc/fragment/screen/HomeScreen.kt | 43 +- .../fragment/screen/RecentDocumentsScreen.kt | 581 ------------------ .../ui/component/crypto/EncryptNavigation.kt | 57 +- .../ui/component/menu/OpenMenuBottomSheet.kt | 95 --- .../ui/component/menu/TwoButtonMenu.kt | 62 -- .../DigiDoc/ui/component/shared/Document.kt | 157 ----- .../ui/component/signing/SigningNavigation.kt | 58 +- .../bottomsheet/ContainerBottomSheet.kt | 2 + .../kotlin/ee/ria/DigiDoc/utils/Constant.kt | 3 - .../main/kotlin/ee/ria/DigiDoc/utils/Route.kt | 9 - .../ria/DigiDoc/viewmodel/EncryptViewModel.kt | 8 +- .../viewmodel/RecentDocumentsViewModel.kt | 184 ------ app/src/main/res/values-et/strings.xml | 14 +- app/src/main/res/values/strings.xml | 14 +- 19 files changed, 92 insertions(+), 1582 deletions(-) delete mode 100644 app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt delete mode 100644 app/src/main/kotlin/ee/ria/DigiDoc/fragment/RecentDocumentsFragment.kt delete mode 100644 app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/RecentDocumentsScreen.kt delete mode 100644 app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/OpenMenuBottomSheet.kt delete mode 100644 app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/TwoButtonMenu.kt delete mode 100644 app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/Document.kt delete mode 100644 app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModel.kt 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/RIADigiDocAppNavigation.kt b/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt index bdeb3cfcc..41579d3fe 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 @@ -217,33 +216,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/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/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..cc6561e7c 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 @@ -189,7 +189,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.close_container) val saveContainerMessage = stringResource(id = R.string.container_save) val dismissRemoveFileDialog = { closeRemoveFileDialog() @@ -412,7 +412,7 @@ fun EncryptNavigation( } BackHandler { - if (!isNestedContainer && encryptViewModel.isEncryptedContainer(cryptoContainer)) { + if (!isNestedContainer && encryptViewModel.isSaveButtonShown(cryptoContainer)) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( @@ -572,7 +572,7 @@ fun EncryptNavigation( }, leftIconContentDescription = R.string.crypto_close_container_title, onLeftButtonClick = { - if (!isNestedContainer && encryptViewModel.isEncryptedContainer(cryptoContainer)) { + if (!isNestedContainer && encryptViewModel.isSaveButtonShown(cryptoContainer)) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( @@ -1052,13 +1052,7 @@ fun EncryptNavigation( !encryptViewModel.isEncryptedContainer(cryptoContainer) && !encryptViewModel.isDecryptedContainer(cryptoContainer), openEditContainerNameDialog = openEditContainerNameDialog, - isSaveButtonShown = ( - encryptViewModel.isEncryptedContainer(cryptoContainer) || - ( - encryptViewModel.isDecryptedContainer(cryptoContainer) && - cryptoContainer?.hasRecipients() == true - ) - ), + isSaveButtonShown = encryptViewModel.isSaveButtonShown(cryptoContainer), cryptoContainer = cryptoContainer, saveFileLauncher = saveFileLauncher, saveFile = saveFile, @@ -1082,36 +1076,49 @@ fun EncryptNavigation( } if (showContainerCloseConfirmationDialog.value) { + val isSaveContainerShown = encryptViewModel.isSaveButtonShown(cryptoContainer) + val dismissIcon = + if (isSaveContainerShown) { + R.drawable.ic_m3_download_48dp_wght400 + } else { + R.drawable.ic_m3_cancel_48dp_wght400 + } + val dismissButtonText = + if (isSaveContainerShown) { + 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_title), 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) { + 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) }, 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..0d8efcd54 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 @@ -182,6 +182,8 @@ fun SigningNavigation( val isCadesContainer = signedContainer?.isCades() == true val isDdocContainer = signedContainer?.isDdoc() == true var isEmptyFileInContainer by remember { mutableStateOf(false) } + val isSignedContainer = signedContainer?.isSigned() == true + val isSaveContainerShown = remember { mutableStateOf(isSignedContainer) } var validSignaturesCount by remember { mutableIntStateOf(0) } var unknownSignaturesCount by remember { mutableIntStateOf(0) } @@ -224,7 +226,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.close_container) val saveContainerMessage = stringResource(id = R.string.container_save) val dismissRemoveFileDialog = { closeRemoveFileDialog() @@ -464,7 +466,7 @@ fun SigningNavigation( } BackHandler { - if (!isNestedContainer) { + if (!isNestedContainer && isSaveContainerShown.value == true) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( @@ -614,6 +616,9 @@ fun SigningNavigation( if (newTime >= (pastTime + 2 * 1000)) { sendAccessibilityEvent(context, getAccessibilityEventType(), signaturesLoaded) } + if (isSaveContainerShown.value != true) { + isSaveContainerShown.value = signatures.count() > 0 + } } } @@ -710,7 +715,7 @@ fun SigningNavigation( }, leftIconContentDescription = R.string.signing_close_container_title, onLeftButtonClick = { - if (!isNestedContainer) { + if (!isNestedContainer && isSaveContainerShown.value == true) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( @@ -805,6 +810,7 @@ fun SigningNavigation( ) } } + isSaveContainerShown.value = true showMessage(signatureAddedSuccessText, SnackbarType.SUCCESS) signatureAddedSuccess.value = false } @@ -1053,6 +1059,7 @@ fun SigningNavigation( ) } openEditContainerNameDialog.value = false + isSaveContainerShown.value = true sendAccessibilityEvent( context, getAccessibilityEventType(), @@ -1114,6 +1121,7 @@ fun SigningNavigation( } } } + isSaveContainerShown.value = true closeRemoveFileDialog() sendAccessibilityEvent(context, getAccessibilityEventType(), fileRemoved) }, @@ -1158,6 +1166,7 @@ fun SigningNavigation( actionSignature, ) } + isSaveContainerShown.value = true closeSignatureDialog() sendAccessibilityEvent(context, getAccessibilityEventType(), signatureRemoved) }, @@ -1203,6 +1212,7 @@ fun SigningNavigation( ContainerBottomSheet( modifier = modifier, showSheet = showContainerBottomSheet, + isSaveButtonShown = isSaveContainerShown.value, isEditContainerButtonShown = signingViewModel.isBottomContainerButtonShown( signedContainer, @@ -1256,36 +1266,48 @@ fun SigningNavigation( } if (showContainerCloseConfirmationDialog.value) { + val dismissIcon = + if (isSaveContainerShown.value == true) { + R.drawable.ic_m3_download_48dp_wght400 + } else { + R.drawable.ic_m3_cancel_48dp_wght400 + } + val dismissButtonText = + if (isSaveContainerShown.value == true) { + 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_title), 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 == true) { + 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) 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..750a9f81b 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..a7ce03d6c 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/utils/Constant.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/utils/Constant.kt @@ -47,9 +47,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..1fdea1520 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.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 @@ -95,12 +92,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..3f1aeb5cd 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,11 @@ class EncryptViewModel ) && isDataFilesInContainer(cryptoContainer) + fun isSaveButtonShown(cryptoContainer: CryptoContainer?): Boolean { + return (isEncryptedContainer(cryptoContainer) || (isDecryptedContainer(cryptoContainer) + && cryptoContainer?.hasRecipients() == true)) + } + fun isSignButtonShown( cryptoContainer: CryptoContainer?, isNestedContainer: Boolean, 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/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index e2ac9e0d7..dc5ea6e80 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -44,7 +44,9 @@ Fail salvestatud Faili salvestamine ebaõnnestus Eemalda + Sulge Eemalda ümbrik + Sulge ümbrik Eemalda fail Salvesta fail Soovid eemaldada faili ümbrikust? @@ -134,18 +136,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. diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2ce3541fc..e31db8bc7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -44,7 +44,9 @@ File saved Failed to save file Remove + Close Remove container + Close container Remove file Save file Remove file from container? @@ -134,18 +136,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. From f0f8fdd9ac901aaf453529df3ce527ffd5652a3b Mon Sep 17 00:00:00 2001 From: Boriss Melikjan Date: Mon, 19 Jan 2026 16:20:00 +0200 Subject: [PATCH 02/11] MOPPAND-1738 KtLint fix. --- .../bottomsheet/ContainerBottomSheet.kt | 2 +- .../ria/DigiDoc/viewmodel/EncryptViewModel.kt | 26 ++++++------------- 2 files changed, 9 insertions(+), 19 deletions(-) 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 750a9f81b..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,7 +37,7 @@ import java.io.File fun ContainerBottomSheet( modifier: Modifier, showSheet: MutableState, - isSaveButtonShown:Boolean = false, + isSaveButtonShown: Boolean = false, isEditContainerButtonShown: Boolean = true, openEditContainerNameDialog: MutableState, isEncryptButtonShown: Boolean = true, 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 3f1aeb5cd..e92b17450 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptViewModel.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptViewModel.kt @@ -89,10 +89,14 @@ class EncryptViewModel ) && isDataFilesInContainer(cryptoContainer) - fun isSaveButtonShown(cryptoContainer: CryptoContainer?): Boolean { - return (isEncryptedContainer(cryptoContainer) || (isDecryptedContainer(cryptoContainer) - && cryptoContainer?.hasRecipients() == true)) - } + fun isSaveButtonShown(cryptoContainer: CryptoContainer?): Boolean = + ( + isEncryptedContainer(cryptoContainer) || + ( + isDecryptedContainer(cryptoContainer) && + cryptoContainer?.hasRecipients() == true + ) + ) fun isSignButtonShown( cryptoContainer: CryptoContainer?, @@ -152,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, From 3e24f52ca1d5bcebfea720f0eafd197fa44212db Mon Sep 17 00:00:00 2001 From: Boriss Melikjan Date: Tue, 20 Jan 2026 11:46:37 +0200 Subject: [PATCH 03/11] MOPPAND-1738 Remove duplicate strings and clean up container folders on app start and on closing of container. --- .../kotlin/ee/ria/DigiDoc/MainActivity.kt | 4 +++ .../ui/component/crypto/EncryptNavigation.kt | 8 +++-- .../ui/component/signing/SigningNavigation.kt | 8 +++-- app/src/main/res/values-et/strings.xml | 2 -- app/src/main/res/values/strings.xml | 2 -- .../utilsLib/container/ContainerUtil.kt | 30 +++++++++++++++++++ 6 files changed, 46 insertions(+), 8 deletions(-) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt b/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt index 78ea19658..735412475 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 @@ -179,6 +180,9 @@ class MainActivity : } } } + + ContainerUtil.removeSignatureContainersDir(applicationContext) + ContainerUtil.removeCryptoContainersDir(applicationContext) } override fun attachBaseContext(newBase: Context?) { 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 cc6561e7c..03f8873ac 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 @@ -189,7 +189,7 @@ fun EncryptNavigation( stringResource(id = R.string.document_remove_last_confirmation_message) } val closeContainerMessage = stringResource(id = R.string.crypto_close_container_message) - val confirmCloseContainerMessage = stringResource(id = R.string.close_container) + val confirmCloseContainerMessage = stringResource(id = R.string.crypto_close_container_title) val saveContainerMessage = stringResource(id = R.string.container_save) val dismissRemoveFileDialog = { closeRemoveFileDialog() @@ -1097,7 +1097,7 @@ fun EncryptNavigation( dismissIcon = dismissIcon, confirmIcon = R.drawable.ic_m3_delete_48dp_wght400, dismissButtonText = dismissButtonText, - confirmButtonText = stringResource(R.string.close_title), + confirmButtonText = stringResource(R.string.close_button), dismissButtonContentDescription = saveContainerMessage, confirmButtonContentDescription = confirmCloseContainerMessage, onDismissRequest = { @@ -1119,6 +1119,10 @@ fun EncryptNavigation( }, onConfirmButton = { showContainerCloseConfirmationDialog.value = false + val containerFile = cryptoContainer?.file + if (containerFile?.exists() == true) { + containerFile.delete() + } sharedContainerViewModel.resetCryptoContainer() handleBackButtonClick(navController, encryptViewModel, sharedContainerViewModel) }, 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 0d8efcd54..b2fa024b0 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 @@ -226,7 +226,7 @@ fun SigningNavigation( stringResource(id = R.string.document_remove_last_confirmation_message) } val closeContainerMessage = stringResource(id = R.string.signing_close_container_message) - val confirmCloseContainerMessage = stringResource(id = R.string.close_container) + val confirmCloseContainerMessage = stringResource(id = R.string.signing_close_container_title) val saveContainerMessage = stringResource(id = R.string.container_save) val dismissRemoveFileDialog = { closeRemoveFileDialog() @@ -1286,7 +1286,7 @@ fun SigningNavigation( dismissIcon = dismissIcon, confirmIcon = R.drawable.ic_m3_delete_48dp_wght400, dismissButtonText = dismissButtonText, - confirmButtonText = stringResource(R.string.close_title), + confirmButtonText = stringResource(R.string.close_button), dismissButtonContentDescription = saveContainerMessage, confirmButtonContentDescription = confirmCloseContainerMessage, onDismissRequest = { @@ -1308,6 +1308,10 @@ fun SigningNavigation( }, onConfirmButton = { showContainerCloseConfirmationDialog.value = false + val containerFile = signedContainer?.getContainerFile() + if (containerFile?.exists() == true) { + containerFile.delete() + } sharedContainerViewModel.resetSignedContainer() sharedContainerViewModel.resetContainerNotifications() handleBackButtonClick(navController, signingViewModel, sharedContainerViewModel) diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index dc5ea6e80..1d1eb5e15 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -44,9 +44,7 @@ Fail salvestatud Faili salvestamine ebaõnnestus Eemalda - Sulge Eemalda ümbrik - Sulge ümbrik Eemalda fail Salvesta fail Soovid eemaldada faili ümbrikust? diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e31db8bc7..a44914da4 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -44,9 +44,7 @@ File saved Failed to save file Remove - Close Remove container - Close container Remove file Save file Remove file from container? 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..aae329f97 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 deleteDirSafely(dir) + } + + fun removeCryptoContainersDir(context: Context): Boolean { + val dir = File(context.filesDir, DIR_CRYPTO_CONTAINERS) + return deleteDirSafely(dir) + } + + private fun deleteDirSafely(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 { From 36d0a217c06bf29d5b100786b03f66266c6a9763 Mon Sep 17 00:00:00 2001 From: Boriss Melikjan Date: Tue, 20 Jan 2026 13:45:48 +0200 Subject: [PATCH 04/11] MOPPAND-1738 Code review suggestions. --- app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt | 6 +++--- .../ee/ria/DigiDoc/utilsLib/container/ContainerUtil.kt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt b/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt index 735412475..1f56d216a 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt @@ -168,6 +168,9 @@ class MainActivity : fileTypeSetup.initializeApplicationFileTypesAssociation(componentClassName) librarySetup.setupLibraries(applicationContext, isLoggingEnabled) + ContainerUtil.removeSignatureContainersDir(applicationContext) + ContainerUtil.removeCryptoContainersDir(applicationContext) + isAppReady = true setContent { @@ -180,9 +183,6 @@ class MainActivity : } } } - - ContainerUtil.removeSignatureContainersDir(applicationContext) - ContainerUtil.removeCryptoContainersDir(applicationContext) } override fun attachBaseContext(newBase: Context?) { 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 aae329f97..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 @@ -284,15 +284,15 @@ object ContainerUtil { fun removeSignatureContainersDir(context: Context): Boolean { val dir = File(context.filesDir, DIR_SIGNATURE_CONTAINERS) - return deleteDirSafely(dir) + return deleteDir(dir) } fun removeCryptoContainersDir(context: Context): Boolean { val dir = File(context.filesDir, DIR_CRYPTO_CONTAINERS) - return deleteDirSafely(dir) + return deleteDir(dir) } - private fun deleteDirSafely(dir: File): Boolean { + private fun deleteDir(dir: File): Boolean { if (!dir.exists()) { debugLog(LOG_TAG, "Directory does not exist: ${dir.path}") return true From e182672d511f6b97420d6bec6e35b330a7a7f2bf Mon Sep 17 00:00:00 2001 From: Boriss Melikjan Date: Thu, 22 Jan 2026 16:24:22 +0200 Subject: [PATCH 05/11] MOPPAND-1738 Fixes after testing phase 1 and fix LDAP search in case of error with one of search providers. --- .../ee/ria/DigiDoc/RIADigiDocAppNavigation.kt | 10 +++ .../ria/DigiDoc/fragment/EncryptFragment.kt | 2 + .../fragment/screen/EncryptRecipientScreen.kt | 4 +- .../DigiDoc/fragment/screen/EncryptScreen.kt | 2 + .../ui/component/crypto/EncryptNavigation.kt | 78 ++++++++++++++----- .../ui/component/signing/SigningNavigation.kt | 69 ++++++++++------ .../kotlin/ee/ria/DigiDoc/utils/Constant.kt | 1 + .../main/kotlin/ee/ria/DigiDoc/utils/Route.kt | 3 + .../repository/RecipientRepositoryImpl.kt | 16 +++- 9 files changed, 136 insertions(+), 49 deletions(-) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt b/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt index 41579d3fe..217d47a66 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt @@ -185,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(), 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/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/ui/component/crypto/EncryptNavigation.kt b/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/crypto/EncryptNavigation.kt index 03f8873ac..5110b9df3 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 @@ -117,6 +117,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 +150,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 +164,8 @@ fun EncryptNavigation( val clickedRecipient = remember { mutableStateOf(null) } val isNestedContainer = sharedContainerViewModel.isNestedContainer(cryptoContainer) + val isSaveContainerShown = rememberSaveable { mutableStateOf(false) } + val isWithEncryptionHandled = rememberSaveable { mutableStateOf(false) } val containerEncryptedSuccess = remember { mutableStateOf(false) } val containerEncryptedSuccessText = stringResource(id = R.string.crypto_create_success) @@ -412,9 +416,11 @@ fun EncryptNavigation( } BackHandler { - if (!isNestedContainer && encryptViewModel.isSaveButtonShown(cryptoContainer)) { + if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { + ContainerUtil.removeCryptoContainersDir(context) + handleBackButtonClick( navController, encryptViewModel, @@ -436,6 +442,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 +466,7 @@ fun EncryptNavigation( delay(500) encryptionButtonEnabled.value = true + isSaveContainerShown.value = true } } } @@ -490,14 +504,22 @@ fun EncryptNavigation( LaunchedEffect(isSaved) { if (isSaved) { + isSaveContainerShown.value = false if (showContainerCloseConfirmationDialog.value) { showContainerCloseConfirmationDialog.value = false + + val containerFile = cryptoContainer?.file + if (containerFile?.exists() == true) { + containerFile.delete() + } + handleBackButtonClick( navController, encryptViewModel, sharedContainerViewModel, ) } + @Suppress("AssignedValueIsNeverRead") isSaved = false } } @@ -505,8 +527,9 @@ fun EncryptNavigation( LaunchedEffect(sharedContainerViewModel.decryptNFCStatus) { sharedContainerViewModel.decryptNFCStatus.asFlow().collect { status -> status?.let { - if (status == true) { + if (status) { withContext(Main) { + isSaveContainerShown.value = true containerDecryptedSuccess.value = true sendAccessibilityEvent( context, @@ -525,8 +548,9 @@ fun EncryptNavigation( LaunchedEffect(sharedContainerViewModel.decryptIDCardStatus) { sharedContainerViewModel.decryptIDCardStatus.asFlow().collect { status -> status?.let { - if (status == true) { + if (status) { withContext(Main) { + isSaveContainerShown.value = true containerDecryptedSuccess.value = true sendAccessibilityEvent( context, @@ -572,9 +596,11 @@ fun EncryptNavigation( }, leftIconContentDescription = R.string.crypto_close_container_title, onLeftButtonClick = { - if (!isNestedContainer && encryptViewModel.isSaveButtonShown(cryptoContainer)) { + if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { + ContainerUtil.removeCryptoContainersDir(context) + handleBackButtonClick( navController, encryptViewModel, @@ -651,14 +677,16 @@ 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 + isSaveContainerShown.value = true } if (encryptViewModel.isEmptyFileInContainer(cryptoContainer) && @@ -751,7 +779,21 @@ fun EncryptNavigation( } }, onMoreOptionsActionButtonClick = { - showContainerBottomSheet.value = true + val isEditContainerButtonShown = ( + !isNestedContainer && + !encryptViewModel.isEncryptedContainer(cryptoContainer) && + !encryptViewModel.isDecryptedContainer(cryptoContainer) + ) + val isSignButtonShown = ( + !isNestedContainer && + encryptViewModel.isEncryptedContainer(cryptoContainer) + ) + val result = ( + isSaveContainerShown.value || + isEditContainerButtonShown || + isSignButtonShown + ) + showContainerBottomSheet.value = (result) }, ) } @@ -881,9 +923,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, @@ -939,6 +983,8 @@ fun EncryptNavigation( if ((cryptoContainer?.dataFiles?.size ?: 0) == 1) { cryptoContainer?.file?.delete() sharedContainerViewModel.resetCryptoContainer() + + ContainerUtil.removeCryptoContainersDir(context) handleBackButtonClick( navController, encryptViewModel, @@ -1043,7 +1089,6 @@ fun EncryptNavigation( ) }, ) - EncryptContainerBottomSheet( modifier = modifier, showSheet = showContainerBottomSheet, @@ -1052,7 +1097,7 @@ fun EncryptNavigation( !encryptViewModel.isEncryptedContainer(cryptoContainer) && !encryptViewModel.isDecryptedContainer(cryptoContainer), openEditContainerNameDialog = openEditContainerNameDialog, - isSaveButtonShown = encryptViewModel.isSaveButtonShown(cryptoContainer), + isSaveButtonShown = isSaveContainerShown.value, cryptoContainer = cryptoContainer, saveFileLauncher = saveFileLauncher, saveFile = saveFile, @@ -1076,15 +1121,14 @@ fun EncryptNavigation( } if (showContainerCloseConfirmationDialog.value) { - val isSaveContainerShown = encryptViewModel.isSaveButtonShown(cryptoContainer) val dismissIcon = - if (isSaveContainerShown) { + if (isSaveContainerShown.value) { R.drawable.ic_m3_download_48dp_wght400 } else { R.drawable.ic_m3_cancel_48dp_wght400 } val dismissButtonText = - if (isSaveContainerShown) { + if (isSaveContainerShown.value) { stringResource(R.string.save) } else { stringResource(R.string.cancel_button) @@ -1104,7 +1148,7 @@ fun EncryptNavigation( showContainerCloseConfirmationDialog.value = false }, onDismissButton = { - if (isSaveContainerShown) { + if (isSaveContainerShown.value) { val file = cryptoContainer?.file if (file != null) { saveFile( @@ -1119,10 +1163,7 @@ fun EncryptNavigation( }, onConfirmButton = { showContainerCloseConfirmationDialog.value = false - val containerFile = cryptoContainer?.file - if (containerFile?.exists() == true) { - containerFile.delete() - } + ContainerUtil.removeCryptoContainersDir(context) sharedContainerViewModel.resetCryptoContainer() handleBackButtonClick(navController, encryptViewModel, sharedContainerViewModel) }, @@ -1141,8 +1182,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) 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 b2fa024b0..b969c0e18 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,8 +183,7 @@ fun SigningNavigation( val isCadesContainer = signedContainer?.isCades() == true val isDdocContainer = signedContainer?.isDdoc() == true var isEmptyFileInContainer by remember { mutableStateOf(false) } - val isSignedContainer = signedContainer?.isSigned() == true - val isSaveContainerShown = remember { mutableStateOf(isSignedContainer) } + val isSaveContainerShown = rememberSaveable { mutableStateOf(false) } var validSignaturesCount by remember { mutableIntStateOf(0) } var unknownSignaturesCount by remember { mutableIntStateOf(0) } @@ -466,9 +466,11 @@ fun SigningNavigation( } BackHandler { - if (!isNestedContainer && isSaveContainerShown.value == true) { + if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { + ContainerUtil.removeSignatureContainersDir(context) + handleBackButtonClick( navController, signingViewModel, @@ -528,7 +530,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 @@ -545,7 +547,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 @@ -616,9 +618,6 @@ fun SigningNavigation( if (newTime >= (pastTime + 2 * 1000)) { sendAccessibilityEvent(context, getAccessibilityEventType(), signaturesLoaded) } - if (isSaveContainerShown.value != true) { - isSaveContainerShown.value = signatures.count() > 0 - } } } @@ -628,6 +627,7 @@ fun SigningNavigation( val announcementText = when { unknownSignaturesCount == 0 && invalidSignaturesCount == 0 -> { + @Suppress("AssignedValueIsNeverRead") validSignaturesCount = signatures.size "$containerHasText, ${validSignaturesText.lowercase()}" } @@ -640,6 +640,7 @@ fun SigningNavigation( } delay(1000) + @Suppress("AssignedValueIsNeverRead") isSignaturesCountLoaded = true sendAccessibilityEvent( context, @@ -660,14 +661,18 @@ fun SigningNavigation( LaunchedEffect(isSaved) { if (isSaved) { + isSaveContainerShown.value = false if (showContainerCloseConfirmationDialog.value) { showContainerCloseConfirmationDialog.value = false + ContainerUtil.removeSignatureContainersDir(context) + handleBackButtonClick( navController, signingViewModel, sharedContainerViewModel, ) } + @Suppress("AssignedValueIsNeverRead") isSaved = false } } @@ -715,9 +720,11 @@ fun SigningNavigation( }, leftIconContentDescription = R.string.signing_close_container_title, onLeftButtonClick = { - if (!isNestedContainer && isSaveContainerShown.value == true) { + if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { + ContainerUtil.removeSignatureContainersDir(context) + handleBackButtonClick( navController, signingViewModel, @@ -884,7 +891,21 @@ fun SigningNavigation( }, onRightActionButtonClick = onEncryptActionClick, onMoreOptionsActionButtonClick = { - showContainerBottomSheet.value = true + val isEditContainerButtonShown = + signingViewModel.isBottomContainerButtonShown( + signedContainer, + isNestedContainer, + ) + val isEncryptButtonShown = + signingViewModel.isEncryptButtonShown( + signedContainer, + isNestedContainer, + ) + showContainerBottomSheet.value = ( + isSaveContainerShown.value || + isEditContainerButtonShown || + isEncryptButtonShown + ) }, ) } @@ -969,10 +990,10 @@ fun SigningNavigation( timestamps, showSignaturesLoadingIndicator.value, signaturesLoading, - true, - false, - onSignatureMoreClick, - onSignatureMoreClick, + showNameAsAllCaps = true, + isDdocValid = false, + onClick = onSignatureMoreClick, + onClickMore = onSignatureMoreClick, ) } } @@ -1046,9 +1067,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, @@ -1106,6 +1129,7 @@ fun SigningNavigation( signedContainer?.getContainerFile()?.delete() sharedContainerViewModel.resetSignedContainer() sharedContainerViewModel.resetContainerNotifications() + ContainerUtil.removeSignatureContainersDir(context) handleBackButtonClick(navController, signingViewModel, sharedContainerViewModel) } else { scope.launch(IO) { @@ -1208,7 +1232,6 @@ fun SigningNavigation( ) }, ) - ContainerBottomSheet( modifier = modifier, showSheet = showContainerBottomSheet, @@ -1220,7 +1243,7 @@ fun SigningNavigation( ), openEditContainerNameDialog = openEditContainerNameDialog, isEncryptButtonShown = - signingViewModel.isBottomContainerButtonShown( + signingViewModel.isEncryptButtonShown( signedContainer, isNestedContainer, ), @@ -1267,13 +1290,13 @@ fun SigningNavigation( if (showContainerCloseConfirmationDialog.value) { val dismissIcon = - if (isSaveContainerShown.value == true) { + if (isSaveContainerShown.value) { R.drawable.ic_m3_download_48dp_wght400 } else { R.drawable.ic_m3_cancel_48dp_wght400 } val dismissButtonText = - if (isSaveContainerShown.value == true) { + if (isSaveContainerShown.value) { stringResource(R.string.save) } else { stringResource(R.string.cancel_button) @@ -1293,7 +1316,7 @@ fun SigningNavigation( showContainerCloseConfirmationDialog.value = false }, onDismissButton = { - if (isSaveContainerShown.value == true) { + if (isSaveContainerShown.value) { val file = signedContainer?.getContainerFile() if (file != null) { saveFile( @@ -1308,10 +1331,7 @@ fun SigningNavigation( }, onConfirmButton = { showContainerCloseConfirmationDialog.value = false - val containerFile = signedContainer?.getContainerFile() - if (containerFile?.exists() == true) { - containerFile.delete() - } + ContainerUtil.removeSignatureContainersDir(context) sharedContainerViewModel.resetSignedContainer() sharedContainerViewModel.resetContainerNotifications() handleBackButtonClick(navController, signingViewModel, sharedContainerViewModel) @@ -1352,8 +1372,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) 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 a7ce03d6c..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" 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 1fdea1520..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 @@ -78,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) 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] From 1bc63422dcefe9632e6846f42e61853852374fe9 Mon Sep 17 00:00:00 2001 From: Boriss Melikjan Date: Mon, 26 Jan 2026 16:08:00 +0200 Subject: [PATCH 06/11] MOPPAND-1738 Do not offer to save container when decrypted. --- .../ee/ria/DigiDoc/ui/component/crypto/EncryptNavigation.kt | 3 --- 1 file changed, 3 deletions(-) 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 5110b9df3..b4c25c7cd 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 @@ -529,7 +529,6 @@ fun EncryptNavigation( status?.let { if (status) { withContext(Main) { - isSaveContainerShown.value = true containerDecryptedSuccess.value = true sendAccessibilityEvent( context, @@ -550,7 +549,6 @@ fun EncryptNavigation( status?.let { if (status) { withContext(Main) { - isSaveContainerShown.value = true containerDecryptedSuccess.value = true sendAccessibilityEvent( context, @@ -686,7 +684,6 @@ fun EncryptNavigation( if (containerDecryptedSuccess.value) { showMessage(containerDecryptedSuccessText, SnackbarType.SUCCESS) containerDecryptedSuccess.value = false - isSaveContainerShown.value = true } if (encryptViewModel.isEmptyFileInContainer(cryptoContainer) && From 4060df9cb872f27497a4d4b9ea75eba648d0bd2c Mon Sep 17 00:00:00 2001 From: Boriss Melikjan Date: Tue, 27 Jan 2026 16:25:06 +0200 Subject: [PATCH 07/11] MOPPAND-1738 Empty file in container message flicker upon closing decrypted container fix. --- .../ria/DigiDoc/ui/component/crypto/EncryptNavigation.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 b4c25c7cd..7c011a2be 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 @@ -508,10 +508,7 @@ fun EncryptNavigation( if (showContainerCloseConfirmationDialog.value) { showContainerCloseConfirmationDialog.value = false - val containerFile = cryptoContainer?.file - if (containerFile?.exists() == true) { - containerFile.delete() - } + ContainerUtil.removeCryptoContainersDir(context) handleBackButtonClick( navController, @@ -687,7 +684,8 @@ fun EncryptNavigation( } if (encryptViewModel.isEmptyFileInContainer(cryptoContainer) && - !encryptViewModel.isEncryptedContainer(cryptoContainer) + !encryptViewModel.isEncryptedContainer(cryptoContainer) && + !encryptViewModel.isDecryptedContainer(cryptoContainer) ) { showMessage(emptyFileInContainerText) } From f81c0e41f959224281ea195624ddc401038aff6c Mon Sep 17 00:00:00 2001 From: Boriss Melikjan Date: Wed, 28 Jan 2026 16:48:36 +0200 Subject: [PATCH 08/11] MOPPAND-1738 Success message upon saving Crypto and Signed container. --- .../ee/ria/DigiDoc/fragment/screen/HomeScreen.kt | 11 +++++++++++ .../ui/component/crypto/EncryptNavigation.kt | 16 +++++++++++++++- .../ui/component/signing/SigningNavigation.kt | 10 +++++++++- app/src/main/res/values-et/strings.xml | 4 ++++ app/src/main/res/values/strings.xml | 4 ++++ 5 files changed, 43 insertions(+), 2 deletions(-) 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 c2f9bc597..7d71d3b0a 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 @@ -53,6 +53,7 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -79,6 +80,8 @@ import ee.ria.DigiDoc.ui.theme.Dimensions.XSPadding import ee.ria.DigiDoc.ui.theme.Dimensions.iconSizeM import ee.ria.DigiDoc.ui.theme.RIADigiDocTheme import ee.ria.DigiDoc.utils.Route +import ee.ria.DigiDoc.utils.snackbar.SnackBarManager +import ee.ria.DigiDoc.utils.snackbar.SnackBarManager.showMessage import ee.ria.DigiDoc.viewmodel.HomeViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedMenuViewModel import kotlinx.coroutines.CoroutineScope @@ -93,6 +96,7 @@ fun HomeScreen( sharedMenuViewModel: SharedMenuViewModel, homeViewModel: HomeViewModel = hiltViewModel(), ) { + val context = LocalContext.current val activity = LocalActivity.current val openCrashDetectorDialog = rememberSaveable { mutableStateOf(false) } val hasUnsentReports by homeViewModel.hasUnsentReports.asFlow().collectAsState(Tasks.forResult(false)) @@ -106,6 +110,13 @@ fun HomeScreen( activity?.finish() } + LaunchedEffect(snackbarMessage) { + snackbarMessage?.let { message -> + showMessage(context, message) + savedStateHandle?.set("snackbar_message", null) + } + } + LaunchedEffect(homeViewModel.didAppCrashOnPreviousExecution(), hasUnsentReports) { if (!homeViewModel.isCrashSendingAlwaysEnabled() && hasUnsentReports.result) { openCrashDetectorDialog.value = true 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 7c011a2be..4124073af 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 @@ -407,7 +407,13 @@ fun EncryptNavigation( sharedContainerViewModel.saveContainerFile(file, result) showMessage(context, R.string.file_saved, SnackbarType.SUCCESS) isSaved = true - } ?: showMessage(context, R.string.file_saved_error) + } ?: run { + cryptoContainer?.file?.let { + sharedContainerViewModel.saveContainerFile(it, result) + showMessage(context, R.string.crypto_saved_container_success, SnackbarType.SUCCESS) + isSaved = true + } ?: showMessage(context, R.string.file_saved_error) + } } catch (_: Exception) { showMessage(context, R.string.file_saved_error) } @@ -514,6 +520,7 @@ fun EncryptNavigation( navController, encryptViewModel, sharedContainerViewModel, + true, ) } @Suppress("AssignedValueIsNeverRead") @@ -1172,6 +1179,7 @@ private fun handleBackButtonClick( navController: NavHostController, encryptViewModel: EncryptViewModel, sharedContainerViewModel: SharedContainerViewModel, + containerSavedSuccess: Boolean = false, ) { sharedContainerViewModel.resetExternalFileUris() sharedContainerViewModel.resetIsSivaConfirmed() @@ -1191,6 +1199,12 @@ private fun handleBackButtonClick( } else { sharedContainerViewModel.clearContainers() encryptViewModel.handleBackButton() + + if (containerSavedSuccess) { + navController.previousBackStackEntry + ?.savedStateHandle + ?.set("snackbar_message", R.string.crypto_saved_container_success) + } navController.navigateUp() } } 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 b969c0e18..5327183b2 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 @@ -455,7 +455,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) } @@ -670,6 +670,7 @@ fun SigningNavigation( navController, signingViewModel, sharedContainerViewModel, + true, ) } @Suppress("AssignedValueIsNeverRead") @@ -1367,6 +1368,7 @@ private fun handleBackButtonClick( navController: NavHostController, signingViewModel: SigningViewModel, sharedContainerViewModel: SharedContainerViewModel, + containerSavedSuccess: Boolean = false, ) { sharedContainerViewModel.resetExternalFileUris() sharedContainerViewModel.resetIsSivaConfirmed() @@ -1386,6 +1388,12 @@ private fun handleBackButtonClick( } else { sharedContainerViewModel.clearContainers() signingViewModel.handleBackButton() + + if (containerSavedSuccess) { + navController.previousBackStackEntry + ?.savedStateHandle + ?.set("snackbar_message", R.string.signature_saved_container_success) + } navController.navigateUp() } } diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml index 1d1eb5e15..cd9b614f1 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -180,6 +180,8 @@ Ümbriku dekrüpteerimine Dekrüpteerimise meetod + Krüptokonteineri salvestamine õnnestus + Ümbriku allkirjastamine Ümbriku nimi @@ -193,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 a44914da4..219da4fa2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -180,6 +180,8 @@ Container decryption Decryption method + Successfully saved crypto container + Container signing Container name @@ -193,6 +195,8 @@ Signature added Add signature + Successfully saved signed container + 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 From 0582e9aadbad3f725d208387001f317be7bb05d3 Mon Sep 17 00:00:00 2001 From: Boriss Melikjan Date: Mon, 2 Feb 2026 15:29:50 +0200 Subject: [PATCH 09/11] MOPPAND-1738 Container saved en text update. --- app/src/main/res/values/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 219da4fa2..57f25e4a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -180,7 +180,7 @@ Container decryption Decryption method - Successfully saved crypto container + Crypto container saved successfully Container signing @@ -195,7 +195,7 @@ Signature added Add signature - Successfully saved signed container + Signed container saved successfully Signing option Mobile-ID, tab %1$d of %2$d Signing option Smart-ID, tab %1$d of %2$d From 7358abb8ca03b39ef7cddfa06c9b3be4aff0a0ca Mon Sep 17 00:00:00 2001 From: Marten Rebane Date: Tue, 30 Jun 2026 18:07:35 +0300 Subject: [PATCH 10/11] Fix white screen --- app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt | 6 ++++-- .../DigiDoc/fragment/screen/AdvancedSettingsScreen.kt | 2 +- .../kotlin/ee/ria/DigiDoc/fragment/screen/HomeScreen.kt | 8 -------- app/src/main/res/drawable/ic_m3_cancel_48dp_wght400.xml | 9 +++++++++ 4 files changed, 14 insertions(+), 11 deletions(-) create mode 100644 app/src/main/res/drawable/ic_m3_cancel_48dp_wght400.xml diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt b/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt index 1f56d216a..634e4ba20 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/MainActivity.kt @@ -168,8 +168,10 @@ class MainActivity : fileTypeSetup.initializeApplicationFileTypesAssociation(componentClassName) librarySetup.setupLibraries(applicationContext, isLoggingEnabled) - ContainerUtil.removeSignatureContainersDir(applicationContext) - ContainerUtil.removeCryptoContainersDir(applicationContext) + if (savedInstanceState == null) { + ContainerUtil.removeSignatureContainersDir(applicationContext) + ContainerUtil.removeCryptoContainersDir(applicationContext) + } isAppReady = true 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/HomeScreen.kt b/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/HomeScreen.kt index 7d71d3b0a..f36ac9fd5 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 @@ -80,7 +80,6 @@ import ee.ria.DigiDoc.ui.theme.Dimensions.XSPadding import ee.ria.DigiDoc.ui.theme.Dimensions.iconSizeM import ee.ria.DigiDoc.ui.theme.RIADigiDocTheme import ee.ria.DigiDoc.utils.Route -import ee.ria.DigiDoc.utils.snackbar.SnackBarManager import ee.ria.DigiDoc.utils.snackbar.SnackBarManager.showMessage import ee.ria.DigiDoc.viewmodel.HomeViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedMenuViewModel @@ -110,13 +109,6 @@ fun HomeScreen( activity?.finish() } - LaunchedEffect(snackbarMessage) { - snackbarMessage?.let { message -> - showMessage(context, message) - savedStateHandle?.set("snackbar_message", null) - } - } - LaunchedEffect(homeViewModel.didAppCrashOnPreviousExecution(), hasUnsentReports) { if (!homeViewModel.isCrashSendingAlwaysEnabled() && hasUnsentReports.result) { openCrashDetectorDialog.value = true 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 @@ + + + From 17478096189d53068fb34ce7ca3ce5122426dc4e Mon Sep 17 00:00:00 2001 From: Marten Rebane Date: Fri, 3 Jul 2026 16:37:52 +0300 Subject: [PATCH 11/11] Code improvements --- .../ria/DigiDoc/fragment/screen/HomeScreen.kt | 3 - .../ui/component/crypto/EncryptNavigation.kt | 75 ++++++++----------- .../ui/component/signing/SigningNavigation.kt | 68 ++++++----------- 3 files changed, 51 insertions(+), 95 deletions(-) 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 f36ac9fd5..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 @@ -53,7 +53,6 @@ import androidx.compose.ui.Alignment.Companion.CenterHorizontally import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource @@ -80,7 +79,6 @@ import ee.ria.DigiDoc.ui.theme.Dimensions.XSPadding import ee.ria.DigiDoc.ui.theme.Dimensions.iconSizeM import ee.ria.DigiDoc.ui.theme.RIADigiDocTheme import ee.ria.DigiDoc.utils.Route -import ee.ria.DigiDoc.utils.snackbar.SnackBarManager.showMessage import ee.ria.DigiDoc.viewmodel.HomeViewModel import ee.ria.DigiDoc.viewmodel.shared.SharedMenuViewModel import kotlinx.coroutines.CoroutineScope @@ -95,7 +93,6 @@ fun HomeScreen( sharedMenuViewModel: SharedMenuViewModel, homeViewModel: HomeViewModel = hiltViewModel(), ) { - val context = LocalContext.current val activity = LocalActivity.current val openCrashDetectorDialog = rememberSaveable { mutableStateOf(false) } val hasUnsentReports by homeViewModel.hasUnsentReports.asFlow().collectAsState(Tasks.forResult(false)) 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 4124073af..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 @@ -166,6 +167,12 @@ fun EncryptNavigation( 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) @@ -405,15 +412,18 @@ 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 - } ?: run { - cryptoContainer?.file?.let { - sharedContainerViewModel.saveContainerFile(it, result) - showMessage(context, R.string.crypto_saved_container_success, SnackbarType.SUCCESS) - isSaved = true - } ?: showMessage(context, R.string.file_saved_error) - } + } ?: showMessage(context, R.string.file_saved_error) } catch (_: Exception) { showMessage(context, R.string.file_saved_error) } @@ -425,9 +435,8 @@ fun EncryptNavigation( if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { - ContainerUtil.removeCryptoContainersDir(context) - handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, @@ -513,14 +522,11 @@ fun EncryptNavigation( isSaveContainerShown.value = false if (showContainerCloseConfirmationDialog.value) { showContainerCloseConfirmationDialog.value = false - - ContainerUtil.removeCryptoContainersDir(context) - handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, - true, ) } @Suppress("AssignedValueIsNeverRead") @@ -601,9 +607,8 @@ fun EncryptNavigation( if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { - ContainerUtil.removeCryptoContainersDir(context) - handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, @@ -781,21 +786,10 @@ fun EncryptNavigation( } }, onMoreOptionsActionButtonClick = { - val isEditContainerButtonShown = ( - !isNestedContainer && - !encryptViewModel.isEncryptedContainer(cryptoContainer) && - !encryptViewModel.isDecryptedContainer(cryptoContainer) - ) - val isSignButtonShown = ( - !isNestedContainer && - encryptViewModel.isEncryptedContainer(cryptoContainer) - ) - val result = ( + showContainerBottomSheet.value = isSaveContainerShown.value || - isEditContainerButtonShown || - isSignButtonShown - ) - showContainerBottomSheet.value = (result) + isEditContainerButtonShown || + isSignButtonShown }, ) } @@ -985,9 +979,8 @@ fun EncryptNavigation( if ((cryptoContainer?.dataFiles?.size ?: 0) == 1) { cryptoContainer?.file?.delete() sharedContainerViewModel.resetCryptoContainer() - - ContainerUtil.removeCryptoContainersDir(context) handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, @@ -1085,6 +1078,7 @@ fun EncryptNavigation( openRemoveFileDialog = openRemoveFileDialog, onBackButtonClick = { handleBackButtonClick( + context, navController, encryptViewModel, sharedContainerViewModel, @@ -1094,10 +1088,7 @@ fun EncryptNavigation( EncryptContainerBottomSheet( modifier = modifier, showSheet = showContainerBottomSheet, - isEditContainerButtonShown = - !isNestedContainer && - !encryptViewModel.isEncryptedContainer(cryptoContainer) && - !encryptViewModel.isDecryptedContainer(cryptoContainer), + isEditContainerButtonShown = isEditContainerButtonShown, openEditContainerNameDialog = openEditContainerNameDialog, isSaveButtonShown = isSaveContainerShown.value, cryptoContainer = cryptoContainer, @@ -1165,9 +1156,8 @@ fun EncryptNavigation( }, onConfirmButton = { showContainerCloseConfirmationDialog.value = false - ContainerUtil.removeCryptoContainersDir(context) sharedContainerViewModel.resetCryptoContainer() - handleBackButtonClick(navController, encryptViewModel, sharedContainerViewModel) + handleBackButtonClick(context, navController, encryptViewModel, sharedContainerViewModel) }, ) } @@ -1176,10 +1166,10 @@ fun EncryptNavigation( } private fun handleBackButtonClick( + context: Context, navController: NavHostController, encryptViewModel: EncryptViewModel, sharedContainerViewModel: SharedContainerViewModel, - containerSavedSuccess: Boolean = false, ) { sharedContainerViewModel.resetExternalFileUris() sharedContainerViewModel.resetIsSivaConfirmed() @@ -1197,14 +1187,9 @@ private fun handleBackButtonClick( } } } else { + ContainerUtil.removeCryptoContainersDir(context) sharedContainerViewModel.clearContainers() encryptViewModel.handleBackButton() - - if (containerSavedSuccess) { - navController.previousBackStackEntry - ?.savedStateHandle - ?.set("snackbar_message", R.string.crypto_saved_container_success) - } navController.navigateUp() } } 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 5327183b2..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 @@ -184,6 +184,8 @@ fun SigningNavigation( 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) } @@ -469,9 +471,8 @@ fun SigningNavigation( if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { - ContainerUtil.removeSignatureContainersDir(context) - handleBackButtonClick( + context, navController, signingViewModel, sharedContainerViewModel, @@ -664,13 +665,11 @@ fun SigningNavigation( isSaveContainerShown.value = false if (showContainerCloseConfirmationDialog.value) { showContainerCloseConfirmationDialog.value = false - ContainerUtil.removeSignatureContainersDir(context) - handleBackButtonClick( + context, navController, signingViewModel, sharedContainerViewModel, - true, ) } @Suppress("AssignedValueIsNeverRead") @@ -724,9 +723,8 @@ fun SigningNavigation( if (!isNestedContainer && isSaveContainerShown.value) { showContainerCloseConfirmationDialog.value = true } else { - ContainerUtil.removeSignatureContainersDir(context) - handleBackButtonClick( + context, navController, signingViewModel, sharedContainerViewModel, @@ -863,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, @@ -892,21 +886,10 @@ fun SigningNavigation( }, onRightActionButtonClick = onEncryptActionClick, onMoreOptionsActionButtonClick = { - val isEditContainerButtonShown = - signingViewModel.isBottomContainerButtonShown( - signedContainer, - isNestedContainer, - ) - val isEncryptButtonShown = - signingViewModel.isEncryptButtonShown( - signedContainer, - isNestedContainer, - ) - showContainerBottomSheet.value = ( + showContainerBottomSheet.value = isSaveContainerShown.value || - isEditContainerButtonShown || - isEncryptButtonShown - ) + isEditContainerButtonShown || + isEncryptButtonShown }, ) } @@ -1130,8 +1113,12 @@ fun SigningNavigation( signedContainer?.getContainerFile()?.delete() sharedContainerViewModel.resetSignedContainer() sharedContainerViewModel.resetContainerNotifications() - ContainerUtil.removeSignatureContainersDir(context) - handleBackButtonClick(navController, signingViewModel, sharedContainerViewModel) + handleBackButtonClick( + context, + navController, + signingViewModel, + sharedContainerViewModel, + ) } else { scope.launch(IO) { try { @@ -1227,6 +1214,7 @@ fun SigningNavigation( openRemoveFileDialog = openRemoveFileDialog, onBackButtonClick = { handleBackButtonClick( + context, navController, signingViewModel, sharedContainerViewModel, @@ -1237,17 +1225,9 @@ fun SigningNavigation( modifier = modifier, showSheet = showContainerBottomSheet, isSaveButtonShown = isSaveContainerShown.value, - isEditContainerButtonShown = - signingViewModel.isBottomContainerButtonShown( - signedContainer, - isNestedContainer, - ), + isEditContainerButtonShown = isEditContainerButtonShown, openEditContainerNameDialog = openEditContainerNameDialog, - isEncryptButtonShown = - signingViewModel.isEncryptButtonShown( - signedContainer, - isNestedContainer, - ), + isEncryptButtonShown = isEncryptButtonShown, signedContainer = signedContainer, onEncryptClick = onEncryptActionClick, saveFileLauncher = saveFileLauncher, @@ -1332,10 +1312,9 @@ fun SigningNavigation( }, onConfirmButton = { showContainerCloseConfirmationDialog.value = false - ContainerUtil.removeSignatureContainersDir(context) sharedContainerViewModel.resetSignedContainer() sharedContainerViewModel.resetContainerNotifications() - handleBackButtonClick(navController, signingViewModel, sharedContainerViewModel) + handleBackButtonClick(context, navController, signingViewModel, sharedContainerViewModel) }, ) } @@ -1365,10 +1344,10 @@ private suspend fun createContainerForSignedPDF( } private fun handleBackButtonClick( + context: Context, navController: NavHostController, signingViewModel: SigningViewModel, sharedContainerViewModel: SharedContainerViewModel, - containerSavedSuccess: Boolean = false, ) { sharedContainerViewModel.resetExternalFileUris() sharedContainerViewModel.resetIsSivaConfirmed() @@ -1386,14 +1365,9 @@ private fun handleBackButtonClick( } } } else { + ContainerUtil.removeSignatureContainersDir(context) sharedContainerViewModel.clearContainers() signingViewModel.handleBackButton() - - if (containerSavedSuccess) { - navController.previousBackStackEntry - ?.savedStateHandle - ?.set("snackbar_message", R.string.signature_saved_container_success) - } navController.navigateUp() } }