diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java index 05b65a92699..3a6a8fc15bb 100644 --- a/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java +++ b/collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java @@ -168,6 +168,7 @@ import org.odk.collect.android.widgets.range.RangePickerIntegerWidget; import org.odk.collect.android.widgets.utilities.ExternalAppRecordingRequester; import org.odk.collect.android.widgets.utilities.FormControllerWaitingForDataRegistry; +import org.odk.collect.android.widgets.utilities.GeoPolyDialogFragment; import org.odk.collect.android.widgets.utilities.InternalRecordingRequester; import org.odk.collect.android.widgets.utilities.WaitingForDataRegistry; import org.odk.collect.androidshared.system.IntentLauncher; @@ -438,6 +439,7 @@ public void onCreate(Bundle savedInstanceState) { .forClass(DeleteRepeatDialogFragment.class, () -> new DeleteRepeatDialogFragment(viewModelFactory)) .forClass(BackgroundAudioPermissionDialogFragment.class, () -> new BackgroundAudioPermissionDialogFragment(viewModelFactory)) .forClass(SelectOneFromMapDialogFragment.class, () -> new SelectOneFromMapDialogFragment(viewModelFactory)) + .forClass(GeoPolyDialogFragment.class, () -> new GeoPolyDialogFragment(viewModelFactory)) .build()); getSupportFragmentManager().setFragmentResultListener(REQUEST_DELETE_REPEAT, this, (requestKey, result) -> deleteGroup()); @@ -901,8 +903,6 @@ protected void onActivityResult(int requestCode, int resultCode, final Intent in loadMedia(intent.getData()); break; case RequestCodes.LOCATION_CAPTURE: - case RequestCodes.GEOSHAPE_CAPTURE: - case RequestCodes.GEOTRACE_CAPTURE: case RequestCodes.BEARING_CAPTURE: case RequestCodes.BARCODE_CAPTURE: case RequestCodes.EX_STRING_CAPTURE: diff --git a/collect_app/src/main/java/org/odk/collect/android/formentry/ODKView.java b/collect_app/src/main/java/org/odk/collect/android/formentry/ODKView.java index 5c66d05ddfd..78a35f5da87 100644 --- a/collect_app/src/main/java/org/odk/collect/android/formentry/ODKView.java +++ b/collect_app/src/main/java/org/odk/collect/android/formentry/ODKView.java @@ -41,6 +41,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.widget.NestedScrollView; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LifecycleOwner; import org.javarosa.core.model.Constants; @@ -145,7 +146,7 @@ public class ODKView extends SwipeHandler.View implements OnLongClickListener, W * @param advancingPage whether this view is being created after a forward swipe through the */ public ODKView( - ComponentActivity context, + FragmentActivity context, final FormEntryPrompt[] questionPrompts, FormEntryCaption[] groups, boolean advancingPage, diff --git a/collect_app/src/main/java/org/odk/collect/android/widgets/GeoShapeWidget.java b/collect_app/src/main/java/org/odk/collect/android/widgets/GeoShapeWidget.java index 401dbcc71f2..02b0bb4e377 100644 --- a/collect_app/src/main/java/org/odk/collect/android/widgets/GeoShapeWidget.java +++ b/collect_app/src/main/java/org/odk/collect/android/widgets/GeoShapeWidget.java @@ -22,27 +22,22 @@ import org.javarosa.core.model.data.IAnswerData; import org.javarosa.core.model.data.StringData; import org.javarosa.form.api.FormEntryPrompt; - import org.odk.collect.android.databinding.GeoshapeQuestionBinding; import org.odk.collect.android.formentry.questions.QuestionDetails; -import org.odk.collect.android.widgets.interfaces.WidgetDataReceiver; import org.odk.collect.android.widgets.interfaces.GeoDataRequester; import org.odk.collect.android.widgets.utilities.GeoWidgetUtils; -import org.odk.collect.android.widgets.utilities.WaitingForDataRegistry; @SuppressLint("ViewConstructor") -public class GeoShapeWidget extends QuestionWidget implements WidgetDataReceiver { +public class GeoShapeWidget extends QuestionWidget { GeoshapeQuestionBinding binding; - private final WaitingForDataRegistry waitingForDataRegistry; private final GeoDataRequester geoDataRequester; - public GeoShapeWidget(Context context, QuestionDetails questionDetails, WaitingForDataRegistry waitingForDataRegistry, + public GeoShapeWidget(Context context, QuestionDetails questionDetails, GeoDataRequester geoDataRequester, Dependencies dependencies) { super(context, dependencies, questionDetails); render(); - this.waitingForDataRegistry = waitingForDataRegistry; this.geoDataRequester = geoDataRequester; } @@ -52,8 +47,7 @@ protected View onCreateWidgetView(Context context, FormEntryPrompt prompt, int a binding.geoAnswerText.setTextSize(TypedValue.COMPLEX_UNIT_DIP, answerFontSize); - binding.simpleButton.setOnClickListener(v -> - geoDataRequester.requestGeoShape(prompt, getAnswerText(), waitingForDataRegistry)); + binding.simpleButton.setOnClickListener(v -> geoDataRequester.requestGeoPoly(prompt)); String stringAnswer = GeoWidgetUtils.getGeoPolyAnswerToDisplay(prompt.getAnswerText()); binding.geoAnswerText.setText(stringAnswer); @@ -104,14 +98,6 @@ public void cancelLongPress() { binding.geoAnswerText.cancelLongPress(); } - @Override - public void setData(Object answer) { - binding.geoAnswerText.setText(answer.toString()); - binding.geoAnswerText.setVisibility(binding.geoAnswerText.getText().toString().isBlank() ? GONE : VISIBLE); - binding.simpleButton.setText(answer.toString().isEmpty() ? org.odk.collect.strings.R.string.get_polygon : org.odk.collect.strings.R.string.view_or_change_polygon); - widgetValueChanged(); - } - private String getAnswerText() { return binding.geoAnswerText.getText().toString(); } diff --git a/collect_app/src/main/java/org/odk/collect/android/widgets/GeoTraceWidget.java b/collect_app/src/main/java/org/odk/collect/android/widgets/GeoTraceWidget.java index 642807e1736..9ba4d89dc8b 100644 --- a/collect_app/src/main/java/org/odk/collect/android/widgets/GeoTraceWidget.java +++ b/collect_app/src/main/java/org/odk/collect/android/widgets/GeoTraceWidget.java @@ -25,30 +25,26 @@ import org.javarosa.form.api.FormEntryPrompt; import org.odk.collect.android.databinding.GeotraceQuestionBinding; import org.odk.collect.android.formentry.questions.QuestionDetails; -import org.odk.collect.maps.MapConfigurator; import org.odk.collect.android.widgets.interfaces.GeoDataRequester; -import org.odk.collect.android.widgets.interfaces.WidgetDataReceiver; import org.odk.collect.android.widgets.utilities.GeoWidgetUtils; -import org.odk.collect.android.widgets.utilities.WaitingForDataRegistry; +import org.odk.collect.maps.MapConfigurator; /** * GeoTraceWidget allows the user to collect a trace of GPS points as the * device moves along a path. */ @SuppressLint("ViewConstructor") -public class GeoTraceWidget extends QuestionWidget implements WidgetDataReceiver { +public class GeoTraceWidget extends QuestionWidget { GeotraceQuestionBinding binding; - private final WaitingForDataRegistry waitingForDataRegistry; private final MapConfigurator mapConfigurator; private final GeoDataRequester geoDataRequester; - public GeoTraceWidget(Context context, QuestionDetails questionDetails, WaitingForDataRegistry waitingForDataRegistry, + public GeoTraceWidget(Context context, QuestionDetails questionDetails, MapConfigurator mapConfigurator, GeoDataRequester geoDataRequester, Dependencies dependencies) { super(context, dependencies, questionDetails); render(); - this.waitingForDataRegistry = waitingForDataRegistry; this.mapConfigurator = mapConfigurator; this.geoDataRequester = geoDataRequester; } @@ -61,7 +57,7 @@ protected View onCreateWidgetView(Context context, FormEntryPrompt prompt, int a binding.simpleButton.setOnClickListener(v -> { if (mapConfigurator.isAvailable(context)) { - geoDataRequester.requestGeoTrace(prompt, getAnswerText(), waitingForDataRegistry); + geoDataRequester.requestGeoPoly(prompt); } else { mapConfigurator.showUnavailableMessage(context); } @@ -116,14 +112,6 @@ public void cancelLongPress() { binding.geoAnswerText.cancelLongPress(); } - @Override - public void setData(Object answer) { - binding.geoAnswerText.setText(answer.toString()); - binding.geoAnswerText.setVisibility(binding.geoAnswerText.getText().toString().isBlank() ? GONE : VISIBLE); - binding.simpleButton.setText(answer.toString().isEmpty() ? org.odk.collect.strings.R.string.get_line : org.odk.collect.strings.R.string.view_or_change_line); - widgetValueChanged(); - } - private String getAnswerText() { return binding.geoAnswerText.getText().toString(); } diff --git a/collect_app/src/main/java/org/odk/collect/android/widgets/WidgetFactory.java b/collect_app/src/main/java/org/odk/collect/android/widgets/WidgetFactory.java index 1b77d56f40b..1c02152a13f 100644 --- a/collect_app/src/main/java/org/odk/collect/android/widgets/WidgetFactory.java +++ b/collect_app/src/main/java/org/odk/collect/android/widgets/WidgetFactory.java @@ -18,10 +18,10 @@ import static org.odk.collect.android.utilities.Appearances.PLACEMENT_MAP; import static org.odk.collect.android.utilities.Appearances.hasAppearance; -import android.app.Activity; import android.content.Context; import android.hardware.SensorManager; +import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LifecycleOwner; import org.javarosa.core.model.Constants; @@ -54,9 +54,6 @@ import org.odk.collect.android.widgets.range.RangePickerDecimalWidget; import org.odk.collect.android.widgets.range.RangePickerIntegerWidget; import org.odk.collect.android.widgets.utilities.ActivityGeoDataRequester; -import org.odk.collect.android.widgets.video.ExVideoWidget; -import org.odk.collect.android.widgets.video.VideoWidget; -import org.odk.collect.audioclips.AudioPlayer; import org.odk.collect.android.widgets.utilities.AudioRecorderRecordingStatusHandler; import org.odk.collect.android.widgets.utilities.DateTimeWidgetUtils; import org.odk.collect.android.widgets.utilities.FileRequester; @@ -66,8 +63,11 @@ import org.odk.collect.android.widgets.utilities.RecordingRequesterProvider; import org.odk.collect.android.widgets.utilities.StringRequester; import org.odk.collect.android.widgets.utilities.WaitingForDataRegistry; +import org.odk.collect.android.widgets.video.ExVideoWidget; +import org.odk.collect.android.widgets.video.VideoWidget; import org.odk.collect.androidshared.system.CameraUtils; import org.odk.collect.androidshared.system.IntentLauncherImpl; +import org.odk.collect.audioclips.AudioPlayer; import org.odk.collect.audiorecorder.recording.AudioRecorder; import org.odk.collect.permissions.PermissionsProvider; import org.odk.collect.settings.SettingsProvider; @@ -82,7 +82,7 @@ public class WidgetFactory { private static final String PICKER_APPEARANCE = "picker"; - private final Activity activity; + private final FragmentActivity activity; private final boolean useExternalRecorder; private final WaitingForDataRegistry waitingForDataRegistry; private final QuestionMediaManager questionMediaManager; @@ -97,7 +97,7 @@ public class WidgetFactory { private final FormController formController; private final SettingsProvider settingsProvider; - public WidgetFactory(Activity activity, + public WidgetFactory(FragmentActivity activity, boolean useExternalRecorder, WaitingForDataRegistry waitingForDataRegistry, QuestionMediaManager questionMediaManager, @@ -180,11 +180,11 @@ public QuestionWidget createWidgetFromPrompt(FormEntryPrompt prompt, Permissions } break; case Constants.DATATYPE_GEOSHAPE: - questionWidget = new GeoShapeWidget(activity, questionDetails, waitingForDataRegistry, + questionWidget = new GeoShapeWidget(activity, questionDetails, new ActivityGeoDataRequester(permissionsProvider, activity), dependencies); break; case Constants.DATATYPE_GEOTRACE: - questionWidget = new GeoTraceWidget(activity, questionDetails, waitingForDataRegistry, + questionWidget = new GeoTraceWidget(activity, questionDetails, MapConfiguratorProvider.getConfigurator(), new ActivityGeoDataRequester(permissionsProvider, activity), dependencies); break; case Constants.DATATYPE_BARCODE: diff --git a/collect_app/src/main/java/org/odk/collect/android/widgets/interfaces/GeoDataRequester.kt b/collect_app/src/main/java/org/odk/collect/android/widgets/interfaces/GeoDataRequester.kt index ae9524818c0..fa97a44bb8c 100644 --- a/collect_app/src/main/java/org/odk/collect/android/widgets/interfaces/GeoDataRequester.kt +++ b/collect_app/src/main/java/org/odk/collect/android/widgets/interfaces/GeoDataRequester.kt @@ -10,15 +10,5 @@ interface GeoDataRequester { waitingForDataRegistry: WaitingForDataRegistry ) - fun requestGeoShape( - prompt: FormEntryPrompt, - answerText: String?, - waitingForDataRegistry: WaitingForDataRegistry - ) - - fun requestGeoTrace( - prompt: FormEntryPrompt, - answerText: String?, - waitingForDataRegistry: WaitingForDataRegistry - ) + fun requestGeoPoly(prompt: FormEntryPrompt) } diff --git a/collect_app/src/main/java/org/odk/collect/android/widgets/items/SelectOneFromMapDialogFragment.kt b/collect_app/src/main/java/org/odk/collect/android/widgets/items/SelectOneFromMapDialogFragment.kt index 6c4278fdcfc..bbd593c5fac 100644 --- a/collect_app/src/main/java/org/odk/collect/android/widgets/items/SelectOneFromMapDialogFragment.kt +++ b/collect_app/src/main/java/org/odk/collect/android/widgets/items/SelectOneFromMapDialogFragment.kt @@ -2,29 +2,19 @@ package org.odk.collect.android.widgets.items import android.content.Context import android.content.res.Resources -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup import androidx.activity.ComponentDialog -import androidx.appcompat.widget.Toolbar -import androidx.fragment.app.FragmentResultListener -import androidx.fragment.app.activityViewModels import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModelProvider -import org.javarosa.core.model.FormIndex import org.javarosa.core.model.SelectChoice import org.javarosa.core.model.data.SelectOneData import org.javarosa.form.api.FormEntryPrompt -import org.odk.collect.android.databinding.SelectOneFromMapDialogLayoutBinding -import org.odk.collect.android.formentry.FormEntryViewModel import org.odk.collect.android.injection.DaggerUtils import org.odk.collect.android.utilities.Appearances import org.odk.collect.android.widgets.utilities.GeoWidgetUtils +import org.odk.collect.android.widgets.utilities.WidgetAnswerDialogFragment import org.odk.collect.androidshared.livedata.MutableNonNullLiveData import org.odk.collect.androidshared.livedata.NonNullLiveData -import org.odk.collect.androidshared.ui.FragmentFactoryBuilder import org.odk.collect.async.Scheduler import org.odk.collect.entities.javarosa.parse.EntitySchema import org.odk.collect.geo.geopoly.GeoPolyUtils.parseGeometry @@ -33,73 +23,40 @@ import org.odk.collect.geo.selection.MappableSelectItem import org.odk.collect.geo.selection.SelectionMapData import org.odk.collect.geo.selection.SelectionMapFragment import org.odk.collect.geo.selection.SelectionMapFragment.Companion.REQUEST_SELECT_ITEM -import org.odk.collect.material.MaterialFullScreenDialogFragment import javax.inject.Inject -class SelectOneFromMapDialogFragment(private val viewModelFactory: ViewModelProvider.Factory) : - MaterialFullScreenDialogFragment(), FragmentResultListener { +class SelectOneFromMapDialogFragment(viewModelFactory: ViewModelProvider.Factory) : + WidgetAnswerDialogFragment( + SelectionMapFragment::class, + viewModelFactory + ) { @Inject lateinit var scheduler: Scheduler - private val formEntryViewModel: FormEntryViewModel by activityViewModels { viewModelFactory } - override fun onAttach(context: Context) { super.onAttach(context) DaggerUtils.getComponent(context).inject(this) - - val formIndex = requireArguments().getSerializable(ARG_FORM_INDEX) as FormIndex - val selectedIndex = requireArguments().getSerializable(ARG_SELECTED_INDEX) as Int? - val prompt = formEntryViewModel.getQuestionPrompt(formIndex) - val selectionMapData = SelectChoicesMapData(resources, scheduler, prompt, selectedIndex) - - childFragmentManager.fragmentFactory = FragmentFactoryBuilder() - .forClass(SelectionMapFragment::class.java) { - SelectionMapFragment( - selectionMapData, - skipSummary = Appearances.hasAppearance(prompt, Appearances.QUICK), - zoomToFitItems = false, - showNewItemButton = false, - onBackPressedDispatcher = { (requireDialog() as ComponentDialog).onBackPressedDispatcher } - ) - } - .build() - - childFragmentManager.setFragmentResultListener(REQUEST_SELECT_ITEM, this, this) - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - val binding = SelectOneFromMapDialogLayoutBinding.inflate(inflater) - return binding.root } - override fun getToolbar(): Toolbar? { - return null - } - - override fun onBackPressed() { - dismiss() - } - - override fun onCloseClicked() { - // No toolbar so not relevant - } + override fun onCreateFragment(prompt: FormEntryPrompt): SelectionMapFragment { + childFragmentManager.setFragmentResultListener(REQUEST_SELECT_ITEM, this) { _, result -> + val selectedIndex = result.getLong(SelectionMapFragment.RESULT_SELECTED_ITEM).toInt() + val selectedChoice = prompt.selectChoices[selectedIndex] + onAnswer(SelectOneData(selectedChoice.selection())) + } - override fun onFragmentResult(requestKey: String, result: Bundle) { - val selectedIndex = result.getLong(SelectionMapFragment.RESULT_SELECTED_ITEM).toInt() - val formIndex = requireArguments().getSerializable(ARG_FORM_INDEX) as FormIndex - val prompt = formEntryViewModel.getQuestionPrompt(formIndex) - val selectedChoice = prompt.selectChoices[selectedIndex] - formEntryViewModel.answerQuestion(formIndex, SelectOneData(selectedChoice.selection())) - dismiss() + val selectedIndex = requireArguments().getSerializable(ARG_SELECTED_INDEX) as Int? + return SelectionMapFragment( + SelectChoicesMapData(this.resources, scheduler, prompt, selectedIndex), + skipSummary = Appearances.hasAppearance(prompt, Appearances.QUICK), + zoomToFitItems = false, + showNewItemButton = false, + onBackPressedDispatcher = { (requireDialog() as ComponentDialog).onBackPressedDispatcher } + ) } companion object { - const val ARG_FORM_INDEX = "form_index" const val ARG_SELECTED_INDEX = "selected_index" } } diff --git a/collect_app/src/main/java/org/odk/collect/android/widgets/items/SelectOneFromMapWidget.kt b/collect_app/src/main/java/org/odk/collect/android/widgets/items/SelectOneFromMapWidget.kt index e3e8b84073b..557131212ed 100644 --- a/collect_app/src/main/java/org/odk/collect/android/widgets/items/SelectOneFromMapWidget.kt +++ b/collect_app/src/main/java/org/odk/collect/android/widgets/items/SelectOneFromMapWidget.kt @@ -15,8 +15,8 @@ import org.javarosa.form.api.FormEntryPrompt import org.odk.collect.android.databinding.SelectOneFromMapWidgetAnswerBinding import org.odk.collect.android.formentry.questions.QuestionDetails import org.odk.collect.android.widgets.QuestionWidget -import org.odk.collect.android.widgets.items.SelectOneFromMapDialogFragment.Companion.ARG_FORM_INDEX import org.odk.collect.android.widgets.items.SelectOneFromMapDialogFragment.Companion.ARG_SELECTED_INDEX +import org.odk.collect.android.widgets.utilities.WidgetAnswerDialogFragment.Companion.ARG_FORM_INDEX import org.odk.collect.androidshared.ui.DialogFragmentUtils import org.odk.collect.permissions.PermissionListener diff --git a/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/ActivityGeoDataRequester.kt b/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/ActivityGeoDataRequester.kt index 90d386f751a..b035a90ea01 100644 --- a/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/ActivityGeoDataRequester.kt +++ b/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/ActivityGeoDataRequester.kt @@ -1,19 +1,20 @@ package org.odk.collect.android.widgets.utilities -import android.app.Activity import android.content.Intent import android.os.Bundle +import androidx.core.os.bundleOf +import androidx.fragment.app.FragmentActivity import org.javarosa.form.api.FormEntryPrompt import org.odk.collect.android.utilities.Appearances import org.odk.collect.android.utilities.ApplicationConstants import org.odk.collect.android.utilities.FormEntryPromptUtils import org.odk.collect.android.widgets.interfaces.GeoDataRequester +import org.odk.collect.androidshared.ui.DialogFragmentUtils import org.odk.collect.geo.Constants.EXTRA_DRAGGABLE_ONLY import org.odk.collect.geo.Constants.EXTRA_READ_ONLY import org.odk.collect.geo.Constants.EXTRA_RETAIN_MOCK_ACCURACY import org.odk.collect.geo.geopoint.GeoPointActivity import org.odk.collect.geo.geopoint.GeoPointMapActivity -import org.odk.collect.geo.geopoly.GeoPolyActivity import org.odk.collect.geo.geopoly.GeoPolyUtils.parseGeometry import org.odk.collect.permissions.PermissionListener import org.odk.collect.permissions.PermissionsProvider @@ -21,7 +22,7 @@ import java.lang.Boolean.parseBoolean class ActivityGeoDataRequester( private val permissionsProvider: PermissionsProvider, - private val activity: Activity + private val activity: FragmentActivity ) : GeoDataRequester { override fun requestGeoPoint( @@ -84,66 +85,15 @@ class ActivityGeoDataRequester( ) } - override fun requestGeoShape( - prompt: FormEntryPrompt, - answerText: String?, - waitingForDataRegistry: WaitingForDataRegistry - ) { - permissionsProvider.requestEnabledLocationPermissions( - activity, - object : PermissionListener { - override fun granted() { - waitingForDataRegistry.waitForData(prompt.index) - - val intent = Intent(activity, GeoPolyActivity::class.java).also { - it.putExtra( - GeoPolyActivity.EXTRA_POLYGON, - ArrayList(parseGeometry(answerText)) - ) - it.putExtra( - GeoPolyActivity.OUTPUT_MODE_KEY, - GeoPolyActivity.OutputMode.GEOSHAPE - ) - it.putExtra(EXTRA_READ_ONLY, prompt.isReadOnly) - it.putExtra(EXTRA_RETAIN_MOCK_ACCURACY, getAllowMockAccuracy(prompt)) - } - - activity.startActivityForResult( - intent, - ApplicationConstants.RequestCodes.GEOSHAPE_CAPTURE - ) - } - } - ) - } - - override fun requestGeoTrace( - prompt: FormEntryPrompt, - answerText: String?, - waitingForDataRegistry: WaitingForDataRegistry - ) { + override fun requestGeoPoly(prompt: FormEntryPrompt) { permissionsProvider.requestEnabledLocationPermissions( activity, object : PermissionListener { override fun granted() { - waitingForDataRegistry.waitForData(prompt.index) - - val intent = Intent(activity, GeoPolyActivity::class.java).also { - it.putExtra( - GeoPolyActivity.EXTRA_POLYGON, - ArrayList(parseGeometry(answerText)) - ) - it.putExtra( - GeoPolyActivity.OUTPUT_MODE_KEY, - GeoPolyActivity.OutputMode.GEOTRACE - ) - it.putExtra(EXTRA_READ_ONLY, prompt.isReadOnly) - it.putExtra(EXTRA_RETAIN_MOCK_ACCURACY, getAllowMockAccuracy(prompt)) - } - - activity.startActivityForResult( - intent, - ApplicationConstants.RequestCodes.GEOTRACE_CAPTURE + DialogFragmentUtils.showIfNotShowing( + GeoPolyDialogFragment::class.java, + bundleOf(WidgetAnswerDialogFragment.ARG_FORM_INDEX to prompt.index), + activity.supportFragmentManager ) } } diff --git a/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/GeoPolyDialogFragment.kt b/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/GeoPolyDialogFragment.kt new file mode 100644 index 00000000000..56d0d8f23be --- /dev/null +++ b/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/GeoPolyDialogFragment.kt @@ -0,0 +1,50 @@ +package org.odk.collect.android.widgets.utilities + +import androidx.lifecycle.ViewModelProvider +import org.javarosa.core.model.Constants +import org.javarosa.core.model.data.StringData +import org.javarosa.form.api.FormEntryPrompt +import org.odk.collect.android.utilities.FormEntryPromptUtils +import org.odk.collect.geo.geopoly.GeoPolyFragment +import org.odk.collect.geo.geopoly.GeoPolyFragment.OutputMode +import org.odk.collect.geo.geopoly.GeoPolyUtils + +class GeoPolyDialogFragment(viewModelFactory: ViewModelProvider.Factory) : + WidgetAnswerDialogFragment( + GeoPolyFragment::class, + viewModelFactory + ) { + + override fun onCreateFragment(prompt: FormEntryPrompt): GeoPolyFragment { + childFragmentManager.setFragmentResultListener( + GeoPolyFragment.REQUEST_GEOPOLY, + this + ) { _, result -> + val result = result.getString(GeoPolyFragment.RESULT_GEOPOLY) + if (result != null) { + onAnswer(StringData(result)) + } else { + dismiss() + } + } + + val outputMode = when (prompt.dataType) { + Constants.DATATYPE_GEOSHAPE -> OutputMode.GEOSHAPE + Constants.DATATYPE_GEOTRACE -> OutputMode.GEOTRACE + else -> null + } + + val retainMockAccuracy = + FormEntryPromptUtils.getBindAttribute(prompt, "allow-mock-accuracy").toBoolean() + + val answer = prompt.answerValue + val inputPolygon = GeoPolyUtils.parseGeometry(answer?.value as String?) + + return GeoPolyFragment( + outputMode, + prompt.isReadOnly, + retainMockAccuracy, + inputPolygon + ) + } +} diff --git a/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/WidgetAnswerDialogFragment.kt b/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/WidgetAnswerDialogFragment.kt new file mode 100644 index 00000000000..fddfed29165 --- /dev/null +++ b/collect_app/src/main/java/org/odk/collect/android/widgets/utilities/WidgetAnswerDialogFragment.kt @@ -0,0 +1,83 @@ +package org.odk.collect.android.widgets.utilities + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.widget.Toolbar +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.commit +import androidx.lifecycle.ViewModelProvider +import org.javarosa.core.model.FormIndex +import org.javarosa.core.model.data.IAnswerData +import org.javarosa.form.api.FormEntryPrompt +import org.odk.collect.android.R +import org.odk.collect.android.databinding.WidgetAnswerDialogLayoutBinding +import org.odk.collect.android.formentry.FormEntryViewModel +import org.odk.collect.androidshared.ui.FragmentFactoryBuilder +import org.odk.collect.material.MaterialFullScreenDialogFragment +import kotlin.reflect.KClass + +abstract class WidgetAnswerDialogFragment( + private val type: KClass, + private val viewModelFactory: ViewModelProvider.Factory +) : MaterialFullScreenDialogFragment() { + + private val formEntryViewModel: FormEntryViewModel by activityViewModels { viewModelFactory } + private val prompt: FormEntryPrompt by lazy { + formEntryViewModel.getQuestionPrompt(requireArguments().getSerializable(ARG_FORM_INDEX) as FormIndex) + } + + abstract fun onCreateFragment(prompt: FormEntryPrompt): T + + override fun onAttach(context: Context) { + super.onAttach(context) + + childFragmentManager.fragmentFactory = FragmentFactoryBuilder() + .forClass(type) { onCreateFragment(prompt) } + .build() + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + val binding = WidgetAnswerDialogLayoutBinding.inflate(inflater) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + if (childFragmentManager.fragments.isEmpty()) { + childFragmentManager.commit { + add(R.id.answer_fragment, type.java, null) + } + } + } + + override fun getToolbar(): Toolbar? { + return null + } + + override fun onBackPressed() { + dismiss() + } + + override fun onCloseClicked() { + // No toolbar so not relevant + } + + fun onAnswer(answer: IAnswerData, dismiss: Boolean = true) { + formEntryViewModel.answerQuestion(prompt.index, answer) + + if (dismiss) { + dismiss() + } + } + + companion object { + const val ARG_FORM_INDEX = "form_index" + } +} diff --git a/collect_app/src/main/res/layout/select_one_from_map_dialog_layout.xml b/collect_app/src/main/res/layout/widget_answer_dialog_layout.xml similarity index 65% rename from collect_app/src/main/res/layout/select_one_from_map_dialog_layout.xml rename to collect_app/src/main/res/layout/widget_answer_dialog_layout.xml index 1e7329f4c1d..1db40a4982d 100644 --- a/collect_app/src/main/res/layout/select_one_from_map_dialog_layout.xml +++ b/collect_app/src/main/res/layout/widget_answer_dialog_layout.xml @@ -4,9 +4,8 @@ android:layout_height="match_parent"> + android:layout_height="match_parent" /> diff --git a/collect_app/src/test/java/org/odk/collect/android/widgets/GeoShapeWidgetTest.java b/collect_app/src/test/java/org/odk/collect/android/widgets/GeoShapeWidgetTest.java index b3390ebe611..20606616fcf 100644 --- a/collect_app/src/test/java/org/odk/collect/android/widgets/GeoShapeWidgetTest.java +++ b/collect_app/src/test/java/org/odk/collect/android/widgets/GeoShapeWidgetTest.java @@ -1,5 +1,17 @@ package org.odk.collect.android.widgets; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.odk.collect.android.widgets.support.GeoWidgetHelpers.stringFromDoubleList; +import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.mockValueChangedListener; +import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.promptWithAnswer; +import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.promptWithReadOnly; +import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.promptWithReadOnlyAndAnswer; +import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.widgetDependencies; +import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.widgetTestActivity; + import android.view.View; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -14,18 +26,6 @@ import org.odk.collect.android.widgets.interfaces.GeoDataRequester; import org.odk.collect.android.widgets.utilities.WaitingForDataRegistry; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.odk.collect.android.widgets.support.GeoWidgetHelpers.stringFromDoubleList; -import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.mockValueChangedListener; -import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.promptWithAnswer; -import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.promptWithReadOnly; -import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.promptWithReadOnlyAndAnswer; -import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.widgetDependencies; -import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.widgetTestActivity; - @RunWith(AndroidJUnit4.class) public class GeoShapeWidgetTest { private final String answer = stringFromDoubleList(); @@ -118,49 +118,12 @@ public void clickingButtonAndAnswerTextViewForLong_callsLongClickListeners() { verify(listener).onLongClick(widget.binding.geoAnswerText); } - @Test - public void setData_updatesWidgetAnswer() { - GeoShapeWidget widget = createWidget(promptWithAnswer(null)); - widget.setData(answer); - assertEquals(widget.getAnswer().getDisplayText(), answer); - } - - @Test - public void setData_updatesWidgetDisplayedAnswer() { - GeoShapeWidget widget = createWidget(promptWithAnswer(null)); - widget.setData(answer); - assertEquals(widget.binding.geoAnswerText.getText().toString(), answer); - } - - @Test - public void setData_whenDataIsNull_updatesButtonLabel() { - GeoShapeWidget widget = createWidget(promptWithAnswer(new StringData(answer))); - widget.setData(""); - assertEquals(widget.binding.simpleButton.getText(), widget.getContext().getString(org.odk.collect.strings.R.string.get_polygon)); - } - - @Test - public void setData_whenDataIsNotNull_updatesButtonLabel() { - GeoShapeWidget widget = createWidget(promptWithAnswer(null)); - widget.setData(answer); - assertEquals(widget.binding.simpleButton.getText(), widget.getContext().getString(org.odk.collect.strings.R.string.view_or_change_polygon)); - } - - @Test - public void setData_callsValueChangeListener() { - GeoShapeWidget widget = createWidget(promptWithAnswer(null)); - WidgetValueChangedListener valueChangedListener = mockValueChangedListener(widget); - widget.setData(answer); - - verify(valueChangedListener).widgetValueChanged(widget); - } - @Test public void buttonClick_requestsGeoShape() { FormEntryPrompt prompt = promptWithAnswer(new StringData(answer)); GeoShapeWidget widget = createWidget(prompt); widget.binding.simpleButton.performClick(); - verify(geoDataRequester).requestGeoShape(prompt, answer, waitingForDataRegistry); + verify(geoDataRequester).requestGeoPoly(prompt); } @Test @@ -170,21 +133,11 @@ public void buttonClick_requestsGeoShape_whenAnswerIsCleared() { widget.clearAnswer(); widget.binding.simpleButton.performClick(); - verify(geoDataRequester).requestGeoShape(prompt, "", waitingForDataRegistry); - } - - @Test - public void buttonClick_requestsGeoShape_whenAnswerIsUpdated() { - FormEntryPrompt prompt = promptWithAnswer(null); - GeoShapeWidget widget = createWidget(prompt); - widget.setData(answer); - widget.binding.simpleButton.performClick(); - - verify(geoDataRequester).requestGeoShape(prompt, answer, waitingForDataRegistry); + verify(geoDataRequester).requestGeoPoly(prompt); } private GeoShapeWidget createWidget(FormEntryPrompt prompt) { return new GeoShapeWidget(widgetTestActivity(), new QuestionDetails(prompt), - waitingForDataRegistry, geoDataRequester, widgetDependencies()); + geoDataRequester, widgetDependencies()); } } diff --git a/collect_app/src/test/java/org/odk/collect/android/widgets/GeoTraceWidgetTest.java b/collect_app/src/test/java/org/odk/collect/android/widgets/GeoTraceWidgetTest.java index 5a4b6a2803e..c8be14888c7 100644 --- a/collect_app/src/test/java/org/odk/collect/android/widgets/GeoTraceWidgetTest.java +++ b/collect_app/src/test/java/org/odk/collect/android/widgets/GeoTraceWidgetTest.java @@ -1,20 +1,5 @@ package org.odk.collect.android.widgets; -import android.view.View; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import org.javarosa.core.model.data.StringData; -import org.javarosa.form.api.FormEntryPrompt; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.odk.collect.android.formentry.questions.QuestionDetails; -import org.odk.collect.maps.MapConfigurator; -import org.odk.collect.android.listeners.WidgetValueChangedListener; -import org.odk.collect.android.widgets.interfaces.GeoDataRequester; -import org.odk.collect.android.widgets.utilities.WaitingForDataRegistry; - import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; @@ -30,17 +15,29 @@ import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.widgetDependencies; import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.widgetTestActivity; +import android.view.View; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.javarosa.core.model.data.StringData; +import org.javarosa.form.api.FormEntryPrompt; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.odk.collect.android.formentry.questions.QuestionDetails; +import org.odk.collect.android.listeners.WidgetValueChangedListener; +import org.odk.collect.android.widgets.interfaces.GeoDataRequester; +import org.odk.collect.maps.MapConfigurator; + @RunWith(AndroidJUnit4.class) public class GeoTraceWidgetTest { private final String answer = stringFromDoubleList(); - private WaitingForDataRegistry waitingForDataRegistry; private GeoDataRequester geoDataRequester; private MapConfigurator mapConfigurator; @Before public void setup() { - waitingForDataRegistry = mock(WaitingForDataRegistry.class); geoDataRequester = mock(GeoDataRequester.class); mapConfigurator = mock(MapConfigurator.class); when(mapConfigurator.isAvailable(any())).thenReturn(true); @@ -125,50 +122,6 @@ public void clickingButtonAndAnswerTextViewForLong_callsLongClickListener() { verify(listener).onLongClick(widget.binding.geoAnswerText); } - @Test - public void setData_updatesWidgetAnswer() { - GeoTraceWidget widget = createWidget(promptWithAnswer(null)); - widget.setData(answer); - assertEquals(widget.getAnswer().getDisplayText(), answer); - } - - @Test - public void setData_setsCorrectAnswerInAnswerTextView() { - GeoTraceWidget widget = createWidget(promptWithAnswer(null)); - widget.setData(answer); - assertEquals(widget.binding.geoAnswerText.getText().toString(), answer); - } - - @Test - public void setData_updatesWidgetDisplayedAnswer() { - GeoTraceWidget widget = createWidget(promptWithAnswer(null)); - widget.setData(answer); - assertEquals(widget.binding.geoAnswerText.getText().toString(), answer); - } - - @Test - public void setData_whenDataIsNull_updatesButtonLabel() { - GeoTraceWidget widget = createWidget(promptWithAnswer(new StringData(answer))); - widget.setData(""); - assertEquals(widget.binding.simpleButton.getText(), widget.getContext().getString(org.odk.collect.strings.R.string.get_line)); - } - - @Test - public void setData_whenDataIsNotNull_updatesButtonLabel() { - GeoTraceWidget widget = createWidget(promptWithAnswer(null)); - widget.setData(answer); - assertEquals(widget.binding.simpleButton.getText(), widget.getContext().getString(org.odk.collect.strings.R.string.view_or_change_line)); - } - - @Test - public void setData_callsValueChangeListener() { - GeoTraceWidget widget = createWidget(promptWithAnswer(null)); - WidgetValueChangedListener valueChangedListener = mockValueChangedListener(widget); - widget.setData(answer); - - verify(valueChangedListener).widgetValueChanged(widget); - } - @Test public void buttonClick_whenMapConfiguratorIsUnavailable_doesNotRequestGeoTrace() { FormEntryPrompt prompt = promptWithAnswer(null); @@ -177,7 +130,7 @@ public void buttonClick_whenMapConfiguratorIsUnavailable_doesNotRequestGeoTrace( when(mapConfigurator.isAvailable(widget.getContext())).thenReturn(false); widget.binding.simpleButton.performClick(); - verify(geoDataRequester, never()).requestGeoTrace(prompt, "", waitingForDataRegistry); + verify(geoDataRequester, never()).requestGeoPoly(prompt); verify(mapConfigurator).showUnavailableMessage(widget.getContext()); } @@ -187,7 +140,7 @@ public void buttonClick_whenMapConfiguratorIsAvailable_requestsGeoTrace() { GeoTraceWidget widget = createWidget(prompt); widget.binding.simpleButton.performClick(); - verify(geoDataRequester).requestGeoTrace(prompt, "", waitingForDataRegistry); + verify(geoDataRequester).requestGeoPoly(prompt); } @Test @@ -197,21 +150,11 @@ public void buttonClick_requestsGeoTrace_whenAnswerIsCleared() { widget.clearAnswer(); widget.binding.simpleButton.performClick(); - verify(geoDataRequester).requestGeoTrace(prompt, "", waitingForDataRegistry); - } - - @Test - public void buttonClick_requestsGeoTrace_whenAnswerIsUpdated() { - FormEntryPrompt prompt = promptWithAnswer(null); - GeoTraceWidget widget = createWidget(prompt); - widget.setData(answer); - widget.binding.simpleButton.performClick(); - - verify(geoDataRequester).requestGeoTrace(prompt, answer, waitingForDataRegistry); + verify(geoDataRequester).requestGeoPoly(prompt); } private GeoTraceWidget createWidget(FormEntryPrompt prompt) { return new GeoTraceWidget(widgetTestActivity(), new QuestionDetails(prompt), - waitingForDataRegistry, mapConfigurator, geoDataRequester, widgetDependencies()); + mapConfigurator, geoDataRequester, widgetDependencies()); } } diff --git a/collect_app/src/test/java/org/odk/collect/android/widgets/WidgetFactoryTest.kt b/collect_app/src/test/java/org/odk/collect/android/widgets/WidgetFactoryTest.kt index 792ffff0fef..9c02fc56d33 100644 --- a/collect_app/src/test/java/org/odk/collect/android/widgets/WidgetFactoryTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/widgets/WidgetFactoryTest.kt @@ -1,6 +1,6 @@ package org.odk.collect.android.widgets -import android.app.Activity +import androidx.fragment.app.FragmentActivity import androidx.test.ext.junit.runners.AndroidJUnit4 import org.hamcrest.CoreMatchers.equalTo import org.hamcrest.CoreMatchers.instanceOf @@ -30,7 +30,7 @@ import org.odk.collect.settings.keys.ProjectKeys @RunWith(AndroidJUnit4::class) class WidgetFactoryTest { - private val activity: Activity = CollectHelpers.buildThemedActivity(WidgetTestActivity::class.java).get() + private val activity: FragmentActivity = CollectHelpers.buildThemedActivity(WidgetTestActivity::class.java).get() private var widgetFactory = WidgetFactory( activity, diff --git a/collect_app/src/test/java/org/odk/collect/android/widgets/items/SelectOneFromMapDialogFragmentTest.kt b/collect_app/src/test/java/org/odk/collect/android/widgets/items/SelectOneFromMapDialogFragmentTest.kt index c53e9bae76f..0bf65b542ab 100644 --- a/collect_app/src/test/java/org/odk/collect/android/widgets/items/SelectOneFromMapDialogFragmentTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/widgets/items/SelectOneFromMapDialogFragmentTest.kt @@ -23,18 +23,18 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever -import org.odk.collect.android.databinding.SelectOneFromMapDialogLayoutBinding +import org.odk.collect.android.databinding.WidgetAnswerDialogLayoutBinding import org.odk.collect.android.formentry.FormEntryViewModel import org.odk.collect.android.injection.config.AppDependencyModule import org.odk.collect.android.storage.StoragePathProvider import org.odk.collect.android.support.CollectHelpers import org.odk.collect.android.support.MockFormEntryPromptBuilder import org.odk.collect.android.utilities.Appearances -import org.odk.collect.android.widgets.items.SelectOneFromMapDialogFragment.Companion.ARG_FORM_INDEX import org.odk.collect.android.widgets.items.SelectOneFromMapDialogFragment.Companion.ARG_SELECTED_INDEX import org.odk.collect.android.widgets.support.FormElementFixtures.selectChoice import org.odk.collect.android.widgets.support.FormElementFixtures.treeElement import org.odk.collect.android.widgets.support.NoOpMapFragment +import org.odk.collect.android.widgets.utilities.WidgetAnswerDialogFragment.Companion.ARG_FORM_INDEX import org.odk.collect.androidshared.ui.FragmentFactoryBuilder import org.odk.collect.async.Scheduler import org.odk.collect.fragmentstest.FragmentScenarioLauncherRule @@ -149,8 +149,8 @@ class SelectOneFromMapDialogFragmentTest { ) scenario.onFragment { - val binding = SelectOneFromMapDialogLayoutBinding.bind(it.view!!) - val fragment = binding.selectionMap.getFragment() + val binding = WidgetAnswerDialogLayoutBinding.bind(it.view!!) + val fragment = binding.answerFragment.getFragment() assertThat(fragment, notNullValue()) assertThat(fragment.skipSummary, equalTo(false)) assertThat(fragment.showNewItemButton, equalTo(false)) @@ -171,8 +171,8 @@ class SelectOneFromMapDialogFragmentTest { ) scenario.onFragment { - val binding = SelectOneFromMapDialogLayoutBinding.bind(it.view!!) - val fragment = binding.selectionMap.getFragment() + val binding = WidgetAnswerDialogLayoutBinding.bind(it.view!!) + val fragment = binding.answerFragment.getFragment() val data = fragment.selectionMapData scheduler.flush() @@ -236,8 +236,8 @@ class SelectOneFromMapDialogFragmentTest { scheduler.flush() scenario.onFragment { - val binding = SelectOneFromMapDialogLayoutBinding.bind(it.view!!) - val fragment = binding.selectionMap.getFragment() + val binding = WidgetAnswerDialogLayoutBinding.bind(it.view!!) + val fragment = binding.answerFragment.getFragment() assertThat(fragment, notNullValue()) assertThat(fragment.skipSummary, equalTo(false)) assertThat(fragment.showNewItemButton, equalTo(false)) @@ -264,8 +264,8 @@ class SelectOneFromMapDialogFragmentTest { scheduler.runBackground() scenario.onFragment { - val binding = SelectOneFromMapDialogLayoutBinding.bind(it.view!!) - val fragment = binding.selectionMap.getFragment() + val binding = WidgetAnswerDialogLayoutBinding.bind(it.view!!) + val fragment = binding.answerFragment.getFragment() assertThat(fragment.skipSummary, equalTo(true)) } } diff --git a/collect_app/src/test/java/org/odk/collect/android/widgets/items/SelectOneFromMapWidgetTest.kt b/collect_app/src/test/java/org/odk/collect/android/widgets/items/SelectOneFromMapWidgetTest.kt index 8fe83f4f6a9..2dc0e8d681b 100644 --- a/collect_app/src/test/java/org/odk/collect/android/widgets/items/SelectOneFromMapWidgetTest.kt +++ b/collect_app/src/test/java/org/odk/collect/android/widgets/items/SelectOneFromMapWidgetTest.kt @@ -32,6 +32,7 @@ import org.odk.collect.android.widgets.support.QuestionWidgetHelpers.promptWithA import org.odk.collect.android.widgets.support.QuestionWidgetHelpers.widgetDependencies import org.odk.collect.android.widgets.utilities.QuestionFontSizeUtils import org.odk.collect.android.widgets.utilities.QuestionFontSizeUtils.FontSize +import org.odk.collect.android.widgets.utilities.WidgetAnswerDialogFragment.Companion.ARG_FORM_INDEX import org.odk.collect.androidshared.ui.FragmentFactoryBuilder import org.odk.collect.maps.MapFragment import org.odk.collect.maps.MapFragmentFactory @@ -132,7 +133,7 @@ class SelectOneFromMapWidgetTest { assertThat(fragment, notNullValue()) assertThat( fragment?.requireArguments() - ?.getSerializable(SelectOneFromMapDialogFragment.ARG_FORM_INDEX), + ?.getSerializable(ARG_FORM_INDEX), equalTo(prompt.index) ) } diff --git a/collect_app/src/test/java/org/odk/collect/android/widgets/utilities/ActivityGeoDataRequesterTest.java b/collect_app/src/test/java/org/odk/collect/android/widgets/utilities/ActivityGeoDataRequesterTest.java index 46817f269ed..2d8b1443f61 100644 --- a/collect_app/src/test/java/org/odk/collect/android/widgets/utilities/ActivityGeoDataRequesterTest.java +++ b/collect_app/src/test/java/org/odk/collect/android/widgets/utilities/ActivityGeoDataRequesterTest.java @@ -8,8 +8,6 @@ import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.odk.collect.android.utilities.ApplicationConstants.RequestCodes.GEOSHAPE_CAPTURE; -import static org.odk.collect.android.utilities.ApplicationConstants.RequestCodes.GEOTRACE_CAPTURE; import static org.odk.collect.android.utilities.ApplicationConstants.RequestCodes.LOCATION_CAPTURE; import static org.odk.collect.android.widgets.support.GeoWidgetHelpers.getRandomDoubleArray; import static org.odk.collect.android.widgets.support.QuestionWidgetHelpers.promptWithAnswer; @@ -19,11 +17,11 @@ import static org.robolectric.Shadows.shadowOf; import static java.util.Arrays.asList; -import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; +import androidx.fragment.app.FragmentActivity; import androidx.test.ext.junit.runners.AndroidJUnit4; import org.javarosa.core.model.FormIndex; @@ -39,13 +37,12 @@ import org.odk.collect.android.widgets.support.FakeWaitingForDataRegistry; import org.odk.collect.geo.geopoint.GeoPointActivity; import org.odk.collect.geo.geopoint.GeoPointMapActivity; -import org.odk.collect.geo.geopoly.GeoPolyActivity; import org.odk.collect.maps.MapPoint; +import org.odk.collect.testshared.MockDialogFragment; +import org.odk.collect.testshared.MockFragmentFactory; import org.robolectric.Robolectric; import org.robolectric.shadows.ShadowActivity; -import java.util.ArrayList; - @RunWith(AndroidJUnit4.class) public class ActivityGeoDataRequesterTest { @@ -53,7 +50,7 @@ public class ActivityGeoDataRequesterTest { private final FakeWaitingForDataRegistry waitingForDataRegistry = new FakeWaitingForDataRegistry(); private final GeoPointData answer = new GeoPointData(getRandomDoubleArray()); - private Activity testActivity; + private FragmentActivity testActivity; private ShadowActivity shadowActivity; private FormEntryPrompt prompt; private FormIndex formIndex; @@ -63,8 +60,9 @@ public class ActivityGeoDataRequesterTest { @Before public void setUp() { - testActivity = Robolectric.buildActivity(Activity.class).get(); + testActivity = Robolectric.buildActivity(FragmentActivity.class).setup().get(); shadowActivity = shadowOf(testActivity); + testActivity.getSupportFragmentManager().setFragmentFactory(new MockFragmentFactory()); prompt = promptWithAnswer(null); formIndex = mock(FormIndex.class); @@ -87,21 +85,11 @@ public void whenPermissionIsNotGranted_requestGeoPoint_doesNotLaunchAnyIntent() } @Test - public void whenPermissionIsNotGranted_requestGeoShape_doesNotLaunchAnyIntent() { + public void whenPermissionIsNotGranted_requestGeoTrace_doesNotOpenDialog() { permissionsProvider.setPermissionGranted(false); - activityGeoDataRequester.requestGeoShape(prompt, "", waitingForDataRegistry); + activityGeoDataRequester.requestGeoPoly(prompt); - assertNull(shadowActivity.getNextStartedActivity()); - assertTrue(waitingForDataRegistry.waiting.isEmpty()); - } - - @Test - public void whenPermissionIsNotGranted_requestGeoTrace_doesNotLaunchAnyIntent() { - permissionsProvider.setPermissionGranted(false); - activityGeoDataRequester.requestGeoTrace(prompt, "", waitingForDataRegistry); - - assertNull(shadowActivity.getNextStartedActivity()); - assertTrue(waitingForDataRegistry.waiting.isEmpty()); + assertThat(testActivity.getSupportFragmentManager().getFragments().size(), equalTo(0)); } @Test @@ -110,18 +98,6 @@ public void whenPermissionIGranted_requestGeoPoint_setsFormIndexWaitingForData() assertTrue(waitingForDataRegistry.waiting.contains(formIndex)); } - @Test - public void whenPermissionIGranted_requestGeoShape_setsFormIndexWaitingForData() { - activityGeoDataRequester.requestGeoShape(prompt, "", waitingForDataRegistry); - assertTrue(waitingForDataRegistry.waiting.contains(formIndex)); - } - - @Test - public void whenPermissionIGranted_requestGeoTrace_setsFormIndexWaitingForData() { - activityGeoDataRequester.requestGeoTrace(prompt, "", waitingForDataRegistry); - assertTrue(waitingForDataRegistry.waiting.contains(formIndex)); - } - @Test public void requestGeoPoint_launchesCorrectIntent() { activityGeoDataRequester.requestGeoPoint(prompt, answer.getDisplayText(), waitingForDataRegistry); @@ -265,68 +241,11 @@ public void whenWidgetHasPlacementMapAppearance_requestGeoPoint_launchesCorrectI } @Test - public void requestGeoShape_launchesCorrectIntent() { - activityGeoDataRequester.requestGeoShape(prompt, "2.0 3.0 4 5; 6.0 7.0 8 9", waitingForDataRegistry); - Intent startedIntent = shadowActivity.getNextStartedActivity(); - - assertEquals(startedIntent.getComponent(), new ComponentName(testActivity, GeoPolyActivity.class)); - assertEquals(shadowActivity.getNextStartedActivityForResult().requestCode, GEOSHAPE_CAPTURE); - - Bundle bundle = startedIntent.getExtras(); - - ArrayList expectedPolygon = new ArrayList<>(); - expectedPolygon.add(new MapPoint(2.0, 3.0, 4, 5)); - expectedPolygon.add(new MapPoint(6.0, 7.0, 8, 9)); - assertThat(bundle.getParcelableArrayList(GeoPolyActivity.EXTRA_POLYGON), equalTo(expectedPolygon)); - - assertThat(bundle.get(GeoPolyActivity.OUTPUT_MODE_KEY), equalTo(GeoPolyActivity.OutputMode.GEOSHAPE)); - assertThat(bundle.getBoolean(EXTRA_READ_ONLY), equalTo(false)); - } - - @Test - public void whenWidgetIsReadOnly_requestGeoShape_launchesCorrectIntent() { - when(prompt.isReadOnly()).thenReturn(true); - activityGeoDataRequester.requestGeoShape(prompt, "2.0 3.0 4 5; 6.0 7.0 8 9", waitingForDataRegistry); - Intent startedIntent = shadowActivity.getNextStartedActivity(); - - assertEquals(startedIntent.getComponent(), new ComponentName(testActivity, GeoPolyActivity.class)); - assertEquals(shadowActivity.getNextStartedActivityForResult().requestCode, GEOSHAPE_CAPTURE); - - Bundle bundle = startedIntent.getExtras(); - assertThat(bundle.getBoolean(EXTRA_READ_ONLY), equalTo(true)); - } - - @Test - public void requestGeoTrace_launchesCorrectIntent() { - activityGeoDataRequester.requestGeoTrace(prompt, "2.0 3.0 4 5; 6.0 7.0 8 9", waitingForDataRegistry); - Intent startedIntent = shadowActivity.getNextStartedActivity(); - - assertEquals(startedIntent.getComponent(), new ComponentName(testActivity, GeoPolyActivity.class)); - assertEquals(shadowActivity.getNextStartedActivityForResult().requestCode, GEOTRACE_CAPTURE); - - Bundle bundle = startedIntent.getExtras(); - - ArrayList expectedPolygon = new ArrayList<>(); - expectedPolygon.add(new MapPoint(2.0, 3.0, 4, 5)); - expectedPolygon.add(new MapPoint(6.0, 7.0, 8, 9)); - assertThat(bundle.getParcelableArrayList(GeoPolyActivity.EXTRA_POLYGON), equalTo(expectedPolygon)); - - assertThat(bundle.get(GeoPolyActivity.OUTPUT_MODE_KEY), equalTo(GeoPolyActivity.OutputMode.GEOTRACE)); - assertThat(bundle.getBoolean(EXTRA_READ_ONLY), equalTo(false)); - } - - @Test - public void whenWidgetIsReadOnly_requestGeoTrace_launchesCorrectIntent() { - when(prompt.isReadOnly()).thenReturn(true); - - activityGeoDataRequester.requestGeoTrace(prompt, "2.0 3.0 4 5; 6.0 7.0 8 9", waitingForDataRegistry); - Intent startedIntent = shadowActivity.getNextStartedActivity(); - - assertEquals(startedIntent.getComponent(), new ComponentName(testActivity, GeoPolyActivity.class)); - assertEquals(shadowActivity.getNextStartedActivityForResult().requestCode, GEOTRACE_CAPTURE); - - Bundle bundle = startedIntent.getExtras(); - assertThat(bundle.getBoolean(EXTRA_READ_ONLY), equalTo(true)); + public void requestGeoTrace_opensDialog() { + activityGeoDataRequester.requestGeoPoly(prompt); + MockDialogFragment mockFragment = (MockDialogFragment) testActivity.getSupportFragmentManager().getFragments().get(0); + assertThat(mockFragment.getFragmentClass(), equalTo(GeoPolyDialogFragment.class)); + assertThat(mockFragment.requireArguments().getSerializable(WidgetAnswerDialogFragment.ARG_FORM_INDEX), equalTo(prompt.getIndex())); } @Test @@ -349,46 +268,4 @@ public void requestGeoPoint_whenWidgetHasAllowMockAccuracy_addsItToIntent() { assertEquals(startedIntent.getComponent(), new ComponentName(testActivity, GeoPointActivity.class)); assertFalse(startedIntent.getBooleanExtra(EXTRA_RETAIN_MOCK_ACCURACY, true)); } - - @Test - public void requestGeoShape_whenWidgetHasAllowMockAccuracy_addsItToIntent() { - when(prompt.getBindAttributes()) - .thenReturn(asList(TreeElement.constructAttributeElement("odk", "allow-mock-accuracy", "true"))); - - activityGeoDataRequester.requestGeoShape(prompt, "blah", waitingForDataRegistry); - - Intent startedIntent = shadowActivity.getNextStartedActivity(); - assertEquals(startedIntent.getComponent(), new ComponentName(testActivity, GeoPolyActivity.class)); - assertTrue(startedIntent.getBooleanExtra(EXTRA_RETAIN_MOCK_ACCURACY, false)); - - when(prompt.getBindAttributes()) - .thenReturn(asList(TreeElement.constructAttributeElement("odk", "allow-mock-accuracy", "false"))); - - activityGeoDataRequester.requestGeoShape(prompt, "blah", waitingForDataRegistry); - - startedIntent = shadowActivity.getNextStartedActivity(); - assertEquals(startedIntent.getComponent(), new ComponentName(testActivity, GeoPolyActivity.class)); - assertFalse(startedIntent.getBooleanExtra(EXTRA_RETAIN_MOCK_ACCURACY, true)); - } - - @Test - public void requestGeoTrace_whenWidgetHasAllowMockAccuracy_addsItToIntent() { - when(prompt.getBindAttributes()) - .thenReturn(asList(TreeElement.constructAttributeElement("odk", "allow-mock-accuracy", "true"))); - - activityGeoDataRequester.requestGeoTrace(prompt, "blah", waitingForDataRegistry); - - Intent startedIntent = shadowActivity.getNextStartedActivity(); - assertEquals(startedIntent.getComponent(), new ComponentName(testActivity, GeoPolyActivity.class)); - assertTrue(startedIntent.getBooleanExtra(EXTRA_RETAIN_MOCK_ACCURACY, false)); - - when(prompt.getBindAttributes()) - .thenReturn(asList(TreeElement.constructAttributeElement("odk", "allow-mock-accuracy", "false"))); - - activityGeoDataRequester.requestGeoTrace(prompt, "blah", waitingForDataRegistry); - - startedIntent = shadowActivity.getNextStartedActivity(); - assertEquals(startedIntent.getComponent(), new ComponentName(testActivity, GeoPolyActivity.class)); - assertFalse(startedIntent.getBooleanExtra(EXTRA_RETAIN_MOCK_ACCURACY, true)); - } } diff --git a/collect_app/src/test/java/org/odk/collect/android/widgets/utilities/GeoPolyDialogFragmentTest.kt b/collect_app/src/test/java/org/odk/collect/android/widgets/utilities/GeoPolyDialogFragmentTest.kt new file mode 100644 index 00000000000..ae88a055f44 --- /dev/null +++ b/collect_app/src/test/java/org/odk/collect/android/widgets/utilities/GeoPolyDialogFragmentTest.kt @@ -0,0 +1,249 @@ +package org.odk.collect.android.widgets.utilities + +import android.os.Bundle +import androidx.core.os.bundleOf +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewmodel.CreationExtras +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.javarosa.core.model.Constants +import org.javarosa.core.model.data.StringData +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import org.odk.collect.android.formentry.FormEntryViewModel +import org.odk.collect.android.support.CollectHelpers +import org.odk.collect.android.support.MockFormEntryPromptBuilder +import org.odk.collect.android.widgets.utilities.WidgetAnswerDialogFragment.Companion.ARG_FORM_INDEX +import org.odk.collect.androidshared.ui.FragmentFactoryBuilder +import org.odk.collect.fragmentstest.FragmentScenarioLauncherRule +import org.odk.collect.geo.geopoly.GeoPolyFragment +import org.odk.collect.geo.geopoly.GeoPolyFragment.OutputMode +import org.odk.collect.maps.MapPoint + +@RunWith(AndroidJUnit4::class) +class GeoPolyDialogFragmentTest { + + private var prompt = MockFormEntryPromptBuilder().build() + private val formEntryViewModel = mock { + on { getQuestionPrompt(prompt.index) } doReturn prompt + } + + private val viewModelFactory = object : ViewModelProvider.Factory { + override fun create(modelClass: Class, extras: CreationExtras): T { + return formEntryViewModel as T + } + } + + @get:Rule + val launcherRule = + FragmentScenarioLauncherRule( + FragmentFactoryBuilder() + .forClass(GeoPolyDialogFragment::class) { + GeoPolyDialogFragment(viewModelFactory) + }.build() + ) + + @Before + fun setup() { + CollectHelpers.setupDemoProject() + } + + @Test + fun `configures GeoPolyFragment with readOnly from prompt`() { + prompt = MockFormEntryPromptBuilder(prompt).withReadOnly(true).build() + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat(it.readOnly, equalTo(true)) + } + + prompt = MockFormEntryPromptBuilder(prompt).withReadOnly(false).build() + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat(it.readOnly, equalTo(false)) + } + } + + @Test + fun `configures GeoPolyFragment with geoshape output mode when prompt is geoshape`() { + prompt = MockFormEntryPromptBuilder(prompt) + .withDataType(Constants.DATATYPE_GEOSHAPE) + .build() + + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat(it.outputMode, equalTo(OutputMode.GEOSHAPE)) + } + } + + @Test + fun `configures GeoPolyFragment with geotrace output mode when prompt is geotrace`() { + prompt = MockFormEntryPromptBuilder(prompt) + .withDataType(Constants.DATATYPE_GEOTRACE) + .build() + + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat(it.outputMode, equalTo(OutputMode.GEOTRACE)) + } + } + + @Test + fun `configures GeoPolyFragment with null output mode when prompt is something else`() { + prompt = MockFormEntryPromptBuilder(prompt) + .withDataType(Constants.DATATYPE_DATE) + .build() + + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat(it.outputMode, equalTo(null)) + } + } + + @Test + fun `configures GeoPolyFragment with retainMockAccruacy from allow-mock-accuracy bind attribute`() { + prompt = MockFormEntryPromptBuilder(prompt) + .build() + + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat(it.retainMockAccuracy, equalTo(false)) + } + + prompt = MockFormEntryPromptBuilder(prompt) + .withBindAttribute(null, "allow-mock-accuracy", "true") + .build() + + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat(it.retainMockAccuracy, equalTo(true)) + } + + prompt = MockFormEntryPromptBuilder(prompt) + .withBindAttribute(null, "allow-mock-accuracy", "false") + .build() + + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat(it.retainMockAccuracy, equalTo(false)) + } + } + + @Test + fun `configures GeoPolyFragment inputPolgyon with existing answer`() { + prompt = MockFormEntryPromptBuilder(prompt) + .build() + + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat(it.inputPolygon, equalTo(emptyList())) + } + + prompt = MockFormEntryPromptBuilder(prompt) + .withAnswer(StringData("0.0 0.0 1.0 1.0; 0.0 1.0 1.0 1.0")) + .build() + + launcherRule.launchAndAssertOnChild( + GeoPolyDialogFragment::class, + bundleOf(ARG_FORM_INDEX to prompt.index) + ) { + assertThat( + it.inputPolygon, + equalTo(listOf(MapPoint(0.0, 0.0, 1.0, 1.0), MapPoint(0.0, 1.0, 1.0, 1.0))) + ) + } + } + + @Test + fun `sets answer when REQUEST_GEOPOLY is returned`() { + prompt = MockFormEntryPromptBuilder(prompt) + .build() + + val answer = "0.0 0.0 1.0 1.0; 0.0 1.0 1.0 1.0" + launcherRule.launch( + GeoPolyDialogFragment::class.java, + bundleOf(ARG_FORM_INDEX to prompt.index) + ).onFragment { + it.childFragmentManager.setFragmentResult( + GeoPolyFragment.REQUEST_GEOPOLY, + bundleOf(GeoPolyFragment.RESULT_GEOPOLY to answer) + ) + } + + verify(formEntryViewModel).answerQuestion(prompt.index, StringData(answer)) + } + + @Test + fun `dismisses when REQUEST_GEOPOLY is returned`() { + prompt = MockFormEntryPromptBuilder(prompt) + .build() + + val answer = "0.0 0.0 1.0 1.0; 0.0 1.0 1.0 1.0" + launcherRule.launch( + GeoPolyDialogFragment::class.java, + bundleOf(ARG_FORM_INDEX to prompt.index) + ).onFragment { + it.childFragmentManager.setFragmentResult( + GeoPolyFragment.REQUEST_GEOPOLY, + bundleOf(GeoPolyFragment.RESULT_GEOPOLY to answer) + ) + + assertThat(it.dialog!!.isShowing, equalTo(false)) + } + } + + @Test + fun `does not set answer when REQUEST_GEOPOLY is cancelled`() { + prompt = MockFormEntryPromptBuilder(prompt) + .build() + + launcherRule.launch( + GeoPolyDialogFragment::class.java, + bundleOf(ARG_FORM_INDEX to prompt.index) + ).onFragment { + it.childFragmentManager.setFragmentResult(GeoPolyFragment.REQUEST_GEOPOLY, Bundle.EMPTY) + } + + verify(formEntryViewModel, never()).answerQuestion(any(), any()) + } + + @Test + fun `dismisses when REQUEST_GEOPOLY is cancelled`() { + prompt = MockFormEntryPromptBuilder(prompt) + .build() + + launcherRule.launch( + GeoPolyDialogFragment::class.java, + bundleOf(ARG_FORM_INDEX to prompt.index) + ).onFragment { + it.childFragmentManager.setFragmentResult(GeoPolyFragment.REQUEST_GEOPOLY, Bundle.EMPTY) + assertThat(it.dialog!!.isShowing, equalTo(false)) + } + } +} diff --git a/fragments-test/src/main/java/org/odk/collect/fragmentstest/FragmentScenarioLauncherRule.kt b/fragments-test/src/main/java/org/odk/collect/fragmentstest/FragmentScenarioLauncherRule.kt index 4573c2657e9..7d0f85a0e41 100644 --- a/fragments-test/src/main/java/org/odk/collect/fragmentstest/FragmentScenarioLauncherRule.kt +++ b/fragments-test/src/main/java/org/odk/collect/fragmentstest/FragmentScenarioLauncherRule.kt @@ -7,6 +7,7 @@ import androidx.fragment.app.FragmentFactory import androidx.fragment.app.testing.FragmentScenario import androidx.lifecycle.Lifecycle import org.junit.rules.ExternalResource +import kotlin.reflect.KClass /** * Alternative to [FragmentScenario] that allows tests to do work before launching the [Fragment] @@ -78,6 +79,20 @@ class FragmentScenarioLauncherRule @JvmOverloads constructor( } } + @Suppress("UNCHECKED_CAST") + fun launchAndAssertOnChild( + fragment: KClass, + args: Bundle, + assertion: (Child) -> Unit + ) { + launch( + fragment.java, + args + ).onFragment { + assertion(it.childFragmentManager.fragments[0] as Child) + } + } + override fun after() { scenarios.forEach(FragmentScenario<*>::close) } diff --git a/geo/src/main/AndroidManifest.xml b/geo/src/main/AndroidManifest.xml index b08c144e936..f7b1bc5811f 100644 --- a/geo/src/main/AndroidManifest.xml +++ b/geo/src/main/AndroidManifest.xml @@ -6,10 +6,6 @@ android:name=".geopoint.GeoPointMapActivity" android:theme="@style/Theme.MaterialComponents" /> - - diff --git a/geo/src/main/java/org/odk/collect/geo/DaggerSetup.kt b/geo/src/main/java/org/odk/collect/geo/DaggerSetup.kt index ae4f8cd6eb9..197b997f452 100644 --- a/geo/src/main/java/org/odk/collect/geo/DaggerSetup.kt +++ b/geo/src/main/java/org/odk/collect/geo/DaggerSetup.kt @@ -13,7 +13,7 @@ import org.odk.collect.geo.geopoint.GeoPointDialogFragment import org.odk.collect.geo.geopoint.GeoPointMapActivity import org.odk.collect.geo.geopoint.GeoPointViewModelFactory import org.odk.collect.geo.geopoint.LocationTrackerGeoPointViewModel -import org.odk.collect.geo.geopoly.GeoPolyActivity +import org.odk.collect.geo.geopoly.GeoPolyFragment import org.odk.collect.geo.selection.SelectionMapFragment import org.odk.collect.location.LocationClient import org.odk.collect.location.satellites.SatelliteInfoClient @@ -45,10 +45,10 @@ interface GeoDependencyComponent { } fun inject(geoPointMapActivity: GeoPointMapActivity) - fun inject(geoPolyActivity: GeoPolyActivity) fun inject(geoPointDialogFragment: GeoPointDialogFragment) fun inject(geoPointActivity: GeoPointActivity) fun inject(selectionMapFragment: SelectionMapFragment) + fun inject(geoPolyFragment: GeoPolyFragment) val scheduler: Scheduler val locationTracker: LocationTracker diff --git a/geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolyActivity.java b/geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolyFragment.java similarity index 70% rename from geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolyActivity.java rename to geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolyFragment.java index 2a14eec9ca9..929fb54e0f1 100644 --- a/geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolyActivity.java +++ b/geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolyFragment.java @@ -1,32 +1,17 @@ -/* - * Copyright (C) 2018 Nafundi - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - package org.odk.collect.geo.geopoly; -import static org.odk.collect.geo.Constants.EXTRA_READ_ONLY; -import static org.odk.collect.geo.Constants.EXTRA_RETAIN_MOCK_ACCURACY; import static org.odk.collect.geo.GeoActivityUtils.requireLocationPermissions; -import android.content.Intent; +import android.content.Context; import android.os.Bundle; import android.view.View; -import android.view.Window; import android.widget.Button; import android.widget.ImageButton; import android.widget.TextView; import androidx.activity.OnBackPressedCallback; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentContainerView; @@ -36,8 +21,6 @@ import org.odk.collect.androidshared.ui.FragmentFactoryBuilder; import org.odk.collect.androidshared.ui.ToastUtils; import org.odk.collect.async.Scheduler; -import org.odk.collect.externalapp.ExternalAppUtils; -import org.odk.collect.geo.Constants; import org.odk.collect.geo.GeoDependencyComponentProvider; import org.odk.collect.geo.GeoUtils; import org.odk.collect.geo.R; @@ -53,10 +36,10 @@ import org.odk.collect.maps.layers.OfflineMapLayersPickerBottomSheetDialogFragment; import org.odk.collect.maps.layers.ReferenceLayerRepository; import org.odk.collect.settings.SettingsProvider; -import org.odk.collect.strings.localization.LocalizedActivity; import org.odk.collect.webpage.WebPageService; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -65,9 +48,11 @@ import javax.inject.Inject; -public class GeoPolyActivity extends LocalizedActivity implements GeoPolySettingsDialogFragment.SettingsDialogCallback { - public static final String EXTRA_POLYGON = "answer"; - public static final String OUTPUT_MODE_KEY = "output_mode"; +public class GeoPolyFragment extends Fragment implements GeoPolySettingsDialogFragment.SettingsDialogCallback { + + public static final String REQUEST_GEOPOLY = "geopoly"; + public static final String RESULT_GEOPOLY = "geopoly"; + public static final String POINTS_KEY = "points"; public static final String INPUT_ACTIVE_KEY = "input_active"; public static final String RECORDING_ENABLED_KEY = "recording_enabled"; @@ -76,12 +61,13 @@ public class GeoPolyActivity extends LocalizedActivity implements GeoPolySetting public static final String ACCURACY_THRESHOLD_INDEX_KEY = "accuracy_threshold_index"; protected Bundle previousState; - public enum OutputMode { GEOTRACE, GEOSHAPE } - private final ScheduledExecutorService executorServiceScheduler = Executors.newSingleThreadScheduledExecutor(); private ScheduledFuture schedulerHandler; - private OutputMode outputMode; + public OutputMode outputMode; + public final Boolean readOnly; + public final List inputPolygon; + public final Boolean retainMockAccuracy; @Inject MapFragmentFactory mapFragmentFactory; @@ -119,19 +105,18 @@ public enum OutputMode { GEOTRACE, GEOSHAPE } private View settingsView; private static final int[] INTERVAL_OPTIONS = { - 1, 5, 10, 20, 30, 60, 300, 600, 1200, 1800 + 1, 5, 10, 20, 30, 60, 300, 600, 1200, 1800 }; private static final int DEFAULT_INTERVAL_INDEX = 3; // default is 20 seconds private static final int[] ACCURACY_THRESHOLD_OPTIONS = { - 0, 3, 5, 10, 15, 20 + 0, 3, 5, 10, 15, 20 }; private static final int DEFAULT_ACCURACY_THRESHOLD_INDEX = 3; // default is 10 meters private boolean inputActive; // whether we are ready for the user to add points private boolean recordingEnabled; // whether points are taken from GPS readings (if not, placed by tapping) private boolean recordingAutomatic; // whether GPS readings are taken at regular intervals (if not, only when user-directed) - private boolean intentReadOnly; // whether the intent requested for the path to be read-only. private int intervalIndex = DEFAULT_INTERVAL_INDEX; @@ -143,26 +128,44 @@ public enum OutputMode { GEOTRACE, GEOSHAPE } private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) { @Override public void handleOnBackPressed() { - if (!intentReadOnly && map != null && !originalPoly.equals(map.getPolyLinePoints(featureId))) { + if (!readOnly && map != null && !originalPoly.equals(map.getPolyLinePoints(featureId))) { showBackDialog(); } else { - finish(); + cancel(); } } }; - @Override public void onCreate(Bundle savedInstanceState) { - ((GeoDependencyComponentProvider) getApplication()).getGeoDependencyComponent().inject(this); + public GeoPolyFragment(@Nullable OutputMode outputMode, Boolean readOnly, Boolean retainMockAccuracy, @NonNull List inputPolygon) { + super(R.layout.geopoly_layout); + this.outputMode = outputMode; + this.readOnly = readOnly; + this.inputPolygon = inputPolygon; + this.retainMockAccuracy = retainMockAccuracy; + } + + public GeoPolyFragment(@Nullable OutputMode outputMode) { + this(outputMode, false, false, Collections.emptyList()); + } + + @Override + public void onAttach(@NonNull Context context) { + super.onAttach(context); + ((GeoDependencyComponentProvider) context.getApplicationContext()).getGeoDependencyComponent().inject(this); - getSupportFragmentManager().setFragmentFactory(new FragmentFactoryBuilder() + getChildFragmentManager().setFragmentFactory(new FragmentFactoryBuilder() .forClass(MapFragment.class, () -> (Fragment) mapFragmentFactory.createMapFragment()) - .forClass(OfflineMapLayersPickerBottomSheetDialogFragment.class, () -> new OfflineMapLayersPickerBottomSheetDialogFragment(getActivityResultRegistry(), referenceLayerRepository, scheduler, settingsProvider, webPageService)) + .forClass(OfflineMapLayersPickerBottomSheetDialogFragment.class, () -> new OfflineMapLayersPickerBottomSheetDialogFragment(requireActivity().getActivityResultRegistry(), referenceLayerRepository, scheduler, settingsProvider, webPageService)) + .forClass(GeoPolySettingsDialogFragment.class, () -> new GeoPolySettingsDialogFragment(this)) .build() ); - super.onCreate(savedInstanceState); + requireLocationPermissions(requireActivity()); + requireActivity().getOnBackPressedDispatcher().addCallback(onBackPressedCallback); + } - requireLocationPermissions(this); + @Override public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); previousState = savedInstanceState; @@ -173,24 +176,18 @@ public void handleOnBackPressed() { recordingAutomatic = savedInstanceState.getBoolean(RECORDING_AUTOMATIC_KEY, false); intervalIndex = savedInstanceState.getInt(INTERVAL_INDEX_KEY, DEFAULT_INTERVAL_INDEX); accuracyThresholdIndex = savedInstanceState.getInt( - ACCURACY_THRESHOLD_INDEX_KEY, DEFAULT_ACCURACY_THRESHOLD_INDEX); + ACCURACY_THRESHOLD_INDEX_KEY, DEFAULT_ACCURACY_THRESHOLD_INDEX); } + } - intentReadOnly = getIntent().getBooleanExtra(EXTRA_READ_ONLY, false); - outputMode = (OutputMode) getIntent().getSerializableExtra(OUTPUT_MODE_KEY); - - requestWindowFeature(Window.FEATURE_NO_TITLE); - setTitle(getString(outputMode == OutputMode.GEOTRACE ? - org.odk.collect.strings.R.string.geotrace_title : org.odk.collect.strings.R.string.geoshape_title)); - setContentView(R.layout.geopoly_layout); - - MapFragment mapFragment = ((FragmentContainerView) findViewById(R.id.map_container)).getFragment(); - mapFragment.init(this::initMap, this::finish); - - getOnBackPressedDispatcher().addCallback(onBackPressedCallback); + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + MapFragment mapFragment = ((FragmentContainerView) view.findViewById(R.id.map_container)).getFragment(); + mapFragment.init(mapFragment1 -> initMap(view, mapFragment1), this::cancel); } - @Override protected void onSaveInstanceState(Bundle state) { + @Override + public void onSaveInstanceState(Bundle state) { super.onSaveInstanceState(state); if (map == null) { // initMap() is called asynchronously, so map can be null if the activity @@ -209,7 +206,8 @@ public void handleOnBackPressed() { state.putInt(ACCURACY_THRESHOLD_INDEX_KEY, accuracyThresholdIndex); } - @Override protected void onDestroy() { + @Override + public void onDestroy() { if (schedulerHandler != null && !schedulerHandler.isCancelled()) { schedulerHandler.cancel(true); } @@ -218,17 +216,17 @@ public void handleOnBackPressed() { super.onDestroy(); } - public void initMap(MapFragment newMapFragment) { + public void initMap(View view, MapFragment newMapFragment) { map = newMapFragment; - locationStatus = findViewById(R.id.location_status); - collectionStatus = findViewById(R.id.collection_status); + locationStatus = view.findViewById(R.id.location_status); + collectionStatus = view.findViewById(R.id.collection_status); settingsView = getLayoutInflater().inflate(R.layout.geopoly_dialog, null); - clearButton = findViewById(R.id.clear); + clearButton = view.findViewById(R.id.clear); clearButton.setOnClickListener(v -> showClearDialog()); - pauseButton = findViewById(R.id.pause); + pauseButton = view.findViewById(R.id.pause); pauseButton.setOnClickListener(v -> { inputActive = false; try { @@ -239,10 +237,10 @@ public void initMap(MapFragment newMapFragment) { updateUi(); }); - backspaceButton = findViewById(R.id.backspace); + backspaceButton = view.findViewById(R.id.backspace); backspaceButton.setOnClickListener(v -> removeLastPoint()); - saveButton = findViewById(R.id.save); + saveButton = view.findViewById(R.id.save); saveButton.setOnClickListener(v -> { if (!map.getPolyLinePoints(featureId).isEmpty()) { if (outputMode == OutputMode.GEOTRACE) { @@ -251,51 +249,46 @@ public void initMap(MapFragment newMapFragment) { saveAsPolygon(); } } else { - finishWithResult(); + setResult(); } }); - playButton = findViewById(R.id.play); + playButton = view.findViewById(R.id.play); playButton.setOnClickListener(v -> { if (map.getPolyLinePoints(featureId).isEmpty()) { - DialogFragmentUtils.showIfNotShowing(GeoPolySettingsDialogFragment.class, getSupportFragmentManager()); + DialogFragmentUtils.showIfNotShowing(GeoPolySettingsDialogFragment.class, getChildFragmentManager()); } else { startInput(); } }); - recordButton = findViewById(R.id.record_button); + recordButton = view.findViewById(R.id.record_button); recordButton.setOnClickListener(v -> recordPoint(map.getGpsLocation())); - findViewById(R.id.layers).setOnClickListener(v -> { - DialogFragmentUtils.showIfNotShowing(OfflineMapLayersPickerBottomSheetDialogFragment.class, getSupportFragmentManager()); + view.findViewById(R.id.layers).setOnClickListener(v -> { + DialogFragmentUtils.showIfNotShowing(OfflineMapLayersPickerBottomSheetDialogFragment.class, getChildFragmentManager()); }); - zoomButton = findViewById(R.id.zoom); + zoomButton = view.findViewById(R.id.zoom); zoomButton.setOnClickListener(v -> map.zoomToCurrentLocation(map.getGpsLocation())); List points = new ArrayList<>(); - Intent intent = getIntent(); - if (intent != null && intent.hasExtra(EXTRA_POLYGON)) { - ArrayList extraPoly = intent.getParcelableArrayListExtra(EXTRA_POLYGON); - - if (!extraPoly.isEmpty()) { - if (outputMode == OutputMode.GEOSHAPE) { - points = extraPoly.subList(0, extraPoly.size() - 1); - } else { - points = extraPoly; - } + if (!inputPolygon.isEmpty()) { + if (outputMode == OutputMode.GEOSHAPE) { + points = inputPolygon.subList(0, inputPolygon.size() - 1); + } else { + points = inputPolygon; } - - originalPoly = extraPoly; } + originalPoly = inputPolygon; + if (restoredPoints != null) { points = restoredPoints; } - featureId = map.addPolyLine(new LineDescription(points, String.valueOf(MapConsts.DEFAULT_STROKE_WIDTH), null, !intentReadOnly, outputMode == OutputMode.GEOSHAPE)); + featureId = map.addPolyLine(new LineDescription(points, String.valueOf(MapConsts.DEFAULT_STROKE_WIDTH), null, !readOnly, outputMode == OutputMode.GEOSHAPE)); - if (inputActive && !intentReadOnly) { + if (inputActive && !readOnly) { startInput(); } @@ -304,7 +297,7 @@ public void initMap(MapFragment newMapFragment) { map.setLongPressListener(this::onClick); map.setGpsLocationEnabled(true); map.setGpsLocationListener(this::onGpsLocation); - map.setRetainMockAccuracy(intent.getBooleanExtra(EXTRA_RETAIN_MOCK_ACCURACY, false)); + map.setRetainMockAccuracy(retainMockAccuracy); if (!map.hasCenter()) { if (!points.isEmpty()) { @@ -319,9 +312,9 @@ public void initMap(MapFragment newMapFragment) { private void saveAsPolyline() { if (map.getPolyLinePoints(featureId).size() > 1) { - finishWithResult(); + setResult(); } else { - ToastUtils.showShortToastInMiddle(this, getString(org.odk.collect.strings.R.string.polyline_validator)); + ToastUtils.showShortToastInMiddle(requireActivity(), getString(org.odk.collect.strings.R.string.polyline_validator)); } } @@ -333,27 +326,28 @@ private void saveAsPolygon() { if (count > 1 && !points.get(0).equals(points.get(count - 1))) { map.appendPointToPolyLine(featureId, points.get(0)); } - finishWithResult(); + setResult(); } else { - ToastUtils.showShortToastInMiddle(this, getString(org.odk.collect.strings.R.string.polygon_validator)); + ToastUtils.showShortToastInMiddle(requireActivity(), getString(org.odk.collect.strings.R.string.polygon_validator)); } } - private void finishWithResult() { + private void setResult() { List points = map.getPolyLinePoints(featureId); String result = GeoUtils.formatPointsResultString(points, outputMode.equals(OutputMode.GEOSHAPE)); - ExternalAppUtils.returnSingleValue(this, result); + Bundle bundle = new Bundle(); + bundle.putString(RESULT_GEOPOLY, result); + getParentFragmentManager().setFragmentResult(REQUEST_GEOPOLY, bundle); } @Override public void startInput() { inputActive = true; if (recordingEnabled && recordingAutomatic) { - boolean retainMockAccuracy = getIntent().getBooleanExtra(Constants.EXTRA_RETAIN_MOCK_ACCURACY, false); locationTracker.start(retainMockAccuracy); recordPoint(map.getGpsLocation()); - schedulerHandler = executorServiceScheduler.scheduleAtFixedRate(() -> runOnUiThread(() -> { + schedulerHandler = executorServiceScheduler.scheduleAtFixedRate(() -> requireActivity().runOnUiThread(() -> { Location currentLocation = locationTracker.getCurrentLocation(); if (currentLocation != null) { @@ -406,6 +400,10 @@ public void setAccuracyThresholdIndex(int accuracyThresholdIndex) { this.accuracyThresholdIndex = accuracyThresholdIndex; } + private void cancel() { + getParentFragmentManager().setFragmentResult(REQUEST_GEOPOLY, Bundle.EMPTY); + } + private void onClick(MapPoint point) { if (inputActive && !recordingEnabled) { appendPointIfNew(point); @@ -414,7 +412,7 @@ private void onClick(MapPoint point) { private void onGpsLocationReady(MapFragment map) { // Don't zoom to current location if a user is manually entering points - if (getWindow().isActive() && (!inputActive || recordingEnabled)) { + if (requireActivity().getWindow().isActive() && (!inputActive || recordingEnabled)) { map.zoomToCurrentLocation(map.getGpsLocation()); } updateUi(); @@ -462,7 +460,7 @@ private void removeLastPoint() { private void clear() { map.clearFeatures(); - featureId = map.addPolyLine(new LineDescription(new ArrayList<>(), String.valueOf(MapConsts.DEFAULT_STROKE_WIDTH), null, !intentReadOnly, outputMode == OutputMode.GEOSHAPE)); + featureId = map.addPolyLine(new LineDescription(new ArrayList<>(), String.valueOf(MapConsts.DEFAULT_STROKE_WIDTH), null, !readOnly, outputMode == OutputMode.GEOSHAPE)); inputActive = false; updateUi(); } @@ -484,7 +482,7 @@ private void updateUi() { settingsView.findViewById(R.id.manual_mode).setEnabled(location != null); settingsView.findViewById(R.id.automatic_mode).setEnabled(location != null); - if (intentReadOnly) { + if (readOnly) { playButton.setEnabled(false); backspaceButton.setEnabled(false); clearButton.setEnabled(false); @@ -508,38 +506,42 @@ private void updateUi() { } collectionStatus.setText( - !inputActive ? getString(org.odk.collect.strings.R.string.collection_status_paused, numPoints) - : !recordingEnabled ? getString(org.odk.collect.strings.R.string.collection_status_placement, numPoints) - : !recordingAutomatic ? getString(org.odk.collect.strings.R.string.collection_status_manual, numPoints) - : !usingThreshold ? ( - minutes > 0 ? - getString(org.odk.collect.strings.R.string.collection_status_auto_minutes, numPoints, minutes) : - getString(org.odk.collect.strings.R.string.collection_status_auto_seconds, numPoints, seconds) + !inputActive ? getString(org.odk.collect.strings.R.string.collection_status_paused, numPoints) + : !recordingEnabled ? getString(org.odk.collect.strings.R.string.collection_status_placement, numPoints) + : !recordingAutomatic ? getString(org.odk.collect.strings.R.string.collection_status_manual, numPoints) + : !usingThreshold ? ( + minutes > 0 ? + getString(org.odk.collect.strings.R.string.collection_status_auto_minutes, numPoints, minutes) : + getString(org.odk.collect.strings.R.string.collection_status_auto_seconds, numPoints, seconds) ) - : ( - minutes > 0 ? - getString(org.odk.collect.strings.R.string.collection_status_auto_minutes_accuracy, numPoints, minutes, meters) : - getString(org.odk.collect.strings.R.string.collection_status_auto_seconds_accuracy, numPoints, seconds, meters) + : ( + minutes > 0 ? + getString(org.odk.collect.strings.R.string.collection_status_auto_minutes_accuracy, numPoints, minutes, meters) : + getString(org.odk.collect.strings.R.string.collection_status_auto_seconds_accuracy, numPoints, seconds, meters) ) ); } private void showClearDialog() { if (!map.getPolyLinePoints(featureId).isEmpty()) { - new MaterialAlertDialogBuilder(this) - .setMessage(org.odk.collect.strings.R.string.geo_clear_warning) - .setPositiveButton(org.odk.collect.strings.R.string.clear, (dialog, id) -> clear()) - .setNegativeButton(org.odk.collect.strings.R.string.cancel, null) - .show(); + new MaterialAlertDialogBuilder(requireContext()) + .setMessage(org.odk.collect.strings.R.string.geo_clear_warning) + .setPositiveButton(org.odk.collect.strings.R.string.clear, (dialog, id) -> clear()) + .setNegativeButton(org.odk.collect.strings.R.string.cancel, null) + .show(); } } private void showBackDialog() { - new MaterialAlertDialogBuilder(this) - .setMessage(getString(org.odk.collect.strings.R.string.geo_exit_warning)) - .setPositiveButton(org.odk.collect.strings.R.string.discard, (dialog, id) -> finish()) - .setNegativeButton(org.odk.collect.strings.R.string.cancel, null) - .show(); + new MaterialAlertDialogBuilder(requireContext()) + .setMessage(getString(org.odk.collect.strings.R.string.geo_exit_warning)) + .setPositiveButton(org.odk.collect.strings.R.string.discard, (dialog, id) -> cancel()) + .setNegativeButton(org.odk.collect.strings.R.string.cancel, null) + .show(); + + } + public enum OutputMode { + GEOTRACE, GEOSHAPE } } diff --git a/geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolySettingsDialogFragment.java b/geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolySettingsDialogFragment.java index 977710bbfbb..fa5c585480f 100644 --- a/geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolySettingsDialogFragment.java +++ b/geo/src/main/java/org/odk/collect/geo/geopoly/GeoPolySettingsDialogFragment.java @@ -1,7 +1,6 @@ package org.odk.collect.geo.geopoly; import android.app.Dialog; -import android.content.Context; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; @@ -29,19 +28,14 @@ public class GeoPolySettingsDialogFragment extends DialogFragment { private View autoOptions; private RadioGroup radioGroup; - protected SettingsDialogCallback callback; + private final SettingsDialogCallback callback; private int checkedRadioButtonId = -1; private int intervalIndex = -1; private int accuracyThresholdIndex = -1; - @Override - public void onAttach(@NonNull Context context) { - super.onAttach(context); - - if (context instanceof SettingsDialogCallback) { - callback = (SettingsDialogCallback) context; - } + public GeoPolySettingsDialogFragment(SettingsDialogCallback callback) { + this.callback = callback; } @NonNull diff --git a/geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolyActivityTest.kt b/geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolyFragmentTest.kt similarity index 53% rename from geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolyActivityTest.kt rename to geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolyFragmentTest.kt index 43e1b771c19..9bc95d7949d 100644 --- a/geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolyActivityTest.kt +++ b/geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolyFragmentTest.kt @@ -1,8 +1,8 @@ package org.odk.collect.geo.geopoly -import android.app.Activity import android.app.Application -import android.content.Intent +import android.os.Bundle +import androidx.fragment.app.FragmentResultListener import androidx.lifecycle.Lifecycle import androidx.test.core.app.ApplicationProvider import androidx.test.espresso.Espresso @@ -12,6 +12,7 @@ import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.RootMatchers.isDialog import androidx.test.espresso.matcher.ViewMatchers.assertThat import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.ext.junit.runners.AndroidJUnit4 import org.hamcrest.Matchers.equalTo @@ -23,14 +24,13 @@ import org.junit.runner.RunWith import org.mockito.Mockito import org.mockito.kotlin.mock import org.mockito.kotlin.verify -import org.odk.collect.androidtest.ActivityScenarioExtensions.isFinishing -import org.odk.collect.androidtest.ActivityScenarioLauncherRule +import org.odk.collect.androidshared.ui.FragmentFactoryBuilder import org.odk.collect.async.Scheduler -import org.odk.collect.geo.Constants -import org.odk.collect.geo.Constants.EXTRA_RETAIN_MOCK_ACCURACY +import org.odk.collect.fragmentstest.FragmentScenarioLauncherRule import org.odk.collect.geo.DaggerGeoDependencyComponent import org.odk.collect.geo.GeoDependencyModule import org.odk.collect.geo.R +import org.odk.collect.geo.geopoly.GeoPolyFragment.OutputMode import org.odk.collect.geo.support.FakeMapFragment import org.odk.collect.geo.support.RobolectricApplication import org.odk.collect.location.tracker.LocationTracker @@ -40,16 +40,18 @@ import org.odk.collect.maps.MapPoint import org.odk.collect.maps.layers.ReferenceLayerRepository import org.odk.collect.settings.InMemSettingsProvider import org.odk.collect.settings.SettingsProvider +import org.odk.collect.strings.R.string +import org.odk.collect.testshared.Assertions import org.odk.collect.webpage.WebPageService import org.robolectric.Shadows @RunWith(AndroidJUnit4::class) -class GeoPolyActivityTest { +class GeoPolyFragmentTest { private val mapFragment = FakeMapFragment() private val locationTracker = mock() @get:Rule - val launcherRule = ActivityScenarioLauncherRule() + val fragmentLauncherRule = FragmentScenarioLauncherRule() @Before fun setUp() { @@ -93,8 +95,11 @@ class GeoPolyActivityTest { @Test fun testLocationTrackerLifecycle() { - val scenario = launcherRule.launch( - GeoPolyActivity::class.java + val scenario = fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { GeoPolyFragment(OutputMode.GEOTRACE) } + .build() ) mapFragment.ready() @@ -105,7 +110,13 @@ class GeoPolyActivityTest { @Test fun recordButton_should_beHiddenForAutomaticMode() { - launcherRule.launch(GeoPolyActivity::class.java) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { GeoPolyFragment(OutputMode.GEOTRACE) } + .build() + ) + mapFragment.ready() startInput(R.id.automatic_mode) onView(withId(R.id.record_button)).check(matches(not(isDisplayed()))) @@ -113,7 +124,13 @@ class GeoPolyActivityTest { @Test fun recordButton_should_beVisibleForManualMode() { - launcherRule.launch(GeoPolyActivity::class.java) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { GeoPolyFragment(OutputMode.GEOTRACE) } + .build() + ) + mapFragment.ready() startInput(R.id.manual_mode) onView(withId(R.id.record_button)).check(matches(isDisplayed())) @@ -121,12 +138,17 @@ class GeoPolyActivityTest { @Test fun whenPolygonExtraPresent_showsPoly() { - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) val polygon = ArrayList() polygon.add(MapPoint(1.0, 2.0, 3.0, 4.0)) - intent.putExtra(GeoPolyActivity.EXTRA_POLYGON, polygon) - launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, false, false, polygon) + } + .build() + ) + mapFragment.ready() val polys = mapFragment.getPolyLines() assertThat(polys.size, equalTo(1)) @@ -135,15 +157,19 @@ class GeoPolyActivityTest { @Test fun whenPolygonExtraPresent_andOutputModeIsShape_showsClosedPoly() { - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) val polygon = ArrayList() polygon.add(MapPoint(1.0, 2.0, 3.0, 4.0)) polygon.add(MapPoint(2.0, 3.0, 3.0, 4.0)) polygon.add(MapPoint(1.0, 2.0, 3.0, 4.0)) - intent.putExtra(GeoPolyActivity.EXTRA_POLYGON, polygon) - intent.putExtra(GeoPolyActivity.OUTPUT_MODE_KEY, GeoPolyActivity.OutputMode.GEOSHAPE) - launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOSHAPE, false, false, polygon) + } + .build() + ) + mapFragment.ready() val polys = mapFragment.getPolyLines() assertThat(polys.size, equalTo(1)) @@ -156,12 +182,15 @@ class GeoPolyActivityTest { @Test fun whenPolygonExtraPresent_andPolyIsEmpty_andOutputModeIsShape_doesNotShowPoly() { - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) - val polygon = ArrayList() - intent.putExtra(GeoPolyActivity.EXTRA_POLYGON, polygon) - intent.putExtra(GeoPolyActivity.OUTPUT_MODE_KEY, GeoPolyActivity.OutputMode.GEOSHAPE) - launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOSHAPE, false, false, emptyList()) + } + .build() + ) + mapFragment.ready() val polys = mapFragment.getPolyLines() assertThat(polys.size, equalTo(1)) @@ -169,24 +198,36 @@ class GeoPolyActivityTest { } @Test - fun whenPolygonExtraPresent_andPolyIsEmpty_pressingBack_finishes() { - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) - val polygon = ArrayList() - intent.putExtra(GeoPolyActivity.EXTRA_POLYGON, polygon) - intent.putExtra(GeoPolyActivity.OUTPUT_MODE_KEY, GeoPolyActivity.OutputMode.GEOSHAPE) - val scenario = launcherRule.launch(intent) + fun whenPolygonExtraPresent_andPolyIsEmpty_pressingBack_setsCancelledResult() { + val scenario = fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, false, false, emptyList()) + } + .build() + ) + mapFragment.ready() + + val resultListener = mock() + scenario.onFragment { it.parentFragmentManager.setFragmentResultListener(GeoPolyFragment.REQUEST_GEOPOLY, it, resultListener) } + Espresso.pressBack() - assertThat(scenario.isFinishing, equalTo(true)) + verify(resultListener).onFragmentResult(GeoPolyFragment.REQUEST_GEOPOLY, Bundle.EMPTY) } @Test fun startingInput_usingAutomaticMode_usesRetainMockAccuracyTrueToStartLocationTracker() { - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) - intent.putExtra(Constants.EXTRA_RETAIN_MOCK_ACCURACY, true) - launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, false, true, emptyList()) + } + .build() + ) + mapFragment.ready() startInput(R.id.automatic_mode) verify(locationTracker).start(true) @@ -194,10 +235,15 @@ class GeoPolyActivityTest { @Test fun startingInput_usingAutomaticMode_usesRetainMockAccuracyFalseToStartLocationTracker() { - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) - intent.putExtra(Constants.EXTRA_RETAIN_MOCK_ACCURACY, false) - launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, false, false, emptyList()) + } + .build() + ) + mapFragment.ready() startInput(R.id.automatic_mode) verify(locationTracker).start(false) @@ -205,7 +251,13 @@ class GeoPolyActivityTest { @Test fun recordingPointManually_whenPointIsADuplicateOfTheLastPoint_skipsPoint() { - launcherRule.launch(GeoPolyActivity::class.java) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { GeoPolyFragment(OutputMode.GEOTRACE) } + .build() + ) + mapFragment.ready() startInput(R.id.manual_mode) mapFragment.setLocation(MapPoint(1.0, 1.0)) @@ -216,7 +268,13 @@ class GeoPolyActivityTest { @Test fun placingPoint_whenPointIsADuplicateOfTheLastPoint_skipsPoint() { - launcherRule.launch(GeoPolyActivity::class.java) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { GeoPolyFragment(OutputMode.GEOTRACE) } + .build() + ) + mapFragment.ready() startInput(R.id.placement_mode) mapFragment.click(MapPoint(1.0, 1.0)) @@ -228,45 +286,55 @@ class GeoPolyActivityTest { fun buttonsShouldBeEnabledInEditableMode() { val polyline = ArrayList() polyline.add(MapPoint(1.0, 2.0, 3.0, 4.0)) - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) - intent.putExtra(GeoPolyActivity.EXTRA_POLYGON, polyline) - val scenario = launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, false, false, polyline) + } + .build() + ) + mapFragment.ready() - scenario.onActivity { activity: GeoPolyActivity -> - assertThat(activity.playButton.isEnabled, equalTo(true)) - assertThat(activity.backspaceButton.isEnabled, equalTo(true)) - assertThat(activity.clearButton.isEnabled, equalTo(true)) - assertThat(activity.saveButton.isEnabled, equalTo(true)) - } + Assertions.assertEnabled(withContentDescription(string.input_method)) + Assertions.assertEnabled(withContentDescription(string.remove_last_point)) + Assertions.assertEnabled(withContentDescription(string.clear)) + Assertions.assertEnabled(withContentDescription(string.save)) } @Test fun buttonsShouldBeDisabledInReadOnlyMode() { val polygon = ArrayList() polygon.add(MapPoint(1.0, 2.0, 3.0, 4.0)) - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) - intent.putExtra(GeoPolyActivity.EXTRA_POLYGON, polygon) - intent.putExtra(Constants.EXTRA_READ_ONLY, true) - val scenario = launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, true, false, polygon) + } + .build() + ) + mapFragment.ready() - scenario.onActivity { activity: GeoPolyActivity -> - assertThat(activity.playButton.isEnabled, equalTo(false)) - assertThat(activity.backspaceButton.isEnabled, equalTo(false)) - assertThat(activity.clearButton.isEnabled, equalTo(false)) - assertThat(activity.saveButton.isEnabled, equalTo(false)) - } + Assertions.assertDisabled(withContentDescription(string.input_method)) + Assertions.assertDisabled(withContentDescription(string.remove_last_point)) + Assertions.assertDisabled(withContentDescription(string.clear)) + Assertions.assertDisabled(withContentDescription(string.save)) } @Test fun polyShouldBeDraggableInEditableMode() { val polyline = ArrayList() polyline.add(MapPoint(1.0, 2.0, 3.0, 4.0)) - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) - intent.putExtra(GeoPolyActivity.EXTRA_POLYGON, polyline) - launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, false, false, polyline) + } + .build() + ) + mapFragment.ready() assertThat(mapFragment.isPolyDraggable(0), equalTo(true)) } @@ -275,37 +343,52 @@ class GeoPolyActivityTest { fun polyShouldNotBeDraggableInReadOnlyMode() { val polygon = ArrayList() polygon.add(MapPoint(1.0, 2.0, 3.0, 4.0)) - val intent = - Intent(ApplicationProvider.getApplicationContext(), GeoPolyActivity::class.java) - intent.putExtra(GeoPolyActivity.EXTRA_POLYGON, polygon) - intent.putExtra(Constants.EXTRA_READ_ONLY, true) - launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, true, false, polygon) + } + .build() + ) + mapFragment.ready() assertThat(mapFragment.isPolyDraggable(0), equalTo(false)) } @Test fun passingRetainMockAccuracyExtra_updatesMapFragmentState() { - val intent = Intent( - ApplicationProvider.getApplicationContext(), - GeoPolyActivity::class.java + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, false, true, emptyList()) + } + .build() ) - intent.putExtra(EXTRA_RETAIN_MOCK_ACCURACY, true) - launcherRule.launch(intent) mapFragment.ready() - assertThat(mapFragment.isRetainMockAccuracy(), equalTo(true)) - intent.putExtra(EXTRA_RETAIN_MOCK_ACCURACY, false) - launcherRule.launch(intent) + fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { + GeoPolyFragment(OutputMode.GEOTRACE, false, false, emptyList()) + } + .build() + ) mapFragment.ready() - assertThat(mapFragment.isRetainMockAccuracy(), equalTo(false)) } @Test - fun recreatingTheActivityWithTheLayersDialogDisplayedDoesNotCrashTheApp() { - val scenario = launcherRule.launch(GeoPolyActivity::class.java) + fun recreatingTheFragmentWithTheLayersDialogDisplayedDoesNotCrashTheApp() { + val scenario = fragmentLauncherRule.launchInContainer( + GeoPolyFragment::class.java, + factory = FragmentFactoryBuilder() + .forClass(GeoPolyFragment::class) { GeoPolyFragment(OutputMode.GEOTRACE) } + .build() + ) mapFragment.ready() onView(withId(R.id.layers)).perform(click()) diff --git a/geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolySettingsDialogFragmentTest.java b/geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolySettingsDialogFragmentTest.java index 5c40bd3897f..f39d0962b3f 100644 --- a/geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolySettingsDialogFragmentTest.java +++ b/geo/src/test/java/org/odk/collect/geo/geopoly/GeoPolySettingsDialogFragmentTest.java @@ -6,7 +6,6 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertFalse; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @@ -36,14 +35,14 @@ public class GeoPolySettingsDialogFragmentTest { private FragmentManager fragmentManager; private GeoPolySettingsDialogFragment dialogFragment; + private GeoPolySettingsDialogFragment.SettingsDialogCallback callback; @Before public void setup() { FragmentActivity activity = RobolectricHelpers.createThemedActivity(FragmentActivity.class); fragmentManager = activity.getSupportFragmentManager(); - dialogFragment = new GeoPolySettingsDialogFragment(); - - dialogFragment.callback = mock(GeoPolySettingsDialogFragment.SettingsDialogCallback.class); + callback = Mockito.mock(GeoPolySettingsDialogFragment.SettingsDialogCallback.class); + dialogFragment = new GeoPolySettingsDialogFragment(callback); } @Test @@ -102,11 +101,11 @@ public void clickingStart_callsCorrectMethodsInCorrectOrder() { dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick(); RobolectricHelpers.runLooper(); - InOrder orderVerifier = Mockito.inOrder(dialogFragment.callback); - orderVerifier.verify(dialogFragment.callback).updateRecordingMode(sampleId); - orderVerifier.verify(dialogFragment.callback).setIntervalIndex(2); - orderVerifier.verify(dialogFragment.callback).setAccuracyThresholdIndex(2); - orderVerifier.verify(dialogFragment.callback).startInput(); + InOrder orderVerifier = Mockito.inOrder(callback); + orderVerifier.verify(callback).updateRecordingMode(sampleId); + orderVerifier.verify(callback).setIntervalIndex(2); + orderVerifier.verify(callback).setAccuracyThresholdIndex(2); + orderVerifier.verify(callback).startInput(); } @Test @@ -139,9 +138,9 @@ public void notSelectingAutomaticMode_doesNotDisplayIntervalAndAccuracyOptions() @Test public void creatingDialog_showsCorrectView() { - when(dialogFragment.callback.getCheckedId()).thenReturn(sampleId); - when(dialogFragment.callback.getIntervalIndex()).thenReturn(2); - when(dialogFragment.callback.getAccuracyThresholdIndex()).thenReturn(2); + when(callback.getCheckedId()).thenReturn(sampleId); + when(callback.getIntervalIndex()).thenReturn(2); + when(callback.getAccuracyThresholdIndex()).thenReturn(2); dialogFragment.show(fragmentManager, "TAG"); RobolectricHelpers.runLooper(); diff --git a/strings/src/main/res/values-ar/strings.xml b/strings/src/main/res/values-ar/strings.xml index 120734566f3..1ee5da0332f 100644 --- a/strings/src/main/res/values-ar/strings.xml +++ b/strings/src/main/res/values-ar/strings.xml @@ -443,12 +443,10 @@ جنوب %1$s%2$s%3$s شمال %1$s%2$s%3$s - مضلع - طريقة الإدخال + طريقة الإدخال إبدأ تفعيل GPS - تتبع المسار - GPS غير مفعلة على جهازك. هل تريد تفعيلها؟ + GPS غير مفعلة على جهازك. هل تريد تفعيلها؟ الميزة تم إنشاؤها مسبقاً. هل ترغب في إزالتها؟ تجاهل التعديلات والعودة إلى ODK؟ يجب توفر 3 نقاط لإنشاء مضلع diff --git a/strings/src/main/res/values-ca/strings.xml b/strings/src/main/res/values-ca/strings.xml index 78e59c0bff4..37a095cc089 100644 --- a/strings/src/main/res/values-ca/strings.xml +++ b/strings/src/main/res/values-ca/strings.xml @@ -254,11 +254,9 @@ - GeoForma - Comença + Comença Activa GPS - GeoTraça - Característica ja creada. Vols netejar-la? + Característica ja creada. Vols netejar-la? Descartar canvis i tornar a ODK? Per crear un polígon calen mínim 3 punts Neteja diff --git a/strings/src/main/res/values-cs/strings.xml b/strings/src/main/res/values-cs/strings.xml index 0b959e8e6db..d6565e0aed4 100644 --- a/strings/src/main/res/values-cs/strings.xml +++ b/strings/src/main/res/values-cs/strings.xml @@ -490,12 +490,10 @@ Jižně %1$s%2$s%3$s Severně %1$s%2$s%3$s - GeoShape - Vstupní metoda + Vstupní metoda Začít Zapnout GPS - GeoTrace - GPS je ve vašem zařízení deaktivováno. Chcete ho povolit? + GPS je ve vašem zařízení deaktivováno. Chcete ho povolit? Funkce již vytvořena. Chcete tuto funkci vymazat? Zrušit změny a vrátit se do ODK? Musí mít alespoň 3 body pro vytvoření polygonu diff --git a/strings/src/main/res/values-da/strings.xml b/strings/src/main/res/values-da/strings.xml index aa1263f4825..57a2712159f 100644 --- a/strings/src/main/res/values-da/strings.xml +++ b/strings/src/main/res/values-da/strings.xml @@ -276,12 +276,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - GeoShape - Inputmetode + Inputmetode Start Tænd GPS - GeoTrace - GPS er slukket på dit udstyr. Har du lyst til at tænde den? + GPS er slukket på dit udstyr. Har du lyst til at tænde den? Funktion allerede oprettet. Vil du fjerne funktionen? Fortryd ændringer og returnér til ODK? Du skal have mindst 3 punkter for at lave et polygon diff --git a/strings/src/main/res/values-de/strings.xml b/strings/src/main/res/values-de/strings.xml index 40319eba0cc..0e5ac464d3f 100644 --- a/strings/src/main/res/values-de/strings.xml +++ b/strings/src/main/res/values-de/strings.xml @@ -499,12 +499,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - GeoShape - Eingabemethode + Eingabemethode Start GPS aktivieren - GeoTrace - GPS ist auf Ihrem Gerät deaktiviert. Möchten Sie es aktivieren? + GPS ist auf Ihrem Gerät deaktiviert. Möchten Sie es aktivieren? Merkmal bereits erzeugt. Möchten Sie das Merkmal löschen? Änderungen verwerfen und zu ODK zurückkehren? Min. 3 Punkte notwendig um ein Polygon zu erzeugen diff --git a/strings/src/main/res/values-es/strings.xml b/strings/src/main/res/values-es/strings.xml index 382ce085186..a8cd85d53c4 100644 --- a/strings/src/main/res/values-es/strings.xml +++ b/strings/src/main/res/values-es/strings.xml @@ -500,12 +500,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - GeoArea - Método de ingreso + Método de ingreso Iniciar Activar el GPS - GeoLínea - El GPS está deshabilitado en este dispositivo. Le gustaría habilitarlo? + El GPS está deshabilitado en este dispositivo. Le gustaría habilitarlo? Función ya creada. ¿Desea borrar la función? ¿Ignorar los cambios y regresar? Debe tener al menos 3 puntos para crear un polígono diff --git a/strings/src/main/res/values-et/strings.xml b/strings/src/main/res/values-et/strings.xml index 37cc053b1c5..1c178de65d1 100644 --- a/strings/src/main/res/values-et/strings.xml +++ b/strings/src/main/res/values-et/strings.xml @@ -263,11 +263,9 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - Polügoon - Alusta + Alusta Luba GPS - Rada - Kujutis on juba loodud. Kas soovite kujutise eemaldada? + Kujutis on juba loodud. Kas soovite kujutise eemaldada? Loobu muudatustest ja ava ODK? Polügooni loomiseks on vaja vähemalt 3 punkti Eemalda diff --git a/strings/src/main/res/values-fa-rAF/strings.xml b/strings/src/main/res/values-fa-rAF/strings.xml index 78d4f87ce86..3e3e2c7945d 100644 --- a/strings/src/main/res/values-fa-rAF/strings.xml +++ b/strings/src/main/res/values-fa-rAF/strings.xml @@ -421,12 +421,10 @@ S %1$s%2$s%3$s شمال %1$s%2$s%3$s - GeoShape - روش وروردی + روش وروردی آغاز فعال ساختن GPS - GeoTrace - در دستگاه شما GPS غیرفعال گردید. آیا میخواهید دوباره آنرا فعال کنید؟ + در دستگاه شما GPS غیرفعال گردید. آیا میخواهید دوباره آنرا فعال کنید؟ ویژگی قبلا ایجاد شده است. آیا می خواهید ویژگی را پاک کنید؟ تغییرات کنار بگذرید و به ODK برگردید؟ برای ایجاد Polygon باید حداقل 3 امتیاز داشته باشد diff --git a/strings/src/main/res/values-fa/strings.xml b/strings/src/main/res/values-fa/strings.xml index 78d4f87ce86..3e3e2c7945d 100644 --- a/strings/src/main/res/values-fa/strings.xml +++ b/strings/src/main/res/values-fa/strings.xml @@ -421,12 +421,10 @@ S %1$s%2$s%3$s شمال %1$s%2$s%3$s - GeoShape - روش وروردی + روش وروردی آغاز فعال ساختن GPS - GeoTrace - در دستگاه شما GPS غیرفعال گردید. آیا میخواهید دوباره آنرا فعال کنید؟ + در دستگاه شما GPS غیرفعال گردید. آیا میخواهید دوباره آنرا فعال کنید؟ ویژگی قبلا ایجاد شده است. آیا می خواهید ویژگی را پاک کنید؟ تغییرات کنار بگذرید و به ODK برگردید؟ برای ایجاد Polygon باید حداقل 3 امتیاز داشته باشد diff --git a/strings/src/main/res/values-fi/strings.xml b/strings/src/main/res/values-fi/strings.xml index 8384b5ca56b..0c5fa36fbaf 100644 --- a/strings/src/main/res/values-fi/strings.xml +++ b/strings/src/main/res/values-fi/strings.xml @@ -499,12 +499,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - GeoShape - Syöttötapa + Syöttötapa Aloita Ota GPS käyttöön - GeoTrace - GPS ei ole päällä laitteessasi. Haluaisitko kytkeä sen päälle? + GPS ei ole päällä laitteessasi. Haluaisitko kytkeä sen päälle? Ominaisuus on jo luotu. Haluatko tyhjentää ominaisuuden? Hylätäänkö muutokset ja palataan ODK:hon? Monikulmion luomiseen tarvitaan vähintään 3 pistettä diff --git a/strings/src/main/res/values-fr/strings.xml b/strings/src/main/res/values-fr/strings.xml index a3c55465b28..60c41557a32 100644 --- a/strings/src/main/res/values-fr/strings.xml +++ b/strings/src/main/res/values-fr/strings.xml @@ -500,12 +500,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - Polygone - Méthode de saisie + Méthode de saisie Démarrer Activer le GPS - Ligne - Le GPS est désactivé sur votre appareil. Voulez vous l\'activer ? + Le GPS est désactivé sur votre appareil. Voulez vous l\'activer ? Objet déjà créé. Souhaitez-vous l\'effacer ? Abandoner les changements et retourner au formulaire? 3 points minimum sont nécessaires pour créer un Polygone diff --git a/strings/src/main/res/values-hi/strings.xml b/strings/src/main/res/values-hi/strings.xml index 312644cd4bb..c1f39032c04 100644 --- a/strings/src/main/res/values-hi/strings.xml +++ b/strings/src/main/res/values-hi/strings.xml @@ -279,11 +279,9 @@ द. %1$s%2$s%3$s उ. %1$s%2$s%3$s - जिओशेप - शुरु करे + शुरु करे जीपीएस सक्षम करें - जिओ ट्रेस - फीचर पहले से ही बना है. क्या आप फीचर को क्लियर करना चाहेंगे? + फीचर पहले से ही बना है. क्या आप फीचर को क्लियर करना चाहेंगे? परिवर्तनों को रद्द करें और वापस ODK पर लौटें? बहुभुज बनाने के लिए कम से कम 3 पॉइंट होने चाहिए स्पष्ट diff --git a/strings/src/main/res/values-in/strings.xml b/strings/src/main/res/values-in/strings.xml index 0d1c3791c79..2e321958865 100644 --- a/strings/src/main/res/values-in/strings.xml +++ b/strings/src/main/res/values-in/strings.xml @@ -488,12 +488,10 @@ S %1$s%2$s%3$s U %1$s%2$s%3$s - GeoShape - Metode masukan + Metode masukan Mulai Aktifkan GPS - Lacak GeoTrace - GPS dinonaktifkan pada perangkat Anda. Apakah Anda ingin mengaktifkannya? + GPS dinonaktifkan pada perangkat Anda. Apakah Anda ingin mengaktifkannya? Fitur sudah dibuat. Apakah Anda ingin menghapus fitur ini? Buang perubahan dan kembali ke ODK? Setidaknya dibutuhkan 3 titik untuk membuat Polygon. diff --git a/strings/src/main/res/values-it/strings.xml b/strings/src/main/res/values-it/strings.xml index 2ef1e155348..1431ec38b9e 100644 --- a/strings/src/main/res/values-it/strings.xml +++ b/strings/src/main/res/values-it/strings.xml @@ -499,12 +499,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - Geo-contorno - Modalità di inserimento + Modalità di inserimento Inizia Abilita GPS - Geo-tracciatura - Il GPS è disabilitato sul tuo dispositivo. Vuoi abilitarlo? + Il GPS è disabilitato sul tuo dispositivo. Vuoi abilitarlo? Caratteristica già creata. Vuoi cancellarla? Vuoi scartare le modifiche e ritornare ad ODK? Devi avere almeno 3 punti per creare un Poligono diff --git a/strings/src/main/res/values-ja/strings.xml b/strings/src/main/res/values-ja/strings.xml index 34e34d010f4..620d852a99d 100644 --- a/strings/src/main/res/values-ja/strings.xml +++ b/strings/src/main/res/values-ja/strings.xml @@ -408,12 +408,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - GeoShape - 入力方法 + 入力方法 開始 GPS を有効にする - GeoTrace - お使いのデバイスで GPS は無効になっています。有効にしますか? + お使いのデバイスで GPS は無効になっています。有効にしますか? 機能は既に作成済です。機能をクリアしますか? 変更を破棄して ODK の戻りますか? ポリゴンを作成するには、少なくとも 3 点指定する必要があります diff --git a/strings/src/main/res/values-km/strings.xml b/strings/src/main/res/values-km/strings.xml index 1de375653c4..cf705bcab2e 100644 --- a/strings/src/main/res/values-km/strings.xml +++ b/strings/src/main/res/values-km/strings.xml @@ -383,11 +383,9 @@ ខាងត្បូង %1$s%2$s%3$s ខាងជើង %1$s%2$s%3$s - GeoShape - ចាប់ផ្តើម + ចាប់ផ្តើម ដំណើរ​ការ​ប្រព័ន្ធ​ចាប់​ទីតាំង - GeoTrace - លក្ខណះ​ពិសេស​ត្រូវ​បាន​បង្កើត។ តើ​អ្នក​ចង់​លុប​លក្ខណះ​នេះ​ដែរ​ឬ​ទេ? + លក្ខណះ​ពិសេស​ត្រូវ​បាន​បង្កើត។ តើ​អ្នក​ចង់​លុប​លក្ខណះ​នេះ​ដែរ​ឬ​ទេ? បោះបង់​ការផ្លាស់​ប្តូរ និងត្រឡប់ទៅកាន់ ODK វិញ? ត្រូវការចំនុចយ៉ាងតិច 3 ដើម្បី​បង្កើតពហុកោណ លុ​បចេញ diff --git a/strings/src/main/res/values-mr/strings.xml b/strings/src/main/res/values-mr/strings.xml index 87316e06ad7..e0197ed2027 100644 --- a/strings/src/main/res/values-mr/strings.xml +++ b/strings/src/main/res/values-mr/strings.xml @@ -273,11 +273,9 @@ दक्षिण %1$s%2$s%3$s उत्तर %1$s%2$s%3$s - जिओ शेप - प्रारंभ करा + प्रारंभ करा जीपीएस सक्षम करा - जिओ ट्रेस - वैशिष्ट्य आधीच तयार केले आपण वैशिष्ट्य साफ करू इच्छिता? + वैशिष्ट्य आधीच तयार केले आपण वैशिष्ट्य साफ करू इच्छिता? बदल टाकून ओडीके कडे परत जा? बहुभुज तयार करण्यासाठी किमान 3 गुण असणे आवश्यक आहे साफ करा diff --git a/strings/src/main/res/values-ne-rNP/strings.xml b/strings/src/main/res/values-ne-rNP/strings.xml index dfbcf63629e..943754dccfc 100644 --- a/strings/src/main/res/values-ne-rNP/strings.xml +++ b/strings/src/main/res/values-ne-rNP/strings.xml @@ -267,8 +267,7 @@ दक्षिण %1$s%2$s%3$s उत्तर %1$s%2$s%3$s - जियो सेप - शुरू + शुरू GPS शुरु गर्नुहोस् मेटाउनुहोस् diff --git a/strings/src/main/res/values-pl/strings.xml b/strings/src/main/res/values-pl/strings.xml index 65640b3942f..2a67b5b373c 100644 --- a/strings/src/main/res/values-pl/strings.xml +++ b/strings/src/main/res/values-pl/strings.xml @@ -388,12 +388,10 @@ Pd %1$s%2$s%3$s Pn %1$s%2$s%3$s - GeoShape - Metoda wprowadzania + Metoda wprowadzania Zacznij Włącz GPS - GeoTrace - GPS jest nieaktywne na twoim urządzeniu. Czy chcesz je włączyc? + GPS jest nieaktywne na twoim urządzeniu. Czy chcesz je włączyc? Właściwość już stworzona. Chcesz usunąć właściwość? Usunąć zmiany i wrócić do ODK? Wymagane są przynajmniej 3 punkty aby utworzyć wielokąt diff --git a/strings/src/main/res/values-pt/strings.xml b/strings/src/main/res/values-pt/strings.xml index 53d0a0e420d..3c78be7e60a 100644 --- a/strings/src/main/res/values-pt/strings.xml +++ b/strings/src/main/res/values-pt/strings.xml @@ -501,12 +501,10 @@ S %1$s %2$s %3$s N %1$s %2$s %3$s - GeoShape - Método de entrada + Método de entrada Iniciar Habilitar GPS - GeoTrace - O GPS está desabilitado no seu dispositivo. Quer habilitá-lo? + O GPS está desabilitado no seu dispositivo. Quer habilitá-lo? A feature já foi criada. Gostaria de limpar a feature? Descartar mudanças e retornar ao ODK? Precisa ter pelo menos 3 pontos para criar Polígono diff --git a/strings/src/main/res/values-ro/strings.xml b/strings/src/main/res/values-ro/strings.xml index da2783fd704..374df2c2e4a 100644 --- a/strings/src/main/res/values-ro/strings.xml +++ b/strings/src/main/res/values-ro/strings.xml @@ -244,11 +244,9 @@ Sud %1$s%2$s%3$s Nord%1$s%2$s%3$s - GeoShape - Start + Start Activați GPS - GeoTrace - + diff --git a/strings/src/main/res/values-ru/strings.xml b/strings/src/main/res/values-ru/strings.xml index 9537566ebd5..56b1d735f98 100644 --- a/strings/src/main/res/values-ru/strings.xml +++ b/strings/src/main/res/values-ru/strings.xml @@ -457,12 +457,10 @@ Ю %1$s%2$s%3$s С %1$s%2$s%3$s - Геофигура - Метод ввода + Метод ввода Начать Включить GPS - Геослед - GPS отключен на вашем устройстве. Хотите включить его? + GPS отключен на вашем устройстве. Хотите включить его? Опция уже создана. Вы хотите удалить опцию? Отказаться от изменений и вернуться к ODK? В многоугольнике должно быть как минимум 3 точки diff --git a/strings/src/main/res/values-rw/strings.xml b/strings/src/main/res/values-rw/strings.xml index 4ad73ffec97..b4e673d9b0b 100644 --- a/strings/src/main/res/values-rw/strings.xml +++ b/strings/src/main/res/values-rw/strings.xml @@ -410,12 +410,10 @@ N\'ukomea guhura n\'iki kibazo, wakigeza kuwa gusabye gukusanya amakuru.S %1$s%2$s%3$s N %1$s%2$s%3$s - Geoshape! - Uburyo bwo kwinjiza amakuru + Uburyo bwo kwinjiza amakuru Tangira Emeza GPS - Ibimenyetso biranga ahantu. - GPS irafunze kuri tabuleti yawe. urifuza kuyifungura? + GPS irafunze kuri tabuleti yawe. urifuza kuyifungura? Byamaze gutungana. Murifuza kubisiba? Siba ibyari byamaze guhindurwa ubundi usubire kuri porogaramu ya ODK Bigomba kuba bifite byibuze ibintu 3 kugirango bikore Polygon diff --git a/strings/src/main/res/values-sl/strings.xml b/strings/src/main/res/values-sl/strings.xml index fbfa147b51c..c18a8289c88 100644 --- a/strings/src/main/res/values-sl/strings.xml +++ b/strings/src/main/res/values-sl/strings.xml @@ -455,12 +455,10 @@ J %1$s%2$s%3$s S %1$s%2$s%3$s - GeoShape - Vhodna metoda + Vhodna metoda Začni Omogoči GPS - GeoTrace - GPS v napravi je onemogočen. Želite omogočiti? + GPS v napravi je onemogočen. Želite omogočiti? Funkcija je že narejena. Želite izbrisati funkcijo? Želite zavreči spremembe in se vrniti v ODK? Potrebne so vsaj 3 točke za ustvarjenje poligona diff --git a/strings/src/main/res/values-sq/strings.xml b/strings/src/main/res/values-sq/strings.xml index ab510796f85..a5e32d4b33b 100644 --- a/strings/src/main/res/values-sq/strings.xml +++ b/strings/src/main/res/values-sq/strings.xml @@ -288,11 +288,9 @@ J %1$s%2$s%3$s V %1$s%2$s%3$s - GeoShape - Fillo + Fillo Aktivizo GPS-in - GeoTrace - Tipari është krijuar tanimë. A doni ta pastroni këtë tipar? + Tipari është krijuar tanimë. A doni ta pastroni këtë tipar? Elimino ndryshimet dhe kthehu në ODK? Duhen së paku 3 pika për të krijuar Poligonin Pastro diff --git a/strings/src/main/res/values-sr/strings.xml b/strings/src/main/res/values-sr/strings.xml index e0734b3d21e..72394e11a3b 100644 --- a/strings/src/main/res/values-sr/strings.xml +++ b/strings/src/main/res/values-sr/strings.xml @@ -359,12 +359,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - GeoShape - Metod unosa + Metod unosa Početak Omogući GPS - GeoTrace - Karakteristika je već kreirana. Da li biste željeli da obrišete karakteristiku? + Karakteristika je već kreirana. Da li biste željeli da obrišete karakteristiku? Odbaci izmjene i vrati se na ODK? Moram imati makar 3 tačke kako bih kreirao Polygon Moraju postojati najmanje 2 tačke kako bi se kreirao Polyline diff --git a/strings/src/main/res/values-sv-rSE/strings.xml b/strings/src/main/res/values-sv-rSE/strings.xml index f8c43963292..4496ae1de8c 100644 --- a/strings/src/main/res/values-sv-rSE/strings.xml +++ b/strings/src/main/res/values-sv-rSE/strings.xml @@ -361,12 +361,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - GeoShape - Inmatningsmetod + Inmatningsmetod Starta Aktivera GPS - GeoTrace - GPS är inte aktiverad på din enhet. Vill du aktivera den? + GPS är inte aktiverad på din enhet. Vill du aktivera den? Objekt redan skapat. Vill du rensa objektet? Ignorera ändringar och återgå till ODK? Minst tre punkter krävs för att skapa en polygon diff --git a/strings/src/main/res/values-sw/strings.xml b/strings/src/main/res/values-sw/strings.xml index 231b7346742..5cad67d854d 100644 --- a/strings/src/main/res/values-sw/strings.xml +++ b/strings/src/main/res/values-sw/strings.xml @@ -395,12 +395,10 @@ Kus %1$s%2$s%3$s Kas %1$s%2$s%3$s - Umbo la kijografia - Njia ya pembejeo + Njia ya pembejeo Anza Wezesha GPS - Fatilisha mstari kijografia - GPS imezimwa kwenye kifaa chako. Ungependa kutumia? + GPS imezimwa kwenye kifaa chako. Ungependa kutumia? Umbo lishatengenezwa. Ungependa kufuta Umbo? Sitisha mabadiliko na rudi kwenye ODK? Lazima uwe na angalau nukta 3 ili kutengeneza poligoni diff --git a/strings/src/main/res/values-te/strings.xml b/strings/src/main/res/values-te/strings.xml index 9509bfe70a5..7523d9d302a 100644 --- a/strings/src/main/res/values-te/strings.xml +++ b/strings/src/main/res/values-te/strings.xml @@ -388,12 +388,10 @@ దక్షిణం %1$s%2$s%3$s ఉత్తరం %1$s%2$s%3$s - జియో షేప్ - ఇన్‌పుట్ పద్ధతి + ఇన్‌పుట్ పద్ధతి మొదలు GPS ని ప్రారంభించండి - జియోట్రేస్ - మీ పరికరంలో GPS డిజేబుల్ చేయబడి ఉంది. మీరు ఎనేబుల్ చేయదలుచుకుంటున్నారా? + మీ పరికరంలో GPS డిజేబుల్ చేయబడి ఉంది. మీరు ఎనేబుల్ చేయదలుచుకుంటున్నారా? ఫీచర్ ఇప్పటికే క్రియేట్ చేయబడింది. మీరు ఫీచర్ తీసెయ్యాలనుకుంటున్నారా? మార్పులను విస్మరించి, ODK కి తిరిగి వెళ్లాలా? బహుభుజిని సృష్టించడానికి కనీసం 3 పాయింట్లు ఉండాలి diff --git a/strings/src/main/res/values-th-rTH/strings.xml b/strings/src/main/res/values-th-rTH/strings.xml index a362dd12b94..c188afac419 100644 --- a/strings/src/main/res/values-th-rTH/strings.xml +++ b/strings/src/main/res/values-th-rTH/strings.xml @@ -269,11 +269,9 @@ ใต้ %1$s%2$s%3$s เหนือ %1$s%2$s%3$s - GeoShape - เริ่มต้น + เริ่มต้น เปิด GPS - GeoTrace - ยกเลิกการเปลี่ยนแปลงและกลับไปที่ ODK? + ยกเลิกการเปลี่ยนแปลงและกลับไปที่ ODK? ต้องมีจุดพิกัด 3 จุดขึ้นไปเพื่อสร้าง Polygon ลบ diff --git a/strings/src/main/res/values-uk/strings.xml b/strings/src/main/res/values-uk/strings.xml index 2925714f2ac..d1805c2d7dd 100644 --- a/strings/src/main/res/values-uk/strings.xml +++ b/strings/src/main/res/values-uk/strings.xml @@ -356,12 +356,10 @@ Південь %1$s%2$s%3$s Північ %1$s%2$s%3$s - ГеоФігура - Метод введення + Метод введення Почати Увімкнути GPS - ГеоМаршрут - Ознака вже створена. Бажаєте очистити ознаку? + Ознака вже створена. Бажаєте очистити ознаку? Скасувати зміни і повернутись до ODK? Для багатокутника необхідно мінімум 3 точки Для створення лінії необхідні принаймні дві точки diff --git a/strings/src/main/res/values-ur/strings.xml b/strings/src/main/res/values-ur/strings.xml index 9bb788fb584..c477a911460 100644 --- a/strings/src/main/res/values-ur/strings.xml +++ b/strings/src/main/res/values-ur/strings.xml @@ -451,12 +451,10 @@ ایس%1$s%2$s%3$s این:%1$s%2$s%3$s - جیو شیپ - اینٹری کی طریقہ + اینٹری کی طریقہ شروع جی پی ایس کو فعال کریں - جیو ٹریس - آپ کے فون کا GPS بند ہے۔ کیا آپ اس کو کھولنا چاھیں گے؟ + آپ کے فون کا GPS بند ہے۔ کیا آپ اس کو کھولنا چاھیں گے؟ فیچر پہلے سے ہی بنائی گئی ہے. کیا آپ کو فیچرکو ختم کرنا چاہیں گے؟ تبدیلیوں کو ختم کریں اور او ڈی کے کے پاس واپس جائیں؟ قطبون بنانے کے لئے کم از کم 3 پوائنٹس لازمی ہیں diff --git a/strings/src/main/res/values-zh-rTW/strings.xml b/strings/src/main/res/values-zh-rTW/strings.xml index c8efb8a0335..a4cdceccac2 100644 --- a/strings/src/main/res/values-zh-rTW/strings.xml +++ b/strings/src/main/res/values-zh-rTW/strings.xml @@ -486,12 +486,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - GeoShape - 輸入方法 + 輸入方法 開始 啟用GPS - GeoTrace - 設備上的GPS已禁用,是否要啟用它? + 設備上的GPS已禁用,是否要啟用它? 對像已建立,是否要清除該對像? 放棄更改並返回 ODK? 必須至少有3個點才能建立多邊形 diff --git a/strings/src/main/res/values-zh/strings.xml b/strings/src/main/res/values-zh/strings.xml index e9724043a39..3d236adefef 100644 --- a/strings/src/main/res/values-zh/strings.xml +++ b/strings/src/main/res/values-zh/strings.xml @@ -487,12 +487,10 @@ S %1$s%2$s%3$s N %1$s%2$s%3$s - GeoShape - 输入方法 + 输入方法 开始 启用GPS - GeoTrace - 设备上的GPS已禁用。是否要启用它? + 设备上的GPS已禁用。是否要启用它? 对象已创建。是否要清除该对象? 放弃更改并返回应用? 必须至少有3个点才能创建多边形 diff --git a/strings/src/main/res/values/strings.xml b/strings/src/main/res/values/strings.xml index a73f788f9a3..9d07e5189f9 100644 --- a/strings/src/main/res/values/strings.xml +++ b/strings/src/main/res/values/strings.xml @@ -618,11 +618,9 @@ N %1$s%2$s%3$s - GeoShape Input method Start Enable GPS - GeoTrace GPS is disabled on your device. Would you like to enable it? Feature already created. Would you like to clear the feature? Discard changes and return to ODK? diff --git a/test-shared/src/main/java/org/odk/collect/testshared/Assertions.kt b/test-shared/src/main/java/org/odk/collect/testshared/Assertions.kt index 90d59e997d3..87afc276413 100644 --- a/test-shared/src/main/java/org/odk/collect/testshared/Assertions.kt +++ b/test-shared/src/main/java/org/odk/collect/testshared/Assertions.kt @@ -10,12 +10,14 @@ import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents import androidx.test.espresso.matcher.ViewMatchers.Visibility.VISIBLE import androidx.test.espresso.matcher.ViewMatchers.hasSibling +import androidx.test.espresso.matcher.ViewMatchers.isEnabled import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import org.hamcrest.CoreMatchers.not import org.hamcrest.Matcher import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.equalTo +import org.odk.collect.testshared.Assertions.assertVisible object Assertions { @@ -41,6 +43,22 @@ object Assertions { onView.check(matches(not(doesNotExist()))) } + fun assertEnabled( + view: Matcher, + root: Matcher? = null, + sibling: Matcher? = null + ) { + assertVisible(allOf(view, isEnabled()), root, sibling) + } + + fun assertDisabled( + view: Matcher, + root: Matcher? = null, + sibling: Matcher? = null, + ) { + assertVisible(allOf(view, not(isEnabled())), root, sibling) + } + fun assertNotVisible(view: Matcher, root: Matcher? = null) { val onView = if (root != null) { onView(allOf(view, withEffectiveVisibility(VISIBLE))).inRoot(root) diff --git a/test-shared/src/main/java/org/odk/collect/testshared/MockFragmentFactory.kt b/test-shared/src/main/java/org/odk/collect/testshared/MockFragmentFactory.kt new file mode 100644 index 00000000000..ebe6fdbf472 --- /dev/null +++ b/test-shared/src/main/java/org/odk/collect/testshared/MockFragmentFactory.kt @@ -0,0 +1,24 @@ +package org.odk.collect.testshared + +import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentFactory + +/** + * Instead of instantiate requested Fragments, will instantiate a [MockFragment] (or + * [MockDialogFragment]) that wraps the Fragment class. This allows a test to check that the right + * Fragments are setup without needing to deal with their actual creation/dependencies. + */ +class MockFragmentFactory : FragmentFactory() { + override fun instantiate(classLoader: ClassLoader, className: String): Fragment { + val fragmentClass = loadFragmentClass(classLoader, className) + return if (DialogFragment::class.java.isAssignableFrom(fragmentClass)) { + MockDialogFragment(fragmentClass) + } else { + MockFragment(fragmentClass) + } + } +} + +class MockFragment(val fragmentClass: Class) : Fragment() +class MockDialogFragment(val fragmentClass: Class) : DialogFragment()