From 732d24e95e4d497d66ebde8bf773bc5f15139d5e Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Tue, 25 Jun 2024 17:30:06 +0200 Subject: [PATCH 1/3] Added new strings --- strings/src/main/res/values/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/strings/src/main/res/values/strings.xml b/strings/src/main/res/values/strings.xml index 1723400bc82..e75cef341ba 100644 --- a/strings/src/main/res/values/strings.xml +++ b/strings/src/main/res/values/strings.xml @@ -500,6 +500,18 @@ Delete layer + + + %d layer can\'t be added + %d layers can\'t be added + + + + The files you selected are not MBTiles. You can only add MBTiles files. + + + Some of the files you selected are not MBTiles. You can only add MBTiles files. + Layers From 50a495a65909f29485e675995b00c1200a47de6a Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Thu, 27 Jun 2024 12:11:21 +0200 Subject: [PATCH 2/3] Display a warning when unsuported layers are selected --- .../maps/layers/OfflineMapLayersImporter.kt | 47 ++++++++++++++++++- .../maps/layers/OfflineMapLayersViewModel.kt | 21 +++++++-- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/maps/src/main/java/org/odk/collect/maps/layers/OfflineMapLayersImporter.kt b/maps/src/main/java/org/odk/collect/maps/layers/OfflineMapLayersImporter.kt index e417aa12e23..20e7d92d0ae 100644 --- a/maps/src/main/java/org/odk/collect/maps/layers/OfflineMapLayersImporter.kt +++ b/maps/src/main/java/org/odk/collect/maps/layers/OfflineMapLayersImporter.kt @@ -8,10 +8,14 @@ import androidx.appcompat.widget.Toolbar import androidx.fragment.app.activityViewModels import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.odk.collect.async.Scheduler import org.odk.collect.maps.databinding.OfflineMapLayersImporterBinding import org.odk.collect.material.MaterialFullScreenDialogFragment import org.odk.collect.settings.SettingsProvider +import org.odk.collect.strings.R +import org.odk.collect.strings.localization.getLocalizedQuantityString +import org.odk.collect.strings.localization.getLocalizedString class OfflineMapLayersImporter( private val referenceLayerRepository: ReferenceLayerRepository, @@ -65,8 +69,19 @@ class OfflineMapLayersImporter( } viewModel.layersToImport.observe(this) { layersToImport -> - val adapter = OfflineMapLayersImporterAdapter(layersToImport) + val adapter = OfflineMapLayersImporterAdapter(layersToImport.value.layers) binding.layers.setAdapter(adapter) + + if (!layersToImport.isConsumed()) { + layersToImport.consume() + + if (layersToImport.value.numberOfSelectedLayers == layersToImport.value.numberOfUnsupportedLayers) { + dismiss() + showNoSupportedLayersWarning(layersToImport.value.numberOfUnsupportedLayers) + } else if (layersToImport.value.numberOfUnsupportedLayers > 0) { + showSomeUnsupportedLayersWarning(layersToImport.value.numberOfSelectedLayers - layersToImport.value.numberOfUnsupportedLayers) + } + } } } @@ -79,4 +94,34 @@ class OfflineMapLayersImporter( override fun getToolbar(): Toolbar { return OfflineMapLayersImporterBinding.bind(requireView()).toolbar } + + private fun showNoSupportedLayersWarning(numberOfLayers: Int) { + MaterialAlertDialogBuilder(requireActivity()) + .setTitle( + requireActivity().getLocalizedQuantityString( + R.plurals.non_mbtiles_files_selected_title, + numberOfLayers, + numberOfLayers + ) + ) + .setMessage(requireActivity().getLocalizedString(R.string.all_non_mbtiles_files_selected_message)) + .setPositiveButton(R.string.ok, null) + .create() + .show() + } + + private fun showSomeUnsupportedLayersWarning(numberOfLayers: Int) { + MaterialAlertDialogBuilder(requireActivity()) + .setTitle( + requireActivity().getLocalizedQuantityString( + R.plurals.non_mbtiles_files_selected_title, + numberOfLayers, + numberOfLayers + ) + ) + .setMessage(requireActivity().getLocalizedString(R.string.some_non_mbtiles_files_selected_message)) + .setPositiveButton(R.string.ok, null) + .create() + .show() + } } diff --git a/maps/src/main/java/org/odk/collect/maps/layers/OfflineMapLayersViewModel.kt b/maps/src/main/java/org/odk/collect/maps/layers/OfflineMapLayersViewModel.kt index 69b7f1b81e7..1842dbe325d 100644 --- a/maps/src/main/java/org/odk/collect/maps/layers/OfflineMapLayersViewModel.kt +++ b/maps/src/main/java/org/odk/collect/maps/layers/OfflineMapLayersViewModel.kt @@ -6,6 +6,7 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import org.odk.collect.analytics.Analytics +import org.odk.collect.androidshared.data.Consumable import org.odk.collect.androidshared.system.copyToFile import org.odk.collect.androidshared.system.getFileExtension import org.odk.collect.androidshared.system.getFileName @@ -27,8 +28,8 @@ class OfflineMapLayersViewModel( private val _existingLayers = MutableLiveData>() val existingLayers: LiveData> = _existingLayers - private val _layersToImport = MutableLiveData>() - val layersToImport: LiveData> = _layersToImport + private val _layersToImport = MutableLiveData>() + val layersToImport: LiveData> = _layersToImport private lateinit var tempLayersDir: File @@ -67,7 +68,15 @@ class OfflineMapLayersViewModel( } } _isLoading.postValue(false) - _layersToImport.postValue(layers.sortedBy { it.name }) + _layersToImport.postValue( + Consumable( + LayersToImport( + uris.size, + uris.size - layers.size, + layers.sortedBy { it.name } + ) + ) + ) }, foreground = { } ) @@ -119,4 +128,10 @@ class OfflineMapLayersViewModel( Analytics.log(event) } + + data class LayersToImport( + val numberOfSelectedLayers: Int, + val numberOfUnsupportedLayers: Int, + val layers: List + ) } From eccd18dab56ac046d21e6cbb66e875cedce1c24a Mon Sep 17 00:00:00 2001 From: Grzegorz Orczykowski Date: Thu, 27 Jun 2024 12:11:29 +0200 Subject: [PATCH 3/3] Added tests --- .../layers/OfflineMapLayersImporterTest.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/maps/src/test/java/org/odk/collect/maps/layers/OfflineMapLayersImporterTest.kt b/maps/src/test/java/org/odk/collect/maps/layers/OfflineMapLayersImporterTest.kt index f3f0cbb5ccd..6e95520acb7 100644 --- a/maps/src/test/java/org/odk/collect/maps/layers/OfflineMapLayersImporterTest.kt +++ b/maps/src/test/java/org/odk/collect/maps/layers/OfflineMapLayersImporterTest.kt @@ -1,9 +1,12 @@ package org.odk.collect.maps.layers +import android.app.Application import androidx.core.net.toUri import androidx.fragment.app.testing.FragmentScenario +import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso.onView import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.RootMatchers.isDialog import androidx.test.espresso.matcher.ViewMatchers.isChecked import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isEnabled @@ -25,6 +28,7 @@ import org.odk.collect.fragmentstest.FragmentScenarioLauncherRule import org.odk.collect.settings.InMemSettingsProvider import org.odk.collect.shared.TempFiles import org.odk.collect.strings.R +import org.odk.collect.strings.localization.getLocalizedQuantityString import org.odk.collect.testshared.FakeScheduler import org.odk.collect.testshared.Interactions import org.odk.collect.testshared.RecyclerViewMatcher @@ -240,6 +244,43 @@ class OfflineMapLayersImporterTest { assertThat(booleanCaptor.secondValue, equalTo(false)) } + @Test + fun `the warning dialog is displayed if some selected files are not supported and the importer dialog is kept displayed`() { + val file1 = TempFiles.createTempFile("layerA", ".txt") + val file2 = TempFiles.createTempFile("layerB", MbtilesFile.FILE_EXTENSION) + + launchFragment().onFragment { + it.viewModel.loadLayersToImport(listOf(file1.toUri(), file2.toUri()), it.requireContext()) + + scheduler.flush() + + val context = ApplicationProvider.getApplicationContext() + onView(withText(context.getLocalizedQuantityString(R.plurals.non_mbtiles_files_selected_title, 1, 1))).inRoot(isDialog()).check(matches(isDisplayed())) + onView(withText(R.string.some_non_mbtiles_files_selected_message)).inRoot(isDialog()).check(matches(isDisplayed())) + onView(withText(R.string.ok)).inRoot(isDialog()).check(matches(isDisplayed())) + + assertThat(it.isVisible, equalTo(true)) + } + } + + @Test + fun `the warning dialog is displayed if all selected files are not supported and the importer dialog is dismissed`() { + val file = TempFiles.createTempFile("layerA", ".txt") + + launchFragment().onFragment { + it.viewModel.loadLayersToImport(listOf(file.toUri()), it.requireContext()) + + scheduler.flush() + + val context = ApplicationProvider.getApplicationContext() + onView(withText(context.getLocalizedQuantityString(R.plurals.non_mbtiles_files_selected_title, 1, 1))).inRoot(isDialog()).check(matches(isDisplayed())) + onView(withText(R.string.all_non_mbtiles_files_selected_message)).inRoot(isDialog()).check(matches(isDisplayed())) + onView(withText(R.string.ok)).inRoot(isDialog()).check(matches(isDisplayed())) + + assertThat(it.isVisible, equalTo(false)) + } + } + private fun launchFragment(): FragmentScenario { return fragmentScenarioLauncherRule.launchInContainer(OfflineMapLayersImporter::class.java) }