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 51f3211d..aaed9ecb 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/ConstantTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/ConstantTest.kt @@ -51,8 +51,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 6126f66c..fc68943e 100644 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/RouteTest.kt +++ b/app/src/androidTest/kotlin/ee/ria/DigiDoc/utils/RouteTest.kt @@ -42,9 +42,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 @@ -82,9 +79,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 3a720459..00000000 --- a/app/src/androidTest/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModelTest.kt +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2017 - 2025 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("Tests", 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()) - } - - @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 df0e2583..89a7180d 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/RIADigiDocAppNavigation.kt @@ -52,7 +52,6 @@ import ee.ria.DigiDoc.fragment.MyEidIdentificationFragment import ee.ria.DigiDoc.fragment.MyEidIdentificationMethodChooserFragment 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 @@ -220,33 +219,6 @@ fun RIADigiDocAppScreen(externalFileUris: List) { 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 b3347726..00000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/RecentDocumentsFragment.kt +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2017 - 2025 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 83715138..7fc6bb7d 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 @@ -74,7 +74,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.TopBar import ee.ria.DigiDoc.ui.theme.Dimensions.SPadding @@ -103,9 +102,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) } @@ -210,27 +206,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 = @@ -318,9 +293,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", ) @@ -334,9 +309,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", ) @@ -350,9 +325,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 6bf3c56a..00000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/fragment/screen/RecentDocumentsScreen.kt +++ /dev/null @@ -1,602 +0,0 @@ -/* - * Copyright 2017 - 2025 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.SnackbarHost -import androidx.compose.material3.SnackbarHostState -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.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.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.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 -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 snackBarHostState = remember { SnackbarHostState() } - val snackBarScope = rememberCoroutineScope() - val scope = rememberCoroutineScope() - - val messages by SnackBarManager.messages.collectAsState(emptyList()) - - val showLoading = remember { mutableStateOf(false) } - val showSivaDialog = remember { mutableStateOf(false) } - val selectedDocument = remember { 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 = - remember { - mutableStateOf( - recentDocumentsViewModel.getRecentDocumentList(fileOpeningMethod), - ) - } - var actionDocument by remember { mutableStateOf(null) } - val openRemoveDocumentDialog = remember { 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) - } - } - } - - LaunchedEffect(messages) { - messages.forEach { message -> - snackBarScope.launch { - snackBarHostState.showSnackbar(message) - } - SnackBarManager.removeMessage(message) - } - } - - Scaffold( - snackbarHost = { - SnackbarHost( - modifier = modifier.padding(vertical = SPadding), - hostState = snackBarHostState, - ) - }, - modifier = - modifier - .semantics { - testTagsAsResourceId = true - }.testTag("recentDocumentsScreen"), - 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 5f29184c..e76a226a 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() @@ -448,7 +448,7 @@ fun EncryptNavigation( } BackHandler { - if (!isNestedContainer && encryptViewModel.isEncryptedContainer(cryptoContainer)) { + if (!isNestedContainer && encryptViewModel.isSaveButtonShown(cryptoContainer)) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( @@ -622,7 +622,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( @@ -1106,13 +1106,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), isSignButtonShown = !isNestedContainer && encryptViewModel.isEncryptedContainer(cryptoContainer), cryptoContainer = cryptoContainer, onSignClick = onSignActionClick, @@ -1138,36 +1132,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 89588b27..00000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/OpenMenuBottomSheet.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2017 - 2025 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 25a9da22..00000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/menu/TwoButtonMenu.kt +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2017 - 2025 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 28c8475c..00000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/ui/component/shared/Document.kt +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2017 - 2025 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 8f259988..b816c790 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 @@ -181,6 +181,8 @@ fun SigningNavigation( val isNestedContainer = sharedContainerViewModel.isNestedContainer(signedContainer) val isXadesContainer = signedContainer?.isXades() == true val isCadesContainer = signedContainer?.isCades() == true + val isSignedContainer = signedContainer?.isSigned() == true + val isSaveContainerShown = remember { mutableStateOf(isSignedContainer) } var validSignaturesCount by remember { mutableIntStateOf(0) } var unknownSignaturesCount by remember { mutableIntStateOf(0) } @@ -219,7 +221,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( @@ -590,6 +592,9 @@ fun SigningNavigation( if (newTime >= (pastTime + 2 * 1000)) { sendAccessibilityEvent(context, getAccessibilityEventType(), signaturesLoaded) } + if (isSaveContainerShown.value != true) { + isSaveContainerShown.value = signatures.count() > 0 + } } } @@ -703,7 +708,7 @@ fun SigningNavigation( }, leftIconContentDescription = R.string.signing_close_container_title, onLeftButtonClick = { - if (!isNestedContainer) { + if (!isNestedContainer && isSaveContainerShown.value == true) { showContainerCloseConfirmationDialog.value = true } else { handleBackButtonClick( @@ -798,6 +803,7 @@ fun SigningNavigation( ) } } + isSaveContainerShown.value = true showMessage(signatureAddedSuccessText) signatureAddedSuccess.value = false } @@ -1046,6 +1052,7 @@ fun SigningNavigation( ) } openEditContainerNameDialog.value = false + isSaveContainerShown.value = true sendAccessibilityEvent( context, getAccessibilityEventType(), @@ -1107,6 +1114,7 @@ fun SigningNavigation( } } } + isSaveContainerShown.value = true closeRemoveFileDialog() sendAccessibilityEvent(context, getAccessibilityEventType(), fileRemoved) }, @@ -1151,6 +1159,7 @@ fun SigningNavigation( actionSignature, ) } + isSaveContainerShown.value = true closeSignatureDialog() sendAccessibilityEvent(context, getAccessibilityEventType(), signatureRemoved) }, @@ -1196,6 +1205,7 @@ fun SigningNavigation( ContainerBottomSheet( modifier = modifier, showSheet = showContainerBottomSheet, + isSaveButtonShown = isSaveContainerShown.value, isEditContainerButtonShown = signingViewModel.isBottomContainerButtonShown( signedContainer, @@ -1249,36 +1259,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 c7a76774..3ef95b79 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 4aeab42b..5b5fc834 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/utils/Constant.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/utils/Constant.kt @@ -48,9 +48,6 @@ object Constant { const val SIGNER_DETAIL_SCREEN = "signer_detail_route" const val CERTIFICATE_DETAIL_SCREEN = "certificate_detail_route" const val RECIPIENT_DETAIL_SCREEN = "recipient_detail_route" - const val RECENT_DOCUMENTS_SCREEN = "recent_documents_route" - const val RECENT_DOCUMENTS_SCREEN_FROM_SIGNING_SCREEN = "recent_documents_route_from_signing_screen" - const val RECENT_DOCUMENTS_FROM_ENCRYPT_SCREEN = "recent_documents_from_encrypt_route" const val SETTINGS_SCREEN = "settings_route" const val SETTINGS_LANGUAGE_CHOOSER_SCREEN = "settings_language_chooser_route" const val SETTINGS_THEME_CHOOSER_SCREEN = "settings_theme_chooser_route" diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.kt b/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.kt index a81e6362..2c7aa954 100644 --- a/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.kt +++ b/app/src/main/kotlin/ee/ria/DigiDoc/utils/Route.kt @@ -42,9 +42,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 @@ -98,12 +95,6 @@ sealed class Route( data object RecipientDetail : Route(RECIPIENT_DETAIL_SCREEN) - data object RecentDocuments : Route(RECENT_DOCUMENTS_SCREEN) - - data object RecentDocumentsFromSigning : Route(RECENT_DOCUMENTS_SCREEN_FROM_SIGNING_SCREEN) - - data object RecentDocumentsFromEncrypt : Route(RECENT_DOCUMENTS_FROM_ENCRYPT_SCREEN) - data object Settings : Route(SETTINGS_SCREEN) data object SettingsLanguageChooser : Route(SETTINGS_LANGUAGE_CHOOSER_SCREEN) diff --git a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptViewModel.kt b/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/EncryptViewModel.kt index f20387f0..df1aab9b 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, @@ -150,20 +152,7 @@ class EncryptViewModel fun getMimetype(file: File): String? = mimeTypeResolver.mimeType(file) - suspend fun getTimestampedContainer( - context: Context, - signedContainer: SignedContainer, - ): SignedContainer { - if (sivaRepository.isTimestampedContainer(signedContainer) && - !signedContainer.isXades() - ) { - return sivaRepository.getTimestampedContainer(context, signedContainer) - } - - return signedContainer - } - - @Throws(Exception::class) + @Throws(Exception::class) suspend fun openSignedContainer( context: Context, container: CryptoContainer?, 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 1847ae0f..00000000 --- a/app/src/main/kotlin/ee/ria/DigiDoc/viewmodel/RecentDocumentsViewModel.kt +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright 2017 - 2025 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) - } - - 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 0164a115..bc5a4bc1 100644 --- a/app/src/main/res/values-et/strings.xml +++ b/app/src/main/res/values-et/strings.xml @@ -40,7 +40,9 @@ Fail salvestatud Faili salvestamine ebaõnnestus Eemalda + Sulge Eemalda ümbrik + Sulge ümbrik Eemalda fail Salvesta fail Soovid eemaldada faili ümbrikust? @@ -127,18 +129,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 37d81193..f7128dfb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,7 +40,9 @@ File saved Failed to save file Remove + Close Remove container + Close container Remove file Save file Remove file from container? @@ -127,18 +129,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.