From 5bb89af3bc25b061bedb484affd6bd06006159ff Mon Sep 17 00:00:00 2001 From: Callum Stott Date: Sat, 14 Oct 2023 12:47:42 +0100 Subject: [PATCH] Add setting to disable bulk finalize --- .../formmanagement/BulkFinalizationTest.kt | 21 +++++++++++++++ .../odk/collect/android/support/pages/Page.kt | 16 +++++++++-- .../activities/InstanceChooserList.java | 27 ++++++++++--------- .../drafts/BulkFinalizationViewModel.kt | 7 ++++- .../res/xml/form_entry_access_preferences.xml | 13 ++++++++- .../settings/keys/ProtectedProjectKeys.kt | 4 ++- strings/src/main/res/values/strings.xml | 3 +++ 7 files changed, 74 insertions(+), 17 deletions(-) diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formmanagement/BulkFinalizationTest.kt b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formmanagement/BulkFinalizationTest.kt index b6cd96c7a2b..7d81458291b 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/feature/formmanagement/BulkFinalizationTest.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/feature/formmanagement/BulkFinalizationTest.kt @@ -8,9 +8,11 @@ import org.junit.Test import org.junit.rules.RuleChain import org.junit.runner.RunWith import org.odk.collect.android.support.TestDependencies +import org.odk.collect.android.support.pages.AccessControlPage import org.odk.collect.android.support.pages.EditSavedFormPage import org.odk.collect.android.support.pages.FormEntryPage.QuestionAndAnswer import org.odk.collect.android.support.pages.MainMenuPage +import org.odk.collect.android.support.pages.ProjectSettingsPage import org.odk.collect.android.support.pages.SaveOrDiscardFormDialog import org.odk.collect.android.support.rules.CollectTestRule import org.odk.collect.android.support.rules.TestRuleChain @@ -189,4 +191,23 @@ class BulkFinalizationTest { .assertNumberOfEditableForms(1) } + + @Test + fun canBeDisabled() { + rule.withProject("http://example.com") + .openProjectSettingsDialog() + .clickSettings() + .clickAccessControl() + .clickFormEntrySettings() + .clickOnString(string.finalize_all_forms) + .pressBack(AccessControlPage()) + .pressBack(ProjectSettingsPage()) + .pressBack(MainMenuPage()) + + .copyForm("one-question.xml", "example.com") + .startBlankForm("One Question") + .fillOutAndSave(QuestionAndAnswer("what is your age", "1892")) + .clickDrafts() + .assertNoOptionsMenu() + } } diff --git a/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/Page.kt b/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/Page.kt index 138f5dd2ac3..ab717024059 100644 --- a/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/Page.kt +++ b/collect_app/src/androidTest/java/org/odk/collect/android/support/pages/Page.kt @@ -2,6 +2,7 @@ package org.odk.collect.android.support.pages import android.app.Application import android.content.pm.ActivityInfo +import android.view.View import androidx.annotation.StringRes import androidx.recyclerview.widget.RecyclerView import androidx.test.core.app.ApplicationProvider @@ -34,6 +35,8 @@ import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.UiSelector import org.hamcrest.CoreMatchers.not +import org.hamcrest.Matcher +import org.hamcrest.Matchers import org.hamcrest.Matchers.allOf import org.hamcrest.core.StringContains.containsString import org.hamcrest.core.StringEndsWith.endsWith @@ -42,7 +45,6 @@ import org.odk.collect.android.BuildConfig import org.odk.collect.android.R import org.odk.collect.android.application.Collect import org.odk.collect.android.storage.StoragePathProvider -import org.odk.collect.android.support.ActivityHelpers import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.support.WaitFor.wait250ms import org.odk.collect.android.support.WaitFor.waitFor @@ -448,7 +450,7 @@ abstract class Page> { fun clickOptionsIcon(expectedOptionString: String): T { tryAgainOnFail({ - Espresso.openActionBarOverflowOrOptionsMenu(ActivityHelpers.getActivity()) + onView(OVERFLOW_BUTTON_MATCHER).perform(click()) assertText(expectedOptionString) }) @@ -474,6 +476,11 @@ abstract class Page> { return destination!!.assertOnPage() } + fun assertNoOptionsMenu(): T { + onView(OVERFLOW_BUTTON_MATCHER).check(doesNotExist()) + return this as T + } + companion object { private fun rotateToLandscape(): ViewAction { return RotateAction(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) @@ -482,5 +489,10 @@ abstract class Page> { private fun rotateToPortrait(): ViewAction { return RotateAction(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) } + + private val OVERFLOW_BUTTON_MATCHER: Matcher = Matchers.anyOf( + allOf(isDisplayed(), withContentDescription("More options")), + allOf(isDisplayed(), withClassName(Matchers.endsWith("OverflowMenuButton"))) + ) } } diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/InstanceChooserList.java b/collect_app/src/main/java/org/odk/collect/android/activities/InstanceChooserList.java index 033f5349043..b61cd2df921 100644 --- a/collect_app/src/main/java/org/odk/collect/android/activities/InstanceChooserList.java +++ b/collect_app/src/main/java/org/odk/collect/android/activities/InstanceChooserList.java @@ -146,7 +146,8 @@ public void onCreate(Bundle savedInstanceState) { BulkFinalizationViewModel bulkFinalizationViewModel = new BulkFinalizationViewModel( scheduler, - instancesDataService + instancesDataService, + settingsProvider ); MaterialProgressDialogFragment.showOn(this, bulkFinalizationViewModel.isFinalizing(), getSupportFragmentManager(), () -> { @@ -155,17 +156,19 @@ public void onCreate(Bundle savedInstanceState) { return dialog; }); - DraftsMenuProvider draftsMenuProvider = new DraftsMenuProvider(this, bulkFinalizationViewModel::finalizeAllDrafts); - addMenuProvider(draftsMenuProvider, this); - bulkFinalizationViewModel.getDraftsCount().observe(this, draftsCount -> { - draftsMenuProvider.setDraftsCount(draftsCount); - invalidateMenu(); - }); - - bulkFinalizationViewModel.getFinalizedForms().observe( - this, - new FinalizeAllSnackbarPresenter(this.findViewById(android.R.id.content), this) - ); + if (bulkFinalizationViewModel.isEnabled()) { + DraftsMenuProvider draftsMenuProvider = new DraftsMenuProvider(this, bulkFinalizationViewModel::finalizeAllDrafts); + addMenuProvider(draftsMenuProvider, this); + bulkFinalizationViewModel.getDraftsCount().observe(this, draftsCount -> { + draftsMenuProvider.setDraftsCount(draftsCount); + invalidateMenu(); + }); + + bulkFinalizationViewModel.getFinalizedForms().observe( + this, + new FinalizeAllSnackbarPresenter(this.findViewById(android.R.id.content), this) + ); + } } private void init() { diff --git a/collect_app/src/main/java/org/odk/collect/android/formmanagement/drafts/BulkFinalizationViewModel.kt b/collect_app/src/main/java/org/odk/collect/android/formmanagement/drafts/BulkFinalizationViewModel.kt index fd440caa216..7e96f787d12 100644 --- a/collect_app/src/main/java/org/odk/collect/android/formmanagement/drafts/BulkFinalizationViewModel.kt +++ b/collect_app/src/main/java/org/odk/collect/android/formmanagement/drafts/BulkFinalizationViewModel.kt @@ -8,10 +8,13 @@ import org.odk.collect.androidshared.data.Consumable import org.odk.collect.androidshared.livedata.MutableNonNullLiveData import org.odk.collect.androidshared.livedata.NonNullLiveData import org.odk.collect.async.Scheduler +import org.odk.collect.settings.SettingsProvider +import org.odk.collect.settings.keys.ProtectedProjectKeys class BulkFinalizationViewModel( private val scheduler: Scheduler, - private val instancesDataService: InstancesDataService + private val instancesDataService: InstancesDataService, + private val settingsProvider: SettingsProvider ) { private val _finalizedForms = MutableLiveData>() val finalizedForms: LiveData> = _finalizedForms @@ -20,6 +23,8 @@ class BulkFinalizationViewModel( val isFinalizing: NonNullLiveData = _isFinalizing val draftsCount = instancesDataService.editableCount + val isEnabled = + settingsProvider.getProtectedSettings().getBoolean(ProtectedProjectKeys.KEY_BULK_FINALIZE) fun finalizeAllDrafts() { _isFinalizing.value = true diff --git a/collect_app/src/main/res/xml/form_entry_access_preferences.xml b/collect_app/src/main/res/xml/form_entry_access_preferences.xml index 7a69e689d5d..f231fe86d85 100644 --- a/collect_app/src/main/res/xml/form_entry_access_preferences.xml +++ b/collect_app/src/main/res/xml/form_entry_access_preferences.xml @@ -43,5 +43,16 @@ android:key="finalize" android:title="@string/finalize" app:iconSpaceReserved="false" /> + + + + + - \ No newline at end of file + diff --git a/settings/src/main/java/org/odk/collect/settings/keys/ProtectedProjectKeys.kt b/settings/src/main/java/org/odk/collect/settings/keys/ProtectedProjectKeys.kt index f746c401d28..dc29614c2ae 100644 --- a/settings/src/main/java/org/odk/collect/settings/keys/ProtectedProjectKeys.kt +++ b/settings/src/main/java/org/odk/collect/settings/keys/ProtectedProjectKeys.kt @@ -43,6 +43,7 @@ object ProtectedProjectKeys { const val KEY_FINALIZE = "finalize" const val ALLOW_OTHER_WAYS_OF_EDITING_FORM = "allow_other_ways_of_editing_form" + const val KEY_BULK_FINALIZE = "bulk_finalize" fun allKeys() = listOf( KEY_ADMIN_PW, @@ -82,6 +83,7 @@ object ProtectedProjectKeys { KEY_SAVE_MID, KEY_SAVE_AS_DRAFT, KEY_FINALIZE, - ALLOW_OTHER_WAYS_OF_EDITING_FORM + ALLOW_OTHER_WAYS_OF_EDITING_FORM, + KEY_BULK_FINALIZE ) } diff --git a/strings/src/main/res/values/strings.xml b/strings/src/main/res/values/strings.xml index d2eac3c8c5e..9eb93a4146e 100644 --- a/strings/src/main/res/values/strings.xml +++ b/strings/src/main/res/values/strings.xml @@ -1255,4 +1255,7 @@ Do you want to finalize %d form? Do you want to finalize %d forms? + + + Uncheck to hide from Drafts