Copy disabled (too large)
Download .txt
Showing preview only (13,013K chars total). Download the full file to get everything.
Repository: opendatakit/collect
Branch: master
Commit: 77b1124ad9a2
Files: 2434
Total size: 11.5 MB
Directory structure:
gitextract_2lkczetw/
├── .circleci/
│ ├── config.yml
│ ├── generate-app-test-list.sh
│ ├── gradle-large.properties
│ ├── gradle.properties
│ └── test_modules.txt
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CODE_OF_CONDUCT.md
│ ├── ISSUE_TEMPLATE/
│ │ └── config.yml
│ ├── ISSUE_TEMPLATE.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── TESTING_RESULT_TEMPLATES.md
├── .gitignore
├── .hgtags
├── LICENSE.md
├── README.md
├── SECURITY.md
├── analytics/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── analytics/
│ ├── Analytics.kt
│ ├── BlockableFirebaseAnalytics.kt
│ └── NoopAnalytics.kt
├── androidshared/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── androidshared/
│ │ └── bitmap/
│ │ ├── ImageCompressorTest.kt
│ │ └── ImageFileUtilsTest.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── androidshared/
│ │ │ ├── async/
│ │ │ │ └── TrackableWorker.kt
│ │ │ ├── bitmap/
│ │ │ │ ├── ImageCompressor.kt
│ │ │ │ └── ImageFileUtils.kt
│ │ │ ├── data/
│ │ │ │ ├── AppState.kt
│ │ │ │ ├── Consumable.kt
│ │ │ │ └── Data.kt
│ │ │ ├── livedata/
│ │ │ │ ├── LiveDataExt.kt
│ │ │ │ ├── LiveDataUtils.java
│ │ │ │ └── NonNullLiveData.kt
│ │ │ ├── system/
│ │ │ │ ├── BroadcastReceiverRegister.kt
│ │ │ │ ├── CameraUtils.java
│ │ │ │ ├── ContextExt.kt
│ │ │ │ ├── ExternalFilesUtils.kt
│ │ │ │ ├── IntentLauncher.kt
│ │ │ │ ├── OpenGLVersionChecker.kt
│ │ │ │ ├── PlayServicesChecker.java
│ │ │ │ ├── ProcessRestoreDetector.kt
│ │ │ │ └── UriExt.kt
│ │ │ ├── ui/
│ │ │ │ ├── AlertStore.kt
│ │ │ │ ├── Animations.kt
│ │ │ │ ├── ColorPickerDialog.kt
│ │ │ │ ├── ComposeThemeProvider.kt
│ │ │ │ ├── DialogFragmentUtils.kt
│ │ │ │ ├── DialogUtils.kt
│ │ │ │ ├── DisplayString.kt
│ │ │ │ ├── EdgeToEdge.kt
│ │ │ │ ├── FragmentFactoryBuilder.kt
│ │ │ │ ├── GroupClickListener.kt
│ │ │ │ ├── ListFragmentStateAdapter.kt
│ │ │ │ ├── MenuExt.kt
│ │ │ │ ├── ObviousProgressBar.kt
│ │ │ │ ├── OneSignTextWatcher.kt
│ │ │ │ ├── PrefUtils.kt
│ │ │ │ ├── ReturnToAppActivity.kt
│ │ │ │ ├── SnackbarUtils.kt
│ │ │ │ ├── ToastUtils.kt
│ │ │ │ ├── compose/
│ │ │ │ │ └── Margins.kt
│ │ │ │ └── multiclicksafe/
│ │ │ │ ├── DoubleClickSafeMaterialButton.kt
│ │ │ │ ├── MultiClickGuard.kt
│ │ │ │ ├── MultiClickSafeMaterialButton.kt
│ │ │ │ ├── MultiClickSafeTextInputEditText.kt
│ │ │ │ └── MultiClickSaveOnClickListener.kt
│ │ │ └── utils/
│ │ │ ├── AppBarUtils.kt
│ │ │ ├── ColorUtils.kt
│ │ │ ├── CompressionUtils.kt
│ │ │ ├── FileExt.kt
│ │ │ ├── InMemUniqueIdGenerator.kt
│ │ │ ├── PathUtils.kt
│ │ │ ├── PreferenceFragmentCompatUtils.kt
│ │ │ ├── ScreenUtils.java
│ │ │ ├── SettingsUniqueIdGenerator.kt
│ │ │ ├── UniqueIdGenerator.kt
│ │ │ └── Validator.kt
│ │ └── res/
│ │ ├── color/
│ │ │ ├── color_error_button_icon.xml
│ │ │ ├── color_on_primary_low_emphasis.xml
│ │ │ ├── color_on_surface_high_emphasis.xml
│ │ │ ├── color_on_surface_low_emphasis.xml
│ │ │ ├── color_on_surface_medium_emphasis.xml
│ │ │ └── color_primary_low_emphasis.xml
│ │ ├── drawable/
│ │ │ ├── color_circle.xml
│ │ │ ├── ic_close_24.xml
│ │ │ ├── list_item_divider.xml
│ │ │ ├── radio_button_inset.xml
│ │ │ └── shadow_up.xml
│ │ ├── layout/
│ │ │ ├── app_bar_layout.xml
│ │ │ └── color_picker_dialog_layout.xml
│ │ └── values/
│ │ ├── attrs.xml
│ │ ├── color_picker_dialog_colors.xml
│ │ ├── dimens.xml
│ │ └── styles.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── androidshared/
│ │ ├── async/
│ │ │ └── TrackableWorkerTest.kt
│ │ ├── livedata/
│ │ │ └── LiveDataUtilsTest.kt
│ │ ├── system/
│ │ │ └── UriExtTest.kt
│ │ ├── ui/
│ │ │ ├── ColorPickerDialogTest.kt
│ │ │ ├── OneSignTextWatcherTest.kt
│ │ │ └── ReturnToAppActivityTest.kt
│ │ └── utils/
│ │ ├── ColorUtilsTest.kt
│ │ ├── CompressionUtilsTest.kt
│ │ ├── DialogFragmentUtilsTest.java
│ │ ├── InMemUniqueIdGeneratorTest.kt
│ │ ├── IntentLauncherImplTest.kt
│ │ ├── PathUtilsTest.kt
│ │ ├── SettingsUniqueIdGeneratorTest.kt
│ │ ├── UniqueIdGeneratorTest.kt
│ │ └── ValidatorTest.kt
│ └── resources/
│ └── robolectric.properties
├── androidtest/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── androidtest/
│ ├── ActivityScenarioExtensions.kt
│ ├── ActivityScenarioLauncherRule.kt
│ ├── DrawableMatcher.kt
│ ├── FakeLifecycleOwner.kt
│ ├── FragmentScenarioExtensions.kt
│ ├── LiveDataTestUtils.kt
│ ├── MainDispatcherRule.kt
│ ├── NodeInteractionExtensions.kt
│ └── RecordedIntentsRule.kt
├── async/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── async/
│ │ ├── Cancellable.kt
│ │ ├── OngoingWorkListener.kt
│ │ ├── Scheduler.kt
│ │ ├── SchedulerAsyncTaskMimic.kt
│ │ ├── SchedulerBuilder.kt
│ │ ├── ScopeCancellable.kt
│ │ ├── TaskRunner.kt
│ │ ├── TaskSpec.kt
│ │ ├── TaskSpecRunner.kt
│ │ ├── TaskSpecScheduler.kt
│ │ ├── coroutines/
│ │ │ └── CoroutineTaskRunner.kt
│ │ ├── network/
│ │ │ ├── ConnectivityProvider.kt
│ │ │ └── NetworkStateProvider.kt
│ │ ├── services/
│ │ │ └── ForegroundServiceTaskSpecRunner.kt
│ │ └── workmanager/
│ │ ├── TaskSpecWorker.kt
│ │ └── WorkManagerTaskSpecScheduler.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── async/
│ ├── TaskSpecTest.kt
│ └── workmanager/
│ └── TaskSpecWorkerTest.kt
├── audio-clips/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── audioclips/
│ │ ├── AudioClipViewModel.kt
│ │ ├── AudioPlayer.kt
│ │ ├── AudioPlayerFactory.kt
│ │ ├── Clip.kt
│ │ ├── PlaybackFailedException.kt
│ │ └── ThreadSafeMediaPlayerWrapper.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── audioclips/
│ └── AudioClipViewModelTest.kt
├── audio-recorder/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── audiorecorder/
│ │ ├── DaggerSetup.kt
│ │ ├── mediarecorder/
│ │ │ └── MediaRecorderRecordingResource.kt
│ │ ├── recorder/
│ │ │ ├── Recorder.kt
│ │ │ ├── RecordingResource.kt
│ │ │ └── RecordingResourceRecorder.kt
│ │ ├── recording/
│ │ │ ├── AudioRecorder.kt
│ │ │ ├── AudioRecorderFactory.kt
│ │ │ ├── AudioRecorderService.kt
│ │ │ └── internal/
│ │ │ ├── ForegroundServiceAudioRecorder.kt
│ │ │ ├── RecordingForegroundServiceNotification.kt
│ │ │ └── RecordingRepository.kt
│ │ └── testsupport/
│ │ └── StubAudioRecorder.kt
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── audiorecorder/
│ │ ├── mediarecorder/
│ │ │ └── AMRRecordingResourceTest.kt
│ │ ├── recorder/
│ │ │ └── RecordingResourceRecorderTest.kt
│ │ ├── recording/
│ │ │ ├── AudioRecorderTest.kt
│ │ │ └── internal/
│ │ │ ├── AudioRecorderServiceTest.kt
│ │ │ ├── ForegroundServiceAudioRecorderTest.kt
│ │ │ └── RecordingForegroundServiceNotificationTest.kt
│ │ ├── support/
│ │ │ └── FakeRecorder.kt
│ │ └── testsupport/
│ │ ├── RobolectricApplication.kt
│ │ └── StubAudioRecorderTest.kt
│ └── resources/
│ └── robolectric.properties
├── benchmark.sh
├── build.gradle
├── check-size.sh
├── codecov.yml
├── collect_app/
│ ├── build.gradle
│ ├── google-services.json
│ ├── libs/
│ │ └── bikram-sambat-1.8.0.jar
│ ├── proguard-rules.txt
│ └── src/
│ ├── androidTest/
│ │ ├── assets/
│ │ │ └── instances/
│ │ │ ├── One Question_2021-06-22_15-55-50.xml
│ │ │ └── one-question-google_2023-08-08_14-51-00.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── android/
│ │ ├── benchmark/
│ │ │ ├── EntitiesBenchmarkTest.kt
│ │ │ ├── FormsUpdateBenchmarkTest.kt
│ │ │ ├── SearchBenchmarkTest.kt
│ │ │ └── support/
│ │ │ └── Benchmarker.kt
│ │ ├── feature/
│ │ │ ├── entitymanagement/
│ │ │ │ └── ViewEntitiesTest.kt
│ │ │ ├── external/
│ │ │ │ ├── AndroidShortcutsTest.kt
│ │ │ │ ├── FormDownloadActionTest.kt
│ │ │ │ ├── FormEditActionTest.kt
│ │ │ │ ├── FormPickActionTest.kt
│ │ │ │ ├── InstanceEditActionTest.kt
│ │ │ │ ├── InstancePickActionTest.kt
│ │ │ │ └── InstanceUploadActionTest.kt
│ │ │ ├── formentry/
│ │ │ │ ├── AddRepeatTest.kt
│ │ │ │ ├── AudioAutoplayTest.kt
│ │ │ │ ├── AudioRecordingTest.java
│ │ │ │ ├── BackgroundAudioRecordingTest.java
│ │ │ │ ├── CascadingSelectTest.kt
│ │ │ │ ├── CatchFormDesignExceptionsTest.kt
│ │ │ │ ├── ContextMenuTest.java
│ │ │ │ ├── DeletingRepeatGroupsTest.java
│ │ │ │ ├── EncryptedFormTest.kt
│ │ │ │ ├── ExternalAudioRecordingTest.java
│ │ │ │ ├── ExternalSecondaryInstanceTest.java
│ │ │ │ ├── ExternalSelectsTest.kt
│ │ │ │ ├── FieldListUpdateTest.kt
│ │ │ │ ├── FillBlankFormWithRepeatGroupTest.kt
│ │ │ │ ├── FormEndTest.kt
│ │ │ │ ├── FormHierarchyTest.java
│ │ │ │ ├── FormLanguageTest.java
│ │ │ │ ├── FormMediaTest.kt
│ │ │ │ ├── FormMetadataTest.kt
│ │ │ │ ├── FormNavigationTest.kt
│ │ │ │ ├── FormSaveTest.kt
│ │ │ │ ├── FormStylingTest.kt
│ │ │ │ ├── GuidanceTest.kt
│ │ │ │ ├── ImageLoadingTest.kt
│ │ │ │ ├── IntentGroupTest.kt
│ │ │ │ ├── InvalidFormTest.kt
│ │ │ │ ├── LikertTest.java
│ │ │ │ ├── NestedIntentGroupTest.kt
│ │ │ │ ├── QuickSaveTest.java
│ │ │ │ ├── QuittingFormTest.java
│ │ │ │ ├── RankingWidgetWithCSVTest.java
│ │ │ │ ├── RequiredAndConstraintQuestionTest.kt
│ │ │ │ ├── SaveIncompleteTest.kt
│ │ │ │ ├── SavePointTest.kt
│ │ │ │ ├── SearchAppearancesTest.kt
│ │ │ │ ├── audit/
│ │ │ │ │ ├── AuditTest.kt
│ │ │ │ │ ├── IdentifyUserTest.kt
│ │ │ │ │ └── TrackChangesReasonTest.kt
│ │ │ │ ├── backgroundlocation/
│ │ │ │ │ ├── LocationTrackingAuditTest.java
│ │ │ │ │ └── SetGeopointActionTest.java
│ │ │ │ ├── dynamicpreload/
│ │ │ │ │ ├── DynamicPreLoadedDataPullTest.kt
│ │ │ │ │ └── DynamicPreLoadedDataSelects.java
│ │ │ │ └── entities/
│ │ │ │ ├── EntityFormApprovalTest.kt
│ │ │ │ ├── EntityFormCreateUpdateTest.kt
│ │ │ │ ├── EntityFormEditTest.kt
│ │ │ │ ├── EntityFormLockingTest.kt
│ │ │ │ ├── EntityFormSpecVersionTest.kt
│ │ │ │ └── EntityListSyncTest.kt
│ │ │ ├── formmanagement/
│ │ │ │ ├── BulkFinalizationTest.kt
│ │ │ │ ├── DeleteBlankFormTest.java
│ │ │ │ ├── FormUpdateTest.kt
│ │ │ │ ├── FormsAdbTest.java
│ │ │ │ ├── GetBlankFormsTest.java
│ │ │ │ ├── HideOldVersionsTest.java
│ │ │ │ ├── ManualUpdatesTest.java
│ │ │ │ ├── MatchExactlyTest.kt
│ │ │ │ └── PreviouslyDownloadedOnlyTest.kt
│ │ │ ├── instancemanagement/
│ │ │ │ ├── AutoSendTest.kt
│ │ │ │ ├── DeleteSavedFormTest.kt
│ │ │ │ ├── EditSavedFormTest.kt
│ │ │ │ ├── InstancesAdbTest.kt
│ │ │ │ ├── PartialSubmissionTest.kt
│ │ │ │ └── SendFinalizedFormTest.kt
│ │ │ ├── maps/
│ │ │ │ └── FormMapTest.kt
│ │ │ ├── projects/
│ │ │ │ ├── AddNewProjectTest.kt
│ │ │ │ ├── DeleteProjectTest.kt
│ │ │ │ ├── GoogleDriveDeprecationTest.kt
│ │ │ │ ├── LaunchScreenTest.kt
│ │ │ │ ├── MobileDeviceManagementTest.kt
│ │ │ │ ├── ProjectsAdbTest.kt
│ │ │ │ ├── SwitchProjectTest.kt
│ │ │ │ └── UpdateProjectTest.kt
│ │ │ ├── settings/
│ │ │ │ ├── ConfigureWithQRCodeTest.java
│ │ │ │ ├── FormEntrySettingsTest.kt
│ │ │ │ ├── FormManagementSettingsTest.kt
│ │ │ │ ├── FormMetadataSettingsTest.kt
│ │ │ │ ├── MovingBackwardsTest.kt
│ │ │ │ ├── ResetProjectTest.kt
│ │ │ │ ├── ServerSettingsTest.java
│ │ │ │ └── SettingLanguageTest.kt
│ │ │ └── smoke/
│ │ │ ├── AllWidgetsFormTest.kt
│ │ │ ├── BadServerTest.java
│ │ │ └── GetAndSubmitFormTest.java
│ │ ├── instrumented/
│ │ │ ├── forms/
│ │ │ │ └── FormUtilsTest.java
│ │ │ ├── tasks/
│ │ │ │ └── FormLoaderTaskTest.java
│ │ │ └── utilities/
│ │ │ ├── CustomSQLiteQueryExecutionTest.java
│ │ │ └── DateTimeUtilsTest.java
│ │ └── support/
│ │ ├── ActivityHelpers.java
│ │ ├── CollectHelpers.kt
│ │ ├── ContentProviderUtils.kt
│ │ ├── CountingTaskExecutorIdlingResource.java
│ │ ├── DummyActivityLauncher.kt
│ │ ├── FakeClickableMapFragment.kt
│ │ ├── FakeLocationClient.java
│ │ ├── FakeNetworkStateProvider.kt
│ │ ├── StorageUtils.kt
│ │ ├── StubOpenRosaServer.kt
│ │ ├── SubmissionParser.kt
│ │ ├── TestDependencies.kt
│ │ ├── TranslatedStringBuilder.kt
│ │ ├── actions/
│ │ │ └── RotateAction.java
│ │ ├── async/
│ │ │ ├── AsyncWorkTracker.kt
│ │ │ ├── AsyncWorkTrackerIdlingResource.kt
│ │ │ ├── TrackingCoroutineAndWorkManagerScheduler.kt
│ │ │ └── TrackingCoroutineDispatcher.kt
│ │ ├── matchers/
│ │ │ ├── CustomMatchers.kt
│ │ │ └── ToastMatcher.java
│ │ ├── pages/
│ │ │ ├── AboutPage.java
│ │ │ ├── AccessControlPage.kt
│ │ │ ├── AddNewRepeatDialog.kt
│ │ │ ├── AppClosedPage.kt
│ │ │ ├── AsyncPage.kt
│ │ │ ├── BlankFormSearchPage.java
│ │ │ ├── BulkFinalizationConfirmationDialogPage.kt
│ │ │ ├── CancelRecordingDialog.java
│ │ │ ├── ChangesReasonPromptPage.java
│ │ │ ├── DeleteSavedFormPage.java
│ │ │ ├── DeleteSelectedDialog.java
│ │ │ ├── EditSavedFormPage.java
│ │ │ ├── EntitiesPage.kt
│ │ │ ├── EntityListPage.kt
│ │ │ ├── ErrorDialog.kt
│ │ │ ├── ErrorPage.kt
│ │ │ ├── ExperimentalPage.java
│ │ │ ├── FillBlankFormPage.java
│ │ │ ├── FirstLaunchPage.kt
│ │ │ ├── FormEndPage.java
│ │ │ ├── FormEntryPage.java
│ │ │ ├── FormHierarchyPage.kt
│ │ │ ├── FormManagementPage.java
│ │ │ ├── FormMapPage.java
│ │ │ ├── FormMetadataPage.java
│ │ │ ├── FormsDownloadResultPage.kt
│ │ │ ├── GetBlankFormPage.java
│ │ │ ├── IdentifyUserPromptPage.java
│ │ │ ├── ListPreferenceDialog.java
│ │ │ ├── MainMenuPage.java
│ │ │ ├── MainMenuSettingsPage.kt
│ │ │ ├── ManualProjectCreatorDialogPage.kt
│ │ │ ├── MapsSettingsPage.java
│ │ │ ├── NotificationDrawer.kt
│ │ │ ├── OkDialog.java
│ │ │ ├── OpenSourceLicensesPage.java
│ │ │ ├── Page.kt
│ │ │ ├── PreferencePage.java
│ │ │ ├── ProjectDisplayPage.kt
│ │ │ ├── ProjectManagementPage.kt
│ │ │ ├── ProjectSettingsDialogPage.kt
│ │ │ ├── ProjectSettingsPage.java
│ │ │ ├── QRCodePage.java
│ │ │ ├── QrCodeProjectCreatorDialogPage.kt
│ │ │ ├── ResetApplicationDialog.java
│ │ │ ├── SaveOrDiscardFormDialog.kt
│ │ │ ├── SaveOrIgnoreDrawingDialog.kt
│ │ │ ├── SavepointRecoveryDialogPage.kt
│ │ │ ├── SelectMinimalDialogPage.kt
│ │ │ ├── SendFinalizedFormPage.kt
│ │ │ ├── ServerAuthDialog.java
│ │ │ ├── ServerSettingsPage.java
│ │ │ ├── ShortcutsPage.java
│ │ │ ├── UserAndDeviceIdentitySettingsPage.java
│ │ │ ├── UserInterfacePage.java
│ │ │ ├── ViewFormPage.kt
│ │ │ └── ViewSentFormPage.java
│ │ └── rules/
│ │ ├── BlankFormTestRule.kt
│ │ ├── CollectTestRule.kt
│ │ ├── FormEntryActivityTestRule.kt
│ │ ├── IdlingResourceRule.kt
│ │ ├── NotificationDrawerRule.kt
│ │ ├── PageComposeRule.kt
│ │ ├── PrepDeviceForTestsRule.kt
│ │ ├── RecentAppsRule.kt
│ │ ├── ResetRotationRule.kt
│ │ ├── ResetStateRule.kt
│ │ ├── RetryOnDeviceErrorRule.kt
│ │ ├── RunnableRule.kt
│ │ └── TestRuleChain.kt
│ ├── debug/
│ │ ├── AndroidManifest.xml
│ │ └── google-services.json
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ └── svg_map_helper.js
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ ├── android/
│ │ │ │ ├── activities/
│ │ │ │ │ ├── AboutActivity.kt
│ │ │ │ │ ├── ActivityUtils.java
│ │ │ │ │ ├── AppListActivity.java
│ │ │ │ │ ├── BearingActivity.java
│ │ │ │ │ ├── CrashHandlerActivity.kt
│ │ │ │ │ ├── DeleteFormsActivity.kt
│ │ │ │ │ ├── FirstLaunchActivity.kt
│ │ │ │ │ ├── FormDownloadListActivity.java
│ │ │ │ │ ├── FormEntryViewModelFactory.kt
│ │ │ │ │ ├── FormFillingActivity.java
│ │ │ │ │ ├── FormListActivity.java
│ │ │ │ │ ├── FormMapActivity.kt
│ │ │ │ │ ├── InstanceChooserList.java
│ │ │ │ │ ├── ScannerWithFlashlightActivity.kt
│ │ │ │ │ └── viewmodels/
│ │ │ │ │ └── FormDownloadListViewModel.java
│ │ │ │ ├── adapters/
│ │ │ │ │ ├── AboutListAdapter.kt
│ │ │ │ │ ├── AbstractSelectListAdapter.java
│ │ │ │ │ ├── FormDownloadListAdapter.java
│ │ │ │ │ ├── InstanceListCursorAdapter.java
│ │ │ │ │ ├── InstanceUploaderAdapter.java
│ │ │ │ │ ├── RankingListAdapter.java
│ │ │ │ │ ├── SelectMultipleListAdapter.java
│ │ │ │ │ └── SelectOneListAdapter.java
│ │ │ │ ├── analytics/
│ │ │ │ │ ├── AnalyticsEvents.kt
│ │ │ │ │ └── AnalyticsUtils.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── Collect.java
│ │ │ │ │ ├── CollectSettingsChangeHandler.kt
│ │ │ │ │ ├── ComposeTheme.kt
│ │ │ │ │ ├── FeatureFlags.kt
│ │ │ │ │ ├── MapboxClassInstanceCreator.kt
│ │ │ │ │ └── initialization/
│ │ │ │ │ ├── AnalyticsInitializer.kt
│ │ │ │ │ ├── ApplicationInitializer.kt
│ │ │ │ │ ├── CachedFormsCleaner.kt
│ │ │ │ │ ├── CrashReportingTree.kt
│ │ │ │ │ ├── ExistingProjectMigrator.kt
│ │ │ │ │ ├── ExistingSettingsMigrator.kt
│ │ │ │ │ ├── GoogleDriveProjectsDeleter.kt
│ │ │ │ │ ├── JavaRosaInitializer.kt
│ │ │ │ │ ├── MapsInitializer.kt
│ │ │ │ │ ├── SavepointsImporter.kt
│ │ │ │ │ ├── ScheduledWorkUpgrade.kt
│ │ │ │ │ ├── UserPropertiesInitializer.kt
│ │ │ │ │ └── upgrade/
│ │ │ │ │ ├── BeforeProjectsInstallDetector.kt
│ │ │ │ │ └── UpgradeInitializer.kt
│ │ │ │ ├── audio/
│ │ │ │ │ ├── AMRAppender.java
│ │ │ │ │ ├── AudioButton.java
│ │ │ │ │ ├── AudioControllerView.java
│ │ │ │ │ ├── AudioFileAppender.java
│ │ │ │ │ ├── AudioRecordingControllerFragment.java
│ │ │ │ │ ├── AudioRecordingErrorDialogFragment.java
│ │ │ │ │ ├── BackgroundAudioHelpDialogFragment.java
│ │ │ │ │ ├── M4AAppender.java
│ │ │ │ │ ├── VolumeBar.java
│ │ │ │ │ └── Waveform.java
│ │ │ │ ├── backgroundwork/
│ │ │ │ │ ├── AutoUpdateTaskSpec.kt
│ │ │ │ │ ├── BackgroundWorkUtils.java
│ │ │ │ │ ├── FormUpdateAndInstanceSubmitScheduler.java
│ │ │ │ │ ├── FormUpdateScheduler.java
│ │ │ │ │ ├── InstanceSubmitScheduler.java
│ │ │ │ │ ├── SendFormsTaskSpec.kt
│ │ │ │ │ ├── SyncFormsTaskSpec.kt
│ │ │ │ │ └── TaskData.kt
│ │ │ │ ├── configure/
│ │ │ │ │ └── qr/
│ │ │ │ │ ├── AppConfigurationGenerator.kt
│ │ │ │ │ ├── CachingQRCodeGenerator.kt
│ │ │ │ │ ├── QRCodeActivityResultDelegate.kt
│ │ │ │ │ ├── QRCodeMenuProvider.kt
│ │ │ │ │ ├── QRCodeScannerFragment.kt
│ │ │ │ │ ├── QRCodeTabsActivity.kt
│ │ │ │ │ ├── QRCodeViewModel.kt
│ │ │ │ │ ├── SettingsBarcodeScannerViewFactory.kt
│ │ │ │ │ └── ShowQRCodeFragment.kt
│ │ │ │ ├── dao/
│ │ │ │ │ ├── CursorLoaderFactory.java
│ │ │ │ │ └── helpers/
│ │ │ │ │ └── InstancesDaoHelper.java
│ │ │ │ ├── database/
│ │ │ │ │ ├── DatabaseConstants.java
│ │ │ │ │ ├── DatabaseObjectMapper.kt
│ │ │ │ │ ├── entities/
│ │ │ │ │ │ └── DatabaseEntitiesRepository.kt
│ │ │ │ │ ├── forms/
│ │ │ │ │ │ ├── DatabaseFormColumns.kt
│ │ │ │ │ │ ├── DatabaseFormsRepository.java
│ │ │ │ │ │ └── FormDatabaseMigrator.java
│ │ │ │ │ ├── instances/
│ │ │ │ │ │ ├── DatabaseInstanceColumns.kt
│ │ │ │ │ │ ├── DatabaseInstancesRepository.java
│ │ │ │ │ │ └── InstanceDatabaseMigrator.java
│ │ │ │ │ ├── itemsets/
│ │ │ │ │ │ └── DatabaseFastExternalItemsetsRepository.kt
│ │ │ │ │ └── savepoints/
│ │ │ │ │ ├── DatabaseSavepointsColumns.kt
│ │ │ │ │ ├── DatabaseSavepointsRepository.kt
│ │ │ │ │ └── SavepointsDatabaseMigrator.kt
│ │ │ │ ├── dynamicpreload/
│ │ │ │ │ ├── DynamicPreloadXFormParserFactory.kt
│ │ │ │ │ ├── ExternalAnswerResolver.java
│ │ │ │ │ ├── ExternalAppsUtils.java
│ │ │ │ │ ├── ExternalDataHandler.java
│ │ │ │ │ ├── ExternalDataManager.java
│ │ │ │ │ ├── ExternalDataManagerImpl.java
│ │ │ │ │ ├── ExternalDataReader.java
│ │ │ │ │ ├── ExternalDataReaderImpl.java
│ │ │ │ │ ├── ExternalDataUseCases.kt
│ │ │ │ │ ├── ExternalDataUtil.java
│ │ │ │ │ ├── ExternalSQLiteOpenHelper.java
│ │ │ │ │ ├── ExternalSelectChoice.java
│ │ │ │ │ └── handler/
│ │ │ │ │ ├── ExternalDataHandlerBase.java
│ │ │ │ │ ├── ExternalDataHandlerPull.java
│ │ │ │ │ ├── ExternalDataHandlerSearch.java
│ │ │ │ │ └── ExternalDataSearchType.java
│ │ │ │ ├── entities/
│ │ │ │ │ └── EntitiesRepositoryProvider.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── EncryptionException.java
│ │ │ │ │ ├── ExternalDataException.java
│ │ │ │ │ ├── ExternalParamsException.java
│ │ │ │ │ └── JavaRosaException.java
│ │ │ │ ├── external/
│ │ │ │ │ ├── AndroidShortcutsActivity.kt
│ │ │ │ │ ├── FormUriActivity.kt
│ │ │ │ │ ├── FormsContract.java
│ │ │ │ │ ├── FormsProvider.java
│ │ │ │ │ ├── InstanceProvider.java
│ │ │ │ │ └── InstancesContract.java
│ │ │ │ ├── fastexternalitemset/
│ │ │ │ │ ├── ItemsetDao.java
│ │ │ │ │ ├── ItemsetDbAdapter.java
│ │ │ │ │ └── XPathParseTool.java
│ │ │ │ ├── formentry/
│ │ │ │ │ ├── BackgroundAudioPermissionDialogFragment.java
│ │ │ │ │ ├── BackgroundAudioViewModel.java
│ │ │ │ │ ├── CurrentFormIndex.kt
│ │ │ │ │ ├── FormAnimation.kt
│ │ │ │ │ ├── FormDefCache.kt
│ │ │ │ │ ├── FormEnd.kt
│ │ │ │ │ ├── FormEndView.kt
│ │ │ │ │ ├── FormEndViewModel.kt
│ │ │ │ │ ├── FormEntryMenuProvider.kt
│ │ │ │ │ ├── FormEntryUseCases.kt
│ │ │ │ │ ├── FormEntryViewModel.java
│ │ │ │ │ ├── FormError.kt
│ │ │ │ │ ├── FormIndexAnimationHandler.kt
│ │ │ │ │ ├── FormLoadingDialogFragment.java
│ │ │ │ │ ├── FormOpeningMode.kt
│ │ │ │ │ ├── FormSessionRepository.kt
│ │ │ │ │ ├── ODKView.java
│ │ │ │ │ ├── PrinterWidgetViewModel.kt
│ │ │ │ │ ├── QuitFormDialog.kt
│ │ │ │ │ ├── RecordingHandler.java
│ │ │ │ │ ├── RecordingWarningDialogFragment.java
│ │ │ │ │ ├── RefreshFormListDialogFragment.java
│ │ │ │ │ ├── SwipeHandler.kt
│ │ │ │ │ ├── audit/
│ │ │ │ │ │ ├── AsyncTaskAuditEventWriter.java
│ │ │ │ │ │ ├── AuditConfig.java
│ │ │ │ │ │ ├── AuditEvent.java
│ │ │ │ │ │ ├── AuditEventCSVLine.java
│ │ │ │ │ │ ├── AuditEventLogger.java
│ │ │ │ │ │ ├── AuditEventSaveTask.java
│ │ │ │ │ │ ├── AuditUtils.kt
│ │ │ │ │ │ ├── ChangesReasonPromptDialogFragment.java
│ │ │ │ │ │ ├── IdentifyUserPromptDialogFragment.java
│ │ │ │ │ │ └── IdentityPromptViewModel.java
│ │ │ │ │ ├── backgroundlocation/
│ │ │ │ │ │ ├── BackgroundLocationHelper.java
│ │ │ │ │ │ ├── BackgroundLocationManager.java
│ │ │ │ │ │ └── BackgroundLocationViewModel.java
│ │ │ │ │ ├── media/
│ │ │ │ │ │ ├── FormMediaUtils.java
│ │ │ │ │ │ └── PromptAutoplayer.java
│ │ │ │ │ ├── questions/
│ │ │ │ │ │ ├── AnswersProvider.java
│ │ │ │ │ │ ├── AudioVideoImageTextLabel.java
│ │ │ │ │ │ ├── NoButtonsItem.java
│ │ │ │ │ │ ├── QuestionDetails.java
│ │ │ │ │ │ └── SelectChoiceUtils.kt
│ │ │ │ │ ├── repeats/
│ │ │ │ │ │ ├── AddRepeatDialog.kt
│ │ │ │ │ │ └── DeleteRepeatDialogFragment.java
│ │ │ │ │ └── saving/
│ │ │ │ │ ├── DiskFormSaver.java
│ │ │ │ │ ├── FormSaveViewModel.java
│ │ │ │ │ ├── FormSaver.java
│ │ │ │ │ ├── SaveAnswerFileErrorDialogFragment.java
│ │ │ │ │ ├── SaveAnswerFileProgressDialogFragment.java
│ │ │ │ │ └── SaveFormProgressDialogFragment.java
│ │ │ │ ├── formhierarchy/
│ │ │ │ │ ├── FormHierarchyFragment.java
│ │ │ │ │ ├── FormHierarchyFragmentHostActivity.kt
│ │ │ │ │ ├── FormHierarchyViewModel.kt
│ │ │ │ │ ├── HierarchyItem.kt
│ │ │ │ │ ├── HierarchyListAdapter.kt
│ │ │ │ │ ├── HierarchyListItemView.kt
│ │ │ │ │ └── QuestionAnswerProcessor.kt
│ │ │ │ ├── formlists/
│ │ │ │ │ ├── blankformlist/
│ │ │ │ │ │ ├── BlankFormListActivity.kt
│ │ │ │ │ │ ├── BlankFormListAdapter.kt
│ │ │ │ │ │ ├── BlankFormListItem.kt
│ │ │ │ │ │ ├── BlankFormListItemView.kt
│ │ │ │ │ │ ├── BlankFormListMenuProvider.kt
│ │ │ │ │ │ ├── BlankFormListViewModel.kt
│ │ │ │ │ │ └── DeleteBlankFormFragment.kt
│ │ │ │ │ ├── savedformlist/
│ │ │ │ │ │ ├── DeleteSavedFormFragment.kt
│ │ │ │ │ │ ├── SavedFormListItemView.kt
│ │ │ │ │ │ ├── SavedFormListListMenuProvider.kt
│ │ │ │ │ │ ├── SavedFormListViewModel.kt
│ │ │ │ │ │ └── SelectableSavedFormListItemViewHolder.kt
│ │ │ │ │ └── sorting/
│ │ │ │ │ ├── FormListSortingAdapter.kt
│ │ │ │ │ ├── FormListSortingBottomSheetDialog.kt
│ │ │ │ │ └── FormListSortingOption.kt
│ │ │ │ ├── formmanagement/
│ │ │ │ │ ├── CollectFormEntryControllerFactory.kt
│ │ │ │ │ ├── FormFillingIntentFactory.kt
│ │ │ │ │ ├── FormSourceExceptionMapper.kt
│ │ │ │ │ ├── FormsDataService.kt
│ │ │ │ │ ├── LocalFormUseCases.kt
│ │ │ │ │ ├── OpenRosaClientProvider.kt
│ │ │ │ │ ├── ServerFormDetails.kt
│ │ │ │ │ ├── ServerFormUseCases.kt
│ │ │ │ │ ├── download/
│ │ │ │ │ │ ├── FormDownloadException.kt
│ │ │ │ │ │ ├── FormDownloadExceptionMapper.kt
│ │ │ │ │ │ ├── FormDownloader.kt
│ │ │ │ │ │ └── ServerFormDownloader.java
│ │ │ │ │ ├── drafts/
│ │ │ │ │ │ ├── BulkFinalizationViewModel.kt
│ │ │ │ │ │ └── DraftsMenuProvider.kt
│ │ │ │ │ ├── finalization/
│ │ │ │ │ │ └── EditedFormFinalizationProcessor.kt
│ │ │ │ │ ├── formmap/
│ │ │ │ │ │ └── FormMapViewModel.kt
│ │ │ │ │ ├── matchexactly/
│ │ │ │ │ │ └── ServerFormsSynchronizer.java
│ │ │ │ │ └── metadata/
│ │ │ │ │ ├── FormMetadata.kt
│ │ │ │ │ └── FormMetadataParser.kt
│ │ │ │ ├── fragments/
│ │ │ │ │ ├── BarCodeScannerFragment.kt
│ │ │ │ │ ├── BarcodeWidgetScannerFragment.kt
│ │ │ │ │ ├── MediaLoadingFragment.java
│ │ │ │ │ ├── dialogs/
│ │ │ │ │ │ ├── FormsDownloadResultDialog.kt
│ │ │ │ │ │ ├── LocationProvidersDisabledDialog.java
│ │ │ │ │ │ ├── MovingBackwardsDialog.java
│ │ │ │ │ │ ├── RangePickerDialogFragment.kt
│ │ │ │ │ │ ├── RankingWidgetDialog.java
│ │ │ │ │ │ ├── ResetSettingsResultDialog.java
│ │ │ │ │ │ ├── SelectMinimalDialog.java
│ │ │ │ │ │ ├── SelectMultiMinimalDialog.java
│ │ │ │ │ │ ├── SelectOneMinimalDialog.java
│ │ │ │ │ │ └── SimpleDialog.java
│ │ │ │ │ └── viewmodels/
│ │ │ │ │ ├── RankingViewModel.java
│ │ │ │ │ └── SelectMinimalViewModel.java
│ │ │ │ ├── geo/
│ │ │ │ │ ├── MapConfiguratorProvider.java
│ │ │ │ │ └── MapFragmentFactoryImpl.kt
│ │ │ │ ├── injection/
│ │ │ │ │ ├── DaggerUtils.kt
│ │ │ │ │ └── config/
│ │ │ │ │ ├── AppDependencyComponent.kt
│ │ │ │ │ ├── AppDependencyModule.java
│ │ │ │ │ ├── CollectDrawDependencyModule.kt
│ │ │ │ │ ├── CollectEntitiesDependencyModule.kt
│ │ │ │ │ ├── CollectGeoDependencyModule.kt
│ │ │ │ │ ├── CollectGoogleMapsDependencyModule.kt
│ │ │ │ │ ├── CollectOsmDroidDependencyModule.kt
│ │ │ │ │ ├── CollectProjectsDependencyModule.kt
│ │ │ │ │ ├── CollectSelfieCameraDependencyModule.kt
│ │ │ │ │ └── ProjectDependencyModuleFactory.kt
│ │ │ │ ├── instancemanagement/
│ │ │ │ │ ├── FinalizeAllSnackbarPresenter.kt
│ │ │ │ │ ├── InstanceDeleter.kt
│ │ │ │ │ ├── InstanceDiskSynchronizer.java
│ │ │ │ │ ├── InstanceExt.kt
│ │ │ │ │ ├── InstanceListItemView.kt
│ │ │ │ │ ├── InstanceSubmitter.kt
│ │ │ │ │ ├── InstancesDataService.kt
│ │ │ │ │ ├── LocalInstancesUseCases.kt
│ │ │ │ │ ├── autosend/
│ │ │ │ │ │ ├── AutoSendSettingsProvider.kt
│ │ │ │ │ │ ├── FormExt.kt
│ │ │ │ │ │ └── InstanceAutoSendFetcher.kt
│ │ │ │ │ └── send/
│ │ │ │ │ ├── InstanceUploaderActivity.java
│ │ │ │ │ ├── InstanceUploaderListActivity.java
│ │ │ │ │ ├── ReadyToSendBanner.kt
│ │ │ │ │ └── ReadyToSendViewModel.kt
│ │ │ │ ├── itemsets/
│ │ │ │ │ └── FastExternalItemsetsRepository.kt
│ │ │ │ ├── javarosawrapper/
│ │ │ │ │ ├── FormController.kt
│ │ │ │ │ ├── FormControllerExt.kt
│ │ │ │ │ ├── FormDesignException.kt
│ │ │ │ │ ├── FormIndexUtils.java
│ │ │ │ │ ├── InstanceMetadata.java
│ │ │ │ │ ├── JavaRosaFormController.java
│ │ │ │ │ └── ValidationResult.kt
│ │ │ │ ├── listeners/
│ │ │ │ │ ├── AdvanceToNextListener.java
│ │ │ │ │ ├── DeleteInstancesListener.java
│ │ │ │ │ ├── DownloadFormsTaskListener.java
│ │ │ │ │ ├── FormListDownloaderListener.java
│ │ │ │ │ ├── FormLoaderListener.java
│ │ │ │ │ ├── InstanceUploaderListener.java
│ │ │ │ │ ├── Result.java
│ │ │ │ │ ├── SelectItemClickListener.java
│ │ │ │ │ ├── ThousandsSeparatorTextWatcher.java
│ │ │ │ │ └── WidgetValueChangedListener.java
│ │ │ │ ├── location/
│ │ │ │ │ └── client/
│ │ │ │ │ └── MaxAccuracyWithinTimeoutLocationClientWrapper.java
│ │ │ │ ├── logic/
│ │ │ │ │ ├── FileReference.java
│ │ │ │ │ ├── FileReferenceFactory.java
│ │ │ │ │ ├── ImmutableDisplayableQuestion.java
│ │ │ │ │ └── actions/
│ │ │ │ │ └── setgeopoint/
│ │ │ │ │ ├── CollectSetGeopointAction.java
│ │ │ │ │ └── CollectSetGeopointActionHandler.java
│ │ │ │ ├── mainmenu/
│ │ │ │ │ ├── CurrentProjectViewModel.kt
│ │ │ │ │ ├── MainMenuActivity.kt
│ │ │ │ │ ├── MainMenuButton.kt
│ │ │ │ │ ├── MainMenuFragment.kt
│ │ │ │ │ ├── MainMenuViewModel.kt
│ │ │ │ │ ├── MainMenuViewModelFactory.kt
│ │ │ │ │ ├── PermissionsDialogFragment.kt
│ │ │ │ │ ├── RequestPermissionsViewModel.kt
│ │ │ │ │ └── StartNewFormButton.kt
│ │ │ │ ├── notifications/
│ │ │ │ │ ├── NotificationManagerNotifier.kt
│ │ │ │ │ ├── NotificationUtils.kt
│ │ │ │ │ ├── Notifier.kt
│ │ │ │ │ └── builders/
│ │ │ │ │ ├── FormUpdatesAvailableNotificationBuilder.kt
│ │ │ │ │ ├── FormUpdatesDownloadedNotificationBuilder.kt
│ │ │ │ │ ├── FormsSubmissionNotificationBuilder.kt
│ │ │ │ │ ├── FormsSyncFailedNotificationBuilder.kt
│ │ │ │ │ └── FormsSyncStoppedNotificationBuilder.kt
│ │ │ │ ├── preferences/
│ │ │ │ │ ├── Defaults.kt
│ │ │ │ │ ├── PreferenceVisibilityHandler.kt
│ │ │ │ │ ├── ProjectPreferencesViewModel.kt
│ │ │ │ │ ├── ServerPreferencesAdder.java
│ │ │ │ │ ├── SettingsExt.kt
│ │ │ │ │ ├── dialogs/
│ │ │ │ │ │ ├── AdminPasswordDialogFragment.kt
│ │ │ │ │ │ ├── ChangeAdminPasswordDialog.kt
│ │ │ │ │ │ ├── DeleteProjectDialog.kt
│ │ │ │ │ │ ├── ResetDialogPreference.java
│ │ │ │ │ │ ├── ResetDialogPreferenceFragmentCompat.java
│ │ │ │ │ │ ├── ResetProgressDialog.kt
│ │ │ │ │ │ └── ServerAuthDialogFragment.java
│ │ │ │ │ ├── filters/
│ │ │ │ │ │ └── ControlCharacterFilter.java
│ │ │ │ │ ├── screens/
│ │ │ │ │ │ ├── AccessControlPreferencesFragment.kt
│ │ │ │ │ │ ├── BaseAdminPreferencesFragment.java
│ │ │ │ │ │ ├── BasePreferencesFragment.kt
│ │ │ │ │ │ ├── BaseProjectPreferencesFragment.java
│ │ │ │ │ │ ├── DevToolsPreferencesFragment.kt
│ │ │ │ │ │ ├── ExperimentalPreferencesFragment.java
│ │ │ │ │ │ ├── FormEntryAccessPreferencesFragment.kt
│ │ │ │ │ │ ├── FormManagementPreferencesFragment.java
│ │ │ │ │ │ ├── FormMetadataPreferencesFragment.java
│ │ │ │ │ │ ├── IdentityPreferencesFragment.kt
│ │ │ │ │ │ ├── MainMenuAccessPreferencesFragment.kt
│ │ │ │ │ │ ├── MapsPreferencesFragment.kt
│ │ │ │ │ │ ├── ProjectDisplayPreferencesFragment.kt
│ │ │ │ │ │ ├── ProjectManagementPreferencesFragment.kt
│ │ │ │ │ │ ├── ProjectPreferencesActivity.kt
│ │ │ │ │ │ ├── ProjectPreferencesFragment.kt
│ │ │ │ │ │ ├── ServerPreferencesFragment.java
│ │ │ │ │ │ ├── UserInterfacePreferencesFragment.java
│ │ │ │ │ │ └── UserSettingsAccessPreferencesFragment.java
│ │ │ │ │ ├── source/
│ │ │ │ │ │ ├── SettingsStore.kt
│ │ │ │ │ │ ├── SharedPreferencesSettings.kt
│ │ │ │ │ │ └── SharedPreferencesSettingsProvider.kt
│ │ │ │ │ └── utilities/
│ │ │ │ │ └── PreferencesUtils.java
│ │ │ │ ├── projects/
│ │ │ │ │ ├── DuplicateProjectConfirmationDialog.kt
│ │ │ │ │ ├── FileDebugLogger.kt
│ │ │ │ │ ├── ManualProjectCreatorDialog.kt
│ │ │ │ │ ├── ProjectCreatorImpl.kt
│ │ │ │ │ ├── ProjectDeleter.kt
│ │ │ │ │ ├── ProjectDependencyModule.kt
│ │ │ │ │ ├── ProjectIconView.kt
│ │ │ │ │ ├── ProjectListItemView.kt
│ │ │ │ │ ├── ProjectResetter.kt
│ │ │ │ │ ├── ProjectSettingsDialog.kt
│ │ │ │ │ ├── ProjectsDataService.kt
│ │ │ │ │ ├── QrCodeProjectCreatorDialog.kt
│ │ │ │ │ └── SettingsConnectionMatcherImpl.kt
│ │ │ │ ├── savepoints/
│ │ │ │ │ ├── SavepointTask.kt
│ │ │ │ │ └── SavepointUseCases.kt
│ │ │ │ ├── state/
│ │ │ │ │ └── DataKeys.kt
│ │ │ │ ├── storage/
│ │ │ │ │ ├── StoragePathProvider.kt
│ │ │ │ │ ├── StoragePaths.kt
│ │ │ │ │ └── StorageSubdirectory.java
│ │ │ │ ├── tasks/
│ │ │ │ │ ├── DownloadFormListTask.java
│ │ │ │ │ ├── DownloadFormsTask.java
│ │ │ │ │ ├── FormLoaderTask.java
│ │ │ │ │ ├── InstanceUploaderTask.java
│ │ │ │ │ ├── MediaLoadingTask.java
│ │ │ │ │ ├── ProgressNotifier.java
│ │ │ │ │ ├── SaveFormIndexTask.java
│ │ │ │ │ ├── SaveFormToDisk.java
│ │ │ │ │ └── SaveToDiskResult.java
│ │ │ │ ├── upload/
│ │ │ │ │ ├── FormUploadException.kt
│ │ │ │ │ ├── InstanceServerUploader.java
│ │ │ │ │ └── InstanceUploader.java
│ │ │ │ ├── utilities/
│ │ │ │ │ ├── ActionRegister.kt
│ │ │ │ │ ├── AdminPasswordProvider.java
│ │ │ │ │ ├── AndroidUserAgent.java
│ │ │ │ │ ├── AnimationUtils.java
│ │ │ │ │ ├── Appearances.kt
│ │ │ │ │ ├── ApplicationConstants.java
│ │ │ │ │ ├── ArrayUtils.java
│ │ │ │ │ ├── AuthDialogUtility.java
│ │ │ │ │ ├── CSVUtils.java
│ │ │ │ │ ├── ChangeLockProvider.kt
│ │ │ │ │ ├── CollectStrictMode.kt
│ │ │ │ │ ├── ContentUriHelper.kt
│ │ │ │ │ ├── ContentUriProvider.java
│ │ │ │ │ ├── ControllableLifecyleOwner.kt
│ │ │ │ │ ├── DialogUtils.java
│ │ │ │ │ ├── EncryptionUtils.java
│ │ │ │ │ ├── ExternalAppIntentProvider.java
│ │ │ │ │ ├── ExternalizableFormDefCache.java
│ │ │ │ │ ├── FileProvider.java
│ │ │ │ │ ├── FileUtils.java
│ │ │ │ │ ├── FormEntryPromptUtils.java
│ │ │ │ │ ├── FormNameUtils.java
│ │ │ │ │ ├── FormUtils.java
│ │ │ │ │ ├── FormsDownloadResultInterpreter.kt
│ │ │ │ │ ├── FormsRepositoryProvider.kt
│ │ │ │ │ ├── FormsUploadResultInterpreter.kt
│ │ │ │ │ ├── HtmlUtils.java
│ │ │ │ │ ├── ImageCompressionController.kt
│ │ │ │ │ ├── InstanceAutoDeleteChecker.kt
│ │ │ │ │ ├── InstanceUploaderUtils.java
│ │ │ │ │ ├── InstancesRepositoryProvider.kt
│ │ │ │ │ ├── LocaleHelper.kt
│ │ │ │ │ ├── MediaUtils.kt
│ │ │ │ │ ├── MyanmarDateUtils.java
│ │ │ │ │ ├── QuestionMediaManager.java
│ │ │ │ │ ├── RankingItemTouchHelperCallback.java
│ │ │ │ │ ├── ReplaceCallback.java
│ │ │ │ │ ├── ResponseMessageParser.java
│ │ │ │ │ ├── SavepointsRepositoryProvider.kt
│ │ │ │ │ ├── SelectOneWidgetUtils.java
│ │ │ │ │ ├── SoftKeyboardController.kt
│ │ │ │ │ ├── ThemeUtils.java
│ │ │ │ │ ├── UnderlyingValuesConcat.java
│ │ │ │ │ ├── ViewUtils.kt
│ │ │ │ │ ├── WebCredentialsUtils.java
│ │ │ │ │ └── ZipUtils.java
│ │ │ │ ├── version/
│ │ │ │ │ ├── VersionDescriptionProvider.java
│ │ │ │ │ └── VersionInformation.java
│ │ │ │ ├── views/
│ │ │ │ │ ├── ChoicesRecyclerView.java
│ │ │ │ │ ├── CustomNumberPicker.kt
│ │ │ │ │ ├── CustomWebView.java
│ │ │ │ │ ├── DayNightProgressDialog.java
│ │ │ │ │ ├── DecoratedBarcodeView.kt
│ │ │ │ │ ├── SlidingTabLayout.java
│ │ │ │ │ ├── SlidingTabStrip.java
│ │ │ │ │ ├── TrackingTouchSlider.kt
│ │ │ │ │ ├── TransparentProgressScreen.kt
│ │ │ │ │ ├── TwoItemMultipleChoiceView.java
│ │ │ │ │ └── WidgetAnswerText.kt
│ │ │ │ └── widgets/
│ │ │ │ ├── AnnotateWidget.java
│ │ │ │ ├── AudioWidget.java
│ │ │ │ ├── BaseImageWidget.java
│ │ │ │ ├── BearingWidget.java
│ │ │ │ ├── CounterWidget.kt
│ │ │ │ ├── DecimalWidget.java
│ │ │ │ ├── DrawWidget.java
│ │ │ │ ├── ExAudioWidget.java
│ │ │ │ ├── ExDecimalWidget.java
│ │ │ │ ├── ExImageWidget.java
│ │ │ │ ├── ExIntegerWidget.java
│ │ │ │ ├── ExStringWidget.java
│ │ │ │ ├── GeoPointMapWidget.java
│ │ │ │ ├── GeoPointWidget.java
│ │ │ │ ├── GeoShapeWidget.java
│ │ │ │ ├── GeoTraceWidget.java
│ │ │ │ ├── ImageWidget.java
│ │ │ │ ├── IntegerWidget.java
│ │ │ │ ├── MediaWidgetAnswerViewModel.kt
│ │ │ │ ├── OSMWidget.java
│ │ │ │ ├── PrinterWidget.kt
│ │ │ │ ├── QuestionWidget.java
│ │ │ │ ├── RatingWidget.java
│ │ │ │ ├── SignatureWidget.java
│ │ │ │ ├── StringNumberWidget.java
│ │ │ │ ├── StringWidget.java
│ │ │ │ ├── TextWidgetAnswer.kt
│ │ │ │ ├── TimedGridWidget.kt
│ │ │ │ ├── TriggerWidget.java
│ │ │ │ ├── UrlWidget.java
│ │ │ │ ├── WidgetAnswer.kt
│ │ │ │ ├── WidgetAnswerView.kt
│ │ │ │ ├── WidgetFactory.java
│ │ │ │ ├── WidgetIconButton.kt
│ │ │ │ ├── arbitraryfile/
│ │ │ │ │ ├── ArbitraryFileWidget.kt
│ │ │ │ │ ├── ArbitraryFileWidgetContent.kt
│ │ │ │ │ ├── ArbitraryFileWidgetDelegate.kt
│ │ │ │ │ ├── ExArbitraryFileWidget.kt
│ │ │ │ │ └── ExArbitraryFileWidgetContent.kt
│ │ │ │ ├── barcode/
│ │ │ │ │ ├── BarcodeWidget.kt
│ │ │ │ │ └── BarcodeWidgetContent.kt
│ │ │ │ ├── datetime/
│ │ │ │ │ ├── DatePickerDetails.java
│ │ │ │ │ ├── DateTimeUtils.java
│ │ │ │ │ ├── DateTimeWidget.java
│ │ │ │ │ ├── DateWidget.java
│ │ │ │ │ ├── TimeWidget.java
│ │ │ │ │ └── pickers/
│ │ │ │ │ ├── BikramSambatDatePickerDialog.java
│ │ │ │ │ ├── BuddhistDatePickerDialog.kt
│ │ │ │ │ ├── CopticDatePickerDialog.java
│ │ │ │ │ ├── CustomDatePickerDialog.java
│ │ │ │ │ ├── CustomTimePickerDialog.java
│ │ │ │ │ ├── EthiopianDatePickerDialog.java
│ │ │ │ │ ├── FixedDatePickerDialog.java
│ │ │ │ │ ├── IslamicDatePickerDialog.java
│ │ │ │ │ ├── MyanmarDatePickerDialog.java
│ │ │ │ │ └── PersianDatePickerDialog.java
│ │ │ │ ├── interfaces/
│ │ │ │ │ ├── FileWidget.java
│ │ │ │ │ ├── GeoDataRequester.kt
│ │ │ │ │ ├── MultiChoiceWidget.java
│ │ │ │ │ ├── Printer.kt
│ │ │ │ │ ├── SelectChoiceLoader.kt
│ │ │ │ │ ├── Widget.java
│ │ │ │ │ └── WidgetDataReceiver.kt
│ │ │ │ ├── items/
│ │ │ │ │ ├── BaseSelectListWidget.java
│ │ │ │ │ ├── ItemsWidgetUtils.kt
│ │ │ │ │ ├── LabelWidget.java
│ │ │ │ │ ├── LikertWidget.java
│ │ │ │ │ ├── ListMultiWidget.java
│ │ │ │ │ ├── ListWidget.java
│ │ │ │ │ ├── RankingWidget.java
│ │ │ │ │ ├── SelectImageMapWidget.java
│ │ │ │ │ ├── SelectMinimalWidget.java
│ │ │ │ │ ├── SelectMultiImageMapWidget.java
│ │ │ │ │ ├── SelectMultiMinimalWidget.java
│ │ │ │ │ ├── SelectMultiWidget.java
│ │ │ │ │ ├── SelectOneFromMapDialogFragment.kt
│ │ │ │ │ ├── SelectOneFromMapWidget.kt
│ │ │ │ │ ├── SelectOneImageMapWidget.java
│ │ │ │ │ ├── SelectOneMinimalWidget.java
│ │ │ │ │ └── SelectOneWidget.java
│ │ │ │ ├── range/
│ │ │ │ │ ├── RangeDecimalWidget.java
│ │ │ │ │ ├── RangeIntegerWidget.java
│ │ │ │ │ ├── RangePickerWidget.java
│ │ │ │ │ └── RangePickerWidgetUtils.kt
│ │ │ │ ├── utilities/
│ │ │ │ │ ├── ActivityGeoDataRequester.kt
│ │ │ │ │ ├── AdditionalAttributes.kt
│ │ │ │ │ ├── AudioFileRequester.java
│ │ │ │ │ ├── AudioRecorderRecordingStatusHandler.java
│ │ │ │ │ ├── BindAttributes.kt
│ │ │ │ │ ├── DateTimeWidgetUtils.java
│ │ │ │ │ ├── ExternalAppRecordingRequester.kt
│ │ │ │ │ ├── FileRequester.kt
│ │ │ │ │ ├── FormControllerWaitingForDataRegistry.java
│ │ │ │ │ ├── GeoPolyDialogFragment.kt
│ │ │ │ │ ├── GeoWidgetUtils.kt
│ │ │ │ │ ├── GetContentAudioFileRequester.kt
│ │ │ │ │ ├── ImageCaptureIntentCreator.kt
│ │ │ │ │ ├── InternalRecordingRequester.java
│ │ │ │ │ ├── QuestionFontSizeUtils.kt
│ │ │ │ │ ├── RangeWidgetUtils.java
│ │ │ │ │ ├── RecordingRequester.java
│ │ │ │ │ ├── RecordingRequesterProvider.java
│ │ │ │ │ ├── RecordingStatusHandler.java
│ │ │ │ │ ├── SearchQueryViewModel.java
│ │ │ │ │ ├── StringRequester.kt
│ │ │ │ │ ├── StringWidgetUtils.java
│ │ │ │ │ ├── ViewModelAudioPlayer.kt
│ │ │ │ │ ├── WaitingForDataRegistry.java
│ │ │ │ │ └── WidgetAnswerDialogFragment.kt
│ │ │ │ ├── video/
│ │ │ │ │ ├── ExVideoWidget.kt
│ │ │ │ │ ├── ExVideoWidgetContent.kt
│ │ │ │ │ ├── VideoWidget.kt
│ │ │ │ │ ├── VideoWidgetAnswer.kt
│ │ │ │ │ └── VideoWidgetContent.kt
│ │ │ │ ├── viewmodels/
│ │ │ │ │ ├── DateTimeViewModel.java
│ │ │ │ │ └── QuestionViewModel.kt
│ │ │ │ └── warnings/
│ │ │ │ └── SpacesInUnderlyingValuesWarning.java
│ │ │ └── utilities/
│ │ │ ├── Result.java
│ │ │ └── UserAgentProvider.java
│ │ └── res/
│ │ ├── anim/
│ │ │ ├── fade_in.xml
│ │ │ ├── fade_out.xml
│ │ │ ├── push_left_in.xml
│ │ │ ├── push_left_out.xml
│ │ │ ├── push_right_in.xml
│ │ │ └── push_right_out.xml
│ │ ├── drawable/
│ │ │ ├── counter_minus_button_background.xml
│ │ │ ├── counter_plus_button_background.xml
│ │ │ ├── counter_value_background.xml
│ │ │ ├── ic_access_time.xml
│ │ │ ├── ic_add_circle_24.xml
│ │ │ ├── ic_arrow_back.xml
│ │ │ ├── ic_arrow_drop_down.xml
│ │ │ ├── ic_arrow_up.xml
│ │ │ ├── ic_baseline_delete_72.xml
│ │ │ ├── ic_baseline_delete_outline_24.xml
│ │ │ ├── ic_baseline_download_72.xml
│ │ │ ├── ic_baseline_edit_72.xml
│ │ │ ├── ic_baseline_error_24.xml
│ │ │ ├── ic_baseline_help_outline_24.xml
│ │ │ ├── ic_baseline_inbox_72.xml
│ │ │ ├── ic_baseline_note_72.xml
│ │ │ ├── ic_baseline_notifications_24.xml
│ │ │ ├── ic_baseline_qr_code_scanner_24.xml
│ │ │ ├── ic_baseline_refresh_24.xml
│ │ │ ├── ic_baseline_refresh_error_24.xml
│ │ │ ├── ic_baseline_search_24.xml
│ │ │ ├── ic_baseline_send_72.xml
│ │ │ ├── ic_baseline_sort_24.xml
│ │ │ ├── ic_cancel.xml
│ │ │ ├── ic_chat_bubble_24dp.xml
│ │ │ ├── ic_check_circle_24.xml
│ │ │ ├── ic_download_24.xml
│ │ │ ├── ic_edit_24.xml
│ │ │ ├── ic_folder_open.xml
│ │ │ ├── ic_form_state_blank.xml
│ │ │ ├── ic_form_state_finalized.xml
│ │ │ ├── ic_form_state_saved.xml
│ │ │ ├── ic_form_state_submission_failed.xml
│ │ │ ├── ic_form_state_submitted.xml
│ │ │ ├── ic_information_outline.xml
│ │ │ ├── ic_map.xml
│ │ │ ├── ic_menu_share.xml
│ │ │ ├── ic_navigate_back.xml
│ │ │ ├── ic_navigate_forward.xml
│ │ │ ├── ic_ondemand_video_black_24dp.xml
│ │ │ ├── ic_outline_assignment_accent_24.xml
│ │ │ ├── ic_outline_cloud_accent_24.xml
│ │ │ ├── ic_outline_color_lens_accent_24.xml
│ │ │ ├── ic_outline_delete_accent_24.xml
│ │ │ ├── ic_outline_edit_24.xml
│ │ │ ├── ic_outline_face_accent_24.xml
│ │ │ ├── ic_outline_forum_24.xml
│ │ │ ├── ic_outline_info_small.xml
│ │ │ ├── ic_outline_lock_24.xml
│ │ │ ├── ic_outline_lock_accent_24.xml
│ │ │ ├── ic_outline_lock_open_24.xml
│ │ │ ├── ic_outline_map_accent_24.xml
│ │ │ ├── ic_outline_menu_accent_24.xml
│ │ │ ├── ic_outline_phonelink_setup_accent_24.xml
│ │ │ ├── ic_outline_qr_code_scanner_accent_24.xml
│ │ │ ├── ic_outline_rate_review_24.xml
│ │ │ ├── ic_outline_refresh_accent_24.xml
│ │ │ ├── ic_outline_settings_accent_24.xml
│ │ │ ├── ic_outline_settings_applications_accent_24.xml
│ │ │ ├── ic_outline_share_24.xml
│ │ │ ├── ic_outline_stars_24.xml
│ │ │ ├── ic_outline_vpn_key_accent_24.xml
│ │ │ ├── ic_outline_warning_accent_24.xml
│ │ │ ├── ic_outline_website_24.xml
│ │ │ ├── ic_pause_24dp.xml
│ │ │ ├── ic_person_24dp.xml
│ │ │ ├── ic_play_arrow_24dp.xml
│ │ │ ├── ic_repeat.xml
│ │ │ ├── ic_room_form_state_complete_24dp.xml
│ │ │ ├── ic_room_form_state_complete_48dp.xml
│ │ │ ├── ic_room_form_state_incomplete_24dp.xml
│ │ │ ├── ic_room_form_state_incomplete_48dp.xml
│ │ │ ├── ic_room_form_state_submission_failed_24dp.xml
│ │ │ ├── ic_room_form_state_submission_failed_48dp.xml
│ │ │ ├── ic_room_form_state_submitted_24dp.xml
│ │ │ ├── ic_room_form_state_submitted_48dp.xml
│ │ │ ├── ic_save_menu_24.xml
│ │ │ ├── ic_send_24.xml
│ │ │ ├── ic_sort.xml
│ │ │ ├── ic_sort_by_alpha.xml
│ │ │ ├── ic_sort_by_last_saved.xml
│ │ │ ├── ic_splash_screen_icon.xml
│ │ │ ├── ic_stop_black_24dp.xml
│ │ │ ├── ic_visibility.xml
│ │ │ ├── ic_volume_up_black_24dp.xml
│ │ │ ├── inset_divider_64dp.xml
│ │ │ ├── main_menu_button_background.xml
│ │ │ ├── odk_logo.xml
│ │ │ ├── pill_filled.xml
│ │ │ ├── pill_unfilled.xml
│ │ │ ├── project_icon_background.xml
│ │ │ ├── question_with_error_border.xml
│ │ │ ├── select_item_border.xml
│ │ │ └── start_new_form_button_background.xml
│ │ ├── drawable-ldrtl/
│ │ │ ├── counter_minus_button_background.xml
│ │ │ └── counter_plus_button_background.xml
│ │ ├── layout/
│ │ │ ├── about_item_layout.xml
│ │ │ ├── about_layout.xml
│ │ │ ├── activity_blank_form_list.xml
│ │ │ ├── activity_custom_scanner.xml
│ │ │ ├── activity_preferences_layout.xml
│ │ │ ├── add_repeat_dialog_layout.xml
│ │ │ ├── admin_password_dialog_layout.xml
│ │ │ ├── annotate_widget.xml
│ │ │ ├── audio_controller_layout.xml
│ │ │ ├── audio_player_layout.xml
│ │ │ ├── audio_recording_controller_fragment.xml
│ │ │ ├── audio_video_image_text_label.xml
│ │ │ ├── audio_widget_answer.xml
│ │ │ ├── background_audio_help_fragment_layout.xml
│ │ │ ├── bearing_widget_answer.xml
│ │ │ ├── blank_form_list_item.xml
│ │ │ ├── bottom_sheet.xml
│ │ │ ├── captioned_item.xml
│ │ │ ├── captioned_list_dialog.xml
│ │ │ ├── changes_reason_dialog.xml
│ │ │ ├── checkbox.xml
│ │ │ ├── circular_progress_indicator.xml
│ │ │ ├── counter_widget.xml
│ │ │ ├── current_project_menu_icon.xml
│ │ │ ├── custom_date_picker_dialog.xml
│ │ │ ├── date_time_widget_answer.xml
│ │ │ ├── date_widget_answer.xml
│ │ │ ├── delete_form_layout.xml
│ │ │ ├── delete_project_dialog_layout.xml
│ │ │ ├── draw_widget.xml
│ │ │ ├── ex_audio_widget_answer.xml
│ │ │ ├── ex_image_widget_answer.xml
│ │ │ ├── ex_string_question_type.xml
│ │ │ ├── first_launch_layout.xml
│ │ │ ├── form_chooser_list.xml
│ │ │ ├── form_chooser_list_item.xml
│ │ │ ├── form_chooser_list_item_icon.xml
│ │ │ ├── form_chooser_list_item_map_button.xml
│ │ │ ├── form_chooser_list_item_multiple_choice.xml
│ │ │ ├── form_chooser_list_item_text.xml
│ │ │ ├── form_download_list.xml
│ │ │ ├── form_entry.xml
│ │ │ ├── form_entry_end.xml
│ │ │ ├── form_hierarchy_layout.xml
│ │ │ ├── form_map_activity.xml
│ │ │ ├── fragment_scan.xml
│ │ │ ├── geopoint_question.xml
│ │ │ ├── geoshape_question.xml
│ │ │ ├── geotrace_question.xml
│ │ │ ├── google_drive_deprecation_banner.xml
│ │ │ ├── help_layout.xml
│ │ │ ├── hierarchy_group_item.xml
│ │ │ ├── hierarchy_host_layout.xml
│ │ │ ├── hierarchy_question_item.xml
│ │ │ ├── hierarchy_repeatable_group_instance_item.xml
│ │ │ ├── hierarchy_repeatable_group_item.xml
│ │ │ ├── identify_user_dialog.xml
│ │ │ ├── image_widget.xml
│ │ │ ├── instance_uploader_list.xml
│ │ │ ├── label_widget.xml
│ │ │ ├── launch_intent_button_layout.xml
│ │ │ ├── main_menu.xml
│ │ │ ├── main_menu_activity.xml
│ │ │ ├── main_menu_button.xml
│ │ │ ├── manual_project_creator_dialog_layout.xml
│ │ │ ├── map_button.xml
│ │ │ ├── no_buttons_item_layout.xml
│ │ │ ├── number_picker_dialog.xml
│ │ │ ├── odk_view.xml
│ │ │ ├── osm_widget_answer.xml
│ │ │ ├── password_dialog_layout.xml
│ │ │ ├── permissions_dialog_layout.xml
│ │ │ ├── printer_widget.xml
│ │ │ ├── progress_bar_view.xml
│ │ │ ├── project_icon_view.xml
│ │ │ ├── project_list_item.xml
│ │ │ ├── project_settings_dialog_layout.xml
│ │ │ ├── qr_code_project_creator_dialog_layout.xml
│ │ │ ├── question_error_layout.xml
│ │ │ ├── question_widget.xml
│ │ │ ├── quit_form_dialog_layout.xml
│ │ │ ├── range_picker_widget_answer.xml
│ │ │ ├── range_widget_horizontal.xml
│ │ │ ├── range_widget_vertical.xml
│ │ │ ├── ranking_item.xml
│ │ │ ├── ranking_widget.xml
│ │ │ ├── rating_widget_answer.xml
│ │ │ ├── ready_to_send_banner.xml
│ │ │ ├── reset_dialog_layout.xml
│ │ │ ├── select_image_map_widget_answer.xml
│ │ │ ├── select_list_widget_answer.xml
│ │ │ ├── select_minimal_dialog_layout.xml
│ │ │ ├── select_minimal_widget_answer.xml
│ │ │ ├── select_multi_item.xml
│ │ │ ├── select_one_from_map_widget_answer.xml
│ │ │ ├── select_one_item.xml
│ │ │ ├── server_auth_dialog.xml
│ │ │ ├── show_qrcode_fragment.xml
│ │ │ ├── signature_widget.xml
│ │ │ ├── sort_item_layout.xml
│ │ │ ├── start_new_from_button.xml
│ │ │ ├── tabs_layout.xml
│ │ │ ├── time_widget_answer.xml
│ │ │ ├── transparent_progress_screen.xml
│ │ │ ├── trigger_widget_answer.xml
│ │ │ ├── url_widget_answer.xml
│ │ │ ├── waveform_layout.xml
│ │ │ ├── widget_answer_dialog_layout.xml
│ │ │ └── widget_answer_text.xml
│ │ ├── menu/
│ │ │ ├── blank_form_list_menu.xml
│ │ │ ├── changes_reason_dialog.xml
│ │ │ ├── drafts.xml
│ │ │ ├── form_hierarchy_menu.xml
│ │ │ ├── form_menu.xml
│ │ │ ├── instance_uploader_menu.xml
│ │ │ ├── main_menu.xml
│ │ │ ├── project_preferences_menu.xml
│ │ │ ├── qr_code_scan_menu.xml
│ │ │ ├── saved_form_list_menu.xml
│ │ │ └── select_minimal_dialog_menu.xml
│ │ ├── navigation/
│ │ │ └── form_entry.xml
│ │ ├── raw/
│ │ │ ├── isrgrootx1.pem
│ │ │ └── keep.xml
│ │ ├── values/
│ │ │ ├── arrays.xml
│ │ │ ├── attrs.xml
│ │ │ ├── buttons.xml
│ │ │ ├── colors.xml
│ │ │ ├── date_time_pickers.xml
│ │ │ ├── dimens.xml
│ │ │ ├── form_entry_activity_theme.xml
│ │ │ ├── form_state_colors.xml
│ │ │ ├── leak_canary_config.xml
│ │ │ ├── material_colors.xml
│ │ │ ├── screen_names.xml
│ │ │ ├── settings_theme.xml
│ │ │ ├── shape.xml
│ │ │ ├── styles.xml
│ │ │ ├── theme.xml
│ │ │ └── typography.xml
│ │ ├── values-night/
│ │ │ └── material_colors.xml
│ │ └── xml/
│ │ ├── access_control_preferences.xml
│ │ ├── dev_tools_preferences.xml
│ │ ├── experimental_preferences.xml
│ │ ├── form_entry_access_preferences.xml
│ │ ├── form_management_preferences.xml
│ │ ├── form_metadata_preferences.xml
│ │ ├── identity_preferences.xml
│ │ ├── main_menu_access_preferences.xml
│ │ ├── maps_preferences.xml
│ │ ├── network_security_config.xml
│ │ ├── odk_server_preferences.xml
│ │ ├── project_display_preferences.xml
│ │ ├── project_management_preferences.xml
│ │ ├── project_preferences.xml
│ │ ├── provider_paths.xml
│ │ ├── server_preferences.xml
│ │ ├── user_interface_preferences.xml
│ │ └── user_settings_access_preferences.xml
│ ├── release/
│ │ └── google-services.json
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ ├── android/
│ │ │ ├── TestSettingsProvider.kt
│ │ │ ├── activities/
│ │ │ │ ├── CrashHandlerActivityTest.kt
│ │ │ │ ├── FirstLaunchActivityTest.kt
│ │ │ │ ├── FormFillingActivityTest.kt
│ │ │ │ └── FormHierarchyFragmentHostActivityTest.kt
│ │ │ ├── application/
│ │ │ │ ├── CollectSettingsChangeHandlerTest.kt
│ │ │ │ ├── RobolectricApplication.java
│ │ │ │ └── initialization/
│ │ │ │ ├── AnalyticsInitializerTest.kt
│ │ │ │ ├── CachedFormsCleanerTest.kt
│ │ │ │ ├── ExistingSettingsMigratorTest.kt
│ │ │ │ ├── GoogleDriveProjectsDeleterTest.kt
│ │ │ │ ├── SavepointsImporterTest.kt
│ │ │ │ ├── ScheduledWorkUpgradeTest.kt
│ │ │ │ └── upgrade/
│ │ │ │ └── BeforeProjectsInstallDetectorTest.kt
│ │ │ ├── audio/
│ │ │ │ ├── AudioButtonTest.java
│ │ │ │ ├── AudioControllerViewTest.java
│ │ │ │ ├── AudioRecordingControllerFragmentTest.java
│ │ │ │ ├── AudioRecordingFormErrorDialogFragmentTest.java
│ │ │ │ └── BackgroundAudioHelpDialogFragmentTest.java
│ │ │ ├── backgroundwork/
│ │ │ │ ├── AutoUpdateTaskSpecTest.kt
│ │ │ │ ├── FormUpdateAndInstanceSubmitSchedulerTest.kt
│ │ │ │ ├── SendFormsTaskSpecTest.kt
│ │ │ │ └── SyncFormsTaskSpecTest.kt
│ │ │ ├── configure/
│ │ │ │ └── qr/
│ │ │ │ ├── QRCodeActivityResultDelegateTest.kt
│ │ │ │ ├── QRCodeMenuProviderTest.kt
│ │ │ │ ├── QRCodeScannerFragmentTest.kt
│ │ │ │ └── QRCodeViewModelTest.kt
│ │ │ ├── database/
│ │ │ │ ├── DatabaseFormsRepositoryTest.java
│ │ │ │ ├── DatabaseInstancesRepositoryTest.kt
│ │ │ │ ├── DatabaseSavepointsRepositoryTest.kt
│ │ │ │ ├── FormDatabaseMigratorTest.java
│ │ │ │ ├── InstanceDatabaseMigratorTest.kt
│ │ │ │ └── entities/
│ │ │ │ └── EntitiesDatabaseMigratorTest.kt
│ │ │ ├── dependencies/
│ │ │ │ ├── BikramSambatTest.java
│ │ │ │ └── PersianCalendarTest.java
│ │ │ ├── dynamicpreload/
│ │ │ │ ├── DynamicPreloadExtraTest.kt
│ │ │ │ ├── DynamicPreloadParseProcessorTest.kt
│ │ │ │ ├── ExternalDataReaderTest.java
│ │ │ │ ├── ExternalDataUseCasesTest.kt
│ │ │ │ └── ExternalDataUtilTest.java
│ │ │ ├── entities/
│ │ │ │ ├── DatabaseEntitiesRepositoryTest.kt
│ │ │ │ ├── EntitiesRepositoryTest.kt
│ │ │ │ ├── InMemEntitiesRepositoryTest.kt
│ │ │ │ └── support/
│ │ │ │ └── EntitySameAsMatcher.kt
│ │ │ ├── external/
│ │ │ │ ├── FormUriActivityTest.kt
│ │ │ │ ├── FormsProviderTest.java
│ │ │ │ └── InstanceProviderTest.java
│ │ │ ├── fakes/
│ │ │ │ └── FakePermissionsProvider.kt
│ │ │ ├── formentry/
│ │ │ │ ├── AppStateFormSessionRepositoryTest.kt
│ │ │ │ ├── AudioVideoImageTextLabelTest.java
│ │ │ │ ├── AudioVideoImageTextLabelVisibilityTest.kt
│ │ │ │ ├── BackgroundAudioPermissionDialogFragmentTest.java
│ │ │ │ ├── BackgroundAudioViewModelTest.java
│ │ │ │ ├── FormEndTest.kt
│ │ │ │ ├── FormEndViewModelTest.kt
│ │ │ │ ├── FormEntryMenuProviderTest.kt
│ │ │ │ ├── FormEntryUseCasesTest.kt
│ │ │ │ ├── FormEntryViewModelTest.java
│ │ │ │ ├── FormLoadingDialogFragmentTest.java
│ │ │ │ ├── FormSessionRepositoryTest.kt
│ │ │ │ ├── InMemoryFormSessionRepositoryTest.kt
│ │ │ │ ├── PrinterWidgetViewModelTest.kt
│ │ │ │ ├── QuitFormDialogTest.kt
│ │ │ │ ├── RecordingHandlerTest.java
│ │ │ │ ├── RefreshFormListDialogFragmentTest.java
│ │ │ │ ├── SaveFormProgressDialogFragmentTest.java
│ │ │ │ ├── audit/
│ │ │ │ │ ├── AsyncTaskAuditEventWriterTest.java
│ │ │ │ │ ├── AuditConfigTest.java
│ │ │ │ │ ├── AuditEventCSVLineTest.java
│ │ │ │ │ ├── AuditEventLoggerTest.java
│ │ │ │ │ ├── AuditEventTest.java
│ │ │ │ │ ├── FormSaveViewModelTest.java
│ │ │ │ │ └── IdentityPromptViewModelTest.java
│ │ │ │ ├── backgroundlocation/
│ │ │ │ │ └── BackgroundLocationManagerTest.java
│ │ │ │ ├── repeats/
│ │ │ │ │ └── DeleteRepeatDialogFragmentTest.java
│ │ │ │ └── support/
│ │ │ │ └── InMemFormSessionRepository.kt
│ │ │ ├── formhierarchy/
│ │ │ │ ├── HierarchyListItemViewTest.kt
│ │ │ │ └── QuestionAnswerProcessorTest.kt
│ │ │ ├── formlists/
│ │ │ │ ├── DeleteBlankFormFragmentTest.kt
│ │ │ │ ├── blankformlist/
│ │ │ │ │ ├── BlankFormListItemTest.kt
│ │ │ │ │ ├── BlankFormListItemViewTest.kt
│ │ │ │ │ ├── BlankFormListMenuProviderTest.kt
│ │ │ │ │ └── BlankFormListViewModelTest.kt
│ │ │ │ └── savedformlist/
│ │ │ │ ├── DeleteSavedFormFragmentTest.kt
│ │ │ │ ├── SavedFormListListMenuProviderTest.kt
│ │ │ │ └── SavedFormListViewModelTest.kt
│ │ │ ├── formmanagement/
│ │ │ │ ├── DownloadMediaFilesServerFormUseCasesTest.kt
│ │ │ │ ├── DownloadUpdatesServerFormUseCasesTest.kt
│ │ │ │ ├── FetchFormDetailsServerFormUseCasesTest.kt
│ │ │ │ ├── FilterFormsToAddTest.kt
│ │ │ │ ├── FormFillingIntentFactoryTest.kt
│ │ │ │ ├── FormSourceExceptionMapperTest.kt
│ │ │ │ ├── FormsDataServiceTest.kt
│ │ │ │ ├── LocalFormUseCasesTest.java
│ │ │ │ ├── ServerFormsSynchronizerTest.java
│ │ │ │ ├── ShouldAddFormFileTest.java
│ │ │ │ ├── download/
│ │ │ │ │ ├── FormDownloadExceptionMapperTest.kt
│ │ │ │ │ └── ServerFormDownloaderTest.java
│ │ │ │ ├── drafts/
│ │ │ │ │ └── DraftsMenuProviderTest.kt
│ │ │ │ ├── formmap/
│ │ │ │ │ └── FormMapViewModelTest.kt
│ │ │ │ └── metadata/
│ │ │ │ └── FormMetadataParserTest.kt
│ │ │ ├── fragments/
│ │ │ │ ├── dialogs/
│ │ │ │ │ ├── FormsDownloadResultDialogTest.kt
│ │ │ │ │ ├── RangePickerDialogFragmentTest.kt
│ │ │ │ │ ├── SelectMinimalDialogTest.java
│ │ │ │ │ ├── SelectMultiMinimalDialogTest.java
│ │ │ │ │ └── SelectOneMinimalDialogTest.java
│ │ │ │ └── support/
│ │ │ │ └── DialogFragmentHelpers.java
│ │ │ ├── geo/
│ │ │ │ └── MapFragmentFactoryImplTest.kt
│ │ │ ├── instancemanagement/
│ │ │ │ ├── InstanceDeleterTest.kt
│ │ │ │ ├── InstanceExtKtTest.kt
│ │ │ │ ├── InstanceListItemViewTest.kt
│ │ │ │ ├── InstancesDataServiceTest.kt
│ │ │ │ ├── LocalInstancesUseCasesTest.kt
│ │ │ │ ├── autosend/
│ │ │ │ │ ├── AutoSendSettingsProviderTest.kt
│ │ │ │ │ ├── FormExtTest.kt
│ │ │ │ │ └── InstanceAutoSendFetcherTest.kt
│ │ │ │ └── send/
│ │ │ │ ├── ReadyToSendBannerTest.kt
│ │ │ │ └── ReadyToSendViewModelTest.kt
│ │ │ ├── javarosawrapper/
│ │ │ │ ├── FakeFormController.java
│ │ │ │ └── FormControllerTest.java
│ │ │ ├── location/
│ │ │ │ └── client/
│ │ │ │ ├── FakeLocationClient.java
│ │ │ │ └── MaxAccuracyWithinTimeoutLocationClientWrapperTest.java
│ │ │ ├── mainmenu/
│ │ │ │ ├── CurrentProjectViewModelTest.kt
│ │ │ │ ├── MainMenuActivityTest.kt
│ │ │ │ ├── MainMenuButtonTest.kt
│ │ │ │ ├── MainMenuViewModelTest.kt
│ │ │ │ ├── PermissionsDialogFragmentTest.kt
│ │ │ │ └── RequestPermissionsViewModelTest.kt
│ │ │ ├── notifications/
│ │ │ │ └── NotificationManagerNotifierTest.kt
│ │ │ ├── preferences/
│ │ │ │ ├── AppConfigurationGeneratorTest.kt
│ │ │ │ ├── ProjectPreferencesViewModelTest.kt
│ │ │ │ ├── ServerPreferencesAdderTest.java
│ │ │ │ ├── dialogs/
│ │ │ │ │ ├── AdminPasswordDialogFragmentTest.kt
│ │ │ │ │ ├── ChangeAdminPasswordDialogTest.kt
│ │ │ │ │ ├── DeleteProjectDialogTest.kt
│ │ │ │ │ ├── ResetProgressDialogTest.kt
│ │ │ │ │ └── ServerAuthDialogFragmentTest.java
│ │ │ │ ├── screens/
│ │ │ │ │ ├── FormEntryAccessPreferencesFragmentTest.kt
│ │ │ │ │ ├── FormManagementPreferencesFragmentTest.kt
│ │ │ │ │ ├── FormMetadataPreferencesFragmentTest.kt
│ │ │ │ │ ├── IdentityPreferencesFragmentTest.kt
│ │ │ │ │ ├── MainMenuAccessPreferencesTest.kt
│ │ │ │ │ ├── MapsPreferencesFragmentTest.kt
│ │ │ │ │ ├── ProjectDisplayPreferencesFragmentTest.kt
│ │ │ │ │ ├── ProjectPreferencesFragmentTest.kt
│ │ │ │ │ └── UserInterfacePreferencesFragmentTest.kt
│ │ │ │ └── source/
│ │ │ │ ├── SettingsStoreTest.kt
│ │ │ │ ├── SharedPreferencesSettingsProviderTest.kt
│ │ │ │ └── SharedPreferencesSettingsTest.kt
│ │ │ ├── projects/
│ │ │ │ ├── ExistingProjectMigratorTest.kt
│ │ │ │ ├── ManualProjectCreatorDialogTest.kt
│ │ │ │ ├── ProjectCreatorImplTest.kt
│ │ │ │ ├── ProjectDeleterTest.kt
│ │ │ │ ├── ProjectIconViewTest.kt
│ │ │ │ ├── ProjectListItemViewTest.kt
│ │ │ │ ├── ProjectResetterTest.kt
│ │ │ │ ├── ProjectSettingsDialogTest.kt
│ │ │ │ ├── ProjectsDataServiceTest.kt
│ │ │ │ ├── QrCodeProjectCreatorDialogTest.kt
│ │ │ │ └── SettingsConnectionMatcherImplTest.kt
│ │ │ ├── savepoints/
│ │ │ │ └── SavepointUseCasesTest.kt
│ │ │ ├── storage/
│ │ │ │ └── StoragePathProviderTest.kt
│ │ │ ├── support/
│ │ │ │ ├── CollectHelpers.java
│ │ │ │ ├── Matchers.kt
│ │ │ │ ├── MockFormEntryPromptBuilder.java
│ │ │ │ ├── SwipableParentActivity.kt
│ │ │ │ └── WidgetTestActivity.kt
│ │ │ ├── tasks/
│ │ │ │ └── SaveFormIndexTaskTest.java
│ │ │ ├── utilities/
│ │ │ │ ├── AdminPasswordProviderTest.java
│ │ │ │ ├── AppearancesTest.kt
│ │ │ │ ├── ArrayUtilsTest.java
│ │ │ │ ├── CSVUtilsTest.java
│ │ │ │ ├── ChangeLockProviderTest.kt
│ │ │ │ ├── ExternalAppIntentProviderTest.kt
│ │ │ │ ├── ExternalAppUtilsTest.java
│ │ │ │ ├── FileUtilsTest.java
│ │ │ │ ├── FormNameUtilsTest.java
│ │ │ │ ├── FormsDownloadResultInterpreterTest.kt
│ │ │ │ ├── FormsRepositoryProviderTest.kt
│ │ │ │ ├── FormsUploadResultInterpreterTest.kt
│ │ │ │ ├── HtmlUtilsTest.kt
│ │ │ │ ├── ImageCompressionControllerTest.kt
│ │ │ │ ├── InstanceAutoDeleteCheckerTest.kt
│ │ │ │ ├── InstanceUploaderUtilsTest.java
│ │ │ │ ├── InstancesRepositoryProviderTest.kt
│ │ │ │ ├── MediaUtilsTest.kt
│ │ │ │ ├── MyanmarDateUtilsTest.java
│ │ │ │ ├── QuestionFontSizeUtilsTest.java
│ │ │ │ ├── StubFormController.kt
│ │ │ │ └── WebCredentialsUtilsTest.java
│ │ │ ├── version/
│ │ │ │ └── VersionInformationTest.java
│ │ │ ├── views/
│ │ │ │ ├── ChoicesRecyclerViewTest.java
│ │ │ │ ├── TrackingTouchSliderTest.java
│ │ │ │ └── helpers/
│ │ │ │ └── PromptAutoplayerTest.java
│ │ │ └── widgets/
│ │ │ ├── AnnotateWidgetTest.java
│ │ │ ├── ArbitraryFileWidgetTest.kt
│ │ │ ├── AudioWidgetTest.java
│ │ │ ├── BarcodeWidgetTest.kt
│ │ │ ├── BearingWidgetTest.java
│ │ │ ├── CounterWidgetTest.kt
│ │ │ ├── DecimalWidgetTest.java
│ │ │ ├── DrawWidgetTest.java
│ │ │ ├── ExArbitraryFileWidgetTest.kt
│ │ │ ├── ExAudioWidgetTest.java
│ │ │ ├── ExDecimalWidgetTest.java
│ │ │ ├── ExImageWidgetTest.java
│ │ │ ├── ExIntegerWidgetTest.java
│ │ │ ├── ExStringWidgetTest.kt
│ │ │ ├── ExVideoWidgetTest.kt
│ │ │ ├── GeoPointMapWidgetTest.java
│ │ │ ├── GeoPointWidgetTest.java
│ │ │ ├── GeoShapeWidgetTest.java
│ │ │ ├── GeoTraceWidgetTest.java
│ │ │ ├── ImageWidgetTest.java
│ │ │ ├── IntegerWidgetTest.java
│ │ │ ├── OSMWidgetTest.java
│ │ │ ├── PrinterWidgetTest.kt
│ │ │ ├── QuestionWidgetTest.java
│ │ │ ├── RatingWidgetTest.java
│ │ │ ├── SignatureWidgetTest.java
│ │ │ ├── StringNumberWidgetTest.java
│ │ │ ├── StringWidgetTest.kt
│ │ │ ├── TriggerWidgetTest.java
│ │ │ ├── UrlWidgetTest.java
│ │ │ ├── VideoWidgetTest.kt
│ │ │ ├── WidgetFactoryTest.kt
│ │ │ ├── base/
│ │ │ │ ├── BinaryWidgetTest.java
│ │ │ │ ├── FileWidgetTest.java
│ │ │ │ ├── GeneralExStringWidgetTest.java
│ │ │ │ ├── GeneralSelectMultiWidgetTest.java
│ │ │ │ ├── GeneralSelectOneWidgetTest.java
│ │ │ │ ├── GeneralStringWidgetTest.java
│ │ │ │ ├── QuestionWidgetTest.java
│ │ │ │ ├── SelectWidgetTest.java
│ │ │ │ └── WidgetTest.java
│ │ │ ├── datetime/
│ │ │ │ ├── DateTimeUtilsTest.java
│ │ │ │ ├── DateTimeWidgetTest.java
│ │ │ │ ├── DateTimeWidgetUtilsTest.java
│ │ │ │ ├── DateWidgetTest.java
│ │ │ │ ├── DaylightSavingTest.java
│ │ │ │ ├── TimeWidgetTest.java
│ │ │ │ └── pickers/
│ │ │ │ ├── BikramSambatDatePickerDialogTest.java
│ │ │ │ ├── BuddhistDatePickerDialogTest.kt
│ │ │ │ ├── CopticDatePickerDialogTest.java
│ │ │ │ ├── EthiopianDatePickerDialogTest.java
│ │ │ │ ├── IslamicDatePickerDialogTest.java
│ │ │ │ ├── MyanmarDatePickerDialogTest.java
│ │ │ │ └── PersianDatePickerDialogTest.java
│ │ │ ├── items/
│ │ │ │ ├── LikertWidgetTest.java
│ │ │ │ ├── ListMultiWidgetTest.java
│ │ │ │ ├── ListWidgetTest.java
│ │ │ │ ├── RankingWidgetTest.java
│ │ │ │ ├── SelectChoicesMapDataTest.kt
│ │ │ │ ├── SelectImageMapWidgetTest.java
│ │ │ │ ├── SelectMultiImageMapWidgetTest.java
│ │ │ │ ├── SelectMultiMinimalWidgetTest.java
│ │ │ │ ├── SelectMultiWidgetTest.java
│ │ │ │ ├── SelectOneFromMapDialogFragmentTest.kt
│ │ │ │ ├── SelectOneFromMapWidgetTest.kt
│ │ │ │ ├── SelectOneImageMapWidgetTest.java
│ │ │ │ ├── SelectOneMinimalWidgetTest.java
│ │ │ │ └── SelectOneWidgetTest.java
│ │ │ ├── range/
│ │ │ │ ├── RangeDecimalWidgetTest.java
│ │ │ │ ├── RangeIntegerWidgetTest.java
│ │ │ │ ├── RangePickerWidgetTest.kt
│ │ │ │ └── RangePickerWidgetUtilsTest.kt
│ │ │ ├── support/
│ │ │ │ ├── FakeQuestionMediaManager.java
│ │ │ │ ├── FakeWaitingForDataRegistry.java
│ │ │ │ ├── FormElementFixtures.kt
│ │ │ │ ├── FormEntryPromptSelectChoiceLoader.kt
│ │ │ │ ├── GeoWidgetHelpers.java
│ │ │ │ ├── NoOpMapFragment.kt
│ │ │ │ ├── QuestionWidgetHelpers.java
│ │ │ │ └── SynchronousImageLoader.kt
│ │ │ ├── utilities/
│ │ │ │ ├── ActivityGeoDataRequesterTest.java
│ │ │ │ ├── AudioRecorderRecordingStatusHandlerTest.java
│ │ │ │ ├── ExternalAppRecordingRequesterTest.kt
│ │ │ │ ├── FileRequesterImplTest.kt
│ │ │ │ ├── GeoPolyDialogFragmentTest.kt
│ │ │ │ ├── GeoWidgetUtilsTest.kt
│ │ │ │ ├── GetContentAudioFileRequesterTest.kt
│ │ │ │ ├── InternalRecordingRequesterTest.java
│ │ │ │ ├── RangeWidgetUtilsTest.java
│ │ │ │ ├── RecordingRequesterProviderTest.java
│ │ │ │ ├── StringRequesterImplTest.kt
│ │ │ │ └── StringWidgetUtilsTest.java
│ │ │ ├── viewmodels/
│ │ │ │ ├── DateTimeViewModelTest.java
│ │ │ │ └── QuestionViewModelTest.kt
│ │ │ └── warnings/
│ │ │ ├── SpacesInUnderlyingValuesTest.java
│ │ │ └── SpacesInUnderlyingValuesWarningTest.java
│ │ └── geo/
│ │ └── javarosa/
│ │ └── IntersectsFunctionHandlerTest.kt
│ └── resources/
│ ├── forms/
│ │ └── simple-search-external-csv.xml
│ ├── media/
│ │ └── simple-search-external-csv-fruits.csv
│ └── robolectric.properties
├── config/
│ ├── checkstyle.xml
│ ├── lint.xml
│ ├── pmd-ruleset.xml
│ └── quality.gradle
├── crash-handler/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── crashhandler/
│ │ │ ├── CrashHandler.kt
│ │ │ ├── CrashView.kt
│ │ │ └── MockCrashView.kt
│ │ └── res/
│ │ └── layout/
│ │ └── crash_layout.xml
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── crashhandler/
│ └── CrashHandlerTest.kt
├── create-release.sh
├── db/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── db/
│ │ └── sqlite/
│ │ ├── AltDatabasePathContext.kt
│ │ ├── CursorExt.kt
│ │ ├── CustomSQLiteQueryBuilder.java
│ │ ├── CustomSQLiteQueryExecutor.java
│ │ ├── DatabaseConnection.kt
│ │ ├── DatabaseMigrator.java
│ │ ├── MigrationListDatabaseMigrator.kt
│ │ ├── RowNumbers.kt
│ │ ├── SQLiteColumns.kt
│ │ ├── SQLiteDatabaseExt.kt
│ │ ├── SQLiteUtils.java
│ │ ├── SqlQuery.kt
│ │ └── SynchronizedDatabaseConnection.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── db/
│ └── sqlite/
│ ├── CustomSQLiteQueryBuilderTest.java
│ ├── DatabaseConnectionTest.kt
│ ├── RowNumbersTest.kt
│ ├── SQLiteUtilsTest.java
│ ├── SqlQueryTest.kt
│ ├── SqliteDatabaseExtTest.kt
│ └── support/
│ └── NoopMigrator.kt
├── debug.keystore
├── docs/
│ ├── ANALYTICS-QUESTIONS.md
│ ├── CODE-GUIDELINES.md
│ ├── CONTRIBUTING.md
│ ├── STATE.md
│ ├── TEST-GUIDELINES.md
│ ├── WIDGETS.md
│ └── WINDOWS-DEV-SETUP.md
├── download-robolectric-deps.sh
├── draw/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── draw/
│ │ │ ├── DaggerSetup.kt
│ │ │ ├── DrawActivity.java
│ │ │ ├── DrawView.kt
│ │ │ ├── DrawViewModel.kt
│ │ │ ├── PenColorPickerDialog.kt
│ │ │ ├── PenColorPickerViewModel.kt
│ │ │ ├── QuitDrawingDialog.kt
│ │ │ └── RobolectricApplication.kt
│ │ └── res/
│ │ └── layout/
│ │ ├── draw_layout.xml
│ │ └── quit_drawing_dialog_layout.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── draw/
│ │ ├── DrawActivityTest.kt
│ │ ├── PenColorPickerDialogTest.kt
│ │ └── PenColorPickerViewModelTest.kt
│ └── resources/
│ └── robolectric.properties
├── entities/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── entities/
│ │ │ ├── DaggerSetup.kt
│ │ │ ├── LocalEntityUseCases.kt
│ │ │ ├── browser/
│ │ │ │ ├── EntitiesFragment.kt
│ │ │ │ ├── EntitiesViewModel.kt
│ │ │ │ ├── EntityBrowserActivity.kt
│ │ │ │ ├── EntityItem.kt
│ │ │ │ └── EntityListsFragment.kt
│ │ │ ├── javarosa/
│ │ │ │ ├── filter/
│ │ │ │ │ ├── LocalEntitiesFilterStrategy.kt
│ │ │ │ │ └── PullDataFunctionHandler.kt
│ │ │ │ ├── finalization/
│ │ │ │ │ ├── EntitiesExtra.kt
│ │ │ │ │ ├── EntityFormFinalizationProcessor.kt
│ │ │ │ │ ├── FormEntity.kt
│ │ │ │ │ └── InvalidEntity.kt
│ │ │ │ ├── intance/
│ │ │ │ │ ├── LocalEntitiesExternalInstanceParserFactory.kt
│ │ │ │ │ ├── LocalEntitiesInstanceAdapter.kt
│ │ │ │ │ └── LocalEntitiesInstanceProvider.kt
│ │ │ │ ├── parse/
│ │ │ │ │ ├── EntityFormExtra.kt
│ │ │ │ │ ├── EntityFormParseProcessor.kt
│ │ │ │ │ ├── EntitySchema.kt
│ │ │ │ │ ├── EntityXFormParserFactory.kt
│ │ │ │ │ ├── SaveTo.kt
│ │ │ │ │ ├── StringExt.kt
│ │ │ │ │ └── XPathExpressionExt.kt
│ │ │ │ └── spec/
│ │ │ │ ├── EntityAction.kt
│ │ │ │ ├── EntityFormParser.kt
│ │ │ │ ├── FormEntityElement.kt
│ │ │ │ └── UnrecognizedEntityVersionException.kt
│ │ │ ├── server/
│ │ │ │ └── EntitySource.kt
│ │ │ └── storage/
│ │ │ ├── EntitiesRepository.kt
│ │ │ ├── Entity.kt
│ │ │ ├── EntityList.kt
│ │ │ ├── InMemEntitiesRepository.kt
│ │ │ └── QueryException.kt
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── entities_layout.xml
│ │ │ ├── entity_list_item_layout.xml
│ │ │ └── list_layout.xml
│ │ └── navigation/
│ │ └── entities_nav.xml
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── entities/
│ ├── LocalEntityUseCasesTest.kt
│ ├── browser/
│ │ └── EntityItemTest.kt
│ └── javarosa/
│ ├── EntitiesTest.kt
│ ├── EntityFormFinalizationProcessorTest.kt
│ ├── EntityFormParseProcessorTest.kt
│ ├── EntityFormParserTest.kt
│ ├── LocalEntitiesInstanceProviderTest.kt
│ ├── filter/
│ │ ├── LocalEntitiesFilterStrategyTest.kt
│ │ └── PullDataFunctionHandlerTest.kt
│ ├── parse/
│ │ ├── StringExtTest.kt
│ │ └── XPathExpressionExtTest.kt
│ └── support/
│ └── EntityXFormsElement.kt
├── errors/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── errors/
│ │ │ ├── ErrorActivity.kt
│ │ │ ├── ErrorAdapter.kt
│ │ │ └── ErrorItem.kt
│ │ └── res/
│ │ └── layout/
│ │ ├── activity_error.xml
│ │ └── error_item.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── errors/
│ │ └── ErrorActivityTest.kt
│ └── resources/
│ └── robolectric.properties
├── external-app/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── externalapp/
│ │ └── ExternalAppUtils.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── externalapp/
│ └── ExternalAppUtilsTest.kt
├── forms/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── forms/
│ │ ├── Form.java
│ │ ├── FormListItem.kt
│ │ ├── FormSource.java
│ │ ├── FormSourceException.kt
│ │ ├── FormsRepository.java
│ │ ├── ManifestFile.kt
│ │ ├── MediaFile.kt
│ │ ├── instances/
│ │ │ ├── Instance.java
│ │ │ └── InstancesRepository.java
│ │ └── savepoints/
│ │ ├── Savepoint.kt
│ │ └── SavepointsRepository.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── forms/
│ └── instances/
│ └── InstanceTest.kt
├── forms-test/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── formstest/
│ │ ├── FormFixtures.kt
│ │ ├── FormUtils.kt
│ │ ├── FormsRepositoryTest.java
│ │ ├── InMemFormsRepository.java
│ │ ├── InMemInstancesRepository.java
│ │ ├── InMemSavepointsRepository.kt
│ │ ├── InstanceFixtures.kt
│ │ ├── InstanceUtils.kt
│ │ ├── InstancesRepositoryTest.kt
│ │ └── SavepointsRepositoryTest.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── formstest/
│ ├── InMemFormsRepositoryTest.java
│ ├── InMemInstancesRepositoryTest.kt
│ └── InMemSavepointsRepositoryTest.kt
├── fragments-test/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── fragmentstest/
│ └── FragmentScenarioLauncherRule.kt
├── geo/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── geo/
│ │ │ ├── Constants.kt
│ │ │ ├── DaggerSetup.kt
│ │ │ ├── GeoActivityUtils.kt
│ │ │ ├── GeoUtils.kt
│ │ │ ├── analytics/
│ │ │ │ └── AnalyticsEvents.kt
│ │ │ ├── geopoint/
│ │ │ │ ├── AccuracyProgressView.kt
│ │ │ │ ├── AccuracyStatusView.kt
│ │ │ │ ├── GeoPointActivity.kt
│ │ │ │ ├── GeoPointDialogFragment.kt
│ │ │ │ ├── GeoPointMapActivity.java
│ │ │ │ ├── GeoPointViewModel.kt
│ │ │ │ └── LocationAccuracy.kt
│ │ │ ├── geopoly/
│ │ │ │ ├── GeoPolyFragment.kt
│ │ │ │ ├── GeoPolySettingsDialogFragment.java
│ │ │ │ ├── GeoPolyUtils.kt
│ │ │ │ ├── GeoPolyViewModel.kt
│ │ │ │ └── InfoDialog.kt
│ │ │ ├── javarosa/
│ │ │ │ └── IntersectsFunctionHandler.kt
│ │ │ └── selection/
│ │ │ ├── MappableSelectItem.kt
│ │ │ ├── SelectionMapFragment.kt
│ │ │ └── SelectionSummarySheet.kt
│ │ └── res/
│ │ ├── color/
│ │ │ └── fab_surface_background_color_less_transparent_disabled.xml
│ │ ├── drawable/
│ │ │ ├── ic_add_location.xml
│ │ │ ├── ic_backspace.xml
│ │ │ ├── ic_crop_frame.xml
│ │ │ ├── ic_distance.xml
│ │ │ ├── ic_info.xml
│ │ │ ├── ic_layers.xml
│ │ │ ├── ic_my_location.xml
│ │ │ ├── ic_note_add.xml
│ │ │ ├── ic_pause_36.xml
│ │ │ └── property_divider.xml
│ │ ├── layout/
│ │ │ ├── accuracy_progress_layout.xml
│ │ │ ├── accuracy_status_layout.xml
│ │ │ ├── geopoint_dialog.xml
│ │ │ ├── geopoint_layout.xml
│ │ │ ├── geopoly_dialog.xml
│ │ │ ├── geopoly_layout.xml
│ │ │ ├── property.xml
│ │ │ ├── selection_map_layout.xml
│ │ │ ├── selection_summary_sheet_layout.xml
│ │ │ └── simple_spinner_dropdown_item.xml
│ │ ├── layout-land/
│ │ │ ├── geopoint_layout.xml
│ │ │ └── geopoly_layout.xml
│ │ └── values/
│ │ ├── attrs.xml
│ │ ├── fab_surface.xml
│ │ └── force_light_surface_overlay.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── geo/
│ │ ├── GeoUtilsTest.kt
│ │ ├── geopoint/
│ │ │ ├── AccuracyProgressViewTest.kt
│ │ │ ├── AccuracyStatusViewTest.kt
│ │ │ ├── GeoPointActivityTest.kt
│ │ │ ├── GeoPointDialogFragmentTest.kt
│ │ │ ├── GeoPointMapActivityTest.java
│ │ │ └── LocationTrackerGeoPointViewModelTest.kt
│ │ ├── geopoly/
│ │ │ ├── GeoPolyFragmentTest.kt
│ │ │ ├── GeoPolySettingsDialogFragmentTest.java
│ │ │ ├── GeoPolyUtilsTest.kt
│ │ │ ├── GeoPolyViewModelTest.kt
│ │ │ └── InfoContentTest.kt
│ │ ├── selection/
│ │ │ ├── SelectionMapFragmentTest.kt
│ │ │ └── SelectionSummarySheetTest.kt
│ │ └── support/
│ │ ├── AccuracyStatusViewMatcher.kt
│ │ ├── FakeLocationTracker.kt
│ │ ├── FakeMapFragment.kt
│ │ ├── Fixtures.kt
│ │ └── RobolectricApplication.kt
│ └── resources/
│ └── robolectric.properties
├── google-maps/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── googlemaps/
│ │ ├── BitmapDescriptorCache.kt
│ │ ├── DaggerSetup.kt
│ │ ├── GoogleMapConfigurator.java
│ │ ├── GoogleMapFragment.java
│ │ ├── GoogleMapsMapBoxOfflineTileProvider.java
│ │ ├── MapPointExt.kt
│ │ ├── circles/
│ │ │ └── CircleFeature.kt
│ │ └── scaleview/
│ │ ├── Drawer.java
│ │ ├── MapScaleModel.java
│ │ ├── MapScaleView.java
│ │ ├── Scale.java
│ │ ├── Scales.java
│ │ └── ViewConfig.java
│ └── res/
│ ├── layout/
│ │ └── map_layout.xml
│ └── values/
│ └── attrs.xml
├── gradle/
│ ├── libs.versions.toml
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── icons/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── res/
│ └── drawable/
│ ├── ic_add_white_24.xml
│ ├── ic_baseline_add_24.xml
│ ├── ic_baseline_barcode_scanner_white_24.xml
│ ├── ic_baseline_calendar_today_white_24.xml
│ ├── ic_baseline_check_24.xml
│ ├── ic_baseline_collapse_24.xml
│ ├── ic_baseline_done_all_24.xml
│ ├── ic_baseline_draw_white_24.xml
│ ├── ic_baseline_expand_24.xml
│ ├── ic_baseline_explore_white_24.xml
│ ├── ic_baseline_format_list_bulleted_white_24.xml
│ ├── ic_baseline_format_list_numbered_white_24.xml
│ ├── ic_baseline_language_24.xml
│ ├── ic_baseline_layers_24.xml
│ ├── ic_baseline_library_music_white_24.xml
│ ├── ic_baseline_list_24.xml
│ ├── ic_baseline_location_off_24.xml
│ ├── ic_baseline_location_on_24.xml
│ ├── ic_baseline_location_on_white_24.xml
│ ├── ic_baseline_markup_white_24.xml
│ ├── ic_baseline_mic_24.xml
│ ├── ic_baseline_mic_off_24.xml
│ ├── ic_baseline_mic_white_24.xml
│ ├── ic_baseline_my_location_white_24.xml
│ ├── ic_baseline_open_in_new_white_24.xml
│ ├── ic_baseline_photo_camera_white_24.xml
│ ├── ic_baseline_photo_library_white_24.xml
│ ├── ic_baseline_print_white_24.xml
│ ├── ic_baseline_qr_code_2_add_24.xml
│ ├── ic_baseline_remove_24.xml
│ ├── ic_baseline_rule_24.xml
│ ├── ic_baseline_settings_24.xml
│ ├── ic_baseline_signature_white_24.xml
│ ├── ic_baseline_time_filled_white_24.xml
│ ├── ic_baseline_visibility_24.xml
│ ├── ic_baseline_warning_24.xml
│ ├── ic_baseline_wifi_off_24.xml
│ ├── ic_clear_white.xml
│ ├── ic_close.xml
│ ├── ic_color_lens_white.xml
│ ├── ic_delete.xml
│ ├── ic_delete_24.xml
│ ├── ic_edit.xml
│ ├── ic_map_marker_big.xml
│ ├── ic_map_marker_small.xml
│ ├── ic_map_marker_with_hole_big.xml
│ ├── ic_map_marker_with_hole_small.xml
│ ├── ic_map_point.xml
│ ├── ic_notification_small.xml
│ ├── ic_outline_info_24.xml
│ ├── ic_outline_polygon_white_24.xml
│ ├── ic_outline_polyline_white_24.xml
│ ├── ic_save.xml
│ └── ic_save_white.xml
├── image-loader/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── imageloader/
│ ├── GlideImageLoader.kt
│ └── svg/
│ ├── SvgDecoder.kt
│ ├── SvgDrawableTranscoder.kt
│ ├── SvgModule.kt
│ └── SvgSoftwareLayerSetter.kt
├── lists/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── lists/
│ │ │ ├── EmptyListView.kt
│ │ │ ├── RecyclerViewUtils.kt
│ │ │ └── selects/
│ │ │ ├── MultiSelectAdapter.kt
│ │ │ ├── MultiSelectControlsFragment.kt
│ │ │ ├── MultiSelectListFragment.kt
│ │ │ ├── MultiSelectViewModel.kt
│ │ │ ├── SelectItem.kt
│ │ │ └── SingleSelectViewModel.kt
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── empty_list_view.xml
│ │ │ ├── multi_select_controls_layout.xml
│ │ │ └── multi_select_list.xml
│ │ └── values/
│ │ └── attrs.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── lists/
│ │ ├── EmptyListViewTest.kt
│ │ ├── RobolectricApplication.kt
│ │ └── selects/
│ │ ├── MultiSelectAdapterTest.kt
│ │ ├── MultiSelectControlsFragmentTest.kt
│ │ ├── MultiSelectListFragmentTest.kt
│ │ ├── MultiSelectViewModelTest.kt
│ │ ├── SingleSelectViewModelTest.kt
│ │ └── support/
│ │ └── TextAndCheckboxViewHolder.kt
│ └── resources/
│ └── robolectric.properties
├── location/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── location/
│ │ ├── AndroidLocationClient.java
│ │ ├── BaseLocationClient.kt
│ │ ├── DaggerSetup.kt
│ │ ├── GoogleFusedLocationClient.kt
│ │ ├── Location.kt
│ │ ├── LocationClient.java
│ │ ├── LocationClientProvider.kt
│ │ ├── LocationUtils.kt
│ │ ├── satellites/
│ │ │ ├── GpsStatusSatelliteInfoClient.kt
│ │ │ └── SatelliteInfoClient.kt
│ │ └── tracker/
│ │ ├── ForegroundServiceLocationTracker.kt
│ │ └── LocationTracker.kt
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── location/
│ │ ├── AndroidLocationClientTest.java
│ │ ├── GoogleFusedLocationClientTest.kt
│ │ ├── LocationClientProviderTest.kt
│ │ ├── LocationUtilsTest.kt
│ │ ├── RobolectricApplication.kt
│ │ ├── TestClientListener.java
│ │ ├── TestLocationListener.java
│ │ └── tracker/
│ │ ├── ForegroundServiceLocationTrackerTest.kt
│ │ ├── LocationTrackerServiceTest.kt
│ │ └── LocationTrackerTest.kt
│ └── resources/
│ └── robolectric.properties
├── mapbox/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── mapbox/
│ │ ├── DynamicPolyLineFeature.kt
│ │ ├── DynamicPolygonFeature.kt
│ │ ├── LineFeature.kt
│ │ ├── MapBoxInitializationFragment.kt
│ │ ├── MapFeature.kt
│ │ ├── MapUtils.kt
│ │ ├── MapboxMapConfigurator.java
│ │ ├── MapboxMapFragment.kt
│ │ ├── MarkerFeature.kt
│ │ ├── StaticPolyLineFeature.kt
│ │ ├── StaticPolygonFeature.kt
│ │ └── TileHttpServer.java
│ └── res/
│ └── layout/
│ └── mapbox_fragment_layout.xml
├── maps/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── maps/
│ │ │ ├── AnalyticsEvents.kt
│ │ │ ├── MapConfigurator.kt
│ │ │ ├── MapConsts.kt
│ │ │ ├── MapFragment.kt
│ │ │ ├── MapFragmentFactory.kt
│ │ │ ├── MapPoint.kt
│ │ │ ├── MapViewModel.kt
│ │ │ ├── MapViewModelMapFragment.kt
│ │ │ ├── ZoomObserver.kt
│ │ │ ├── circles/
│ │ │ │ ├── CircleDescription.kt
│ │ │ │ └── CurrentLocationDelegate.kt
│ │ │ ├── layers/
│ │ │ │ ├── DirectoryReferenceLayerRepository.kt
│ │ │ │ ├── MapFragmentReferenceLayerUtils.kt
│ │ │ │ ├── MbtilesFile.java
│ │ │ │ ├── OfflineMapLayersImporterAdapter.kt
│ │ │ │ ├── OfflineMapLayersImporterDialogFragment.kt
│ │ │ │ ├── OfflineMapLayersPickerAdapter.kt
│ │ │ │ ├── OfflineMapLayersPickerBottomSheetDialogFragment.kt
│ │ │ │ ├── OfflineMapLayersViewModel.kt
│ │ │ │ ├── ReferenceLayerRepository.kt
│ │ │ │ └── TileSource.java
│ │ │ ├── markers/
│ │ │ │ ├── MarkerDescription.kt
│ │ │ │ ├── MarkerIconCreator.kt
│ │ │ │ └── MarkerIconDescription.kt
│ │ │ └── traces/
│ │ │ ├── LineDescription.kt
│ │ │ ├── PolygonDescription.kt
│ │ │ └── TraceDescription.kt
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── ic_crosshairs.xml
│ │ └── layout/
│ │ ├── offline_map_layers_importer.xml
│ │ ├── offline_map_layers_importer_item.xml
│ │ ├── offline_map_layers_picker.xml
│ │ └── offline_map_layers_picker_item.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── maps/
│ │ ├── LineDescriptionTest.kt
│ │ ├── MarkerIconDescriptionTest.kt
│ │ ├── PolygonDescriptionTest.kt
│ │ ├── RobolectricApplication.kt
│ │ ├── TraceDescriptionTest.kt
│ │ └── layers/
│ │ ├── DirectoryReferenceLayerRepositoryTest.kt
│ │ ├── InMemReferenceLayerRepository.kt
│ │ ├── MapFragmentReferenceLayerUtilsTest.kt
│ │ ├── OfflineMapLayersImporterDialogFragmentTest.kt
│ │ └── OfflineMapLayersPickerBottomSheetDialogFragmentTest.kt
│ └── resources/
│ └── robolectric.properties
├── material/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── material/
│ │ │ ├── BottomSheetBehavior.kt
│ │ │ ├── ErrorsPill.kt
│ │ │ ├── MaterialFullScreenDialogFragment.kt
│ │ │ ├── MaterialPill.kt
│ │ │ ├── MaterialProgressDialogFragment.java
│ │ │ └── Pill.kt
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── pill.xml
│ │ │ └── progress_dialog.xml
│ │ └── values/
│ │ ├── attrs.xml
│ │ ├── material_3_button_icon_end_style.xml
│ │ └── material_full_screen_dialog_theme.xml
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── material/
│ ├── ErrorsPillTest.kt
│ └── MaterialProgressDialogFragmentTest.java
├── metadata/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── metadata/
│ │ ├── InstallIDProvider.kt
│ │ └── PropertyManager.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── metadata/
│ ├── PropertyManagerTest.kt
│ └── SettingsInstallIDProviderTest.kt
├── mobile-device-management/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── mobiledevicemanagement/
│ │ │ ├── MDMConfigHandler.kt
│ │ │ └── MDMConfigObserver.kt
│ │ └── res/
│ │ └── xml/
│ │ └── managed_configuration.xml
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── mobiledevicemanagement/
│ ├── MDMConfigHandlerTest.kt
│ └── MDMConfigObserverTest.kt
├── nbistubs/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ └── AndroidManifest.xml
├── open-rosa/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── openrosa/
│ │ ├── forms/
│ │ │ ├── DocumentFetchResult.java
│ │ │ ├── EntityIntegrity.kt
│ │ │ ├── OpenRosaClient.kt
│ │ │ └── OpenRosaXmlFetcher.java
│ │ ├── http/
│ │ │ ├── CaseInsensitiveEmptyHeaders.java
│ │ │ ├── CaseInsensitiveHeaders.java
│ │ │ ├── CollectThenSystemContentTypeMapper.java
│ │ │ ├── HttpCredentials.java
│ │ │ ├── HttpCredentialsInterface.java
│ │ │ ├── HttpGetResult.java
│ │ │ ├── HttpHeadResult.java
│ │ │ ├── HttpPostResult.java
│ │ │ ├── OpenRosaConstants.kt
│ │ │ ├── OpenRosaHttpInterface.java
│ │ │ └── okhttp/
│ │ │ ├── OkHttpCaseInsensitiveHeaders.java
│ │ │ ├── OkHttpConnection.java
│ │ │ ├── OkHttpOpenRosaServerClientProvider.java
│ │ │ ├── OpenRosaServerClient.java
│ │ │ └── OpenRosaServerClientProvider.java
│ │ └── parse/
│ │ ├── Kxml2OpenRosaResponseParser.kt
│ │ └── OpenRosaResponseParser.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── openrosa/
│ ├── forms/
│ │ ├── OpenRosaClientTest.kt
│ │ └── OpenRosaXmlFetcherTest.java
│ ├── http/
│ │ ├── CaseInsensitiveEmptyHeadersTest.java
│ │ ├── CollectThenSystemContentTypeMapperTest.java
│ │ ├── OpenRosaGetRequestTest.java
│ │ ├── OpenRosaHeadRequestTest.java
│ │ ├── OpenRosaPostRequestTest.java
│ │ └── okhttp/
│ │ ├── OkHttpCaseInsensitiveHeadersTest.java
│ │ ├── OkHttpConnectionGetRequestTest.java
│ │ ├── OkHttpConnectionHeadRequestTest.java
│ │ ├── OkHttpConnectionPostRequestTest.java
│ │ ├── OkHttpOpenRosaServerClientProviderTest.java
│ │ └── OpenRosaServerClientProviderTest.java
│ ├── parse/
│ │ └── Kxml2OpenRosaResponseParserTest.kt
│ └── support/
│ ├── MockWebServerHelper.java
│ ├── MockWebServerRule.kt
│ └── StubWebCredentialsProvider.java
├── osmdroid/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── osmdroid/
│ │ ├── DaggerSetup.kt
│ │ ├── OsmDroidInitializer.kt
│ │ ├── OsmDroidMapConfigurator.java
│ │ ├── OsmDroidMapFragment.java
│ │ ├── OsmMBTileModuleProvider.java
│ │ ├── OsmMBTileProvider.java
│ │ ├── OsmMBTileSource.java
│ │ └── WebMapService.java
│ └── res/
│ └── layout/
│ └── osm_map_layout.xml
├── permissions/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── permissions/
│ │ │ ├── LocationAccessibilityChecker.kt
│ │ │ ├── PermissionListener.kt
│ │ │ ├── PermissionsChecker.kt
│ │ │ ├── PermissionsDialogCreator.kt
│ │ │ ├── PermissionsProvider.kt
│ │ │ └── RequestPermissionsAPI.kt
│ │ └── res/
│ │ └── drawable/
│ │ ├── ic_photo_camera.xml
│ │ ├── ic_room_24dp.xml
│ │ └── ic_storage.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── permissions/
│ │ ├── PermissionsDialogCreatorTest.kt
│ │ └── PermissionsProviderTest.kt
│ └── resources/
│ └── robolectric.properties
├── printer/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── printer/
│ └── HtmlPrinter.kt
├── projects/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── projects/
│ │ ├── DaggerSetup.kt
│ │ ├── InMemProjectsRepository.kt
│ │ ├── Project.kt
│ │ ├── ProjectConfigurationResult.kt
│ │ ├── ProjectCreator.kt
│ │ ├── ProjectDependencyFactory.kt
│ │ ├── ProjectsRepository.kt
│ │ ├── SettingsConnectionMatcher.kt
│ │ └── SharedPreferencesProjectsRepository.kt
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── projects/
│ │ ├── InMemProjectsRepositoryTest.kt
│ │ ├── ProjectsRepositoryTest.kt
│ │ ├── SharedPreferencesProjectsRepositoryTest.kt
│ │ └── support/
│ │ └── RobolectricApplication.kt
│ └── resources/
│ └── robolectric.properties
├── qr-code/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── qrcode/
│ │ │ ├── BarcodeFilter.kt
│ │ │ ├── BarcodeScannerViewContainer.kt
│ │ │ ├── DetectedState.kt
│ │ │ ├── FlashlightToggle.kt
│ │ │ ├── ScannerControls.kt
│ │ │ ├── ScannerOverlay.kt
│ │ │ ├── mlkit/
│ │ │ │ ├── MlKitBarcodeScannerViewFactory.kt
│ │ │ │ └── PlayServicesFallbackBarcodeScannerViewFactory.kt
│ │ │ └── zxing/
│ │ │ ├── QRCodeCreator.kt
│ │ │ ├── QRCodeDecoder.kt
│ │ │ └── ZxingBarcodeScannerViewFactory.kt
│ │ └── res/
│ │ └── layout/
│ │ ├── mlkit_barcode_scanner_layout.xml
│ │ └── zxing_barcode_scanner_layout.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── qrcode/
│ │ ├── BarcodeFilterTest.kt
│ │ └── QRCodeEncodeDecodeTest.kt
│ └── resources/
│ └── robolectric.properties
├── secrets.gradle
├── selfie-camera/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── selfiecamera/
│ │ │ ├── Camera.kt
│ │ │ ├── CameraXCamera.kt
│ │ │ ├── CaptureSelfieActivity.kt
│ │ │ └── DaggerSetup.kt
│ │ └── res/
│ │ └── layout/
│ │ └── activity_capture_selfie.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── selfiecamera/
│ │ ├── CaptureSelfieActivityTest.kt
│ │ └── support/
│ │ └── RobolectricApplication.kt
│ └── resources/
│ └── robolectric.properties
├── service-test/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── servicetest/
│ ├── NotificationDetails.kt
│ └── ServiceScenario.kt
├── settings/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── settings/
│ │ │ ├── InMemSettingsProvider.kt
│ │ │ ├── ODKAppSettingsImporter.kt
│ │ │ ├── ODKAppSettingsMigrator.java
│ │ │ ├── SettingsProvider.kt
│ │ │ ├── enums/
│ │ │ │ ├── AutoSend.kt
│ │ │ │ ├── FormUpdateMode.java
│ │ │ │ ├── GuidanceHintMode.kt
│ │ │ │ ├── StringIdEnum.kt
│ │ │ │ └── StringIdEnumUtils.kt
│ │ │ ├── importing/
│ │ │ │ ├── ProjectDetailsCreatorImpl.kt
│ │ │ │ └── SettingsImporter.kt
│ │ │ ├── keys/
│ │ │ │ ├── AppConfigurationKeys.kt
│ │ │ │ ├── MetaKeys.kt
│ │ │ │ ├── ProjectKeys.kt
│ │ │ │ └── ProtectedProjectKeys.kt
│ │ │ ├── migration/
│ │ │ │ ├── KeyCombiner.java
│ │ │ │ ├── KeyExtractor.java
│ │ │ │ ├── KeyMover.java
│ │ │ │ ├── KeyRenamer.java
│ │ │ │ ├── KeyTranslator.java
│ │ │ │ ├── KeyUpdater.java
│ │ │ │ ├── KeyValuePair.java
│ │ │ │ ├── Migration.java
│ │ │ │ ├── MigrationUtils.java
│ │ │ │ └── ValueTranslator.java
│ │ │ └── validation/
│ │ │ └── JsonSchemaSettingsValidator.kt
│ │ ├── res/
│ │ │ └── values/
│ │ │ └── strings.xml
│ │ └── resources/
│ │ └── client-settings.schema.json
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── settings/
│ ├── ODKAppSettingsImporterTest.kt
│ ├── ODKAppSettingsMigratorTest.java
│ ├── importing/
│ │ ├── ProjectDetailsCreatorImplTest.kt
│ │ └── SettingsImporterTest.kt
│ ├── migration/
│ │ ├── KeyCombinerTest.java
│ │ ├── KeyExtractorTest.java
│ │ ├── KeyMoverTest.java
│ │ ├── KeyRemoverTest.java
│ │ ├── KeyRenamerTest.java
│ │ ├── KeyTranslatorTest.java
│ │ └── ValueTranslatorTest.java
│ ├── support/
│ │ └── SettingsUtils.kt
│ └── validation/
│ ├── JsonSchemaSettingsValidatorTest.kt
│ └── OriginalJsonSchemaSettingsValidatorTest.kt
├── settings.gradle
├── shadows/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── shadows/
│ │ └── ShadowAndroidXAlertDialog.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── shadows/
│ └── ShadowAndroidXAlertDialogTest.kt
├── shared/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── shared/
│ │ ├── DebugLogger.kt
│ │ ├── PathUtils.kt
│ │ ├── Query.kt
│ │ ├── TempFiles.kt
│ │ ├── TimeInMs.kt
│ │ ├── collections/
│ │ │ └── CollectionExtensions.kt
│ │ ├── files/
│ │ │ └── FileExt.kt
│ │ ├── geometry/
│ │ │ └── Geometry.kt
│ │ ├── injection/
│ │ │ └── ObjectProvider.kt
│ │ ├── locks/
│ │ │ ├── BooleanChangeLock.kt
│ │ │ ├── ChangeLock.kt
│ │ │ └── ThreadSafeBooleanChangeLock.kt
│ │ ├── result/
│ │ │ └── Result.kt
│ │ ├── settings/
│ │ │ ├── InMemSettings.kt
│ │ │ └── Settings.kt
│ │ └── strings/
│ │ ├── Md5.kt
│ │ ├── RandomString.java
│ │ ├── StringUtils.kt
│ │ └── UUIDGenerator.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── shared/
│ ├── Md5Test.kt
│ ├── PathUtilsTest.kt
│ ├── QuickCheck.kt
│ ├── collections/
│ │ └── CollectionExtensionsTest.kt
│ ├── files/
│ │ └── FileExtTest.kt
│ ├── geometry/
│ │ ├── GeometryTest.kt
│ │ └── support/
│ │ ├── GeometryTestUtils.kt
│ │ └── GeometryTestUtilsTest.kt
│ ├── locks/
│ │ ├── BooleanChangeLockTest.kt
│ │ ├── ChangeLockTest.kt
│ │ └── ThreadSafeBooleanChangeLockTest.kt
│ └── strings/
│ └── StringUtilsTest.kt
├── strings/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── strings/
│ │ │ ├── format/
│ │ │ │ └── LengthFormatter.kt
│ │ │ └── localization/
│ │ │ ├── LocalizedActivity.kt
│ │ │ └── LocalizedApplication.kt
│ │ └── res/
│ │ ├── values/
│ │ │ ├── strings.xml
│ │ │ └── untranslated.xml
│ │ ├── values-af/
│ │ │ └── strings.xml
│ │ ├── values-am/
│ │ │ └── strings.xml
│ │ ├── values-ar/
│ │ │ └── strings.xml
│ │ ├── values-bg/
│ │ │ └── strings.xml
│ │ ├── values-bn/
│ │ │ └── strings.xml
│ │ ├── values-ca/
│ │ │ └── strings.xml
│ │ ├── values-cs/
│ │ │ └── strings.xml
│ │ ├── values-da/
│ │ │ └── strings.xml
│ │ ├── values-de/
│ │ │ └── strings.xml
│ │ ├── values-es/
│ │ │ └── strings.xml
│ │ ├── values-es-rSV/
│ │ │ └── strings.xml
│ │ ├── values-et/
│ │ │ └── strings.xml
│ │ ├── values-fa/
│ │ │ └── strings.xml
│ │ ├── values-fa-rAF/
│ │ │ └── strings.xml
│ │ ├── values-fi/
│ │ │ └── strings.xml
│ │ ├── values-fr/
│ │ │ └── strings.xml
│ │ ├── values-hi/
│ │ │ └── strings.xml
│ │ ├── values-ht/
│ │ │ └── strings.xml
│ │ ├── values-in/
│ │ │ └── strings.xml
│ │ ├── values-it/
│ │ │ └── strings.xml
│ │ ├── values-ja/
│ │ │ └── strings.xml
│ │ ├── values-ka/
│ │ │ └── strings.xml
│ │ ├── values-km/
│ │ │ └── strings.xml
│ │ ├── values-ln/
│ │ │ └── strings.xml
│ │ ├── values-lo-rLA/
│ │ │ └── strings.xml
│ │ ├── values-lt/
│ │ │ └── strings.xml
│ │ ├── values-mg/
│ │ │ └── strings.xml
│ │ ├── values-ml/
│ │ │ └── strings.xml
│ │ ├── values-mr/
│ │ │ └── strings.xml
│ │ ├── values-ms/
│ │ │ └── strings.xml
│ │ ├── values-my/
│ │ │ └── strings.xml
│ │ ├── values-ne-rNP/
│ │ │ └── strings.xml
│ │ ├── values-nl/
│ │ │ └── strings.xml
│ │ ├── values-no/
│ │ │ └── strings.xml
│ │ ├── values-pl/
│ │ │ └── strings.xml
│ │ ├── values-ps/
│ │ │ └── strings.xml
│ │ ├── values-pt/
│ │ │ └── strings.xml
│ │ ├── values-ro/
│ │ │ └── strings.xml
│ │ ├── values-ru/
│ │ │ └── strings.xml
│ │ ├── values-rw/
│ │ │ └── strings.xml
│ │ ├── values-si/
│ │ │ └── strings.xml
│ │ ├── values-sl/
│ │ │ └── strings.xml
│ │ ├── values-so/
│ │ │ └── strings.xml
│ │ ├── values-sq/
│ │ │ └── strings.xml
│ │ ├── values-sr/
│ │ │ └── strings.xml
│ │ ├── values-sv-rSE/
│ │ │ └── strings.xml
│ │ ├── values-sw/
│ │ │ └── strings.xml
│ │ ├── values-sw-rKE/
│ │ │ └── strings.xml
│ │ ├── values-te/
│ │ │ └── strings.xml
│ │ ├── values-th-rTH/
│ │ │ └── strings.xml
│ │ ├── values-ti/
│ │ │ └── strings.xml
│ │ ├── values-tl/
│ │ │ └── strings.xml
│ │ ├── values-tl-rPH/
│ │ │ └── strings.xml
│ │ ├── values-tr/
│ │ │ └── strings.xml
│ │ ├── values-uk/
│ │ │ └── strings.xml
│ │ ├── values-ur/
│ │ │ └── strings.xml
│ │ ├── values-ur-rPK/
│ │ │ └── strings.xml
│ │ ├── values-vi/
│ │ │ └── strings.xml
│ │ ├── values-zh/
│ │ │ └── strings.xml
│ │ ├── values-zh-rTW/
│ │ │ └── strings.xml
│ │ └── values-zu/
│ │ └── strings.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── strings/
│ │ └── format/
│ │ ├── DateFormatsTest.kt
│ │ └── LengthFormatterTest.kt
│ └── resources/
│ └── robolectric.properties
├── test-forms/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ └── resources/
│ ├── forms/
│ │ ├── Empty First Repeat.xml
│ │ ├── RepeatGroupAndGroup.xml
│ │ ├── RepeatTitles_1648.xml
│ │ ├── TestRepeat.xml
│ │ ├── all-widgets.xml
│ │ ├── audio-question.xml
│ │ ├── basic.xml
│ │ ├── different-search-appearances.xml
│ │ ├── dynamic_and_static_choices.xml
│ │ ├── dynamic_required_question.xml
│ │ ├── emptyGroupFieldList.xml
│ │ ├── encrypted-no-instanceID.xml
│ │ ├── encrypted.xml
│ │ ├── entity-update-pulldata.xml
│ │ ├── external-audio-question.xml
│ │ ├── external-csv-search-broken.xml
│ │ ├── external-csv-search.xml
│ │ ├── external_csv_form.xml
│ │ ├── external_data_questions.xml
│ │ ├── external_select.xml
│ │ ├── external_select_10.xml
│ │ ├── external_select_csv.xml
│ │ ├── field-list-repeat.xml
│ │ ├── fieldlist-updates.xml
│ │ ├── fixed-count-repeat.xml
│ │ ├── form1.xml
│ │ ├── form2.xml
│ │ ├── form3.xml
│ │ ├── form4.xml
│ │ ├── form5.xml
│ │ ├── form6.xml
│ │ ├── form7.xml
│ │ ├── form8.xml
│ │ ├── form9.xml
│ │ ├── formHierarchy1.xml
│ │ ├── formHierarchy2.xml
│ │ ├── formHierarchy3.xml
│ │ ├── form_design_error.xml
│ │ ├── form_styling.xml
│ │ ├── form_with_images.xml
│ │ ├── g6Error.xml
│ │ ├── hints_textq.xml
│ │ ├── identify-user-audit-false.xml
│ │ ├── identify-user-audit.xml
│ │ ├── intent-group.xml
│ │ ├── internal-audio-question.xml
│ │ ├── invalid-form.xml
│ │ ├── likert_test.xml
│ │ ├── location-audit.xml
│ │ ├── manyQ.xml
│ │ ├── metadata.xml
│ │ ├── nested-intent-group.xml
│ │ ├── numberInCSV.xml
│ │ ├── one-question-audit.xml
│ │ ├── one-question-autoplay.xml
│ │ ├── one-question-autosend-disabled.xml
│ │ ├── one-question-autosend.xml
│ │ ├── one-question-background-audio-audit.xml
│ │ ├── one-question-background-audio-multiple.xml
│ │ ├── one-question-background-audio.xml
│ │ ├── one-question-editable.xml
│ │ ├── one-question-encrypted-unicode.xml
│ │ ├── one-question-entity-create-and-update.xml
│ │ ├── one-question-entity-follow-up.xml
│ │ ├── one-question-entity-registration-broken.xml
│ │ ├── one-question-entity-registration-editable.xml
│ │ ├── one-question-entity-registration-id.xml
│ │ ├── one-question-entity-registration-v2020.1.xml
│ │ ├── one-question-entity-registration-v2023.1.xml
│ │ ├── one-question-entity-registration.xml
│ │ ├── one-question-entity-update-and-create.xml
│ │ ├── one-question-entity-update-editable.xml
│ │ ├── one-question-entity-update.xml
│ │ ├── one-question-last-saved-updated.xml
│ │ ├── one-question-last-saved.xml
│ │ ├── one-question-partial.xml
│ │ ├── one-question-repeat.xml
│ │ ├── one-question-translation.xml
│ │ ├── one-question-updated.xml
│ │ ├── one-question-uuid-instance-name.xml
│ │ ├── one-question-with-constraint.xml
│ │ ├── one-question.xml
│ │ ├── pull_data.xml
│ │ ├── random.xml
│ │ ├── randomTest_broken.xml
│ │ ├── ranking_widget.xml
│ │ ├── repeat_group_form.xml
│ │ ├── repeat_group_new.xml
│ │ ├── repeat_group_wrapped_with_a_regular_group.xml
│ │ ├── repeat_groups.xml
│ │ ├── repeat_in_field_list.xml
│ │ ├── repeat_without_label.xml
│ │ ├── requiredQuestionInFieldList.xml
│ │ ├── required_question_with_audio.xml
│ │ ├── required_question_with_custom_error_message.xml
│ │ ├── search_and_select.xml
│ │ ├── selectOneExternal.xml
│ │ ├── select_one_external.xml
│ │ ├── setgeopoint-action.xml
│ │ ├── simple-search-external-csv.xml
│ │ ├── single-geopoint.xml
│ │ ├── start-geopoint.xml
│ │ ├── string_widgets_in_field_list.xml
│ │ ├── track-changes-reason-on-edit.xml
│ │ ├── two-question-audit-track-changes.xml
│ │ ├── two-question-audit.xml
│ │ ├── two-question-external.xml
│ │ ├── two-question-required.xml
│ │ ├── two-question-save-incomplete-required.xml
│ │ ├── two-question-save-incomplete.xml
│ │ ├── two-question-updated.xml
│ │ ├── two-question.xml
│ │ ├── two-questions-in-group.xml
│ │ └── validate.xml
│ └── media/
│ ├── external-csv-search-produce.csv
│ ├── external_csv_cities.csv
│ ├── external_csv_countries.csv
│ ├── external_csv_neighbourhoods.csv
│ ├── external_data.csv
│ ├── external_data.xml
│ ├── external_data_10.xml
│ ├── external_data_broken.csv
│ ├── fruits.csv
│ ├── itemSets.csv
│ ├── people.csv
│ ├── selectOneExternal-media/
│ │ └── itemsets.csv
│ ├── simple-search-external-csv-fruits.csv
│ ├── test.m4a
│ └── updated-people.csv
├── test-shared/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── testshared/
│ ├── ActivityControllerRule.kt
│ ├── ActivityExt.kt
│ ├── AssertIntentsHelper.kt
│ ├── AssertionFramework.kt
│ ├── ComposeAssertions.kt
│ ├── ComposeInteractions.kt
│ ├── DummyActivity.kt
│ ├── ErrorIntentLauncher.kt
│ ├── EspressoAssertions.kt
│ ├── EspressoInteractions.kt
│ ├── FakeAudioPlayer.kt
│ ├── FakeBarcodeScannerView.kt
│ ├── FakeBroadcastReceiverRegister.kt
│ ├── FakeScheduler.kt
│ ├── FragmentResultRecorder.kt
│ ├── LocationTestUtils.kt
│ ├── MockFragmentFactory.kt
│ ├── MockWebPageService.kt
│ ├── RecyclerViewMatcher.kt
│ ├── RobolectricHelpers.kt
│ ├── SliderExt.kt
│ ├── TimeZoneSetter.kt
│ ├── ViewActions.kt
│ ├── ViewMatchers.kt
│ └── WaitFor.kt
├── timedgrid/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── timedgrid/
│ │ ├── AssessmentType.kt
│ │ ├── CommonTimedGridRenderer.kt
│ │ ├── FinishType.kt
│ │ ├── NavigationAwareWidget.kt
│ │ ├── PausableCountDownTimer.kt
│ │ ├── TimedGridRenderer.kt
│ │ ├── TimedGridState.kt
│ │ ├── TimedGridSummary.kt
│ │ ├── TimedGridSummaryAnswerCreator.kt
│ │ ├── TimedGridViewModel.kt
│ │ ├── TimedGridWidgetConfiguration.kt
│ │ ├── TimedGridWidgetDelegate.kt
│ │ └── TimedGridWidgetLayout.kt
│ └── res/
│ ├── color/
│ │ └── timed_grid_button_tint_selector.xml
│ ├── drawable/
│ │ └── row_number_background.xml
│ ├── layout/
│ │ ├── timed_grid.xml
│ │ ├── timed_grid_item_button.xml
│ │ └── timed_grid_item_row.xml
│ └── values/
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── upgrade/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── upgrade/
│ │ ├── AppUpgrader.kt
│ │ ├── LaunchState.kt
│ │ └── Upgrade.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── upgrade/
│ ├── AppUpgraderTest.kt
│ └── VersionCodeLaunchStateTest.kt
└── web-page/
├── .gitignore
├── build.gradle.kts
└── src/
├── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── webpage/
│ ├── CustomTabsWebPageService.kt
│ └── WebPageService.kt
└── test/
└── java/
└── org/
└── odk/
└── collect/
└── webpage/
└── CustomTabsWebPageServiceTest.java
================================================
FILE CONTENTS
================================================
================================================
FILE: .circleci/config.yml
================================================
# This config and the the Gradle flags/opts are based on: https://circleci.com/docs/2.0/language-android/
# and https://support.circleci.com/hc/en-us/articles/360021812453
version: 2
references:
android_config_small: &android_config_small
working_directory: ~/work
docker:
- image: cimg/android:2026.02
resource_class: small
android_config: &android_config
working_directory: ~/work
docker:
- image: cimg/android:2026.02
resource_class: large
android_config_large: &android_config_large
working_directory: ~/work
docker:
- image: cimg/android:2026.02
resource_class: xlarge
gcloud_config: &gcloud_config
working_directory: ~/work
docker:
- image: cimg/gcp:2025.01
resource_class: small
jobs:
compile:
<<: *android_config
steps:
- checkout
- restore_cache:
keys:
- compile-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- compile-
- run:
name: Copy gradle config
command: mkdir -p ~/.gradle && cp .circleci/gradle.properties ~/.gradle/gradle.properties
- run:
name: Download Robolectric deps
command: ./download-robolectric-deps.sh
- run:
name: Compile code
command: ./gradlew assembleDebug
- save_cache:
paths:
- ~/.gradle/caches
- ~/.gradle/wrapper
key: compile-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- save_cache:
paths:
- ~/work
key: workflow-{{ .Environment.CIRCLE_WORKFLOW_ID }}
- persist_to_workspace:
root: ~/work
paths:
- collect_app/build/outputs/apk
create_dependency_backup:
<<: *android_config_small
steps:
- checkout
- restore_cache:
keys:
- workflow-{{ .Environment.CIRCLE_WORKFLOW_ID }}
- restore_cache:
keys:
- compile-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- compile-
- run:
name: Create Maven repo from dependencies
command: ./gradlew cacheToMavenLocal
- run:
name: Compress Maven repo
command: tar -cvzf maven.tar .local-m2
- store_artifacts:
path: maven.tar
check_quality:
<<: *android_config
steps:
- checkout
- restore_cache:
keys:
- workflow-{{ .Environment.CIRCLE_WORKFLOW_ID }}
- restore_cache:
keys:
- compile-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- compile-
- run:
name: Copy gradle config
command: mkdir -p ~/.gradle && cp .circleci/gradle.properties ~/.gradle/gradle.properties
- run:
name: Run code quality checks
command: ./gradlew pmd ktlintCheck checkstyle
- run:
name: Run Android lint
command: ./gradlew lintDebug
test_modules:
<<: *android_config
parallelism: 4
steps:
- checkout
- restore_cache:
keys:
- workflow-{{ .Environment.CIRCLE_WORKFLOW_ID }}
- restore_cache:
keys:
- test_modules-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- test_modules
- compile-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- compile-
- run:
name: Copy gradle config
command: mkdir -p ~/.gradle && cp .circleci/gradle.properties ~/.gradle/gradle.properties
- run:
name: Generate list of modules for this fork
command: |
cat .circleci/test_modules.txt | circleci tests split > .circleci/fork_test_modules.txt && \
echo "Modules for this fork:" && \
cat .circleci/fork_test_modules.txt
- run:
name: Run module unit tests
command: |
./gradlew $(cat .circleci/fork_test_modules.txt | awk '{for (i=1; i<=NF; i++) printf "%s:testDebug ",$i}')
- store_test_results:
path: collect_app/build/test-results
- save_cache:
paths:
- ~/.gradle/caches
- ~/.gradle/wrapper
key: test_modules-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
test_app:
<<: *android_config
parallelism: 4
steps:
- checkout
- restore_cache:
keys:
- workflow-{{ .Environment.CIRCLE_WORKFLOW_ID }}
- restore_cache:
keys:
- test_app-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- test_app-
- compile-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- compile-
- run:
name: Copy gradle config
command: mkdir -p ~/.gradle && cp .circleci/gradle.properties ~/.gradle/gradle.properties
- run:
name: Generate list of test classes
command: .circleci/generate-app-test-list.sh
- run:
name: Generate list of tests for this fork
command: |
cat .circleci/collect_app_test_classes.txt | circleci tests split > .circleci/fork_test_classes.txt && \
echo "Tests for this fork:" && \
cat .circleci/fork_test_classes.txt && \
echo "" && \
echo "Will run command:" && \
echo "./gradlew collect_app:testDebug $(cat .circleci/fork_test_classes.txt | awk '{for (i=1; i<=NF; i++) printf "--tests %s ",$i}')"
- run:
name: Run app unit tests
command: |
./gradlew collect_app:testDebug $(cat .circleci/fork_test_classes.txt | awk '{for (i=1; i<=NF; i++) printf "--tests %s ",$i}')
- store_artifacts:
path: collect_app/build/reports
destination: reports
- store_test_results:
path: collect_app/build/test-results
- save_cache:
paths:
- ~/.gradle/caches
- ~/.gradle/wrapper
key: test_app-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
build_instrumented:
<<: *android_config_large
steps:
- checkout
- restore_cache:
keys:
- workflow-{{ .Environment.CIRCLE_WORKFLOW_ID }}
- restore_cache:
keys:
- build_instrumented-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- build_instrumented-
- compile-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- compile-
- run:
name: Copy gradle config
command: mkdir -p ~/.gradle && cp .circleci/gradle-large.properties ~/.gradle/gradle.properties
- run:
name: Assemble connected test build
command: ./gradlew assembleDebugAndroidTest
- save_cache:
paths:
- ~/.gradle/caches
- ~/.gradle/wrapper
key: build_instrumented-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- persist_to_workspace:
root: ~/work
paths:
- collect_app/build/outputs/apk
build_release:
<<: *android_config
steps:
- checkout
- restore_cache:
keys:
- workflow-{{ .Environment.CIRCLE_WORKFLOW_ID }}
- restore_cache:
keys:
- compile-{{ checksum "gradle/libs.versions.toml" }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
- compile-
- run:
name: Copy gradle config
command: mkdir -p ~/.gradle && cp .circleci/gradle.properties ~/.gradle/gradle.properties
- run:
name: Set up secrets.properties for Maps frameworks
command: |
if [[ -n "$GOOGLE_MAPS_API_KEY" ]]; then \
echo "GOOGLE_MAPS_API_KEY=$GOOGLE_MAPS_API_KEY" >> secrets.properties
echo "MAPBOX_ACCESS_TOKEN=$MAPBOX_ACCESS_TOKEN" >> secrets.properties
echo "MAPBOX_DOWNLOADS_TOKEN=$MAPBOX_DOWNLOADS_TOKEN" >> secrets.properties
fi
- run:
name: Assemble self signed release build
command: ./gradlew assembleSelfSignedRelease
- run:
name: Check APK size hasn't increased
command: |
if [[ -n "$GOOGLE_MAPS_API_KEY" ]]; then \
./check-size.sh 20447232
else
./check-size.sh 11010048
fi
- run:
name: Copy APK to predictable path for artifact storage
command: cp collect_app/build/outputs/apk/selfSignedRelease/*.apk selfSignedRelease.apk
- store_artifacts:
path: selfSignedRelease.apk
test_smoke_instrumented:
<<: *gcloud_config
steps:
- attach_workspace:
at: /tmp/workspace
- run:
name: Authorize gcloud
command: |
if [[ "$CIRCLE_PROJECT_USERNAME" == "getodk" ]]; then \
gcloud config set project api-project-322300403941
echo $GCLOUD_SERVICE_KEY | base64 --decode > client-secret.json
gcloud auth activate-service-account --key-file client-secret.json
fi
- run:
name: Run integration tests
command: |
if [[ "$CIRCLE_PROJECT_USERNAME" == "getodk" ]]; then \
echo "y" | gcloud beta firebase test android run \
--type instrumentation \
--app /tmp/workspace/collect_app/build/outputs/apk/debug/ODK-Collect-debug.apk \
--test /tmp/workspace/collect_app/build/outputs/apk/androidTest/debug/ODK-Collect-debug-androidTest.apk \
--device model=MediumPhone.arm,version=34,locale=en,orientation=portrait \
--results-bucket opendatakit-collect-test-results \
--directories-to-pull /sdcard --timeout 20m \
--test-targets "package org.odk.collect.android.feature.smoke"
fi
no_output_timeout: 25m
test_instrumented:
<<: *gcloud_config
steps:
- attach_workspace:
at: /tmp/workspace
- run:
name: Authorize gcloud
command: |
if [[ -n "$GCLOUD_SERVICE_KEY" ]]; then \
gcloud config set project api-project-322300403941
echo $GCLOUD_SERVICE_KEY | base64 --decode > client-secret.json
gcloud auth activate-service-account --key-file client-secret.json
fi
- run:
name: Run integration tests
command: |
echo "y" | gcloud beta firebase test android run \
--type instrumentation \
--num-uniform-shards=25 \
--app /tmp/workspace/collect_app/build/outputs/apk/debug/ODK-Collect-debug.apk \
--test /tmp/workspace/collect_app/build/outputs/apk/androidTest/debug/ODK-Collect-debug-androidTest.apk \
--device model=MediumPhone.arm,version=34,locale=en,orientation=portrait \
--results-bucket opendatakit-collect-test-results \
--directories-to-pull /sdcard --timeout 20m \
--test-targets "notPackage org.odk.collect.android.regression" \
--test-targets "notPackage org.odk.collect.android.benchmark"
no_output_timeout: 25m
workflows:
version: 2
pr:
jobs:
- hold_pr:
type: approval
filters:
branches:
ignore:
- master
- ^v((20)[0-9]{2})\.\d+\.x$
- compile:
requires:
- hold_pr
- check_quality:
requires:
- compile
- test_modules:
requires:
- compile
- test_app:
requires:
- compile
- build_release:
requires:
- compile
- build_instrumented:
requires:
- compile
master:
jobs:
- compile:
filters:
branches:
only:
- master
- ^v((20)[0-9]{2})\.\d+\.x$
- check_quality:
requires:
- compile
- test_modules:
requires:
- compile
- test_app:
requires:
- compile
- build_release:
requires:
- compile
- build_instrumented:
requires:
- compile
- test_smoke_instrumented:
requires:
- build_instrumented
nightly:
triggers:
- schedule:
cron: "0 0 * * 1-5"
filters:
branches:
only:
- master
jobs:
- compile
- build_instrumented:
requires:
- compile
- test_instrumented:
requires:
- build_instrumented
release:
jobs:
- compile:
filters:
tags:
only: /^v((20)[0-9]{2})\.\d+\.\d+$/ # matches semvers like v1.2.3
branches:
ignore: /.*/
- create_dependency_backup:
requires:
- compile
filters:
tags:
only: /^v((20)[0-9]{2})\.\d+\.\d+$/ # matches semvers like v1.2.3
branches:
ignore: /.*/
================================================
FILE: .circleci/generate-app-test-list.sh
================================================
ls -R collect_app/src/test/java/ | grep Test.java > .circleci/collect_app_test_files.txt
ls -R collect_app/src/test/java/ | grep Test.kt >> .circleci/collect_app_test_files.txt
cat .circleci/collect_app_test_files.txt | sed "s/\.java//" | sed "s/\.kt//" > .circleci/collect_app_test_classes.txt
================================================
FILE: .circleci/gradle-large.properties
================================================
# Gradle config for "X-Large" Circle CI resource (https://circleci.com/pricing/price-list/)
org.gradle.jvmargs=-Xmx8g -XX:MaxMetaspaceSize=512m -Dkotlin.daemon.jvm.options=-Xmx2g
org.gradle.daemon=false
org.gradle.parallel=true
org.gradle.workers.max=8
test.heap.max=1g
================================================
FILE: .circleci/gradle.properties
================================================
# Gradle config for "Large" Circle CI resource (https://circleci.com/pricing/price-list/)
org.gradle.jvmargs=-Xmx2560m -XX:MaxMetaspaceSize=1g
org.gradle.daemon=false
org.gradle.parallel=true
org.gradle.workers.max=4
test.heap.max=1g
================================================
FILE: .circleci/test_modules.txt
================================================
shared
forms-test
androidshared
async
strings
audio-clips
audio-recorder
projects
location
geo
upgrade
permissions
settings
maps
errors
crash-handler
entities
qr-code
shadows
metadata
selfie-camera
draw
printer
lists
web-page
open-rosa
mobile-device-management
material
================================================
FILE: .editorconfig
================================================
root = true
[*.{kt,kts}]
ktlint_standard_no-blank-lines-in-chained-method-calls = disabled
ktlint_standard_trailing-comma-on-call-site = disabled
ktlint_standard_trailing-comma-on-declaration-site = disabled
ktlint_standard_function-signature = disabled
ktlint_standard_no-empty-first-line-in-class-body = disabled
ktlint_standard_argument-list-wrapping = disabled
ktlint_standard_parameter-list-wrapping = disabled
ktlint_standard_multiline-expression-wrapping = disabled
ktlint_standard_max-line-length = disabled
ktlint_standard_string-template-indent = disabled
ktlint_standard_annotation = disabled
ktlint_standard_value-parameter-comment = disabled
ktlint_standard_property-naming = disabled
ktlint_standard_value-argument-comment = disabled
ktlint_standard_blank-line-before-declaration = disabled
ktlint_standard_no-consecutive-comments = disabled
ktlint_standard_enum-wrapping = disabled
ktlint_standard_statement-wrapping = disabled
ktlint_standard_try-catch-finally-spacing = disabled
ktlint_standard_wrapping = disabled
ktlint_standard_chain-method-continuation = disabled
ktlint_standard_function-expression-body = disabled
ktlint_standard_class-signature = disabled
ktlint_standard_binary-expression-wrapping = disabled
ktlint_standard_condition-wrapping = disabled
ktlint_standard_function-literal = disabled
ktlint_standard_backing-property-naming = disabled
ktlint_function_naming_ignore_when_annotated_with = Composable
ktlint_standard_no-unused-imports=enabled
================================================
FILE: .gitattributes
================================================
* text=lf
================================================
FILE: .github/CODE_OF_CONDUCT.md
================================================
Please refer to the project-wide [ODK Code of Conduct](https://github.com/getodk/governance/blob/master/CODE-OF-CONDUCT.md).
================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
- name: "Report an issue"
about: "For when Collect is behaving in an unexpected way"
url: "https://forum.getodk.org/c/support/6"
- name: "Request a feature"
about: "For when Collect is missing functionality"
url: "https://forum.getodk.org/c/features/9"
- name: "Everything else"
about: "For everything else"
url: "https://forum.getodk.org/c/support/6"
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
<!--
Thank you for taking the time to report an ODK Collect issue!
This space is for bugs that have clear reproduction steps and expected behavior. For unexpected behavior that is unclear how to address, general usage questions, form design questions, and to ask about the source code, please visit the ODK forum: https://forum.getodk.org
Before filling the template below, visit https://github.com/getodk/collect/issues?q=is%3Aissue and search to see whether your issue was already reported or fixed. If you find a match, comment on it or add a +1 rather than posting a new issue. If you find a problem you know how to fix, submit a pull request. 🎉
Feature suggestions should be described [in the forum Features category](https://forum.getodk.org/c/features) and discussed by the broader user community. Once there is a clear way forward, issues should be filed on the relevant repositories.
-->
#### ODK Collect version
#### Android version
#### Device used
#### Problem description
#### Steps to reproduce the problem
#### Expected behavior
#### Other information
Things you tried, stack traces, related issues, suggestions on how to fix it...
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
Closes #
<!--
Thank you for contributing to ODK Collect!
Before sending this PR, please read
https://github.com/getodk/collect/blob/master/docs/CONTRIBUTING.md
-->
#### Why is this the best possible solution? Were any other approaches considered?
#### How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?
#### Do we need any specific form for testing your changes? If so, please attach one.
#### Does this change require updates to documentation? If so, please file an issue [here]( https://github.com/getodk/docs/issues/new) and include the link below.
#### Before submitting this PR, please make sure you have:
- [ ] added or modified tests for any new or changed behavior
- [ ] run `./gradlew connectedAndroidTest` (or `./gradlew testLab`) and confirmed all checks still pass
- [ ] added a comment above any new strings describing it for translators
- [ ] added any new strings with date formatting to `DateFormatsTest`
- [ ] verified that any code or assets from external sources are properly credited in comments and/or in the [about file](https://github.com/getodk/collect/blob/master/collect_app/src/main/assets/open_source_licenses.html).
- [ ] verified that any new UI elements use theme colors. [UI Components Style guidelines](https://github.com/getodk/collect/blob/master/docs/CODE-GUIDELINES.md#ui-components-style-guidelines)
================================================
FILE: .github/TESTING_RESULT_TEMPLATES.md
================================================
# Testing result templates
## Tested with success!
#### Verified on: [List of devices/os versions]
#### Verified cases: [optional]
***
## Bug[s] has[have] been found! / Regression[s] has[have] been found!
#### Verified on: [List of devices/os versions]
#### Problem visible on: [only if it's related to some specific devices otherwise it’s the same as above]
#### Steps to reproduce:
#### Current behavior:
#### Expected behavior:
#### Other information: [optional]
================================================
FILE: .gitignore
================================================
build
.gradle
.idea
.kotlin
local.properties
*.iml
.DS_Store
*.sublime-project
# emacs backup files
*.*~
# gradle env props
secrets.properties
# built application files
*.apk
*.ap_
# files for the Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Android Studio files
.externalNativeBuild/
.navigation/
captures/
# Visual Studio Code files
.project
.settings/
collect_app/.classpath
collect_app/.project
collect_app/.settings/
# Config for the official ODK Collect build
collect_app/src/odkCollectRelease/
# Files generated during CI runs
.circleci/collect_app_test_files.txt
.circleci/collect_app_test_classes.txt
.circleci/fork_test_classes.txt
# Robolectric dependencies
robolectric-deps/
**/robolectric-deps.properties
.local-m2
apks
================================================
FILE: .hgtags
================================================
5836a17a0e6f28b4e5b7a7abc9152446f4806ee2 v1.0.0
30d9314729db181177bb7209d1c16cd65a5095eb v1.1.0
827cf67bd902b80c37dce8d4ee8d2d29437408a5 v1.1.1
82798e410f5ab043721b3998e23861fd365a9801 v1.1.2
78fc6b2df0575ec79b94c0196babacbafcb7f958 v1.1.3
c6308d8be8cd99d060a06f3b10c87f5722776098 v1.1.4
f85cab690aa86dc86c2d53f12def22f16d9c0c5b v1.1.7-beta1
165ee1feda811cccefda77ae83896b7cb3209b6c v1.1.7-rc1
0c06023f4bae172a79a5aad27ca8d66798f2109e v1.1.7-rc2
caeb88584771e1e569c87eed7cca90738c867189 v1.1.7
96568099c2ec1408508178568fc5ca2503ec670b v1.2 RC1
96568099c2ec1408508178568fc5ca2503ec670b v1.2 RC1
15185704d55c220b02abcf5de6170a106f387f5b v1.2 RC1
88476e4dc9845a0a276bb57f93911d8aacc68912 v1.2.0
88476e4dc9845a0a276bb57f93911d8aacc68912 v1.2.0
ccb4a2556950af32ac153c26ca1f71faebbf8a9b v1.2.0
ccb4a2556950af32ac153c26ca1f71faebbf8a9b v1.2.0
2a9b44755f0a5af2558b7e22ac9948377b6e9597 v1.2.0
2a9b44755f0a5af2558b7e22ac9948377b6e9597 v1.2.0
a5faa6055d4bdbc5db786673c28d9872142120c7 v1.2.0
a5faa6055d4bdbc5db786673c28d9872142120c7 v1.2.0
0000000000000000000000000000000000000000 v1.2.0
b0ac9ab2b5bccea1b160e774b28a1f0c8af9a9e8 v1.2.0 rev 1012
3e7860308f3d7520ff91b8fb27bc5123450cfaa4 v1.2.0 rev 1013
3e7860308f3d7520ff91b8fb27bc5123450cfaa4 v1.2.0 rev 1013
b7d1fccb1a29ae62185bef1a123e80dbcdbc171c v1.2.0 rev 1013
29d29a54a208785e93d8ed2024e2222115ede489 v1.2.0 rev 1014
29d29a54a208785e93d8ed2024e2222115ede489 v1.2.0 rev 1014
0000000000000000000000000000000000000000 v1.2.0 rev 1014
abbf6ad98f25f0f33216ed11125a3a9cee8f90bc v1.2.1 rev 1014
6b017ddf90f21ab24a9b0f219657d48b1bdfc204 v1.2.1 rev 1015
fbc9d90492a631209d8587d299d08b0d005c681a v1.2.1 rev 1016
fd9ea977734c1bdbb37f40217becb4ec50d356d2 v1.2.1 rev 1017
b25021b3a495e039ef4a2a932a95df705648dc3c v1.2.0 rev 1010 prerelease
797ac8701d2c93b13c5cc585d39ff0a1a74d70d2 v1.2.0 rev 1011 prerelease
6e3dcf4780ac5e8a62b8d92f4111638cbc9a53e9 v1.2.0 rev 1009 prerelease
dd3c2816eb879b2b922d2e4c52bc95c8996a8f25 v1.2.0 rev 1008 RC2
06063863a95919cd18dc36841febbf21ca0fa60e v1.2.0 rev 1008 RC1
7f9a9e162ff5de00b4384de5a6bd7b0132ad1e9c v1.2.0 rev 1007 beta
a5a35a6eaf724a9c080a8ab85a2190f8fa6470fa v1.2.0 rev 1006 beta
eca30a5719a2d603c364d470bedfbd7c90c89e35 v1.2.0 rev 1005 beta
820c7e389d341953d0158290f880c9226d144870 v1.2.0 rev 1004 alpha
5630fb8134d1e8169517b5586b05cc4183370e56 v1.2.0 rev 1003 alpha
f628ef1bc45293e3d5fea77ac881a7b7652af554 v1.2.0 rev 1001 alpha
8c5544b1b0f4d469f35832c730d02a3bfbdb21ea v1.2.0 rev 1000 alpha
7f9a9e162ff5de00b4384de5a6bd7b0132ad1e9c v1.2.0 rev 1007 RC1
7f9a9e162ff5de00b4384de5a6bd7b0132ad1e9c v1.2.0 rev 1007 beta
0000000000000000000000000000000000000000 v1.2.0 rev 1007 beta
a5a35a6eaf724a9c080a8ab85a2190f8fa6470fa v1.2.0 rev 1006 RC1
a5a35a6eaf724a9c080a8ab85a2190f8fa6470fa v1.2.0 rev 1006 beta
0000000000000000000000000000000000000000 v1.2.0 rev 1006 beta
eca30a5719a2d603c364d470bedfbd7c90c89e35 v1.2.0 rev 1005 RC1
eca30a5719a2d603c364d470bedfbd7c90c89e35 v1.2.0 rev 1005 beta
0000000000000000000000000000000000000000 v1.2.0 rev 1005 beta
820c7e389d341953d0158290f880c9226d144870 v1.2.0 rev 1004 RC1
820c7e389d341953d0158290f880c9226d144870 v1.2.0 rev 1004 alpha
0000000000000000000000000000000000000000 v1.2.0 rev 1004 alpha
5630fb8134d1e8169517b5586b05cc4183370e56 v1.2.0 rev 1003 RC1
5630fb8134d1e8169517b5586b05cc4183370e56 v1.2.0 rev 1003 alpha
0000000000000000000000000000000000000000 v1.2.0 rev 1003 alpha
f628ef1bc45293e3d5fea77ac881a7b7652af554 v1.2.0 rev 1001 RC1
f628ef1bc45293e3d5fea77ac881a7b7652af554 v1.2.0 rev 1001 alpha
0000000000000000000000000000000000000000 v1.2.0 rev 1001 alpha
8c5544b1b0f4d469f35832c730d02a3bfbdb21ea v1.2.0 rev 1000 RC1
8c5544b1b0f4d469f35832c730d02a3bfbdb21ea v1.2.0 rev 1000 alpha
0000000000000000000000000000000000000000 v1.2.0 rev 1000 alpha
7be1f7e0a0dc36b67f8a584c7f420c240eefd5c6 v1.2.1 rev 1018 (plus swahili)
c11fe1aebaca2e213f71e61ad2337791d02723e0 v1.2.1 rev 1019
64e490522c2ef5ebc9af8a1a2c0dbf78bb213c06 v1.2.1 rev 1020
ea230f4ee12c303640c30090c30a9ac386324d5a v1.2.2 rev 1021
ea230f4ee12c303640c30090c30a9ac386324d5a v1.2.2 rev 1021
3b8a3847d1bea472176a27548d602b119d0e695d v1.2.2 rev 1021
3b8a3847d1bea472176a27548d602b119d0e695d v1.2.2 rev 1021
f714913194222fb579c5ef6e59092c788eeea300 v1.2.2 rev 1021
1759bdc402730ed7a16d26e7aba44aa43f822355 v1.2.2 rev 1023
cef8219f74248bd4d295e5c105b9f0ced3ac7f8d v1.3 rev 1025
a3d74ab96cdee3e972f7e507776a3834f497fbe0 v1.3 rev 1027
5c2ce96c94c6daf5277befaabf54ab74f088437b v1.3 rev 1029
1c8b890c69fbccc47de596c8592d21c7439646ba v1.3 rev 1030
e1e6c70eef9913497c65de897e229a6e0d871ad8 v1.4 rev 1033
68616e24e60d963334c762daba5784c7af763d76 v1.4 rev 1034
aa63d3a1f3295cf5a4a6bc74d8f1a4d4939fdd8b v1.4 rev 1035
db03f3497da8c76883e475a14a2b034e61e29c37 v1.4 rev 1036
db03f3497da8c76883e475a14a2b034e61e29c37 v1.4 rev 1036
8f77433258b376de5a6a7267df03ecc7cdecc1a4 v1.4 rev 1036
86afc4615411eaeee61271790b2bae5de7b94176 v1.4 rev 1037
3254b74a70d9c4f4088ba710fd6d7f542ffe166f v1.4 rev 1038
bc69898dc4ccb061079ee82f9b52da34de8400b7 v1.4.3 rev 1040
cd78ad21a3c8b1f106cdbb35a55ddfd1c2cf021f v1.4.3 rev 1041
7bf86c85d2dac5d6353bf2007a5c817654bbfdfc v1.4.3 rev 1042
753b33ca1fa800b517fcb6156d55370b986cedeb v1.4.4 rev 1044
753b33ca1fa800b517fcb6156d55370b986cedeb v1.4.4 rev 1044
c0d79cc86e2da93dfb03165a520de2c42b5cf061 v1.4.4 rev 1044
72d2e66877f36e0d3b607a6dbc4207c96dcc4c0c v1.4.4 rev 1045
712f8174b16bd95ece6bac433f871a19d59ea46c v1.4.4 rev 1046
5a92ad65ed775aa744b0abfe1aaa3ef756bd82f3 v1.4.5 rev 1048
5a92ad65ed775aa744b0abfe1aaa3ef756bd82f3 v1.4.5 rev 1048
1a6028ae2ce23b2eb50bd7ee3b2674effd2620af v1.4.5 rev 1048
aacc424db2a79c201abd6dcb481af830d017477f v1.4.6 rev 1049 for testing
aacc424db2a79c201abd6dcb481af830d017477f v1.4.6 rev 1049 for testing
1fced08f31e9e4f10fcdf067debdbad3557899de v1.4.6 rev 1049 for testing
================================================
FILE: LICENSE.md
================================================
Apache License
==============
_Version 2.0, January 2004_
_<<http://www.apache.org/licenses/>>_
### Terms and Conditions for use, reproduction, and distribution
#### 1. Definitions
“License” shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
“Licensor” shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
“Legal Entity” shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, “control” means **(i)** the power, direct or
indirect, to cause the direction or management of such entity, whether by
contract or otherwise, or **(ii)** ownership of fifty percent (50%) or more of the
outstanding shares, or **(iii)** beneficial ownership of such entity.
“You” (or “Your”) shall mean an individual or Legal Entity exercising
permissions granted by this License.
“Source” form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
“Object” form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object code,
generated documentation, and conversions to other media types.
“Work” shall mean the work of authorship, whether in Source or Object form, made
available under the License, as indicated by a copyright notice that is included
in or attached to the work (an example is provided in the Appendix below).
“Derivative Works” shall mean any work, whether in Source or Object form, that
is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative Works
shall not include works that remain separable from, or merely link (or bind by
name) to the interfaces of, the Work and Derivative Works thereof.
“Contribution” shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative Works
thereof, that is intentionally submitted to Licensor for inclusion in the Work
by the copyright owner or by an individual or Legal Entity authorized to submit
on behalf of the copyright owner. For the purposes of this definition,
“submitted” means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor for
the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as “Not a Contribution.”
“Contributor” shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
#### 2. Grant of Copyright License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the Work and such
Derivative Works in Source or Object form.
#### 3. Grant of Patent License
Subject to the terms and conditions of this License, each Contributor hereby
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
such license applies only to those patent claims licensable by such Contributor
that are necessarily infringed by their Contribution(s) alone or by combination
of their Contribution(s) with the Work to which such Contribution(s) was
submitted. If You institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
Contribution incorporated within the Work constitutes direct or contributory
patent infringement, then any patent licenses granted to You under this License
for that Work shall terminate as of the date such litigation is filed.
#### 4. Redistribution
You may reproduce and distribute copies of the Work or Derivative Works thereof
in any medium, with or without modifications, and in Source or Object form,
provided that You meet the following conditions:
* **(a)** You must give any other recipients of the Work or Derivative Works a copy of
this License; and
* **(b)** You must cause any modified files to carry prominent notices stating that You
changed the files; and
* **(c)** You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source form
of the Work, excluding those notices that do not pertain to any part of the
Derivative Works; and
* **(d)** If the Work includes a “NOTICE” text file as part of its distribution, then any
Derivative Works that You distribute must include a readable copy of the
attribution notices contained within such NOTICE file, excluding those notices
that do not pertain to any part of the Derivative Works, in at least one of the
following places: within a NOTICE text file distributed as part of the
Derivative Works; within the Source form or documentation, if provided along
with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents of
the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works that
You distribute, alongside or as an addendum to the NOTICE text from the Work,
provided that such additional attribution notices cannot be construed as
modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a whole,
provided Your use, reproduction, and distribution of the Work otherwise complies
with the conditions stated in this License.
#### 5. Submission of Contributions
Unless You explicitly state otherwise, any Contribution intentionally submitted
for inclusion in the Work by You to the Licensor shall be under the terms and
conditions of this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify the terms of
any separate license agreement you may have executed with Licensor regarding
such Contributions.
#### 6. Trademarks
This License does not grant permission to use the trade names, trademarks,
service marks, or product names of the Licensor, except as required for
reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
#### 7. Disclaimer of Warranty
Unless required by applicable law or agreed to in writing, Licensor provides the
Work (and each Contributor provides its Contributions) on an “AS IS” BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
including, without limitation, any warranties or conditions of TITLE,
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
solely responsible for determining the appropriateness of using or
redistributing the Work and assume any risks associated with Your exercise of
permissions under this License.
#### 8. Limitation of Liability
In no event and under no legal theory, whether in tort (including negligence),
contract, or otherwise, unless required by applicable law (such as deliberate
and grossly negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special, incidental,
or consequential damages of any character arising as a result of this License or
out of the use or inability to use the Work (including but not limited to
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
any and all other commercial damages or losses), even if such Contributor has
been advised of the possibility of such damages.
#### 9. Accepting Warranty or Additional Liability
While redistributing the Work or Derivative Works thereof, You may choose to
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
other liability obligations and/or rights consistent with this License. However,
in accepting such obligations, You may act only on Your own behalf and on Your
sole responsibility, not on behalf of any other Contributor, and only if You
agree to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
_END OF TERMS AND CONDITIONS_
### APPENDIX: How to apply the Apache License to your work
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets `[]` replaced with your own
identifying information. (Don't include the brackets!) The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same “printed page” as the copyright notice for easier identification within
third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
================================================
FILE: README.md
================================================
# ODK Collect

[](https://opensource.org/licenses/Apache-2.0)
[](https://circleci.com/gh/getodk/collect)
[](https://slack.getodk.org)
ODK Collect is an Android app for filling out forms. It is designed to be used in resource-constrained environments with challenges such as unreliable connectivity or power infrastructure. ODK Collect is part the ODK project, a free and open-source set of tools which help organizations author, field, and manage mobile data collection solutions. Learn more about ODK and its history [here](https://getodk.org/) and read about example ODK deployments [here](https://forum.getodk.org/c/showcase).
ODK Collect renders forms that are compliant with the [ODK XForms standard](https://getodk.github.io/xforms-spec/), a subset of the [XForms 1.1 standard](https://www.w3.org/TR/xforms/) with some extensions. The form parsing is done by the [JavaRosa library](https://github.com/getodk/javarosa) which Collect includes as a dependency.
Please note that the `master` branch reflects ongoing development and is not production-ready.
## Table of Contents
* [Learn more about ODK Collect](#learn-more-about-odk-collect)
* [Release cycle](#release-cycle)
* [Downloading builds](#downloading-builds)
* [Suggesting new features](#suggesting-new-features)
* Contributing
* [Contributing code](#contributing-code)
* [Contributing translations](#contributing-translations)
* Developing
* [Setting up your development environment](#setting-up-your-development-environment)
* [Testing a form without a server](#testing-a-form-without-a-server)
* [Using APIs for local development](#using-apis-for-local-development)
* [Debugging JavaRosa](#debugging-javarosa)
* [Troubleshooting](#troubleshooting)
* [Test devices](#test-devices)
* [Creating signed releases for Google Play Store](#creating-signed-releases-for-google-play-store)
## Learn more about ODK Collect
* ODK website: [https://getodk.org](https://getodk.org)
* ODK Collect usage documentation: [https://docs.getodk.org/collect-intro/](https://docs.getodk.org/collect-intro/)
* ODK forum: [https://forum.getodk.org](https://forum.getodk.org)
* ODK developer Slack chat: [https://slack.getodk.org](https://slack.getodk.org)
## Release cycle
The work to be done is continuously revised and prioritized in [the backlog](https://github.com/orgs/getodk/projects/9/views/8) by the Collect team. The majority of this is influenced by the priorities in [the ODK roadmap](https://getodk.org/roadmap). Releases are planned to happen every 2-3 months (resulting in ~4 releases a year). This goal is to balance the pace of delivery with keeping things stable for users while also minimizing the risk in each release.
Sometimes issues will be assigned to core team members before they are actually started (moved to "in progress") to make it clear who's going to be working on what.
Once the majority of high risk or visible work is done for a release, a new beta will then be released to the Play Store by [@lognaturel](https://github.com/lognaturel) and that will be used for regression testing by [@getodk/testers](https://github.com/orgs/getodk/teams/testers). If any problems are found, the release is blocked until we can merge fixes. Regression testing should continue on the original beta build (rather than a new one with fixes) unless problems block the rest of testing. Once the process is complete, [@lognaturel](https://github.com/lognaturel) pushes the releases to the Play Store following [these instructions](#creating-signed-releases-for-google-play-store).
Fixes to a previous release should be merged to a "release" branch (`v2023.2.x` for example) so as to leave `master` available for the current release's work. If hotfix changes are needed in the current release as well then these can be merged in as a PR after hotfix releases (generally easiest as a single PR for the whole hotfix release). This approach can also be used if work for the next release starts before the current one is out - the next release continues on `master` while the release is on a release branch.
At the beginning of each release cycle, [@grzesiek2010](https://github.com/grzesiek2010) updates all dependencies that have compatible upgrades available and ensures that the build targets the latest SDK.
## Downloading builds
Per-commit debug builds can be found on [CircleCI](https://circleci.com/gh/getodk/collect). Login with your GitHub account, click the build you'd like, then find the APK in the Artifacts tab.
If you are looking to use ODK Collect, we strongly recommend using the [Play Store build](https://play.google.com/store/apps/details?id=org.odk.collect.android). Current and previous production builds can be found in [Releases](https://github.com/getodk/collect/releases).
## Suggesting new features
We try to make sure that all issues in the issue tracker are as close to fully specified as possible so that they can be closed by a pull request. Feature suggestions should be described [in the forum Features category](https://forum.getodk.org/c/features) and discussed by the broader user community. Once there is a clear way forward, issues should be filed on the relevant repositories. More controversial features will be discussed as part of the Technical Steering Committee's [roadmapping process](https://github.com/getodk/governance/blob/master/TSC-1/STANDARD-OPERATING-PROCEDURES.md#roadmap).
## Contributing code
Any and all contributions to the project are welcome. ODK Collect is used across the world primarily by organizations with a social purpose so you can have real impact!
Issues tagged as [good first issue](https://github.com/getodk/collect/labels/good%20first%20issue) should be a good place to start. There are also currently many issues tagged as [needs reproduction](https://github.com/getodk/collect/labels/needs%20reproduction) which need someone to try to reproduce them with the current version of ODK Collect and comment on the issue with their findings.
If you're ready to contribute code, see [the contribution guide](docs/CONTRIBUTING.md).
## Contributing translations
If you know a language other than English, consider contributing translations through [Transifex](https://explore.transifex.com/getodk/collect/).
Translations are updated right before the first beta for a release and before the release itself. To update translations, download the zip from https://explore.transifex.com/getodk/collect/. The contents of each folder then need to be moved to the Android project folders. A quick script like [the one in this gist](https://gist.github.com/lognaturel/9974fab4e7579fac034511cd4944176b) can help. We currently copy everything from Transifex to minimize manual intervention. Sometimes translation files will only get comment changes. When new languages are updated in Transifex, they need to be added to the script above. Additionally, `ApplicationConstants.TRANSLATIONS_AVAILABLE` needs to be updated. This array provides the choices for the language preference in settings. Ideally the list could be dynamically generated.
## Setting up your development environment
1. Download and install [Git](https://git-scm.com/downloads) and add it to your PATH
1. Download and install [Android Studio](https://developer.android.com/studio/index.html)
1. Fork the collect project ([why and how to fork](https://help.github.com/articles/fork-a-repo/))
1. Clone your fork of the project locally. At the command line:
git clone https://github.com/YOUR-GITHUB-USERNAME/collect
If you prefer not to use the command line, you can use Android Studio to create a new project from version control using `https://github.com/YOUR-GITHUB-USERNAME/collect`.
1. Use Android Studio to import the project from its Gradle settings. To run the project, click on the green arrow at the top of the screen.
1. Windows developers: continue configuring Android Studio with the steps in this document: [Developing ODK Collect on Windows](docs/WINDOWS-DEV-SETUP.md).
1. Make sure you can run unit tests by running everything under `collect_app/src/test/java` in Android Studio or on the command line:
```
./gradlew testDebug
```
1. Make sure you can run instrumented tests by running everything under `collect_app/src/androidTest/java` in Android Studio or on the command line:
```
./gradlew connectedAndroidTest
```
**Note:** You can see the emulator setup used on CI in `.circleci/config.yml`.
## Customizing the development environment
### Changing JVM heap size
You can customize the heap size that is used for compiling and running tests. Increasing these will most likely speed up compilation and tests on your local machine. The default values are specified in the project's `gradle.properties` and this can be overriden by your user level `gradle.properties` (found in your `GRADLE_USER_HOME` directory). An example `gradle.properties` that would give you a heap size of 4GB (rather than the default 1GB) would look like:
```
org.gradle.jvmargs=-Xmx4096m
```
## Testing a form without a server
When you first run Collect, it is set to download forms from [https://demo.getodk.org/](https://demo.getodk.org/), the demo server. You can sometimes verify your changes with those forms but it can also be helpful to put a specific test form on your device. Here are some options for that:
1. The `All question types` form from the default server is [here](https://docs.google.com/spreadsheets/d/1af_Sl8A_L8_EULbhRLHVl8OclCfco09Hq2tqb9CslwQ/edit#gid=0). You can also try [example forms](https://github.com/XLSForm/example-forms) and [test forms](https://github.com/XLSForm/test-forms) or [make your own](https://xlsform.org).
1. Convert the XLSForm (xlsx) to XForm (xml). Use the [ODK website](http://getodk.org/xlsform/) or [XLSForm Offline](https://gumroad.com/l/xlsform-offline) or [pyxform](https://github.com/XLSForm/pyxform).
1. Once you have the XForm, use [adb](https://developer.android.com/studio/command-line/adb.html) to push the form to your device (after [enabling USB debugging](https://www.kingoapp.com/root-tutorials/how-to-enable-usb-debugging-mode-on-android.htm)) or emulator.
```
adb push my_form.xml /sdcard/Android/data/org.odk.collect.android/files/projects/{project-id}/forms
```
If you are using the demo project, kindly replace `{project_id}` with `DEMO`
4. Launch ODK Collect and tap `+ Start new form`. The new form will be there.
More information about using Android Debug Bridge with Collect can be found [here](https://docs.getodk.org/collect-adb/).
## Using APIs for local development
Certain functions in ODK Collect depend on cloud services that require API keys or authorization steps to work. Here are the steps you need to take in order to use these functions in your development builds.
**Google Maps API**: When the "Google Maps SDK" option is selected in the "User interface" settings, ODK Collect uses the Google Maps API for displaying maps in the geospatial question types (GeoPoint, GeoTrace, and GeoShape). To enable this API:
1. [Get a Google Maps API key](https://developers.google.com/maps/documentation/android-api/signup). Note that this requires a credit card number, though the card will not be charged immediately; some free API usage is permitted. You should carefully read the terms before providing a credit card number.
1. Edit or create `secrets.properties` and set the `GOOGLE_MAPS_API_KEY` property to your API key. You should end up with a line that looks like this:
```
GOOGLE_MAPS_API_KEY=AIbzvW8e0ub...
```
**Mapbox Maps SDK for Android**: When the "Mapbox SDK" option is selected in the "User interface" settings, ODK Collect uses the Mapbox SDK for displaying maps in the geospatial question types (GeoPoint, GeoTrace, and GeoShape). To enable this API:
1. [Create a Mapbox account](https://www.mapbox.com/signup/). Note that signing up with the "Pay-As-You-Go" plan does not require a credit card. Mapbox provides free API usage up to the monthly thresholds documented at [https://www.mapbox.com/pricing](https://www.mapbox.com/pricing). If your usage exceeds these thresholds, you will receive e-mail with instructions on how to add a credit card for payment; services will remain live until the end of the 30-day billing term, after which the account will be deactivated and will require a credit card to reactivate.
2. Find your access token on your [account page](https://account.mapbox.com/) - it should be in "Tokens" as "Default public token".
3. Edit or create `secrets.properties` and set the `MAPBOX_ACCESS_TOKEN` property to your access token. You should end up with a line that looks like this:
```
MAPBOX_ACCESS_TOKEN=pk.eyJk3bumVp4i...
```
4. Create a new secret token with the "DOWNLOADS:READ" secret scope and then add it to `secrets.properties` as `MAPBOX_DOWNLOADS_TOKEN`.
*Note: Mapbox will not be available as an option in compiled versions of Collect unless you follow the steps above. Mapbox will also not be available on x86 devices as the native libraries are excluded to reduce the APK size. If you need to use an x86 device, you can force the build to include x86 libs by include the `x86Libs` Gradle parameter. For example, to build a debug APK with x86 libs: `./gradlew assembleDebug -Px86Libs`.*
## Debugging JavaRosa
JavaRosa is the form engine that powers Collect. If you want to debug or change that code while running Collect you can deploy it locally with Maven (you'll need `mvn` and `sed` installed):
1. Build and install your changes of JavaRosa (into your local Maven repo):
```bash
./gradlew publishToMavenLocal
```
1. Change `const val javarosa = javarosa_online` in `Dependencies.kt` to `const val javarosa = javarosa_local`
## Troubleshooting
#### Error when running Robolectric tests from Android Studio on macOS: `build/intermediates/bundles/debug/AndroidManifest.xml (No such file or directory)`
> Configure the default JUnit test runner configuration in order to work around a bug where IntelliJ / Android Studio does not set the working directory to the module being tested. This can be accomplished by editing the run configurations, Defaults -> JUnit and changing the working directory value to $MODULE_DIR$.
> Source: [Robolectric Wiki](https://github.com/robolectric/robolectric/wiki/Running-tests-in-Android-Studio#notes-for-mac).
#### Android Studio Error: `SDK location not found. Define location with sdk.dir in the local.properties file or with an ANDROID_HOME environment variable.`
When cloning the project from Android Studio, click "No" when prompted to open the `build.gradle` file and then open project.
#### Execution failed for task ':collect_app:transformClassesWithInstantRunForDebug'.
We have seen this problem happen in both IntelliJ IDEA and Android Studio, and believe it to be due to a bug in the IDE, which we can't fix. As a workaround, turning off [Instant Run](https://developer.android.com/studio/run/#set-up-ir) will usually avoid this problem. The problem is fixed in Android Studio 3.5 with the new [Apply Changes](https://medium.com/androiddevelopers/android-studio-project-marble-apply-changes-e3048662e8cd) feature.
#### Moving to the main view if user minimizes the app
If you build the app on your own using Android Studio `(Build -> Build APK)` and then install it (from an `.apk` file), you might notice this strange behaviour thoroughly described: [#1280](https://github.com/getodk/collect/issues/1280) and [#1142](https://github.com/getodk/collect/issues/1142).
This problem occurs building other apps as well.
#### gradlew Failure: `FAILURE: Build failed with an exception.`
If you encounter an error similar to this when running `gradlew`:
```
FAILURE: Build failed with an exception
What went wrong:
A problem occurred configuring project ':collect_app'.
> Failed to notify project evaluation listener.
> Could not initialize class com.android.sdklib.repository.AndroidSdkHandler
```
You may have a mismatch between the embedded Android SDK Java and the JDK installed on your machine. You may wish to set your **JAVA_HOME** environment variable to that SDK. For example, on macOS:
`export JAVA_HOME="/Applications/Android\ Studio.app/Contents/jre/Contents/Home/"
`
Note that this change might cause problems with other Java-based applications (e.g., if you uninstall Android Studio).
#### gradlew Failure: `java.lang.NullPointerException (no error message).`
If you encounter the `java.lang.NullPointerException (no error message).` when running `gradlew`, please make sure your Java version for this project is Java 17.
This can be configured under **File > Project Structure** in Android Studio, or by editing `$USER_HOME/.gradle/gradle.properties` to set `org.gradle.java.home=(path to JDK home)` for command-line use.
#### `Unable to resolve artifact: Missing` while running tests
This is encountered when Robolectric has problems downloading the jars it needs for different Android SDK levels. If you keep running into this you can download the JARs locally and point Robolectric to them by doing:
```
./download-robolectric-deps.sh
```
## Test devices
Devices that @getodk/testers have available for testing are as follows:
* Xiaomi Redmi 9T 4GB - Android 10
* Pixel 7a 8GB - Android 14
* LG Nexus 5X 2GB - Android 8.1
* Samsung Galaxy M12 4GB - Android 11
* Samsung Galaxy M23 4GB - Android 14
* Xiaomi Redmi 7 3GB - Android 10
* Pixel 6a 6GB - Android 13
* Pixel 3a 4GB - Android 12
* Huawei Y560-L01 1GB - Android 5.1
## Creating signed releases for Google Play Store
Maintainers keep a folder with a clean checkout of the code and use [jenv.be](https://www.jenv.be) in that folder to ensure compilation with Java 17.
### Release prerequisites:
- a`local.properties` file in the root folder with the following:
```
sdk.dir=/path/to/android/sdk
```
- the keystore file and passwords
- a `secrets.properties` file in the root project folder folder with the following:
```
// secrets.properties
RELEASE_STORE_FILE=/path/to/collect.keystore
RELEASE_STORE_PASSWORD=secure-store-password
RELEASE_KEY_ALIAS=key-alias
RELEASE_KEY_PASSWORD=secure-alias-password
```
- a `google-services.json` file in the `collect_app/src/odkCollectRelease` folder. The contents of the file are similar to the contents of `collect_app/src/google-services.json`.
### Release checklist:
- update translations
- make sure CI is green for the chosen commit
- run `./gradlew releaseCheck`. If successful, a signed release will be at `collect_app/build/outputs/apk` (with an old version name)
- verify a basic "happy path": scan a QR code to configure a new project, get a blank form, fill it, open the form map (confirms that the Google Maps key is correct), send form
- run `./benchmark.sh` with a real device connected to verify performance
- To run benchmarks a project will need to be set up in Central with the benchmark forms and app users. The forms and entities needed for that are available [here](https://drive.google.com/drive/folders/1dPLvDY0LhVX-5qTUEs6EDoraDnLpUS0g?usp=drive_link).
- verify new APK can be installed as update to previous version and that above "happy path" works in that case also
- create and publish scheduled forum post with release description
- write Play Store release notes, include link to forum post
- when creating a major release:
- Tag the commit for the release (`vX.X.0`)
- Run `./create-release.sh <last release version code> <release tag>`
- when creating a patch release:
- Tag the commit for the patch release (`vX.X.X`)
- (If beta has started for next release) Tag the commit for the beta release (`vX.X.X-beta.X`)
- Run `./create-release.sh <last release version code> <patch release tag> <beta release tag>`
- when creating a beta release:
- Tag the commit for the beta release (`vX.X.X-beta.X`)
- Run `./create-release.sh <last release version code> <beta release tag>`
- add a release to Github [here](https://github.com/getodk/collect/releases), generate release notes and attach the APK
- upload APK(s) to Play Store
- When creating a hotfix, the beta APK should be uploaded second as it will have a higher version code
- backup dependencies for the release by downloading the `vX.X.X.tar` artifact from the `create_dependency_backup` job on Circle CI (for the release commit) and then uploading it to [this folder](https://drive.google.com/drive/folders/1_tMKBFLdhzFZF9GKNeob4FbARjdfbtJu?usp=share_link)
- backup a self signed release APK by downloading the `selfSignedRelease.apk` from the `build_release` job on Circle CI (for the release commit) and then upload to [this folder](https://drive.google.com/drive/folders/1pbbeNaMTziFhtZmedOs0If3BeYu3Ex5x?usp=share_link)
## Compiling a previous release using backed-up dependencies
1. Download the `.tar` for relevant release tag
2. Extract `.local-m2` into the project directory:
```bash
tar -xf maven.tar -C <collect project directory>
```
The project will now be able to fetch dependencies that are no longer available (but were used to compile the release) from the `.local-m2` Maven repo.
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Reporting a Vulnerability
See our [Vulnerability Disclosure Policy](https://getodk.org/vdp).
================================================
FILE: analytics/.gitignore
================================================
/build
================================================
FILE: analytics/build.gradle.kts
================================================
plugins {
alias(libs.plugins.androidLibrary)
}
apply(from = "../config/quality.gradle")
android {
compileSdk = libs.versions.compileSdk.get().toInt()
defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
namespace = "org.odk.collect.analytics"
}
dependencies {
implementation(libs.kotlinStdlib)
implementation(libs.androidxCoreKtx)
implementation(libs.firebaseCrashlytics)
implementation(libs.firebaseAnalytics)
}
================================================
FILE: analytics/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: analytics/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest>
================================================
FILE: analytics/src/main/java/org/odk/collect/analytics/Analytics.kt
================================================
package org.odk.collect.analytics
interface Analytics {
fun logEvent(event: String)
fun logEventWithParam(event: String, key: String, value: String)
fun logNonFatal(throwable: Throwable)
fun logMessage(message: String)
fun setAnalyticsCollectionEnabled(isAnalyticsEnabled: Boolean)
fun setUserProperty(name: String, value: String)
companion object {
private var instance: Analytics = NoopAnalytics()
private val params = mutableMapOf<String, String>()
fun setInstance(analytics: Analytics) {
this.instance = analytics
}
@JvmStatic
fun log(event: String) {
instance.logEvent(event)
}
@JvmStatic
fun log(event: String, key: String) {
val paramValue = params[key]
if (paramValue != null) {
log(event, key, paramValue)
} else {
log(event)
}
}
@JvmStatic
fun log(event: String, key: String, value: String) {
instance.logEventWithParam(event, key, value)
}
@JvmStatic
fun setParam(key: String, value: String) {
params[key] = value
}
@JvmStatic
fun getParamValue(key: String): String? {
return params[key]
}
fun setUserProperty(name: String, value: String) {
instance.setUserProperty(name, value)
}
fun logNonFatal(throwable: Throwable) {
instance.logNonFatal(throwable)
}
}
}
================================================
FILE: analytics/src/main/java/org/odk/collect/analytics/BlockableFirebaseAnalytics.kt
================================================
package org.odk.collect.analytics
import android.app.Application
import android.os.Bundle
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.crashlytics.FirebaseCrashlytics
class BlockableFirebaseAnalytics(application: Application) : Analytics {
private val firebaseAnalytics = FirebaseAnalytics.getInstance(application)
private val crashlytics = FirebaseCrashlytics.getInstance()
override fun logEvent(event: String) {
firebaseAnalytics.logEvent(event, null)
}
override fun logEventWithParam(event: String, key: String, value: String) {
val bundle = Bundle()
bundle.putString(key, value)
firebaseAnalytics.logEvent(event, bundle)
}
override fun logNonFatal(throwable: Throwable) {
crashlytics.recordException(throwable)
}
override fun logMessage(message: String) {
crashlytics.log(message)
}
override fun setAnalyticsCollectionEnabled(isAnalyticsEnabled: Boolean) {
firebaseAnalytics.setAnalyticsCollectionEnabled(isAnalyticsEnabled)
crashlytics.setCrashlyticsCollectionEnabled(isAnalyticsEnabled)
}
override fun setUserProperty(name: String, value: String) {
firebaseAnalytics.setUserProperty(name, value)
}
}
================================================
FILE: analytics/src/main/java/org/odk/collect/analytics/NoopAnalytics.kt
================================================
package org.odk.collect.analytics
class NoopAnalytics : Analytics {
override fun logEvent(event: String) {}
override fun logEventWithParam(event: String, key: String, value: String) {}
override fun logNonFatal(throwable: Throwable) {}
override fun logMessage(message: String) {}
override fun setAnalyticsCollectionEnabled(isAnalyticsEnabled: Boolean) {}
override fun setUserProperty(name: String, value: String) {}
}
================================================
FILE: androidshared/.gitignore
================================================
/build
================================================
FILE: androidshared/build.gradle.kts
================================================
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinKsp)
alias(libs.plugins.composeCompiler)
}
apply(from = "../config/quality.gradle")
android {
compileSdk = libs.versions.compileSdk.get().toInt()
buildFeatures {
viewBinding = true
compose = true
}
defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
}
}
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
testOptions {
unitTests.isIncludeAndroidResources = true
}
namespace = "org.odk.collect.androidshared"
}
dependencies {
coreLibraryDesugaring(libs.desugar)
implementation(project(":icons"))
implementation(project(":strings"))
implementation(project(":shared"))
implementation(project(":async"))
implementation(libs.kotlinStdlib)
implementation(libs.androidxCoreKtx)
implementation(libs.androidxLifecycleLivedataKtx)
implementation(libs.androidMaterial)
implementation(libs.androidxFragmentKtx)
implementation(libs.androidxPreferenceKtx)
implementation(libs.timber)
implementation(libs.androidxExinterface)
implementation(libs.playServicesLocation)
val composeBom = platform(libs.androidxComposeBom)
implementation(composeBom)
implementation(libs.androidXComposeMaterial)
testImplementation(project(":test-shared"))
testImplementation(project(":androidtest"))
testImplementation(libs.junit)
testImplementation(libs.androidxTestExtJunit)
testImplementation(libs.androidxTestEspressoCore)
testImplementation(libs.robolectric)
testImplementation(libs.mockitoKotlin)
testImplementation(libs.androidxArchCoreTesting)
androidTestImplementation(libs.androidxTestExtJunit)
androidTestImplementation(libs.junit)
debugImplementation(project(":fragments-test"))
}
================================================
FILE: androidshared/src/androidTest/java/org/odk/collect/androidshared/bitmap/ImageCompressorTest.kt
================================================
/*
* Copyright 2017 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.androidshared.bitmap
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.exifinterface.media.ExifInterface
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File
@RunWith(AndroidJUnit4::class)
class ImageCompressorTest {
private lateinit var testImagePath: String
private val imageCompressor = ImageCompressor
@Test
fun imageShouldNotBeChangedIfMaxPixelsIsZero() {
saveTestBitmap(3000, 2000)
imageCompressor.execute(testImagePath, 0)
val image = ImageFileUtils.getBitmap(testImagePath, BitmapFactory.Options())!!
assertThat(3000, equalTo(image.width))
assertThat(2000, equalTo(image.height))
}
@Test
fun imageShouldNotBeChangedIfMaxPixelsIsSmallerThanZero() {
saveTestBitmap(3000, 2000)
imageCompressor.execute(testImagePath, -10)
val image = ImageFileUtils.getBitmap(testImagePath, BitmapFactory.Options())!!
assertThat(3000, equalTo(image.width))
assertThat(2000, equalTo(image.height))
}
@Test
fun imageShouldNotBeChangedIfMaxPixelsIsNotSmallerThanTheEdgeWhenWidthIsBiggerThanHeight() {
saveTestBitmap(3000, 2000)
imageCompressor.execute(testImagePath, 3000)
val image = ImageFileUtils.getBitmap(testImagePath, BitmapFactory.Options())!!
assertThat(3000, equalTo(image.width))
assertThat(2000, equalTo(image.height))
}
@Test
fun imageShouldNotBeChangedIfMaxPixelsIsNotSmallerThanTheLongEdgeWhenWidthIsSmallerThanHeight() {
saveTestBitmap(2000, 3000)
imageCompressor.execute(testImagePath, 4000)
val image = ImageFileUtils.getBitmap(testImagePath, BitmapFactory.Options())!!
assertThat(2000, equalTo(image.width))
assertThat(3000, equalTo(image.height))
}
@Test
fun imageShouldNotBeChangedIfMaxPixelsIsNotSmallerThanTheLongEdgeWhenWidthEqualsHeight() {
saveTestBitmap(3000, 3000)
imageCompressor.execute(testImagePath, 3000)
val image = ImageFileUtils.getBitmap(testImagePath, BitmapFactory.Options())!!
assertThat(3000, equalTo(image.width))
assertThat(3000, equalTo(image.height))
}
@Test
fun imageShouldBeCompressedIfMaxPixelsIsSmallerThanTheLongEdgeWhenWidthIsBiggerThanHeight() {
saveTestBitmap(4000, 3000)
imageCompressor.execute(testImagePath, 2000)
val image = ImageFileUtils.getBitmap(testImagePath, BitmapFactory.Options())!!
assertThat(2000, equalTo(image.width))
assertThat(1500, equalTo(image.height))
}
@Test
fun imageShouldBeCompressedIfMaxPixelsIsSmallerThanTheLongEdgeWhenWidthIsSmallerThanHeight() {
saveTestBitmap(3000, 4000)
imageCompressor.execute(testImagePath, 2000)
val image = ImageFileUtils.getBitmap(testImagePath, BitmapFactory.Options())!!
assertThat(1500, equalTo(image.width))
assertThat(2000, equalTo(image.height))
}
@Test
fun imageShouldBeCompressedIfMaxPixelsIsSmallerThanTheLongEdgeWhenWidthEqualsHeight() {
saveTestBitmap(3000, 3000)
imageCompressor.execute(testImagePath, 2000)
val image = ImageFileUtils.getBitmap(testImagePath, BitmapFactory.Options())!!
assertThat(2000, equalTo(image.width))
assertThat(2000, equalTo(image.height))
}
@Test
fun keepExifAfterScaling() {
val attributes = mutableMapOf(
// supported exif tags
ExifInterface.TAG_DATETIME to "2014:01:23 14:57:18",
ExifInterface.TAG_DATETIME_ORIGINAL to "2014:01:23 14:57:18",
ExifInterface.TAG_DATETIME_DIGITIZED to "2014:01:23 14:57:18",
ExifInterface.TAG_OFFSET_TIME to "+1:00",
ExifInterface.TAG_OFFSET_TIME_ORIGINAL to "+1:00",
ExifInterface.TAG_OFFSET_TIME_DIGITIZED to "+1:00",
ExifInterface.TAG_SUBSEC_TIME to "First photo",
ExifInterface.TAG_SUBSEC_TIME_ORIGINAL to "0",
ExifInterface.TAG_SUBSEC_TIME_DIGITIZED to "0",
ExifInterface.TAG_IMAGE_DESCRIPTION to "Photo from Poland",
ExifInterface.TAG_MAKE to "OLYMPUS IMAGING CORP",
ExifInterface.TAG_MODEL to "STYLUS1",
ExifInterface.TAG_SOFTWARE to "Version 1.0",
ExifInterface.TAG_ARTIST to "Grzegorz",
ExifInterface.TAG_COPYRIGHT to "G",
ExifInterface.TAG_MAKER_NOTE to "OLYMPUS",
ExifInterface.TAG_USER_COMMENT to "First photo",
ExifInterface.TAG_IMAGE_UNIQUE_ID to "123456789",
ExifInterface.TAG_CAMERA_OWNER_NAME to "John",
ExifInterface.TAG_BODY_SERIAL_NUMBER to "987654321",
ExifInterface.TAG_GPS_ALTITUDE to "41/1",
ExifInterface.TAG_GPS_ALTITUDE_REF to "0",
ExifInterface.TAG_GPS_DATESTAMP to "2014:01:23",
ExifInterface.TAG_GPS_TIMESTAMP to "14:57:18",
ExifInterface.TAG_GPS_LATITUDE to "50/1,49/1,8592/1000",
ExifInterface.TAG_GPS_LATITUDE_REF to "N",
ExifInterface.TAG_GPS_LONGITUDE to "0/1,8/1,12450/1000",
ExifInterface.TAG_GPS_LONGITUDE_REF to "W",
ExifInterface.TAG_GPS_SATELLITES to "8",
ExifInterface.TAG_GPS_STATUS to "A",
ExifInterface.TAG_ORIENTATION to "1",
// unsupported exif tags
ExifInterface.TAG_THUMBNAIL_IMAGE_LENGTH to "5",
ExifInterface.TAG_DNG_VERSION to "100"
)
saveTestBitmap(3000, 4000, attributes)
imageCompressor.execute(testImagePath, 2000)
val exifData = ExifInterface(testImagePath)
for (attributeName in attributes.keys) {
if (attributeName == ExifInterface.TAG_THUMBNAIL_IMAGE_LENGTH ||
attributeName == ExifInterface.TAG_DNG_VERSION
) {
assertThat(exifData.getAttribute(attributeName), equalTo(null))
} else {
assertThat(exifData.getAttribute(attributeName), equalTo(attributes[attributeName]))
}
}
}
@Test
fun verifyNoRotationAppliedForExifRotation() {
val attributes = mapOf(ExifInterface.TAG_ORIENTATION to ExifInterface.ORIENTATION_ROTATE_90.toString())
saveTestBitmap(3000, 4000, attributes)
imageCompressor.execute(testImagePath, 4000)
val image = ImageFileUtils.getBitmap(testImagePath, BitmapFactory.Options())!!
assertThat(3000, equalTo(image.width))
assertThat(4000, equalTo(image.height))
}
private fun saveTestBitmap(width: Int, height: Int, attributes: Map<String, String> = emptyMap()) {
testImagePath = File.createTempFile("test", ".jpg").absolutePath
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
ImageFileUtils.saveBitmapToFile(bitmap, testImagePath)
val exifInterface = ExifInterface(testImagePath)
for ((key, value) in attributes) {
exifInterface.setAttribute(key, value)
}
exifInterface.saveAttributes()
}
}
================================================
FILE: androidshared/src/androidTest/java/org/odk/collect/androidshared/bitmap/ImageFileUtilsTest.kt
================================================
/*
* Copyright 2017 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.androidshared.bitmap
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import androidx.exifinterface.media.ExifInterface
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import timber.log.Timber
import java.io.File
import java.io.IOException
@RunWith(AndroidJUnit4::class)
class ImageFileUtilsTest {
private lateinit var sourceFile: File
private lateinit var destinationFile: File
private lateinit var attributes: MutableMap<String, String>
@Before
fun setup() {
sourceFile = createTempImageFile("source")
destinationFile = createTempImageFile("destination")
attributes = HashMap()
}
@Test
fun copyAndRotateImageNinety() {
attributes[ExifInterface.TAG_ORIENTATION] = ExifInterface.ORIENTATION_ROTATE_90.toString()
saveTestBitmapToFile(sourceFile.absolutePath, attributes)
ImageFileUtils.copyImageAndApplyExifRotation(sourceFile, destinationFile)
val image = ImageFileUtils.getBitmap(
destinationFile.absolutePath,
BitmapFactory.Options()
)!!
assertEquals(2, image.width)
assertEquals(1, image.height)
assertEquals(Color.GREEN, image.getPixel(0, 0))
assertEquals(Color.RED, image.getPixel(1, 0))
verifyNoExifOrientationInDestinationFile(destinationFile.absolutePath)
}
@Test
fun copyAndRotateImageTwoSeventy() {
attributes[ExifInterface.TAG_ORIENTATION] = ExifInterface.ORIENTATION_ROTATE_270.toString()
saveTestBitmapToFile(sourceFile.absolutePath, attributes)
ImageFileUtils.copyImageAndApplyExifRotation(sourceFile, destinationFile)
val image = ImageFileUtils.getBitmap(
destinationFile.absolutePath,
BitmapFactory.Options()
)!!
assertEquals(2, image.width)
assertEquals(1, image.height)
assertEquals(Color.RED, image.getPixel(0, 0))
assertEquals(Color.GREEN, image.getPixel(1, 0))
verifyNoExifOrientationInDestinationFile(destinationFile.absolutePath)
}
@Test
fun copyAndRotateImageOneEighty() {
attributes[ExifInterface.TAG_ORIENTATION] = ExifInterface.ORIENTATION_ROTATE_180.toString()
saveTestBitmapToFile(sourceFile.absolutePath, attributes)
ImageFileUtils.copyImageAndApplyExifRotation(sourceFile, destinationFile)
val image = ImageFileUtils.getBitmap(
destinationFile.absolutePath,
BitmapFactory.Options()
)!!
assertEquals(1, image.width)
assertEquals(2, image.height)
assertEquals(Color.GREEN, image.getPixel(0, 0))
assertEquals(Color.RED, image.getPixel(0, 1))
verifyNoExifOrientationInDestinationFile(destinationFile.absolutePath)
}
@Test
fun copyAndRotateImageUndefined() {
attributes[ExifInterface.TAG_ORIENTATION] = ExifInterface.ORIENTATION_UNDEFINED.toString()
saveTestBitmapToFile(sourceFile.absolutePath, attributes)
ImageFileUtils.copyImageAndApplyExifRotation(sourceFile, destinationFile)
val image = ImageFileUtils.getBitmap(
destinationFile.absolutePath,
BitmapFactory.Options()
)!!
assertEquals(1, image.width)
assertEquals(2, image.height)
assertEquals(Color.RED, image.getPixel(0, 0))
assertEquals(Color.GREEN, image.getPixel(0, 1))
verifyNoExifOrientationInDestinationFile(destinationFile.absolutePath)
}
@Test
fun copyAndRotateImageNoExif() {
saveTestBitmapToFile(sourceFile.absolutePath, null)
ImageFileUtils.copyImageAndApplyExifRotation(sourceFile, destinationFile)
val image = ImageFileUtils.getBitmap(
destinationFile.absolutePath,
BitmapFactory.Options()
)!!
assertEquals(1, image.width)
assertEquals(2, image.height)
assertEquals(Color.RED, image.getPixel(0, 0))
assertEquals(Color.GREEN, image.getPixel(0, 1))
verifyNoExifOrientationInDestinationFile(destinationFile.absolutePath)
}
/**
* These cases all have a window smaller than the image so the image should be scaled down.
* Note that the scaling isn't exact -- the factor is the closest power of 2 to the exact one.
*/
@Test
fun scaleDownBitmapWhenNeeded() {
runScaleTest(1000, 1000, 500, 500, 500, 500, false)
runScaleTest(600, 800, 600, 200, 150, 200, false)
runScaleTest(500, 400, 250, 200, 250, 200, false)
}
@Test
fun doNotScaleDownBitmapWhenNotNeeded() {
runScaleTest(1000, 1000, 2000, 2000, 1000, 1000, false)
runScaleTest(600, 800, 600, 800, 600, 800, false)
runScaleTest(500, 400, 600, 600, 500, 400, false)
runScaleTest(2000, 800, 4000, 2000, 2000, 800, false)
}
@Test
fun accuratelyScaleBitmapToDisplay() {
runScaleTest(1000, 1000, 500, 500, 500, 500, true)
runScaleTest(600, 800, 600, 200, 150, 200, true)
runScaleTest(500, 400, 250, 200, 250, 200, true)
runScaleTest(2000, 800, 300, 400, 300, 120, true)
runScaleTest(1000, 1000, 2000, 2000, 2000, 2000, true)
runScaleTest(600, 800, 600, 800, 600, 800, true)
runScaleTest(500, 400, 600, 600, 600, 480, true)
runScaleTest(2000, 800, 4000, 2000, 4000, 1600, true)
}
private fun runScaleTest(
imageHeight: Int,
imageWidth: Int,
windowHeight: Int,
windowWidth: Int,
expectedHeight: Int,
expectedWidth: Int,
shouldScaleAccurately: Boolean
) {
ScaleImageTest()
.createBitmap(imageHeight, imageWidth)
.scaleBitmapToDisplay(windowHeight, windowWidth, shouldScaleAccurately)
.assertScaledBitmapDimensions(expectedHeight, expectedWidth)
}
private fun verifyNoExifOrientationInDestinationFile(destinationFilePath: String) {
val exifData = getTestImageExif(destinationFilePath)
if (exifData != null) {
assertEquals(
ExifInterface.ORIENTATION_UNDEFINED,
exifData.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED
)
)
}
}
private fun saveTestBitmapToFile(
filePath: String,
attributes: Map<String, String>?
) {
val bitmap = Bitmap.createBitmap(1, 2, Bitmap.Config.ARGB_8888)
bitmap.setPixel(0, 0, Color.RED)
bitmap.setPixel(0, 1, Color.GREEN)
ImageFileUtils.saveBitmapToFile(bitmap, filePath)
if (attributes != null) {
try {
val exifInterface = ExifInterface(filePath)
for (attributeName in attributes.keys) {
exifInterface.setAttribute(attributeName, attributes[attributeName])
}
exifInterface.saveAttributes()
} catch (e: IOException) {
Timber.w(e)
}
}
}
private fun getTestImageExif(imagePath: String): ExifInterface? {
try {
return ExifInterface(imagePath)
} catch (e: Exception) {
Timber.w(e)
}
return null
}
private fun createTempImageFile(imageName: String): File {
val temp = File.createTempFile(imageName, ".png")
temp.deleteOnExit()
return temp
}
private class ScaleImageTest {
private val cache = ApplicationProvider.getApplicationContext<Context>().externalCacheDir
private val imageFile = File(cache, "testImage.jpeg")
private var scaledBitmap: Bitmap? = null
fun createBitmap(imageHeight: Int, imageWidth: Int): ScaleImageTest {
val bitmap = Bitmap.createBitmap(imageWidth, imageHeight, Bitmap.Config.ARGB_8888)
ImageFileUtils.saveBitmapToFile(bitmap, imageFile.absolutePath)
return this
}
fun scaleBitmapToDisplay(
windowHeight: Int,
windowWidth: Int,
shouldScaleAccurately: Boolean
): ScaleImageTest {
scaledBitmap = ImageFileUtils.getBitmapScaledToDisplay(
imageFile,
windowHeight,
windowWidth,
shouldScaleAccurately
)
return this
}
fun assertScaledBitmapDimensions(expectedHeight: Int, expectedWidth: Int) {
assertEquals(expectedHeight.toLong(), scaledBitmap!!.height.toLong())
assertEquals(expectedWidth.toLong(), scaledBitmap!!.width.toLong())
}
}
}
================================================
FILE: androidshared/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<activity android:name="org.odk.collect.androidshared.ui.ReturnToAppActivity" />
</application>
</manifest>
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/async/TrackableWorker.kt
================================================
package org.odk.collect.androidshared.async
import org.odk.collect.androidshared.livedata.MutableNonNullLiveData
import org.odk.collect.androidshared.livedata.NonNullLiveData
import org.odk.collect.async.Scheduler
import java.util.concurrent.atomic.AtomicInteger
import java.util.function.Consumer
import java.util.function.Supplier
class TrackableWorker(private val scheduler: Scheduler) {
private val _isWorking = MutableNonNullLiveData(false)
val isWorking: NonNullLiveData<Boolean> = _isWorking
private var activeBackgroundJobsCounter = AtomicInteger(0)
fun <T> immediate(background: Supplier<T>, foreground: Consumer<T>) {
activeBackgroundJobsCounter.incrementAndGet()
_isWorking.value = true
scheduler.immediate(background) { result ->
if (activeBackgroundJobsCounter.decrementAndGet() == 0) {
_isWorking.value = false
}
foreground.accept(result)
}
}
fun immediate(background: Runnable) {
immediate(
background = {
background.run()
},
foreground = {}
)
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/bitmap/ImageCompressor.kt
================================================
/*
* Copyright 2017 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.androidshared.bitmap
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import androidx.exifinterface.media.ExifInterface
import timber.log.Timber
object ImageCompressor {
/**
* Before proceed with scaling or rotating, make sure existing exif information is stored/restored.
* @author Khuong Ninh (khuong.ninh@it-development.com)
*/
fun execute(imagePath: String, maxPixels: Int) {
backupExifData(imagePath)
scaleDownImage(imagePath, maxPixels)
restoreExifData(imagePath)
}
/**
* This method is used to reduce an original picture size.
* maxPixels refers to the max pixels of the long edge, the short edge is scaled proportionately.
*/
private fun scaleDownImage(imagePath: String, maxPixels: Int) {
if (maxPixels <= 0) {
return
}
var image = ImageFileUtils.getBitmap(imagePath, BitmapFactory.Options())
if (image != null) {
val originalWidth = image.width.toDouble()
val originalHeight = image.height.toDouble()
if (originalWidth > originalHeight && originalWidth > maxPixels) {
val newHeight = (originalHeight / (originalWidth / maxPixels)).toInt()
image = Bitmap.createScaledBitmap(image, maxPixels, newHeight, false)
ImageFileUtils.saveBitmapToFile(image, imagePath)
} else if (originalHeight > maxPixels) {
val newWidth = (originalWidth / (originalHeight / maxPixels)).toInt()
image = Bitmap.createScaledBitmap(image, newWidth, maxPixels, false)
ImageFileUtils.saveBitmapToFile(image, imagePath)
}
}
}
private fun backupExifData(imagePath: String) {
try {
val exif = ExifInterface(imagePath)
for ((key, _) in exifDataBackup) {
exifDataBackup[key] = exif.getAttribute(key)
}
} catch (e: Throwable) {
Timber.w(e)
}
}
private fun restoreExifData(imagePath: String) {
try {
val exif = ExifInterface(imagePath)
for ((key, value) in exifDataBackup) {
exif.setAttribute(key, value)
}
exif.saveAttributes()
} catch (e: Throwable) {
Timber.w(e)
}
}
private val exifDataBackup = mutableMapOf<String, String?>(
ExifInterface.TAG_DATETIME to null,
ExifInterface.TAG_DATETIME_ORIGINAL to null,
ExifInterface.TAG_DATETIME_DIGITIZED to null,
ExifInterface.TAG_OFFSET_TIME to null,
ExifInterface.TAG_OFFSET_TIME_ORIGINAL to null,
ExifInterface.TAG_OFFSET_TIME_DIGITIZED to null,
ExifInterface.TAG_SUBSEC_TIME to null,
ExifInterface.TAG_SUBSEC_TIME_ORIGINAL to null,
ExifInterface.TAG_SUBSEC_TIME_DIGITIZED to null,
ExifInterface.TAG_IMAGE_DESCRIPTION to null,
ExifInterface.TAG_MAKE to null,
ExifInterface.TAG_MODEL to null,
ExifInterface.TAG_SOFTWARE to null,
ExifInterface.TAG_ARTIST to null,
ExifInterface.TAG_COPYRIGHT to null,
ExifInterface.TAG_MAKER_NOTE to null,
ExifInterface.TAG_USER_COMMENT to null,
ExifInterface.TAG_IMAGE_UNIQUE_ID to null,
ExifInterface.TAG_CAMERA_OWNER_NAME to null,
ExifInterface.TAG_BODY_SERIAL_NUMBER to null,
ExifInterface.TAG_GPS_ALTITUDE to null,
ExifInterface.TAG_GPS_ALTITUDE_REF to null,
ExifInterface.TAG_GPS_DATESTAMP to null,
ExifInterface.TAG_GPS_TIMESTAMP to null,
ExifInterface.TAG_GPS_LATITUDE to null,
ExifInterface.TAG_GPS_LATITUDE_REF to null,
ExifInterface.TAG_GPS_LONGITUDE to null,
ExifInterface.TAG_GPS_LONGITUDE_REF to null,
ExifInterface.TAG_GPS_SATELLITES to null,
ExifInterface.TAG_GPS_STATUS to null,
ExifInterface.TAG_ORIENTATION to null
)
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/bitmap/ImageFileUtils.kt
================================================
package org.odk.collect.androidshared.bitmap
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.graphics.BitmapFactory
import android.graphics.Matrix
import androidx.exifinterface.media.ExifInterface
import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.lang.Exception
import java.util.Locale
import kotlin.math.ceil
object ImageFileUtils {
// 80% JPEG quality gives a greater file size reduction with almost no loss in quality
private const val IMAGE_COMPRESS_QUALITY = 80
private val EXIF_ORIENTATION_ROTATIONS = arrayOf(
ExifInterface.ORIENTATION_ROTATE_90,
ExifInterface.ORIENTATION_ROTATE_180,
ExifInterface.ORIENTATION_ROTATE_270
)
@JvmStatic
fun saveBitmapToFile(bitmap: Bitmap?, path: String) {
val compressFormat =
if (path.lowercase(Locale.getDefault()).endsWith(".png")) {
CompressFormat.PNG
} else {
CompressFormat.JPEG
}
try {
if (bitmap != null) {
FileOutputStream(path).use { out -> bitmap.compress(compressFormat, IMAGE_COMPRESS_QUALITY, out) }
}
} catch (e: Exception) {
Timber.e(e)
}
}
/*
This method is used to avoid OutOfMemoryError exception during loading an image.
If the exception occurs we catch it and try to load a smaller image.
*/
@JvmStatic
fun getBitmap(path: String?, originalOptions: BitmapFactory.Options): Bitmap? {
val newOptions = BitmapFactory.Options()
newOptions.inSampleSize = originalOptions.inSampleSize
if (newOptions.inSampleSize <= 0) {
newOptions.inSampleSize = 1
}
val bitmap: Bitmap? = try {
BitmapFactory.decodeFile(path, originalOptions)
} catch (e: OutOfMemoryError) {
Timber.i(e)
newOptions.inSampleSize++
return getBitmap(path, newOptions)
}
return bitmap
}
@JvmStatic
fun getBitmapScaledToDisplay(file: File, screenHeight: Int, screenWidth: Int): Bitmap? {
return getBitmapScaledToDisplay(file, screenHeight, screenWidth, false)
}
/**
* Scales image according to the given display
*
* @param file containing the image
* @param screenHeight height of the display
* @param screenWidth width of the display
* @param upscaleEnabled determines whether the image should be up-scaled or not
* if the window size is greater than the image size
* @return scaled bitmap
*/
@JvmStatic
fun getBitmapScaledToDisplay(
file: File,
screenHeight: Int,
screenWidth: Int,
upscaleEnabled: Boolean
): Bitmap? {
// Determine image size of file
var options = BitmapFactory.Options()
options.inJustDecodeBounds = true
getBitmap(file.absolutePath, options)
var bitmap: Bitmap?
val scale: Double
if (upscaleEnabled) {
// Load full size bitmap image
options = BitmapFactory.Options()
options.inInputShareable = true
options.inPurgeable = true
bitmap = getBitmap(file.absolutePath, options)
val heightScale = options.outHeight.toDouble() / screenHeight
val widthScale = options.outWidth.toDouble() / screenWidth
scale = widthScale.coerceAtLeast(heightScale)
val newHeight = ceil(options.outHeight / scale)
val newWidth = ceil(options.outWidth / scale)
if (bitmap != null) {
bitmap = Bitmap.createScaledBitmap(
bitmap,
newWidth.toInt(),
newHeight.toInt(),
false
)
}
} else {
val heightScale = options.outHeight / screenHeight
val widthScale = options.outWidth / screenWidth
// Powers of 2 work faster, sometimes, according to the doc.
// We're just doing closest size that still fills the screen.
scale = widthScale.coerceAtLeast(heightScale).toDouble()
// get bitmap with scale ( < 1 is the same as 1)
options = BitmapFactory.Options()
options.inInputShareable = true
options.inPurgeable = true
options.inSampleSize = scale.toInt()
bitmap = getBitmap(file.absolutePath, options)
}
if (bitmap != null) {
Timber.i(
"Screen is %dx%d. Image has been scaled down by %f to %dx%d",
screenHeight,
screenWidth,
scale,
bitmap.height,
bitmap.width
)
}
return bitmap
}
/**
* While copying the file, apply the exif rotation of sourceFile to destinationFile
* so that sourceFile with EXIF has same orientation as destinationFile without EXIF
*/
@JvmStatic
fun copyImageAndApplyExifRotation(sourceFile: File, destFile: File) {
var sourceFileExif: ExifInterface? = null
try {
sourceFileExif = ExifInterface(sourceFile)
} catch (e: IOException) {
Timber.w(e)
}
if (sourceFileExif == null ||
!EXIF_ORIENTATION_ROTATIONS.contains(
sourceFileExif
.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED
)
)
) {
// Source Image doesn't have any EXIF Rotations, so a normal file copy will suffice
sourceFile.copyTo(destFile, true)
} else {
val sourceImage = getBitmap(sourceFile.absolutePath, BitmapFactory.Options())
val orientation = sourceFileExif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_UNDEFINED
)
when (orientation) {
ExifInterface.ORIENTATION_ROTATE_90 -> rotateBitmapAndSaveToFile(
sourceImage,
90,
destFile.absolutePath
)
ExifInterface.ORIENTATION_ROTATE_180 -> rotateBitmapAndSaveToFile(
sourceImage,
180,
destFile.absolutePath
)
ExifInterface.ORIENTATION_ROTATE_270 -> rotateBitmapAndSaveToFile(
sourceImage,
270,
destFile.absolutePath
)
}
}
}
private fun rotateBitmapAndSaveToFile(image: Bitmap?, degrees: Int, filePath: String) {
var imageToSave = image
try {
val matrix = Matrix()
matrix.postRotate(degrees.toFloat())
if (image != null) {
imageToSave = Bitmap.createBitmap(image, 0, 0, image.width, image.height, matrix, true)
}
} catch (e: OutOfMemoryError) {
Timber.w(e)
}
saveBitmapToFile(imageToSave, filePath)
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/data/AppState.kt
================================================
package org.odk.collect.androidshared.data
import android.app.Activity
import android.app.Application
import android.app.Service
import android.content.Context
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
/**
* [AppState] can be used as a shared store of state that lives at an "app"/"in-memory" level
* rather than being tied to a specific component. This could be shared state between different
* [Activity] objects or a way of communicating between a [Service] and other components.
* [AppState] can be used as an alternative to Dagger singleton objects or static fields.
*
* [AppState] should not be used to share state between views or components on the same screen or make
* up part of the same flow. For this, using Jetpack's [ViewModel] at either a [Fragment] or [Activity]
* level is more appropriate.
*
* The easiest way to use [AppState] is have an instance owned by your app's [Application] object
* and implement the [StateStore] interface:
*
* ```
* class MyApplication : Application(), StateStore {
* private val appState = AppState()
* }
* ```
*
* The [AppState] instance can then be accessed anywhere the [Application] is available using the
* [getState] extension function.
*
*/
class AppState {
private val map = mutableMapOf<String, Any?>()
@Suppress("UNCHECKED_CAST")
fun <T> get(key: String, default: T): T {
return map.getOrPut(key) { default } as T
}
@Suppress("UNCHECKED_CAST")
fun <T> get(key: String): T? {
return map[key] as T?
}
fun <T> getFlow(key: String, default: T): StateFlow<T> {
return get(key, MutableStateFlow(default))
}
fun set(key: String, value: Any?) {
map[key] = value
}
fun <T> setFlow(key: String, value: T) {
get<MutableStateFlow<T>>(key).let {
if (it != null) {
it.value = value
} else {
map[key] = MutableStateFlow(value)
}
}
}
fun clear() {
map.clear()
}
fun clear(key: String) {
map.remove(key)
}
}
interface StateStore {
fun getState(): AppState
}
fun Application.getState(): AppState {
try {
val stateStore = this as StateStore
return stateStore.getState()
} catch (e: ClassCastException) {
throw ClassCastException("${this.javaClass} cannot be cast to StateStore")
}
}
fun Context.getState(): AppState {
return (applicationContext as Application).getState()
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/data/Consumable.kt
================================================
package org.odk.collect.androidshared.data
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
/**
* Useful for values that are read multiple times but only used
* once (like an error that shows a dialog once).
*/
data class Consumable<T>(val value: T) {
private var consumed = false
fun isConsumed(): Boolean {
return consumed
}
fun consume() {
consumed = true
}
}
fun <T> LiveData<out Consumable<T>?>.consume(lifecycleOwner: LifecycleOwner, consumer: (T) -> Unit) {
observe(lifecycleOwner) { consumable ->
if (consumable != null && !consumable.isConsumed()) {
consumable.consume()
consumer(consumable.value)
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/data/Data.kt
================================================
package org.odk.collect.androidshared.data
import kotlinx.coroutines.flow.StateFlow
import org.odk.collect.androidshared.data.Updatable.Data
import org.odk.collect.androidshared.data.Updatable.QualifiedData
import kotlin.reflect.KProperty
sealed interface Updatable<T> {
class QualifiedData<T>(
private val appState: AppState,
private val key: String,
private val default: T
) : Updatable<T> {
fun flow(qualifier: String): StateFlow<T> {
return appState.getFlow("$qualifier:$key", default)
}
fun set(qualifier: String?, value: T) {
appState.setFlow("$qualifier:$key", value)
}
}
class Data<T>(private val appState: AppState, private val key: String, private val default: T) :
Updatable<T> {
fun flow(): StateFlow<T> {
return appState.getFlow(key, default)
}
fun set(value: T) {
appState.setFlow(key, value)
}
}
}
abstract class DataService(
private val appState: AppState,
private val onUpdate: (() -> Unit)? = null
) {
private val updaters = mutableListOf<Updater<*>>()
fun update(qualifier: String? = null) {
updaters.forEach { it.update(qualifier) }
onUpdate?.invoke()
}
protected fun <T> data(key: String, default: T): DataDelegate<T> {
val data = Data(appState, key, default)
return DataDelegate(data)
}
protected fun <T> data(key: String, default: T, updater: () -> T): DataDelegate<T> {
val data = Data(appState, key, default)
updaters.add(Updater(data) { updater() })
return DataDelegate(data)
}
protected fun <T> qualifiedData(
key: String,
default: T
): QualifiedDataDelegate<T> {
val data = QualifiedData(appState, key, default)
return QualifiedDataDelegate(data)
}
protected fun <T> qualifiedData(
key: String,
default: T,
updater: (String) -> T
): QualifiedDataDelegate<T> {
val data = QualifiedData(appState, key, default)
updaters.add(Updater(data) { it: String? -> updater(it!!) })
return QualifiedDataDelegate(data)
}
class QualifiedDataDelegate<T>(private val data: QualifiedData<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): QualifiedData<T> {
return data
}
}
class DataDelegate<T>(private val data: Data<T>) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): Data<T> {
return data
}
}
private class Updater<T>(
private val updatable: Updatable<T>,
private val updater: (String?) -> T
) {
fun update(qualifier: String? = null) {
when (updatable) {
is Data -> updatable.set(updater(qualifier))
is QualifiedData -> updatable.set(qualifier, updater(qualifier))
}
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/livedata/LiveDataExt.kt
================================================
package org.odk.collect.androidshared.livedata
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
object LiveDataExt {
fun <T, U> LiveData<T>.combine(other: LiveData<U>): LiveData<Pair<T?, U?>> {
return LiveDataUtils.combine(this, other)
}
fun <T, U> LiveData<T>.runningFold(initial: U, operation: (U, T) -> U): LiveData<U> {
val mediator = MediatorLiveData<U>()
var accum = initial
mediator.addSource(this) {
accum = operation(accum, it)
mediator.value = accum
}
return mediator
}
/**
* Returns a [LiveData] where each value is a [Pair] made up of the latest value and the
* previous value.
*/
fun <T : Any?> LiveData<T>.withLast(): LiveData<Pair<T?, T?>> {
return this.runningFold(Pair(null, null) as Pair<T?, T?>) { last, current ->
Pair(last.second, current)
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/livedata/LiveDataUtils.java
================================================
package org.odk.collect.androidshared.livedata;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import org.odk.collect.async.Cancellable;
import java.util.function.Consumer;
import java.util.function.Function;
import kotlin.Pair;
import kotlin.Triple;
public class LiveDataUtils {
private LiveDataUtils() {
}
public static <T> Cancellable observe(LiveData<T> liveData, Consumer<T> consumer) {
Observer<T> observer = value -> {
if (value != null) {
consumer.accept(value);
}
};
liveData.observeForever(observer);
return () -> {
liveData.removeObserver(observer);
return true;
};
}
public static <T> LiveData<T> liveDataOf(T value) {
return new MutableLiveData<>(value);
}
public static <T, U> LiveData<Pair<T, U>> combine(LiveData<T> one, LiveData<U> two) {
return new CombinedLiveData<>(
new LiveData[]{one, two},
values -> new Pair<>((T) values[0], (U) values[1])
);
}
public static <T, U, V> LiveData<Triple<T, U, V>> combine3(LiveData<T> one, LiveData<U> two, LiveData<V> three) {
return new CombinedLiveData<>(
new LiveData[]{one, two, three},
values -> new Triple<>((T) values[0], (U) values[1], (V) values[2])
);
}
public static <T, U, V, W> LiveData<Quad<T, U, V, W>> combine4(LiveData<T> one, LiveData<U> two, LiveData<V> three, LiveData<W> four) {
return new CombinedLiveData<>(
new LiveData[]{one, two, three, four},
values -> new Quad<>((T) values[0], (U) values[1], (V) values[2], (W) values[3])
);
}
private static class CombinedLiveData<T> extends MediatorLiveData<T> {
private final Object[] values;
private final Function<Object[], T> map;
private T lastEmitted;
CombinedLiveData(LiveData<?>[] sources, Function<Object[], T> map) {
this.map = map;
values = new Object[sources.length];
for (int i = 0; i < sources.length; i++) {
int index = i;
addSource(sources[i], value -> {
values[index] = value;
update();
});
}
update();
}
private void update() {
T newValue = map.apply(values);
if (lastEmitted == null || !lastEmitted.equals(newValue)) {
lastEmitted = newValue;
setValue(newValue);
}
}
}
public static class Quad<T, U, V, W> {
public final T first;
public final U second;
public final V third;
public final W fourth;
public Quad(T first, U second, V third, W fourth) {
this.first = first;
this.second = second;
this.third = third;
this.fourth = fourth;
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/livedata/NonNullLiveData.kt
================================================
package org.odk.collect.androidshared.livedata
import androidx.lifecycle.LiveData
/**
* Allows creating LiveData values that can be used without null checks
*/
abstract class NonNullLiveData<T : Any>(value: T) : LiveData<T>(value) {
override fun getValue(): T {
return super.getValue() as T
}
}
class MutableNonNullLiveData<T : Any>(value: T) : NonNullLiveData<T>(value) {
public override fun postValue(value: T) {
super.postValue(value)
}
public override fun setValue(value: T) {
super.setValue(value)
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/BroadcastReceiverRegister.kt
================================================
package org.odk.collect.androidshared.system
import android.content.BroadcastReceiver
import android.content.Context
import android.content.IntentFilter
interface BroadcastReceiverRegister {
fun registerReceiver(receiver: BroadcastReceiver, filter: IntentFilter)
fun unregisterReceiver(receiver: BroadcastReceiver)
}
class BroadcastReceiverRegisterImpl(private val context: Context) : BroadcastReceiverRegister {
override fun registerReceiver(receiver: BroadcastReceiver, filter: IntentFilter) {
context.registerReceiver(receiver, filter)
}
override fun unregisterReceiver(receiver: BroadcastReceiver) {
context.unregisterReceiver(receiver)
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/CameraUtils.java
================================================
package org.odk.collect.androidshared.system;
/*
Copyright 2018 Theodoros Tyrovouzis
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.
*/
import android.content.Context;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import timber.log.Timber;
public class CameraUtils {
public static int getFrontCameraId() {
for (int camNo = 0; camNo < Camera.getNumberOfCameras(); camNo++) {
Camera.CameraInfo camInfo = new Camera.CameraInfo();
Camera.getCameraInfo(camNo, camInfo);
if (camInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
return camNo;
}
}
Timber.w("No Available front camera");
return -1;
}
public boolean isFrontCameraAvailable(Context context) {
try {
//https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html
CameraManager cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
if (cameraManager != null) {
String[] cameraId = cameraManager.getCameraIdList();
for (String id : cameraId) {
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
return true;
}
}
}
} catch (CameraAccessException | NullPointerException e) {
Timber.e(e);
}
return false; // No front-facing camera found
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/ContextExt.kt
================================================
package org.odk.collect.androidshared.system
import android.app.Activity
import android.content.Context
import android.content.res.Configuration
import android.util.TypedValue
import androidx.annotation.AttrRes
object ContextExt {
/**
* Be careful when using this method to retrieve colors, especially for those defined
* using selectors as it might not work well.
* In such cases consider using [com.google.android.material.color.MaterialColors.getColor] instead.
*/
@JvmStatic
fun getThemeAttributeValue(context: Context, @AttrRes resId: Int): Int {
val outValue = TypedValue()
context.theme.resolveAttribute(resId, outValue, true)
return outValue.data
}
@JvmStatic
fun Context.isDarkTheme(): Boolean {
val uiMode: Int = this.resources.configuration.uiMode
return (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/ExternalFilesUtils.kt
================================================
package org.odk.collect.androidshared.system
import android.content.Context
import java.io.File
import java.io.IOException
object ExternalFilesUtils {
@JvmStatic
fun testExternalFilesAccess(context: Context) {
val externalFilesDir = context.getExternalFilesDir(null)
if (externalFilesDir == null) {
throw IllegalStateException("External files dir is null!")
} else {
try {
val testFile = File(externalFilesDir, ".test")
testFile.createNewFile()
testFile.delete()
} catch (e: IOException) {
if (!externalFilesDir.exists()) {
throw IllegalStateException(
"External files dir does not exist: ${externalFilesDir.absolutePath}"
)
} else {
throw IllegalStateException(
"App can't write to external files dir: ${externalFilesDir.absolutePath}",
e
)
}
}
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/IntentLauncher.kt
================================================
package org.odk.collect.androidshared.system
import android.app.Activity
import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
object IntentLauncherImpl : IntentLauncher {
override fun launch(context: Context, intent: Intent?, onError: () -> Unit) {
try {
context.startActivity(intent)
} catch (e: Exception) {
onError()
} catch (e: Error) {
onError()
}
}
override fun launchForResult(
activity: Activity,
intent: Intent?,
requestCode: Int,
onError: () -> Unit
) {
try {
activity.startActivityForResult(intent, requestCode)
} catch (e: Exception) {
onError()
} catch (e: Error) {
onError()
}
}
override fun launchForResult(
resultLauncher: ActivityResultLauncher<Intent>,
intent: Intent?,
onError: () -> Unit
) {
try {
resultLauncher.launch(intent)
} catch (e: Exception) {
onError()
} catch (e: Error) {
onError()
}
}
}
interface IntentLauncher {
fun launch(context: Context, intent: Intent?, onError: () -> Unit)
fun launchForResult(activity: Activity, intent: Intent?, requestCode: Int, onError: () -> Unit)
fun launchForResult(
resultLauncher: ActivityResultLauncher<Intent>,
intent: Intent?,
onError: () -> Unit
)
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/OpenGLVersionChecker.kt
================================================
package org.odk.collect.androidshared.system
import android.app.ActivityManager
import android.content.Context
/**
* Checks if the device supports the given OpenGL ES version.
*
* Note: This approach may not be 100% reliable because `reqGlEsVersion` indicates
* the highest version of OpenGL ES that the device's hardware is guaranteed to support
* at runtime. However, it might not always reflect the actual version available.
*
* For a more reliable method, refer to https://developer.android.com/develop/ui/views/graphics/opengl/about-opengl#version-check.
* This recommended approach is more complex to implement but offers better accuracy.
*/
object OpenGLVersionChecker {
@JvmStatic
fun isOpenGLv2Supported(context: Context): Boolean {
return (context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager)
.deviceConfigurationInfo.reqGlEsVersion >= 0x20000
}
@JvmStatic
fun isOpenGLv3Supported(context: Context): Boolean {
return (context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager)
.deviceConfigurationInfo.reqGlEsVersion >= 0x30000
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/PlayServicesChecker.java
================================================
package org.odk.collect.androidshared.system;
import android.app.Activity;
import android.content.Context;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
/** Created by Divya on 3/2/2017. */
public class PlayServicesChecker {
private static final int PLAY_SERVICE_ERROR_REQUEST_CODE = 1000;
private static int lastResultCode = ConnectionResult.SUCCESS;
/** Returns true if Google Play Services is installed and up to date. */
public boolean isGooglePlayServicesAvailable(Context context) {
lastResultCode = GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(context);
return lastResultCode == ConnectionResult.SUCCESS;
}
/** Shows an error dialog for the last call to isGooglePlayServicesAvailable(). */
public void showGooglePlayServicesAvailabilityErrorDialog(Context context) {
GoogleApiAvailability.getInstance().getErrorDialog(
(Activity) context, lastResultCode, PLAY_SERVICE_ERROR_REQUEST_CODE).show();
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/ProcessRestoreDetector.kt
================================================
package org.odk.collect.androidshared.system
import android.content.Context
import android.os.Bundle
import org.odk.collect.androidshared.data.getState
import org.odk.collect.shared.strings.UUIDGenerator
object ProcessRestoreDetector {
@JvmStatic
fun registerOnSaveInstanceState(context: Context, outState: Bundle) {
val uuid = UUIDGenerator().generateUUID()
context.getState().set("${getKey()}:$uuid", Any())
outState.putString(getKey(), uuid)
}
@JvmStatic
fun isProcessRestoring(context: Context, savedInstanceState: Bundle?): Boolean {
return if (savedInstanceState != null) {
val bundleUuid = savedInstanceState.getString(getKey())
context.getState().get<Any>("${getKey()}:$bundleUuid") == null
} else {
false
}
}
private fun getKey() = this::class.qualifiedName
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/UriExt.kt
================================================
package org.odk.collect.androidshared.system
import android.content.ContentResolver
import android.content.Context
import android.net.Uri
import android.provider.OpenableColumns
import android.webkit.MimeTypeMap
import androidx.core.net.toFile
import java.io.File
import java.io.FileOutputStream
fun Uri.copyToFile(context: Context, dest: File) {
try {
context.contentResolver.openInputStream(this)?.use { inputStream ->
FileOutputStream(dest).use { outputStream ->
inputStream.copyTo(outputStream)
}
}
} catch (e: Exception) {
// ignore
}
}
fun Uri.getFileExtension(context: Context): String? {
var extension = getFileName(context)?.substringAfterLast(".", "")
if (extension.isNullOrEmpty()) {
val mimeType = context.contentResolver.getType(this)
extension = if (scheme == ContentResolver.SCHEME_CONTENT) {
MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType)
} else {
MimeTypeMap.getFileExtensionFromUrl(toString())
}
if (extension.isNullOrEmpty()) {
extension = mimeType?.substringAfterLast("/", "")
}
}
return if (extension.isNullOrEmpty()) {
null
} else {
".$extension"
}
}
fun Uri.getFileName(context: Context): String? {
var fileName: String? = null
try {
when (scheme) {
ContentResolver.SCHEME_FILE -> fileName = toFile().name
ContentResolver.SCHEME_CONTENT -> {
val cursor = context.contentResolver.query(this, null, null, null, null)
cursor?.use {
if (it.moveToFirst()) {
val fileNameColumnIndex = it.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (fileNameColumnIndex != -1) {
fileName = it.getString(fileNameColumnIndex)
}
}
}
}
ContentResolver.SCHEME_ANDROID_RESOURCE -> {
// for uris like [android.resource://com.example.app/1234567890]
val resourceId = lastPathSegment?.toIntOrNull()
if (resourceId != null) {
fileName = context.resources.getResourceName(resourceId)
} else {
// for uris like [android.resource://com.example.app/raw/sample]
val packageName = authority
if (pathSegments.size >= 2) {
val resourceType = pathSegments[0]
val resourceEntryName = pathSegments[1]
val resId = context.resources.getIdentifier(resourceEntryName, resourceType, packageName)
if (resId != 0) {
fileName = context.resources.getResourceName(resId)
}
}
}
}
}
if (fileName == null) {
fileName = path?.substringAfterLast("/")
}
} catch (e: Exception) {
// ignore
}
return fileName
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/AlertStore.kt
================================================
package org.odk.collect.androidshared.ui
/**
* Component for recording "alerts". This is useful for testing transient UI elements like toasts,
* flashes or snackbars that are susceptible to flakiness with assertions running after they have
* disappeared.
*/
class AlertStore {
var enabled = false
private var recordedAlerts = mutableListOf<String>()
fun register(alert: String) {
if (enabled) {
recordedAlerts.add(alert)
}
}
fun popAll(): List<String> {
val copy = recordedAlerts.toList()
recordedAlerts.clear()
return copy
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/Animations.kt
================================================
package org.odk.collect.androidshared.ui
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.view.View
import org.odk.collect.androidshared.ui.Animations.DISABLE_ANIMATIONS
/**
* Helpers/extensions for running animations. These are "test safe" in that animations can be disabled
* using [DISABLE_ANIMATIONS] - this should be set to `true` in Robolectric tests to avoid
* infinite loops.
*/
object Animations {
var DISABLE_ANIMATIONS = false
@JvmStatic
fun View.createAlphaAnimation(
startValue: Float,
endValue: Float,
duration: Long
): DisableableAnimatorWrapper {
val animation = ValueAnimator.ofFloat(startValue, endValue)
animation.duration = duration
animation.addUpdateListener {
this.alpha = it.animatedValue as Float
}
return DisableableAnimatorWrapper(animation)
}
}
class DisableableAnimatorWrapper(private val wrapped: Animator) {
fun onEnd(onEnd: () -> Unit): DisableableAnimatorWrapper {
wrapped.addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator) {
}
override fun onAnimationEnd(animation: Animator) {
onEnd()
}
override fun onAnimationCancel(animation: Animator) {
}
override fun onAnimationRepeat(animation: Animator) {
}
})
return this
}
fun then(other: DisableableAnimatorWrapper): DisableableAnimatorWrapper {
val set = AnimatorSet()
set.playSequentially(this.wrapped, other.wrapped)
return DisableableAnimatorWrapper(set)
}
fun start() {
if (!DISABLE_ANIMATIONS) {
wrapped.start()
} else {
// Just run listeners immediately if we're not running the actual animations
if (wrapped is AnimatorSet) {
(wrapped.childAnimations + wrapped).forEach { anim ->
anim.listeners?.forEach {
it.onAnimationStart(wrapped)
it.onAnimationEnd(wrapped)
}
}
} else {
wrapped.listeners?.forEach {
it.onAnimationStart(wrapped)
it.onAnimationEnd(wrapped)
}
}
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/ColorPickerDialog.kt
================================================
package org.odk.collect.androidshared
import android.app.Dialog
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.odk.collect.androidshared.databinding.ColorPickerDialogLayoutBinding
import java.lang.Exception
class ColorPickerDialog : DialogFragment() {
lateinit var binding: ColorPickerDialogLayoutBinding
val model: ColorPickerViewModel by activityViewModels()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = ColorPickerDialogLayoutBinding.inflate(LayoutInflater.from(context))
binding.hexColor.doOnTextChanged { color, _, _, _ ->
updateCurrentColorCircle("#$color")
}
binding.currentColor.text = requireArguments().getString(CURRENT_ICON)!!
fixHexColorPrefix()
setListeners()
setCurrentColor(requireArguments().getString(CURRENT_COLOR)!!)
return MaterialAlertDialogBuilder(requireContext())
.setView(binding.root)
.setTitle(org.odk.collect.strings.R.string.project_color)
.setNegativeButton(org.odk.collect.strings.R.string.cancel) { _, _ -> dismiss() }
.setPositiveButton(org.odk.collect.strings.R.string.ok) { _, _ -> model.pickColor("#${binding.hexColor.text}") }
.create()
}
private fun setListeners() {
binding.color1.setOnClickListener { setCurrentColor(R.color.color1) }
binding.color2.setOnClickListener { setCurrentColor(R.color.color2) }
binding.color3.setOnClickListener { setCurrentColor(R.color.color3) }
binding.color4.setOnClickListener { setCurrentColor(R.color.color4) }
binding.color5.setOnClickListener { setCurrentColor(R.color.color5) }
binding.color6.setOnClickListener { setCurrentColor(R.color.color6) }
binding.color7.setOnClickListener { setCurrentColor(R.color.color7) }
binding.color8.setOnClickListener { setCurrentColor(R.color.color8) }
binding.color9.setOnClickListener { setCurrentColor(R.color.color9) }
binding.color10.setOnClickListener { setCurrentColor(R.color.color10) }
binding.color11.setOnClickListener { setCurrentColor(R.color.color11) }
binding.color12.setOnClickListener { setCurrentColor(R.color.color12) }
binding.color13.setOnClickListener { setCurrentColor(R.color.color13) }
binding.color14.setOnClickListener { setCurrentColor(R.color.color14) }
binding.color15.setOnClickListener { setCurrentColor(R.color.color15) }
}
private fun setCurrentColor(color: Int) {
setCurrentColor("#${Integer.toHexString(ContextCompat.getColor(requireContext(), color)).substring(2)}")
}
private fun setCurrentColor(color: String) {
binding.hexColor.setText(color.substring(1))
}
private fun updateCurrentColorCircle(color: String) {
try {
(binding.currentColor.background as GradientDrawable).setColor(Color.parseColor(color))
binding.hexColor.error = null
(dialog as? AlertDialog)?.also {
it.getButton(AlertDialog.BUTTON_POSITIVE).alpha = 1f
it.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true
}
} catch (e: Exception) {
binding.hexColor.error = getString(org.odk.collect.strings.R.string.invalid_hex_code)
(dialog as? AlertDialog)?.also {
it.getButton(AlertDialog.BUTTON_POSITIVE).alpha = 0.3f
it.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false
}
}
}
// https://github.com/material-components/material-components-android/issues/773#issuecomment-603759649
private fun fixHexColorPrefix() {
val prefixView = binding.hexColorLayout.findViewById<AppCompatTextView>(com.google.android.material.R.id.textinput_prefix_text)
prefixView.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
prefixView.gravity = Gravity.CENTER
}
companion object {
const val CURRENT_COLOR = "CURRENT_COLOR"
const val CURRENT_ICON = "CURRENT_ICON"
}
}
class ColorPickerViewModel : ViewModel() {
private val _pickedColor = MutableLiveData<String>()
val pickedColor: LiveData<String> = _pickedColor
fun pickColor(color: String) {
_pickedColor.value = color
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/ComposeThemeProvider.kt
================================================
package org.odk.collect.androidshared.ui
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
/**
* Interface that allows a [android.content.Context] (such as an [android.app.Activity]) to
* provide a Compose Theme to any child [ComposeView] instances:
*
* ```kotlin
* class MyActivity : AppCompatActivity(), ComposeThemeProvider {
*
* override fun onCreate(savedInstanceState: Bundle?) {
* super.onCreate(savedInstanceState)
* setContentView(R.layout.my_activity_layout)
* findViewById<ComposeView>(R.id.compose_view).setContextThemedContent {
* Text("Hello, world!")
* }
* }
*
* @Composable
* override fun Theme(content: @Composable (() -> Unit)) {
* MyTheme { content() }
* }
* }
*/
interface ComposeThemeProvider {
@Composable
fun Theme(content: @Composable () -> Unit)
companion object {
fun ComposeView.setContextThemedContent(
viewCompositionStrategy: ViewCompositionStrategy,
content: @Composable () -> Unit
) {
setViewCompositionStrategy(viewCompositionStrategy)
setContent {
val themeProvider = context as? ComposeThemeProvider
if (themeProvider != null) {
themeProvider.Theme {
content()
}
} else {
content()
}
}
}
@Deprecated("Use overload instead")
fun ComposeView.setContextThemedContent(content: @Composable () -> Unit) {
setContextThemedContent(ViewCompositionStrategy.Default, content)
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/DialogFragmentUtils.kt
================================================
package org.odk.collect.androidshared.ui
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import timber.log.Timber
import kotlin.reflect.KClass
object DialogFragmentUtils {
@JvmStatic
fun <T : DialogFragment> showIfNotShowing(
dialogClass: Class<T>,
fragmentManager: FragmentManager
) {
showIfNotShowing(dialogClass, null, fragmentManager)
}
@JvmStatic
fun <T : DialogFragment> showIfNotShowing(
dialogClass: Class<T>,
args: Bundle?,
fragmentManager: FragmentManager
) {
if (fragmentManager.isDestroyed) {
return
}
val fragmentFactory = fragmentManager.fragmentFactory
val instance = fragmentFactory.instantiate(dialogClass.classLoader, dialogClass.name) as T
instance.arguments = args
showIfNotShowing(instance, dialogClass, fragmentManager)
}
@JvmStatic
fun <T : DialogFragment> showIfNotShowing(
newDialog: T,
dialogClass: Class<T>,
fragmentManager: FragmentManager
) {
showIfNotShowing(newDialog, dialogClass.name, fragmentManager)
}
@JvmStatic
fun <T : DialogFragment> showIfNotShowing(
newDialog: T,
tag: String,
fragmentManager: FragmentManager
) {
if (fragmentManager.isStateSaved) {
return
}
val existingDialog = fragmentManager.findFragmentByTag(tag) as T?
if (existingDialog == null) {
newDialog.show(fragmentManager.beginTransaction(), tag)
// We need to execute this transaction. Otherwise a follow up call to this method
// could happen before the Fragment exists in the Fragment Manager and so the
// call to findFragmentByTag would return null and result in second dialog being show.
try {
fragmentManager.executePendingTransactions()
} catch (e: IllegalStateException) {
Timber.w(e)
}
}
}
@JvmStatic
fun dismissDialog(dialogClazz: Class<*>, fragmentManager: FragmentManager) {
dismissDialog(dialogClazz.name, fragmentManager)
}
@JvmStatic
fun dismissDialog(tag: String, fragmentManager: FragmentManager) {
val existingDialog = fragmentManager.findFragmentByTag(tag) as DialogFragment?
if (existingDialog != null) {
existingDialog.dismissAllowingStateLoss()
// We need to execute this transaction. Otherwise a next attempt to display a dialog
// could happen before the Fragment is dismissed in Fragment Manager and so the
// call to findFragmentByTag would return something (not null) and as a result the
// next dialog won't be displayed.
try {
fragmentManager.executePendingTransactions()
} catch (e: IllegalStateException) {
Timber.w(e)
}
}
}
fun <T : DialogFragment> FragmentManager.showIfNotShowing(dialogClass: KClass<T>) {
showIfNotShowing(dialogClass.java, this)
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/DialogUtils.kt
================================================
package org.odk.collect.androidshared.ui
import android.content.Context
import androidx.annotation.StringRes
import com.google.android.material.dialog.MaterialAlertDialogBuilder
object DialogUtils {
@JvmStatic
fun show(
context: Context,
@StringRes titleRes: Int,
@StringRes messageRes: Int,
) {
MaterialAlertDialogBuilder(context)
.setTitle(titleRes)
.setMessage(messageRes)
.setPositiveButton(org.odk.collect.strings.R.string.ok, null)
.show()
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/DisplayString.kt
================================================
package org.odk.collect.androidshared.ui
import android.content.Context
sealed class DisplayString {
data class Raw(val value: String) : DisplayString()
data class Resource(val resource: Int) : DisplayString()
fun getString(context: Context): String {
return when (this) {
is Raw -> value
is Resource -> context.getString(resource)
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/EdgeToEdge.kt
================================================
package org.odk.collect.androidshared.ui
import android.app.Activity
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.view.Window
import androidx.annotation.LayoutRes
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding
import org.odk.collect.androidshared.system.ContextExt.isDarkTheme
object EdgeToEdge {
@JvmStatic
fun Activity.setView(@LayoutRes layout: Int, edgeToEdge: Boolean) {
window.handleEdgeToEdge(this, edgeToEdge)
setContentView(layout)
}
@JvmStatic
fun Activity.setView(view: View, edgeToEdge: Boolean) {
window.handleEdgeToEdge(this, edgeToEdge)
setContentView(view)
}
fun Window.handleEdgeToEdge(context: Context, edgeToEdge: Boolean = false) {
WindowCompat.enableEdgeToEdge(this)
WindowCompat.getInsetsController(this, this.decorView).let {
val darkTheme = context.isDarkTheme()
it.isAppearanceLightStatusBars = !darkTheme
it.isAppearanceLightNavigationBars = !darkTheme
}
if (!edgeToEdge) {
avoidEdgeToEdge()
}
}
fun View.applyBottomBarInsetMargins() {
ViewCompat.setOnApplyWindowInsetsListener(this) { v, windowInsets ->
val systemBarsInsets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
val keyboardInsets = windowInsets.getInsets(WindowInsetsCompat.Type.ime())
v.updatePadding(
bottom = maxOf(0, keyboardInsets.bottom - systemBarsInsets.bottom)
)
windowInsets
}
}
private fun Window.avoidEdgeToEdge() {
val contentView = decorView.findViewById<View>(android.R.id.content)
contentView.addSystemBarInsetMargins()
}
private fun View.addSystemBarInsetMargins() {
ViewCompat.setOnApplyWindowInsetsListener(this) { v, windowInsets ->
val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
v.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = insets.top
bottomMargin = insets.bottom
leftMargin = insets.left
rightMargin = insets.right
}
windowInsets
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/FragmentFactoryBuilder.kt
================================================
package org.odk.collect.androidshared.ui
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentFactory
import kotlin.reflect.KClass
/**
* Convenience object for creating [FragmentFactory] instances without needing to use an inner,
* private or anonymous class.
*/
class FragmentFactoryBuilder {
private val classesAndFactories = mutableListOf<Pair<Class<*>, () -> Fragment>>()
fun forClass(fragmentClass: KClass<*>, factory: () -> Fragment): FragmentFactoryBuilder {
return forClass(fragmentClass.java, factory)
}
fun forClass(fragmentClass: Class<*>, factory: () -> Fragment): FragmentFactoryBuilder {
classesAndFactories.add(Pair(fragmentClass, factory))
return this
}
fun build(): FragmentFactory {
return object : androidx.fragment.app.FragmentFactory() {
override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
val fragmentClass = loadFragmentClass(classLoader, className)
val factory =
classesAndFactories.find { it.first.isAssignableFrom(fragmentClass) }?.second
return if (factory != null) {
factory()
} else {
super.instantiate(classLoader, className)
}
}
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/GroupClickListener.kt
================================================
package org.odk.collect.androidshared.ui
import android.view.View
import androidx.constraintlayout.widget.Group
// https://stackoverflow.com/questions/59020818/group-multiple-views-in-constraint-layout-to-set-only-one-click-listener
fun Group.addOnClickListener(listener: (view: View) -> Unit) {
referencedIds.forEach { id ->
rootView.findViewById<View>(id).setOnClickListener(listener)
}
}
fun List<View>.addOnClickListener(listener: (view: View) -> Unit) {
forEach {
it.setOnClickListener(listener)
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/ListFragmentStateAdapter.kt
================================================
package org.odk.collect.androidshared.ui
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
class ListFragmentStateAdapter(
activity: FragmentActivity,
private val fragments: List<Class<out Fragment>>
) : FragmentStateAdapter(activity) {
private val fragmentFactory = activity.supportFragmentManager.fragmentFactory
override fun createFragment(position: Int): Fragment {
return fragmentFactory.instantiate(
Thread.currentThread().contextClassLoader,
fragments[position].name
)
}
override fun getItemCount(): Int {
return fragments.size
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/MenuExt.kt
================================================
package org.odk.collect.androidshared.ui
import android.annotation.SuppressLint
import android.view.Menu
import androidx.appcompat.view.menu.MenuBuilder
/**
* Currently, there is no public API to add icons to popup menus.
* The following workaround is for API 21+, and uses library-only APIs,
* and is not guaranteed to work in future versions.
*/
@SuppressLint("RestrictedApi")
fun Menu.enableIconsVisibility() {
if (this is MenuBuilder) {
this.setOptionalIconsVisible(true)
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/ObviousProgressBar.kt
================================================
package org.odk.collect.androidshared.ui
import android.content.Context
import android.os.Handler
import android.util.AttributeSet
import com.google.android.material.progressindicator.LinearProgressIndicator
/**
* A progress bar that shows for a minimum amount fo time so it's obvious to the user that
* something has happened.
*/
class ObviousProgressBar(
context: Context,
attrs: AttributeSet?
) : LinearProgressIndicator(context, attrs) {
private val handler = Handler()
private var shownAt: Long? = null
init {
super.setVisibility(GONE)
super.setIndeterminate(true)
}
override fun show() {
handler.removeCallbacksAndMessages(null)
shownAt = System.currentTimeMillis()
super.setVisibility(VISIBLE)
}
override fun hide() {
if (shownAt != null) {
val timeShown = System.currentTimeMillis() - shownAt!!
if (timeShown < MINIMUM_SHOW_TIME) {
val delay = MINIMUM_SHOW_TIME - timeShown
handler.removeCallbacksAndMessages(null)
handler.postDelayed({ this.makeGone() }, delay)
} else {
makeGone()
}
} else {
makeGone()
}
}
private fun makeGone() {
super.setVisibility(GONE)
shownAt = null
}
companion object {
private const val MINIMUM_SHOW_TIME = 750
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/OneSignTextWatcher.kt
================================================
package org.odk.collect.androidshared.ui
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
import org.odk.collect.shared.strings.StringUtils
class OneSignTextWatcher(private val editText: EditText) : TextWatcher {
lateinit var oldTextString: String
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
oldTextString = s.toString()
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(editable: Editable?) {
editable.toString().let {
if (it != oldTextString) {
val trimmedString = StringUtils.firstCharacterOrEmoji(it)
editText.setText(trimmedString)
editText.setSelection(trimmedString.length)
}
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/PrefUtils.kt
================================================
package org.odk.collect.androidshared.ui
import android.content.Context
import androidx.preference.ListPreference
import org.odk.collect.shared.settings.Settings
object PrefUtils {
@JvmStatic
fun createListPref(
context: Context,
key: String,
title: String,
labelIds: IntArray,
values: Array<String>,
settings: Settings
): ListPreference {
val labels: Array<String?> = labelIds.map { context.getString(it) }.toTypedArray()
return createListPref(context, key, title, labels, values, settings)
}
/**
* Gets an integer value from the shared preferences. If the preference has
* a string value, attempts to convert it to an integer. If the preference
* is not found or is not a valid integer, returns the defaultValue.
*/
@JvmStatic
fun getInt(key: String?, defaultValue: Int, settings: Settings): Int {
val value: Any? = settings.getAll()[key]
if (value is Int) {
return value
}
if (value is String) {
try {
return Integer.parseInt(value)
} catch (e: NumberFormatException) {
// ignore
}
}
return defaultValue
}
private fun createListPref(
context: Context,
key: String,
title: String,
labels: Array<String?>,
values: Array<String>,
settings: Settings
): ListPreference {
ensurePrefHasValidValue(key, values, settings)
return ListPreference(context).also {
it.key = key
it.isPersistent = true
it.title = title
it.dialogTitle = title
it.entries = labels
it.entryValues = values
it.summary = "%s"
}
}
private fun ensurePrefHasValidValue(
key: String,
validValues: Array<String>,
settings: Settings
) {
val value = settings.getString(key)
if (validValues.indexOf(value) < 0) {
if (validValues.isNotEmpty()) {
settings.save(key, validValues[0])
} else {
settings.remove(key)
}
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/ReturnToAppActivity.kt
================================================
package org.odk.collect.androidshared.ui
import android.app.Activity
import android.os.Bundle
/**
* This Activity will close as soon as it is started. This means it can be used as the content
* intent of a notification so clicking it will effectively return to the screen the user
* was last on (knowing what that Activity was).
*/
class ReturnToAppActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
finish()
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/SnackbarUtils.kt
================================================
/*
Copyright 2018 Shobhit
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.androidshared.ui
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.google.android.material.R
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import org.odk.collect.androidshared.data.Consumable
/**
* Convenience wrapper around Android's [Snackbar] API.
*/
object SnackbarUtils {
@JvmStatic
val alertStore = AlertStore()
const val DURATION_SHORT = 3500
const val DURATION_LONG = 5500
private var lastSnackbar: Snackbar? = null
/**
* Displays snackbar with {@param message} and multi-line message enabled.
*
* @param parentView The view to find a parent from.
* @param anchorView The view this snackbar should be anchored above.
* @param message The text to show. Can be formatted text.
* @param displayDismissButton True if the dismiss button should be displayed, false otherwise.
*/
@JvmStatic
@JvmOverloads
fun showSnackbar(
parentView: View,
message: String,
duration: Int,
anchorView: View? = null,
action: Action? = null,
displayDismissButton: Boolean = false,
onDismiss: () -> Unit = {}
) {
if (message.isBlank()) {
return
}
val snackbar = make(
parentView,
message,
duration,
anchorView,
action,
displayDismissButton,
onDismiss
)
show(snackbar)
}
fun show(snackbar: Snackbar) {
if (snackbar != lastSnackbar) {
lastSnackbar?.dismiss()
}
snackbar.show()
lastSnackbar = snackbar
val message = snackbar.view.findViewById<TextView>(R.id.snackbar_text).text.toString()
alertStore.register(message)
}
fun make(
parentView: View,
message: String,
duration: Int,
anchorView: View? = null,
action: Action? = null,
displayDismissButton: Boolean = false,
onDismiss: () -> Unit = {}
): Snackbar = Snackbar.make(parentView, message.trim(), duration).apply {
val textView =
view.findViewById<TextView>(R.id.snackbar_text)
textView.isSingleLine = false
if (anchorView?.visibility != View.GONE) {
this.anchorView = anchorView
}
if (displayDismissButton) {
view.findViewById<Button>(R.id.snackbar_action).let {
val dismissButton = ImageView(view.context).apply {
setImageResource(org.odk.collect.androidshared.R.drawable.ic_close_24)
setOnClickListener {
dismiss()
}
contentDescription =
context.getString(org.odk.collect.strings.R.string.close_snackbar)
}
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
params.setMargins(16, 0, 0, 0)
(it.parent as ViewGroup).addView(dismissButton, params)
}
}
if (action != null) {
setAction(action.text) {
action.beforeDismiss.invoke()
dismiss()
}
}
}.addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
super.onDismissed(transientBottomBar, event)
onDismiss()
lastSnackbar = null
}
})
data class SnackbarDetails @JvmOverloads constructor(
val text: String,
val action: Action? = null
)
data class Action(val text: String, val beforeDismiss: () -> Unit = {})
abstract class SnackbarPresenterObserver<T : Any?>(private val parentView: View) :
Observer<Consumable<T>?> {
abstract fun getSnackbarDetails(value: T): SnackbarDetails
override fun onChanged(consumable: Consumable<T>?) {
if (consumable != null && !consumable.isConsumed()) {
showSnackbar(
parentView,
getSnackbarDetails(consumable.value).text,
DURATION_LONG,
action = getSnackbarDetails(consumable.value).action
)
consumable.consume()
}
}
}
fun <T : Any?> LiveData<Consumable<T>?>.showSnackbar(lifecycleOwner: LifecycleOwner, parentView: View, details: (T) -> SnackbarDetails) {
observe(lifecycleOwner, object : SnackbarPresenterObserver<T>(parentView) {
override fun getSnackbarDetails(value: T): SnackbarDetails {
return details(value)
}
})
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/ToastUtils.kt
================================================
package org.odk.collect.androidshared.ui
import android.app.Activity
import android.app.Application
import android.os.Build
import android.view.Gravity
import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.odk.collect.strings.localization.getLocalizedString
/**
* Convenience wrapper around Android's [Toast] API.
*/
object ToastUtils {
@JvmStatic
val alertStore = AlertStore()
private lateinit var lastToast: Toast
private lateinit var application: Application
@JvmStatic
fun showShortToast(message: String) {
showToast(message)
}
@JvmStatic
fun showShortToast(messageResource: Int) {
showToast(
application.getLocalizedString(messageResource)
)
}
@JvmStatic
fun showLongToast(message: String) {
showToast(message, Toast.LENGTH_LONG)
}
@JvmStatic
fun showLongToast(messageResource: Int) {
showToast(
application.getLocalizedString(messageResource),
Toast.LENGTH_LONG
)
}
@JvmStatic
@Deprecated("Toast position cannot be customized on API 30 and above. A dialog is shown instead for this API levels.")
fun showShortToastInMiddle(activity: Activity, message: String) {
showToastInMiddle(activity, message)
}
@JvmStatic
fun setApplication(application: Application) {
this.application = application
}
private fun showToast(
message: String,
duration: Int = Toast.LENGTH_SHORT
) {
hideLastToast()
lastToast = Toast.makeText(application, message, duration)
lastToast.show()
alertStore.register(message)
}
private fun showToastInMiddle(
activity: Activity,
message: String,
duration: Int = Toast.LENGTH_SHORT
) {
if (Build.VERSION.SDK_INT < 30) {
hideLastToast()
lastToast = Toast.makeText(activity.applicationContext, message, duration)
try {
val group = lastToast.view as ViewGroup?
val messageTextView = group!!.getChildAt(0) as TextView
messageTextView.textSize = 21f
messageTextView.gravity = Gravity.CENTER
} catch (ignored: Exception) {
// ignored
}
lastToast.setGravity(Gravity.CENTER, 0, 0)
lastToast.show()
alertStore.register(message)
} else {
MaterialAlertDialogBuilder(activity)
.setMessage(message)
.setPositiveButton(org.odk.collect.strings.R.string.ok, null)
.create()
.show()
}
}
private fun hideLastToast() {
if (ToastUtils::lastToast.isInitialized) {
lastToast.cancel()
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/compose/Margins.kt
================================================
package org.odk.collect.androidshared.ui.compose
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import org.odk.collect.androidshared.R.dimen
@Composable
fun marginStandard(): Dp {
return dimensionResource(id = dimen.margin_standard)
}
@Composable
fun marginSmall(): Dp {
return dimensionResource(id = dimen.margin_small)
}
@Composable
fun marginExtraSmall(): Dp {
return dimensionResource(id = dimen.margin_extra_small)
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/DoubleClickSafeMaterialButton.kt
================================================
package org.odk.collect.androidshared.ui.multiclicksafe
import android.content.Context
import android.util.AttributeSet
import com.google.android.material.button.MaterialButton
class DoubleClickSafeMaterialButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : MaterialButton(context, attrs, defStyleAttr) {
override fun performClick(): Boolean {
return allowClick() && super.performClick()
}
/**
* Use [MultiClickGuard] with a scope unique to this object (class name + hash).
*/
private fun allowClick(): Boolean {
val scope = javaClass.name + hashCode()
return MultiClickGuard.allowClick(scope)
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/MultiClickGuard.kt
================================================
package org.odk.collect.androidshared.ui.multiclicksafe
import android.os.SystemClock
object MultiClickGuard {
@JvmField
var test = false
private var lastClickTime: Long = 0
private var lastClickName: String = javaClass.name
@JvmStatic
fun allowClickFast(className: String = javaClass.name): Boolean {
return allowClick(className, 500)
}
/**
* Debounce multiple clicks within the same scope
*
* @param scope If not provided, the Java class name of the element
* is used. However, this approach is imperfect, as elements on the same screen might belong to
* different classes. Consequently, clicks on these elements are treated as interactions occurring
* on two distinct screens, not protecting from rapid clicking.
*/
@JvmStatic
@JvmOverloads
fun allowClick(scope: String = javaClass.name, clickDebounceMs: Long = 1000): Boolean {
if (test) {
return true
}
val elapsedRealtime = SystemClock.elapsedRealtime()
val isSameClass = scope == lastClickName
val isBeyondThreshold = elapsedRealtime - lastClickTime > clickDebounceMs
val isBeyondTestThreshold =
lastClickTime == 0L || lastClickTime == elapsedRealtime // just for tests
val allowClick = !isSameClass || isBeyondThreshold || isBeyondTestThreshold
if (allowClick) {
lastClickTime = elapsedRealtime
lastClickName = scope
}
return allowClick
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/MultiClickSafeMaterialButton.kt
================================================
package org.odk.collect.androidshared.ui.multiclicksafe
import android.content.Context
import android.util.AttributeSet
import androidx.core.content.withStyledAttributes
import com.google.android.material.button.MaterialButton
import org.odk.collect.androidshared.R
import org.odk.collect.androidshared.ui.multiclicksafe.MultiClickGuard.allowClick
open class MultiClickSafeMaterialButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : MaterialButton(context, attrs, defStyleAttr) {
private lateinit var screenName: String
init {
context.withStyledAttributes(attrs, R.styleable.MultiClickSafeMaterialButton) {
screenName = getString(R.styleable.MultiClickSafeMaterialButton_screenName) ?: javaClass.name
}
}
override fun performClick(): Boolean {
return allowClick(screenName) && super.performClick()
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/MultiClickSafeTextInputEditText.kt
================================================
package org.odk.collect.androidshared.ui.multiclicksafe
import android.content.Context
import android.util.AttributeSet
import com.google.android.material.textfield.TextInputEditText
import org.odk.collect.androidshared.ui.multiclicksafe.MultiClickGuard.allowClick
class MultiClickSafeTextInputEditText : TextInputEditText {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet?) : super(
context,
attrs
)
override fun performClick(): Boolean {
return allowClick() && super.performClick()
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/MultiClickSaveOnClickListener.kt
================================================
package org.odk.collect.androidshared.ui.multiclicksafe
import android.view.View
abstract class MultiClickSafeOnClickListener : View.OnClickListener {
abstract fun onSafeClick(v: View)
override fun onClick(v: View) {
if (MultiClickGuard.allowClick()) {
onSafeClick(v)
}
}
}
fun View.setMultiClickSafeOnClickListener(listener: (View) -> Unit) {
setOnClickListener(object : MultiClickSafeOnClickListener() {
override fun onSafeClick(v: View) {
listener(v)
}
})
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/AppBarUtils.kt
================================================
package org.odk.collect.androidshared.utils
import android.app.Activity
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
object AppBarUtils {
@JvmStatic
fun setupAppBarLayout(activity: Activity, title: CharSequence) {
val toolbar = activity.findViewById<Toolbar>(org.odk.collect.androidshared.R.id.toolbar)
if (toolbar != null && activity is AppCompatActivity) {
toolbar.title = title
activity.setSupportActionBar(toolbar)
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/ColorUtils.kt
================================================
package org.odk.collect.androidshared.utils
import androidx.annotation.ColorInt
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.toColorInt
@ColorInt
fun String.sanitizeToColorInt() = try {
var sanitizedColor = if (this.startsWith("#")) {
this
} else {
"#$this"
}
if (sanitizedColor.length == 4) {
sanitizedColor = shorthandToLonghandHexColor(sanitizedColor)
}
sanitizedColor.toColorInt()
} catch (e: IllegalArgumentException) {
null
}
fun Int.opaque(): Int {
return ColorUtils.setAlphaComponent(this, 100)
}
private fun shorthandToLonghandHexColor(shorthandColor: String): String {
return shorthandColor.substring(1).fold("#") { accum, char ->
"$accum$char$char"
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/CompressionUtils.kt
================================================
/*
* Copyright (C) 2017 Shobhit
*
* 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.androidshared.utils
import android.util.Base64
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.lang.IllegalArgumentException
import java.util.zip.DataFormatException
import java.util.zip.Deflater
import java.util.zip.Inflater
object CompressionUtils {
@Throws(IOException::class)
fun compress(data: String?): String {
if (data == null || data.isEmpty()) {
return ""
}
// Encode string into bytes
val input = data.toByteArray(charset("UTF-8"))
val deflater = Deflater()
deflater.setInput(input)
// Compress the bytes
val outputStream = ByteArrayOutputStream(data.length)
deflater.finish()
val buffer = ByteArray(1024)
while (!deflater.finished()) {
val count = deflater.deflate(buffer) // returns the generated code... index
outputStream.write(buffer, 0, count)
}
outputStream.close()
val output = outputStream.toByteArray()
// Encode to base64
return Base64.encodeToString(output, Base64.NO_WRAP)
}
@Throws(
IOException::class,
DataFormatException::class,
IllegalArgumentException::class
)
fun decompress(compressedString: String?): String {
if (compressedString == null || compressedString.isEmpty()) {
return ""
}
// Decode from base64
val output = Base64.decode(compressedString, Base64.NO_WRAP)
val inflater = Inflater()
inflater.setInput(output)
// Decompresses the bytes
val outputStream = ByteArrayOutputStream(output.size)
val buffer = ByteArray(1024)
while (!inflater.finished()) {
val count = inflater.inflate(buffer)
outputStream.write(buffer, 0, count)
}
outputStream.close()
inflater.end()
val result = outputStream.toByteArray()
// Decode the bytes into a String
return String(result, charset("UTF-8"))
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/FileExt.kt
================================================
package org.odk.collect.androidshared.utils
import android.content.Context
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import androidx.core.net.toUri
import timber.log.Timber
import java.io.File
fun File.getVideoThumbnail(context: Context): Bitmap? {
val retriever = MediaMetadataRetriever()
return try {
retriever.setDataSource(context, this.toUri())
retriever.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC)
} catch (e: Exception) {
Timber.w(e)
null
} finally {
retriever.release()
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/InMemUniqueIdGenerator.kt
================================================
package org.odk.collect.androidshared.utils
import java.util.concurrent.atomic.AtomicInteger
class InMemUniqueIdGenerator() : UniqueIdGenerator {
private val ids = mutableMapOf<String, Int>()
private var next = AtomicInteger(1)
override fun getInt(identifier: String): Int {
return ids.getOrPut(identifier) { next.getAndIncrement() }
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/PathUtils.kt
================================================
package org.odk.collect.androidshared.utils
import org.odk.collect.shared.files.FileExt.sanitizedCanonicalPath
import timber.log.Timber
import java.io.File
import java.io.IOException
object PathUtils {
@JvmStatic
fun getAbsoluteFilePath(dirPath: String, filePath: String): String {
val absoluteFilePath =
if (filePath.startsWith(dirPath)) filePath else dirPath + File.separator + filePath
val absoluteFile = File(absoluteFilePath)
return try {
if (absoluteFile.sanitizedCanonicalPath().startsWith(File(dirPath).sanitizedCanonicalPath())) {
absoluteFilePath
} else {
throw SecurityException("Contact support@getodk.org. Attempt to access file outside of Collect directory: $absoluteFilePath")
}
} catch (e: IOException) {
Timber.e(
"Failed attempt to access canonicalPath:\n" +
"dirPath: $dirPath\n" +
"filePath: $filePath\n" +
"absoluteFilePath: $absoluteFilePath\n" +
"absoluteFilePath exists: ${absoluteFile.exists()}\n"
)
absoluteFilePath
}
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/PreferenceFragmentCompatUtils.kt
================================================
package org.odk.collect.androidshared.utils
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
fun <T : Preference> PreferenceFragmentCompat.getPreference(key: String): T {
return this.findPreference(key)!!
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/ScreenUtils.java
================================================
/*
* Copyright 2019 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.androidshared.utils;
import android.content.Context;
import android.content.res.Configuration;
import android.util.DisplayMetrics;
public class ScreenUtils {
private final Context context;
public ScreenUtils(Context context) {
this.context = context;
}
public static int getScreenWidth(Context context) {
return getDisplayMetrics(context).widthPixels;
}
public static int getScreenHeight(Context context) {
return getDisplayMetrics(context).heightPixels;
}
public static float xdpi(Context context) {
return getDisplayMetrics(context).xdpi;
}
public static float ydpi(Context context) {
return getDisplayMetrics(context).ydpi;
}
private static DisplayMetrics getDisplayMetrics(Context context) {
return context.getResources().getDisplayMetrics();
}
public int getScreenSizeConfiguration() {
return context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/SettingsUniqueIdGenerator.kt
================================================
package org.odk.collect.androidshared.utils
import org.odk.collect.shared.settings.Settings
class SettingsUniqueIdGenerator(private val settings: Settings) : UniqueIdGenerator {
override fun getInt(identifier: String): Int {
return synchronized(this) {
val identifierKey = identifierKey(identifier)
if (settings.contains(identifierKey)) {
settings.getInt(identifierKey)
} else {
val next = getNextInt()
settings.save(identifierKey, next)
next
}
}
}
private fun getNextInt(): Int {
val next = if (settings.contains(NEXT_ID_KEY)) {
settings.getInt(NEXT_ID_KEY)
} else {
1
}
settings.save(NEXT_ID_KEY, next + 1)
return next
}
companion object {
private const val KEY_PREFIX = "uniqueId"
private const val NEXT_ID_KEY = "$KEY_PREFIX:next"
private fun identifierKey(identifier: String) = "$KEY_PREFIX:identifier:$identifier"
}
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/UniqueIdGenerator.kt
================================================
package org.odk.collect.androidshared.utils
interface UniqueIdGenerator {
fun getInt(identifier: String): Int
}
================================================
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/Validator.kt
================================================
/*
* Copyright 2016 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.androidshared.utils
import android.util.Patterns
import android.webkit.URLUtil
import java.util.regex.Pattern
object Validator {
/*
There are lots of ways to validate email addresses and it's hard to find one perfect.
That's why we use here a very simple approach just to confirm that passed string contains:
-any number of characters before @ (at least one)
-one @ char
-any number of characters after @ (at least one)
*/
@JvmStatic
fun isEmailAddressValid(emailAddress: String): Boolean {
return Pattern
.compile(".+@.+")
.matcher(emailAddress)
.matches()
}
@JvmStatic
fun isUrlValid(url: String): Boolean {
return URLUtil.isValidUrl(url) && Patterns.WEB_URL.matcher(url).matches()
}
}
================================================
FILE: androidshared/src/main/res/color/color_error_button_icon.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorError" android:state_enabled="true"/>
<item android:alpha="@dimen/material_emphasis_disabled_background" android:color="?colorOnSurface"/>
</selector>
================================================
FILE: androidshared/src/main/res/color/color_on_primary_low_emphasis.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
Provides a medium emphasis version of the `colorOnPrimary` Material Theming attribute (found
here: https://m3.material.io/styles/color/the-color-system/tokens#7fd4440e-986d-443f-8b3a-4933bff16646).
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="@dimen/low_emphasis" android:color="?attr/colorOnPrimary" />
</selector>
================================================
FILE: androidshared/src/main/res/color/color_on_surface_high_emphasis.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
Provides a medium emphasis version of the `colorOnSurface` Material Theming attribute (found
here: https://material.io/develop/android/theming/typography/). Material Themes suggest using
different emphasis/transparency values on text but Android doesn't have a way to set this on a
`TextView` itself.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="@dimen/high_emphasis" android:color="?attr/colorOnSurface" />
</selector>
================================================
FILE: androidshared/src/main/res/color/color_on_surface_low_emphasis.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
Provides a medium emphasis version of the `colorOnSurface` Material Theming attribute (found
here: https://material.io/develop/android/theming/typography/). Material Themes suggest using
different emphasis/transparency values on text but Android doesn't have a way to set this on a
`TextView` itself.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="@dimen/low_emphasis" android:color="?attr/colorOnSurface" />
</selector>
================================================
FILE: androidshared/src/main/res/color/color_on_surface_medium_emphasis.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
Provides a medium emphasis version of the `colorOnSurface` Material Theming attribute (found
here: https://material.io/develop/android/theming/typography/). Material Themes suggest using
different emphasis/transparency values on text but Android doesn't have a way to set this on a
`TextView` itself.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="@dimen/medium_emphasis" android:color="?attr/colorOnSurface" />
</selector>
================================================
FILE: androidshared/src/main/res/color/color_primary_low_emphasis.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
Provides a medium emphasis version of the `colorPrimary` Material Theming attribute (found
here: https://material.io/develop/android/theming/typography/). Material Themes suggest using
different emphasis/transparency values on text but Android doesn't have a way to set this on a
`TextView` itself.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:alpha="@dimen/low_emphasis" android:color="?attr/colorPrimary" />
</selector>
================================================
FILE: androidshared/src/main/res/drawable/color_circle.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval" />
================================================
FILE: androidshared/src/main/res/drawable/ic_close_24.xml
================================================
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24"
android:tint="?colorSurface">
<path
android:fillColor="?colorSurface"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>
================================================
FILE: androidshared/src/main/res/drawable/list_item_divider.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Material Design reference: https://material.io/design/components/dividers.html#specs -->
<item>
<shape android:shape="rectangle">
<size android:height="1dp" />
<solid android:color="@color/color_on_surface_low_emphasis"/>
</shape>
</item>
</layer-list>
================================================
FILE: androidshared/src/main/res/drawable/radio_button_inset.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<inset xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="?android:attr/listChoiceIndicatorSingle"
android:insetLeft="11dp"
android:insetRight="@dimen/margin_standard"/>
================================================
FILE: androidshared/src/main/res/drawable/shadow_up.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape
android:dither="true"
android:shape="rectangle">
<gradient
android:angle="270"
android:endColor="#22000000"
android:startColor="#00000000"/>
<size android:height="10dp"/>
</shape>
</item>
</layer-list>
================================================
FILE: androidshared/src/main/res/layout/app_bar_layout.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
When scrolling, the top app bar fills with a contrasting color to create a visual separation.
This works automatically if your scrolling view (e.g., `RecyclerView`, `ListView`) is placed directly
beneath the `AppBarLayout`. However, if the scrolling view is nested within another view
(such as a `ConstraintLayout`, which is common in this app), you need to help the app bar determine
whether it should lift by setting `app:liftOnScrollTargetViewId` to the ID of the scrolling view.
Since this `AppBarLayout` is used throughout the app with various scrolling views, it’s best to
use a shared ID like `scrollable_container`.
If the scrollable view is added programmatically or it is displayed in a `ViewPager` with a
shared id, it may not work as expected anyway, and `app:liftOnScrollTargetViewId` might
need to be updated programmatically after adding such a view.
The `ODKView` and its `odk_view_container` or `DeleteFormsActivity` are good examples of this scenario.
-->
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:liftOnScrollTargetViewId="@+id/scrollable_container">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:title="Title" />
<org.odk.collect.androidshared.ui.ObviousProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.AppBarLayout>
================================================
FILE: androidshared/src/main/res/layout/color_picker_dialog_layout.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_height="match_parent"
android:layout_width="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/margin_standard">
<!-- 1st row -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/color1"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/margin_extra_small"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintWidth_percent="0.2"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/color_circle"
android:backgroundTint="@color/color1" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/color2"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/margin_extra_small"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintWidth_percent="0.2"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintStart_toEndOf="@id/color1"
app:layout_constraintTop_toTopOf="parent" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/color_circle"
android:backgroundTint="@color/color2" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/color3"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/margin_extra_small"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintWidth_percent="0.2"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintStart_toEndOf="@id/color2"
app:layout_constraintTop_toTopOf="parent" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/color_circle"
android:backgroundTint="@color/color3" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/color4"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/margin_extra_small"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintWidth_percent="0.2"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintStart_toEndOf="@id/color3"
app:layout_constraintTop_toTopOf="parent" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/color_circle"
android:backgroundTint="@color/color4" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/color5"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/margin_extra_small"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintWidth_percent="0.2"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintStart_toEndOf="@id/color4"
app:layout_constraintTop_toTopOf="parent" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/color_circle"
android:backgroundTint="@color/color5" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- 2nd row -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/color6"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/margin_extra_small"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintWidth_percent="0.2"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/color1" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/color_circle"
android:backgroundTint="@color/color6" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/color7"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/margin_extra_small"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintWidth_percent="0.2"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintStart_toEndOf="@id/color6"
app:layout_constraintTop_toBottomOf="@id/color2" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/color_circle"
android:backgroundTint="@color/color7" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/color8"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/margin_extra_small"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintWidth_percent="0.2"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintStart_toEndOf="@id/color7"
app:layout_constraintTop_toBottomOf="@id/color3" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/color_circle"
android:backgroundTint="@color/color8" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/color9"
android:layout_width="0dp"
android:layout_height="0dp"
android:padding="@dimen/margin_extra_small"
android:background="?attr/selectableItemBackgroundBorderless"
app:layout_constraintWidth_percent="0.2"
app:layout_constraintDimensionRatio="w,1:1"
app:layout_constraintStart_toEndOf="@id/color8"
app:layout_constraintTop_toBottomOf="@id/color4" >
Showing preview only (215K chars total). Download the full file or copy to clipboard to get everything.
gitextract_2lkczetw/
├── .circleci/
│ ├── config.yml
│ ├── generate-app-test-list.sh
│ ├── gradle-large.properties
│ ├── gradle.properties
│ └── test_modules.txt
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CODE_OF_CONDUCT.md
│ ├── ISSUE_TEMPLATE/
│ │ └── config.yml
│ ├── ISSUE_TEMPLATE.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── TESTING_RESULT_TEMPLATES.md
├── .gitignore
├── .hgtags
├── LICENSE.md
├── README.md
├── SECURITY.md
├── analytics/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── analytics/
│ ├── Analytics.kt
│ ├── BlockableFirebaseAnalytics.kt
│ └── NoopAnalytics.kt
├── androidshared/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── androidshared/
│ │ └── bitmap/
│ │ ├── ImageCompressorTest.kt
│ │ └── ImageFileUtilsTest.kt
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── androidshared/
│ │ │ ├── async/
│ │ │ │ └── TrackableWorker.kt
│ │ │ ├── bitmap/
│ │ │ │ ├── ImageCompressor.kt
│ │ │ │ └── ImageFileUtils.kt
│ │ │ ├── data/
│ │ │ │ ├── AppState.kt
│ │ │ │ ├── Consumable.kt
│ │ │ │ └── Data.kt
│ │ │ ├── livedata/
│ │ │ │ ├── LiveDataExt.kt
│ │ │ │ ├── LiveDataUtils.java
│ │ │ │ └── NonNullLiveData.kt
│ │ │ ├── system/
│ │ │ │ ├── BroadcastReceiverRegister.kt
│ │ │ │ ├── CameraUtils.java
│ │ │ │ ├── ContextExt.kt
│ │ │ │ ├── ExternalFilesUtils.kt
│ │ │ │ ├── IntentLauncher.kt
│ │ │ │ ├── OpenGLVersionChecker.kt
│ │ │ │ ├── PlayServicesChecker.java
│ │ │ │ ├── ProcessRestoreDetector.kt
│ │ │ │ └── UriExt.kt
│ │ │ ├── ui/
│ │ │ │ ├── AlertStore.kt
│ │ │ │ ├── Animations.kt
│ │ │ │ ├── ColorPickerDialog.kt
│ │ │ │ ├── ComposeThemeProvider.kt
│ │ │ │ ├── DialogFragmentUtils.kt
│ │ │ │ ├── DialogUtils.kt
│ │ │ │ ├── DisplayString.kt
│ │ │ │ ├── EdgeToEdge.kt
│ │ │ │ ├── FragmentFactoryBuilder.kt
│ │ │ │ ├── GroupClickListener.kt
│ │ │ │ ├── ListFragmentStateAdapter.kt
│ │ │ │ ├── MenuExt.kt
│ │ │ │ ├── ObviousProgressBar.kt
│ │ │ │ ├── OneSignTextWatcher.kt
│ │ │ │ ├── PrefUtils.kt
│ │ │ │ ├── ReturnToAppActivity.kt
│ │ │ │ ├── SnackbarUtils.kt
│ │ │ │ ├── ToastUtils.kt
│ │ │ │ ├── compose/
│ │ │ │ │ └── Margins.kt
│ │ │ │ └── multiclicksafe/
│ │ │ │ ├── DoubleClickSafeMaterialButton.kt
│ │ │ │ ├── MultiClickGuard.kt
│ │ │ │ ├── MultiClickSafeMaterialButton.kt
│ │ │ │ ├── MultiClickSafeTextInputEditText.kt
│ │ │ │ └── MultiClickSaveOnClickListener.kt
│ │ │ └── utils/
│ │ │ ├── AppBarUtils.kt
│ │ │ ├── ColorUtils.kt
│ │ │ ├── CompressionUtils.kt
│ │ │ ├── FileExt.kt
│ │ │ ├── InMemUniqueIdGenerator.kt
│ │ │ ├── PathUtils.kt
│ │ │ ├── PreferenceFragmentCompatUtils.kt
│ │ │ ├── ScreenUtils.java
│ │ │ ├── SettingsUniqueIdGenerator.kt
│ │ │ ├── UniqueIdGenerator.kt
│ │ │ └── Validator.kt
│ │ └── res/
│ │ ├── color/
│ │ │ ├── color_error_button_icon.xml
│ │ │ ├── color_on_primary_low_emphasis.xml
│ │ │ ├── color_on_surface_high_emphasis.xml
│ │ │ ├── color_on_surface_low_emphasis.xml
│ │ │ ├── color_on_surface_medium_emphasis.xml
│ │ │ └── color_primary_low_emphasis.xml
│ │ ├── drawable/
│ │ │ ├── color_circle.xml
│ │ │ ├── ic_close_24.xml
│ │ │ ├── list_item_divider.xml
│ │ │ ├── radio_button_inset.xml
│ │ │ └── shadow_up.xml
│ │ ├── layout/
│ │ │ ├── app_bar_layout.xml
│ │ │ └── color_picker_dialog_layout.xml
│ │ └── values/
│ │ ├── attrs.xml
│ │ ├── color_picker_dialog_colors.xml
│ │ ├── dimens.xml
│ │ └── styles.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── androidshared/
│ │ ├── async/
│ │ │ └── TrackableWorkerTest.kt
│ │ ├── livedata/
│ │ │ └── LiveDataUtilsTest.kt
│ │ ├── system/
│ │ │ └── UriExtTest.kt
│ │ ├── ui/
│ │ │ ├── ColorPickerDialogTest.kt
│ │ │ ├── OneSignTextWatcherTest.kt
│ │ │ └── ReturnToAppActivityTest.kt
│ │ └── utils/
│ │ ├── ColorUtilsTest.kt
│ │ ├── CompressionUtilsTest.kt
│ │ ├── DialogFragmentUtilsTest.java
│ │ ├── InMemUniqueIdGeneratorTest.kt
│ │ ├── IntentLauncherImplTest.kt
│ │ ├── PathUtilsTest.kt
│ │ ├── SettingsUniqueIdGeneratorTest.kt
│ │ ├── UniqueIdGeneratorTest.kt
│ │ └── ValidatorTest.kt
│ └── resources/
│ └── robolectric.properties
├── androidtest/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── androidtest/
│ ├── ActivityScenarioExtensions.kt
│ ├── ActivityScenarioLauncherRule.kt
│ ├── DrawableMatcher.kt
│ ├── FakeLifecycleOwner.kt
│ ├── FragmentScenarioExtensions.kt
│ ├── LiveDataTestUtils.kt
│ ├── MainDispatcherRule.kt
│ ├── NodeInteractionExtensions.kt
│ └── RecordedIntentsRule.kt
├── async/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── async/
│ │ ├── Cancellable.kt
│ │ ├── OngoingWorkListener.kt
│ │ ├── Scheduler.kt
│ │ ├── SchedulerAsyncTaskMimic.kt
│ │ ├── SchedulerBuilder.kt
│ │ ├── ScopeCancellable.kt
│ │ ├── TaskRunner.kt
│ │ ├── TaskSpec.kt
│ │ ├── TaskSpecRunner.kt
│ │ ├── TaskSpecScheduler.kt
│ │ ├── coroutines/
│ │ │ └── CoroutineTaskRunner.kt
│ │ ├── network/
│ │ │ ├── ConnectivityProvider.kt
│ │ │ └── NetworkStateProvider.kt
│ │ ├── services/
│ │ │ └── ForegroundServiceTaskSpecRunner.kt
│ │ └── workmanager/
│ │ ├── TaskSpecWorker.kt
│ │ └── WorkManagerTaskSpecScheduler.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── async/
│ ├── TaskSpecTest.kt
│ └── workmanager/
│ └── TaskSpecWorkerTest.kt
├── audio-clips/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── audioclips/
│ │ ├── AudioClipViewModel.kt
│ │ ├── AudioPlayer.kt
│ │ ├── AudioPlayerFactory.kt
│ │ ├── Clip.kt
│ │ ├── PlaybackFailedException.kt
│ │ └── ThreadSafeMediaPlayerWrapper.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── audioclips/
│ └── AudioClipViewModelTest.kt
├── audio-recorder/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── audiorecorder/
│ │ ├── DaggerSetup.kt
│ │ ├── mediarecorder/
│ │ │ └── MediaRecorderRecordingResource.kt
│ │ ├── recorder/
│ │ │ ├── Recorder.kt
│ │ │ ├── RecordingResource.kt
│ │ │ └── RecordingResourceRecorder.kt
│ │ ├── recording/
│ │ │ ├── AudioRecorder.kt
│ │ │ ├── AudioRecorderFactory.kt
│ │ │ ├── AudioRecorderService.kt
│ │ │ └── internal/
│ │ │ ├── ForegroundServiceAudioRecorder.kt
│ │ │ ├── RecordingForegroundServiceNotification.kt
│ │ │ └── RecordingRepository.kt
│ │ └── testsupport/
│ │ └── StubAudioRecorder.kt
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── audiorecorder/
│ │ ├── mediarecorder/
│ │ │ └── AMRRecordingResourceTest.kt
│ │ ├── recorder/
│ │ │ └── RecordingResourceRecorderTest.kt
│ │ ├── recording/
│ │ │ ├── AudioRecorderTest.kt
│ │ │ └── internal/
│ │ │ ├── AudioRecorderServiceTest.kt
│ │ │ ├── ForegroundServiceAudioRecorderTest.kt
│ │ │ └── RecordingForegroundServiceNotificationTest.kt
│ │ ├── support/
│ │ │ └── FakeRecorder.kt
│ │ └── testsupport/
│ │ ├── RobolectricApplication.kt
│ │ └── StubAudioRecorderTest.kt
│ └── resources/
│ └── robolectric.properties
├── benchmark.sh
├── build.gradle
├── check-size.sh
├── codecov.yml
├── collect_app/
│ ├── build.gradle
│ ├── google-services.json
│ ├── libs/
│ │ └── bikram-sambat-1.8.0.jar
│ ├── proguard-rules.txt
│ └── src/
│ ├── androidTest/
│ │ ├── assets/
│ │ │ └── instances/
│ │ │ ├── One Question_2021-06-22_15-55-50.xml
│ │ │ └── one-question-google_2023-08-08_14-51-00.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── android/
│ │ ├── benchmark/
│ │ │ ├── EntitiesBenchmarkTest.kt
│ │ │ ├── FormsUpdateBenchmarkTest.kt
│ │ │ ├── SearchBenchmarkTest.kt
│ │ │ └── support/
│ │ │ └── Benchmarker.kt
│ │ ├── feature/
│ │ │ ├── entitymanagement/
│ │ │ │ └── ViewEntitiesTest.kt
│ │ │ ├── external/
│ │ │ │ ├── AndroidShortcutsTest.kt
│ │ │ │ ├── FormDownloadActionTest.kt
│ │ │ │ ├── FormEditActionTest.kt
│ │ │ │ ├── FormPickActionTest.kt
│ │ │ │ ├── InstanceEditActionTest.kt
│ │ │ │ ├── InstancePickActionTest.kt
│ │ │ │ └── InstanceUploadActionTest.kt
│ │ │ ├── formentry/
│ │ │ │ ├── AddRepeatTest.kt
│ │ │ │ ├── AudioAutoplayTest.kt
│ │ │ │ ├── AudioRecordingTest.java
│ │ │ │ ├── BackgroundAudioRecordingTest.java
│ │ │ │ ├── CascadingSelectTest.kt
│ │ │ │ ├── CatchFormDesignExceptionsTest.kt
│ │ │ │ ├── ContextMenuTest.java
│ │ │ │ ├── DeletingRepeatGroupsTest.java
│ │ │ │ ├── EncryptedFormTest.kt
│ │ │ │ ├── ExternalAudioRecordingTest.java
│ │ │ │ ├── ExternalSecondaryInstanceTest.java
│ │ │ │ ├── ExternalSelectsTest.kt
│ │ │ │ ├── FieldListUpdateTest.kt
│ │ │ │ ├── FillBlankFormWithRepeatGroupTest.kt
│ │ │ │ ├── FormEndTest.kt
│ │ │ │ ├── FormHierarchyTest.java
│ │ │ │ ├── FormLanguageTest.java
│ │ │ │ ├── FormMediaTest.kt
│ │ │ │ ├── FormMetadataTest.kt
│ │ │ │ ├── FormNavigationTest.kt
│ │ │ │ ├── FormSaveTest.kt
│ │ │ │ ├── FormStylingTest.kt
│ │ │ │ ├── GuidanceTest.kt
│ │ │ │ ├── ImageLoadingTest.kt
│ │ │ │ ├── IntentGroupTest.kt
│ │ │ │ ├── InvalidFormTest.kt
│ │ │ │ ├── LikertTest.java
│ │ │ │ ├── NestedIntentGroupTest.kt
│ │ │ │ ├── QuickSaveTest.java
│ │ │ │ ├── QuittingFormTest.java
│ │ │ │ ├── RankingWidgetWithCSVTest.java
│ │ │ │ ├── RequiredAndConstraintQuestionTest.kt
│ │ │ │ ├── SaveIncompleteTest.kt
│ │ │ │ ├── SavePointTest.kt
│ │ │ │ ├── SearchAppearancesTest.kt
│ │ │ │ ├── audit/
│ │ │ │ │ ├── AuditTest.kt
│ │ │ │ │ ├── IdentifyUserTest.kt
│ │ │ │ │ └── TrackChangesReasonTest.kt
│ │ │ │ ├── backgroundlocation/
│ │ │ │ │ ├── LocationTrackingAuditTest.java
│ │ │ │ │ └── SetGeopointActionTest.java
│ │ │ │ ├── dynamicpreload/
│ │ │ │ │ ├── DynamicPreLoadedDataPullTest.kt
│ │ │ │ │ └── DynamicPreLoadedDataSelects.java
│ │ │ │ └── entities/
│ │ │ │ ├── EntityFormApprovalTest.kt
│ │ │ │ ├── EntityFormCreateUpdateTest.kt
│ │ │ │ ├── EntityFormEditTest.kt
│ │ │ │ ├── EntityFormLockingTest.kt
│ │ │ │ ├── EntityFormSpecVersionTest.kt
│ │ │ │ └── EntityListSyncTest.kt
│ │ │ ├── formmanagement/
│ │ │ │ ├── BulkFinalizationTest.kt
│ │ │ │ ├── DeleteBlankFormTest.java
│ │ │ │ ├── FormUpdateTest.kt
│ │ │ │ ├── FormsAdbTest.java
│ │ │ │ ├── GetBlankFormsTest.java
│ │ │ │ ├── HideOldVersionsTest.java
│ │ │ │ ├── ManualUpdatesTest.java
│ │ │ │ ├── MatchExactlyTest.kt
│ │ │ │ └── PreviouslyDownloadedOnlyTest.kt
│ │ │ ├── instancemanagement/
│ │ │ │ ├── AutoSendTest.kt
│ │ │ │ ├── DeleteSavedFormTest.kt
│ │ │ │ ├── EditSavedFormTest.kt
│ │ │ │ ├── InstancesAdbTest.kt
│ │ │ │ ├── PartialSubmissionTest.kt
│ │ │ │ └── SendFinalizedFormTest.kt
│ │ │ ├── maps/
│ │ │ │ └── FormMapTest.kt
│ │ │ ├── projects/
│ │ │ │ ├── AddNewProjectTest.kt
│ │ │ │ ├── DeleteProjectTest.kt
│ │ │ │ ├── GoogleDriveDeprecationTest.kt
│ │ │ │ ├── LaunchScreenTest.kt
│ │ │ │ ├── MobileDeviceManagementTest.kt
│ │ │ │ ├── ProjectsAdbTest.kt
│ │ │ │ ├── SwitchProjectTest.kt
│ │ │ │ └── UpdateProjectTest.kt
│ │ │ ├── settings/
│ │ │ │ ├── ConfigureWithQRCodeTest.java
│ │ │ │ ├── FormEntrySettingsTest.kt
│ │ │ │ ├── FormManagementSettingsTest.kt
│ │ │ │ ├── FormMetadataSettingsTest.kt
│ │ │ │ ├── MovingBackwardsTest.kt
│ │ │ │ ├── ResetProjectTest.kt
│ │ │ │ ├── ServerSettingsTest.java
│ │ │ │ └── SettingLanguageTest.kt
│ │ │ └── smoke/
│ │ │ ├── AllWidgetsFormTest.kt
│ │ │ ├── BadServerTest.java
│ │ │ └── GetAndSubmitFormTest.java
│ │ ├── instrumented/
│ │ │ ├── forms/
│ │ │ │ └── FormUtilsTest.java
│ │ │ ├── tasks/
│ │ │ │ └── FormLoaderTaskTest.java
│ │ │ └── utilities/
│ │ │ ├── CustomSQLiteQueryExecutionTest.java
│ │ │ └── DateTimeUtilsTest.java
│ │ └── support/
│ │ ├── ActivityHelpers.java
│ │ ├── CollectHelpers.kt
│ │ ├── ContentProviderUtils.kt
│ │ ├── CountingTaskExecutorIdlingResource.java
│ │ ├── DummyActivityLauncher.kt
│ │ ├── FakeClickableMapFragment.kt
│ │ ├── FakeLocationClient.java
│ │ ├── FakeNetworkStateProvider.kt
│ │ ├── StorageUtils.kt
│ │ ├── StubOpenRosaServer.kt
│ │ ├── SubmissionParser.kt
│ │ ├── TestDependencies.kt
│ │ ├── TranslatedStringBuilder.kt
│ │ ├── actions/
│ │ │ └── RotateAction.java
│ │ ├── async/
│ │ │ ├── AsyncWorkTracker.kt
│ │ │ ├── AsyncWorkTrackerIdlingResource.kt
│ │ │ ├── TrackingCoroutineAndWorkManagerScheduler.kt
│ │ │ └── TrackingCoroutineDispatcher.kt
│ │ ├── matchers/
│ │ │ ├── CustomMatchers.kt
│ │ │ └── ToastMatcher.java
│ │ ├── pages/
│ │ │ ├── AboutPage.java
│ │ │ ├── AccessControlPage.kt
│ │ │ ├── AddNewRepeatDialog.kt
│ │ │ ├── AppClosedPage.kt
│ │ │ ├── AsyncPage.kt
│ │ │ ├── BlankFormSearchPage.java
│ │ │ ├── BulkFinalizationConfirmationDialogPage.kt
│ │ │ ├── CancelRecordingDialog.java
│ │ │ ├── ChangesReasonPromptPage.java
│ │ │ ├── DeleteSavedFormPage.java
│ │ │ ├── DeleteSelectedDialog.java
│ │ │ ├── EditSavedFormPage.java
│ │ │ ├── EntitiesPage.kt
│ │ │ ├── EntityListPage.kt
│ │ │ ├── ErrorDialog.kt
│ │ │ ├── ErrorPage.kt
│ │ │ ├── ExperimentalPage.java
│ │ │ ├── FillBlankFormPage.java
│ │ │ ├── FirstLaunchPage.kt
│ │ │ ├── FormEndPage.java
│ │ │ ├── FormEntryPage.java
│ │ │ ├── FormHierarchyPage.kt
│ │ │ ├── FormManagementPage.java
│ │ │ ├── FormMapPage.java
│ │ │ ├── FormMetadataPage.java
│ │ │ ├── FormsDownloadResultPage.kt
│ │ │ ├── GetBlankFormPage.java
│ │ │ ├── IdentifyUserPromptPage.java
│ │ │ ├── ListPreferenceDialog.java
│ │ │ ├── MainMenuPage.java
│ │ │ ├── MainMenuSettingsPage.kt
│ │ │ ├── ManualProjectCreatorDialogPage.kt
│ │ │ ├── MapsSettingsPage.java
│ │ │ ├── NotificationDrawer.kt
│ │ │ ├── OkDialog.java
│ │ │ ├── OpenSourceLicensesPage.java
│ │ │ ├── Page.kt
│ │ │ ├── PreferencePage.java
│ │ │ ├── ProjectDisplayPage.kt
│ │ │ ├── ProjectManagementPage.kt
│ │ │ ├── ProjectSettingsDialogPage.kt
│ │ │ ├── ProjectSettingsPage.java
│ │ │ ├── QRCodePage.java
│ │ │ ├── QrCodeProjectCreatorDialogPage.kt
│ │ │ ├── ResetApplicationDialog.java
│ │ │ ├── SaveOrDiscardFormDialog.kt
│ │ │ ├── SaveOrIgnoreDrawingDialog.kt
│ │ │ ├── SavepointRecoveryDialogPage.kt
│ │ │ ├── SelectMinimalDialogPage.kt
│ │ │ ├── SendFinalizedFormPage.kt
│ │ │ ├── ServerAuthDialog.java
│ │ │ ├── ServerSettingsPage.java
│ │ │ ├── ShortcutsPage.java
│ │ │ ├── UserAndDeviceIdentitySettingsPage.java
│ │ │ ├── UserInterfacePage.java
│ │ │ ├── ViewFormPage.kt
│ │ │ └── ViewSentFormPage.java
│ │ └── rules/
│ │ ├── BlankFormTestRule.kt
│ │ ├── CollectTestRule.kt
│ │ ├── FormEntryActivityTestRule.kt
│ │ ├── IdlingResourceRule.kt
│ │ ├── NotificationDrawerRule.kt
│ │ ├── PageComposeRule.kt
│ │ ├── PrepDeviceForTestsRule.kt
│ │ ├── RecentAppsRule.kt
│ │ ├── ResetRotationRule.kt
│ │ ├── ResetStateRule.kt
│ │ ├── RetryOnDeviceErrorRule.kt
│ │ ├── RunnableRule.kt
│ │ └── TestRuleChain.kt
│ ├── debug/
│ │ ├── AndroidManifest.xml
│ │ └── google-services.json
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── assets/
│ │ │ └── svg_map_helper.js
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ ├── android/
│ │ │ │ ├── activities/
│ │ │ │ │ ├── AboutActivity.kt
│ │ │ │ │ ├── ActivityUtils.java
│ │ │ │ │ ├── AppListActivity.java
│ │ │ │ │ ├── BearingActivity.java
│ │ │ │ │ ├── CrashHandlerActivity.kt
│ │ │ │ │ ├── DeleteFormsActivity.kt
│ │ │ │ │ ├── FirstLaunchActivity.kt
│ │ │ │ │ ├── FormDownloadListActivity.java
│ │ │ │ │ ├── FormEntryViewModelFactory.kt
│ │ │ │ │ ├── FormFillingActivity.java
│ │ │ │ │ ├── FormListActivity.java
│ │ │ │ │ ├── FormMapActivity.kt
│ │ │ │ │ ├── InstanceChooserList.java
│ │ │ │ │ ├── ScannerWithFlashlightActivity.kt
│ │ │ │ │ └── viewmodels/
│ │ │ │ │ └── FormDownloadListViewModel.java
│ │ │ │ ├── adapters/
│ │ │ │ │ ├── AboutListAdapter.kt
│ │ │ │ │ ├── AbstractSelectListAdapter.java
│ │ │ │ │ ├── FormDownloadListAdapter.java
│ │ │ │ │ ├── InstanceListCursorAdapter.java
│ │ │ │ │ ├── InstanceUploaderAdapter.java
│ │ │ │ │ ├── RankingListAdapter.java
│ │ │ │ │ ├── SelectMultipleListAdapter.java
│ │ │ │ │ └── SelectOneListAdapter.java
│ │ │ │ ├── analytics/
│ │ │ │ │ ├── AnalyticsEvents.kt
│ │ │ │ │ └── AnalyticsUtils.kt
│ │ │ │ ├── application/
│ │ │ │ │ ├── Collect.java
│ │ │ │ │ ├── CollectSettingsChangeHandler.kt
│ │ │ │ │ ├── ComposeTheme.kt
│ │ │ │ │ ├── FeatureFlags.kt
│ │ │ │ │ ├── MapboxClassInstanceCreator.kt
│ │ │ │ │ └── initialization/
│ │ │ │ │ ├── AnalyticsInitializer.kt
│ │ │ │ │ ├── ApplicationInitializer.kt
│ │ │ │ │ ├── CachedFormsCleaner.kt
│ │ │ │ │ ├── CrashReportingTree.kt
│ │ │ │ │ ├── ExistingProjectMigrator.kt
│ │ │ │ │ ├── ExistingSettingsMigrator.kt
│ │ │ │ │ ├── GoogleDriveProjectsDeleter.kt
│ │ │ │ │ ├── JavaRosaInitializer.kt
│ │ │ │ │ ├── MapsInitializer.kt
│ │ │ │ │ ├── SavepointsImporter.kt
│ │ │ │ │ ├── ScheduledWorkUpgrade.kt
│ │ │ │ │ ├── UserPropertiesInitializer.kt
│ │ │ │ │ └── upgrade/
│ │ │ │ │ ├── BeforeProjectsInstallDetector.kt
│ │ │ │ │ └── UpgradeInitializer.kt
│ │ │ │ ├── audio/
│ │ │ │ │ ├── AMRAppender.java
│ │ │ │ │ ├── AudioButton.java
│ │ │ │ │ ├── AudioControllerView.java
│ │ │ │ │ ├── AudioFileAppender.java
│ │ │ │ │ ├── AudioRecordingControllerFragment.java
│ │ │ │ │ ├── AudioRecordingErrorDialogFragment.java
│ │ │ │ │ ├── BackgroundAudioHelpDialogFragment.java
│ │ │ │ │ ├── M4AAppender.java
│ │ │ │ │ ├── VolumeBar.java
│ │ │ │ │ └── Waveform.java
│ │ │ │ ├── backgroundwork/
│ │ │ │ │ ├── AutoUpdateTaskSpec.kt
│ │ │ │ │ ├── BackgroundWorkUtils.java
│ │ │ │ │ ├── FormUpdateAndInstanceSubmitScheduler.java
│ │ │ │ │ ├── FormUpdateScheduler.java
│ │ │ │ │ ├── InstanceSubmitScheduler.java
│ │ │ │ │ ├── SendFormsTaskSpec.kt
│ │ │ │ │ ├── SyncFormsTaskSpec.kt
│ │ │ │ │ └── TaskData.kt
│ │ │ │ ├── configure/
│ │ │ │ │ └── qr/
│ │ │ │ │ ├── AppConfigurationGenerator.kt
│ │ │ │ │ ├── CachingQRCodeGenerator.kt
│ │ │ │ │ ├── QRCodeActivityResultDelegate.kt
│ │ │ │ │ ├── QRCodeMenuProvider.kt
│ │ │ │ │ ├── QRCodeScannerFragment.kt
│ │ │ │ │ ├── QRCodeTabsActivity.kt
│ │ │ │ │ ├── QRCodeViewModel.kt
│ │ │ │ │ ├── SettingsBarcodeScannerViewFactory.kt
│ │ │ │ │ └── ShowQRCodeFragment.kt
│ │ │ │ ├── dao/
│ │ │ │ │ ├── CursorLoaderFactory.java
│ │ │ │ │ └── helpers/
│ │ │ │ │ └── InstancesDaoHelper.java
│ │ │ │ ├── database/
│ │ │ │ │ ├── DatabaseConstants.java
│ │ │ │ │ ├── DatabaseObjectMapper.kt
│ │ │ │ │ ├── entities/
│ │ │ │ │ │ └── DatabaseEntitiesRepository.kt
│ │ │ │ │ ├── forms/
│ │ │ │ │ │ ├── DatabaseFormColumns.kt
│ │ │ │ │ │ ├── DatabaseFormsRepository.java
│ │ │ │ │ │ └── FormDatabaseMigrator.java
│ │ │ │ │ ├── instances/
│ │ │ │ │ │ ├── DatabaseInstanceColumns.kt
│ │ │ │ │ │ ├── DatabaseInstancesRepository.java
│ │ │ │ │ │ └── InstanceDatabaseMigrator.java
│ │ │ │ │ ├── itemsets/
│ │ │ │ │ │ └── DatabaseFastExternalItemsetsRepository.kt
│ │ │ │ │ └── savepoints/
│ │ │ │ │ ├── DatabaseSavepointsColumns.kt
│ │ │ │ │ ├── DatabaseSavepointsRepository.kt
│ │ │ │ │ └── SavepointsDatabaseMigrator.kt
│ │ │ │ ├── dynamicpreload/
│ │ │ │ │ ├── DynamicPreloadXFormParserFactory.kt
│ │ │ │ │ ├── ExternalAnswerResolver.java
│ │ │ │ │ ├── ExternalAppsUtils.java
│ │ │ │ │ ├── ExternalDataHandler.java
│ │ │ │ │ ├── ExternalDataManager.java
│ │ │ │ │ ├── ExternalDataManagerImpl.java
│ │ │ │ │ ├── ExternalDataReader.java
│ │ │ │ │ ├── ExternalDataReaderImpl.java
│ │ │ │ │ ├── ExternalDataUseCases.kt
│ │ │ │ │ ├── ExternalDataUtil.java
│ │ │ │ │ ├── ExternalSQLiteOpenHelper.java
│ │ │ │ │ ├── ExternalSelectChoice.java
│ │ │ │ │ └── handler/
│ │ │ │ │ ├── ExternalDataHandlerBase.java
│ │ │ │ │ ├── ExternalDataHandlerPull.java
│ │ │ │ │ ├── ExternalDataHandlerSearch.java
│ │ │ │ │ └── ExternalDataSearchType.java
│ │ │ │ ├── entities/
│ │ │ │ │ └── EntitiesRepositoryProvider.kt
│ │ │ │ ├── exception/
│ │ │ │ │ ├── EncryptionException.java
│ │ │ │ │ ├── ExternalDataException.java
│ │ │ │ │ ├── ExternalParamsException.java
│ │ │ │ │ └── JavaRosaException.java
│ │ │ │ ├── external/
│ │ │ │ │ ├── AndroidShortcutsActivity.kt
│ │ │ │ │ ├── FormUriActivity.kt
│ │ │ │ │ ├── FormsContract.java
│ │ │ │ │ ├── FormsProvider.java
│ │ │ │ │ ├── InstanceProvider.java
│ │ │ │ │ └── InstancesContract.java
│ │ │ │ ├── fastexternalitemset/
│ │ │ │ │ ├── ItemsetDao.java
│ │ │ │ │ ├── ItemsetDbAdapter.java
│ │ │ │ │ └── XPathParseTool.java
│ │ │ │ ├── formentry/
│ │ │ │ │ ├── BackgroundAudioPermissionDialogFragment.java
│ │ │ │ │ ├── BackgroundAudioViewModel.java
│ │ │ │ │ ├── CurrentFormIndex.kt
│ │ │ │ │ ├── FormAnimation.kt
│ │ │ │ │ ├── FormDefCache.kt
│ │ │ │ │ ├── FormEnd.kt
│ │ │ │ │ ├── FormEndView.kt
│ │ │ │ │ ├── FormEndViewModel.kt
│ │ │ │ │ ├── FormEntryMenuProvider.kt
│ │ │ │ │ ├── FormEntryUseCases.kt
│ │ │ │ │ ├── FormEntryViewModel.java
│ │ │ │ │ ├── FormError.kt
│ │ │ │ │ ├── FormIndexAnimationHandler.kt
│ │ │ │ │ ├── FormLoadingDialogFragment.java
│ │ │ │ │ ├── FormOpeningMode.kt
│ │ │ │ │ ├── FormSessionRepository.kt
│ │ │ │ │ ├── ODKView.java
│ │ │ │ │ ├── PrinterWidgetViewModel.kt
│ │ │ │ │ ├── QuitFormDialog.kt
│ │ │ │ │ ├── RecordingHandler.java
│ │ │ │ │ ├── RecordingWarningDialogFragment.java
│ │ │ │ │ ├── RefreshFormListDialogFragment.java
│ │ │ │ │ ├── SwipeHandler.kt
│ │ │ │ │ ├── audit/
│ │ │ │ │ │ ├── AsyncTaskAuditEventWriter.java
│ │ │ │ │ │ ├── AuditConfig.java
│ │ │ │ │ │ ├── AuditEvent.java
│ │ │ │ │ │ ├── AuditEventCSVLine.java
│ │ │ │ │ │ ├── AuditEventLogger.java
│ │ │ │ │ │ ├── AuditEventSaveTask.java
│ │ │ │ │ │ ├── AuditUtils.kt
│ │ │ │ │ │ ├── ChangesReasonPromptDialogFragment.java
│ │ │ │ │ │ ├── IdentifyUserPromptDialogFragment.java
│ │ │ │ │ │ └── IdentityPromptViewModel.java
│ │ │ │ │ ├── backgroundlocation/
│ │ │ │ │ │ ├── BackgroundLocationHelper.java
│ │ │ │ │ │ ├── BackgroundLocationManager.java
│ │ │ │ │ │ └── BackgroundLocationViewModel.java
│ │ │ │ │ ├── media/
│ │ │ │ │ │ ├── FormMediaUtils.java
│ │ │ │ │ │ └── PromptAutoplayer.java
│ │ │ │ │ ├── questions/
│ │ │ │ │ │ ├── AnswersProvider.java
│ │ │ │ │ │ ├── AudioVideoImageTextLabel.java
│ │ │ │ │ │ ├── NoButtonsItem.java
│ │ │ │ │ │ ├── QuestionDetails.java
│ │ │ │ │ │ └── SelectChoiceUtils.kt
│ │ │ │ │ ├── repeats/
│ │ │ │ │ │ ├── AddRepeatDialog.kt
│ │ │ │ │ │ └── DeleteRepeatDialogFragment.java
│ │ │ │ │ └── saving/
│ │ │ │ │ ├── DiskFormSaver.java
│ │ │ │ │ ├── FormSaveViewModel.java
│ │ │ │ │ ├── FormSaver.java
│ │ │ │ │ ├── SaveAnswerFileErrorDialogFragment.java
│ │ │ │ │ ├── SaveAnswerFileProgressDialogFragment.java
│ │ │ │ │ └── SaveFormProgressDialogFragment.java
│ │ │ │ ├── formhierarchy/
│ │ │ │ │ ├── FormHierarchyFragment.java
│ │ │ │ │ ├── FormHierarchyFragmentHostActivity.kt
│ │ │ │ │ ├── FormHierarchyViewModel.kt
│ │ │ │ │ ├── HierarchyItem.kt
│ │ │ │ │ ├── HierarchyListAdapter.kt
│ │ │ │ │ ├── HierarchyListItemView.kt
│ │ │ │ │ └── QuestionAnswerProcessor.kt
│ │ │ │ ├── formlists/
│ │ │ │ │ ├── blankformlist/
│ │ │ │ │ │ ├── BlankFormListActivity.kt
│ │ │ │ │ │ ├── BlankFormListAdapter.kt
│ │ │ │ │ │ ├── BlankFormListItem.kt
│ │ │ │ │ │ ├── BlankFormListItemView.kt
│ │ │ │ │ │ ├── BlankFormListMenuProvider.kt
│ │ │ │ │ │ ├── BlankFormListViewModel.kt
│ │ │ │ │ │ └── DeleteBlankFormFragment.kt
│ │ │ │ │ ├── savedformlist/
│ │ │ │ │ │ ├── DeleteSavedFormFragment.kt
│ │ │ │ │ │ ├── SavedFormListItemView.kt
│ │ │ │ │ │ ├── SavedFormListListMenuProvider.kt
│ │ │ │ │ │ ├── SavedFormListViewModel.kt
│ │ │ │ │ │ └── SelectableSavedFormListItemViewHolder.kt
│ │ │ │ │ └── sorting/
│ │ │ │ │ ├── FormListSortingAdapter.kt
│ │ │ │ │ ├── FormListSortingBottomSheetDialog.kt
│ │ │ │ │ └── FormListSortingOption.kt
│ │ │ │ ├── formmanagement/
│ │ │ │ │ ├── CollectFormEntryControllerFactory.kt
│ │ │ │ │ ├── FormFillingIntentFactory.kt
│ │ │ │ │ ├── FormSourceExceptionMapper.kt
│ │ │ │ │ ├── FormsDataService.kt
│ │ │ │ │ ├── LocalFormUseCases.kt
│ │ │ │ │ ├── OpenRosaClientProvider.kt
│ │ │ │ │ ├── ServerFormDetails.kt
│ │ │ │ │ ├── ServerFormUseCases.kt
│ │ │ │ │ ├── download/
│ │ │ │ │ │ ├── FormDownloadException.kt
│ │ │ │ │ │ ├── FormDownloadExceptionMapper.kt
│ │ │ │ │ │ ├── FormDownloader.kt
│ │ │ │ │ │ └── ServerFormDownloader.java
│ │ │ │ │ ├── drafts/
│ │ │ │ │ │ ├── BulkFinalizationViewModel.kt
│ │ │ │ │ │ └── DraftsMenuProvider.kt
│ │ │ │ │ ├── finalization/
│ │ │ │ │ │ └── EditedFormFinalizationProcessor.kt
│ │ │ │ │ ├── formmap/
│ │ │ │ │ │ └── FormMapViewModel.kt
│ │ │ │ │ ├── matchexactly/
│ │ │ │ │ │ └── ServerFormsSynchronizer.java
│ │ │ │ │ └── metadata/
│ │ │ │ │ ├── FormMetadata.kt
│ │ │ │ │ └── FormMetadataParser.kt
│ │ │ │ ├── fragments/
│ │ │ │ │ ├── BarCodeScannerFragment.kt
│ │ │ │ │ ├── BarcodeWidgetScannerFragment.kt
│ │ │ │ │ ├── MediaLoadingFragment.java
│ │ │ │ │ ├── dialogs/
│ │ │ │ │ │ ├── FormsDownloadResultDialog.kt
│ │ │ │ │ │ ├── LocationProvidersDisabledDialog.java
│ │ │ │ │ │ ├── MovingBackwardsDialog.java
│ │ │ │ │ │ ├── RangePickerDialogFragment.kt
│ │ │ │ │ │ ├── RankingWidgetDialog.java
│ │ │ │ │ │ ├── ResetSettingsResultDialog.java
│ │ │ │ │ │ ├── SelectMinimalDialog.java
│ │ │ │ │ │ ├── SelectMultiMinimalDialog.java
│ │ │ │ │ │ ├── SelectOneMinimalDialog.java
│ │ │ │ │ │ └── SimpleDialog.java
│ │ │ │ │ └── viewmodels/
│ │ │ │ │ ├── RankingViewModel.java
│ │ │ │ │ └── SelectMinimalViewModel.java
│ │ │ │ ├── geo/
│ │ │ │ │ ├── MapConfiguratorProvider.java
│ │ │ │ │ └── MapFragmentFactoryImpl.kt
│ │ │ │ ├── injection/
│ │ │ │ │ ├── DaggerUtils.kt
│ │ │ │ │ └── config/
│ │ │ │ │ ├── AppDependencyComponent.kt
│ │ │ │ │ ├── AppDependencyModule.java
│ │ │ │ │ ├── CollectDrawDependencyModule.kt
│ │ │ │ │ ├── CollectEntitiesDependencyModule.kt
│ │ │ │ │ ├── CollectGeoDependencyModule.kt
│ │ │ │ │ ├── CollectGoogleMapsDependencyModule.kt
│ │ │ │ │ ├── CollectOsmDroidDependencyModule.kt
│ │ │ │ │ ├── CollectProjectsDependencyModule.kt
│ │ │ │ │ ├── CollectSelfieCameraDependencyModule.kt
│ │ │ │ │ └── ProjectDependencyModuleFactory.kt
│ │ │ │ ├── instancemanagement/
│ │ │ │ │ ├── FinalizeAllSnackbarPresenter.kt
│ │ │ │ │ ├── InstanceDeleter.kt
│ │ │ │ │ ├── InstanceDiskSynchronizer.java
│ │ │ │ │ ├── InstanceExt.kt
│ │ │ │ │ ├── InstanceListItemView.kt
│ │ │ │ │ ├── InstanceSubmitter.kt
│ │ │ │ │ ├── InstancesDataService.kt
│ │ │ │ │ ├── LocalInstancesUseCases.kt
│ │ │ │ │ ├── autosend/
│ │ │ │ │ │ ├── AutoSendSettingsProvider.kt
│ │ │ │ │ │ ├── FormExt.kt
│ │ │ │ │ │ └── InstanceAutoSendFetcher.kt
│ │ │ │ │ └── send/
│ │ │ │ │ ├── InstanceUploaderActivity.java
│ │ │ │ │ ├── InstanceUploaderListActivity.java
│ │ │ │ │ ├── ReadyToSendBanner.kt
│ │ │ │ │ └── ReadyToSendViewModel.kt
│ │ │ │ ├── itemsets/
│ │ │ │ │ └── FastExternalItemsetsRepository.kt
│ │ │ │ ├── javarosawrapper/
│ │ │ │ │ ├── FormController.kt
│ │ │ │ │ ├── FormControllerExt.kt
│ │ │ │ │ ├── FormDesignException.kt
│ │ │ │ │ ├── FormIndexUtils.java
│ │ │ │ │ ├── InstanceMetadata.java
│ │ │ │ │ ├── JavaRosaFormController.java
│ │ │ │ │ └── ValidationResult.kt
│ │ │ │ ├── listeners/
│ │ │ │ │ ├── AdvanceToNextListener.java
│ │ │ │ │ ├── DeleteInstancesListener.java
│ │ │ │ │ ├── DownloadFormsTaskListener.java
│ │ │ │ │ ├── FormListDownloaderListener.java
│ │ │ │ │ ├── FormLoaderListener.java
│ │ │ │ │ ├── InstanceUploaderListener.java
│ │ │ │ │ ├── Result.java
│ │ │ │ │ ├── SelectItemClickListener.java
│ │ │ │ │ ├── ThousandsSeparatorTextWatcher.java
│ │ │ │ │ └── WidgetValueChangedListener.java
│ │ │ │ ├── location/
│ │ │ │ │ └── client/
│ │ │ │ │ └── MaxAccuracyWithinTimeoutLocationClientWrapper.java
│ │ │ │ ├── logic/
│ │ │ │ │ ├── FileReference.java
│ │ │ │ │ ├── FileReferenceFactory.java
│ │ │ │ │ ├── ImmutableDisplayableQuestion.java
│ │ │ │ │ └── actions/
│ │ │ │ │ └── setgeopoint/
│ │ │ │ │ ├── CollectSetGeopointAction.java
│ │ │ │ │ └── CollectSetGeopointActionHandler.java
│ │ │ │ ├── mainmenu/
│ │ │ │ │ ├── CurrentProjectViewModel.kt
│ │ │ │ │ ├── MainMenuActivity.kt
│ │ │ │ │ ├── MainMenuButton.kt
│ │ │ │ │ ├── MainMenuFragment.kt
│ │ │ │ │ ├── MainMenuViewModel.kt
│ │ │ │ │ ├── MainMenuViewModelFactory.kt
│ │ │ │ │ ├── PermissionsDialogFragment.kt
│ │ │ │ │ ├── RequestPermissionsViewModel.kt
│ │ │ │ │ └── StartNewFormButton.kt
│ │ │ │ ├── notifications/
│ │ │ │ │ ├── NotificationManagerNotifier.kt
│ │ │ │ │ ├── NotificationUtils.kt
│ │ │ │ │ ├── Notifier.kt
│ │ │ │ │ └── builders/
│ │ │ │ │ ├── FormUpdatesAvailableNotificationBuilder.kt
│ │ │ │ │ ├── FormUpdatesDownloadedNotificationBuilder.kt
│ │ │ │ │ ├── FormsSubmissionNotificationBuilder.kt
│ │ │ │ │ ├── FormsSyncFailedNotificationBuilder.kt
│ │ │ │ │ └── FormsSyncStoppedNotificationBuilder.kt
│ │ │ │ ├── preferences/
│ │ │ │ │ ├── Defaults.kt
│ │ │ │ │ ├── PreferenceVisibilityHandler.kt
│ │ │ │ │ ├── ProjectPreferencesViewModel.kt
│ │ │ │ │ ├── ServerPreferencesAdder.java
│ │ │ │ │ ├── SettingsExt.kt
│ │ │ │ │ ├── dialogs/
│ │ │ │ │ │ ├── AdminPasswordDialogFragment.kt
│ │ │ │ │ │ ├── ChangeAdminPasswordDialog.kt
│ │ │ │ │ │ ├── DeleteProjectDialog.kt
│ │ │ │ │ │ ├── ResetDialogPreference.java
│ │ │ │ │ │ ├── ResetDialogPreferenceFragmentCompat.java
│ │ │ │ │ │ ├── ResetProgressDialog.kt
│ │ │ │ │ │ └── ServerAuthDialogFragment.java
│ │ │ │ │ ├── filters/
│ │ │ │ │ │ └── ControlCharacterFilter.java
│ │ │ │ │ ├── screens/
│ │ │ │ │ │ ├── AccessControlPreferencesFragment.kt
│ │ │ │ │ │ ├── BaseAdminPreferencesFragment.java
│ │ │ │ │ │ ├── BasePreferencesFragment.kt
│ │ │ │ │ │ ├── BaseProjectPreferencesFragment.java
│ │ │ │ │ │ ├── DevToolsPreferencesFragment.kt
│ │ │ │ │ │ ├── ExperimentalPreferencesFragment.java
│ │ │ │ │ │ ├── FormEntryAccessPreferencesFragment.kt
│ │ │ │ │ │ ├── FormManagementPreferencesFragment.java
│ │ │ │ │ │ ├── FormMetadataPreferencesFragment.java
│ │ │ │ │ │ ├── IdentityPreferencesFragment.kt
│ │ │ │ │ │ ├── MainMenuAccessPreferencesFragment.kt
│ │ │ │ │ │ ├── MapsPreferencesFragment.kt
│ │ │ │ │ │ ├── ProjectDisplayPreferencesFragment.kt
│ │ │ │ │ │ ├── ProjectManagementPreferencesFragment.kt
│ │ │ │ │ │ ├── ProjectPreferencesActivity.kt
│ │ │ │ │ │ ├── ProjectPreferencesFragment.kt
│ │ │ │ │ │ ├── ServerPreferencesFragment.java
│ │ │ │ │ │ ├── UserInterfacePreferencesFragment.java
│ │ │ │ │ │ └── UserSettingsAccessPreferencesFragment.java
│ │ │ │ │ ├── source/
│ │ │ │ │ │ ├── SettingsStore.kt
│ │ │ │ │ │ ├── SharedPreferencesSettings.kt
│ │ │ │ │ │ └── SharedPreferencesSettingsProvider.kt
│ │ │ │ │ └── utilities/
│ │ │ │ │ └── PreferencesUtils.java
│ │ │ │ ├── projects/
│ │ │ │ │ ├── DuplicateProjectConfirmationDialog.kt
│ │ │ │ │ ├── FileDebugLogger.kt
│ │ │ │ │ ├── ManualProjectCreatorDialog.kt
│ │ │ │ │ ├── ProjectCreatorImpl.kt
│ │ │ │ │ ├── ProjectDeleter.kt
│ │ │ │ │ ├── ProjectDependencyModule.kt
│ │ │ │ │ ├── ProjectIconView.kt
│ │ │ │ │ ├── ProjectListItemView.kt
│ │ │ │ │ ├── ProjectResetter.kt
│ │ │ │ │ ├── ProjectSettingsDialog.kt
│ │ │ │ │ ├── ProjectsDataService.kt
│ │ │ │ │ ├── QrCodeProjectCreatorDialog.kt
│ │ │ │ │ └── SettingsConnectionMatcherImpl.kt
│ │ │ │ ├── savepoints/
│ │ │ │ │ ├── SavepointTask.kt
│ │ │ │ │ └── SavepointUseCases.kt
│ │ │ │ ├── state/
│ │ │ │ │ └── DataKeys.kt
│ │ │ │ ├── storage/
│ │ │ │ │ ├── StoragePathProvider.kt
│ │ │ │ │ ├── StoragePaths.kt
│ │ │ │ │ └── StorageSubdirectory.java
│ │ │ │ ├── tasks/
│ │ │ │ │ ├── DownloadFormListTask.java
│ │ │ │ │ ├── DownloadFormsTask.java
│ │ │ │ │ ├── FormLoaderTask.java
│ │ │ │ │ ├── InstanceUploaderTask.java
│ │ │ │ │ ├── MediaLoadingTask.java
│ │ │ │ │ ├── ProgressNotifier.java
│ │ │ │ │ ├── SaveFormIndexTask.java
│ │ │ │ │ ├── SaveFormToDisk.java
│ │ │ │ │ └── SaveToDiskResult.java
│ │ │ │ ├── upload/
│ │ │ │ │ ├── FormUploadException.kt
│ │ │ │ │ ├── InstanceServerUploader.java
│ │ │ │ │ └── InstanceUploader.java
│ │ │ │ ├── utilities/
│ │ │ │ │ ├── ActionRegister.kt
│ │ │ │ │ ├── AdminPasswordProvider.java
│ │ │ │ │ ├── AndroidUserAgent.java
│ │ │ │ │ ├── AnimationUtils.java
│ │ │ │ │ ├── Appearances.kt
│ │ │ │ │ ├── ApplicationConstants.java
│ │ │ │ │ ├── ArrayUtils.java
│ │ │ │ │ ├── AuthDialogUtility.java
│ │ │ │ │ ├── CSVUtils.java
│ │ │ │ │ ├── ChangeLockProvider.kt
│ │ │ │ │ ├── CollectStrictMode.kt
│ │ │ │ │ ├── ContentUriHelper.kt
│ │ │ │ │ ├── ContentUriProvider.java
│ │ │ │ │ ├── ControllableLifecyleOwner.kt
│ │ │ │ │ ├── DialogUtils.java
│ │ │ │ │ ├── EncryptionUtils.java
│ │ │ │ │ ├── ExternalAppIntentProvider.java
│ │ │ │ │ ├── ExternalizableFormDefCache.java
│ │ │ │ │ ├── FileProvider.java
│ │ │ │ │ ├── FileUtils.java
│ │ │ │ │ ├── FormEntryPromptUtils.java
│ │ │ │ │ ├── FormNameUtils.java
│ │ │ │ │ ├── FormUtils.java
│ │ │ │ │ ├── FormsDownloadResultInterpreter.kt
│ │ │ │ │ ├── FormsRepositoryProvider.kt
│ │ │ │ │ ├── FormsUploadResultInterpreter.kt
│ │ │ │ │ ├── HtmlUtils.java
│ │ │ │ │ ├── ImageCompressionController.kt
│ │ │ │ │ ├── InstanceAutoDeleteChecker.kt
│ │ │ │ │ ├── InstanceUploaderUtils.java
│ │ │ │ │ ├── InstancesRepositoryProvider.kt
│ │ │ │ │ ├── LocaleHelper.kt
│ │ │ │ │ ├── MediaUtils.kt
│ │ │ │ │ ├── MyanmarDateUtils.java
│ │ │ │ │ ├── QuestionMediaManager.java
│ │ │ │ │ ├── RankingItemTouchHelperCallback.java
│ │ │ │ │ ├── ReplaceCallback.java
│ │ │ │ │ ├── ResponseMessageParser.java
│ │ │ │ │ ├── SavepointsRepositoryProvider.kt
│ │ │ │ │ ├── SelectOneWidgetUtils.java
│ │ │ │ │ ├── SoftKeyboardController.kt
│ │ │ │ │ ├── ThemeUtils.java
│ │ │ │ │ ├── UnderlyingValuesConcat.java
│ │ │ │ │ ├── ViewUtils.kt
│ │ │ │ │ ├── WebCredentialsUtils.java
│ │ │ │ │ └── ZipUtils.java
│ │ │ │ ├── version/
│ │ │ │ │ ├── VersionDescriptionProvider.java
│ │ │ │ │ └── VersionInformation.java
│ │ │ │ ├── views/
│ │ │ │ │ ├── ChoicesRecyclerView.java
│ │ │ │ │ ├── CustomNumberPicker.kt
│ │ │ │ │ ├── CustomWebView.java
│ │ │ │ │ ├── DayNightProgressDialog.java
│ │ │ │ │ ├── DecoratedBarcodeView.kt
│ │ │ │ │ ├── SlidingTabLayout.java
│ │ │ │ │ ├── SlidingTabStrip.java
│ │ │ │ │ ├── TrackingTouchSlider.kt
│ │ │ │ │ ├── TransparentProgressScreen.kt
│ │ │ │ │ ├── TwoItemMultipleChoiceView.java
│ │ │ │ │ └── WidgetAnswerText.kt
│ │ │ │ └── widgets/
│ │ │ │ ├── AnnotateWidget.java
│ │ │ │ ├── AudioWidget.java
│ │ │ │ ├── BaseImageWidget.java
│ │ │ │ ├── BearingWidget.java
│ │ │ │ ├── CounterWidget.kt
│ │ │ │ ├── DecimalWidget.java
│ │ │ │ ├── DrawWidget.java
│ │ │ │ ├── ExAudioWidget.java
│ │ │ │ ├── ExDecimalWidget.java
│ │ │ │ ├── ExImageWidget.java
│ │ │ │ ├── ExIntegerWidget.java
│ │ │ │ ├── ExStringWidget.java
│ │ │ │ ├── GeoPointMapWidget.java
│ │ │ │ ├── GeoPointWidget.java
│ │ │ │ ├── GeoShapeWidget.java
│ │ │ │ ├── GeoTraceWidget.java
│ │ │ │ ├── ImageWidget.java
│ │ │ │ ├── IntegerWidget.java
│ │ │ │ ├── MediaWidgetAnswerViewModel.kt
│ │ │ │ ├── OSMWidget.java
│ │ │ │ ├── PrinterWidget.kt
│ │ │ │ ├── QuestionWidget.java
│ │ │ │ ├── RatingWidget.java
│ │ │ │ ├── SignatureWidget.java
│ │ │ │ ├── StringNumberWidget.java
│ │ │ │ ├── StringWidget.java
│ │ │ │ ├── TextWidgetAnswer.kt
│ │ │ │ ├── TimedGridWidget.kt
│ │ │ │ ├── TriggerWidget.java
│ │ │ │ ├── UrlWidget.java
│ │ │ │ ├── WidgetAnswer.kt
│ │ │ │ ├── WidgetAnswerView.kt
│ │ │ │ ├── WidgetFactory.java
│ │ │ │ ├── WidgetIconButton.kt
│ │ │ │ ├── arbitraryfile/
│ │ │ │ │ ├── ArbitraryFileWidget.kt
│ │ │ │ │ ├── ArbitraryFileWidgetContent.kt
│ │ │ │ │ ├── ArbitraryFileWidgetDelegate.kt
│ │ │ │ │ ├── ExArbitraryFileWidget.kt
│ │ │ │ │ └── ExArbitraryFileWidgetContent.kt
│ │ │ │ ├── barcode/
│ │ │ │ │ ├── BarcodeWidget.kt
│ │ │ │ │ └── BarcodeWidgetContent.kt
│ │ │ │ ├── datetime/
│ │ │ │ │ ├── DatePickerDetails.java
│ │ │ │ │ ├── DateTimeUtils.java
│ │ │ │ │ ├── DateTimeWidget.java
│ │ │ │ │ ├── DateWidget.java
│ │ │ │ │ ├── TimeWidget.java
│ │ │ │ │ └── pickers/
│ │ │ │ │ ├── BikramSambatDatePickerDialog.java
│ │ │ │ │ ├── BuddhistDatePickerDialog.kt
│ │ │ │ │ ├── CopticDatePickerDialog.java
│ │ │ │ │ ├── CustomDatePickerDialog.java
│ │ │ │ │ ├── CustomTimePickerDialog.java
│ │ │ │ │ ├── EthiopianDatePickerDialog.java
│ │ │ │ │ ├── FixedDatePickerDialog.java
│ │ │ │ │ ├── IslamicDatePickerDialog.java
│ │ │ │ │ ├── MyanmarDatePickerDialog.java
│ │ │ │ │ └── PersianDatePickerDialog.java
│ │ │ │ ├── interfaces/
│ │ │ │ │ ├── FileWidget.java
│ │ │ │ │ ├── GeoDataRequester.kt
│ │ │ │ │ ├── MultiChoiceWidget.java
│ │ │ │ │ ├── Printer.kt
│ │ │ │ │ ├── SelectChoiceLoader.kt
│ │ │ │ │ ├── Widget.java
│ │ │ │ │ └── WidgetDataReceiver.kt
│ │ │ │ ├── items/
│ │ │ │ │ ├── BaseSelectListWidget.java
│ │ │ │ │ ├── ItemsWidgetUtils.kt
│ │ │ │ │ ├── LabelWidget.java
│ │ │ │ │ ├── LikertWidget.java
│ │ │ │ │ ├── ListMultiWidget.java
│ │ │ │ │ ├── ListWidget.java
│ │ │ │ │ ├── RankingWidget.java
│ │ │ │ │ ├── SelectImageMapWidget.java
│ │ │ │ │ ├── SelectMinimalWidget.java
│ │ │ │ │ ├── SelectMultiImageMapWidget.java
│ │ │ │ │ ├── SelectMultiMinimalWidget.java
│ │ │ │ │ ├── SelectMultiWidget.java
│ │ │ │ │ ├── SelectOneFromMapDialogFragment.kt
│ │ │ │ │ ├── SelectOneFromMapWidget.kt
│ │ │ │ │ ├── SelectOneImageMapWidget.java
│ │ │ │ │ ├── SelectOneMinimalWidget.java
│ │ │ │ │ └── SelectOneWidget.java
│ │ │ │ ├── range/
│ │ │ │ │ ├── RangeDecimalWidget.java
│ │ │ │ │ ├── RangeIntegerWidget.java
│ │ │ │ │ ├── RangePickerWidget.java
│ │ │ │ │ └── RangePickerWidgetUtils.kt
│ │ │ │ ├── utilities/
│ │ │ │ │ ├── ActivityGeoDataRequester.kt
│ │ │ │ │ ├── AdditionalAttributes.kt
│ │ │ │ │ ├── AudioFileRequester.java
│ │ │ │ │ ├── AudioRecorderRecordingStatusHandler.java
│ │ │ │ │ ├── BindAttributes.kt
│ │ │ │ │ ├── DateTimeWidgetUtils.java
│ │ │ │ │ ├── ExternalAppRecordingRequester.kt
│ │ │ │ │ ├── FileRequester.kt
│ │ │ │ │ ├── FormControllerWaitingForDataRegistry.java
│ │ │ │ │ ├── GeoPolyDialogFragment.kt
│ │ │ │ │ ├── GeoWidgetUtils.kt
│ │ │ │ │ ├── GetContentAudioFileRequester.kt
│ │ │ │ │ ├── ImageCaptureIntentCreator.kt
│ │ │ │ │ ├── InternalRecordingRequester.java
│ │ │ │ │ ├── QuestionFontSizeUtils.kt
│ │ │ │ │ ├── RangeWidgetUtils.java
│ │ │ │ │ ├── RecordingRequester.java
│ │ │ │ │ ├── RecordingRequesterProvider.java
│ │ │ │ │ ├── RecordingStatusHandler.java
│ │ │ │ │ ├── SearchQueryViewModel.java
│ │ │ │ │ ├── StringRequester.kt
│ │ │ │ │ ├── StringWidgetUtils.java
│ │ │ │ │ ├── ViewModelAudioPlayer.kt
│ │ │ │ │ ├── WaitingForDataRegistry.java
│ │ │ │ │ └── WidgetAnswerDialogFragment.kt
│ │ │ │ ├── video/
│ │ │ │ │ ├── ExVideoWidget.kt
│ │ │ │ │ ├── ExVideoWidgetContent.kt
│ │ │ │ │ ├── VideoWidget.kt
│ │ │ │ │ ├── VideoWidgetAnswer.kt
│ │ │ │ │ └── VideoWidgetContent.kt
│ │ │ │ ├── viewmodels/
│ │ │ │ │ ├── DateTimeViewModel.java
│ │ │ │ │ └── QuestionViewModel.kt
│ │ │ │ └── warnings/
│ │ │ │ └── SpacesInUnderlyingValuesWarning.java
│ │ │ └── utilities/
│ │ │ ├── Result.java
│ │ │ └── UserAgentProvider.java
│ │ └── res/
│ │ ├── anim/
│ │ │ ├── fade_in.xml
│ │ │ ├── fade_out.xml
│ │ │ ├── push_left_in.xml
│ │ │ ├── push_left_out.xml
│ │ │ ├── push_right_in.xml
│ │ │ └── push_right_out.xml
│ │ ├── drawable/
│ │ │ ├── counter_minus_button_background.xml
│ │ │ ├── counter_plus_button_background.xml
│ │ │ ├── counter_value_background.xml
│ │ │ ├── ic_access_time.xml
│ │ │ ├── ic_add_circle_24.xml
│ │ │ ├── ic_arrow_back.xml
│ │ │ ├── ic_arrow_drop_down.xml
│ │ │ ├── ic_arrow_up.xml
│ │ │ ├── ic_baseline_delete_72.xml
│ │ │ ├── ic_baseline_delete_outline_24.xml
│ │ │ ├── ic_baseline_download_72.xml
│ │ │ ├── ic_baseline_edit_72.xml
│ │ │ ├── ic_baseline_error_24.xml
│ │ │ ├── ic_baseline_help_outline_24.xml
│ │ │ ├── ic_baseline_inbox_72.xml
│ │ │ ├── ic_baseline_note_72.xml
│ │ │ ├── ic_baseline_notifications_24.xml
│ │ │ ├── ic_baseline_qr_code_scanner_24.xml
│ │ │ ├── ic_baseline_refresh_24.xml
│ │ │ ├── ic_baseline_refresh_error_24.xml
│ │ │ ├── ic_baseline_search_24.xml
│ │ │ ├── ic_baseline_send_72.xml
│ │ │ ├── ic_baseline_sort_24.xml
│ │ │ ├── ic_cancel.xml
│ │ │ ├── ic_chat_bubble_24dp.xml
│ │ │ ├── ic_check_circle_24.xml
│ │ │ ├── ic_download_24.xml
│ │ │ ├── ic_edit_24.xml
│ │ │ ├── ic_folder_open.xml
│ │ │ ├── ic_form_state_blank.xml
│ │ │ ├── ic_form_state_finalized.xml
│ │ │ ├── ic_form_state_saved.xml
│ │ │ ├── ic_form_state_submission_failed.xml
│ │ │ ├── ic_form_state_submitted.xml
│ │ │ ├── ic_information_outline.xml
│ │ │ ├── ic_map.xml
│ │ │ ├── ic_menu_share.xml
│ │ │ ├── ic_navigate_back.xml
│ │ │ ├── ic_navigate_forward.xml
│ │ │ ├── ic_ondemand_video_black_24dp.xml
│ │ │ ├── ic_outline_assignment_accent_24.xml
│ │ │ ├── ic_outline_cloud_accent_24.xml
│ │ │ ├── ic_outline_color_lens_accent_24.xml
│ │ │ ├── ic_outline_delete_accent_24.xml
│ │ │ ├── ic_outline_edit_24.xml
│ │ │ ├── ic_outline_face_accent_24.xml
│ │ │ ├── ic_outline_forum_24.xml
│ │ │ ├── ic_outline_info_small.xml
│ │ │ ├── ic_outline_lock_24.xml
│ │ │ ├── ic_outline_lock_accent_24.xml
│ │ │ ├── ic_outline_lock_open_24.xml
│ │ │ ├── ic_outline_map_accent_24.xml
│ │ │ ├── ic_outline_menu_accent_24.xml
│ │ │ ├── ic_outline_phonelink_setup_accent_24.xml
│ │ │ ├── ic_outline_qr_code_scanner_accent_24.xml
│ │ │ ├── ic_outline_rate_review_24.xml
│ │ │ ├── ic_outline_refresh_accent_24.xml
│ │ │ ├── ic_outline_settings_accent_24.xml
│ │ │ ├── ic_outline_settings_applications_accent_24.xml
│ │ │ ├── ic_outline_share_24.xml
│ │ │ ├── ic_outline_stars_24.xml
│ │ │ ├── ic_outline_vpn_key_accent_24.xml
│ │ │ ├── ic_outline_warning_accent_24.xml
│ │ │ ├── ic_outline_website_24.xml
│ │ │ ├── ic_pause_24dp.xml
│ │ │ ├── ic_person_24dp.xml
│ │ │ ├── ic_play_arrow_24dp.xml
│ │ │ ├── ic_repeat.xml
│ │ │ ├── ic_room_form_state_complete_24dp.xml
│ │ │ ├── ic_room_form_state_complete_48dp.xml
│ │ │ ├── ic_room_form_state_incomplete_24dp.xml
│ │ │ ├── ic_room_form_state_incomplete_48dp.xml
│ │ │ ├── ic_room_form_state_submission_failed_24dp.xml
│ │ │ ├── ic_room_form_state_submission_failed_48dp.xml
│ │ │ ├── ic_room_form_state_submitted_24dp.xml
│ │ │ ├── ic_room_form_state_submitted_48dp.xml
│ │ │ ├── ic_save_menu_24.xml
│ │ │ ├── ic_send_24.xml
│ │ │ ├── ic_sort.xml
│ │ │ ├── ic_sort_by_alpha.xml
│ │ │ ├── ic_sort_by_last_saved.xml
│ │ │ ├── ic_splash_screen_icon.xml
│ │ │ ├── ic_stop_black_24dp.xml
│ │ │ ├── ic_visibility.xml
│ │ │ ├── ic_volume_up_black_24dp.xml
│ │ │ ├── inset_divider_64dp.xml
│ │ │ ├── main_menu_button_background.xml
│ │ │ ├── odk_logo.xml
│ │ │ ├── pill_filled.xml
│ │ │ ├── pill_unfilled.xml
│ │ │ ├── project_icon_background.xml
│ │ │ ├── question_with_error_border.xml
│ │ │ ├── select_item_border.xml
│ │ │ └── start_new_form_button_background.xml
│ │ ├── drawable-ldrtl/
│ │ │ ├── counter_minus_button_background.xml
│ │ │ └── counter_plus_button_background.xml
│ │ ├── layout/
│ │ │ ├── about_item_layout.xml
│ │ │ ├── about_layout.xml
│ │ │ ├── activity_blank_form_list.xml
│ │ │ ├── activity_custom_scanner.xml
│ │ │ ├── activity_preferences_layout.xml
│ │ │ ├── add_repeat_dialog_layout.xml
│ │ │ ├── admin_password_dialog_layout.xml
│ │ │ ├── annotate_widget.xml
│ │ │ ├── audio_controller_layout.xml
│ │ │ ├── audio_player_layout.xml
│ │ │ ├── audio_recording_controller_fragment.xml
│ │ │ ├── audio_video_image_text_label.xml
│ │ │ ├── audio_widget_answer.xml
│ │ │ ├── background_audio_help_fragment_layout.xml
│ │ │ ├── bearing_widget_answer.xml
│ │ │ ├── blank_form_list_item.xml
│ │ │ ├── bottom_sheet.xml
│ │ │ ├── captioned_item.xml
│ │ │ ├── captioned_list_dialog.xml
│ │ │ ├── changes_reason_dialog.xml
│ │ │ ├── checkbox.xml
│ │ │ ├── circular_progress_indicator.xml
│ │ │ ├── counter_widget.xml
│ │ │ ├── current_project_menu_icon.xml
│ │ │ ├── custom_date_picker_dialog.xml
│ │ │ ├── date_time_widget_answer.xml
│ │ │ ├── date_widget_answer.xml
│ │ │ ├── delete_form_layout.xml
│ │ │ ├── delete_project_dialog_layout.xml
│ │ │ ├── draw_widget.xml
│ │ │ ├── ex_audio_widget_answer.xml
│ │ │ ├── ex_image_widget_answer.xml
│ │ │ ├── ex_string_question_type.xml
│ │ │ ├── first_launch_layout.xml
│ │ │ ├── form_chooser_list.xml
│ │ │ ├── form_chooser_list_item.xml
│ │ │ ├── form_chooser_list_item_icon.xml
│ │ │ ├── form_chooser_list_item_map_button.xml
│ │ │ ├── form_chooser_list_item_multiple_choice.xml
│ │ │ ├── form_chooser_list_item_text.xml
│ │ │ ├── form_download_list.xml
│ │ │ ├── form_entry.xml
│ │ │ ├── form_entry_end.xml
│ │ │ ├── form_hierarchy_layout.xml
│ │ │ ├── form_map_activity.xml
│ │ │ ├── fragment_scan.xml
│ │ │ ├── geopoint_question.xml
│ │ │ ├── geoshape_question.xml
│ │ │ ├── geotrace_question.xml
│ │ │ ├── google_drive_deprecation_banner.xml
│ │ │ ├── help_layout.xml
│ │ │ ├── hierarchy_group_item.xml
│ │ │ ├── hierarchy_host_layout.xml
│ │ │ ├── hierarchy_question_item.xml
│ │ │ ├── hierarchy_repeatable_group_instance_item.xml
│ │ │ ├── hierarchy_repeatable_group_item.xml
│ │ │ ├── identify_user_dialog.xml
│ │ │ ├── image_widget.xml
│ │ │ ├── instance_uploader_list.xml
│ │ │ ├── label_widget.xml
│ │ │ ├── launch_intent_button_layout.xml
│ │ │ ├── main_menu.xml
│ │ │ ├── main_menu_activity.xml
│ │ │ ├── main_menu_button.xml
│ │ │ ├── manual_project_creator_dialog_layout.xml
│ │ │ ├── map_button.xml
│ │ │ ├── no_buttons_item_layout.xml
│ │ │ ├── number_picker_dialog.xml
│ │ │ ├── odk_view.xml
│ │ │ ├── osm_widget_answer.xml
│ │ │ ├── password_dialog_layout.xml
│ │ │ ├── permissions_dialog_layout.xml
│ │ │ ├── printer_widget.xml
│ │ │ ├── progress_bar_view.xml
│ │ │ ├── project_icon_view.xml
│ │ │ ├── project_list_item.xml
│ │ │ ├── project_settings_dialog_layout.xml
│ │ │ ├── qr_code_project_creator_dialog_layout.xml
│ │ │ ├── question_error_layout.xml
│ │ │ ├── question_widget.xml
│ │ │ ├── quit_form_dialog_layout.xml
│ │ │ ├── range_picker_widget_answer.xml
│ │ │ ├── range_widget_horizontal.xml
│ │ │ ├── range_widget_vertical.xml
│ │ │ ├── ranking_item.xml
│ │ │ ├── ranking_widget.xml
│ │ │ ├── rating_widget_answer.xml
│ │ │ ├── ready_to_send_banner.xml
│ │ │ ├── reset_dialog_layout.xml
│ │ │ ├── select_image_map_widget_answer.xml
│ │ │ ├── select_list_widget_answer.xml
│ │ │ ├── select_minimal_dialog_layout.xml
│ │ │ ├── select_minimal_widget_answer.xml
│ │ │ ├── select_multi_item.xml
│ │ │ ├── select_one_from_map_widget_answer.xml
│ │ │ ├── select_one_item.xml
│ │ │ ├── server_auth_dialog.xml
│ │ │ ├── show_qrcode_fragment.xml
│ │ │ ├── signature_widget.xml
│ │ │ ├── sort_item_layout.xml
│ │ │ ├── start_new_from_button.xml
│ │ │ ├── tabs_layout.xml
│ │ │ ├── time_widget_answer.xml
│ │ │ ├── transparent_progress_screen.xml
│ │ │ ├── trigger_widget_answer.xml
│ │ │ ├── url_widget_answer.xml
│ │ │ ├── waveform_layout.xml
│ │ │ ├── widget_answer_dialog_layout.xml
│ │ │ └── widget_answer_text.xml
│ │ ├── menu/
│ │ │ ├── blank_form_list_menu.xml
│ │ │ ├── changes_reason_dialog.xml
│ │ │ ├── drafts.xml
│ │ │ ├── form_hierarchy_menu.xml
│ │ │ ├── form_menu.xml
│ │ │ ├── instance_uploader_menu.xml
│ │ │ ├── main_menu.xml
│ │ │ ├── project_preferences_menu.xml
│ │ │ ├── qr_code_scan_menu.xml
│ │ │ ├── saved_form_list_menu.xml
│ │ │ └── select_minimal_dialog_menu.xml
│ │ ├── navigation/
│ │ │ └── form_entry.xml
│ │ ├── raw/
│ │ │ ├── isrgrootx1.pem
│ │ │ └── keep.xml
│ │ ├── values/
│ │ │ ├── arrays.xml
│ │ │ ├── attrs.xml
│ │ │ ├── buttons.xml
│ │ │ ├── colors.xml
│ │ │ ├── date_time_pickers.xml
│ │ │ ├── dimens.xml
│ │ │ ├── form_entry_activity_theme.xml
│ │ │ ├── form_state_colors.xml
│ │ │ ├── leak_canary_config.xml
│ │ │ ├── material_colors.xml
│ │ │ ├── screen_names.xml
│ │ │ ├── settings_theme.xml
│ │ │ ├── shape.xml
│ │ │ ├── styles.xml
│ │ │ ├── theme.xml
│ │ │ └── typography.xml
│ │ ├── values-night/
│ │ │ └── material_colors.xml
│ │ └── xml/
│ │ ├── access_control_preferences.xml
│ │ ├── dev_tools_preferences.xml
│ │ ├── experimental_preferences.xml
│ │ ├── form_entry_access_preferences.xml
│ │ ├── form_management_preferences.xml
│ │ ├── form_metadata_preferences.xml
│ │ ├── identity_preferences.xml
│ │ ├── main_menu_access_preferences.xml
│ │ ├── maps_preferences.xml
│ │ ├── network_security_config.xml
│ │ ├── odk_server_preferences.xml
│ │ ├── project_display_preferences.xml
│ │ ├── project_management_preferences.xml
│ │ ├── project_preferences.xml
│ │ ├── provider_paths.xml
│ │ ├── server_preferences.xml
│ │ ├── user_interface_preferences.xml
│ │ └── user_settings_access_preferences.xml
│ ├── release/
│ │ └── google-services.json
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ ├── android/
│ │ │ ├── TestSettingsProvider.kt
│ │ │ ├── activities/
│ │ │ │ ├── CrashHandlerActivityTest.kt
│ │ │ │ ├── FirstLaunchActivityTest.kt
│ │ │ │ ├── FormFillingActivityTest.kt
│ │ │ │ └── FormHierarchyFragmentHostActivityTest.kt
│ │ │ ├── application/
│ │ │ │ ├── CollectSettingsChangeHandlerTest.kt
│ │ │ │ ├── RobolectricApplication.java
│ │ │ │ └── initialization/
│ │ │ │ ├── AnalyticsInitializerTest.kt
│ │ │ │ ├── CachedFormsCleanerTest.kt
│ │ │ │ ├── ExistingSettingsMigratorTest.kt
│ │ │ │ ├── GoogleDriveProjectsDeleterTest.kt
│ │ │ │ ├── SavepointsImporterTest.kt
│ │ │ │ ├── ScheduledWorkUpgradeTest.kt
│ │ │ │ └── upgrade/
│ │ │ │ └── BeforeProjectsInstallDetectorTest.kt
│ │ │ ├── audio/
│ │ │ │ ├── AudioButtonTest.java
│ │ │ │ ├── AudioControllerViewTest.java
│ │ │ │ ├── AudioRecordingControllerFragmentTest.java
│ │ │ │ ├── AudioRecordingFormErrorDialogFragmentTest.java
│ │ │ │ └── BackgroundAudioHelpDialogFragmentTest.java
│ │ │ ├── backgroundwork/
│ │ │ │ ├── AutoUpdateTaskSpecTest.kt
│ │ │ │ ├── FormUpdateAndInstanceSubmitSchedulerTest.kt
│ │ │ │ ├── SendFormsTaskSpecTest.kt
│ │ │ │ └── SyncFormsTaskSpecTest.kt
│ │ │ ├── configure/
│ │ │ │ └── qr/
│ │ │ │ ├── QRCodeActivityResultDelegateTest.kt
│ │ │ │ ├── QRCodeMenuProviderTest.kt
│ │ │ │ ├── QRCodeScannerFragmentTest.kt
│ │ │ │ └── QRCodeViewModelTest.kt
│ │ │ ├── database/
│ │ │ │ ├── DatabaseFormsRepositoryTest.java
│ │ │ │ ├── DatabaseInstancesRepositoryTest.kt
│ │ │ │ ├── DatabaseSavepointsRepositoryTest.kt
│ │ │ │ ├── FormDatabaseMigratorTest.java
│ │ │ │ ├── InstanceDatabaseMigratorTest.kt
│ │ │ │ └── entities/
│ │ │ │ └── EntitiesDatabaseMigratorTest.kt
│ │ │ ├── dependencies/
│ │ │ │ ├── BikramSambatTest.java
│ │ │ │ └── PersianCalendarTest.java
│ │ │ ├── dynamicpreload/
│ │ │ │ ├── DynamicPreloadExtraTest.kt
│ │ │ │ ├── DynamicPreloadParseProcessorTest.kt
│ │ │ │ ├── ExternalDataReaderTest.java
│ │ │ │ ├── ExternalDataUseCasesTest.kt
│ │ │ │ └── ExternalDataUtilTest.java
│ │ │ ├── entities/
│ │ │ │ ├── DatabaseEntitiesRepositoryTest.kt
│ │ │ │ ├── EntitiesRepositoryTest.kt
│ │ │ │ ├── InMemEntitiesRepositoryTest.kt
│ │ │ │ └── support/
│ │ │ │ └── EntitySameAsMatcher.kt
│ │ │ ├── external/
│ │ │ │ ├── FormUriActivityTest.kt
│ │ │ │ ├── FormsProviderTest.java
│ │ │ │ └── InstanceProviderTest.java
│ │ │ ├── fakes/
│ │ │ │ └── FakePermissionsProvider.kt
│ │ │ ├── formentry/
│ │ │ │ ├── AppStateFormSessionRepositoryTest.kt
│ │ │ │ ├── AudioVideoImageTextLabelTest.java
│ │ │ │ ├── AudioVideoImageTextLabelVisibilityTest.kt
│ │ │ │ ├── BackgroundAudioPermissionDialogFragmentTest.java
│ │ │ │ ├── BackgroundAudioViewModelTest.java
│ │ │ │ ├── FormEndTest.kt
│ │ │ │ ├── FormEndViewModelTest.kt
│ │ │ │ ├── FormEntryMenuProviderTest.kt
│ │ │ │ ├── FormEntryUseCasesTest.kt
│ │ │ │ ├── FormEntryViewModelTest.java
│ │ │ │ ├── FormLoadingDialogFragmentTest.java
│ │ │ │ ├── FormSessionRepositoryTest.kt
│ │ │ │ ├── InMemoryFormSessionRepositoryTest.kt
│ │ │ │ ├── PrinterWidgetViewModelTest.kt
│ │ │ │ ├── QuitFormDialogTest.kt
│ │ │ │ ├── RecordingHandlerTest.java
│ │ │ │ ├── RefreshFormListDialogFragmentTest.java
│ │ │ │ ├── SaveFormProgressDialogFragmentTest.java
│ │ │ │ ├── audit/
│ │ │ │ │ ├── AsyncTaskAuditEventWriterTest.java
│ │ │ │ │ ├── AuditConfigTest.java
│ │ │ │ │ ├── AuditEventCSVLineTest.java
│ │ │ │ │ ├── AuditEventLoggerTest.java
│ │ │ │ │ ├── AuditEventTest.java
│ │ │ │ │ ├── FormSaveViewModelTest.java
│ │ │ │ │ └── IdentityPromptViewModelTest.java
│ │ │ │ ├── backgroundlocation/
│ │ │ │ │ └── BackgroundLocationManagerTest.java
│ │ │ │ ├── repeats/
│ │ │ │ │ └── DeleteRepeatDialogFragmentTest.java
│ │ │ │ └── support/
│ │ │ │ └── InMemFormSessionRepository.kt
│ │ │ ├── formhierarchy/
│ │ │ │ ├── HierarchyListItemViewTest.kt
│ │ │ │ └── QuestionAnswerProcessorTest.kt
│ │ │ ├── formlists/
│ │ │ │ ├── DeleteBlankFormFragmentTest.kt
│ │ │ │ ├── blankformlist/
│ │ │ │ │ ├── BlankFormListItemTest.kt
│ │ │ │ │ ├── BlankFormListItemViewTest.kt
│ │ │ │ │ ├── BlankFormListMenuProviderTest.kt
│ │ │ │ │ └── BlankFormListViewModelTest.kt
│ │ │ │ └── savedformlist/
│ │ │ │ ├── DeleteSavedFormFragmentTest.kt
│ │ │ │ ├── SavedFormListListMenuProviderTest.kt
│ │ │ │ └── SavedFormListViewModelTest.kt
│ │ │ ├── formmanagement/
│ │ │ │ ├── DownloadMediaFilesServerFormUseCasesTest.kt
│ │ │ │ ├── DownloadUpdatesServerFormUseCasesTest.kt
│ │ │ │ ├── FetchFormDetailsServerFormUseCasesTest.kt
│ │ │ │ ├── FilterFormsToAddTest.kt
│ │ │ │ ├── FormFillingIntentFactoryTest.kt
│ │ │ │ ├── FormSourceExceptionMapperTest.kt
│ │ │ │ ├── FormsDataServiceTest.kt
│ │ │ │ ├── LocalFormUseCasesTest.java
│ │ │ │ ├── ServerFormsSynchronizerTest.java
│ │ │ │ ├── ShouldAddFormFileTest.java
│ │ │ │ ├── download/
│ │ │ │ │ ├── FormDownloadExceptionMapperTest.kt
│ │ │ │ │ └── ServerFormDownloaderTest.java
│ │ │ │ ├── drafts/
│ │ │ │ │ └── DraftsMenuProviderTest.kt
│ │ │ │ ├── formmap/
│ │ │ │ │ └── FormMapViewModelTest.kt
│ │ │ │ └── metadata/
│ │ │ │ └── FormMetadataParserTest.kt
│ │ │ ├── fragments/
│ │ │ │ ├── dialogs/
│ │ │ │ │ ├── FormsDownloadResultDialogTest.kt
│ │ │ │ │ ├── RangePickerDialogFragmentTest.kt
│ │ │ │ │ ├── SelectMinimalDialogTest.java
│ │ │ │ │ ├── SelectMultiMinimalDialogTest.java
│ │ │ │ │ └── SelectOneMinimalDialogTest.java
│ │ │ │ └── support/
│ │ │ │ └── DialogFragmentHelpers.java
│ │ │ ├── geo/
│ │ │ │ └── MapFragmentFactoryImplTest.kt
│ │ │ ├── instancemanagement/
│ │ │ │ ├── InstanceDeleterTest.kt
│ │ │ │ ├── InstanceExtKtTest.kt
│ │ │ │ ├── InstanceListItemViewTest.kt
│ │ │ │ ├── InstancesDataServiceTest.kt
│ │ │ │ ├── LocalInstancesUseCasesTest.kt
│ │ │ │ ├── autosend/
│ │ │ │ │ ├── AutoSendSettingsProviderTest.kt
│ │ │ │ │ ├── FormExtTest.kt
│ │ │ │ │ └── InstanceAutoSendFetcherTest.kt
│ │ │ │ └── send/
│ │ │ │ ├── ReadyToSendBannerTest.kt
│ │ │ │ └── ReadyToSendViewModelTest.kt
│ │ │ ├── javarosawrapper/
│ │ │ │ ├── FakeFormController.java
│ │ │ │ └── FormControllerTest.java
│ │ │ ├── location/
│ │ │ │ └── client/
│ │ │ │ ├── FakeLocationClient.java
│ │ │ │ └── MaxAccuracyWithinTimeoutLocationClientWrapperTest.java
│ │ │ ├── mainmenu/
│ │ │ │ ├── CurrentProjectViewModelTest.kt
│ │ │ │ ├── MainMenuActivityTest.kt
│ │ │ │ ├── MainMenuButtonTest.kt
│ │ │ │ ├── MainMenuViewModelTest.kt
│ │ │ │ ├── PermissionsDialogFragmentTest.kt
│ │ │ │ └── RequestPermissionsViewModelTest.kt
│ │ │ ├── notifications/
│ │ │ │ └── NotificationManagerNotifierTest.kt
│ │ │ ├── preferences/
│ │ │ │ ├── AppConfigurationGeneratorTest.kt
│ │ │ │ ├── ProjectPreferencesViewModelTest.kt
│ │ │ │ ├── ServerPreferencesAdderTest.java
│ │ │ │ ├── dialogs/
│ │ │ │ │ ├── AdminPasswordDialogFragmentTest.kt
│ │ │ │ │ ├── ChangeAdminPasswordDialogTest.kt
│ │ │ │ │ ├── DeleteProjectDialogTest.kt
│ │ │ │ │ ├── ResetProgressDialogTest.kt
│ │ │ │ │ └── ServerAuthDialogFragmentTest.java
│ │ │ │ ├── screens/
│ │ │ │ │ ├── FormEntryAccessPreferencesFragmentTest.kt
│ │ │ │ │ ├── FormManagementPreferencesFragmentTest.kt
│ │ │ │ │ ├── FormMetadataPreferencesFragmentTest.kt
│ │ │ │ │ ├── IdentityPreferencesFragmentTest.kt
│ │ │ │ │ ├── MainMenuAccessPreferencesTest.kt
│ │ │ │ │ ├── MapsPreferencesFragmentTest.kt
│ │ │ │ │ ├── ProjectDisplayPreferencesFragmentTest.kt
│ │ │ │ │ ├── ProjectPreferencesFragmentTest.kt
│ │ │ │ │ └── UserInterfacePreferencesFragmentTest.kt
│ │ │ │ └── source/
│ │ │ │ ├── SettingsStoreTest.kt
│ │ │ │ ├── SharedPreferencesSettingsProviderTest.kt
│ │ │ │ └── SharedPreferencesSettingsTest.kt
│ │ │ ├── projects/
│ │ │ │ ├── ExistingProjectMigratorTest.kt
│ │ │ │ ├── ManualProjectCreatorDialogTest.kt
│ │ │ │ ├── ProjectCreatorImplTest.kt
│ │ │ │ ├── ProjectDeleterTest.kt
│ │ │ │ ├── ProjectIconViewTest.kt
│ │ │ │ ├── ProjectListItemViewTest.kt
│ │ │ │ ├── ProjectResetterTest.kt
│ │ │ │ ├── ProjectSettingsDialogTest.kt
│ │ │ │ ├── ProjectsDataServiceTest.kt
│ │ │ │ ├── QrCodeProjectCreatorDialogTest.kt
│ │ │ │ └── SettingsConnectionMatcherImplTest.kt
│ │ │ ├── savepoints/
│ │ │ │ └── SavepointUseCasesTest.kt
│ │ │ ├── storage/
│ │ │ │ └── StoragePathProviderTest.kt
│ │ │ ├── support/
│ │ │ │ ├── CollectHelpers.java
│ │ │ │ ├── Matchers.kt
│ │ │ │ ├── MockFormEntryPromptBuilder.java
│ │ │ │ ├── SwipableParentActivity.kt
│ │ │ │ └── WidgetTestActivity.kt
│ │ │ ├── tasks/
│ │ │ │ └── SaveFormIndexTaskTest.java
│ │ │ ├── utilities/
│ │ │ │ ├── AdminPasswordProviderTest.java
│ │ │ │ ├── AppearancesTest.kt
│ │ │ │ ├── ArrayUtilsTest.java
│ │ │ │ ├── CSVUtilsTest.java
│ │ │ │ ├── ChangeLockProviderTest.kt
│ │ │ │ ├── ExternalAppIntentProviderTest.kt
│ │ │ │ ├── ExternalAppUtilsTest.java
│ │ │ │ ├── FileUtilsTest.java
│ │ │ │ ├── FormNameUtilsTest.java
│ │ │ │ ├── FormsDownloadResultInterpreterTest.kt
│ │ │ │ ├── FormsRepositoryProviderTest.kt
│ │ │ │ ├── FormsUploadResultInterpreterTest.kt
│ │ │ │ ├── HtmlUtilsTest.kt
│ │ │ │ ├── ImageCompressionControllerTest.kt
│ │ │ │ ├── InstanceAutoDeleteCheckerTest.kt
│ │ │ │ ├── InstanceUploaderUtilsTest.java
│ │ │ │ ├── InstancesRepositoryProviderTest.kt
│ │ │ │ ├── MediaUtilsTest.kt
│ │ │ │ ├── MyanmarDateUtilsTest.java
│ │ │ │ ├── QuestionFontSizeUtilsTest.java
│ │ │ │ ├── StubFormController.kt
│ │ │ │ └── WebCredentialsUtilsTest.java
│ │ │ ├── version/
│ │ │ │ └── VersionInformationTest.java
│ │ │ ├── views/
│ │ │ │ ├── ChoicesRecyclerViewTest.java
│ │ │ │ ├── TrackingTouchSliderTest.java
│ │ │ │ └── helpers/
│ │ │ │ └── PromptAutoplayerTest.java
│ │ │ └── widgets/
│ │ │ ├── AnnotateWidgetTest.java
│ │ │ ├── ArbitraryFileWidgetTest.kt
│ │ │ ├── AudioWidgetTest.java
│ │ │ ├── BarcodeWidgetTest.kt
│ │ │ ├── BearingWidgetTest.java
│ │ │ ├── CounterWidgetTest.kt
│ │ │ ├── DecimalWidgetTest.java
│ │ │ ├── DrawWidgetTest.java
│ │ │ ├── ExArbitraryFileWidgetTest.kt
│ │ │ ├── ExAudioWidgetTest.java
│ │ │ ├── ExDecimalWidgetTest.java
│ │ │ ├── ExImageWidgetTest.java
│ │ │ ├── ExIntegerWidgetTest.java
│ │ │ ├── ExStringWidgetTest.kt
│ │ │ ├── ExVideoWidgetTest.kt
│ │ │ ├── GeoPointMapWidgetTest.java
│ │ │ ├── GeoPointWidgetTest.java
│ │ │ ├── GeoShapeWidgetTest.java
│ │ │ ├── GeoTraceWidgetTest.java
│ │ │ ├── ImageWidgetTest.java
│ │ │ ├── IntegerWidgetTest.java
│ │ │ ├── OSMWidgetTest.java
│ │ │ ├── PrinterWidgetTest.kt
│ │ │ ├── QuestionWidgetTest.java
│ │ │ ├── RatingWidgetTest.java
│ │ │ ├── SignatureWidgetTest.java
│ │ │ ├── StringNumberWidgetTest.java
│ │ │ ├── StringWidgetTest.kt
│ │ │ ├── TriggerWidgetTest.java
│ │ │ ├── UrlWidgetTest.java
│ │ │ ├── VideoWidgetTest.kt
│ │ │ ├── WidgetFactoryTest.kt
│ │ │ ├── base/
│ │ │ │ ├── BinaryWidgetTest.java
│ │ │ │ ├── FileWidgetTest.java
│ │ │ │ ├── GeneralExStringWidgetTest.java
│ │ │ │ ├── GeneralSelectMultiWidgetTest.java
│ │ │ │ ├── GeneralSelectOneWidgetTest.java
│ │ │ │ ├── GeneralStringWidgetTest.java
│ │ │ │ ├── QuestionWidgetTest.java
│ │ │ │ ├── SelectWidgetTest.java
│ │ │ │ └── WidgetTest.java
│ │ │ ├── datetime/
│ │ │ │ ├── DateTimeUtilsTest.java
│ │ │ │ ├── DateTimeWidgetTest.java
│ │ │ │ ├── DateTimeWidgetUtilsTest.java
│ │ │ │ ├── DateWidgetTest.java
│ │ │ │ ├── DaylightSavingTest.java
│ │ │ │ ├── TimeWidgetTest.java
│ │ │ │ └── pickers/
│ │ │ │ ├── BikramSambatDatePickerDialogTest.java
│ │ │ │ ├── BuddhistDatePickerDialogTest.kt
│ │ │ │ ├── CopticDatePickerDialogTest.java
│ │ │ │ ├── EthiopianDatePickerDialogTest.java
│ │ │ │ ├── IslamicDatePickerDialogTest.java
│ │ │ │ ├── MyanmarDatePickerDialogTest.java
│ │ │ │ └── PersianDatePickerDialogTest.java
│ │ │ ├── items/
│ │ │ │ ├── LikertWidgetTest.java
│ │ │ │ ├── ListMultiWidgetTest.java
│ │ │ │ ├── ListWidgetTest.java
│ │ │ │ ├── RankingWidgetTest.java
│ │ │ │ ├── SelectChoicesMapDataTest.kt
│ │ │ │ ├── SelectImageMapWidgetTest.java
│ │ │ │ ├── SelectMultiImageMapWidgetTest.java
│ │ │ │ ├── SelectMultiMinimalWidgetTest.java
│ │ │ │ ├── SelectMultiWidgetTest.java
│ │ │ │ ├── SelectOneFromMapDialogFragmentTest.kt
│ │ │ │ ├── SelectOneFromMapWidgetTest.kt
│ │ │ │ ├── SelectOneImageMapWidgetTest.java
│ │ │ │ ├── SelectOneMinimalWidgetTest.java
│ │ │ │ └── SelectOneWidgetTest.java
│ │ │ ├── range/
│ │ │ │ ├── RangeDecimalWidgetTest.java
│ │ │ │ ├── RangeIntegerWidgetTest.java
│ │ │ │ ├── RangePickerWidgetTest.kt
│ │ │ │ └── RangePickerWidgetUtilsTest.kt
│ │ │ ├── support/
│ │ │ │ ├── FakeQuestionMediaManager.java
│ │ │ │ ├── FakeWaitingForDataRegistry.java
│ │ │ │ ├── FormElementFixtures.kt
│ │ │ │ ├── FormEntryPromptSelectChoiceLoader.kt
│ │ │ │ ├── GeoWidgetHelpers.java
│ │ │ │ ├── NoOpMapFragment.kt
│ │ │ │ ├── QuestionWidgetHelpers.java
│ │ │ │ └── SynchronousImageLoader.kt
│ │ │ ├── utilities/
│ │ │ │ ├── ActivityGeoDataRequesterTest.java
│ │ │ │ ├── AudioRecorderRecordingStatusHandlerTest.java
│ │ │ │ ├── ExternalAppRecordingRequesterTest.kt
│ │ │ │ ├── FileRequesterImplTest.kt
│ │ │ │ ├── GeoPolyDialogFragmentTest.kt
│ │ │ │ ├── GeoWidgetUtilsTest.kt
│ │ │ │ ├── GetContentAudioFileRequesterTest.kt
│ │ │ │ ├── InternalRecordingRequesterTest.java
│ │ │ │ ├── RangeWidgetUtilsTest.java
│ │ │ │ ├── RecordingRequesterProviderTest.java
│ │ │ │ ├── StringRequesterImplTest.kt
│ │ │ │ └── StringWidgetUtilsTest.java
│ │ │ ├── viewmodels/
│ │ │ │ ├── DateTimeViewModelTest.java
│ │ │ │ └── QuestionViewModelTest.kt
│ │ │ └── warnings/
│ │ │ ├── SpacesInUnderlyingValuesTest.java
│ │ │ └── SpacesInUnderlyingValuesWarningTest.java
│ │ └── geo/
│ │ └── javarosa/
│ │ └── IntersectsFunctionHandlerTest.kt
│ └── resources/
│ ├── forms/
│ │ └── simple-search-external-csv.xml
│ ├── media/
│ │ └── simple-search-external-csv-fruits.csv
│ └── robolectric.properties
├── config/
│ ├── checkstyle.xml
│ ├── lint.xml
│ ├── pmd-ruleset.xml
│ └── quality.gradle
├── crash-handler/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── crashhandler/
│ │ │ ├── CrashHandler.kt
│ │ │ ├── CrashView.kt
│ │ │ └── MockCrashView.kt
│ │ └── res/
│ │ └── layout/
│ │ └── crash_layout.xml
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── crashhandler/
│ └── CrashHandlerTest.kt
├── create-release.sh
├── db/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── db/
│ │ └── sqlite/
│ │ ├── AltDatabasePathContext.kt
│ │ ├── CursorExt.kt
│ │ ├── CustomSQLiteQueryBuilder.java
│ │ ├── CustomSQLiteQueryExecutor.java
│ │ ├── DatabaseConnection.kt
│ │ ├── DatabaseMigrator.java
│ │ ├── MigrationListDatabaseMigrator.kt
│ │ ├── RowNumbers.kt
│ │ ├── SQLiteColumns.kt
│ │ ├── SQLiteDatabaseExt.kt
│ │ ├── SQLiteUtils.java
│ │ ├── SqlQuery.kt
│ │ └── SynchronizedDatabaseConnection.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── db/
│ └── sqlite/
│ ├── CustomSQLiteQueryBuilderTest.java
│ ├── DatabaseConnectionTest.kt
│ ├── RowNumbersTest.kt
│ ├── SQLiteUtilsTest.java
│ ├── SqlQueryTest.kt
│ ├── SqliteDatabaseExtTest.kt
│ └── support/
│ └── NoopMigrator.kt
├── debug.keystore
├── docs/
│ ├── ANALYTICS-QUESTIONS.md
│ ├── CODE-GUIDELINES.md
│ ├── CONTRIBUTING.md
│ ├── STATE.md
│ ├── TEST-GUIDELINES.md
│ ├── WIDGETS.md
│ └── WINDOWS-DEV-SETUP.md
├── download-robolectric-deps.sh
├── draw/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── draw/
│ │ │ ├── DaggerSetup.kt
│ │ │ ├── DrawActivity.java
│ │ │ ├── DrawView.kt
│ │ │ ├── DrawViewModel.kt
│ │ │ ├── PenColorPickerDialog.kt
│ │ │ ├── PenColorPickerViewModel.kt
│ │ │ ├── QuitDrawingDialog.kt
│ │ │ └── RobolectricApplication.kt
│ │ └── res/
│ │ └── layout/
│ │ ├── draw_layout.xml
│ │ └── quit_drawing_dialog_layout.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── draw/
│ │ ├── DrawActivityTest.kt
│ │ ├── PenColorPickerDialogTest.kt
│ │ └── PenColorPickerViewModelTest.kt
│ └── resources/
│ └── robolectric.properties
├── entities/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── entities/
│ │ │ ├── DaggerSetup.kt
│ │ │ ├── LocalEntityUseCases.kt
│ │ │ ├── browser/
│ │ │ │ ├── EntitiesFragment.kt
│ │ │ │ ├── EntitiesViewModel.kt
│ │ │ │ ├── EntityBrowserActivity.kt
│ │ │ │ ├── EntityItem.kt
│ │ │ │ └── EntityListsFragment.kt
│ │ │ ├── javarosa/
│ │ │ │ ├── filter/
│ │ │ │ │ ├── LocalEntitiesFilterStrategy.kt
│ │ │ │ │ └── PullDataFunctionHandler.kt
│ │ │ │ ├── finalization/
│ │ │ │ │ ├── EntitiesExtra.kt
│ │ │ │ │ ├── EntityFormFinalizationProcessor.kt
│ │ │ │ │ ├── FormEntity.kt
│ │ │ │ │ └── InvalidEntity.kt
│ │ │ │ ├── intance/
│ │ │ │ │ ├── LocalEntitiesExternalInstanceParserFactory.kt
│ │ │ │ │ ├── LocalEntitiesInstanceAdapter.kt
│ │ │ │ │ └── LocalEntitiesInstanceProvider.kt
│ │ │ │ ├── parse/
│ │ │ │ │ ├── EntityFormExtra.kt
│ │ │ │ │ ├── EntityFormParseProcessor.kt
│ │ │ │ │ ├── EntitySchema.kt
│ │ │ │ │ ├── EntityXFormParserFactory.kt
│ │ │ │ │ ├── SaveTo.kt
│ │ │ │ │ ├── StringExt.kt
│ │ │ │ │ └── XPathExpressionExt.kt
│ │ │ │ └── spec/
│ │ │ │ ├── EntityAction.kt
│ │ │ │ ├── EntityFormParser.kt
│ │ │ │ ├── FormEntityElement.kt
│ │ │ │ └── UnrecognizedEntityVersionException.kt
│ │ │ ├── server/
│ │ │ │ └── EntitySource.kt
│ │ │ └── storage/
│ │ │ ├── EntitiesRepository.kt
│ │ │ ├── Entity.kt
│ │ │ ├── EntityList.kt
│ │ │ ├── InMemEntitiesRepository.kt
│ │ │ └── QueryException.kt
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── entities_layout.xml
│ │ │ ├── entity_list_item_layout.xml
│ │ │ └── list_layout.xml
│ │ └── navigation/
│ │ └── entities_nav.xml
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── entities/
│ ├── LocalEntityUseCasesTest.kt
│ ├── browser/
│ │ └── EntityItemTest.kt
│ └── javarosa/
│ ├── EntitiesTest.kt
│ ├── EntityFormFinalizationProcessorTest.kt
│ ├── EntityFormParseProcessorTest.kt
│ ├── EntityFormParserTest.kt
│ ├── LocalEntitiesInstanceProviderTest.kt
│ ├── filter/
│ │ ├── LocalEntitiesFilterStrategyTest.kt
│ │ └── PullDataFunctionHandlerTest.kt
│ ├── parse/
│ │ ├── StringExtTest.kt
│ │ └── XPathExpressionExtTest.kt
│ └── support/
│ └── EntityXFormsElement.kt
├── errors/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── errors/
│ │ │ ├── ErrorActivity.kt
│ │ │ ├── ErrorAdapter.kt
│ │ │ └── ErrorItem.kt
│ │ └── res/
│ │ └── layout/
│ │ ├── activity_error.xml
│ │ └── error_item.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── errors/
│ │ └── ErrorActivityTest.kt
│ └── resources/
│ └── robolectric.properties
├── external-app/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── externalapp/
│ │ └── ExternalAppUtils.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── externalapp/
│ └── ExternalAppUtilsTest.kt
├── forms/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── forms/
│ │ ├── Form.java
│ │ ├── FormListItem.kt
│ │ ├── FormSource.java
│ │ ├── FormSourceException.kt
│ │ ├── FormsRepository.java
│ │ ├── ManifestFile.kt
│ │ ├── MediaFile.kt
│ │ ├── instances/
│ │ │ ├── Instance.java
│ │ │ └── InstancesRepository.java
│ │ └── savepoints/
│ │ ├── Savepoint.kt
│ │ └── SavepointsRepository.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── forms/
│ └── instances/
│ └── InstanceTest.kt
├── forms-test/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── formstest/
│ │ ├── FormFixtures.kt
│ │ ├── FormUtils.kt
│ │ ├── FormsRepositoryTest.java
│ │ ├── InMemFormsRepository.java
│ │ ├── InMemInstancesRepository.java
│ │ ├── InMemSavepointsRepository.kt
│ │ ├── InstanceFixtures.kt
│ │ ├── InstanceUtils.kt
│ │ ├── InstancesRepositoryTest.kt
│ │ └── SavepointsRepositoryTest.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── formstest/
│ ├── InMemFormsRepositoryTest.java
│ ├── InMemInstancesRepositoryTest.kt
│ └── InMemSavepointsRepositoryTest.kt
├── fragments-test/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── fragmentstest/
│ └── FragmentScenarioLauncherRule.kt
├── geo/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── geo/
│ │ │ ├── Constants.kt
│ │ │ ├── DaggerSetup.kt
│ │ │ ├── GeoActivityUtils.kt
│ │ │ ├── GeoUtils.kt
│ │ │ ├── analytics/
│ │ │ │ └── AnalyticsEvents.kt
│ │ │ ├── geopoint/
│ │ │ │ ├── AccuracyProgressView.kt
│ │ │ │ ├── AccuracyStatusView.kt
│ │ │ │ ├── GeoPointActivity.kt
│ │ │ │ ├── GeoPointDialogFragment.kt
│ │ │ │ ├── GeoPointMapActivity.java
│ │ │ │ ├── GeoPointViewModel.kt
│ │ │ │ └── LocationAccuracy.kt
│ │ │ ├── geopoly/
│ │ │ │ ├── GeoPolyFragment.kt
│ │ │ │ ├── GeoPolySettingsDialogFragment.java
│ │ │ │ ├── GeoPolyUtils.kt
│ │ │ │ ├── GeoPolyViewModel.kt
│ │ │ │ └── InfoDialog.kt
│ │ │ ├── javarosa/
│ │ │ │ └── IntersectsFunctionHandler.kt
│ │ │ └── selection/
│ │ │ ├── MappableSelectItem.kt
│ │ │ ├── SelectionMapFragment.kt
│ │ │ └── SelectionSummarySheet.kt
│ │ └── res/
│ │ ├── color/
│ │ │ └── fab_surface_background_color_less_transparent_disabled.xml
│ │ ├── drawable/
│ │ │ ├── ic_add_location.xml
│ │ │ ├── ic_backspace.xml
│ │ │ ├── ic_crop_frame.xml
│ │ │ ├── ic_distance.xml
│ │ │ ├── ic_info.xml
│ │ │ ├── ic_layers.xml
│ │ │ ├── ic_my_location.xml
│ │ │ ├── ic_note_add.xml
│ │ │ ├── ic_pause_36.xml
│ │ │ └── property_divider.xml
│ │ ├── layout/
│ │ │ ├── accuracy_progress_layout.xml
│ │ │ ├── accuracy_status_layout.xml
│ │ │ ├── geopoint_dialog.xml
│ │ │ ├── geopoint_layout.xml
│ │ │ ├── geopoly_dialog.xml
│ │ │ ├── geopoly_layout.xml
│ │ │ ├── property.xml
│ │ │ ├── selection_map_layout.xml
│ │ │ ├── selection_summary_sheet_layout.xml
│ │ │ └── simple_spinner_dropdown_item.xml
│ │ ├── layout-land/
│ │ │ ├── geopoint_layout.xml
│ │ │ └── geopoly_layout.xml
│ │ └── values/
│ │ ├── attrs.xml
│ │ ├── fab_surface.xml
│ │ └── force_light_surface_overlay.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── geo/
│ │ ├── GeoUtilsTest.kt
│ │ ├── geopoint/
│ │ │ ├── AccuracyProgressViewTest.kt
│ │ │ ├── AccuracyStatusViewTest.kt
│ │ │ ├── GeoPointActivityTest.kt
│ │ │ ├── GeoPointDialogFragmentTest.kt
│ │ │ ├── GeoPointMapActivityTest.java
│ │ │ └── LocationTrackerGeoPointViewModelTest.kt
│ │ ├── geopoly/
│ │ │ ├── GeoPolyFragmentTest.kt
│ │ │ ├── GeoPolySettingsDialogFragmentTest.java
│ │ │ ├── GeoPolyUtilsTest.kt
│ │ │ ├── GeoPolyViewModelTest.kt
│ │ │ └── InfoContentTest.kt
│ │ ├── selection/
│ │ │ ├── SelectionMapFragmentTest.kt
│ │ │ └── SelectionSummarySheetTest.kt
│ │ └── support/
│ │ ├── AccuracyStatusViewMatcher.kt
│ │ ├── FakeLocationTracker.kt
│ │ ├── FakeMapFragment.kt
│ │ ├── Fixtures.kt
│ │ └── RobolectricApplication.kt
│ └── resources/
│ └── robolectric.properties
├── google-maps/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── googlemaps/
│ │ ├── BitmapDescriptorCache.kt
│ │ ├── DaggerSetup.kt
│ │ ├── GoogleMapConfigurator.java
│ │ ├── GoogleMapFragment.java
│ │ ├── GoogleMapsMapBoxOfflineTileProvider.java
│ │ ├── MapPointExt.kt
│ │ ├── circles/
│ │ │ └── CircleFeature.kt
│ │ └── scaleview/
│ │ ├── Drawer.java
│ │ ├── MapScaleModel.java
│ │ ├── MapScaleView.java
│ │ ├── Scale.java
│ │ ├── Scales.java
│ │ └── ViewConfig.java
│ └── res/
│ ├── layout/
│ │ └── map_layout.xml
│ └── values/
│ └── attrs.xml
├── gradle/
│ ├── libs.versions.toml
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── gradlew.bat
├── icons/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── res/
│ └── drawable/
│ ├── ic_add_white_24.xml
│ ├── ic_baseline_add_24.xml
│ ├── ic_baseline_barcode_scanner_white_24.xml
│ ├── ic_baseline_calendar_today_white_24.xml
│ ├── ic_baseline_check_24.xml
│ ├── ic_baseline_collapse_24.xml
│ ├── ic_baseline_done_all_24.xml
│ ├── ic_baseline_draw_white_24.xml
│ ├── ic_baseline_expand_24.xml
│ ├── ic_baseline_explore_white_24.xml
│ ├── ic_baseline_format_list_bulleted_white_24.xml
│ ├── ic_baseline_format_list_numbered_white_24.xml
│ ├── ic_baseline_language_24.xml
│ ├── ic_baseline_layers_24.xml
│ ├── ic_baseline_library_music_white_24.xml
│ ├── ic_baseline_list_24.xml
│ ├── ic_baseline_location_off_24.xml
│ ├── ic_baseline_location_on_24.xml
│ ├── ic_baseline_location_on_white_24.xml
│ ├── ic_baseline_markup_white_24.xml
│ ├── ic_baseline_mic_24.xml
│ ├── ic_baseline_mic_off_24.xml
│ ├── ic_baseline_mic_white_24.xml
│ ├── ic_baseline_my_location_white_24.xml
│ ├── ic_baseline_open_in_new_white_24.xml
│ ├── ic_baseline_photo_camera_white_24.xml
│ ├── ic_baseline_photo_library_white_24.xml
│ ├── ic_baseline_print_white_24.xml
│ ├── ic_baseline_qr_code_2_add_24.xml
│ ├── ic_baseline_remove_24.xml
│ ├── ic_baseline_rule_24.xml
│ ├── ic_baseline_settings_24.xml
│ ├── ic_baseline_signature_white_24.xml
│ ├── ic_baseline_time_filled_white_24.xml
│ ├── ic_baseline_visibility_24.xml
│ ├── ic_baseline_warning_24.xml
│ ├── ic_baseline_wifi_off_24.xml
│ ├── ic_clear_white.xml
│ ├── ic_close.xml
│ ├── ic_color_lens_white.xml
│ ├── ic_delete.xml
│ ├── ic_delete_24.xml
│ ├── ic_edit.xml
│ ├── ic_map_marker_big.xml
│ ├── ic_map_marker_small.xml
│ ├── ic_map_marker_with_hole_big.xml
│ ├── ic_map_marker_with_hole_small.xml
│ ├── ic_map_point.xml
│ ├── ic_notification_small.xml
│ ├── ic_outline_info_24.xml
│ ├── ic_outline_polygon_white_24.xml
│ ├── ic_outline_polyline_white_24.xml
│ ├── ic_save.xml
│ └── ic_save_white.xml
├── image-loader/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── imageloader/
│ ├── GlideImageLoader.kt
│ └── svg/
│ ├── SvgDecoder.kt
│ ├── SvgDrawableTranscoder.kt
│ ├── SvgModule.kt
│ └── SvgSoftwareLayerSetter.kt
├── lists/
│ ├── .gitignore
│ ├── build.gradle.kts
│ ├── proguard-rules.pro
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── lists/
│ │ │ ├── EmptyListView.kt
│ │ │ ├── RecyclerViewUtils.kt
│ │ │ └── selects/
│ │ │ ├── MultiSelectAdapter.kt
│ │ │ ├── MultiSelectControlsFragment.kt
│ │ │ ├── MultiSelectListFragment.kt
│ │ │ ├── MultiSelectViewModel.kt
│ │ │ ├── SelectItem.kt
│ │ │ └── SingleSelectViewModel.kt
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── empty_list_view.xml
│ │ │ ├── multi_select_controls_layout.xml
│ │ │ └── multi_select_list.xml
│ │ └── values/
│ │ └── attrs.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── lists/
│ │ ├── EmptyListViewTest.kt
│ │ ├── RobolectricApplication.kt
│ │ └── selects/
│ │ ├── MultiSelectAdapterTest.kt
│ │ ├── MultiSelectControlsFragmentTest.kt
│ │ ├── MultiSelectListFragmentTest.kt
│ │ ├── MultiSelectViewModelTest.kt
│ │ ├── SingleSelectViewModelTest.kt
│ │ └── support/
│ │ └── TextAndCheckboxViewHolder.kt
│ └── resources/
│ └── robolectric.properties
├── location/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── location/
│ │ ├── AndroidLocationClient.java
│ │ ├── BaseLocationClient.kt
│ │ ├── DaggerSetup.kt
│ │ ├── GoogleFusedLocationClient.kt
│ │ ├── Location.kt
│ │ ├── LocationClient.java
│ │ ├── LocationClientProvider.kt
│ │ ├── LocationUtils.kt
│ │ ├── satellites/
│ │ │ ├── GpsStatusSatelliteInfoClient.kt
│ │ │ └── SatelliteInfoClient.kt
│ │ └── tracker/
│ │ ├── ForegroundServiceLocationTracker.kt
│ │ └── LocationTracker.kt
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── location/
│ │ ├── AndroidLocationClientTest.java
│ │ ├── GoogleFusedLocationClientTest.kt
│ │ ├── LocationClientProviderTest.kt
│ │ ├── LocationUtilsTest.kt
│ │ ├── RobolectricApplication.kt
│ │ ├── TestClientListener.java
│ │ ├── TestLocationListener.java
│ │ └── tracker/
│ │ ├── ForegroundServiceLocationTrackerTest.kt
│ │ ├── LocationTrackerServiceTest.kt
│ │ └── LocationTrackerTest.kt
│ └── resources/
│ └── robolectric.properties
├── mapbox/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── mapbox/
│ │ ├── DynamicPolyLineFeature.kt
│ │ ├── DynamicPolygonFeature.kt
│ │ ├── LineFeature.kt
│ │ ├── MapBoxInitializationFragment.kt
│ │ ├── MapFeature.kt
│ │ ├── MapUtils.kt
│ │ ├── MapboxMapConfigurator.java
│ │ ├── MapboxMapFragment.kt
│ │ ├── MarkerFeature.kt
│ │ ├── StaticPolyLineFeature.kt
│ │ ├── StaticPolygonFeature.kt
│ │ └── TileHttpServer.java
│ └── res/
│ └── layout/
│ └── mapbox_fragment_layout.xml
├── maps/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── maps/
│ │ │ ├── AnalyticsEvents.kt
│ │ │ ├── MapConfigurator.kt
│ │ │ ├── MapConsts.kt
│ │ │ ├── MapFragment.kt
│ │ │ ├── MapFragmentFactory.kt
│ │ │ ├── MapPoint.kt
│ │ │ ├── MapViewModel.kt
│ │ │ ├── MapViewModelMapFragment.kt
│ │ │ ├── ZoomObserver.kt
│ │ │ ├── circles/
│ │ │ │ ├── CircleDescription.kt
│ │ │ │ └── CurrentLocationDelegate.kt
│ │ │ ├── layers/
│ │ │ │ ├── DirectoryReferenceLayerRepository.kt
│ │ │ │ ├── MapFragmentReferenceLayerUtils.kt
│ │ │ │ ├── MbtilesFile.java
│ │ │ │ ├── OfflineMapLayersImporterAdapter.kt
│ │ │ │ ├── OfflineMapLayersImporterDialogFragment.kt
│ │ │ │ ├── OfflineMapLayersPickerAdapter.kt
│ │ │ │ ├── OfflineMapLayersPickerBottomSheetDialogFragment.kt
│ │ │ │ ├── OfflineMapLayersViewModel.kt
│ │ │ │ ├── ReferenceLayerRepository.kt
│ │ │ │ └── TileSource.java
│ │ │ ├── markers/
│ │ │ │ ├── MarkerDescription.kt
│ │ │ │ ├── MarkerIconCreator.kt
│ │ │ │ └── MarkerIconDescription.kt
│ │ │ └── traces/
│ │ │ ├── LineDescription.kt
│ │ │ ├── PolygonDescription.kt
│ │ │ └── TraceDescription.kt
│ │ └── res/
│ │ ├── drawable/
│ │ │ └── ic_crosshairs.xml
│ │ └── layout/
│ │ ├── offline_map_layers_importer.xml
│ │ ├── offline_map_layers_importer_item.xml
│ │ ├── offline_map_layers_picker.xml
│ │ └── offline_map_layers_picker_item.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── maps/
│ │ ├── LineDescriptionTest.kt
│ │ ├── MarkerIconDescriptionTest.kt
│ │ ├── PolygonDescriptionTest.kt
│ │ ├── RobolectricApplication.kt
│ │ ├── TraceDescriptionTest.kt
│ │ └── layers/
│ │ ├── DirectoryReferenceLayerRepositoryTest.kt
│ │ ├── InMemReferenceLayerRepository.kt
│ │ ├── MapFragmentReferenceLayerUtilsTest.kt
│ │ ├── OfflineMapLayersImporterDialogFragmentTest.kt
│ │ └── OfflineMapLayersPickerBottomSheetDialogFragmentTest.kt
│ └── resources/
│ └── robolectric.properties
├── material/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── material/
│ │ │ ├── BottomSheetBehavior.kt
│ │ │ ├── ErrorsPill.kt
│ │ │ ├── MaterialFullScreenDialogFragment.kt
│ │ │ ├── MaterialPill.kt
│ │ │ ├── MaterialProgressDialogFragment.java
│ │ │ └── Pill.kt
│ │ └── res/
│ │ ├── layout/
│ │ │ ├── pill.xml
│ │ │ └── progress_dialog.xml
│ │ └── values/
│ │ ├── attrs.xml
│ │ ├── material_3_button_icon_end_style.xml
│ │ └── material_full_screen_dialog_theme.xml
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── material/
│ ├── ErrorsPillTest.kt
│ └── MaterialProgressDialogFragmentTest.java
├── metadata/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── metadata/
│ │ ├── InstallIDProvider.kt
│ │ └── PropertyManager.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── metadata/
│ ├── PropertyManagerTest.kt
│ └── SettingsInstallIDProviderTest.kt
├── mobile-device-management/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── mobiledevicemanagement/
│ │ │ ├── MDMConfigHandler.kt
│ │ │ └── MDMConfigObserver.kt
│ │ └── res/
│ │ └── xml/
│ │ └── managed_configuration.xml
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── mobiledevicemanagement/
│ ├── MDMConfigHandlerTest.kt
│ └── MDMConfigObserverTest.kt
├── nbistubs/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ └── AndroidManifest.xml
├── open-rosa/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── openrosa/
│ │ ├── forms/
│ │ │ ├── DocumentFetchResult.java
│ │ │ ├── EntityIntegrity.kt
│ │ │ ├── OpenRosaClient.kt
│ │ │ └── OpenRosaXmlFetcher.java
│ │ ├── http/
│ │ │ ├── CaseInsensitiveEmptyHeaders.java
│ │ │ ├── CaseInsensitiveHeaders.java
│ │ │ ├── CollectThenSystemContentTypeMapper.java
│ │ │ ├── HttpCredentials.java
│ │ │ ├── HttpCredentialsInterface.java
│ │ │ ├── HttpGetResult.java
│ │ │ ├── HttpHeadResult.java
│ │ │ ├── HttpPostResult.java
│ │ │ ├── OpenRosaConstants.kt
│ │ │ ├── OpenRosaHttpInterface.java
│ │ │ └── okhttp/
│ │ │ ├── OkHttpCaseInsensitiveHeaders.java
│ │ │ ├── OkHttpConnection.java
│ │ │ ├── OkHttpOpenRosaServerClientProvider.java
│ │ │ ├── OpenRosaServerClient.java
│ │ │ └── OpenRosaServerClientProvider.java
│ │ └── parse/
│ │ ├── Kxml2OpenRosaResponseParser.kt
│ │ └── OpenRosaResponseParser.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── openrosa/
│ ├── forms/
│ │ ├── OpenRosaClientTest.kt
│ │ └── OpenRosaXmlFetcherTest.java
│ ├── http/
│ │ ├── CaseInsensitiveEmptyHeadersTest.java
│ │ ├── CollectThenSystemContentTypeMapperTest.java
│ │ ├── OpenRosaGetRequestTest.java
│ │ ├── OpenRosaHeadRequestTest.java
│ │ ├── OpenRosaPostRequestTest.java
│ │ └── okhttp/
│ │ ├── OkHttpCaseInsensitiveHeadersTest.java
│ │ ├── OkHttpConnectionGetRequestTest.java
│ │ ├── OkHttpConnectionHeadRequestTest.java
│ │ ├── OkHttpConnectionPostRequestTest.java
│ │ ├── OkHttpOpenRosaServerClientProviderTest.java
│ │ └── OpenRosaServerClientProviderTest.java
│ ├── parse/
│ │ └── Kxml2OpenRosaResponseParserTest.kt
│ └── support/
│ ├── MockWebServerHelper.java
│ ├── MockWebServerRule.kt
│ └── StubWebCredentialsProvider.java
├── osmdroid/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── osmdroid/
│ │ ├── DaggerSetup.kt
│ │ ├── OsmDroidInitializer.kt
│ │ ├── OsmDroidMapConfigurator.java
│ │ ├── OsmDroidMapFragment.java
│ │ ├── OsmMBTileModuleProvider.java
│ │ ├── OsmMBTileProvider.java
│ │ ├── OsmMBTileSource.java
│ │ └── WebMapService.java
│ └── res/
│ └── layout/
│ └── osm_map_layout.xml
├── permissions/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── permissions/
│ │ │ ├── LocationAccessibilityChecker.kt
│ │ │ ├── PermissionListener.kt
│ │ │ ├── PermissionsChecker.kt
│ │ │ ├── PermissionsDialogCreator.kt
│ │ │ ├── PermissionsProvider.kt
│ │ │ └── RequestPermissionsAPI.kt
│ │ └── res/
│ │ └── drawable/
│ │ ├── ic_photo_camera.xml
│ │ ├── ic_room_24dp.xml
│ │ └── ic_storage.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── permissions/
│ │ ├── PermissionsDialogCreatorTest.kt
│ │ └── PermissionsProviderTest.kt
│ └── resources/
│ └── robolectric.properties
├── printer/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── printer/
│ └── HtmlPrinter.kt
├── projects/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── projects/
│ │ ├── DaggerSetup.kt
│ │ ├── InMemProjectsRepository.kt
│ │ ├── Project.kt
│ │ ├── ProjectConfigurationResult.kt
│ │ ├── ProjectCreator.kt
│ │ ├── ProjectDependencyFactory.kt
│ │ ├── ProjectsRepository.kt
│ │ ├── SettingsConnectionMatcher.kt
│ │ └── SharedPreferencesProjectsRepository.kt
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── projects/
│ │ ├── InMemProjectsRepositoryTest.kt
│ │ ├── ProjectsRepositoryTest.kt
│ │ ├── SharedPreferencesProjectsRepositoryTest.kt
│ │ └── support/
│ │ └── RobolectricApplication.kt
│ └── resources/
│ └── robolectric.properties
├── qr-code/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── qrcode/
│ │ │ ├── BarcodeFilter.kt
│ │ │ ├── BarcodeScannerViewContainer.kt
│ │ │ ├── DetectedState.kt
│ │ │ ├── FlashlightToggle.kt
│ │ │ ├── ScannerControls.kt
│ │ │ ├── ScannerOverlay.kt
│ │ │ ├── mlkit/
│ │ │ │ ├── MlKitBarcodeScannerViewFactory.kt
│ │ │ │ └── PlayServicesFallbackBarcodeScannerViewFactory.kt
│ │ │ └── zxing/
│ │ │ ├── QRCodeCreator.kt
│ │ │ ├── QRCodeDecoder.kt
│ │ │ └── ZxingBarcodeScannerViewFactory.kt
│ │ └── res/
│ │ └── layout/
│ │ ├── mlkit_barcode_scanner_layout.xml
│ │ └── zxing_barcode_scanner_layout.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── qrcode/
│ │ ├── BarcodeFilterTest.kt
│ │ └── QRCodeEncodeDecodeTest.kt
│ └── resources/
│ └── robolectric.properties
├── secrets.gradle
├── selfie-camera/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── selfiecamera/
│ │ │ ├── Camera.kt
│ │ │ ├── CameraXCamera.kt
│ │ │ ├── CaptureSelfieActivity.kt
│ │ │ └── DaggerSetup.kt
│ │ └── res/
│ │ └── layout/
│ │ └── activity_capture_selfie.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── selfiecamera/
│ │ ├── CaptureSelfieActivityTest.kt
│ │ └── support/
│ │ └── RobolectricApplication.kt
│ └── resources/
│ └── robolectric.properties
├── service-test/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── servicetest/
│ ├── NotificationDetails.kt
│ └── ServiceScenario.kt
├── settings/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ ├── consumer-rules.pro
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── settings/
│ │ │ ├── InMemSettingsProvider.kt
│ │ │ ├── ODKAppSettingsImporter.kt
│ │ │ ├── ODKAppSettingsMigrator.java
│ │ │ ├── SettingsProvider.kt
│ │ │ ├── enums/
│ │ │ │ ├── AutoSend.kt
│ │ │ │ ├── FormUpdateMode.java
│ │ │ │ ├── GuidanceHintMode.kt
│ │ │ │ ├── StringIdEnum.kt
│ │ │ │ └── StringIdEnumUtils.kt
│ │ │ ├── importing/
│ │ │ │ ├── ProjectDetailsCreatorImpl.kt
│ │ │ │ └── SettingsImporter.kt
│ │ │ ├── keys/
│ │ │ │ ├── AppConfigurationKeys.kt
│ │ │ │ ├── MetaKeys.kt
│ │ │ │ ├── ProjectKeys.kt
│ │ │ │ └── ProtectedProjectKeys.kt
│ │ │ ├── migration/
│ │ │ │ ├── KeyCombiner.java
│ │ │ │ ├── KeyExtractor.java
│ │ │ │ ├── KeyMover.java
│ │ │ │ ├── KeyRenamer.java
│ │ │ │ ├── KeyTranslator.java
│ │ │ │ ├── KeyUpdater.java
│ │ │ │ ├── KeyValuePair.java
│ │ │ │ ├── Migration.java
│ │ │ │ ├── MigrationUtils.java
│ │ │ │ └── ValueTranslator.java
│ │ │ └── validation/
│ │ │ └── JsonSchemaSettingsValidator.kt
│ │ ├── res/
│ │ │ └── values/
│ │ │ └── strings.xml
│ │ └── resources/
│ │ └── client-settings.schema.json
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── settings/
│ ├── ODKAppSettingsImporterTest.kt
│ ├── ODKAppSettingsMigratorTest.java
│ ├── importing/
│ │ ├── ProjectDetailsCreatorImplTest.kt
│ │ └── SettingsImporterTest.kt
│ ├── migration/
│ │ ├── KeyCombinerTest.java
│ │ ├── KeyExtractorTest.java
│ │ ├── KeyMoverTest.java
│ │ ├── KeyRemoverTest.java
│ │ ├── KeyRenamerTest.java
│ │ ├── KeyTranslatorTest.java
│ │ └── ValueTranslatorTest.java
│ ├── support/
│ │ └── SettingsUtils.kt
│ └── validation/
│ ├── JsonSchemaSettingsValidatorTest.kt
│ └── OriginalJsonSchemaSettingsValidatorTest.kt
├── settings.gradle
├── shadows/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── shadows/
│ │ └── ShadowAndroidXAlertDialog.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── shadows/
│ └── ShadowAndroidXAlertDialogTest.kt
├── shared/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── shared/
│ │ ├── DebugLogger.kt
│ │ ├── PathUtils.kt
│ │ ├── Query.kt
│ │ ├── TempFiles.kt
│ │ ├── TimeInMs.kt
│ │ ├── collections/
│ │ │ └── CollectionExtensions.kt
│ │ ├── files/
│ │ │ └── FileExt.kt
│ │ ├── geometry/
│ │ │ └── Geometry.kt
│ │ ├── injection/
│ │ │ └── ObjectProvider.kt
│ │ ├── locks/
│ │ │ ├── BooleanChangeLock.kt
│ │ │ ├── ChangeLock.kt
│ │ │ └── ThreadSafeBooleanChangeLock.kt
│ │ ├── result/
│ │ │ └── Result.kt
│ │ ├── settings/
│ │ │ ├── InMemSettings.kt
│ │ │ └── Settings.kt
│ │ └── strings/
│ │ ├── Md5.kt
│ │ ├── RandomString.java
│ │ ├── StringUtils.kt
│ │ └── UUIDGenerator.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── shared/
│ ├── Md5Test.kt
│ ├── PathUtilsTest.kt
│ ├── QuickCheck.kt
│ ├── collections/
│ │ └── CollectionExtensionsTest.kt
│ ├── files/
│ │ └── FileExtTest.kt
│ ├── geometry/
│ │ ├── GeometryTest.kt
│ │ └── support/
│ │ ├── GeometryTestUtils.kt
│ │ └── GeometryTestUtilsTest.kt
│ ├── locks/
│ │ ├── BooleanChangeLockTest.kt
│ │ ├── ChangeLockTest.kt
│ │ └── ThreadSafeBooleanChangeLockTest.kt
│ └── strings/
│ └── StringUtilsTest.kt
├── strings/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── odk/
│ │ │ └── collect/
│ │ │ └── strings/
│ │ │ ├── format/
│ │ │ │ └── LengthFormatter.kt
│ │ │ └── localization/
│ │ │ ├── LocalizedActivity.kt
│ │ │ └── LocalizedApplication.kt
│ │ └── res/
│ │ ├── values/
│ │ │ ├── strings.xml
│ │ │ └── untranslated.xml
│ │ ├── values-af/
│ │ │ └── strings.xml
│ │ ├── values-am/
│ │ │ └── strings.xml
│ │ ├── values-ar/
│ │ │ └── strings.xml
│ │ ├── values-bg/
│ │ │ └── strings.xml
│ │ ├── values-bn/
│ │ │ └── strings.xml
│ │ ├── values-ca/
│ │ │ └── strings.xml
│ │ ├── values-cs/
│ │ │ └── strings.xml
│ │ ├── values-da/
│ │ │ └── strings.xml
│ │ ├── values-de/
│ │ │ └── strings.xml
│ │ ├── values-es/
│ │ │ └── strings.xml
│ │ ├── values-es-rSV/
│ │ │ └── strings.xml
│ │ ├── values-et/
│ │ │ └── strings.xml
│ │ ├── values-fa/
│ │ │ └── strings.xml
│ │ ├── values-fa-rAF/
│ │ │ └── strings.xml
│ │ ├── values-fi/
│ │ │ └── strings.xml
│ │ ├── values-fr/
│ │ │ └── strings.xml
│ │ ├── values-hi/
│ │ │ └── strings.xml
│ │ ├── values-ht/
│ │ │ └── strings.xml
│ │ ├── values-in/
│ │ │ └── strings.xml
│ │ ├── values-it/
│ │ │ └── strings.xml
│ │ ├── values-ja/
│ │ │ └── strings.xml
│ │ ├── values-ka/
│ │ │ └── strings.xml
│ │ ├── values-km/
│ │ │ └── strings.xml
│ │ ├── values-ln/
│ │ │ └── strings.xml
│ │ ├── values-lo-rLA/
│ │ │ └── strings.xml
│ │ ├── values-lt/
│ │ │ └── strings.xml
│ │ ├── values-mg/
│ │ │ └── strings.xml
│ │ ├── values-ml/
│ │ │ └── strings.xml
│ │ ├── values-mr/
│ │ │ └── strings.xml
│ │ ├── values-ms/
│ │ │ └── strings.xml
│ │ ├── values-my/
│ │ │ └── strings.xml
│ │ ├── values-ne-rNP/
│ │ │ └── strings.xml
│ │ ├── values-nl/
│ │ │ └── strings.xml
│ │ ├── values-no/
│ │ │ └── strings.xml
│ │ ├── values-pl/
│ │ │ └── strings.xml
│ │ ├── values-ps/
│ │ │ └── strings.xml
│ │ ├── values-pt/
│ │ │ └── strings.xml
│ │ ├── values-ro/
│ │ │ └── strings.xml
│ │ ├── values-ru/
│ │ │ └── strings.xml
│ │ ├── values-rw/
│ │ │ └── strings.xml
│ │ ├── values-si/
│ │ │ └── strings.xml
│ │ ├── values-sl/
│ │ │ └── strings.xml
│ │ ├── values-so/
│ │ │ └── strings.xml
│ │ ├── values-sq/
│ │ │ └── strings.xml
│ │ ├── values-sr/
│ │ │ └── strings.xml
│ │ ├── values-sv-rSE/
│ │ │ └── strings.xml
│ │ ├── values-sw/
│ │ │ └── strings.xml
│ │ ├── values-sw-rKE/
│ │ │ └── strings.xml
│ │ ├── values-te/
│ │ │ └── strings.xml
│ │ ├── values-th-rTH/
│ │ │ └── strings.xml
│ │ ├── values-ti/
│ │ │ └── strings.xml
│ │ ├── values-tl/
│ │ │ └── strings.xml
│ │ ├── values-tl-rPH/
│ │ │ └── strings.xml
│ │ ├── values-tr/
│ │ │ └── strings.xml
│ │ ├── values-uk/
│ │ │ └── strings.xml
│ │ ├── values-ur/
│ │ │ └── strings.xml
│ │ ├── values-ur-rPK/
│ │ │ └── strings.xml
│ │ ├── values-vi/
│ │ │ └── strings.xml
│ │ ├── values-zh/
│ │ │ └── strings.xml
│ │ ├── values-zh-rTW/
│ │ │ └── strings.xml
│ │ └── values-zu/
│ │ └── strings.xml
│ └── test/
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── strings/
│ │ └── format/
│ │ ├── DateFormatsTest.kt
│ │ └── LengthFormatterTest.kt
│ └── resources/
│ └── robolectric.properties
├── test-forms/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ └── resources/
│ ├── forms/
│ │ ├── Empty First Repeat.xml
│ │ ├── RepeatGroupAndGroup.xml
│ │ ├── RepeatTitles_1648.xml
│ │ ├── TestRepeat.xml
│ │ ├── all-widgets.xml
│ │ ├── audio-question.xml
│ │ ├── basic.xml
│ │ ├── different-search-appearances.xml
│ │ ├── dynamic_and_static_choices.xml
│ │ ├── dynamic_required_question.xml
│ │ ├── emptyGroupFieldList.xml
│ │ ├── encrypted-no-instanceID.xml
│ │ ├── encrypted.xml
│ │ ├── entity-update-pulldata.xml
│ │ ├── external-audio-question.xml
│ │ ├── external-csv-search-broken.xml
│ │ ├── external-csv-search.xml
│ │ ├── external_csv_form.xml
│ │ ├── external_data_questions.xml
│ │ ├── external_select.xml
│ │ ├── external_select_10.xml
│ │ ├── external_select_csv.xml
│ │ ├── field-list-repeat.xml
│ │ ├── fieldlist-updates.xml
│ │ ├── fixed-count-repeat.xml
│ │ ├── form1.xml
│ │ ├── form2.xml
│ │ ├── form3.xml
│ │ ├── form4.xml
│ │ ├── form5.xml
│ │ ├── form6.xml
│ │ ├── form7.xml
│ │ ├── form8.xml
│ │ ├── form9.xml
│ │ ├── formHierarchy1.xml
│ │ ├── formHierarchy2.xml
│ │ ├── formHierarchy3.xml
│ │ ├── form_design_error.xml
│ │ ├── form_styling.xml
│ │ ├── form_with_images.xml
│ │ ├── g6Error.xml
│ │ ├── hints_textq.xml
│ │ ├── identify-user-audit-false.xml
│ │ ├── identify-user-audit.xml
│ │ ├── intent-group.xml
│ │ ├── internal-audio-question.xml
│ │ ├── invalid-form.xml
│ │ ├── likert_test.xml
│ │ ├── location-audit.xml
│ │ ├── manyQ.xml
│ │ ├── metadata.xml
│ │ ├── nested-intent-group.xml
│ │ ├── numberInCSV.xml
│ │ ├── one-question-audit.xml
│ │ ├── one-question-autoplay.xml
│ │ ├── one-question-autosend-disabled.xml
│ │ ├── one-question-autosend.xml
│ │ ├── one-question-background-audio-audit.xml
│ │ ├── one-question-background-audio-multiple.xml
│ │ ├── one-question-background-audio.xml
│ │ ├── one-question-editable.xml
│ │ ├── one-question-encrypted-unicode.xml
│ │ ├── one-question-entity-create-and-update.xml
│ │ ├── one-question-entity-follow-up.xml
│ │ ├── one-question-entity-registration-broken.xml
│ │ ├── one-question-entity-registration-editable.xml
│ │ ├── one-question-entity-registration-id.xml
│ │ ├── one-question-entity-registration-v2020.1.xml
│ │ ├── one-question-entity-registration-v2023.1.xml
│ │ ├── one-question-entity-registration.xml
│ │ ├── one-question-entity-update-and-create.xml
│ │ ├── one-question-entity-update-editable.xml
│ │ ├── one-question-entity-update.xml
│ │ ├── one-question-last-saved-updated.xml
│ │ ├── one-question-last-saved.xml
│ │ ├── one-question-partial.xml
│ │ ├── one-question-repeat.xml
│ │ ├── one-question-translation.xml
│ │ ├── one-question-updated.xml
│ │ ├── one-question-uuid-instance-name.xml
│ │ ├── one-question-with-constraint.xml
│ │ ├── one-question.xml
│ │ ├── pull_data.xml
│ │ ├── random.xml
│ │ ├── randomTest_broken.xml
│ │ ├── ranking_widget.xml
│ │ ├── repeat_group_form.xml
│ │ ├── repeat_group_new.xml
│ │ ├── repeat_group_wrapped_with_a_regular_group.xml
│ │ ├── repeat_groups.xml
│ │ ├── repeat_in_field_list.xml
│ │ ├── repeat_without_label.xml
│ │ ├── requiredQuestionInFieldList.xml
│ │ ├── required_question_with_audio.xml
│ │ ├── required_question_with_custom_error_message.xml
│ │ ├── search_and_select.xml
│ │ ├── selectOneExternal.xml
│ │ ├── select_one_external.xml
│ │ ├── setgeopoint-action.xml
│ │ ├── simple-search-external-csv.xml
│ │ ├── single-geopoint.xml
│ │ ├── start-geopoint.xml
│ │ ├── string_widgets_in_field_list.xml
│ │ ├── track-changes-reason-on-edit.xml
│ │ ├── two-question-audit-track-changes.xml
│ │ ├── two-question-audit.xml
│ │ ├── two-question-external.xml
│ │ ├── two-question-required.xml
│ │ ├── two-question-save-incomplete-required.xml
│ │ ├── two-question-save-incomplete.xml
│ │ ├── two-question-updated.xml
│ │ ├── two-question.xml
│ │ ├── two-questions-in-group.xml
│ │ └── validate.xml
│ └── media/
│ ├── external-csv-search-produce.csv
│ ├── external_csv_cities.csv
│ ├── external_csv_countries.csv
│ ├── external_csv_neighbourhoods.csv
│ ├── external_data.csv
│ ├── external_data.xml
│ ├── external_data_10.xml
│ ├── external_data_broken.csv
│ ├── fruits.csv
│ ├── itemSets.csv
│ ├── people.csv
│ ├── selectOneExternal-media/
│ │ └── itemsets.csv
│ ├── simple-search-external-csv-fruits.csv
│ ├── test.m4a
│ └── updated-people.csv
├── test-shared/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── testshared/
│ ├── ActivityControllerRule.kt
│ ├── ActivityExt.kt
│ ├── AssertIntentsHelper.kt
│ ├── AssertionFramework.kt
│ ├── ComposeAssertions.kt
│ ├── ComposeInteractions.kt
│ ├── DummyActivity.kt
│ ├── ErrorIntentLauncher.kt
│ ├── EspressoAssertions.kt
│ ├── EspressoInteractions.kt
│ ├── FakeAudioPlayer.kt
│ ├── FakeBarcodeScannerView.kt
│ ├── FakeBroadcastReceiverRegister.kt
│ ├── FakeScheduler.kt
│ ├── FragmentResultRecorder.kt
│ ├── LocationTestUtils.kt
│ ├── MockFragmentFactory.kt
│ ├── MockWebPageService.kt
│ ├── RecyclerViewMatcher.kt
│ ├── RobolectricHelpers.kt
│ ├── SliderExt.kt
│ ├── TimeZoneSetter.kt
│ ├── ViewActions.kt
│ ├── ViewMatchers.kt
│ └── WaitFor.kt
├── timedgrid/
│ ├── .gitignore
│ ├── build.gradle.kts
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── timedgrid/
│ │ ├── AssessmentType.kt
│ │ ├── CommonTimedGridRenderer.kt
│ │ ├── FinishType.kt
│ │ ├── NavigationAwareWidget.kt
│ │ ├── PausableCountDownTimer.kt
│ │ ├── TimedGridRenderer.kt
│ │ ├── TimedGridState.kt
│ │ ├── TimedGridSummary.kt
│ │ ├── TimedGridSummaryAnswerCreator.kt
│ │ ├── TimedGridViewModel.kt
│ │ ├── TimedGridWidgetConfiguration.kt
│ │ ├── TimedGridWidgetDelegate.kt
│ │ └── TimedGridWidgetLayout.kt
│ └── res/
│ ├── color/
│ │ └── timed_grid_button_tint_selector.xml
│ ├── drawable/
│ │ └── row_number_background.xml
│ ├── layout/
│ │ ├── timed_grid.xml
│ │ ├── timed_grid_item_button.xml
│ │ └── timed_grid_item_row.xml
│ └── values/
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── upgrade/
│ ├── .gitignore
│ ├── README.md
│ ├── build.gradle.kts
│ └── src/
│ ├── main/
│ │ ├── AndroidManifest.xml
│ │ └── java/
│ │ └── org/
│ │ └── odk/
│ │ └── collect/
│ │ └── upgrade/
│ │ ├── AppUpgrader.kt
│ │ ├── LaunchState.kt
│ │ └── Upgrade.kt
│ └── test/
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── upgrade/
│ ├── AppUpgraderTest.kt
│ └── VersionCodeLaunchStateTest.kt
└── web-page/
├── .gitignore
├── build.gradle.kts
└── src/
├── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── org/
│ └── odk/
│ └── collect/
│ └── webpage/
│ ├── CustomTabsWebPageService.kt
│ └── WebPageService.kt
└── test/
└── java/
└── org/
└── odk/
└── collect/
└── webpage/
└── CustomTabsWebPageServiceTest.java
Showing preview only (520K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (5828 symbols across 571 files)
FILE: androidshared/src/main/java/org/odk/collect/androidshared/livedata/LiveDataUtils.java
class LiveDataUtils (line 16) | public class LiveDataUtils {
method LiveDataUtils (line 18) | private LiveDataUtils() {
method observe (line 22) | public static <T> Cancellable observe(LiveData<T> liveData, Consumer<T...
method liveDataOf (line 36) | public static <T> LiveData<T> liveDataOf(T value) {
method combine (line 40) | public static <T, U> LiveData<Pair<T, U>> combine(LiveData<T> one, Liv...
method combine3 (line 47) | public static <T, U, V> LiveData<Triple<T, U, V>> combine3(LiveData<T>...
method combine4 (line 54) | public static <T, U, V, W> LiveData<Quad<T, U, V, W>> combine4(LiveDat...
class CombinedLiveData (line 61) | private static class CombinedLiveData<T> extends MediatorLiveData<T> {
method CombinedLiveData (line 67) | CombinedLiveData(LiveData<?>[] sources, Function<Object[], T> map) {
method update (line 82) | private void update() {
class Quad (line 92) | public static class Quad<T, U, V, W> {
method Quad (line 99) | public Quad(T first, U second, V third, W fourth) {
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/CameraUtils.java
class CameraUtils (line 27) | public class CameraUtils {
method getFrontCameraId (line 28) | public static int getFrontCameraId() {
method isFrontCameraAvailable (line 41) | public boolean isFrontCameraAvailable(Context context) {
FILE: androidshared/src/main/java/org/odk/collect/androidshared/system/PlayServicesChecker.java
class PlayServicesChecker (line 11) | public class PlayServicesChecker {
method isGooglePlayServicesAvailable (line 16) | public boolean isGooglePlayServicesAvailable(Context context) {
method showGooglePlayServicesAvailabilityErrorDialog (line 23) | public void showGooglePlayServicesAvailabilityErrorDialog(Context cont...
FILE: androidshared/src/main/java/org/odk/collect/androidshared/utils/ScreenUtils.java
class ScreenUtils (line 23) | public class ScreenUtils {
method ScreenUtils (line 27) | public ScreenUtils(Context context) {
method getScreenWidth (line 31) | public static int getScreenWidth(Context context) {
method getScreenHeight (line 35) | public static int getScreenHeight(Context context) {
method xdpi (line 39) | public static float xdpi(Context context) {
method ydpi (line 43) | public static float ydpi(Context context) {
method getDisplayMetrics (line 47) | private static DisplayMetrics getDisplayMetrics(Context context) {
method getScreenSizeConfiguration (line 51) | public int getScreenSizeConfiguration() {
FILE: androidshared/src/test/java/org/odk/collect/androidshared/utils/DialogFragmentUtilsTest.java
class DialogFragmentUtilsTest (line 20) | @RunWith(AndroidJUnit4.class)
method showIfNotShowing_onlyEverOpensOneDialog (line 23) | @Test
method showIfNotShowing_whenActivitySavedState_doesNotShowDialog (line 37) | @Test
method showIfNotShowing_whenActivityDestroyed_doesNotShowDialog (line 47) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/AudioRecordingTest.java
class AudioRecordingTest (line 25) | @RunWith(AndroidJUnit4.class)
method providesAudioRecorder (line 31) | @Override
method onAudioQuestion_withoutAudioQuality_canRecordAndPlayBackInApp (line 55) | @Test
method onAudioQuestion_withQualitySpecified_canRecordAudioInApp (line 67) | @Test
method whileRecording_pressingBack_showsWarning_andStaysOnSameScreen (line 80) | @Test
method whileRecording_swipingToADifferentScreen_showsWarning_andStaysOnSameScreen (line 91) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/BackgroundAudioRecordingTest.java
class BackgroundAudioRecordingTest (line 43) | @RunWith(AndroidJUnit4.class)
method providesAudioRecorder (line 52) | @Override
method providesPermissionsChecker (line 69) | @Override
method providesPermissionsProvider (line 74) | @Override
method fillingOutForm_recordsAudio (line 86) | @Test
method fillingOutForm_withMultipleRecordActions_recordsAudioOnceForAllOfThem (line 108) | @Test
method pressingBackWhileRecording_andClickingSave_exitsForm (line 131) | @Test
method uncheckingRecordAudio_andConfirming_endsAndDeletesRecording (line 141) | @Test
method whenRecordAudioPermissionNotGranted_openingForm_andDenyingPermissions_closesForm (line 161) | @Test
method viewForm_doesNotRecordAudio (line 176) | @Test
class RevokeableRecordAudioPermissionsChecker (line 188) | private static class RevokeableRecordAudioPermissionsChecker extends C...
method RevokeableRecordAudioPermissionsChecker (line 192) | RevokeableRecordAudioPermissionsChecker(Context context) {
method isPermissionGranted (line 196) | @Override
method revoke (line 205) | public void revoke() {
class ControllableRecordAudioPermissionsProvider (line 210) | private static class ControllableRecordAudioPermissionsProvider extend...
method ControllableRecordAudioPermissionsProvider (line 215) | ControllableRecordAudioPermissionsProvider(PermissionsChecker permis...
method requestRecordAudioPermission (line 219) | @Override
method makeControllable (line 228) | public void makeControllable() {
method additionalExplanationClosed (line 232) | public void additionalExplanationClosed() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ContextMenuTest.java
class ContextMenuTest (line 10) | public class ContextMenuTest {
method whenRemoveStringAnswer_ShouldAppropriateQuestionBeCleared (line 19) | @Test
method whenLongPressedOnEditText_ShouldNotRemoveAnswerOptionAppear (line 38) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/DeletingRepeatGroupsTest.java
class DeletingRepeatGroupsTest (line 19) | @Ignore
method requestingDeletionOfFirstRepeat_deletesFirstRepeat (line 29) | @Test
method requestingDeletionOfMiddleRepeat_deletesMiddleRepeat (line 37) | @Test
method requestingDeletionOfLastRepeat_deletesLastRepeat (line 46) | @Test
method requestingDeletionOfFirstRepeatInHierarchy_deletesFirstRepeat (line 57) | @Test
method requestingDeletionOfMiddleRepeatInHierarchy_deletesMiddleRepeat (line 76) | @Test
method requestingDeletionOfLastRepeatInHierarchy_deletesLastRepeat (line 95) | @Test
method requestingDeletionOfAllRepeatsInHierarchyStartingFromIndexThatWillBeDeleted_shouldBringAUserToTheFirstRelevantQuestionBeforeTheGroup (line 114) | @Test
method requestingDeletionOfAllRepeatsInHierarchyStartingFromIndexThatWillNotBeDeleted_shouldBringAUserBackToTheSameIndex (line 132) | @Test
method requestingDeletionOfAllRepeatsInHierarchyStartingFromTheEndView_shouldBringAUserToTheEndView (line 149) | @Test
method requestingDeletionOfFirstRepeatWithFieldList_deletesFirstRepeat (line 167) | @Test
method requestingDeletionOfMiddleRepeatWithFieldList_deletesMiddleRepeat (line 181) | @Test
method requestingDeletionOfLastRepeatWithFieldList_deletesLastRepeat (line 195) | @Test
method requestingDeletionOfFirstRepeatWithFieldListInHierarchy_deletesFirstRepeat (line 209) | @Test
method requestingDeletionOfMiddleRepeatWithFieldListInHierarchy_deletesMiddleRepeat (line 229) | @Test
method requestingDeletionOfLastRepeatWithFieldListInHierarchy_deletesLastRepeat (line 248) | @Test
method requestingDeletionOfAllRepeatsWithFieldListInHierarchyStartingFromIndexThatWillBeDeleted_shouldBringAUserToTheFirstRelevantQuestionBeforeTheGroup (line 267) | @Test
method requestingDeletionOfAllRepeatsWithFieldListInHierarchyStartingFromIndexThatWillNotBeDeleted_shouldBringAUserBackToTheSameIndex (line 291) | @Test
method requestingDeletionOfAllRepeatsWithFieldListInHierarchyStartingFromTheEndView_shouldBringAUserToTheEndView (line 308) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalAudioRecordingTest.java
class ExternalAudioRecordingTest (line 28) | @RunWith(AndroidJUnit4.class)
method onAudioQuestion_whenAudioQualityIsExternal_usesExternalRecorder (line 54) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/ExternalSecondaryInstanceTest.java
class ExternalSecondaryInstanceTest (line 23) | @RunWith(AndroidJUnit4.class)
method displaysAllOptionsFromXMLSecondaryInstance (line 32) | @Test
method displaysAllOptionsFromCSVSecondaryInstance (line 43) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/FormHierarchyTest.java
class FormHierarchyTest (line 22) | public class FormHierarchyTest {
method allRelevantQuestionsShouldBeVisibleInHierarchyView (line 30) | @Test
method notRelevantRepeatGroupsShouldNotBeVisibleInHierarchy (line 47) | @Test
method repeatGroupsShouldBeVisibleAsAppropriate (line 91) | @Test
method deletingLastGroupShouldNotBreakHierarchy (line 121) | @Test
method deletingLastGroupAndAddingOneShouldNotBreakHierarchy (line 156) | @Test
method showRepeatsPickerWhenFirstRepeatIsEmpty (line 176) | @Test
method regularGroupThatWrapsARepeatableGroupShouldBeTreatedAsARegularOne (line 190) | @Test
method when_openHierarchyViewFromLastPage_should_mainGroupViewBeVisible (line 203) | @Test
method hierachyView_shouldNotChangeAfterScreenRotation (line 215) | @Test
method theListOfQuestionsShouldBeScrolledToTheLastDisplayedQuestionAfterOpeningTheHierarchy (line 228) | @Test
method whenViewFormInHierarchyForRepeatGroup_noDeleteButtonAppears (line 241) | @Test
method whenViewFormInHierarchy_clickingOnQuestion_doesNothing (line 257) | @Test
method clickingBackButtonAfterNavigatingInTheHierarchyOfGroups_movesToTheQuestionDisplayedBeforeOpeningTheHierarchy (line 270) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/FormLanguageTest.java
class FormLanguageTest (line 14) | @RunWith(AndroidJUnit4.class)
method canSwitchLanguagesInForm (line 22) | @Test
method languageChoiceIsPersisted (line 35) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/LikertTest.java
class LikertTest (line 26) | public class LikertTest {
method allText_canClick (line 35) | @Test
method allImages_canClick (line 43) | @Test
method insufficientText_canClick (line 51) | @Test
method insufficientImages_canClick (line 59) | @Test
method missingImage_canClick (line 67) | @Test
method missingText_canClick (line 75) | @Test
method onlyOneRemainsClicked (line 83) | @Test
method testImagesLoad (line 94) | @Test
method updateTest_SelectionChangeAtOneCascadeLevelWithLikert_ShouldUpdateNextLevels (line 104) | @Test
method openWidgetList (line 141) | private void openWidgetList() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/QuickSaveTest.java
class QuickSaveTest (line 13) | @RunWith(AndroidJUnit4.class)
method whenFillingForm_clickingSaveIcon_savesCurrentAnswers (line 22) | @Test
method whenFillingForm_withViolatedConstraintsOnCurrentScreen_clickingSaveIcon_savesCurrentAnswers (line 40) | @Test
method whenEditingANonFinalizedForm_withViolatedConstraintsOnCurrentScreen_clickingSaveIcon_savesCurrentAnswers (line 55) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/QuittingFormTest.java
class QuittingFormTest (line 15) | @RunWith(AndroidJUnit4.class)
method whenFillingForm_pressingBack_andClickingSaveChanges_savesCurrentAnswers (line 24) | @Test
method whenFillingForm_pressingBack_andClickingIgnoreChanges_doesNotSaveForm (line 43) | @Test
method whenFillingForm_saving_andPressingBack_andClickingIgnoreChanges_savesAnswersBeforeSave (line 55) | @Test
method whenFillingForm_withViolatedConstraintsOnCurrentScreen_pressingBack_andClickingSaveChanges_savesCurrentAnswers (line 72) | @Test
method whenEditingANonFinalizedForm_withViolatedConstraintsOnCurrentScreen_pressingBack_andClickingSaveChanges_savesCurrentAnswers (line 88) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/RankingWidgetWithCSVTest.java
class RankingWidgetWithCSVTest (line 12) | public class RankingWidgetWithCSVTest {
method rankingWidget_shouldDisplayItemsFromSearchFunc (line 23) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/backgroundlocation/LocationTrackingAuditTest.java
class LocationTrackingAuditTest (line 24) | public class LocationTrackingAuditTest {
method providesFusedLocationClient (line 32) | @Override
method locationTrackingIsLogged_andLocationIsLoggedForEachQuestion (line 39) | @Test
method locationCollectionToggle_ShouldBeAvailable (line 72) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/backgroundlocation/SetGeopointActionTest.java
class SetGeopointActionTest (line 10) | public class SetGeopointActionTest {
method locationCollectionSnackbar_ShouldBeDisplayedAtFormLaunch (line 19) | @Test
method locationCollectionToggle_ShouldBeAvailable (line 28) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formentry/dynamicpreload/DynamicPreLoadedDataSelects.java
class DynamicPreLoadedDataSelects (line 18) | public class DynamicPreLoadedDataSelects {
method withoutFilterAndWithFilter_displaysMatchingChoices (line 26) | @Test
method displayErrorWhenFilesAreMissing (line 49) | @Test
method displayWarningWhenQueryIsBad (line 58) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formmanagement/DeleteBlankFormTest.java
class DeleteBlankFormTest (line 14) | @RunWith(AndroidJUnit4.class)
method deletingAForm_removesFormFromBlankFormList (line 24) | @Test
method deletingAForm_whenThereFilledForms_removesFormFromBlankFormList_butAllowsEditingFilledForms (line 39) | @Test
method afterFillingAForm_andDeletingIt_allowsFormToBeReDownloaded (line 66) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formmanagement/FormsAdbTest.java
class FormsAdbTest (line 20) | @RunWith(AndroidJUnit4.class)
method canUpdateAFormFromDisk (line 30) | @Test
method canUpdateFormOnDiskFromServer (line 46) | @Test
method canDeleteFormFromDisk (line 64) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formmanagement/GetBlankFormsTest.java
class GetBlankFormsTest (line 14) | @RunWith(AndroidJUnit4.class)
method whenThereIsAnAuthenticationErrorFetchingFormList_allowsUserToReenterCredentials (line 25) | @Test
method whenThereIsAnErrorFetchingFormList_showsError (line 38) | @Test
method whenThereIsAnErrorFetchingForms_showsError (line 48) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formmanagement/HideOldVersionsTest.java
class HideOldVersionsTest (line 14) | @RunWith(AndroidJUnit4.class)
method whenHideOldVersionsEnabled_onlyTheNewestVersionOfAFormShowsInFormList (line 23) | @Test
method whenHideOldVersionsDisabled_allVersionOfAFormShowsInFormList (line 36) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/formmanagement/ManualUpdatesTest.java
class ManualUpdatesTest (line 17) | @RunWith(AndroidJUnit4.class)
method whenManualUpdatesEnabled_getBlankFormsIsAvailable (line 26) | @Test
method whenManualUpdatesEnabled_fillBlankFormRefreshButtonIsGone (line 33) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/settings/ConfigureWithQRCodeTest.java
class ConfigureWithQRCodeTest (line 30) | @RunWith(AndroidJUnit4.class)
method providesQRCodeGenerator (line 34) | @Override
method teardown (line 46) | @After
method clickConfigureQRCode_opensScanner_andThenScanning_importsSettings (line 52) | @Test
method clickConfigureQRCode_andClickingOnView_showsQRCode (line 73) | @Test
method whenThereIsAnAdminPassword_canRemoveFromQRCode (line 87) | @Test
method whenThereIsAServerPassword_canRemoveFromQRCode (line 102) | @Test
class StubQRCodeGenerator (line 125) | private static class StubQRCodeGenerator implements QRCodeGenerator {
method generateQRCode (line 129) | @Override
method setup (line 134) | public void setup() {
method teardown (line 141) | public void teardown() {
method getQRCodeFilePath (line 148) | String getQRCodeFilePath() {
method getDrawableID (line 152) | int getDrawableID() {
method saveBitmap (line 156) | private void saveBitmap(Bitmap bitmap) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/settings/ServerSettingsTest.java
class ServerSettingsTest (line 16) | @RunWith(AndroidJUnit4.class)
method whenUsingODKServer_canAddCredentialsForServer (line 28) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/smoke/BadServerTest.java
class BadServerTest (line 17) | @RunWith(AndroidJUnit4.class)
method whenHashNotIncludedInFormList_showError (line 27) | @Test
method whenMediaFileHasMissingPrefix_showsAsUpdated (line 49) | @Test
method whenMediaFileHasUnstableHash_butIsIdentical_doesNotShowAsUpdatedAfterRedownload (line 66) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/feature/smoke/GetAndSubmitFormTest.java
class GetAndSubmitFormTest (line 15) | @RunWith(AndroidJUnit4.class)
method canGetBlankForm_fillItIn_andSubmit (line 25) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/instrumented/forms/FormUtilsTest.java
class FormUtilsTest (line 35) | public class FormUtilsTest {
method create (line 39) | @Override
method setUp (line 48) | @Before
method sessionRootTranslatorOrderDoesNotMatter (line 57) | @Test
method hostStringsOrderedCorrectly (line 90) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/instrumented/tasks/FormLoaderTaskTest.java
class FormLoaderTaskTest (line 37) | public class FormLoaderTaskTest {
method create (line 47) | @Override
method loadSearchFromExternalCSVmultipleTimes (line 70) | @Test
FILE: collect_app/src/androidTest/java/org/odk/collect/android/instrumented/utilities/CustomSQLiteQueryExecutionTest.java
class CustomSQLiteQueryExecutionTest (line 39) | @RunWith(AndroidJUnit4.class)
method dropTableTest (line 60) | @Test
method renameTableTest (line 71) | @Test
method copyTableTest (line 88) | @Test
method addColumnTest (line 128) | @Test
method checkValues (line 150) | private void checkValues(String tableName) {
method createTestTable (line 169) | private void createTestTable() {
method insertExampleData (line 177) | private void insertExampleData(String tableName) {
method tableExists (line 184) | private boolean tableExists(String tableName) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/instrumented/utilities/DateTimeUtilsTest.java
class DateTimeUtilsTest (line 39) | @RunWith(AndroidJUnit4.class)
method setUp (line 55) | @Before
method getDateTimeLabelTest (line 71) | @Test
method resetTimeZone (line 106) | @After
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/ActivityHelpers.java
class ActivityHelpers (line 21) | public final class ActivityHelpers {
method ActivityHelpers (line 23) | private ActivityHelpers() {
method getActivity (line 26) | public static Activity getActivity() {
method getLaunchIntent (line 55) | public static Intent getLaunchIntent() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/CountingTaskExecutorIdlingResource.java
class CountingTaskExecutorIdlingResource (line 6) | public class CountingTaskExecutorIdlingResource extends CountingTaskExec...
method getName (line 10) | @Override
method isIdleNow (line 15) | @Override
method registerIdleTransitionCallback (line 20) | @Override
method onIdle (line 25) | @Override
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/FakeLocationClient.java
class FakeLocationClient (line 11) | public class FakeLocationClient implements LocationClient {
method start (line 17) | public void start(LocationClientListener listener) {
method stop (line 24) | public void stop() {
method isLocationAvailable (line 32) | public boolean isLocationAvailable() {
method requestLocationUpdates (line 36) | public void requestLocationUpdates(LocationListener locationListener) {
method stopLocationUpdates (line 41) | public void stopLocationUpdates() {
method setListener (line 45) | @Override
method getLastLocation (line 50) | public Location getLastLocation() {
method isMonitoringLocation (line 54) | public boolean isMonitoringLocation() {
method setPriority (line 58) | public void setPriority(Priority priority) { }
method setRetainMockAccuracy (line 60) | @Override
method setUpdateInterval (line 65) | public void setUpdateInterval(long updateInterval) { }
method setLocation (line 67) | public void setLocation(Location location) {
method updateLocationListener (line 72) | private void updateLocationListener() {
method getListener (line 78) | private LocationClientListener getListener() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/actions/RotateAction.java
class RotateAction (line 16) | public class RotateAction implements ViewAction {
method RotateAction (line 20) | public RotateAction(int screenOrientation) {
method getConstraints (line 24) | @Override
method getDescription (line 29) | @Override
method perform (line 34) | @Override
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/matchers/ToastMatcher.java
class ToastMatcher (line 11) | public class ToastMatcher extends TypeSafeMatcher<Root> {
method describeTo (line 13) | @Override
method matchesSafely (line 18) | @Override
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/AboutPage.java
class AboutPage (line 11) | public class AboutPage extends Page<AboutPage> {
method assertOnPage (line 13) | @Override
method scrollToOpenSourceLibrariesLicenses (line 19) | public AboutPage scrollToOpenSourceLibrariesLicenses() {
method clickOnOpenSourceLibrariesLicenses (line 24) | public OpenSourceLicensesPage clickOnOpenSourceLibrariesLicenses() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/BlankFormSearchPage.java
class BlankFormSearchPage (line 8) | public class BlankFormSearchPage extends Page<BlankFormSearchPage> {
method assertOnPage (line 10) | @Override
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/CancelRecordingDialog.java
class CancelRecordingDialog (line 3) | public class CancelRecordingDialog extends Page<CancelRecordingDialog> {
method CancelRecordingDialog (line 7) | CancelRecordingDialog(String formName) {
method assertOnPage (line 11) | @Override
method clickOk (line 17) | public FormEntryPage clickOk() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/ChangesReasonPromptPage.java
class ChangesReasonPromptPage (line 16) | public class ChangesReasonPromptPage extends Page<ChangesReasonPromptPag...
method ChangesReasonPromptPage (line 20) | public ChangesReasonPromptPage(String formName) {
method assertOnPage (line 24) | @Override
method enterReason (line 33) | public ChangesReasonPromptPage enterReason(String reason) {
method clickSave (line 38) | public MainMenuPage clickSave() {
method clickSave (line 43) | public <D extends Page<D>> D clickSave(D destination) {
method pressClose (line 55) | public <D extends Page<D>> D pressClose(D destination) {
method clickSaveWithValidationError (line 60) | public ChangesReasonPromptPage clickSaveWithValidationError() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/DeleteSavedFormPage.java
class DeleteSavedFormPage (line 8) | public class DeleteSavedFormPage extends Page<DeleteSavedFormPage> {
method assertOnPage (line 10) | @Override
method clickBlankForms (line 16) | public DeleteSavedFormPage clickBlankForms() {
method clickForm (line 21) | public DeleteSavedFormPage clickForm(String formName) {
method clickDeleteSelected (line 26) | public DeleteSelectedDialog clickDeleteSelected(int numberSelected) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/DeleteSelectedDialog.java
class DeleteSelectedDialog (line 3) | public class DeleteSelectedDialog extends Page<DeleteSelectedDialog> {
method DeleteSelectedDialog (line 8) | public DeleteSelectedDialog(int numberSelected, DeleteSavedFormPage de...
method assertOnPage (line 13) | @Override
method clickDeleteForms (line 19) | public DeleteSavedFormPage clickDeleteForms() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/EditSavedFormPage.java
class EditSavedFormPage (line 40) | public class EditSavedFormPage extends Page<EditSavedFormPage> {
method assertOnPage (line 42) | @Override
method checkInstanceState (line 48) | public EditSavedFormPage checkInstanceState(String instanceName, Strin...
method clickOnFormWithDialog (line 58) | public OkDialog clickOnFormWithDialog(String instanceName) {
method clickOnFormWithIdentityPrompt (line 63) | public IdentifyUserPromptPage clickOnFormWithIdentityPrompt(String for...
method clickOnForm (line 68) | public FormHierarchyPage clickOnForm(String formName, String instanceN...
method clickOnForm (line 73) | public FormHierarchyPage clickOnForm(String formName) {
method clickOnFormWithSavepoint (line 78) | public SavepointRecoveryDialogPage clickOnFormWithSavepoint(String for...
method clickOnFormClosingApp (line 83) | public AppClosedPage clickOnFormClosingApp(String formName) {
method scrollToAndClickOnForm (line 88) | private void scrollToAndClickOnForm(String formName) {
method clickMenuFilter (line 92) | public EditSavedFormPage clickMenuFilter() {
method searchInBar (line 97) | public EditSavedFormPage searchInBar(String query) {
method clickFinalizeAll (line 102) | public BulkFinalizationConfirmationDialogPage clickFinalizeAll(int cou...
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/ExperimentalPage.java
class ExperimentalPage (line 3) | public class ExperimentalPage extends Page<ExperimentalPage> {
method assertOnPage (line 5) | @Override
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/FillBlankFormPage.java
class FillBlankFormPage (line 24) | public class FillBlankFormPage extends Page<FillBlankFormPage> {
method assertOnPage (line 26) | @Override
method clickOnFormWithIdentityPrompt (line 32) | public IdentifyUserPromptPage clickOnFormWithIdentityPrompt(String for...
method clickOnSortByButton (line 37) | public FillBlankFormPage clickOnSortByButton() {
method clickMenuFilter (line 42) | public FillBlankFormPage clickMenuFilter() {
method searchInBar (line 47) | public BlankFormSearchPage searchInBar(String query) {
method checkMapIconDisplayedForForm (line 52) | public FillBlankFormPage checkMapIconDisplayedForForm(String formName) {
method checkMapIconNotDisplayedForForm (line 59) | public FillBlankFormPage checkMapIconNotDisplayedForForm(String formNa...
method clickOnMapIconForForm (line 66) | public FormMapPage clickOnMapIconForForm(String formName) {
method clickOnForm (line 73) | public FormEntryPage clickOnForm(String formName) {
method clickOnFormButton (line 78) | private void clickOnFormButton(String formName) {
method clickOnEmptyForm (line 84) | public FormEndPage clickOnEmptyForm(String formName) {
method clickRefresh (line 89) | public FillBlankFormPage clickRefresh() {
method clickRefreshWithError (line 94) | public FillBlankFormPage clickRefreshWithError() {
method clickRefreshWithAuthError (line 99) | public ServerAuthDialog clickRefreshWithAuthError() {
method assertFormExists (line 104) | public FillBlankFormPage assertFormExists(String formName) {
method assertFormDoesNotExist (line 115) | public FillBlankFormPage assertFormDoesNotExist(String formName) {
method assertNoForms (line 122) | public FillBlankFormPage assertNoForms() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/FormEndPage.java
class FormEndPage (line 10) | public class FormEndPage extends Page<FormEndPage> {
method FormEndPage (line 14) | public FormEndPage(String formName) {
method assertOnPage (line 18) | @Override
method clickSaveAsDraft (line 24) | public <D extends Page<D>> D clickSaveAsDraft(D destination) {
method clickSaveAsDraft (line 28) | public MainMenuPage clickSaveAsDraft() {
method clickSaveAsDraftWithError (line 32) | public FormEndPage clickSaveAsDraftWithError(String errorMsg) {
method clickFinalize (line 38) | public <D extends Page<D>> D clickFinalize(D destination) {
method clickFinalize (line 42) | public MainMenuPage clickFinalize() {
method clickFinalizeWithError (line 47) | public FormEndPage clickFinalizeWithError(String errorMsg) {
method clickSend (line 53) | public MainMenuPage clickSend() {
method clickSaveAndExitBackToMap (line 57) | public FormMapPage clickSaveAndExitBackToMap() {
method clickSaveAndExitWithError (line 61) | public FormEntryPage clickSaveAndExitWithError(String errorText) {
method clickSaveAndExitWithChangesReasonPrompt (line 67) | public ChangesReasonPromptPage clickSaveAndExitWithChangesReasonPrompt...
method clickGoToArrow (line 71) | public FormHierarchyPage clickGoToArrow() {
method swipeToPreviousQuestion (line 76) | public FormEntryPage swipeToPreviousQuestion(String questionText) {
method swipeToPreviousQuestion (line 80) | public FormEntryPage swipeToPreviousQuestion(String questionText, bool...
method clickOptionsIcon (line 84) | public FormEndPage clickOptionsIcon() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/FormEntryPage.java
class FormEntryPage (line 43) | public class FormEntryPage extends Page<FormEntryPage> {
method FormEntryPage (line 47) | public FormEntryPage(String formName) {
method assertOnPage (line 51) | @Override
method fillOut (line 71) | public FormEntryPage fillOut(QuestionAndAnswer... questionsAndAnswers) {
method fillOutAndSave (line 87) | public <D extends Page<D>> D fillOutAndSave(D destination, QuestionAnd...
method fillOutAndSave (line 93) | public MainMenuPage fillOutAndSave(QuestionAndAnswer... questionsAndAn...
method fillOutAndFinalize (line 99) | public MainMenuPage fillOutAndFinalize(QuestionAndAnswer... questionsA...
method swipeToNextQuestion (line 105) | public FormEntryPage swipeToNextQuestion(String questionText) {
method swipeToNextQuestion (line 109) | public FormEntryPage swipeToNextQuestion(String questionText, boolean ...
method swipeToPreviousQuestion (line 121) | public FormEntryPage swipeToPreviousQuestion(String questionText) {
method swipeToPreviousQuestion (line 125) | public FormEntryPage swipeToPreviousQuestion(String questionText, bool...
method swipeToNextRepeat (line 137) | public FormEntryPage swipeToNextRepeat(String repeatLabel, int repeatN...
method swipeToEndScreen (line 144) | public FormEndPage swipeToEndScreen(String instanceName) {
method swipeToEndScreen (line 149) | public FormEndPage swipeToEndScreen() {
method swipeToNextQuestionWithError (line 154) | public ErrorDialog swipeToNextQuestionWithError(boolean isFatal) {
method swipeToNextQuestionWithConstraintViolation (line 159) | public FormEntryPage swipeToNextQuestionWithConstraintViolation(int co...
method swipeToNextQuestionWithConstraintViolation (line 166) | public FormEntryPage swipeToNextQuestionWithConstraintViolation(String...
method assertQuestionText (line 173) | private void assertQuestionText(String text) {
method clickOptionsIcon (line 177) | public FormEntryPage clickOptionsIcon() {
method clickProjectSettings (line 181) | public ProjectSettingsPage clickProjectSettings() {
method assertNavigationButtonsAreDisplayed (line 186) | public FormEntryPage assertNavigationButtonsAreDisplayed() {
method assertNavigationButtonsAreHidden (line 192) | public FormEntryPage assertNavigationButtonsAreHidden() {
method assertGoToIconExists (line 198) | public FormEntryPage assertGoToIconExists() {
method assertGoToIconDoesNotExist (line 203) | public FormEntryPage assertGoToIconDoesNotExist() {
method clickGoToArrow (line 208) | public FormHierarchyPage clickGoToArrow() {
method clickWidgetButton (line 217) | public FormEntryPage clickWidgetButton() {
method clickRankingButton (line 222) | public FormEntryPage clickRankingButton() {
method deleteGroup (line 227) | public FormEntryPage deleteGroup(String questionText) {
method clickForwardButton (line 234) | public FormEntryPage clickForwardButton() {
method clickForwardButtonToEndScreen (line 240) | public FormEndPage clickForwardButtonToEndScreen() {
method clickBackwardButton (line 246) | public FormEntryPage clickBackwardButton() {
method clickSave (line 252) | public FormEntryPage clickSave() {
method clickSaveWithError (line 257) | public FormEntryPage clickSaveWithError(String errorMsg) {
method clickSaveWithChangesReasonPrompt (line 263) | public ChangesReasonPromptPage clickSaveWithChangesReasonPrompt() {
method clickPlus (line 268) | public AddNewRepeatDialog clickPlus(String repeatName) {
method longPressOnQuestion (line 273) | public FormEntryPage longPressOnQuestion(int id, int index) {
method longPressOnQuestion (line 278) | public FormEntryPage longPressOnQuestion(String question) {
method longPressOnQuestion (line 283) | public FormEntryPage longPressOnQuestion(String question, boolean isRe...
method removeResponse (line 297) | public FormEntryPage removeResponse() {
method swipeToNextQuestionWithRepeatGroup (line 302) | public AddNewRepeatDialog swipeToNextQuestionWithRepeatGroup(String re...
method swipeToPreviousQuestionWithRepeatGroup (line 307) | public AddNewRepeatDialog swipeToPreviousQuestionWithRepeatGroup(Strin...
method answerQuestion (line 312) | public FormEntryPage answerQuestion(String question, String answer) {
method answerQuestion (line 317) | public FormEntryPage answerQuestion(String question, boolean isRequire...
method answerQuestion (line 332) | @Deprecated
method assertAnswer (line 339) | public FormEntryPage assertAnswer(String questionText, String answer) {
method assertAnswer (line 343) | public FormEntryPage assertAnswer(String questionText, String answer, ...
method clickOnQuestionField (line 354) | public FormEntryPage clickOnQuestionField(String questionText) {
method assertQuestion (line 359) | public FormEntryPage assertQuestion(String text) {
method assertQuestion (line 363) | public FormEntryPage assertQuestion(String text, boolean isRequired) {
method assertNoQuestion (line 373) | public FormEntryPage assertNoQuestion(String text) {
method assertNoQuestion (line 377) | public FormEntryPage assertNoQuestion(String text, boolean isRequired) {
method flingLeft (line 387) | private void flingLeft() {
method flingRight (line 393) | private void flingRight() {
method openSelectMinimalDialog (line 399) | public SelectMinimalDialogPage openSelectMinimalDialog() {
method openSelectMinimalDialog (line 403) | public SelectMinimalDialogPage openSelectMinimalDialog(int index) {
method assertSelectMinimalDialogAnswer (line 408) | public FormEntryPage assertSelectMinimalDialogAnswer(@Nullable String ...
method swipeToEndScreenWhileRecording (line 417) | public OkDialog swipeToEndScreenWhileRecording() {
method clickRecordAudio (line 424) | public CancelRecordingDialog clickRecordAudio() {
method pressBackAndDiscardChanges (line 429) | public MainMenuPage pressBackAndDiscardChanges() {
method pressBackAndDiscardChanges (line 435) | public <D extends Page<D>> D pressBackAndDiscardChanges(D destination) {
method pressBackAndSaveAsDraft (line 441) | public MainMenuPage pressBackAndSaveAsDraft() {
method pressBackAndSaveAsDraft (line 447) | public <D extends Page<D>> D pressBackAndSaveAsDraft(D destination) {
method pressBackAndDiscardForm (line 453) | public MainMenuPage pressBackAndDiscardForm() {
method pressBackAndDiscardForm (line 459) | public <D extends Page<D>> D pressBackAndDiscardForm(D destination) {
method assertBackgroundLocationSnackbarShown (line 465) | public FormEntryPage assertBackgroundLocationSnackbarShown() {
method setRating (line 470) | public FormEntryPage setRating(float value) {
method assertQuestionHasFocus (line 475) | public FormEntryPage assertQuestionHasFocus(String questionText) {
method getQuestionFieldMatcher (line 481) | private static @NonNull Matcher<View> getQuestionFieldMatcher(String q...
method assertImageViewShowsImage (line 488) | public FormEntryPage assertImageViewShowsImage(int resourceid, Bitmap ...
class QuestionAndAnswer (line 493) | public static class QuestionAndAnswer {
method QuestionAndAnswer (line 499) | public QuestionAndAnswer(String question, String answer) {
method QuestionAndAnswer (line 503) | public QuestionAndAnswer(String question, String answer, boolean isR...
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/FormManagementPage.java
class FormManagementPage (line 15) | public class FormManagementPage extends Page<FormManagementPage> {
method assertOnPage (line 17) | @Override
method clickUpdateForms (line 23) | public ListPreferenceDialog<FormManagementPage> clickUpdateForms() {
method clickAutomaticUpdateFrequency (line 28) | public ListPreferenceDialog<FormManagementPage> clickAutomaticUpdateFr...
method clickAutoSend (line 33) | public ListPreferenceDialog<FormManagementPage> clickAutoSend() {
method openShowGuidanceForQuestions (line 38) | public FormManagementPage openShowGuidanceForQuestions() {
method openConstraintProcessing (line 43) | public FormManagementPage openConstraintProcessing() {
method scrollToConstraintProcessing (line 48) | public FormManagementPage scrollToConstraintProcessing() {
method checkIfConstraintProcessingIsDisabled (line 54) | public FormManagementPage checkIfConstraintProcessingIsDisabled() {
method checkIfConstraintProcessingIsEnabled (line 59) | public FormManagementPage checkIfConstraintProcessingIsEnabled() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/FormMapPage.java
class FormMapPage (line 5) | public class FormMapPage extends Page<FormMapPage> {
method FormMapPage (line 9) | public FormMapPage(String formName) {
method assertOnPage (line 13) | @Override
method clickFillBlankFormButton (line 20) | public FormEntryPage clickFillBlankFormButton(String formName) {
method selectForm (line 25) | public FormMapPage selectForm(FakeClickableMapFragment mapFragment, in...
method clickEditSavedForm (line 30) | public FormHierarchyPage clickEditSavedForm(String formName) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/FormMetadataPage.java
class FormMetadataPage (line 7) | public class FormMetadataPage extends PreferencePage<FormMetadataPage> {
method assertOnPage (line 9) | @Override
method clickEmail (line 15) | public FormMetadataPage clickEmail() {
method clickUsername (line 20) | public FormMetadataPage clickUsername() {
method clickPhoneNumber (line 25) | public FormMetadataPage clickPhoneNumber() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/GetBlankFormPage.java
class GetBlankFormPage (line 11) | public class GetBlankFormPage extends Page<GetBlankFormPage> {
method assertOnPage (line 13) | @Override
method clickGetSelected (line 19) | public FormsDownloadResultPage clickGetSelected() {
method clickClearAll (line 24) | public GetBlankFormPage clickClearAll() {
method clickSelectAll (line 29) | public GetBlankFormPage clickSelectAll() {
method clickForm (line 34) | public GetBlankFormPage clickForm(String formName) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/IdentifyUserPromptPage.java
class IdentifyUserPromptPage (line 10) | public class IdentifyUserPromptPage extends Page<IdentifyUserPromptPage> {
method IdentifyUserPromptPage (line 14) | public IdentifyUserPromptPage(String formName) {
method assertOnPage (line 19) | @Override
method enterIdentity (line 26) | public IdentifyUserPromptPage enterIdentity(String identity) {
method clickKeyboardEnter (line 31) | public <D extends Page<D>> D clickKeyboardEnter(D destination) {
method clickKeyboardEnterWithValidationError (line 36) | public IdentifyUserPromptPage clickKeyboardEnterWithValidationError() {
method pressClose (line 41) | public MainMenuPage pressClose() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/ListPreferenceDialog.java
class ListPreferenceDialog (line 3) | public class ListPreferenceDialog<T extends Page<T>> extends Page<ListPr...
method ListPreferenceDialog (line 8) | ListPreferenceDialog(int title, T page) {
method assertOnPage (line 13) | @Override
method clickOption (line 19) | public T clickOption(int option) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/MainMenuPage.java
class MainMenuPage (line 26) | public class MainMenuPage extends Page<MainMenuPage> {
method assertOnPage (line 28) | @Override
method openProjectSettingsDialog (line 40) | public ProjectSettingsDialogPage openProjectSettingsDialog() {
method startBlankForm (line 47) | public FormEntryPage startBlankForm(String formName) {
method startBlankFormWithSavepoint (line 52) | public SavepointRecoveryDialogPage startBlankFormWithSavepoint(String ...
method startBlankFormWithRepeatGroup (line 57) | public AddNewRepeatDialog startBlankFormWithRepeatGroup(String formNam...
method startBlankFormWithError (line 62) | public ErrorDialog startBlankFormWithError(String formName, boolean is...
method startBlankFormWithDialog (line 67) | public OkDialog startBlankFormWithDialog(String formName) {
method clickFillBlankForm (line 72) | public FillBlankFormPage clickFillBlankForm() {
method goToBlankForm (line 80) | private void goToBlankForm(String formName) {
method clickDrafts (line 84) | public EditSavedFormPage clickDrafts() {
method clickDrafts (line 89) | public EditSavedFormPage clickDrafts(int formCount) {
method assertNumberOfFinalizedForms (line 94) | public MainMenuPage assertNumberOfFinalizedForms(int number) {
method assertNumberOfEditableForms (line 103) | public MainMenuPage assertNumberOfEditableForms(int number) {
method assertNumberOfSentForms (line 113) | private MainMenuPage assertNumberOfSentForms(int number) {
method clickGetBlankForm (line 123) | public GetBlankFormPage clickGetBlankForm() {
method clickGetBlankForm (line 127) | public <D extends Page<D>> D clickGetBlankForm(D destination) {
method clickSendFinalizedForm (line 132) | public SendFinalizedFormPage clickSendFinalizedForm(int number) {
method setServer (line 138) | public MainMenuPage setServer(String url) {
method enableManualUpdates (line 149) | public MainMenuPage enableManualUpdates() {
method enablePreviouslyDownloadedOnlyUpdates (line 159) | public MainMenuPage enablePreviouslyDownloadedOnlyUpdates() {
method enablePreviouslyDownloadedOnlyUpdatesWithAutomaticDownload (line 169) | public MainMenuPage enablePreviouslyDownloadedOnlyUpdatesWithAutomatic...
method enableMatchExactly (line 180) | public MainMenuPage enableMatchExactly() {
method enableAutoSend (line 190) | public MainMenuPage enableAutoSend(TrackingCoroutineAndWorkManagerSche...
method addAndSwitchToProject (line 203) | public MainMenuPage addAndSwitchToProject(String serverUrl) {
method clickGetBlankFormWithAuthenticationError (line 211) | public ServerAuthDialog clickGetBlankFormWithAuthenticationError() {
method clickGetBlankFormWithError (line 216) | public OkDialog clickGetBlankFormWithError() {
method clickViewSentForm (line 221) | public ViewSentFormPage clickViewSentForm(int number) {
method clickDeleteSavedForm (line 227) | public DeleteSavedFormPage clickDeleteSavedForm() {
method assertProjectIcon (line 232) | public MainMenuPage assertProjectIcon(String projectIcon) {
method copyForm (line 237) | public MainMenuPage copyForm(String formFilename) {
method copyForm (line 241) | public MainMenuPage copyForm(String formFilename, String projectName) {
method copyForm (line 245) | public MainMenuPage copyForm(String formFilename, List<String> mediaFi...
method copyForm (line 249) | public MainMenuPage copyForm(String formFilename, List<String> mediaFi...
method copyForm (line 253) | public MainMenuPage copyForm(String formFilename, List<String> mediaFi...
method copyInstance (line 263) | public MainMenuPage copyInstance(String instanceFileName) {
method copyInstance (line 268) | public MainMenuPage copyInstance(String instanceFileName, String proje...
method openEntityBrowser (line 278) | public EntitiesPage openEntityBrowser() {
method refreshForms (line 287) | public MainMenuPage refreshForms() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/MapsSettingsPage.java
class MapsSettingsPage (line 3) | public class MapsSettingsPage extends Page<MapsSettingsPage> {
method assertOnPage (line 5) | @Override
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/OkDialog.java
class OkDialog (line 25) | public class OkDialog extends Page<OkDialog> {
method assertOnPage (line 27) | @Override
method clickOK (line 33) | public <D extends Page<D>> D clickOK(D destination) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/OpenSourceLicensesPage.java
class OpenSourceLicensesPage (line 3) | class OpenSourceLicensesPage extends Page<OpenSourceLicensesPage> {
method assertOnPage (line 5) | @Override
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/PreferencePage.java
class PreferencePage (line 14) | abstract class PreferencePage<T extends Page<T>> extends Page<T> {
method assertPreference (line 16) | public T assertPreference(int name, String summary) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/ProjectSettingsPage.java
class ProjectSettingsPage (line 10) | public class ProjectSettingsPage extends Page<ProjectSettingsPage> {
method assertOnPage (line 12) | @Override
method clickProjectDisplay (line 18) | public ProjectDisplayPage clickProjectDisplay() {
method clickProjectManagement (line 23) | public ProjectManagementPage clickProjectManagement() {
method clickAccessControl (line 28) | public AccessControlPage clickAccessControl() {
method clickOnUserInterface (line 33) | public UserInterfacePage clickOnUserInterface() {
method openFormManagement (line 38) | public FormManagementPage openFormManagement() {
method clickServerSettings (line 43) | public ServerSettingsPage clickServerSettings() {
method clickMaps (line 48) | public MapsSettingsPage clickMaps() {
method clickUserAndDeviceIdentity (line 54) | public UserAndDeviceIdentitySettingsPage clickUserAndDeviceIdentity() {
method checkIfServerOptionIsDisplayed (line 59) | public ProjectSettingsPage checkIfServerOptionIsDisplayed() {
method checkIfFormManagementOptionIsDisplayed (line 64) | public ProjectSettingsPage checkIfFormManagementOptionIsDisplayed() {
method checkIfServerOptionIsNotDisplayed (line 69) | public ProjectSettingsPage checkIfServerOptionIsNotDisplayed() {
method clickFormManagement (line 74) | public FormManagementPage clickFormManagement() {
method clickExperimental (line 79) | public ExperimentalPage clickExperimental() {
method setAdminPassword (line 84) | public ProjectSettingsPage setAdminPassword(String password) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/QRCodePage.java
class QRCodePage (line 19) | public class QRCodePage extends Page<QRCodePage> {
method assertOnPage (line 20) | @Override
method clickScanFragment (line 26) | public QRCodePage clickScanFragment() {
method clickView (line 31) | public QRCodePage clickView() {
method assertImageViewShowsImage (line 42) | public QRCodePage assertImageViewShowsImage(int resourceid, Bitmap ima...
method clickOnMenu (line 47) | public QRCodePage clickOnMenu() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/ResetApplicationDialog.java
class ResetApplicationDialog (line 9) | public class ResetApplicationDialog extends Page<ResetApplicationDialog> {
method assertOnPage (line 11) | @Override
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/ServerAuthDialog.java
class ServerAuthDialog (line 3) | public class ServerAuthDialog extends Page<ServerAuthDialog> {
method assertOnPage (line 5) | @Override
method fillUsername (line 11) | public ServerAuthDialog fillUsername(String username) {
method fillPassword (line 16) | public ServerAuthDialog fillPassword(String password) {
method clickOK (line 21) | public <D extends Page<D>> D clickOK(D destination) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/ServerSettingsPage.java
class ServerSettingsPage (line 7) | public class ServerSettingsPage extends Page<ServerSettingsPage> {
method assertOnPage (line 9) | @Override
method clickServerUsername (line 15) | public ServerSettingsPage clickServerUsername() {
method clickOnURL (line 20) | public ServerSettingsPage clickOnURL() {
method clickServerPassword (line 25) | public ServerSettingsPage clickServerPassword() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/ShortcutsPage.java
class ShortcutsPage (line 9) | public class ShortcutsPage extends Page<ShortcutsPage> {
method ShortcutsPage (line 13) | public ShortcutsPage(ActivityScenario<AndroidShortcutsActivity> scenar...
method assertOnPage (line 17) | @Override
method selectForm (line 23) | public Intent selectForm(String formName) {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/UserAndDeviceIdentitySettingsPage.java
class UserAndDeviceIdentitySettingsPage (line 3) | public class UserAndDeviceIdentitySettingsPage extends Page<UserAndDevic...
method assertOnPage (line 5) | @Override
method clickFormMetadata (line 11) | public FormMetadataPage clickFormMetadata() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/UserInterfacePage.java
class UserInterfacePage (line 9) | public class UserInterfacePage extends Page<UserInterfacePage> {
method assertOnPage (line 11) | @Override
method clickOnLanguage (line 17) | public UserInterfacePage clickOnLanguage() {
method clickOnSelectedLanguage (line 22) | public MainMenuPage clickOnSelectedLanguage(String language) {
method clickNavigation (line 27) | public UserInterfacePage clickNavigation() {
method clickUseSwipesAndButtons (line 32) | public UserInterfacePage clickUseSwipesAndButtons() {
method clickOnTheme (line 37) | public UserInterfacePage clickOnTheme() {
method clickUseNavigationButtons (line 42) | public UserInterfacePage clickUseNavigationButtons() {
method clickSwipes (line 47) | public UserInterfacePage clickSwipes() {
FILE: collect_app/src/androidTest/java/org/odk/collect/android/support/pages/ViewSentFormPage.java
class ViewSentFormPage (line 9) | public class ViewSentFormPage extends Page<ViewSentFormPage> {
method assertOnPage (line 11) | @Override
method clickOnForm (line 17) | public ViewFormPage clickOnForm(String formName) {
FILE: collect_app/src/main/assets/svg_map_helper.js
function selectArea (line 25) | function selectArea(areaId) {
function unselectArea (line 29) | function unselectArea(areaId) {
function notifyChanges (line 33) | function notifyChanges() {
function clearAreas (line 37) | function clearAreas() {
function addSelectedArea (line 47) | function addSelectedArea(selectedAreaId) {
function addArea (line 55) | function addArea(areaId) {
function setSelectMode (line 59) | function setSelectMode(isSingleSelect) {
function clickOnArea (line 63) | function clickOnArea(areaId) {
FILE: collect_app/src/main/java/org/odk/collect/android/activities/ActivityUtils.java
class ActivityUtils (line 6) | public final class ActivityUtils {
method ActivityUtils (line 8) | private ActivityUtils() {
method startActivityAndCloseAllOthers (line 12) | public static <A extends Activity> void startActivityAndCloseAllOthers...
FILE: collect_app/src/main/java/org/odk/collect/android/activities/AppListActivity.java
class AppListActivity (line 53) | public abstract class AppListActivity extends LocalizedActivity {
method onCreate (line 76) | @Override
method toggleChecked (line 90) | public static boolean toggleChecked(ListView lv) {
method setAllToCheckedState (line 101) | public static void setAllToCheckedState(ListView lv, boolean check) {
method toggleButtonLabel (line 113) | public static void toggleButtonLabel(Button toggleButton, ListView lv) {
method setContentView (line 121) | @Override
method setContentView (line 127) | @Override
method init (line 133) | private void init() {
method onResume (line 150) | @Override
method onSaveInstanceState (line 156) | @Override
method onRestoreInstanceState (line 169) | @Override
method onCreateOptionsMenu (line 177) | @Override
method onOptionsItemSelected (line 223) | @Override
method checkPreviouslyCheckedItems (line 245) | protected void checkPreviouslyCheckedItems() {
method updateAdapter (line 265) | protected abstract void updateAdapter();
method getSortingOrderKey (line 267) | protected abstract String getSortingOrderKey();
method areCheckedItems (line 269) | protected boolean areCheckedItems() {
method getCheckedCount (line 273) | protected int getCheckedCount() {
method saveSelectedSortingOrder (line 277) | private void saveSelectedSortingOrder(int selectedStringOrder) {
method restoreSelectedSortingOrder (line 282) | protected void restoreSelectedSortingOrder() {
method getSelectedSortingOrder (line 286) | protected int getSelectedSortingOrder() {
method getFilterText (line 293) | protected CharSequence getFilterText() {
method clearSearchView (line 297) | protected void clearSearchView() {
method hideProgressBarAndAllow (line 301) | protected void hideProgressBarAndAllow() {
method hideProgressBar (line 305) | private void hideProgressBar() {
method showProgressBar (line 309) | protected void showProgressBar() {
FILE: collect_app/src/main/java/org/odk/collect/android/activities/BearingActivity.java
class BearingActivity (line 31) | public class BearingActivity extends LocalizedActivity implements Sensor...
method onCreate (line 43) | @Override
method onPause (line 55) | @Override
method onResume (line 67) | @Override
method setupBearingDialog (line 79) | private void setupBearingDialog() {
method returnBearing (line 111) | private void returnBearing() {
method onAccuracyChanged (line 119) | @Override
method onSensorChanged (line 125) | @Override
method formatDegrees (line 179) | public static String formatDegrees(double degrees) {
method normalizeDegrees (line 183) | public static double normalizeDegrees(double value) {
FILE: collect_app/src/main/java/org/odk/collect/android/activities/FormDownloadListActivity.java
class FormDownloadListActivity (line 94) | public class FormDownloadListActivity extends FormListActivity implement...
method onCreate (line 134) | @SuppressWarnings("unchecked")
method init (line 149) | private void init(Bundle savedInstanceState) {
method clearChoices (line 252) | private void clearChoices() {
method onItemClick (line 257) | @Override
method downloadFormList (line 272) | private void downloadFormList() {
method onRestoreInstanceState (line 307) | @Override
method onSaveInstanceState (line 313) | @Override
method getSortingOrderKey (line 319) | @Override
method updateAdapter (line 324) | @Override
method checkPreviouslyCheckedItems (line 350) | @Override
method sortList (line 362) | private void sortList() {
method getFilesToDownload (line 372) | private ArrayList<ServerFormDetails> getFilesToDownload() {
method startFormsDownload (line 389) | @SuppressWarnings("unchecked")
method onRetainCustomNonConfigurationInstance (line 413) | @Override
method onDestroy (line 422) | @Override
method onResume (line 433) | @Override
method onPause (line 450) | @Override
method isLocalFormSuperseded (line 458) | public boolean isLocalFormSuperseded(String formId) {
method selectSupersededForms (line 473) | private void selectSupersededForms() {
method formListDownloadingComplete (line 484) | @Override
method performDownloadModeDownload (line 551) | private void performDownloadModeDownload() {
method createAlertDialog (line 582) | private void createAlertDialog(String title, String message, final boo...
method createAuthDialog (line 611) | private void createAuthDialog() {
method createCancelDialog (line 622) | private void createCancelDialog() {
method progressUpdate (line 632) | @Override
method formsDownloadingComplete (line 642) | @Override
method formsDownloadingCancelled (line 670) | @Override
method updatedCredentials (line 690) | @Override
method cancelledUpdatingCredentials (line 706) | @Override
method setReturnResult (line 711) | private void setReturnResult(boolean successful, @Nullable String mess...
method cleanUpWebCredentials (line 724) | private void cleanUpWebCredentials() {
method onCancelFormLoading (line 735) | @Override
method onCloseDownloadingResult (line 758) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/activities/FormFillingActivity.java
class FormFillingActivity (line 226) | public class FormFillingActivity extends LocalizedActivity implements Co...
method allowSwiping (line 293) | @Override
method handleOnBackPressed (line 396) | @Override
method onCreate (line 416) | @Override
method setupViewModels (line 556) | private void setupViewModels(FormEntryViewModelFactory formEntryViewMo...
method handleValidationResult (line 649) | private void handleValidationResult(ODKView view, ValidationResult val...
method formControllerAvailable (line 667) | private void formControllerAvailable(@NonNull FormController formContr...
method setupFields (line 673) | private void setupFields(Bundle savedInstanceState) {
method loadForm (line 699) | private void loadForm() {
method loadFromIntent (line 736) | private void loadFromIntent(Intent intent) {
method initToolbar (line 751) | private void initToolbar() {
method nonblockingCreateSavePointData (line 763) | private void nonblockingCreateSavePointData() {
method getFormController (line 794) | @Nullable
method onSaveInstanceState (line 799) | @Override
method onActivityResult (line 837) | @Override
method loadMedia (line 930) | private void loadMedia(Uri uri) {
method getWidgetWaitingForBinaryData (line 940) | public QuestionWidget getWidgetWaitingForBinaryData() {
method setWidgetData (line 955) | public void setWidgetData(Object data) {
method clearAnswer (line 986) | private void clearAnswer(QuestionWidget qw) {
method onCreateContextMenu (line 994) | @Override
method onContextItemSelected (line 1009) | @Override
method deleteGroup (line 1028) | public void deleteGroup() {
method onRetainCustomNonConfigurationInstance (line 1041) | @Override
method createView (line 1060) | private SwipeHandler.View createView(int event, boolean advancingPage) {
method createODKView (line 1128) | @NotNull
method releaseOdkView (line 1140) | private void releaseOdkView() {
method createViewForFormEnd (line 1155) | private SwipeHandler.View createViewForFormEnd(FormController formCont...
method dispatchTouchEvent (line 1195) | @Override
method onSwipeForward (line 1205) | @Override
method onSwipeBackward (line 1210) | @Override
method moveScreen (line 1215) | private void moveScreen(Direction direction) {
method onScreenChange (line 1265) | @Override
method onScreenRefresh (line 1289) | @Override
method animateToNextView (line 1299) | private void animateToNextView(int event) {
method animateToPreviousView (line 1322) | private void animateToPreviousView(int event) {
method showView (line 1332) | public void showView(SwipeHandler.View next, FormAnimationType from) {
method createRepeatDialog (line 1425) | private void createRepeatDialog() {
method createErrorDialog (line 1483) | private void createErrorDialog(FormError error) {
method saveForm (line 1518) | public boolean saveForm(boolean exit, boolean complete, String updated...
method handleSaveResult (line 1533) | private void handleSaveResult(FormSaveViewModel.SaveResult result) {
method getAbsoluteInstancePath (line 1613) | @Nullable
method createClearDialog (line 1622) | private void createClearDialog(final QuestionWidget qw) {
method createLanguageDialog (line 1660) | public void createLanguageDialog() {
method updateNavigationButtonVisibility (line 1694) | private void updateNavigationButtonVisibility() {
method onStart (line 1704) | @Override
method onPause (line 1722) | @Override
method onResume (line 1729) | @Override
method onResumeFragments (line 1772) | @Override
method onKeyDown (line 1788) | @Override
method onDestroy (line 1809) | @Override
method afterAllAnimations (line 1838) | private void afterAllAnimations() {
method onAnimationEnd (line 1855) | @Override
method onAnimationRepeat (line 1870) | @Override
method onAnimationStart (line 1874) | @Override
method loadingComplete (line 1894) | @Override
method loadingError (line 2037) | @Override
method showFormLoadErrorAndExit (line 2042) | private void showFormLoadErrorAndExit(String errorMsg) {
method onProgressStep (line 2052) | public void onProgressStep(String stepMessage) {
method next (line 2061) | public void next() {
method finishAndReturnInstance (line 2072) | private void finishAndReturnInstance() {
method exit (line 2094) | private void exit() {
method advance (line 2100) | @Override
method onSavePointError (line 2105) | @Override
method onSaveFormIndexError (line 2112) | @Override
method onDateChanged (line 2119) | @Override
method onTimeChanged (line 2124) | @Override
method onRankingChanged (line 2129) | @Override
method updateSelectedItems (line 2138) | @Override
method onCancelFormLoading (line 2148) | @Override
method onDataChanged (line 2160) | private void onDataChanged(Object data) {
method getCurrentViewIfODKView (line 2173) | @Nullable
class EmptyView (line 2184) | static class EmptyView extends SwipeHandler.View {
method EmptyView (line 2185) | EmptyView(Context context) {
method shouldSuppressFlingGesture (line 2189) | @Override
method verticalScrollView (line 2194) | @Nullable
class LocationProvidersReceiver (line 2201) | private class LocationProvidersReceiver extends BroadcastReceiver {
method onReceive (line 2202) | @Override
method activityDisplayed (line 2211) | private void activityDisplayed() {
method displayUIFor (line 2232) | private void displayUIFor(@Nullable BackgroundLocationManager.Backgrou...
method widgetValueChanged (line 2259) | @Override
method updateFieldListQuestions (line 2299) | private void updateFieldListQuestions(FormIndex lastChangedIndex) thro...
method getAnswers (line 2309) | private HashMap<FormIndex, IAnswerData> getAnswers() {
FILE: collect_app/src/main/java/org/odk/collect/android/activities/FormListActivity.java
class FormListActivity (line 10) | public abstract class FormListActivity extends AppListActivity {
method getSortingOrder (line 19) | protected String getSortingOrder() {
FILE: collect_app/src/main/java/org/odk/collect/android/activities/InstanceChooserList.java
class InstanceChooserList (line 74) | @Deprecated
method onCreate (line 107) | @Override
method init (line 174) | private void init() {
method onItemClick (line 182) | @Override
method setupAdapter (line 218) | private void setupAdapter() {
method getSortingOrderKey (line 231) | @Override
method updateAdapter (line 236) | @Override
method onCreateLoader (line 241) | @NonNull
method onLoadFinished (line 252) | @Override
method onLoaderReset (line 258) | @Override
method getSortingOrder (line 263) | protected String getSortingOrder() {
FILE: collect_app/src/main/java/org/odk/collect/android/activities/viewmodels/FormDownloadListViewModel.java
class FormDownloadListViewModel (line 30) | public class FormDownloadListViewModel extends ViewModel {
method getFormDetailsByFormId (line 59) | public HashMap<String, ServerFormDetails> getFormDetailsByFormId() {
method setFormDetailsByFormId (line 63) | public void setFormDetailsByFormId(HashMap<String, ServerFormDetails> ...
method clearFormDetailsByFormId (line 67) | public void clearFormDetailsByFormId() {
method getAlertTitle (line 71) | public String getAlertTitle() {
method setAlertTitle (line 75) | public void setAlertTitle(String alertTitle) {
method getAlertDialogMsg (line 79) | public String getAlertDialogMsg() {
method setAlertDialogMsg (line 83) | public void setAlertDialogMsg(String alertDialogMsg) {
method isAlertShowing (line 87) | public boolean isAlertShowing() {
method setAlertShowing (line 91) | public void setAlertShowing(boolean alertShowing) {
method shouldExit (line 95) | public boolean shouldExit() {
method setShouldExit (line 99) | public void setShouldExit(boolean shouldExit) {
method getFormList (line 103) | public ArrayList<HashMap<String, String>> getFormList() {
method clearFormList (line 107) | public void clearFormList() {
method addForm (line 111) | public void addForm(HashMap<String, String> item) {
method addForm (line 115) | public void addForm(int index, HashMap<String, String> item) {
method getSelectedFormIds (line 119) | public LinkedHashSet<String> getSelectedFormIds() {
method addSelectedFormId (line 123) | public void addSelectedFormId(String selectedFormId) {
method removeSelectedFormId (line 127) | public void removeSelectedFormId(String selectedFormId) {
method clearSelectedFormIds (line 131) | public void clearSelectedFormIds() {
method isDownloadOnlyMode (line 135) | public boolean isDownloadOnlyMode() {
method setDownloadOnlyMode (line 139) | public void setDownloadOnlyMode(boolean downloadOnlyMode) {
method getFormResults (line 143) | public HashMap<String, Boolean> getFormResults() {
method putFormResult (line 147) | public void putFormResult(String formId, boolean result) {
method getPassword (line 151) | public String getPassword() {
method setPassword (line 155) | public void setPassword(String password) {
method getUsername (line 159) | public String getUsername() {
method setUsername (line 163) | public void setUsername(String username) {
method getUrl (line 167) | public String getUrl() {
method setUrl (line 171) | public void setUrl(String url) {
method getFormIdsToDownload (line 175) | public String[] getFormIdsToDownload() {
method setFormIdsToDownload (line 179) | public void setFormIdsToDownload(String[] formIdsToDownload) {
method isCancelDialogShowing (line 183) | public boolean isCancelDialogShowing() {
method setCancelDialogShowing (line 187) | public void setCancelDialogShowing(boolean cancelDialogShowing) {
method wasLoadingCanceled (line 191) | public boolean wasLoadingCanceled() {
method setLoadingCanceled (line 195) | public void setLoadingCanceled(boolean loadingCanceled) {
class Factory (line 199) | public static class Factory implements ViewModelProvider.Factory {
method create (line 201) | @SuppressWarnings("unchecked")
FILE: collect_app/src/main/java/org/odk/collect/android/adapters/AbstractSelectListAdapter.java
class AbstractSelectListAdapter (line 62) | public abstract class AbstractSelectListAdapter extends RecyclerView.Ada...
method AbstractSelectListAdapter (line 76) | AbstractSelectListAdapter(Context context, List<SelectChoice> items, F...
method onBindViewHolder (line 91) | @Override
method getItemCount (line 96) | @Override
method getFilter (line 101) | @Override
method createButton (line 133) | abstract CompoundButton createButton(int index, ViewGroup parent);
method setUpButton (line 135) | void setUpButton(TextView button, int index) {
method isItemSelected (line 141) | boolean isItemSelected(List<Selection> selectedItems, @NonNull Selecti...
method onItemClick (line 150) | abstract void onItemClick(Selection selection, View view);
method getSelectedItems (line 152) | public abstract List<Selection> getSelectedItems();
method getFilteredItems (line 154) | public List<SelectChoice> getFilteredItems() {
method getAudioPlayer (line 158) | public AudioPlayer getAudioPlayer() {
method setContext (line 162) | public void setContext(Context context) {
method setAudioPlayer (line 166) | public void setAudioPlayer(AudioPlayer audioPlayer) {
class ViewHolder (line 170) | abstract class ViewHolder extends RecyclerView.ViewHolder {
method ViewHolder (line 174) | ViewHolder(View itemView) {
method bind (line 178) | void bind(final int index) {
method getImageFile (line 190) | private File getImageFile(int index) {
method getChoiceText (line 204) | private String getChoiceText(int index) {
method getErrorMsg (line 209) | private String getErrorMsg(File imageFile) {
method enableLongClickToAllowRemovingAnswers (line 213) | private void enableLongClickToAllowRemovingAnswers(View view) {
method addMediaFromChoice (line 227) | public void addMediaFromChoice(AudioVideoImageTextLabel audioVideoIm...
method getImageURI (line 258) | private String getImageURI(int index, List<SelectChoice> items) {
method adjustAudioVideoImageTextLabelForFlexAppearance (line 269) | void adjustAudioVideoImageTextLabelForFlexAppearance() {
method playAudio (line 282) | public void playAudio(SelectChoice selectChoice) {
method getNumColumns (line 290) | public int getNumColumns() {
method clearAnswer (line 294) | public abstract void clearAnswer();
method hasAnswerChanged (line 296) | public abstract boolean hasAnswerChanged();
FILE: collect_app/src/main/java/org/odk/collect/android/adapters/FormDownloadListAdapter.java
class FormDownloadListAdapter (line 37) | public class FormDownloadListAdapter extends ArrayAdapter {
method FormDownloadListAdapter (line 42) | public FormDownloadListAdapter(Context context, ArrayList<HashMap<Stri...
method setFromIdsToDetails (line 49) | public void setFromIdsToDetails(HashMap<String, ServerFormDetails> for...
class ViewHolder (line 53) | private static class ViewHolder {
method getView (line 59) | public View getView(int position, View convertView, @NonNull ViewGroup...
FILE: collect_app/src/main/java/org/odk/collect/android/adapters/InstanceListCursorAdapter.java
class InstanceListCursorAdapter (line 31) | public class InstanceListCursorAdapter extends SimpleCursorAdapter {
method InstanceListCursorAdapter (line 35) | public InstanceListCursorAdapter(Context context, int layout, Cursor c...
method setOnItemClickListener (line 40) | public void setOnItemClickListener(OnItemClickListener onItemClickList...
method getView (line 44) | @Override
type OnItemClickListener (line 64) | public interface OnItemClickListener {
method onItemClick (line 65) | void onItemClick(View view, int position);
FILE: collect_app/src/main/java/org/odk/collect/android/adapters/InstanceUploaderAdapter.java
class InstanceUploaderAdapter (line 19) | public class InstanceUploaderAdapter extends CursorAdapter {
method InstanceUploaderAdapter (line 23) | public InstanceUploaderAdapter(Context context, Cursor cursor, Consume...
method newView (line 28) | @Override
method bindView (line 35) | @Override
method setSelected (line 49) | public void setSelected(Set<Long> ids) {
FILE: collect_app/src/main/java/org/odk/collect/android/adapters/RankingListAdapter.java
class RankingListAdapter (line 41) | public class RankingListAdapter extends Adapter<ItemViewHolder> {
method RankingListAdapter (line 46) | public RankingListAdapter(List<SelectChoice> items, FormEntryPrompt fo...
method onCreateViewHolder (line 51) | @NonNull
method onBindViewHolder (line 57) | @Override
method onItemMove (line 63) | public void onItemMove(int fromPosition, int toPosition) {
method getItemCount (line 68) | @Override
method getItems (line 73) | public List<SelectChoice> getItems() {
class ItemViewHolder (line 77) | public static class ItemViewHolder extends ViewHolder {
method ItemViewHolder (line 83) | ItemViewHolder(View itemView) {
method onItemSelected (line 91) | public void onItemSelected() {
method onItemClear (line 98) | public void onItemClear() {
FILE: collect_app/src/main/java/org/odk/collect/android/adapters/SelectMultipleListAdapter.java
class SelectMultipleListAdapter (line 44) | public class SelectMultipleListAdapter extends AbstractSelectListAdapter {
method SelectMultipleListAdapter (line 50) | public SelectMultipleListAdapter(List<Selection> selectedItems, Select...
method onCreateViewHolder (line 60) | @Override
class ViewHolder (line 67) | class ViewHolder extends AbstractSelectListAdapter.ViewHolder {
method ViewHolder (line 68) | ViewHolder(View v) {
method bind (line 79) | void bind(final int index) {
method createButton (line 95) | @Override
method checkCheckBoxIfNeeded (line 115) | private void checkCheckBoxIfNeeded(CheckBox checkBox, int index) {
method onItemClick (line 125) | @Override
method addItem (line 146) | public void addItem(Selection item) {
method removeItem (line 152) | public void removeItem(Selection item) {
method clearAnswer (line 161) | @Override
method getSelectedItems (line 167) | @Override
method hasAnswerChanged (line 172) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/adapters/SelectOneListAdapter.java
class SelectOneListAdapter (line 46) | public class SelectOneListAdapter extends AbstractSelectListAdapter impl...
method SelectOneListAdapter (line 53) | public SelectOneListAdapter(String selectedValue, SelectItemClickListe...
method onCreateViewHolder (line 62) | @Override
method onCheckedChanged (line 69) | @Override
method setSelectItemClickListener (line 80) | public void setSelectItemClickListener(SelectItemClickListener listene...
class ViewHolder (line 84) | class ViewHolder extends AbstractSelectListAdapter.ViewHolder {
method ViewHolder (line 85) | ViewHolder(View v) {
method bind (line 96) | void bind(final int index) {
method createButton (line 111) | @Override
method onItemClick (line 125) | @Override
method getSelectedItems (line 139) | @Override
method clearAnswer (line 146) | @Override
method getSelectedItem (line 158) | public Selection getSelectedItem() {
method hasAnswerChanged (line 169) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/application/Collect.java
class Collect (line 94) | public class Collect extends Application implements
method getInstance (line 131) | @Deprecated
method getExternalDataManager (line 136) | public ExternalDataManager getExternalDataManager() {
method setExternalDataManager (line 140) | public void setExternalDataManager(ExternalDataManager externalDataMan...
method onCreate (line 144) | @Override
method setupDagger (line 163) | private void setupDagger() {
method getAudioRecorderDependencyComponent (line 202) | @NotNull
method getProjectsDependencyComponent (line 208) | @NotNull
method onConfigurationChanged (line 214) | @Override
method getComponent (line 222) | @Nullable
method setComponent (line 227) | public void setComponent(AppDependencyComponent applicationComponent) {
method getFormIdentifierHash (line 239) | public static String getFormIdentifierHash(String formId, String formV...
method fixGoogleBug154855417 (line 249) | private void fixGoogleBug154855417() {
method getLocale (line 266) | @NotNull
method getState (line 276) | @NotNull
method getGeoDependencyComponent (line 282) | @NonNull
method getOsmDroidDependencyComponent (line 295) | @NonNull
method getObjectProvider (line 307) | @NonNull
method getEntitiesDependencyComponent (line 313) | @NonNull
method getSelfieCameraDependencyComponent (line 325) | @NonNull
method getGoogleMapsDependencyComponent (line 331) | @NonNull
method getDrawDependencyComponent (line 343) | @NonNull
method getLocationDependencyComponent (line 349) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/audio/AMRAppender.java
class AMRAppender (line 8) | public class AMRAppender implements AudioFileAppender {
method append (line 10) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/audio/AudioButton.java
class AudioButton (line 28) | public class AudioButton extends MultiClickSafeMaterialButton implements...
method AudioButton (line 34) | public AudioButton(Context context) {
method AudioButton (line 39) | public AudioButton(Context context, AttributeSet attrs) {
method isPlaying (line 44) | public Boolean isPlaying() {
method setPlaying (line 48) | public void setPlaying(Boolean isPlaying) {
method setListener (line 53) | public void setListener(Listener listener) {
method onClick (line 57) | @Override
method initView (line 66) | private void initView() {
method render (line 71) | private void render() {
type Listener (line 79) | public interface Listener {
method onPlayClicked (line 81) | void onPlayClicked();
method onStopClicked (line 83) | void onStopClicked();
FILE: collect_app/src/main/java/org/odk/collect/android/audio/AudioControllerView.java
class AudioControllerView (line 34) | public class AudioControllerView extends FrameLayout {
method AudioControllerView (line 50) | public AudioControllerView(Context context) {
method AudioControllerView (line 54) | public AudioControllerView(Context context, AttributeSet attrs) {
method playClicked (line 74) | private void playClicked() {
method onPositionChanged (line 86) | private void onPositionChanged(Integer newPosition) {
method setPlaying (line 95) | public void setPlaying(Boolean playing) {
method setListener (line 105) | public void setListener(Listener listener) {
method setPosition (line 109) | public void setPosition(Integer position) {
method setDuration (line 115) | public void setDuration(Integer duration) {
method renderPosition (line 123) | private void renderPosition(Integer position) {
type SwipableParent (line 131) | public interface SwipableParent {
method allowSwiping (line 132) | void allowSwiping(boolean allowSwiping);
type Listener (line 135) | public interface Listener {
method onPlayClicked (line 137) | void onPlayClicked();
method onPauseClicked (line 139) | void onPauseClicked();
method onPositionChanged (line 141) | void onPositionChanged(Integer newPosition);
method onRemoveClicked (line 143) | void onRemoveClicked();
class SwipeListener (line 146) | private class SwipeListener implements SeekBar.OnSeekBarChangeListener {
method onStartTrackingTouch (line 151) | @Override
method onProgressChanged (line 162) | @Override
method onStopTrackingTouch (line 169) | @Override
method isSwiping (line 182) | Boolean isSwiping() {
FILE: collect_app/src/main/java/org/odk/collect/android/audio/AudioFileAppender.java
type AudioFileAppender (line 6) | public interface AudioFileAppender {
method append (line 8) | void append(File one, File two) throws IOException;
FILE: collect_app/src/main/java/org/odk/collect/android/audio/AudioRecordingControllerFragment.java
class AudioRecordingControllerFragment (line 33) | public class AudioRecordingControllerFragment extends Fragment {
method AudioRecordingControllerFragment (line 44) | public AudioRecordingControllerFragment(ViewModelProvider.Factory view...
method onAttach (line 48) | @Override
method onCreateView (line 58) | @Nullable
method onViewCreated (line 66) | @Override
method update (line 90) | private void update(boolean hasBackgroundRecording, boolean isBackgrou...
method renderRecordingProblem (line 115) | private void renderRecordingProblem(String string) {
method renderRecordingInProgress (line 123) | private void renderRecordingInProgress(RecordingSession session, boole...
method renderControls (line 135) | private void renderControls(RecordingSession session) {
FILE: collect_app/src/main/java/org/odk/collect/android/audio/AudioRecordingErrorDialogFragment.java
class AudioRecordingErrorDialogFragment (line 21) | public class AudioRecordingErrorDialogFragment extends DialogFragment {
method onAttach (line 29) | @Override
method onCreateDialog (line 36) | @NonNull
method onDismiss (line 51) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/audio/BackgroundAudioHelpDialogFragment.java
class BackgroundAudioHelpDialogFragment (line 16) | public class BackgroundAudioHelpDialogFragment extends DialogFragment {
method onCreateDialog (line 18) | @NonNull
FILE: collect_app/src/main/java/org/odk/collect/android/audio/M4AAppender.java
class M4AAppender (line 15) | public class M4AAppender implements AudioFileAppender {
method append (line 17) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/audio/VolumeBar.java
class VolumeBar (line 19) | public class VolumeBar extends LinearLayout {
method VolumeBar (line 39) | public VolumeBar(@NonNull Context context) {
method VolumeBar (line 44) | public VolumeBar(@NonNull Context context, @Nullable AttributeSet attr...
method VolumeBar (line 49) | public VolumeBar(@NonNull Context context, @Nullable AttributeSet attr...
method init (line 54) | private void init(Context context) {
method onLayout (line 63) | @Override
method addAmplitude (line 82) | public void addAmplitude(int amplitude) {
method getLatestAmplitude (line 100) | @Nullable
method getBestPipSize (line 105) | private int getBestPipSize(int availableWidth) {
method createPipView (line 113) | @NotNull
FILE: collect_app/src/main/java/org/odk/collect/android/audio/Waveform.java
class Waveform (line 19) | public class Waveform extends FrameLayout {
method Waveform (line 28) | public Waveform(@NotNull Context context) {
method Waveform (line 33) | public Waveform(@NotNull Context context, @NotNull AttributeSet attrs) {
method Waveform (line 38) | public Waveform(@NotNull Context context, @NotNull AttributeSet attrs,...
method init (line 43) | private void init(Context context, @Nullable AttributeSet attrs) {
method onLayout (line 52) | @Override
method addAmplitude (line 58) | public void addAmplitude(int amplitude) {
method getLatestAmplitude (line 72) | @Nullable
method clear (line 77) | public void clear() {
FILE: collect_app/src/main/java/org/odk/collect/android/backgroundwork/BackgroundWorkUtils.java
class BackgroundWorkUtils (line 5) | public final class BackgroundWorkUtils {
method BackgroundWorkUtils (line 12) | private BackgroundWorkUtils() {
method getPeriodInMilliseconds (line 16) | public static long getPeriodInMilliseconds(String period, Context cont...
FILE: collect_app/src/main/java/org/odk/collect/android/backgroundwork/FormUpdateAndInstanceSubmitScheduler.java
class FormUpdateAndInstanceSubmitScheduler (line 18) | public class FormUpdateAndInstanceSubmitScheduler implements FormUpdateS...
method FormUpdateAndInstanceSubmitScheduler (line 24) | public FormUpdateAndInstanceSubmitScheduler(Scheduler scheduler, Setti...
method scheduleUpdates (line 30) | @Override
method scheduleAutoUpdate (line 52) | private void scheduleAutoUpdate(long periodInMilliseconds, String proj...
method scheduleMatchExactly (line 58) | private void scheduleMatchExactly(long periodInMilliseconds, String pr...
method cancelUpdates (line 64) | @Override
method scheduleAutoSend (line 70) | @Override
method scheduleFormAutoSend (line 90) | @Override
method cancelSubmit (line 98) | @Override
method getAutoSendTag (line 103) | @NotNull
method getAutoSendFormTag (line 108) | public String getAutoSendFormTag(String projectId) {
method getMatchExactlyTag (line 112) | @NotNull
method getAutoUpdateTag (line 117) | @NotNull
FILE: collect_app/src/main/java/org/odk/collect/android/backgroundwork/FormUpdateScheduler.java
type FormUpdateScheduler (line 3) | public interface FormUpdateScheduler {
method scheduleUpdates (line 5) | void scheduleUpdates(String projectId);
method cancelUpdates (line 7) | void cancelUpdates(String projectId);
FILE: collect_app/src/main/java/org/odk/collect/android/backgroundwork/InstanceSubmitScheduler.java
type InstanceSubmitScheduler (line 3) | public interface InstanceSubmitScheduler {
method scheduleAutoSend (line 5) | void scheduleAutoSend(String projectId);
method scheduleFormAutoSend (line 7) | void scheduleFormAutoSend(String projectId);
method cancelSubmit (line 9) | void cancelSubmit(String projectId);
FILE: collect_app/src/main/java/org/odk/collect/android/dao/CursorLoaderFactory.java
class CursorLoaderFactory (line 13) | @Deprecated
method CursorLoaderFactory (line 19) | public CursorLoaderFactory(ProjectsDataService projectsDataService) {
method createSentInstancesCursorLoader (line 23) | public CursorLoader createSentInstancesCursorLoader(CharSequence charS...
method createEditableInstancesCursorLoader (line 46) | public CursorLoader createEditableInstancesCursorLoader(CharSequence c...
method createFinalizedInstancesCursorLoader (line 67) | public CursorLoader createFinalizedInstancesCursorLoader(CharSequence ...
method createCompletedUndeletedInstancesCursorLoader (line 90) | public CursorLoader createCompletedUndeletedInstancesCursorLoader(Char...
method getInstancesCursorLoader (line 121) | private CursorLoader getInstancesCursorLoader(String selection, String...
method getUriWithAnalyticsParam (line 133) | private Uri getUriWithAnalyticsParam(Uri uri) {
FILE: collect_app/src/main/java/org/odk/collect/android/dao/helpers/InstancesDaoHelper.java
class InstancesDaoHelper (line 30) | @Deprecated
method InstancesDaoHelper (line 33) | private InstancesDaoHelper() {
method isInstanceComplete (line 47) | public static boolean isInstanceComplete(FormController formController) {
FILE: collect_app/src/main/java/org/odk/collect/android/database/DatabaseConstants.java
class DatabaseConstants (line 3) | public final class DatabaseConstants {
method DatabaseConstants (line 20) | private DatabaseConstants() {
FILE: collect_app/src/main/java/org/odk/collect/android/database/forms/DatabaseFormsRepository.java
class DatabaseFormsRepository (line 59) | public class DatabaseFormsRepository implements FormsRepository {
method DatabaseFormsRepository (line 67) | public DatabaseFormsRepository(Context context, String dbPath, String ...
method get (line 81) | @Nullable
method getLatestByFormIdAndVersion (line 87) | @Nullable
method getOneByPath (line 98) | @Nullable
method getOneByMd5Hash (line 106) | @Nullable
method getAll (line 118) | @Override
method getAllByFormIdAndVersion (line 124) | @Override
method getAllByFormId (line 133) | @Override
method getAllNotDeletedByFormId (line 139) | @Override
method getAllNotDeletedByFormIdAndVersion (line 146) | @Override
method save (line 157) | @Override
method delete (line 192) | @Override
method softDelete (line 200) | @Override
method deleteByMd5Hash (line 208) | @Override
method deleteAll (line 216) | @Override
method restore (line 221) | @Override
method rawQuery (line 228) | public Cursor rawQuery(Map<String, String> projectionMap, String[] pro...
method queryForForm (line 232) | @Nullable
method queryForForms (line 239) | private List<Form> queryForForms(String selection, String[] selectionA...
method queryAndReturnCursor (line 245) | private Cursor queryAndReturnCursor(Map<String, String> projectionMap,...
method insertForm (line 288) | private Long insertForm(ContentValues values) {
method updateForm (line 293) | private void updateForm(Long id, ContentValues values) {
method deleteForms (line 298) | private void deleteForms(String selection, String[] selectionArgs) {
method getFormsFromCursor (line 310) | @NotNull
method deleteFilesForForm (line 337) | private void deleteFilesForForm(Form form) {
FILE: collect_app/src/main/java/org/odk/collect/android/database/forms/FormDatabaseMigrator.java
class FormDatabaseMigrator (line 35) | public class FormDatabaseMigrator implements DatabaseMigrator {
method onCreate (line 46) | public void onCreate(SQLiteDatabase db) {
method onUpgrade (line 50) | @SuppressWarnings({"checkstyle:FallThrough"})
method upgradeToVersion2 (line 85) | private void upgradeToVersion2(SQLiteDatabase db) {
method upgradeToVersion4 (line 90) | private void upgradeToVersion4(SQLiteDatabase db, int oldVersion) {
method upgradeToVersion5 (line 210) | private void upgradeToVersion5(SQLiteDatabase db) {
method upgradeToVersion6 (line 215) | private void upgradeToVersion6(SQLiteDatabase db) {
method upgradeToVersion7 (line 219) | private void upgradeToVersion7(SQLiteDatabase db) {
method upgradeToVersion8 (line 227) | private void upgradeToVersion8(SQLiteDatabase db) {
method upgradeToVersion9 (line 231) | private void upgradeToVersion9(SQLiteDatabase db) {
method upgradeToVersion10 (line 242) | private void upgradeToVersion10(SQLiteDatabase db) {
method upgradeToVersion11 (line 253) | private void upgradeToVersion11(SQLiteDatabase db) {
method upgradeToVersion12 (line 264) | private void upgradeToVersion12(SQLiteDatabase db) {
method upgradeToVersion13 (line 268) | private void upgradeToVersion13(SQLiteDatabase db) {
method upgradeToVersion14 (line 279) | private void upgradeToVersion14(SQLiteDatabase db) {
method createFormsTableV4 (line 283) | private void createFormsTableV4(SQLiteDatabase db, String tableName) {
method createFormsTableV7 (line 304) | public void createFormsTableV7(SQLiteDatabase db) {
method createFormsTableV8 (line 324) | public void createFormsTableV8(SQLiteDatabase database) {
method createFormsTableV9 (line 345) | public void createFormsTableV9(SQLiteDatabase db) {
method createFormsTableV10 (line 366) | public void createFormsTableV10(SQLiteDatabase db) {
method createFormsTableV11 (line 387) | public void createFormsTableV11(SQLiteDatabase db) {
method createFormsTableV12 (line 408) | public void createFormsTableV12(SQLiteDatabase db) {
method createFormsTableV13 (line 430) | public void createFormsTableV13(SQLiteDatabase db) {
method createFormsTableV14 (line 452) | private void createFormsTableV14(SQLiteDatabase db) {
FILE: collect_app/src/main/java/org/odk/collect/android/database/instances/DatabaseInstancesRepository.java
class DatabaseInstancesRepository (line 46) | public final class DatabaseInstancesRepository implements InstancesRepos...
method DatabaseInstancesRepository (line 52) | public DatabaseInstancesRepository(Context context, String dbPath, Str...
method get (line 65) | @Override
method getOneByPath (line 76) | @Override
method getAll (line 90) | @Override
method getAllNotDeleted (line 99) | @Override
method getAllByStatus (line 108) | @Override
method getCountByStatus (line 115) | @Override
method getAllByFormId (line 123) | @Override
method getAllNotDeletedByFormIdAndVersion (line 132) | @Override
method delete (line 147) | @Override
method deleteAll (line 164) | @Override
method save (line 183) | @Override
method deleteWithLogging (line 220) | @Override
method rawQuery (line 232) | public Cursor rawQuery(String[] projection, String selection, String[]...
method getCursorForAllByStatus (line 236) | private Cursor getCursorForAllByStatus(String[] status) {
method query (line 245) | private Cursor query(String[] projection, String selection, String[] s...
method insert (line 281) | private long insert(ContentValues values) throws IntegrityException {
method update (line 293) | private void update(Long instanceId, ContentValues values) {
method deleteInstanceFiles (line 302) | private void deleteInstanceFiles(Instance instance) {
method getInstancesFromCursor (line 306) | private static List<Instance> getInstancesFromCursor(Cursor cursor, St...
FILE: collect_app/src/main/java/org/odk/collect/android/database/instances/InstanceDatabaseMigrator.java
class InstanceDatabaseMigrator (line 35) | public class InstanceDatabaseMigrator implements DatabaseMigrator {
method onCreate (line 43) | public void onCreate(SQLiteDatabase db) {
method onUpgrade (line 47) | @SuppressWarnings({"checkstyle:FallThrough"})
method upgradeToVersion2 (line 75) | private void upgradeToVersion2(SQLiteDatabase db) {
method upgradeToVersion3 (line 87) | private void upgradeToVersion3(SQLiteDatabase db) {
method upgradeToVersion4 (line 91) | private void upgradeToVersion4(SQLiteDatabase db) {
method upgradeToVersion5 (line 101) | private void upgradeToVersion5(SQLiteDatabase db) {
method dropObsoleteColumns (line 126) | private void dropObsoleteColumns(SQLiteDatabase db, String[] relevantC...
method upgradeToVersion6 (line 136) | private void upgradeToVersion6(SQLiteDatabase db, String name) {
method upgradeToVersion7 (line 141) | private void upgradeToVersion7(SQLiteDatabase db) {
method upgradeToVersion8 (line 149) | private void upgradeToVersion8(SQLiteDatabase db) {
method upgradeToVersion9 (line 154) | private void upgradeToVersion9(SQLiteDatabase db) {
method upgradeToVersion10 (line 159) | private void upgradeToVersion10(SQLiteDatabase db) {
method createInstancesTableV5 (line 167) | private void createInstancesTableV5(SQLiteDatabase db, String name) {
method createInstancesTableV6 (line 181) | public void createInstancesTableV6(SQLiteDatabase db) {
method createInstancesTableV7 (line 197) | public void createInstancesTableV7(SQLiteDatabase db) {
method createInstancesTableV8 (line 213) | public void createInstancesTableV8(SQLiteDatabase db) {
method createInstancesTableV9 (line 230) | public void createInstancesTableV9(SQLiteDatabase db) {
method createInstancesTableV10 (line 251) | public void createInstancesTableV10(SQLiteDatabase db) {
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalAnswerResolver.java
class ExternalAnswerResolver (line 46) | public class ExternalAnswerResolver extends DefaultAnswerResolver {
method resolveAnswer (line 48) | @Override
method createBugRuntimeException (line 171) | private RuntimeException createBugRuntimeException(TreeElement treeEle...
method createCustomSelectChoices (line 177) | protected List<SelectChoice> createCustomSelectChoices(String complete...
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalAppsUtils.java
class ExternalAppsUtils (line 55) | public final class ExternalAppsUtils {
method ExternalAppsUtils (line 60) | private ExternalAppsUtils() {
method extractIntentName (line 63) | public static String extractIntentName(String exString) {
method extractParameters (line 73) | public static Map<String, String> extractParameters(String exString) {
method getParamPairs (line 96) | private static List<String> getParamPairs(String paramsStr) {
method populateParameters (line 116) | public static void populateParameters(Intent intent, Map<String, Strin...
method getValueRepresentedBy (line 135) | public static Object getValueRepresentedBy(String text, TreeReference ...
method asStringData (line 167) | public static StringData asStringData(Object value) {
method asIntegerData (line 171) | public static IntegerData asIntegerData(Object value) {
method asDecimalData (line 185) | public static DecimalData asDecimalData(Object value) {
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalDataHandler.java
type ExternalDataHandler (line 28) | public interface ExternalDataHandler extends IFunctionHandler {
method setExternalDataManager (line 30) | void setExternalDataManager(ExternalDataManager externalDataManager);
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalDataManager.java
type ExternalDataManager (line 28) | public interface ExternalDataManager {
method getDatabase (line 40) | ExternalSQLiteOpenHelper getDatabase(String dataSetName, boolean requi...
method close (line 42) | void close();
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalDataManagerImpl.java
class ExternalDataManagerImpl (line 37) | public class ExternalDataManagerImpl implements ExternalDataManager {
method ExternalDataManagerImpl (line 43) | public ExternalDataManagerImpl(File mediaFolder) {
method getDatabase (line 47) | @Override
method close (line 67) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalDataReader.java
type ExternalDataReader (line 29) | public interface ExternalDataReader {
method doImport (line 31) | void doImport(Map<String, File> externalDataMap);
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalDataReaderImpl.java
class ExternalDataReaderImpl (line 40) | public class ExternalDataReaderImpl implements ExternalDataReader {
method ExternalDataReaderImpl (line 45) | public ExternalDataReaderImpl(Supplier<Boolean> isCancelled, Consumer<...
method doImport (line 50) | @Override
method doImportDataSetAndContinue (line 64) | private boolean doImportDataSetAndContinue(String dataSetName, File da...
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalDataUtil.java
class ExternalDataUtil (line 60) | public final class ExternalDataUtil {
method ExternalDataUtil (line 73) | private ExternalDataUtil() {
method toSafeColumnName (line 77) | public static String toSafeColumnName(String columnName, Map<String, S...
method toSafeColumnName (line 88) | public static String toSafeColumnName(String columnName) {
method findMatchingColumnsAfterSafeningNames (line 95) | public static List<String> findMatchingColumnsAfterSafeningNames(Strin...
method getSearchXPathExpression (line 111) | public static XPathFuncExpr getSearchXPathExpression(String appearance) {
method populateExternalChoices (line 166) | public static ArrayList<SelectChoice> populateExternalChoices(FormEntr...
method createMapWithDisplayingColumns (line 239) | public static LinkedHashMap<String, String> createMapWithDisplayingCol...
method createListOfColumns (line 261) | public static List<String> createListOfColumns(String columnString) {
method splitTrimmed (line 274) | private static List<String> splitTrimmed(String displayColumns, String...
method splitTrimmed (line 285) | private static List<String> splitTrimmed(String text, String separator) {
method containsAnyData (line 297) | public static boolean containsAnyData(String[] row) {
method fillUpNullValues (line 309) | public static String[] fillUpNullValues(String[] row, String[] headerR...
method nullSafe (line 327) | public static String nullSafe(String value) {
method isAnInteger (line 331) | public static boolean isAnInteger(String value) {
method containsConfigurationChoice (line 351) | private static boolean containsConfigurationChoice(List<SelectChoice> ...
method getFilePath (line 360) | private static String getFilePath(XPathFuncExpr xpathfuncexpr, FormCon...
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalSQLiteOpenHelper.java
class ExternalSQLiteOpenHelper (line 59) | public class ExternalSQLiteOpenHelper extends SQLiteOpenHelper {
method ExternalSQLiteOpenHelper (line 71) | ExternalSQLiteOpenHelper(File dbFile) {
method importFromCSV (line 75) | void importFromCSV(File dataSetFile, ExternalDataReader externalDataRe...
method onCreate (line 92) | @Override
method onCreateNamed (line 114) | private void onCreateNamed(SQLiteDatabase db, String tableName) throws...
method isCancelled (line 279) | protected boolean isCancelled() {
method createAndPopulateMetadataTable (line 285) | static void createAndPopulateMetadataTable(SQLiteDatabase db, String m...
method getLastMd5Hash (line 301) | static String getLastMd5Hash(SQLiteDatabase db, String metadataTableNa...
method shouldUpdateDBforDataSet (line 319) | static boolean shouldUpdateDBforDataSet(File dbFile, File dataSetFile) {
method shouldUpdateDBforDataSet (line 324) | static boolean shouldUpdateDBforDataSet(SQLiteDatabase db, String data...
method onUpgrade (line 337) | @Override
method onProgress (line 341) | private void onProgress(String message) {
method removeByteOrderMark (line 351) | private String removeByteOrderMark(String bomCheckString) {
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/ExternalSelectChoice.java
class ExternalSelectChoice (line 28) | public class ExternalSelectChoice extends SelectChoice {
method ExternalSelectChoice (line 32) | public ExternalSelectChoice(String labelOrID, String value, boolean is...
method getImage (line 36) | public String getImage() {
method setImage (line 40) | public void setImage(String image) {
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/handler/ExternalDataHandlerBase.java
class ExternalDataHandlerBase (line 31) | public abstract class ExternalDataHandlerBase implements ExternalDataHan...
method getExternalDataManager (line 35) | public ExternalDataManager getExternalDataManager() {
method setExternalDataManager (line 39) | public void setExternalDataManager(ExternalDataManager externalDataMan...
method ExternalDataHandlerBase (line 43) | protected ExternalDataHandlerBase(ExternalDataManager externalDataMana...
method normalize (line 53) | protected String normalize(String dataSetName) {
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/handler/ExternalDataHandlerPull.java
class ExternalDataHandlerPull (line 42) | public class ExternalDataHandlerPull extends ExternalDataHandlerBase {
method ExternalDataHandlerPull (line 46) | public ExternalDataHandlerPull(ExternalDataManager externalDataManager) {
method getName (line 50) | @Override
method getPrototypes (line 55) | @Override
method rawArgs (line 60) | @Override
method realTime (line 65) | @Override
method eval (line 70) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/handler/ExternalDataHandlerSearch.java
class ExternalDataHandlerSearch (line 51) | public class ExternalDataHandlerSearch extends ExternalDataHandlerBase {
method ExternalDataHandlerSearch (line 59) | public ExternalDataHandlerSearch(ExternalDataManager externalDataManag...
method getDisplayColumns (line 67) | public String getDisplayColumns() {
method getValueColumn (line 71) | public String getValueColumn() {
method getImageColumn (line 75) | public String getImageColumn() {
method getName (line 79) | @Override
method getPrototypes (line 84) | @Override
method rawArgs (line 89) | @Override
method realTime (line 94) | @Override
method eval (line 99) | @Override
method createDynamicSelectChoices (line 200) | protected ArrayList<SelectChoice> createDynamicSelectChoices(Cursor c,
method createLikeExpression (line 245) | protected String createLikeExpression(List<String> queriedColumns) {
method buildLabel (line 263) | protected String buildLabel(Cursor c, LinkedHashMap<String, String> se...
FILE: collect_app/src/main/java/org/odk/collect/android/dynamicpreload/handler/ExternalDataSearchType.java
type ExternalDataSearchType (line 26) | enum ExternalDataSearchType {
method getSingleLikeArgument (line 29) | @Override
method getSingleLikeArgument (line 36) | @Override
method getSingleLikeArgument (line 43) | @Override
method getSingleLikeArgument (line 50) | @Override
method ExternalDataSearchType (line 58) | ExternalDataSearchType(String keyword) {
method getKeyword (line 62) | public String getKeyword() {
method getByKeyword (line 66) | public static ExternalDataSearchType getByKeyword(String keyword,
method constructLikeArguments (line 80) | public String[] constructLikeArguments(String queriedValue, int times) {
method getSingleLikeArgument (line 88) | protected abstract String getSingleLikeArgument(String queriedValue);
FILE: collect_app/src/main/java/org/odk/collect/android/exception/EncryptionException.java
class EncryptionException (line 26) | public class EncryptionException extends Exception {
method EncryptionException (line 28) | public EncryptionException(String detailMessage, Throwable throwable) {
FILE: collect_app/src/main/java/org/odk/collect/android/exception/ExternalDataException.java
class ExternalDataException (line 26) | public class ExternalDataException extends RuntimeException {
method ExternalDataException (line 28) | public ExternalDataException(String detailMessage) {
method ExternalDataException (line 32) | public ExternalDataException(String detailMessage, Throwable throwable) {
FILE: collect_app/src/main/java/org/odk/collect/android/exception/ExternalParamsException.java
class ExternalParamsException (line 26) | public class ExternalParamsException extends Exception {
method ExternalParamsException (line 28) | public ExternalParamsException(String detailMessage, Throwable throwab...
FILE: collect_app/src/main/java/org/odk/collect/android/exception/JavaRosaException.java
class JavaRosaException (line 28) | public class JavaRosaException extends Exception {
method JavaRosaException (line 30) | public JavaRosaException(Throwable throwable) {
FILE: collect_app/src/main/java/org/odk/collect/android/external/FormsContract.java
class FormsContract (line 30) | public final class FormsContract {
method getUri (line 39) | public static Uri getUri(String projectId, Long formDbId) {
method getUri (line 43) | public static Uri getUri(String projectId) {
method getContentNewestFormsByFormIdUri (line 55) | @Deprecated
method FormsContract (line 60) | private FormsContract() {
FILE: collect_app/src/main/java/org/odk/collect/android/external/FormsProvider.java
class FormsProvider (line 67) | public class FormsProvider extends ContentProvider {
method deferDaggerInit (line 95) | private void deferDaggerInit() {
method onCreate (line 99) | @Override
method query (line 104) | @Override
method getType (line 170) | @Override
method insert (line 185) | @Override
method delete (line 195) | @Override
method update (line 231) | @Override
method getFormsRepository (line 236) | @NotNull
method getProjectId (line 241) | private String getProjectId(@NonNull Uri uri) {
method databaseQuery (line 251) | private Cursor databaseQuery(String projectId, String[] projection, St...
method logServerEvent (line 255) | private void logServerEvent(String projectId, String event) {
FILE: collect_app/src/main/java/org/odk/collect/android/external/InstanceProvider.java
class InstanceProvider (line 48) | public class InstanceProvider extends ContentProvider {
method onCreate (line 69) | @Override
method query (line 74) | @Override
method dbQuery (line 106) | private Cursor dbQuery(String projectId, String[] projection, String s...
method getType (line 110) | @Override
method insert (line 124) | @Override
method delete (line 149) | @Override
method update (line 199) | @Override
method getProjectId (line 204) | private String getProjectId(@NonNull Uri uri) {
method logServerEvent (line 214) | private void logServerEvent(String projectId, String event) {
FILE: collect_app/src/main/java/org/odk/collect/android/external/InstancesContract.java
class InstancesContract (line 21) | public final class InstancesContract {
method getUri (line 27) | public static Uri getUri(String projectId) {
method getUri (line 31) | public static Uri getUri(String projectId, Long instanceDbId) {
method InstancesContract (line 36) | private InstancesContract() {
FILE: collect_app/src/main/java/org/odk/collect/android/fastexternalitemset/ItemsetDao.java
class ItemsetDao (line 39) | public class ItemsetDao {
method ItemsetDao (line 44) | public ItemsetDao(ItemsetDbAdapter adapter) {
method getItemLabel (line 48) | public String getItemLabel(String itemName, String mediaFolderPath, St...
method getItems (line 88) | public List<SelectChoice> getItems(FormEntryPrompt formEntryPrompt, XP...
method getNodesetString (line 98) | private String getNodesetString(FormEntryPrompt formEntryPrompt) {
method getQueryString (line 105) | private String getQueryString(String nodesetStr) {
method getSelectionStringAndPopulateArguments (line 110) | private String getSelectionStringAndPopulateArguments(String queryStri...
method getSelectionArgs (line 175) | @SuppressWarnings("PMD.AvoidThrowingNewInstanceOfSameException")
method getItemsFromDatabase (line 220) | private List<SelectChoice> getItemsFromDatabase(String selection, Stri...
method getItemsetFile (line 275) | public File getItemsetFile(String mediaFolderPath) {
FILE: collect_app/src/main/java/org/odk/collect/android/fastexternalitemset/ItemsetDbAdapter.java
class ItemsetDbAdapter (line 24) | public class ItemsetDbAdapter implements Closeable {
class DatabaseHelper (line 46) | private static class DatabaseHelper extends SQLiteOpenHelper {
method DatabaseHelper (line 47) | DatabaseHelper() {
method onCreate (line 51) | @Override
method onUpgrade (line 58) | @Override
method open (line 87) | public ItemsetDbAdapter open() throws SQLException {
method close (line 93) | @Override
method createTable (line 98) | public boolean createTable(String formHash, String pathHash, String[] ...
method addRow (line 133) | public boolean addRow(String tableName, String[] columns, String[] new...
method beginTransaction (line 147) | public void beginTransaction() {
method commit (line 151) | public void commit() {
method query (line 155) | public Cursor query(String hash, String selection, String[] selectionA...
method dropTable (line 160) | public void dropTable(String pathHash, String path) {
method getItemsets (line 172) | public Cursor getItemsets(String path) {
method getItemsets (line 180) | public Cursor getItemsets() {
method update (line 184) | public void update(ContentValues values, String where, String[] whereA...
method delete (line 188) | public void delete(String path) {
method getMd5FromString (line 207) | public static String getMd5FromString(String toEncode) {
FILE: collect_app/src/main/java/org/odk/collect/android/fastexternalitemset/XPathParseTool.java
class XPathParseTool (line 11) | public class XPathParseTool {
method parseXPath (line 12) | public XPathExpression parseXPath(String xpath) throws XPathSyntaxExce...
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/BackgroundAudioPermissionDialogFragment.java
class BackgroundAudioPermissionDialogFragment (line 24) | public class BackgroundAudioPermissionDialogFragment extends DialogFragm...
method BackgroundAudioPermissionDialogFragment (line 33) | public BackgroundAudioPermissionDialogFragment(ViewModelProvider.Facto...
method onAttach (line 37) | @Override
method onCreateDialog (line 44) | @NonNull
method onOKClicked (line 58) | private void onOKClicked(FragmentActivity activity) {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/BackgroundAudioViewModel.java
class BackgroundAudioViewModel (line 31) | public class BackgroundAudioViewModel extends ViewModel {
method BackgroundAudioViewModel (line 50) | public BackgroundAudioViewModel(AudioRecorder audioRecorder, Settings ...
method onCleared (line 66) | @Override
method isPermissionRequired (line 72) | public LiveData<Boolean> isPermissionRequired() {
method isBackgroundRecordingEnabled (line 76) | public NonNullLiveData<Boolean> isBackgroundRecordingEnabled() {
method setBackgroundRecordingEnabled (line 80) | public void setBackgroundRecordingEnabled(boolean enabled) {
method isBackgroundRecording (line 97) | public boolean isBackgroundRecording() {
method grantAudioPermission (line 101) | public void grantAudioPermission() {
method handleRecordAction (line 113) | private void handleRecordAction(TreeReference treeReference, String qu...
method startBackgroundRecording (line 137) | private void startBackgroundRecording(String quality, HashSet<TreeRefe...
type RecordAudioActionRegistry (line 148) | public interface RecordAudioActionRegistry {
method register (line 150) | void register(BiConsumer<TreeReference, String> listener);
method unregister (line 152) | void unregister();
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/FormEntryViewModel.java
class FormEntryViewModel (line 57) | public class FormEntryViewModel extends ViewModel implements SelectChoic...
method FormEntryViewModel (line 85) | @SuppressWarnings("WeakerAccess")
method getSessionId (line 104) | public String getSessionId() {
method getFormController (line 111) | @Deprecated
method getCurrentIndex (line 116) | public LiveData<CurrentFormIndex> getCurrentIndex() {
method getError (line 120) | public LiveData<FormError> getError() {
method isLoading (line 124) | public NonNullLiveData<Boolean> isLoading() {
method promptForNewRepeat (line 128) | @SuppressWarnings("WeakerAccess")
method jumpToNewRepeat (line 139) | public void jumpToNewRepeat() {
method addRepeat (line 143) | public void addRepeat() {
method cancelRepeatPrompt (line 167) | public void cancelRepeatPrompt() {
method errorDisplayed (line 189) | public void errorDisplayed() {
method canAddRepeat (line 193) | public boolean canAddRepeat() {
method moveForward (line 203) | public void moveForward(HashMap<FormIndex, IAnswerData> answers) {
method moveForward (line 207) | public void moveForward(HashMap<FormIndex, IAnswerData> answers, Boole...
method moveBackward (line 226) | public void moveBackward(HashMap<FormIndex, IAnswerData> answers) {
method updateAnswersForScreen (line 245) | public boolean updateAnswersForScreen(HashMap<FormIndex, IAnswerData> ...
method saveScreenAnswersToFormController (line 252) | public boolean saveScreenAnswersToFormController(HashMap<FormIndex, IA...
method openHierarchy (line 271) | public void openHierarchy() {
method hasBackgroundRecording (line 275) | public NonNullLiveData<Boolean> hasBackgroundRecording() {
method getQuestionPrompt (line 279) | public FormEntryPrompt getQuestionPrompt(FormIndex formIndex) {
method answerQuestion (line 283) | public void answerQuestion(FormIndex index, IAnswerData answer) {
method loadSelectChoices (line 306) | @NonNull
method onCleared (line 319) | @Override
method refreshSync (line 327) | @Deprecated
method refresh (line 332) | public void refresh() {
method updateIndex (line 339) | private void updateIndex(boolean isAsync, @Nullable ValidationResult v...
method updateIndex (line 343) | private void updateIndex(boolean isAsync, @Nullable ValidationResult v...
method exit (line 380) | public void exit() {
method validateForm (line 386) | public void validateForm() {
method preloadSelectChoices (line 402) | private void preloadSelectChoices() throws RepeatsInFieldListException...
method changeLanguage (line 418) | public void changeLanguage(String newLanguage) {
method isFormEditableAfterFinalization (line 429) | public boolean isFormEditableAfterFinalization() {
method shouldShowNewEditMessage (line 438) | public boolean shouldShowNewEditMessage() {
method saveFieldList (line 449) | public FormEntryPrompt[] saveFieldList(HashMap<FormIndex, IAnswerData>...
method isQuestionRecalculated (line 473) | private boolean isQuestionRecalculated(FormEntryPrompt mutableQuestion...
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/FormLoadingDialogFragment.java
class FormLoadingDialogFragment (line 24) | public class FormLoadingDialogFragment extends MaterialProgressDialogFra...
type FormLoadingDialogFragmentListener (line 26) | public interface FormLoadingDialogFragmentListener {
method onCancelFormLoading (line 27) | void onCancelFormLoading();
method onAttach (line 38) | @Override
method getCancelButtonText (line 51) | @Override
method getOnCancelCallback (line 56) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/ODKView.java
class ODKView (line 115) | @SuppressLint("ViewConstructor")
method ODKView (line 152) | public ODKView(
method setupAudioErrors (line 219) | private void setupAudioErrors() {
method autoplayIfNeeded (line 232) | private void autoplayIfNeeded(boolean advancingPage) {
method autoplayAudio (line 246) | private Boolean autoplayAudio(FormEntryPrompt firstPrompt) {
method autoplayVideo (line 255) | private void autoplayVideo(FormEntryPrompt prompt) {
method configureIntentGroup (line 273) | private void configureIntentGroup(ComponentActivity context, FormEntry...
method addWidgetForQuestion (line 300) | private void addWidgetForQuestion(FormEntryPrompt question) {
method addWidgetForQuestion (line 317) | private void addWidgetForQuestion(FormEntryPrompt question, int index) {
method isInIntentGroup (line 361) | private boolean isInIntentGroup(FormEntryPrompt question) {
method configureWidgetForQuestion (line 376) | private QuestionWidget configureWidgetForQuestion(FormEntryPrompt ques...
method getDividerView (line 390) | private View getDividerView() {
method getAnswers (line 409) | public HashMap<FormIndex, IAnswerData> getAnswers() {
method setGroupText (line 426) | private void setGroupText(FormEntryCaption[] groups) {
method getGroupsPath (line 443) | @NonNull
method getGroupsPath (line 455) | @NonNull
method addIntentLaunchButton (line 485) | private void addIntentLaunchButton(Context context, FormEntryPrompt[] ...
method setFocus (line 556) | public void setFocus(Context context) {
method isDisplayed (line 565) | public boolean isDisplayed(QuestionWidget qw) {
method focusToTopOf (line 571) | public void focusToTopOf(FormIndex index) {
method focusToTopOf (line 580) | public void focusToTopOf(@Nullable QuestionWidget qw) {
method setDataForFields (line 590) | public void setDataForFields(Bundle bundle) throws JavaRosaException {
method shouldSuppressFlingGesture (line 651) | @Override
method verticalScrollView (line 661) | @Nullable
method clearAnswer (line 670) | public boolean clearAnswer() {
method getWidgets (line 681) | public ArrayList<QuestionWidget> getWidgets() {
method setOnFocusChangeListener (line 685) | @Override
method onLongClick (line 693) | @Override
method cancelLongPress (line 698) | @Override
method setErrorForQuestionWithIndex (line 706) | public void setErrorForQuestionWithIndex(FormIndex formIndex, String e...
method removeWidgetAt (line 720) | private void removeWidgetAt(int index) {
method setWidgetValueChangedListener (line 753) | public void setWidgetValueChangedListener(WidgetValueChangedListener l...
method widgetValueChanged (line 757) | public void widgetValueChanged(QuestionWidget changedWidget) {
method onUpdated (line 763) | public void onUpdated(FormIndex lastChangedIndex, FormEntryPrompt[] qu...
method updateQuestions (line 807) | private void updateQuestions(FormEntryPrompt[] prompts) {
method isNavigationBlocked (line 814) | @Nullable
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/RecordingHandler.java
class RecordingHandler (line 21) | public class RecordingHandler {
method RecordingHandler (line 29) | public RecordingHandler(QuestionMediaManager questionMediaManager, Lif...
method handle (line 37) | public void handle(FormController formController, RecordingSession ses...
method handleBackgroundRecording (line 55) | private void handleBackgroundRecording(FormController formController, ...
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/RecordingWarningDialogFragment.java
class RecordingWarningDialogFragment (line 12) | public class RecordingWarningDialogFragment extends DialogFragment {
method onCreateDialog (line 14) | @NonNull
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/RefreshFormListDialogFragment.java
class RefreshFormListDialogFragment (line 9) | public class RefreshFormListDialogFragment extends MaterialProgressDialo...
method onAttach (line 13) | @Override
method getCancelButtonText (line 25) | @Override
method getOnCancelCallback (line 30) | @Override
type RefreshFormListDialogFragmentListener (line 39) | public interface RefreshFormListDialogFragmentListener {
method onCancelFormLoading (line 40) | void onCancelFormLoading();
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/audit/AsyncTaskAuditEventWriter.java
class AsyncTaskAuditEventWriter (line 10) | public class AsyncTaskAuditEventWriter implements AuditEventLogger.Audit...
method AsyncTaskAuditEventWriter (line 19) | public AsyncTaskAuditEventWriter(@NonNull File file, boolean isLocatio...
method writeEvents (line 27) | @Override
method isWriting (line 33) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/audit/AuditConfig.java
class AuditConfig (line 30) | public class AuditConfig {
method AuditConfig (line 57) | public AuditConfig(String mode, String locationMinInterval, String loc...
method getMode (line 66) | private LocationClient.Priority getMode(@NonNull String mode) {
method getLocationPriority (line 81) | @Nullable
method getLocationMinInterval (line 86) | @Nullable
method getLocationMaxAge (line 95) | @Nullable
method isLocationEnabled (line 100) | public boolean isLocationEnabled() {
method isTrackingChangesEnabled (line 104) | public boolean isTrackingChangesEnabled() {
method isIdentifyUserEnabled (line 108) | public boolean isIdentifyUserEnabled() {
method isTrackChangesReasonEnabled (line 112) | public boolean isTrackChangesReasonEnabled() {
class Builder (line 116) | public static class Builder {
method setMode (line 124) | public Builder setMode(String mode) {
method setLocationMinInterval (line 129) | public Builder setLocationMinInterval(String locationMinInterval) {
method setLocationMaxAge (line 134) | public Builder setLocationMaxAge(String locationMaxAge) {
method setIsTrackingChangesEnabled (line 139) | public Builder setIsTrackingChangesEnabled(boolean isTrackingChanges...
method setIsIdentifyUserEnabled (line 144) | public Builder setIsIdentifyUserEnabled(boolean isIdentifyUserEnable...
method setIsTrackChangesReasonEnabled (line 149) | public Builder setIsTrackChangesReasonEnabled(boolean isTrackChanges...
method createAuditConfig (line 154) | public AuditConfig createAuditConfig() {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/audit/AuditEvent.java
class AuditEvent (line 24) | public class AuditEvent {
type AuditEventType (line 26) | public enum AuditEventType {
method AuditEventType (line 88) | AuditEventType(String value, boolean isLogged, boolean isInterval, b...
method AuditEventType (line 96) | AuditEventType(String value, boolean isInterval) {
method AuditEventType (line 100) | AuditEventType(String value) {
method getValue (line 104) | public String getValue() {
method isLogged (line 108) | public boolean isLogged() {
method isInterval (line 115) | public boolean isInterval() {
method isLocationRelated (line 119) | public boolean isLocationRelated() {
method AuditEvent (line 142) | public AuditEvent(long start, AuditEventType auditEventType) {
method AuditEvent (line 146) | public AuditEvent(long start, AuditEventType auditEventType,
method isIntervalAuditEventType (line 159) | public boolean isIntervalAuditEventType() {
method setEnd (line 166) | public void setEnd(long endTime) {
method isEndTimeSet (line 171) | public boolean isEndTimeSet() {
method getAuditEventType (line 175) | public AuditEventType getAuditEventType() {
method getFormIndex (line 179) | public FormIndex getFormIndex() {
method hasNewAnswer (line 183) | public boolean hasNewAnswer() {
method isLocationAlreadySet (line 187) | public boolean isLocationAlreadySet() {
method setLocationCoordinates (line 193) | public void setLocationCoordinates(String latitude, String longitude, ...
method setUser (line 199) | public void setUser(String user) {
method recordValueChange (line 203) | public boolean recordValueChange(String newValue) {
method getChangeReason (line 216) | public String getChangeReason() {
method getLatitude (line 220) | public String getLatitude() {
method getLongitude (line 224) | public String getLongitude() {
method getAccuracy (line 228) | public String getAccuracy() {
method getUser (line 232) | public String getUser() {
method getStart (line 236) | public long getStart() {
method getOldValue (line 240) | @NonNull
method getNewValue (line 245) | @NonNull
method getEnd (line 250) | public long getEnd() {
method getAuditEventTypeFromFecType (line 255) | public static AuditEventType getAuditEventTypeFromFecType(int fcEvent) {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/audit/AuditEventCSVLine.java
class AuditEventCSVLine (line 14) | public final class AuditEventCSVLine {
method AuditEventCSVLine (line 16) | private AuditEventCSVLine() {
method toCSVLine (line 20) | public static String toCSVLine(AuditEvent auditEvent, boolean isTracki...
method getXPathPath (line 72) | private static String getXPathPath(FormIndex formIndex) {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/audit/AuditEventLogger.java
class AuditEventLogger (line 31) | public class AuditEventLogger {
method AuditEventLogger (line 44) | public AuditEventLogger(AuditConfig auditConfig, AuditEventWriter writ...
method logEvent (line 50) | public void logEvent(AuditEvent.AuditEventType eventType, boolean writ...
method logEvent (line 58) | public void logEvent(AuditEvent.AuditEventType eventType, FormIndex fo...
method internalLog (line 63) | private synchronized void internalLog(AuditEvent.AuditEventType eventT...
method flush (line 108) | public synchronized void flush() {
method addLocationCoordinatesToAuditEvent (line 115) | private void addLocationCoordinatesToAuditEvent(AuditEvent auditEvent,...
method addNewValueToQuestionAuditEvent (line 123) | private void addNewValueToQuestionAuditEvent(AuditEvent aev, FormContr...
method isDuplicateOfLastLocationEvent (line 130) | boolean isDuplicateOfLastLocationEvent(AuditEvent.AuditEventType event...
method isDuplicatedIntervalEvent (line 140) | private boolean isDuplicatedIntervalEvent(AuditEvent newAuditEvent) {
method finalizeEvents (line 154) | private void finalizeEvents() {
method setIntervalEventFinalParameters (line 171) | private void setIntervalEventFinalParameters(AuditEvent aev, long end,...
method shouldBeIgnored (line 194) | private boolean shouldBeIgnored(AuditEvent.AuditEventType eventType) {
method shouldEventBeLogged (line 204) | private boolean shouldEventBeLogged(AuditEvent aev) {
method writeEvents (line 212) | private void writeEvents() {
method getEventTime (line 224) | private long getEventTime() {
method addLocation (line 232) | public void addLocation(Location location) {
method getMostAccurateLocation (line 236) | @Nullable
method removeExpiredLocations (line 251) | private void removeExpiredLocations(long currentTime) {
method isAuditEnabled (line 267) | boolean isAuditEnabled() {
method getLocations (line 271) | List<Location> getLocations() {
method isUserRequired (line 275) | public boolean isUserRequired() {
method setUser (line 279) | public void setUser(String user) {
method getUser (line 283) | public String getUser() {
method isChangeReasonRequired (line 287) | public boolean isChangeReasonRequired() {
type AuditEventWriter (line 291) | public interface AuditEventWriter {
method writeEvents (line 293) | void writeEvents(List<AuditEvent> auditEvents);
method isWriting (line 295) | boolean isWriting();
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/audit/AuditEventSaveTask.java
class AuditEventSaveTask (line 21) | public class AuditEventSaveTask extends AsyncTask<AuditEvent, Void, Void> {
method AuditEventSaveTask (line 35) | public AuditEventSaveTask(@NonNull File file, boolean isLocationEnable...
method doInBackground (line 43) | @Override
method updateHeaderIfNeeded (line 76) | private boolean updateHeaderIfNeeded() {
method shouldHeaderBeUpdated (line 110) | private boolean shouldHeaderBeUpdated(String header) {
method getHeader (line 117) | private String getHeader() {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/audit/ChangesReasonPromptDialogFragment.java
class ChangesReasonPromptDialogFragment (line 21) | public class ChangesReasonPromptDialogFragment extends MaterialFullScree...
method onCreateView (line 25) | @Nullable
method onViewCreated (line 31) | @Override
method onAttach (line 68) | @Override
method onBackPressed (line 74) | @Override
method getToolbar (line 79) | @Override
method onCloseClicked (line 84) | @Override
method shouldShowSoftKeyboard (line 89) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/audit/IdentifyUserPromptDialogFragment.java
class IdentifyUserPromptDialogFragment (line 19) | public class IdentifyUserPromptDialogFragment extends MaterialFullScreen...
method onCreateView (line 23) | @Override
method onViewCreated (line 28) | @Override
method onAttach (line 65) | @Override
method onCloseClicked (line 77) | @Override
method onBackPressed (line 83) | @Override
method getToolbar (line 89) | @Override
method shouldShowSoftKeyboard (line 94) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/audit/IdentityPromptViewModel.java
class IdentityPromptViewModel (line 13) | public class IdentityPromptViewModel extends ViewModel {
method IdentityPromptViewModel (line 24) | public IdentityPromptViewModel() {
method formLoaded (line 28) | public void formLoaded(@NonNull FormController formController) {
method requiresIdentityToContinue (line 34) | public LiveData<Boolean> requiresIdentityToContinue() {
method isFormEntryCancelled (line 38) | public LiveData<Boolean> isFormEntryCancelled() {
method getUser (line 42) | public String getUser() {
method setIdentity (line 46) | public void setIdentity(String identity) {
method done (line 50) | public void done() {
method promptDismissed (line 58) | public void promptDismissed() {
method updateRequiresIdentity (line 62) | private void updateRequiresIdentity() {
method userIsValid (line 68) | private static boolean userIsValid(String user) {
method getFormTitle (line 72) | public String getFormTitle() {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/backgroundlocation/BackgroundLocationHelper.java
class BackgroundLocationHelper (line 30) | public class BackgroundLocationHelper {
method BackgroundLocationHelper (line 37) | public BackgroundLocationHelper(
method isAndroidLocationPermissionGranted (line 49) | boolean isAndroidLocationPermissionGranted() {
method isBackgroundLocationPreferenceEnabled (line 53) | boolean isBackgroundLocationPreferenceEnabled() {
method arePlayServicesAvailable (line 57) | boolean arePlayServicesAvailable() {
method isCurrentFormSet (line 64) | boolean isCurrentFormSet() {
method currentFormCollectsBackgroundLocation (line 73) | boolean currentFormCollectsBackgroundLocation() {
method currentFormAuditsLocation (line 83) | boolean currentFormAuditsLocation() {
method getCurrentFormAuditConfig (line 92) | AuditConfig getCurrentFormAuditConfig() {
method logAuditEvent (line 101) | void logAuditEvent(AuditEvent.AuditEventType eventType) {
method provideLocationToAuditLogger (line 110) | void provideLocationToAuditLogger(Location location) {
method getFormController (line 114) | @Nullable
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/backgroundlocation/BackgroundLocationManager.java
class BackgroundLocationManager (line 27) | public class BackgroundLocationManager implements LocationClient.Locatio...
method BackgroundLocationManager (line 40) | public BackgroundLocationManager(LocationClient locationClient, Backgr...
method formFinishedLoading (line 49) | public void formFinishedLoading() {
method activityDisplayed (line 58) | public BackgroundLocationMessage activityDisplayed() {
method activityHidden (line 111) | public void activityHidden() {
method isPendingPermissionCheck (line 119) | public boolean isPendingPermissionCheck() {
method locationPermissionGranted (line 123) | public BackgroundLocationMessage locationPermissionGranted() {
method locationPermissionDenied (line 161) | public void locationPermissionDenied() {
method backgroundLocationPreferenceToggled (line 174) | public void backgroundLocationPreferenceToggled() {
method locationPermissionChanged (line 192) | public void locationPermissionChanged() {
method locationProvidersChanged (line 204) | public void locationProvidersChanged() {
method startLocationRequests (line 217) | private void startLocationRequests() {
method stopLocationRequests (line 232) | private void stopLocationRequests() {
method onLocationChanged (line 238) | @Override
method onClientStart (line 246) | @Override
method onClientStartFailure (line 251) | @Override
method onClientStop (line 256) | @Override
type BackgroundLocationState (line 261) | private enum BackgroundLocationState {
type BackgroundLocationMessage (line 287) | public enum BackgroundLocationMessage {
method BackgroundLocationMessage (line 298) | BackgroundLocationMessage(int messageTextResourceId, boolean isMenuC...
method getMessageTextResourceId (line 303) | public int getMessageTextResourceId() {
method isMenuCharacterNeeded (line 307) | public boolean isMenuCharacterNeeded() {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/backgroundlocation/BackgroundLocationViewModel.java
class BackgroundLocationViewModel (line 19) | public class BackgroundLocationViewModel extends ViewModel {
method BackgroundLocationViewModel (line 23) | public BackgroundLocationViewModel(BackgroundLocationManager locationM...
method formFinishedLoading (line 27) | public void formFinishedLoading() {
method activityDisplayed (line 31) | public BackgroundLocationManager.BackgroundLocationMessage activityDis...
method activityHidden (line 35) | public void activityHidden() {
method isBackgroundLocationPermissionsCheckNeeded (line 39) | public boolean isBackgroundLocationPermissionsCheckNeeded() {
method locationPermissionsGranted (line 43) | public BackgroundLocationManager.BackgroundLocationMessage locationPer...
method locationPermissionsDenied (line 47) | public void locationPermissionsDenied() {
method locationPermissionChanged (line 51) | public void locationPermissionChanged() {
method locationProvidersChanged (line 55) | public void locationProvidersChanged() {
method backgroundLocationPreferenceToggled (line 59) | public void backgroundLocationPreferenceToggled(Settings generalSettin...
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/media/FormMediaUtils.java
class FormMediaUtils (line 17) | public final class FormMediaUtils {
method FormMediaUtils (line 19) | private FormMediaUtils() {
method getClip (line 23) | @Nullable
method getClipID (line 34) | public static String getClipID(FormEntryPrompt prompt) {
method getClipID (line 38) | public static String getClipID(FormEntryPrompt prompt, SelectChoice se...
method getPlayableAudioURI (line 42) | @Nullable
method getPlayableAudioURI (line 47) | @Nullable
method deriveReference (line 57) | @Nullable
method getPlayColor (line 71) | public static int getPlayColor(FormEntryPrompt prompt, ThemeUtils them...
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/media/PromptAutoplayer.java
class PromptAutoplayer (line 20) | public class PromptAutoplayer {
method PromptAutoplayer (line 28) | public PromptAutoplayer(AudioPlayer audioPlayer, ReferenceManager refe...
method autoplayIfNeeded (line 33) | public Boolean autoplayIfNeeded(FormEntryPrompt prompt) {
method hasAudioAutoplay (line 60) | private boolean hasAudioAutoplay(String autoplayOption) {
method getSelectClips (line 64) | private List<Clip> getSelectClips(FormEntryPrompt prompt) {
method appearanceDoesNotShowControls (line 89) | private boolean appearanceDoesNotShowControls(String appearance) {
method getPromptClip (line 95) | private Clip getPromptClip(FormEntryPrompt prompt) {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/questions/AnswersProvider.java
type AnswersProvider (line 8) | public interface AnswersProvider {
method getAnswers (line 9) | HashMap<FormIndex, IAnswerData> getAnswers();
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/questions/AudioVideoImageTextLabel.java
class AudioVideoImageTextLabel (line 49) | public class AudioVideoImageTextLabel extends RelativeLayout implements ...
method AudioVideoImageTextLabel (line 60) | public AudioVideoImageTextLabel(Context context) {
method AudioVideoImageTextLabel (line 66) | public AudioVideoImageTextLabel(Context context, AttributeSet attrs) {
method setTextView (line 72) | public void setTextView(TextView questionText) {
method setText (line 85) | public void setText(String questionText, boolean isRequiredQuestion, f...
method setAudio (line 98) | public void setAudio(String audioURI, AudioPlayer audioPlayer) {
method setImage (line 128) | public void setImage(@NonNull File imageFile, ImageLoader imageLoader) {
method setBigImage (line 141) | public void setBigImage(@NonNull File bigImageFile) {
method setVideo (line 145) | public void setVideo(@NonNull File videoFile) {
method setPlayTextColor (line 153) | public void setPlayTextColor(int textColor) {
method setMediaUtils (line 157) | public void setMediaUtils(MediaUtils mediaUtils) {
method playVideo (line 161) | public void playVideo() {
method getLabelTextView (line 165) | public TextView getLabelTextView() {
method getImageView (line 169) | public ImageView getImageView() {
method getMissingImage (line 173) | public TextView getMissingImage() {
method getVideoButton (line 177) | public Button getVideoButton() {
method getAudioButton (line 181) | public Button getAudioButton() {
method onClick (line 185) | @Override
method setEnabled (line 194) | @Override
method isEnabled (line 200) | @Override
method onImageClick (line 205) | private void onImageClick() {
method selectItem (line 213) | private void selectItem() {
method setItemClickListener (line 225) | public void setItemClickListener(SelectItemClickListener listener) {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/questions/NoButtonsItem.java
class NoButtonsItem (line 16) | public class NoButtonsItem extends FrameLayout {
method NoButtonsItem (line 20) | public NoButtonsItem(Context context, boolean enabled, ImageLoader ima...
method setUpNoButtonsItem (line 29) | public void setUpNoButtonsItem(File imageFile, String choiceText, Stri...
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/questions/QuestionDetails.java
class QuestionDetails (line 10) | public class QuestionDetails {
method QuestionDetails (line 15) | public QuestionDetails(FormEntryPrompt prompt) {
method QuestionDetails (line 19) | public QuestionDetails(FormEntryPrompt prompt, boolean readOnlyOverrid...
method getPrompt (line 24) | public FormEntryPrompt getPrompt() {
method isReadOnly (line 28) | public boolean isReadOnly() {
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/repeats/DeleteRepeatDialogFragment.java
class DeleteRepeatDialogFragment (line 24) | public class DeleteRepeatDialogFragment extends DialogFragment {
method DeleteRepeatDialogFragment (line 31) | public DeleteRepeatDialogFragment(ViewModelProvider.Factory viewModelF...
method onAttach (line 35) | @Override
method onCreateDialog (line 43) | @NonNull
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/saving/DiskFormSaver.java
class DiskFormSaver (line 15) | public class DiskFormSaver implements FormSaver {
method save (line 17) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/saving/FormSaveViewModel.java
class FormSaveViewModel (line 60) | public class FormSaveViewModel extends ViewModel implements MaterialProg...
method FormSaveViewModel (line 98) | public FormSaveViewModel(SavedStateHandle stateHandle, Supplier<Long> ...
method onCleared (line 132) | @Override
method saveForm (line 137) | public void saveForm(Uri instanceContentURI, boolean shouldFinalize, S...
method ignoreChanges (line 157) | public void ignoreChanges() {
method resumeSave (line 198) | public void resumeSave() {
method getAbsoluteInstancePath (line 217) | @Nullable
method isSaving (line 222) | public boolean isSaving() {
method cancel (line 226) | @Override
method setReason (line 235) | public void setReason(@NonNull String reason) {
method getReason (line 239) | public String getReason() {
method saveReason (line 243) | private boolean saveReason() {
method saveToDisk (line 252) | private void saveToDisk(SaveRequest saveRequest) {
method handleTaskResult (line 267) | private void handleTaskResult(SaveToDiskResult taskResult, SaveRequest...
method getSaveResult (line 321) | public LiveData<SaveResult> getSaveResult() {
method resumeFormEntry (line 325) | public void resumeFormEntry() {
method requiresReasonToSave (line 329) | private boolean requiresReasonToSave() {
method getFormName (line 335) | public String getFormName() {
method deleteAnswerFile (line 342) | @Override
method replaceAnswerFile (line 356) | @Override
method createAnswerFile (line 369) | @Override
method getAnswerFile (line 412) | @Override
method isSavingAnswerFile (line 422) | public LiveData<Boolean> isSavingAnswerFile() {
method clearMediaFiles (line 426) | private void clearMediaFiles() {
method getAnswerFileError (line 431) | public LiveData<String> getAnswerFileError() {
method answerFileErrorDisplayed (line 435) | public void answerFileErrorDisplayed() {
method canBeFullyDiscarded (line 439) | public boolean canBeFullyDiscarded() {
method getLastSavedTime (line 443) | public Long getLastSavedTime() {
method getInstance (line 447) | @Nullable
method removeSavepoint (line 452) | private void removeSavepoint(long formDbId, @Nullable Long instanceDbI...
class SaveResult (line 460) | public static class SaveResult {
method SaveResult (line 465) | SaveResult(State state, SaveRequest request) {
method SaveResult (line 469) | SaveResult(State state, SaveRequest request, String message) {
method getState (line 475) | public State getState() {
method getMessage (line 479) | public String getMessage() {
type State (line 483) | public enum State {
method getRequest (line 493) | public SaveRequest getRequest() {
class SaveRequest (line 498) | public static class SaveRequest {
method SaveRequest (line 505) | SaveRequest(Uri instanceContentURI, boolean viewExiting, String upda...
method shouldFinalize (line 512) | public boolean shouldFinalize() {
method viewExiting (line 516) | public boolean viewExiting() {
class SaveTask (line 521) | private static class SaveTask extends AsyncTask<Void, String, SaveToDi...
method SaveTask (line 535) | SaveTask(SaveRequest saveRequest, FormSaver formSaver, FormControlle...
method doInBackground (line 549) | @Override
method onProgressUpdate (line 558) | @Override
method onPostExecute (line 563) | @Override
type Listener (line 568) | interface Listener {
method onProgressPublished (line 569) | void onProgressPublished(String progress);
method onComplete (line 571) | void onComplete(SaveToDiskResult saveToDiskResult);
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/saving/FormSaver.java
type FormSaver (line 14) | public interface FormSaver {
method save (line 15) | SaveToDiskResult save(Uri instanceContentURI, FormController formContr...
type ProgressListener (line 18) | interface ProgressListener {
method onProgressUpdate (line 19) | void onProgressUpdate(String message);
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/saving/SaveAnswerFileErrorDialogFragment.java
class SaveAnswerFileErrorDialogFragment (line 15) | public class SaveAnswerFileErrorDialogFragment extends DialogFragment {
method SaveAnswerFileErrorDialogFragment (line 20) | public SaveAnswerFileErrorDialogFragment(ViewModelProvider.Factory vie...
method onAttach (line 24) | @Override
method onCreateDialog (line 32) | @NonNull
method onDismiss (line 42) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/saving/SaveAnswerFileProgressDialogFragment.java
class SaveAnswerFileProgressDialogFragment (line 9) | public class SaveAnswerFileProgressDialogFragment extends MaterialProgre...
method onAttach (line 11) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/formentry/saving/SaveFormProgressDialogFragment.java
class SaveFormProgressDialogFragment (line 12) | public class SaveFormProgressDialogFragment extends MaterialProgressDial...
method SaveFormProgressDialogFragment (line 17) | public SaveFormProgressDialogFragment(ViewModelProvider.Factory viewMo...
method onAttach (line 21) | @Override
method getOnCancelCallback (line 39) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/formhierarchy/FormHierarchyFragment.java
class FormHierarchyFragment (line 72) | public class FormHierarchyFragment extends Fragment {
method FormHierarchyFragment (line 89) | public FormHierarchyFragment(
method onAttach (line 106) | @Override
method handleInstanceEditResult (line 180) | private void handleInstanceEditResult(InstanceEditResult result) {
method openEditedInstance (line 194) | private void openEditedInstance(long dbId) {
method showOpenDraftEditDialog (line 200) | private void showOpenDraftEditDialog(Instance instance) {
method showOpenFinalizedEditDialog (line 219) | private void showOpenFinalizedEditDialog(Instance instance) {
method onViewCreated (line 240) | @Override
method refreshView (line 314) | public void refreshView() {
method refreshView (line 321) | private void refreshView(boolean isGoingUp) {
method calculateElementsToDisplay (line 362) | private void calculateElementsToDisplay(FormController formController,...
method jumpToHierarchyStartIndex (line 550) | private void jumpToHierarchyStartIndex() {
method isScreenEvent (line 600) | private boolean isScreenEvent(FormController formController, FormIndex...
method goUpLevel (line 612) | protected void goUpLevel() {
method getCurrentPath (line 638) | private CharSequence getCurrentPath() {
method onElementClick (line 662) | private void onElementClick(HierarchyItem item) {
method onQuestionClicked (line 683) | void onQuestionClicked(FormIndex index) {
method createErrorDialog (line 705) | protected void createErrorDialog(String errorMsg) {
method isDisplayingSingleGroup (line 730) | private boolean isDisplayingSingleGroup() {
method configureButtons (line 735) | private void configureButtons(FormHierarchyLayoutBinding binding, Form...
method didDeleteLastRepeatItem (line 769) | private boolean didDeleteLastRepeatItem() {
method didDeleteFirstRepeatItem (line 780) | private boolean didDeleteFirstRepeatItem() {
method onRepeatDeleted (line 787) | private void onRepeatDeleted() {
method goToPreviousEvent (line 809) | private void goToPreviousEvent() {
method navigateToTheLastRelevantIndex (line 822) | private void navigateToTheLastRelevantIndex(FormController formControl...
class FormHiearchyMenuProvider (line 845) | private static class FormHiearchyMenuProvider implements MenuProvider {
method FormHiearchyMenuProvider (line 853) | FormHiearchyMenuProvider(FormEntryViewModel formEntryViewModel, Form...
method onCreateMenu (line 861) | @Override
method onPrepareMenu (line 866) | @Override
method onMenuItemSelected (line 881) | @Override
method isGroupSizeLocked (line 904) | private boolean isGroupSizeLocked(FormIndex index) {
type OnClickListener (line 910) | interface OnClickListener {
method onEditClicked (line 911) | void onEditClicked();
method onGoUpClicked (line 913) | void onGoUpClicked();
method onAddRepeatClicked (line 915) | void onAddRepeatClicked();
method onDeleteRepeatClicked (line 917) | void onDeleteRepeatClicked();
FILE: collect_app/src/main/java/org/odk/collect/android/formmanagement/download/ServerFormDownloader.java
class ServerFormDownloader (line 36) | public class ServerFormDownloader implements FormDownloader {
method ServerFormDownloader (line 47) | public ServerFormDownloader(FormSource formSource, FormsRepository for...
method downloadForm (line 58) | @Override
method processOneForm (line 92) | private void processOneForm(ServerFormDetails fd, OngoingWorkListener ...
method isSubmissionOk (line 154) | private boolean isSubmissionOk(FormMetadata formMetadata) {
method installEverything (line 159) | private void installEverything(String tempMediaPath, FileResult fileRe...
method cleanUp (line 214) | private void cleanUp(FileResult fileResult, String tempMediaPath) {
method findOrCreateForm (line 230) | private FormResult findOrCreateForm(File formFile, FormMetadata formMe...
method saveNewForm (line 244) | private Form saveNewForm(FormMetadata formMetadata, File formFile, Str...
method downloadXform (line 266) | private FileResult downloadXform(String formName, String url, OngoingW...
method getFormFileName (line 287) | @NotNull
method validateHash (line 299) | private static String validateHash(String hash) {
method moveMediaFiles (line 303) | private static void moveMediaFiles(String tempMediaPath, File formMedi...
class FormResult (line 320) | private static class FormResult {
method FormResult (line 325) | private FormResult(Form form, boolean isNew) {
method isNew (line 330) | private boolean isNew() {
method getForm (line 334) | public Form getForm() {
class FileResult (line 339) | private static class FileResult {
method FileResult (line 344) | FileResult(File file, boolean isNew) {
method getFile (line 349) | private File getFile() {
method isNew (line 353) | private boolean isNew() {
class ProgressReporterAndSupplierStateListener (line 358) | private static class ProgressReporterAndSupplierStateListener implemen...
method ProgressReporterAndSupplierStateListener (line 362) | ProgressReporterAndSupplierStateListener(ProgressReporter progressRe...
method progressUpdate (line 367) | @Override
method isCancelled (line 374) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/formmanagement/matchexactly/ServerFormsSynchronizer.java
class ServerFormsSynchronizer (line 15) | public class ServerFormsSynchronizer {
method ServerFormsSynchronizer (line 23) | public ServerFormsSynchronizer(ServerFormsDetailsFetcher serverFormsDe...
method synchronize (line 31) | public void synchronize() throws FormSourceException {
type ServerFormsDetailsFetcher (line 59) | public interface ServerFormsDetailsFetcher {
method fetchFormDetails (line 60) | List<ServerFormDetails> fetchFormDetails(FormsRepository formsReposi...
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/MediaLoadingFragment.java
class MediaLoadingFragment (line 15) | public class MediaLoadingFragment extends Fragment {
method beginMediaLoadingTask (line 19) | public void beginMediaLoadingTask(Uri uri, FormController formControll...
method onCreate (line 24) | @Override
method onAttach (line 30) | @Override
method isMediaLoadingTaskRunning (line 38) | public boolean isMediaLoadingTaskRunning() {
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/dialogs/LocationProvidersDisabledDialog.java
class LocationProvidersDisabledDialog (line 30) | public class LocationProvidersDisabledDialog extends DialogFragment {
method newInstance (line 34) | public static LocationProvidersDisabledDialog newInstance() {
method show (line 43) | @Override
method onCreateDialog (line 55) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/dialogs/MovingBackwardsDialog.java
class MovingBackwardsDialog (line 27) | public class MovingBackwardsDialog extends DialogFragment {
type MovingBackwardsDialogListener (line 31) | public interface MovingBackwardsDialogListener {
method preventOtherWaysOfEditingForm (line 32) | void preventOtherWaysOfEditingForm();
method onAttach (line 37) | @Override
method onCreateDialog (line 45) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/dialogs/RankingWidgetDialog.java
class RankingWidgetDialog (line 53) | public class RankingWidgetDialog extends DialogFragment {
type RankingListener (line 60) | public interface RankingListener {
method onRankingChanged (line 61) | void onRankingChanged(List<SelectChoice> items);
method RankingWidgetDialog (line 64) | public RankingWidgetDialog() {
method RankingWidgetDialog (line 67) | public RankingWidgetDialog(List<SelectChoice> items, FormEntryPrompt f...
method onAttach (line 72) | @Override
method onCreateDialog (line 84) | @Override
method setUpRankingLayout (line 96) | private NestedScrollView setUpRankingLayout() {
method setUpPositionsLayout (line 113) | private LinearLayout setUpPositionsLayout() {
method setUpRecyclerView (line 132) | private RecyclerView setUpRecyclerView() {
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/dialogs/ResetSettingsResultDialog.java
class ResetSettingsResultDialog (line 27) | public class ResetSettingsResultDialog extends DialogFragment {
type ResetSettingsResultDialogListener (line 32) | public interface ResetSettingsResultDialogListener {
method onDialogClosed (line 33) | void onDialogClosed();
method newInstance (line 38) | public static ResetSettingsResultDialog newInstance(String dialogMessa...
method onAttach (line 47) | @Override
method onCreateDialog (line 55) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/dialogs/SelectMinimalDialog.java
class SelectMinimalDialog (line 30) | public abstract class SelectMinimalDialog extends MaterialFullScreenDial...
type SelectMinimalDialogListener (line 44) | public interface SelectMinimalDialogListener {
method updateSelectedItems (line 45) | void updateSelectedItems(List<Selection> items);
method SelectMinimalDialog (line 48) | public SelectMinimalDialog() {
method SelectMinimalDialog (line 51) | public SelectMinimalDialog(boolean isFlex, boolean isAutoComplete) {
method onAttach (line 56) | @Override
method onCreateView (line 69) | @Override
method onViewCreated (line 75) | @Override
method onDestroyView (line 82) | @Override
method onCloseClicked (line 89) | @Override
method onBackPressed (line 94) | @Override
method closeDialogAndSaveAnswers (line 99) | protected void closeDialogAndSaveAnswers() {
method getToolbar (line 107) | @Nullable
method initToolbar (line 113) | private void initToolbar() {
method initSearchBar (line 121) | private void initSearchBar() {
method initRecyclerView (line 143) | private void initRecyclerView() {
method setListener (line 150) | public void setListener(SelectMinimalDialogListener listener) {
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/dialogs/SelectMultiMinimalDialog.java
class SelectMultiMinimalDialog (line 14) | public class SelectMultiMinimalDialog extends SelectMinimalDialog {
method SelectMultiMinimalDialog (line 15) | public SelectMultiMinimalDialog() {
method SelectMultiMinimalDialog (line 18) | public SelectMultiMinimalDialog(List<Selection> selectedItems, boolean...
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/dialogs/SelectOneMinimalDialog.java
class SelectOneMinimalDialog (line 17) | public class SelectOneMinimalDialog extends SelectMinimalDialog implemen...
method SelectOneMinimalDialog (line 18) | public SelectOneMinimalDialog() {
method SelectOneMinimalDialog (line 21) | public SelectOneMinimalDialog(String selectedItem, boolean isFlex, boo...
method onViewCreated (line 29) | @Override
method onItemClicked (line 36) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/dialogs/SimpleDialog.java
class SimpleDialog (line 33) | public class SimpleDialog extends DialogFragment {
method newInstance (line 43) | public static SimpleDialog newInstance(String dialogTitle, int iconId,...
method show (line 61) | @Override
method onCreateDialog (line 73) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/viewmodels/RankingViewModel.java
class RankingViewModel (line 12) | public class RankingViewModel extends ViewModel {
method RankingViewModel (line 17) | private RankingViewModel(List<SelectChoice> items, FormEntryPrompt for...
method getItems (line 22) | public List<SelectChoice> getItems() {
method getFormEntryPrompt (line 26) | public FormEntryPrompt getFormEntryPrompt() {
class Factory (line 30) | public static class Factory implements ViewModelProvider.Factory {
method Factory (line 34) | public Factory(List<SelectChoice> items, FormEntryPrompt formEntryPr...
method create (line 39) | @NonNull
FILE: collect_app/src/main/java/org/odk/collect/android/fragments/viewmodels/SelectMinimalViewModel.java
class SelectMinimalViewModel (line 9) | public class SelectMinimalViewModel extends ViewModel {
method SelectMinimalViewModel (line 14) | private SelectMinimalViewModel(AbstractSelectListAdapter selectListAda...
method getSelectListAdapter (line 20) | public AbstractSelectListAdapter getSelectListAdapter() {
method isFlex (line 24) | public boolean isFlex() {
method isAutoComplete (line 28) | public boolean isAutoComplete() {
class Factory (line 32) | public static class Factory implements ViewModelProvider.Factory {
method Factory (line 37) | public Factory(AbstractSelectListAdapter selectListAdapter, boolean ...
method create (line 43) | @NonNull
FILE: collect_app/src/main/java/org/odk/collect/android/geo/MapConfiguratorProvider.java
class MapConfiguratorProvider (line 32) | public class MapConfiguratorProvider {
method MapConfiguratorProvider (line 42) | private MapConfiguratorProvider() {
method initOptions (line 51) | public static void initOptions(Context context) {
method getConfigurator (line 121) | public static @NonNull
method getConfigurator (line 130) | public static @NonNull MapConfigurator getConfigurator(String id) {
method getIds (line 135) | public static String[] getIds() {
method getLabelIds (line 144) | public static int[] getLabelIds() {
method isMapboxSupported (line 152) | private static boolean isMapboxSupported() {
method getOption (line 160) | private static @NonNull SourceOption getOption(String id) {
method getApplication (line 173) | private static Collect getApplication() {
class SourceOption (line 177) | private static class SourceOption {
method SourceOption (line 182) | private SourceOption(String id, int labelId, MapConfigurator cftor) {
FILE: collect_app/src/main/java/org/odk/collect/android/injection/config/AppDependencyModule.java
class AppDependencyModule (line 164) | @Module
method context (line 167) | @Provides
method provideMimeTypeMap (line 172) | @Provides
method providesUserAgent (line 177) | @Provides
method provideHttpInterface (line 183) | @Provides
method provideWebCredentials (line 194) | @Provides
method providesAnalytics (line 199) | @Provides
method providesPermissionsProvider (line 210) | @Provides
method providesReferenceManager (line 215) | @Provides
method providesSettingsProvider (line 220) | @Provides
method providesInstallIDProvider (line 227) | @Provides
method providesStoragePathProvider (line 232) | @Provides
method providesAdminPasswordProvider (line 243) | @Provides
method providesFormUpdateManger (line 248) | @Provides
method providesFormSubmitManager (line 253) | @Provides
method providesNetworkStateProvider (line 258) | @Provides
method providesQRCodeGenerator (line 263) | @Provides
method providesVersionInformation (line 268) | @Provides
method providesFileProvider (line 273) | @Provides
method providesWorkManager (line 278) | @Provides
method providesScheduler (line 283) | @Provides
method providesPreferenceMigrator (line 292) | @Provides
method providesPropertyManager (line 297) | @Provides
method providesSettingsChangeHandler (line 303) | @Provides
method providesODKAppSettingsImporter (line 308) | @Provides
method providesQRCodeDecoder (line 333) | @Provides
method providesNotifier (line 338) | @Provides
method providesChangeLockProvider (line 343) | @Provides
method providesScreenUtils (line 349) | @Provides
method providesAudioRecorder (line 354) | @Provides
method provideEntitiesRepositoryProvider (line 359) | @Provides
method provideSoftKeyboardController (line 364) | @Provides
method providesJsonPreferencesGenerator (line 369) | @Provides
method providesPermissionsChecker (line 374) | @Provides
method providesExternalAppIntentProvider (line 380) | @Provides
method providesFormSessionStore (line 386) | @Provides
method providesWebPageService (line 391) | @Provides
method providesProjectsRepository (line 396) | @Provides
method providesProjectCreator (line 402) | @Provides
method providesGson (line 408) | @Provides
method providesUUIDGenerator (line 413) | @Provides
method providesInstancesDataService (line 419) | @Provides
method providesItemsetsRepository (line 433) | @Provides
method providesCurrentProjectProvider (line 438) | @Provides
method providesFormsRepositoryProvider (line 443) | @Provides
method providesInstancesRepositoryProvider (line 448) | @Provides
method providesSavepointsRepositoryProvider (line 453) | @Provides
method providesProjectPreferencesViewModel (line 458) | @Provides
method providesReadyToSendViewModel (line 463) | @Provides
method providesMainMenuViewModelFactory (line 468) | @Provides
method providesAnalyticsInitializer (line 478) | @Provides
method providesFormSourceProvider (line 483) | @Provides
method providesFormsUpdater (line 488) | @Provides
method providesAutoSendSettingsProvider (line 493) | @Provides
method providesExistingProjectMigrator (line 498) | @Provides
method providesFormUpdatesUpgrader (line 503) | @Provides
method providesExistingSettingsMigrator (line 508) | @Provides
method providesGoogleDriveProjectsDeleter (line 513) | @Provides
method providesUpgradeInitializer (line 518) | @Provides
method providesApplicationInitializer (line 532) | @Provides
method providesProjectDeleter (line 537) | @Provides
method providesProjectResetter (line 542) | @Provides
method providesDisabledPreferencesRemover (line 547) | @Provides
method providesReferenceLayerRepository (line 552) | @Provides
method providesIntentLauncher (line 563) | @Provides
method providesLocationClient (line 568) | @Provides
method providesFusedLocationClient (line 573) | @Provides
method providesMediaUtils (line 579) | @Provides
method providesMapFragmentFactory (line 584) | @Provides
method providesImageLoader (line 589) | @Provides
method providesBlankFormListViewModel (line 594) | @Provides
method providesImageCompressorManager (line 599) | @Provides
method formEntryControllerFactory (line 605) | @Provides
method providesBroadcastReceiverRegister (line 612) | @Provides
method providesRestrictionsManager (line 617) | @Provides
method providesMDMConfigObserver (line 622) | @Provides
method providesBarcodeScannerViewFactory (line 653) | @Provides
method providesAudioPlayerFactory (line 658) | @Provides
method providesUniqueIdGenerator (line 663) | @Provides
FILE: collect_app/src/main/java/org/odk/collect/android/instancemanagement/InstanceDiskSynchronizer.java
class InstanceDiskSynchronizer (line 53) | public class InstanceDiskSynchronizer {
method getStatusMessage (line 63) | public String getStatusMessage() {
method InstanceDiskSynchronizer (line 67) | public InstanceDiskSynchronizer(SettingsProvider settingsProvider) {
method doInBackground (line 74) | public String doInBackground() {
method getFormIdFromInstance (line 157) | private String getFormIdFromInstance(final String instancePath) {
method getInstanceIdFromInstance (line 171) | private String getInstanceIdFromInstance(final String instancePath) {
method encryptInstanceIfNeeded (line 185) | private void encryptInstanceIfNeeded(Form form, Instance instance) thr...
method encryptInstance (line 193) | private void encryptInstance(Instance instance) throws EncryptionExcep...
method shouldInstanceBeEncrypted (line 222) | private boolean shouldInstanceBeEncrypted(Form form) {
FILE: collect_app/src/main/java/org/odk/collect/android/instancemanagement/send/InstanceUploaderActivity.java
class InstanceUploaderActivity (line 57) | public class InstanceUploaderActivity extends LocalizedActivity implemen...
method onCreate (line 96) | @Override
method init (line 106) | private void init(Bundle savedInstanceState) {
method onResume (line 203) | @Override
method onPostResume (line 214) | @Override
method onSaveInstanceState (line 220) | @Override
method onRetainCustomNonConfigurationInstance (line 238) | @Override
method onDestroy (line 243) | @Override
method uploadingComplete (line 251) | @Override
method progressUpdate (line 271) | @Override
method onCreateDialog (line 277) | @Override
method authRequest (line 323) | @Override
method createUploadInstancesResultDialog (line 359) | private void createUploadInstancesResultDialog(String message) {
method updatedCredentials (line 367) | @Override
method cancelledUpdatingCredentials (line 385) | @Override
method getReferrerUri (line 390) | private String getReferrerUri() {
FILE: collect_app/src/main/java/org/odk/collect/android/instancemanagement/send/InstanceUploaderListActivity.java
class InstanceUploaderListActivity (line 93) | public class InstanceUploaderListActivity extends LocalizedActivity impl...
method onCreate (line 141) | @Override
method onUploadButtonsClicked (line 186) | public void onUploadButtonsClicked() {
method init (line 209) | void init() {
method updateAutoSendStatus (line 254) | private void updateAutoSendStatus() {
method onResume (line 272) | @Override
method uploadSelectedFiles (line 279) | private void uploadSelectedFiles(long[] instanceIds) {
method onCreateOptionsMenu (line 285) | @Override
method onOptionsItemSelected (line 336) | @Override
method createPreferencesMenu (line 366) | private void createPreferencesMenu() {
method onItemClick (line 371) | @Override
method onSaveInstanceState (line 385) | @Override
method onActivityResult (line 399) | @Override
method setupAdapter (line 420) | private void setupAdapter() {
method getSortingOrderKey (line 428) | private String getSortingOrderKey() {
method updateAdapter (line 432) | private void updateAdapter() {
method onCreateLoader (line 436) | @NonNull
method onLoadFinished (line 447) | @Override
method onLoaderReset (line 461) | @Override
method onLongClick (line 466) | @Override
method showSentAndUnsentChoices (line 475) | private boolean showSentAndUnsentChoices() {
method getSortingOrder (line 504) | private String getSortingOrder() {
method getSelectedSortingOrder (line 523) | private int getSelectedSortingOrder() {
method restoreSelectedSortingOrder (line 530) | private void restoreSelectedSortingOrder() {
method showProgressBar (line 534) | private void showProgressBar() {
method hideProgressBarAndAllow (line 538) | private void hideProgressBarAndAllow() {
method hideProgressBar (line 542) | private void hideProgressBar() {
method getFilterText (line 546) | private CharSequence getFilterText() {
method saveSelectedSortingOrder (line 550) | private void saveSelectedSortingOrder(int selectedStringOrder) {
FILE: collect_app/src/main/java/org/odk/collect/android/javarosawrapper/FormIndexUtils.java
class FormIndexUtils (line 10) | public final class FormIndexUtils {
method FormIndexUtils (line 12) | private FormIndexUtils() {
method getPreviousLevel (line 21) | @Nullable
method getRepeatGroupIndex (line 30) | @Nullable
FILE: collect_app/src/main/java/org/odk/collect/android/javarosawrapper/InstanceMetadata.java
class InstanceMetadata (line 14) | public class InstanceMetadata {
method InstanceMetadata (line 19) | public InstanceMetadata(String instanceId, String instanceName, AuditC...
FILE: collect_app/src/main/java/org/odk/collect/android/javarosawrapper/JavaRosaFormController.java
class JavaRosaFormController (line 75) | public class JavaRosaFormController implements FormController {
method JavaRosaFormController (line 104) | public JavaRosaFormController(File mediaFolder, FormEntryController fe...
method isEditing (line 111) | @Override
method getFormDef (line 116) | public FormDef getFormDef() {
method getMediaFolder (line 120) | public File getMediaFolder() {
method getInstanceFile (line 124) | @Nullable
method setInstanceFile (line 129) | public void setInstanceFile(File instanceFile) {
method getAbsoluteInstancePath (line 133) | @Nullable
method getLastSavedPath (line 138) | @Nullable
method setIndexWaitingForData (line 143) | public void setIndexWaitingForData(FormIndex index) {
method getIndexWaitingForData (line 147) | public FormIndex getIndexWaitingForData() {
method getAuditEventLogger (line 151) | public AuditEventLogger getAuditEventLogger() {
method getXPath (line 165) | public String getXPath(FormIndex index) {
method getIndexFromXPath (line 196) | @Nullable
method getEvent (line 229) | public int getEvent() {
method getEvent (line 233) | public int getEvent(FormIndex index) {
method getFormIndex (line 237) | public FormIndex getFormIndex() {
method getLanguages (line 241) | public String[] getLanguages() {
method getFormTitle (line 245) | public String getFormTitle() {
method getLanguage (line 249) | public String getLanguage() {
method getBindAttribute (line 253) | private String getBindAttribute(FormIndex idx, String attributeNamespa...
method getCaptionHierarchy (line 263) | private FormEntryCaption[] getCaptionHierarchy() {
method getCaptionHierarchy (line 272) | private FormEntryCaption[] getCaptionHierarchy(FormIndex index) {
method getCaptionPrompt (line 276) | public FormEntryCaption getCaptionPrompt(FormIndex index) {
method getCaptionPrompt (line 280) | public FormEntryCaption getCaptionPrompt() {
method finalizeForm (line 284) | public void finalizeForm() {
method getInstance (line 291) | private FormInstance getInstance() {
method groupIsFieldList (line 301) | private boolean groupIsFieldList(FormIndex index) {
method repeatIsFieldList (line 311) | private boolean repeatIsFieldList(FormIndex index) {
method getAppearanceAttr (line 318) | private String getAppearanceAttr(@NonNull FormIndex index) {
method usesDatabaseExternalDataFeature (line 328) | public boolean usesDatabaseExternalDataFeature(@NonNull FormIndex inde...
method indexIsInFieldList (line 336) | public boolean indexIsInFieldList(FormIndex index) {
method indexIsInFieldList (line 364) | public boolean indexIsInFieldList() {
method currentPromptIsQuestion (line 368) | public boolean currentPromptIsQuestion() {
method isCurrentQuestionFirstInForm (line 375) | public boolean isCurrentQuestionFirstInForm() {
method answerQuestion (line 388) | public int answerQuestion(FormIndex index, IAnswerData data) throws Ja...
method validateAnswerConstraint (line 396) | public ValidationResult validateAnswerConstraint(FormIndex index, IAns...
method validateAnswers (line 405) | public ValidationResult validateAnswers(boolean moveToInvalidIndex) th...
method getFailedValidationResult (line 423) | private ValidationResult getFailedValidationResult(FormIndex index, in...
method saveAnswer (line 443) | public boolean saveAnswer(FormIndex index, IAnswerData data) throws Ja...
method stepToNextEvent (line 456) | public int stepToNextEvent(boolean stepIntoGroup) {
method stepOverGroup (line 466) | public int stepOverGroup() {
method stepToPreviousScreenEvent (line 477) | public int stepToPreviousScreenEvent() throws JavaRosaException {
method stepToNextScreenEvent (line 513) | public int stepToNextScreenEvent() throws JavaRosaException {
method stepToNextEventType (line 556) | private int stepToNextEventType(int eventType) {
method stepToOuterScreenEvent (line 568) | public int stepToOuterScreenEvent() {
method isDisplayableGroup (line 610) | public boolean isDisplayableGroup(FormIndex index) {
method isPresentationGroup (line 622) | private boolean isPresentationGroup(FormIndex groupIndex) {
method isLogicalGroup (line 633) | private boolean isLogicalGroup(FormIndex groupIndex) {
method saveAllScreenAnswers (line 642) | public ValidationResult saveAllScreenAnswers(HashMap<FormIndex, IAnswe...
method saveOneScreenAnswer (line 659) | public ValidationResult saveOneScreenAnswer(FormIndex index, IAnswerDa...
method stepToPreviousEvent (line 676) | public int stepToPreviousEvent() {
method jumpToIndex (line 730) | public int jumpToIndex(FormIndex index) {
method jumpToNewRepeatPrompt (line 734) | public void jumpToNewRepeatPrompt() {
method newRepeat (line 745) | public void newRepeat() {
method deleteRepeat (line 749) | public void deleteRepeat() {
method setLanguage (line 754) | public void setLanguage(String language) {
method getQuestionPrompts (line 758) | @NonNull
method isGroupEmpty (line 792) | private boolean isGroupEmpty() {
method getIndicesForGroup (line 800) | private List<FormIndex> getIndicesForGroup(GroupDef gd) {
method getIndicesForGroup (line 805) | private List<FormIndex> getIndicesForGroup(GroupDef gd, FormIndex curr...
method isGroupRelevant (line 825) | public boolean isGroupRelevant() {
method getQuestionPrompt (line 836) | public FormEntryPrompt getQuestionPrompt(FormIndex index) {
method getQuestionPrompt (line 840) | public FormEntryPrompt getQuestionPrompt() {
method getQuestionPromptConstraintText (line 844) | public String getQuestionPromptConstraintText(FormIndex index) {
method currentCaptionPromptIsQuestion (line 848) | public boolean currentCaptionPromptIsQuestion() {
method getQuestionPromptRequiredText (line 852) | public String getQuestionPromptRequiredText(FormIndex index) {
method getGroupsForIndex (line 889) | public FormEntryCaption[] getGroupsForIndex(FormIndex index) {
method indexContainsRepeatableGroup (line 915) | public boolean indexContainsRepeatableGroup() {
method indexContainsRepeatableGroup (line 919) | public boolean indexContainsRepeatableGroup(FormIndex formIndex) {
method getLastRepeatedGroupRepeatCount (line 932) | public int getLastRepeatedGroupRepeatCount() {
method getLastRepeatedGroupName (line 945) | public String getLastRepeatedGroupName() {
method getLastGroup (line 963) | private FormEntryCaption getLastGroup() {
method getLastGroupText (line 972) | public String getLastGroupText() {
method getSubmissionDataReference (line 982) | private IDataReference getSubmissionDataReference() {
method isSubmissionEntireForm (line 993) | public boolean isSubmissionEntireForm() {
method getFilledInFormXml (line 998) | public ByteArrayPayload getFilledInFormXml() throws IOException {
method getSubmissionXml (line 1006) | public ByteArrayPayload getSubmissionXml() throws IOException {
method findDepthFirst (line 1016) | private TreeElement findDepthFirst(TreeElement parent, String name) {
method getSubmissionMetadata (line 1032) | public InstanceMetadata getSubmissionMetadata() {
method currentFormAuditsLocation (line 1109) | public boolean currentFormAuditsLocation() {
method currentFormCollectsBackgroundLocation (line 1115) | public boolean currentFormCollectsBackgroundLocation() {
method getAnswer (line 1119) | public IAnswerData getAnswer(TreeReference treeReference) {
method getEntities (line 1123) | public EntitiesExtra getEntities() {
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/AdvanceToNextListener.java
type AdvanceToNextListener (line 17) | public interface AdvanceToNextListener {
method advance (line 19) | void advance();
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/DeleteInstancesListener.java
type DeleteInstancesListener (line 23) | public interface DeleteInstancesListener {
method deleteComplete (line 24) | void deleteComplete(int deletedInstances);
method progressUpdate (line 26) | void progressUpdate(int progress, int total);
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/DownloadFormsTaskListener.java
type DownloadFormsTaskListener (line 25) | public interface DownloadFormsTaskListener {
method formsDownloadingComplete (line 26) | void formsDownloadingComplete(Map<ServerFormDetails, FormDownloadExcep...
method progressUpdate (line 28) | void progressUpdate(String currentFile, int progress, int total);
method formsDownloadingCancelled (line 30) | void formsDownloadingCancelled();
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/FormListDownloaderListener.java
type FormListDownloaderListener (line 25) | public interface FormListDownloaderListener {
method formListDownloadingComplete (line 26) | void formListDownloadingComplete(HashMap<String, ServerFormDetails> fo...
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/FormLoaderListener.java
type FormLoaderListener (line 24) | public interface FormLoaderListener extends ProgressNotifier {
method loadingComplete (line 25) | void loadingComplete(FormLoaderTask task, FormDef fd, String warningMsg);
method loadingError (line 27) | void loadingError(String errorMsg);
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/InstanceUploaderListener.java
type InstanceUploaderListener (line 24) | public interface InstanceUploaderListener {
method uploadingComplete (line 25) | void uploadingComplete(HashMap<String, String> result);
method progressUpdate (line 27) | void progressUpdate(int progress, int total);
method authRequest (line 29) | void authRequest(Uri url, HashMap<String, String> doneSoFar);
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/Result.java
type Result (line 8) | public interface Result<T> {
method onComplete (line 9) | void onComplete(T result);
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/SelectItemClickListener.java
type SelectItemClickListener (line 3) | public interface SelectItemClickListener {
method onItemClicked (line 4) | void onItemClicked();
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/ThousandsSeparatorTextWatcher.java
class ThousandsSeparatorTextWatcher (line 18) | public class ThousandsSeparatorTextWatcher implements TextWatcher {
method ThousandsSeparatorTextWatcher (line 23) | public ThousandsSeparatorTextWatcher(EditText editText) {
method beforeTextChanged (line 35) | @Override
method onTextChanged (line 40) | @Override
method afterTextChanged (line 43) | @Override
method getDecimalFormattedString (line 67) | private static String getDecimalFormattedString(String value) {
method getOriginalString (line 101) | public static String getOriginalString(String string) {
FILE: collect_app/src/main/java/org/odk/collect/android/listeners/WidgetValueChangedListener.java
type WidgetValueChangedListener (line 5) | public interface WidgetValueChangedListener {
method widgetValueChanged (line 6) | void widgetValueChanged(QuestionWidget changedWidget);
FILE: collect_app/src/main/java/org/odk/collect/android/location/client/MaxAccuracyWithinTimeoutLocationClientWrapper.java
class MaxAccuracyWithinTimeoutLocationClientWrapper (line 21) | public class MaxAccuracyWithinTimeoutLocationClientWrapper implements Lo...
method MaxAccuracyWithinTimeoutLocationClientWrapper (line 34) | public MaxAccuracyWithinTimeoutLocationClientWrapper(LocationClient lo...
method requestLocationUpdates (line 45) | public void requestLocationUpdates(long timeoutSeconds) {
method onClientStart (line 61) | @Override
method onClientStartFailure (line 73) | @Override
method onClientStop (line 78) | @Override
method onLocationChanged (line 94) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/logic/FileReference.java
class FileReference (line 19) | public class FileReference implements Reference {
method FileReference (line 23) | public FileReference(String localPart, String referencePart) {
method getInternalURI (line 28) | private String getInternalURI() {
method doesBinaryExist (line 32) | @Override
method getStream (line 37) | @Override
method getURI (line 42) | @Override
method isReadOnly (line 47) | @Override
method getOutputStream (line 52) | @Override
method remove (line 57) | @Override
method getLocalURI (line 63) | @Override
method probeAlternativeReferences (line 68) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/logic/FileReferenceFactory.java
class FileReferenceFactory (line 13) | public class FileReferenceFactory extends PrefixedRootFactory {
method FileReferenceFactory (line 17) | public FileReferenceFactory(String localRoot) {
method factory (line 24) | @Override
method toString (line 29) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/logic/ImmutableDisplayableQuestion.java
class ImmutableDisplayableQuestion (line 34) | public class ImmutableDisplayableQuestion {
method ImmutableDisplayableQuestion (line 79) | public ImmutableDisplayableQuestion(FormEntryPrompt question) {
method getFormIndex (line 95) | public FormIndex getFormIndex() {
method getAnswerText (line 99) | public Object getAnswerText() {
method sameAs (line 107) | public boolean sameAs(FormEntryPrompt question) {
method selectChoiceListsEqual (line 119) | private static boolean selectChoiceListsEqual(List<SelectChoice> selec...
method getGuidanceHintText (line 140) | private static String getGuidanceHintText(FormEntryPrompt question) {
method selectChoicesEqual (line 152) | private static boolean selectChoicesEqual(SelectChoice selectChoice1, ...
FILE: collect_app/src/main/java/org/odk/collect/android/logic/actions/setgeopoint/CollectSetGeopointAction.java
class CollectSetGeopointAction (line 52) | public class CollectSetGeopointAction extends SetGeopointAction implemen...
method CollectSetGeopointAction (line 57) | public CollectSetGeopointAction() {
method CollectSetGeopointAction (line 62) | CollectSetGeopointAction(TreeReference targetReference) {
method requestLocationUpdates (line 66) | @Override
method onLocationChanged (line 89) | @Override
method isBackgroundLocationEnabled (line 101) | private boolean isBackgroundLocationEnabled() {
FILE: collect_app/src/main/java/org/odk/collect/android/logic/actions/setgeopoint/CollectSetGeopointActionHandler.java
class CollectSetGeopointActionHandler (line 25) | public class CollectSetGeopointActionHandler extends SetGeopointActionHa...
method getSetGeopointAction (line 26) | public SetGeopointAction getSetGeopointAction() {
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/ServerPreferencesAdder.java
class ServerPreferencesAdder (line 28) | public class ServerPreferencesAdder {
method ServerPreferencesAdder (line 32) | public ServerPreferencesAdder(PreferenceFragmentCompat fragment) {
method add (line 36) | public boolean add() {
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/dialogs/ResetDialogPreference.java
class ResetDialogPreference (line 26) | public class ResetDialogPreference extends DialogPreference {
method ResetDialogPreference (line 28) | public ResetDialogPreference(Context context, AttributeSet attrs) {
method getDialogLayoutResource (line 32) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/dialogs/ResetDialogPreferenceFragmentCompat.java
class ResetDialogPreferenceFragmentCompat (line 34) | public class ResetDialogPreferenceFragmentCompat extends PreferenceDialo...
method newInstance (line 47) | public static ResetDialogPreferenceFragmentCompat newInstance(String k...
method onAttach (line 55) | @Override
method onBindDialogView (line 63) | @Override
method onStart (line 78) | @Override
method onDetach (line 84) | @Override
method onClick (line 94) | @Override
method onDialogClosed (line 101) | @Override
method resetSelected (line 105) | private void resetSelected() {
method handleResult (line 147) | private void handleResult(final List<Integer> resetActions, List<Integ...
method onCheckedChanged (line 217) | @Override
method adjustResetButtonAccessibility (line 222) | public void adjustResetButtonAccessibility() {
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/dialogs/ServerAuthDialogFragment.java
class ServerAuthDialogFragment (line 22) | public class ServerAuthDialogFragment extends DialogFragment {
method onAttach (line 29) | @Override
method onCreateDialog (line 35) | @NonNull
method getDialogView (line 56) | public View getDialogView() {
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/filters/ControlCharacterFilter.java
class ControlCharacterFilter (line 9) | public class ControlCharacterFilter implements InputFilter {
method filter (line 10) | public CharSequence filter(CharSequence source, int start, int end,
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/screens/BaseAdminPreferencesFragment.java
class BaseAdminPreferencesFragment (line 12) | public abstract class BaseAdminPreferencesFragment extends BasePreferenc...
method onAttach (line 17) | @Override
method onCreatePreferences (line 24) | @Override
method onResume (line 29) | @Override
method onPause (line 35) | @Override
method onSettingChanged (line 41) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/screens/BaseProjectPreferencesFragment.java
class BaseProjectPreferencesFragment (line 21) | public abstract class BaseProjectPreferencesFragment extends BasePrefere...
method onAttach (line 36) | @Override
method onCreatePreferences (line 46) | @Override
method onViewCreated (line 51) | @Override
method onResume (line 57) | @Override
method onPause (line 63) | @Override
method onSettingChanged (line 69) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/screens/ExperimentalPreferencesFragment.java
class ExperimentalPreferencesFragment (line 15) | public class ExperimentalPreferencesFragment extends BaseProjectPreferen...
method onCreatePreferences (line 17) | @Override
method onViewCreated (line 34) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/screens/FormManagementPreferencesFragment.java
class FormManagementPreferencesFragment (line 48) | public class FormManagementPreferencesFragment extends BaseProjectPrefer...
method onAttach (line 56) | @Override
method onCreatePreferences (line 62) | @Override
method onSettingChanged (line 77) | @Override
method updateDisabledPrefs (line 90) | private void updateDisabledPrefs() {
method initListPref (line 126) | private void initListPref(String key) {
method initPref (line 143) | private void initPref(String key) {
method initGuidancePrefs (line 156) | private void initGuidancePrefs() {
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/screens/FormMetadataPreferencesFragment.java
class FormMetadataPreferencesFragment (line 27) | public class FormMetadataPreferencesFragment extends BaseProjectPreferen...
method onAttach (line 35) | @Override
method onCreatePreferences (line 41) | @Override
method onActivityCreated (line 51) | @Override
method setupPrefs (line 58) | private void setupPrefs() {
class PropertyManagerPropertySummaryProvider (line 78) | private class PropertyManagerPropertySummaryProvider implements Prefer...
method PropertyManagerPropertySummaryProvider (line 83) | PropertyManagerPropertySummaryProvider(PropertyManager propertyManag...
method provideSummary (line 88) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/screens/ServerPreferencesFragment.java
class ServerPreferencesFragment (line 38) | public class ServerPreferencesFragment extends BaseProjectPreferencesFra...
method onAttach (line 44) | @Override
method onCreatePreferences (line 50) | @Override
method addServerPreferences (line 57) | public void addServerPreferences() {
method createChangeListener (line 83) | private Preference.OnPreferenceChangeListener createChangeListener() {
method maskPasswordSummary (line 125) | private void maskPasswordSummary(String password) {
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/screens/UserInterfacePreferencesFragment.java
class UserInterfacePreferencesFragment (line 38) | public class UserInterfacePreferencesFragment extends BaseProjectPrefere...
method onAttach (line 47) | @Override
method onCreatePreferences (line 58) | @Override
method initNavigationPrefs (line 68) | private void initNavigationPrefs() {
method initFontSizePref (line 82) | private void initFontSizePref() {
method initLanguagePrefs (line 96) | private void initLanguagePrefs() {
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/screens/UserSettingsAccessPreferencesFragment.java
class UserSettingsAccessPreferencesFragment (line 7) | public class UserSettingsAccessPreferencesFragment extends BaseAdminPref...
method onCreatePreferences (line 9) | @Override
FILE: collect_app/src/main/java/org/odk/collect/android/preferences/utilities/PreferencesUtils.java
class PreferencesUtils (line 6) | public final class PreferencesUtils {
method PreferencesUtils (line 8) | private PreferencesUtils() {
method displayDisabled (line 12) | public static void displayDisabled(CheckBoxPreference preference, bool...
method displayDisabled (line 19) | public static void displayDisabled(Preference preference, String displ...
FILE: collect_app/src/main/java/org/odk/collect/android/storage/StorageSubdirectory.java
type StorageSubdirectory (line 3) | public enum StorageSubdirectory {
method StorageSubdirectory (line 15) | StorageSubdirectory(String directoryName) {
method getDirectoryName (line 19) | public String getDirectoryName() {
FILE: collect_app/src/main/java/org/odk/collect/android/tasks/DownloadFormListTask.java
class DownloadFormListTask (line 52) | @Deprecated
method DownloadFormListTask (line 74) | public DownloadFormListTask() {
method doInBackground (line 83) | @Override
method onPostExecute (line 107) | @Override
method setDownloaderListener (line 125) | public void setDownloaderListener(FormListDownloaderListener sl) {
method setAlternateCredentials (line 131) | public void setAlternateCredentials(WebCredentialsUtils webCredentials...
method setTemporaryCredentials (line 145) | private void setTemporaryCredentials() {
method clearTemporaryCredentials (line 159) | private void clearTemporaryCredentials() {
FILE: collect_app/src/main/java/org/odk/collect/android/tasks/DownloadFormsTask.java
class DownloadFormsTask (line 40) | public class DownloadFormsTask extends
method DownloadFormsTask (line 47) | public DownloadFormsTask(String projectId, FormsDataService formsDataS...
method doInBackground (line 52) | @Override
method onCancelled (line 67) | @Override
method onPostExecute (line 76) | @Override
method onProgressUpdate (line 85) | @Override
method setDownloaderListener (line 98) | public void setDownloaderListener(DownloadFormsTaskListener sl) {
FILE: collect_app/src/main/java/org/odk/collect/android/tasks/FormLoaderTask.java
class FormLoaderTask (line 83) | public class FormLoaderTask extends SchedulerAsyncTaskMimic<Void, String...
method onPreExecute (line 106) | @Override
method getInstancePath (line 111) | public String getInstancePath() {
method getForm (line 115) | public Form getForm() {
method getInstance (line 119) | public Instance getInstance() {
class FECWrapper (line 123) | public static class FECWrapper {
method FECWrapper (line 127) | protected FECWrapper(FormController controller, boolean usedSavepoin...
method getController (line 132) | public FormController getController() {
method hasUsedSavepoint (line 136) | protected boolean hasUsedSavepoint() {
method free (line 140) | protected void free() {
method FormLoaderTask (line 147) | public FormLoaderTask(Uri uri, String uriMimeType, String xpath, Strin...
method doInBackground (line 163) | @Override
method logFormDetails (line 285) | private void logFormDetails(File formFile, File formMediaDir) {
method unzipMediaFiles (line 314) | private static void unzipMediaFiles(File formMediaDir) {
method createFormDefFromCacheOrXml (line 333) | private FormDef createFormDefFromCacheOrXml(String formPath, File form...
method processItemSets (line 366) | private void processItemSets(File formMediaDir) {
method initializeForm (line 405) | private boolean initializeForm(FormDef formDef, FormEntryController fe...
method onProgressUpdate (line 449) | @Override
method importData (line 461) | public static void importData(File instanceFile, FormEntryController f...
method onCancelled (line 504) | @Override
method onPostExecute (line 511) | @Override
method setFormLoaderListener (line 528) | public void setFormLoaderListener(FormLoaderListener sl) {
method getFormController (line 534) | public FormController getFormController() {
method hasUsedSavepoint (line 538) | public boolean hasUsedSavepoint() {
method destroy (line 542) | public void destroy() {
method hasPendingActivityResult (line 549) | public boolean hasPendingActivityResult() {
method getRequestCode (line 553) | public int getRequestCode() {
method getResultCode (line 557) | public int getResultCode() {
method getIntent (line 561) | public Intent getIntent() {
method setActivityResult (line 565) | public void setActivityResult(int requestCode, int resultCode, Intent ...
method readCSV (line 572) | private void readCSV(File csv, String formHash, String pathHash) {
method getFormDef (line 615) | public FormDef getFormDef() {
type FormEntryControllerFactory (line 619) | public interface FormEntryControllerFactory {
method create (line 620) | FormEntryController create(@NonNull FormDef formDef, @NonNull File f...
FILE: collect_app/src/main/java/org/odk/collect/android/tasks/InstanceUploaderTask.java
class InstanceUploaderTask (line 58) | public class InstanceUploaderTask extends AsyncTask<Long, Integer, Insta
Copy disabled (too large)
Download .json
Condensed preview — 2434 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (12,983K chars).
[
{
"path": ".circleci/config.yml",
"chars": 13777,
"preview": "# This config and the the Gradle flags/opts are based on: https://circleci.com/docs/2.0/language-android/\n# and https://"
},
{
"path": ".circleci/generate-app-test-list.sh",
"chars": 295,
"preview": "ls -R collect_app/src/test/java/ | grep Test.java > .circleci/collect_app_test_files.txt\nls -R collect_app/src/test/java"
},
{
"path": ".circleci/gradle-large.properties",
"chars": 271,
"preview": "# Gradle config for \"X-Large\" Circle CI resource (https://circleci.com/pricing/price-list/)\n\norg.gradle.jvmargs=-Xmx8g -"
},
{
"path": ".circleci/gradle.properties",
"chars": 235,
"preview": "# Gradle config for \"Large\" Circle CI resource (https://circleci.com/pricing/price-list/)\n\norg.gradle.jvmargs=-Xmx2560m "
},
{
"path": ".circleci/test_modules.txt",
"chars": 270,
"preview": "shared\nforms-test\nandroidshared\nasync\nstrings\naudio-clips\naudio-recorder\nprojects\nlocation\ngeo\nupgrade\npermissions\nsetti"
},
{
"path": ".editorconfig",
"chars": 1481,
"preview": "root = true\n\n[*.{kt,kts}]\nktlint_standard_no-blank-lines-in-chained-method-calls = disabled\nktlint_standard_trailing-com"
},
{
"path": ".gitattributes",
"chars": 10,
"preview": "* text=lf\n"
},
{
"path": ".github/CODE_OF_CONDUCT.md",
"chars": 124,
"preview": "Please refer to the project-wide [ODK Code of Conduct](https://github.com/getodk/governance/blob/master/CODE-OF-CONDUCT."
},
{
"path": ".github/ISSUE_TEMPLATE/config.yml",
"chars": 424,
"preview": "blank_issues_enabled: true\ncontact_links: \n - name: \"Report an issue\"\n about: \"For when Collect is behaving in an un"
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 1162,
"preview": "<!-- \n\nThank you for taking the time to report an ODK Collect issue!\n\nThis space is for bugs that have clear reproductio"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 1491,
"preview": "Closes #\n\n<!-- \nThank you for contributing to ODK Collect!\n\nBefore sending this PR, please read\nhttps://github.com/getod"
},
{
"path": ".github/TESTING_RESULT_TEMPLATES.md",
"chars": 475,
"preview": "# Testing result templates\n\n## Tested with success!\n#### Verified on: [List of devices/os versions]\n#### Verified cases:"
},
{
"path": ".gitignore",
"chars": 778,
"preview": "build\n.gradle\n.idea\n.kotlin\nlocal.properties\n*.iml\n.DS_Store\n*.sublime-project\n\n# emacs backup files\n*.*~\n\n# gradle env "
},
{
"path": ".hgtags",
"chars": 5792,
"preview": "5836a17a0e6f28b4e5b7a7abc9152446f4806ee2 v1.0.0\n30d9314729db181177bb7209d1c16cd65a5095eb v1.1.0\n827cf67bd902b80c37dce8d4"
},
{
"path": "LICENSE.md",
"chars": 10426,
"preview": "Apache License\n==============\n\n_Version 2.0, January 2004_ \n_<<http://www.apache.org/licenses/>>_\n\n### Terms and "
},
{
"path": "README.md",
"chars": 21484,
"preview": "# ODK Collect\n\n\n[.\n"
},
{
"path": "analytics/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "analytics/build.gradle.kts",
"chars": 877,
"preview": "plugins {\n alias(libs.plugins.androidLibrary)\n}\n\napply(from = \"../config/quality.gradle\")\n\nandroid {\n compileSdk ="
},
{
"path": "analytics/proguard-rules.pro",
"chars": 751,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "analytics/src/main/AndroidManifest.xml",
"chars": 336,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <uses-"
},
{
"path": "analytics/src/main/java/org/odk/collect/analytics/Analytics.kt",
"chars": 1561,
"preview": "package org.odk.collect.analytics\n\ninterface Analytics {\n fun logEvent(event: String)\n fun logEventWithParam(event"
},
{
"path": "analytics/src/main/java/org/odk/collect/analytics/BlockableFirebaseAnalytics.kt",
"chars": 1279,
"preview": "package org.odk.collect.analytics\n\nimport android.app.Application\nimport android.os.Bundle\nimport com.google.firebase.an"
},
{
"path": "analytics/src/main/java/org/odk/collect/analytics/NoopAnalytics.kt",
"chars": 442,
"preview": "package org.odk.collect.analytics\n\nclass NoopAnalytics : Analytics {\n override fun logEvent(event: String) {}\n ove"
},
{
"path": "androidshared/.gitignore",
"chars": 7,
"preview": "/build\n"
},
{
"path": "androidshared/build.gradle.kts",
"chars": 2128,
"preview": "plugins {\n alias(libs.plugins.androidLibrary)\n alias(libs.plugins.kotlinKsp)\n alias(libs.plugins.composeCompile"
},
{
"path": "androidshared/src/androidTest/java/org/odk/collect/androidshared/bitmap/ImageCompressorTest.kt",
"chars": 7838,
"preview": "/*\n * Copyright 2017 Nafundi\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use th"
},
{
"path": "androidshared/src/androidTest/java/org/odk/collect/androidshared/bitmap/ImageFileUtilsTest.kt",
"chars": 9474,
"preview": "/*\n * Copyright 2017 Nafundi\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use th"
},
{
"path": "androidshared/src/main/AndroidManifest.xml",
"chars": 328,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <uses-"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/async/TrackableWorker.kt",
"chars": 1149,
"preview": "package org.odk.collect.androidshared.async\n\nimport org.odk.collect.androidshared.livedata.MutableNonNullLiveData\nimport"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/bitmap/ImageCompressor.kt",
"chars": 4573,
"preview": "/*\n * Copyright 2017 Nafundi\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use th"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/bitmap/ImageFileUtils.kt",
"chars": 7272,
"preview": "package org.odk.collect.androidshared.bitmap\n\nimport android.graphics.Bitmap\nimport android.graphics.Bitmap.CompressForm"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/data/AppState.kt",
"chars": 2615,
"preview": "package org.odk.collect.androidshared.data\n\nimport android.app.Activity\nimport android.app.Application\nimport android.ap"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/data/Consumable.kt",
"chars": 733,
"preview": "package org.odk.collect.androidshared.data\n\nimport androidx.lifecycle.LifecycleOwner\nimport androidx.lifecycle.LiveData\n"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/data/Data.kt",
"chars": 2967,
"preview": "package org.odk.collect.androidshared.data\n\nimport kotlinx.coroutines.flow.StateFlow\nimport org.odk.collect.androidshare"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/livedata/LiveDataExt.kt",
"chars": 945,
"preview": "package org.odk.collect.androidshared.livedata\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MediatorLiv"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/livedata/LiveDataUtils.java",
"chars": 3102,
"preview": "package org.odk.collect.androidshared.livedata;\n\nimport androidx.lifecycle.LiveData;\nimport androidx.lifecycle.MediatorL"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/livedata/NonNullLiveData.kt",
"chars": 563,
"preview": "package org.odk.collect.androidshared.livedata\n\nimport androidx.lifecycle.LiveData\n\n/**\n * Allows creating LiveData valu"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/system/BroadcastReceiverRegister.kt",
"chars": 690,
"preview": "package org.odk.collect.androidshared.system\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nim"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/system/CameraUtils.java",
"chars": 2318,
"preview": "package org.odk.collect.androidshared.system;\n\n/*\nCopyright 2018 Theodoros Tyrovouzis\n\nLicensed under the Apache License"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/system/ContextExt.kt",
"chars": 943,
"preview": "package org.odk.collect.androidshared.system\n\nimport android.app.Activity\nimport android.content.Context\nimport android."
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/system/ExternalFilesUtils.kt",
"chars": 1096,
"preview": "package org.odk.collect.androidshared.system\n\nimport android.content.Context\nimport java.io.File\nimport java.io.IOExcept"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/system/IntentLauncher.kt",
"chars": 1522,
"preview": "package org.odk.collect.androidshared.system\n\nimport android.app.Activity\nimport android.content.Context\nimport android."
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/system/OpenGLVersionChecker.kt",
"chars": 1148,
"preview": "package org.odk.collect.androidshared.system\n\nimport android.app.ActivityManager\nimport android.content.Context\n\n/**\n * "
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/system/PlayServicesChecker.java",
"chars": 1084,
"preview": "package org.odk.collect.androidshared.system;\n\nimport android.app.Activity;\nimport android.content.Context;\n\nimport com."
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/system/ProcessRestoreDetector.kt",
"chars": 886,
"preview": "package org.odk.collect.androidshared.system\n\nimport android.content.Context\nimport android.os.Bundle\nimport org.odk.col"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/system/UriExt.kt",
"chars": 3150,
"preview": "package org.odk.collect.androidshared.system\n\nimport android.content.ContentResolver\nimport android.content.Context\nimpo"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/AlertStore.kt",
"chars": 613,
"preview": "package org.odk.collect.androidshared.ui\n\n/**\n * Component for recording \"alerts\". This is useful for testing transient "
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/Animations.kt",
"chars": 2456,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.animation.Animator\nimport android.animation.AnimatorSet\nimport "
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/ColorPickerDialog.kt",
"chars": 5016,
"preview": "package org.odk.collect.androidshared\n\nimport android.app.Dialog\nimport android.graphics.Color\nimport android.graphics.d"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/ComposeThemeProvider.kt",
"chars": 1787,
"preview": "package org.odk.collect.androidshared.ui\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui.platform"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/DialogFragmentUtils.kt",
"chars": 3165,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.os.Bundle\nimport androidx.fragment.app.DialogFragment\nimport an"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/DialogUtils.kt",
"chars": 548,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.content.Context\nimport androidx.annotation.StringRes\nimport com"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/DisplayString.kt",
"chars": 398,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.content.Context\n\nsealed class DisplayString {\n\n data class R"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/EdgeToEdge.kt",
"chars": 2449,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.app.Activity\nimport android.content.Context\nimport android.view"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/FragmentFactoryBuilder.kt",
"chars": 1361,
"preview": "package org.odk.collect.androidshared.ui\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentFac"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/GroupClickListener.kt",
"chars": 540,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.view.View\nimport androidx.constraintlayout.widget.Group\n\n// htt"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/ListFragmentStateAdapter.kt",
"chars": 710,
"preview": "package org.odk.collect.androidshared.ui\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentAct"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/MenuExt.kt",
"chars": 502,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.annotation.SuppressLint\nimport android.view.Menu\nimport android"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/ObviousProgressBar.kt",
"chars": 1430,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.content.Context\nimport android.os.Handler\nimport android.util.A"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/OneSignTextWatcher.kt",
"chars": 872,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.text.Editable\nimport android.text.TextWatcher\nimport android.wi"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/PrefUtils.kt",
"chars": 2224,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.content.Context\nimport androidx.preference.ListPreference\nimpor"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/ReturnToAppActivity.kt",
"chars": 505,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.app.Activity\nimport android.os.Bundle\n\n/**\n * This Activity wil"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/SnackbarUtils.kt",
"chars": 5733,
"preview": "/*\n\nCopyright 2018 Shobhit\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file exc"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/ToastUtils.kt",
"chars": 2921,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.app.Activity\nimport android.app.Application\nimport android.os.B"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/compose/Margins.kt",
"chars": 517,
"preview": "package org.odk.collect.androidshared.ui.compose\n\nimport androidx.compose.runtime.Composable\nimport androidx.compose.ui."
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/DoubleClickSafeMaterialButton.kt",
"chars": 723,
"preview": "package org.odk.collect.androidshared.ui.multiclicksafe\n\nimport android.content.Context\nimport android.util.AttributeSet"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/MultiClickGuard.kt",
"chars": 1525,
"preview": "package org.odk.collect.androidshared.ui.multiclicksafe\n\nimport android.os.SystemClock\n\nobject MultiClickGuard {\n @Jv"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/MultiClickSafeMaterialButton.kt",
"chars": 929,
"preview": "package org.odk.collect.androidshared.ui.multiclicksafe\n\nimport android.content.Context\nimport android.util.AttributeSet"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/MultiClickSafeTextInputEditText.kt",
"chars": 585,
"preview": "package org.odk.collect.androidshared.ui.multiclicksafe\n\nimport android.content.Context\nimport android.util.AttributeSet"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/ui/multiclicksafe/MultiClickSaveOnClickListener.kt",
"chars": 542,
"preview": "package org.odk.collect.androidshared.ui.multiclicksafe\n\nimport android.view.View\n\nabstract class MultiClickSafeOnClickL"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/AppBarUtils.kt",
"chars": 532,
"preview": "package org.odk.collect.androidshared.utils\n\nimport android.app.Activity\nimport androidx.appcompat.app.AppCompatActivity"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/ColorUtils.kt",
"chars": 768,
"preview": "package org.odk.collect.androidshared.utils\n\nimport androidx.annotation.ColorInt\nimport androidx.core.graphics.ColorUtil"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/CompressionUtils.kt",
"chars": 2648,
"preview": "/*\n * Copyright (C) 2017 Shobhit\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use t"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/FileExt.kt",
"chars": 595,
"preview": "package org.odk.collect.androidshared.utils\n\nimport android.content.Context\nimport android.graphics.Bitmap\nimport androi"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/InMemUniqueIdGenerator.kt",
"chars": 366,
"preview": "package org.odk.collect.androidshared.utils\n\nimport java.util.concurrent.atomic.AtomicInteger\n\nclass InMemUniqueIdGenera"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/PathUtils.kt",
"chars": 1215,
"preview": "package org.odk.collect.androidshared.utils\n\nimport org.odk.collect.shared.files.FileExt.sanitizedCanonicalPath\nimport t"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/PreferenceFragmentCompatUtils.kt",
"chars": 254,
"preview": "package org.odk.collect.androidshared.utils\n\nimport androidx.preference.Preference\nimport androidx.preference.Preference"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/ScreenUtils.java",
"chars": 1635,
"preview": "/*\n * Copyright 2019 Nafundi\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use th"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/SettingsUniqueIdGenerator.kt",
"chars": 1071,
"preview": "package org.odk.collect.androidshared.utils\n\nimport org.odk.collect.shared.settings.Settings\n\nclass SettingsUniqueIdGene"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/UniqueIdGenerator.kt",
"chars": 117,
"preview": "package org.odk.collect.androidshared.utils\n\ninterface UniqueIdGenerator {\n fun getInt(identifier: String): Int\n}\n"
},
{
"path": "androidshared/src/main/java/org/odk/collect/androidshared/utils/Validator.kt",
"chars": 1419,
"preview": "/*\n * Copyright 2016 Nafundi\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use th"
},
{
"path": "androidshared/src/main/res/color/color_error_button_icon.xml",
"chars": 294,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <item a"
},
{
"path": "androidshared/src/main/res/color/color_on_primary_low_emphasis.xml",
"chars": 416,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\nProvides a medium emphasis version of the `colorOnPrimary` Material Theming"
},
{
"path": "androidshared/src/main/res/color/color_on_surface_high_emphasis.xml",
"chars": 520,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\nProvides a medium emphasis version of the `colorOnSurface` Material Theming"
},
{
"path": "androidshared/src/main/res/color/color_on_surface_low_emphasis.xml",
"chars": 519,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\nProvides a medium emphasis version of the `colorOnSurface` Material Theming"
},
{
"path": "androidshared/src/main/res/color/color_on_surface_medium_emphasis.xml",
"chars": 522,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\nProvides a medium emphasis version of the `colorOnSurface` Material Theming"
},
{
"path": "androidshared/src/main/res/color/color_primary_low_emphasis.xml",
"chars": 515,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<!--\nProvides a medium emphasis version of the `colorPrimary` Material Theming a"
},
{
"path": "androidshared/src/main/res/drawable/color_circle.xml",
"chars": 132,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:sha"
},
{
"path": "androidshared/src/main/res/drawable/ic_close_24.xml",
"chars": 408,
"preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:width=\"24dp\"\n android:height=\"24dp\"\n "
},
{
"path": "androidshared/src/main/res/drawable/list_item_divider.xml",
"chars": 424,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <!--"
},
{
"path": "androidshared/src/main/res/drawable/radio_button_inset.xml",
"chars": 246,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<inset xmlns:android=\"http://schemas.android.com/apk/res/android\"\n android:dra"
},
{
"path": "androidshared/src/main/res/drawable/shadow_up.xml",
"chars": 449,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n <item"
},
{
"path": "androidshared/src/main/res/layout/app_bar_layout.xml",
"chars": 2391,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n When scrolling, the top app bar fills with a contrasting color to creat"
},
{
"path": "androidshared/src/main/res/layout/color_picker_dialog_layout.xml",
"chars": 16380,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:"
},
{
"path": "androidshared/src/main/res/values/attrs.xml",
"chars": 200,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <declare-styleable name=\"MultiClickSafeMaterialButton\">\n <"
},
{
"path": "androidshared/src/main/res/values/color_picker_dialog_colors.xml",
"chars": 719,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <!-- Color picker palette -->\n <color name=\"color1\">#EA4633</c"
},
{
"path": "androidshared/src/main/res/values/dimens.xml",
"chars": 676,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <!-- \"Standard\" set of margin sizes for Android (pre and post Mat"
},
{
"path": "androidshared/src/main/res/values/styles.xml",
"chars": 397,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <style name=\"Widget.AndroidShared.ButtonBar\" parent=\"\">\n <"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/async/TrackableWorkerTest.kt",
"chars": 891,
"preview": "package org.odk.collect.androidshared.async\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.hamcrest.Co"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/livedata/LiveDataUtilsTest.kt",
"chars": 1650,
"preview": "package org.odk.collect.androidshared.livedata\n\nimport androidx.arch.core.executor.testing.InstantTaskExecutorRule\nimpor"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/system/UriExtTest.kt",
"chars": 1383,
"preview": "package org.odk.collect.androidshared.system\n\nimport android.app.Application\nimport androidx.core.net.toUri\nimport andro"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/ui/ColorPickerDialogTest.kt",
"chars": 5052,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.graphics.Color\nimport android.graphics.drawable.GradientDrawabl"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/ui/OneSignTextWatcherTest.kt",
"chars": 932,
"preview": "package org.odk.collect.androidshared.ui\n\nimport android.widget.EditText\nimport androidx.test.platform.app.Instrumentati"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/ui/ReturnToAppActivityTest.kt",
"chars": 622,
"preview": "package org.odk.collect.androidshared.ui\n\nimport androidx.lifecycle.Lifecycle\nimport androidx.test.ext.junit.rules.Activ"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/utils/ColorUtilsTest.kt",
"chars": 1291,
"preview": "package org.odk.collect.androidshared.utils\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.hamcrest.Ma"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/utils/CompressionUtilsTest.kt",
"chars": 1946,
"preview": "package org.odk.collect.androidshared.utils\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.hamcrest.Ma"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/utils/DialogFragmentUtilsTest.java",
"chars": 2465,
"preview": "package org.odk.collect.androidshared.utils;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\nimport static org.ham"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/utils/InMemUniqueIdGeneratorTest.kt",
"chars": 207,
"preview": "package org.odk.collect.androidshared.utils\n\nclass InMemUniqueIdGeneratorTest : UniqueIdGeneratorTest() {\n override f"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/utils/IntentLauncherImplTest.kt",
"chars": 4202,
"preview": "package org.odk.collect.androidshared.utils\n\nimport android.app.Activity\nimport android.content.Context\nimport android.c"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/utils/PathUtilsTest.kt",
"chars": 1463,
"preview": "package org.odk.collect.androidshared.utils\n\nimport org.hamcrest.MatcherAssert.assertThat\nimport org.hamcrest.Matchers.e"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/utils/SettingsUniqueIdGeneratorTest.kt",
"chars": 1346,
"preview": "package org.odk.collect.androidshared.utils\n\nimport org.hamcrest.MatcherAssert.assertThat\nimport org.hamcrest.Matchers.e"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/utils/UniqueIdGeneratorTest.kt",
"chars": 729,
"preview": "package org.odk.collect.androidshared.utils\n\nimport org.hamcrest.MatcherAssert.assertThat\nimport org.hamcrest.Matchers.e"
},
{
"path": "androidshared/src/test/java/org/odk/collect/androidshared/utils/ValidatorTest.kt",
"chars": 6234,
"preview": "/*\n * Copyright 2017 Nafundi\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use th"
},
{
"path": "androidshared/src/test/resources/robolectric.properties",
"chars": 7,
"preview": "sdk=33\n"
},
{
"path": "androidtest/.gitignore",
"chars": 6,
"preview": "/build"
},
{
"path": "androidtest/README.md",
"chars": 218,
"preview": "# androidtest\n\nIncludes helpers (arguably missing from `androidx.test:core`) for writing [\"Local tests\" and \"Instrumente"
},
{
"path": "androidtest/build.gradle.kts",
"chars": 1466,
"preview": "plugins {\n alias(libs.plugins.androidLibrary)\n}\n\napply(from = \"../config/quality.gradle\")\n\nandroid {\n compileSdk ="
},
{
"path": "androidtest/src/main/AndroidManifest.xml",
"chars": 122,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n</manifest"
},
{
"path": "androidtest/src/main/java/org/odk/collect/androidtest/ActivityScenarioExtensions.kt",
"chars": 944,
"preview": "package org.odk.collect.androidtest\n\nimport android.app.Activity\nimport android.os.Bundle\nimport android.os.PersistableB"
},
{
"path": "androidtest/src/main/java/org/odk/collect/androidtest/ActivityScenarioLauncherRule.kt",
"chars": 1571,
"preview": "package org.odk.collect.androidtest\n\nimport android.app.Activity\nimport android.content.Intent\nimport androidx.test.core"
},
{
"path": "androidtest/src/main/java/org/odk/collect/androidtest/DrawableMatcher.kt",
"chars": 2780,
"preview": "package org.odk.collect.androidtest\n\nimport android.app.Application\nimport android.graphics.Bitmap\nimport android.graphi"
},
{
"path": "androidtest/src/main/java/org/odk/collect/androidtest/FakeLifecycleOwner.kt",
"chars": 547,
"preview": "package org.odk.collect.androidtest\n\nimport androidx.lifecycle.Lifecycle\nimport androidx.lifecycle.LifecycleOwner\nimport"
},
{
"path": "androidtest/src/main/java/org/odk/collect/androidtest/FragmentScenarioExtensions.kt",
"chars": 494,
"preview": "package org.odk.collect.androidtest\n\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentResultLi"
},
{
"path": "androidtest/src/main/java/org/odk/collect/androidtest/LiveDataTestUtils.kt",
"chars": 1636,
"preview": "package org.odk.collect.androidtest\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.Observer\nimport java.u"
},
{
"path": "androidtest/src/main/java/org/odk/collect/androidtest/MainDispatcherRule.kt",
"chars": 884,
"preview": "package org.odk.collect.androidtest\n\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.ExperimentalCorouti"
},
{
"path": "androidtest/src/main/java/org/odk/collect/androidtest/NodeInteractionExtensions.kt",
"chars": 1029,
"preview": "package org.odk.collect.androidtest\n\nimport android.app.Application\nimport androidx.annotation.StringRes\nimport androidx"
},
{
"path": "androidtest/src/main/java/org/odk/collect/androidtest/RecordedIntentsRule.kt",
"chars": 967,
"preview": "package org.odk.collect.androidtest\n\nimport androidx.test.core.app.ActivityScenario\nimport androidx.test.espresso.intent"
},
{
"path": "async/.gitignore",
"chars": 6,
"preview": "/build"
},
{
"path": "async/build.gradle.kts",
"chars": 1400,
"preview": "plugins {\n alias(libs.plugins.androidLibrary)\n}\n\napply(from = \"../config/quality.gradle\")\n\nandroid {\n compileSdk ="
},
{
"path": "async/proguard-rules.pro",
"chars": 750,
"preview": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguar"
},
{
"path": "async/src/main/AndroidManifest.xml",
"chars": 557,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n xmlns:tools=\"http://schemas.android.com/tools\">"
},
{
"path": "async/src/main/java/org/odk/collect/async/Cancellable.kt",
"chars": 83,
"preview": "package org.odk.collect.async\n\ninterface Cancellable {\n fun cancel(): Boolean\n}\n"
},
{
"path": "async/src/main/java/org/odk/collect/async/OngoingWorkListener.kt",
"chars": 860,
"preview": "/*\n * Copyright 2018 Nafundi\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use th"
},
{
"path": "async/src/main/java/org/odk/collect/async/Scheduler.kt",
"chars": 4026,
"preview": "package org.odk.collect.async\n\nimport androidx.annotation.StringRes\nimport kotlinx.coroutines.flow.Flow\nimport java.util"
},
{
"path": "async/src/main/java/org/odk/collect/async/SchedulerAsyncTaskMimic.kt",
"chars": 2281,
"preview": "package org.odk.collect.async\n\nimport android.os.AsyncTask\n\n/**\n * Basic reimplementation of the [AsyncTask] API that al"
},
{
"path": "async/src/main/java/org/odk/collect/async/SchedulerBuilder.kt",
"chars": 2910,
"preview": "package org.odk.collect.async\n\nimport kotlinx.coroutines.flow.Flow\nimport java.util.function.Consumer\nimport java.util.f"
},
{
"path": "async/src/main/java/org/odk/collect/async/ScopeCancellable.kt",
"chars": 278,
"preview": "package org.odk.collect.async\n\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.cancel\n\ninternal class"
},
{
"path": "async/src/main/java/org/odk/collect/async/TaskRunner.kt",
"chars": 450,
"preview": "package org.odk.collect.async\n\nimport kotlinx.coroutines.Runnable\nimport kotlinx.coroutines.flow.Flow\nimport java.util.f"
},
{
"path": "async/src/main/java/org/odk/collect/async/TaskSpec.kt",
"chars": 1881,
"preview": "package org.odk.collect.async\n\nimport android.content.Context\nimport androidx.work.BackoffPolicy\nimport java.util.functi"
},
{
"path": "async/src/main/java/org/odk/collect/async/TaskSpecRunner.kt",
"chars": 173,
"preview": "package org.odk.collect.async\n\ninterface TaskSpecRunner {\n fun run(tag: String, taskSpec: TaskSpec, inputData: Map<St"
},
{
"path": "async/src/main/java/org/odk/collect/async/TaskSpecScheduler.kt",
"chars": 354,
"preview": "package org.odk.collect.async\n\ninterface TaskSpecScheduler {\n fun schedule(\n tag: String,\n spec: TaskSp"
},
{
"path": "async/src/main/java/org/odk/collect/async/coroutines/CoroutineTaskRunner.kt",
"chars": 1963,
"preview": "package org.odk.collect.async.coroutines\n\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers"
},
{
"path": "async/src/main/java/org/odk/collect/async/network/ConnectivityProvider.kt",
"chars": 917,
"preview": "package org.odk.collect.async.network\n\nimport android.content.Context\nimport android.net.ConnectivityManager\nimport org."
},
{
"path": "async/src/main/java/org/odk/collect/async/network/NetworkStateProvider.kt",
"chars": 262,
"preview": "package org.odk.collect.async.network\n\nimport org.odk.collect.async.Scheduler\n\ninterface NetworkStateProvider {\n val "
},
{
"path": "async/src/main/java/org/odk/collect/async/services/ForegroundServiceTaskSpecRunner.kt",
"chars": 4571,
"preview": "package org.odk.collect.async.services\n\nimport android.app.Application\nimport android.app.NotificationChannel\nimport and"
},
{
"path": "async/src/main/java/org/odk/collect/async/workmanager/TaskSpecWorker.kt",
"chars": 1891,
"preview": "package org.odk.collect.async.workmanager\n\nimport android.content.Context\nimport androidx.work.Worker\nimport androidx.wo"
},
{
"path": "async/src/main/java/org/odk/collect/async/workmanager/WorkManagerTaskSpecScheduler.kt",
"chars": 3829,
"preview": "package org.odk.collect.async.workmanager\n\nimport android.net.NetworkCapabilities\nimport android.net.NetworkRequest\nimpo"
},
{
"path": "async/src/test/java/org/odk/collect/async/TaskSpecTest.kt",
"chars": 1009,
"preview": "package org.odk.collect.async\n\nimport android.content.Context\nimport androidx.work.BackoffPolicy\nimport org.hamcrest.Mat"
},
{
"path": "async/src/test/java/org/odk/collect/async/workmanager/TaskSpecWorkerTest.kt",
"chars": 5612,
"preview": "package org.odk.collect.async.workmanager\n\nimport android.content.Context\nimport androidx.test.core.app.ApplicationProvi"
},
{
"path": "audio-clips/.gitignore",
"chars": 6,
"preview": "/build"
},
{
"path": "audio-clips/build.gradle.kts",
"chars": 1405,
"preview": "plugins {\n alias(libs.plugins.androidLibrary)\n}\n\napply(from = \"../config/quality.gradle\")\n\nandroid {\n compileSdk ="
},
{
"path": "audio-clips/src/main/AndroidManifest.xml",
"chars": 88,
"preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n /\n</manifest>"
},
{
"path": "audio-clips/src/main/java/org/odk/collect/audioclips/AudioClipViewModel.kt",
"chars": 6797,
"preview": "package org.odk.collect.audioclips\n\nimport android.media.MediaPlayer\nimport androidx.lifecycle.LiveData\nimport androidx."
},
{
"path": "audio-clips/src/main/java/org/odk/collect/audioclips/AudioPlayer.kt",
"chars": 530,
"preview": "package org.odk.collect.audioclips\n\nimport androidx.lifecycle.LiveData\nimport java.util.function.Consumer\n\ninterface Aud"
},
{
"path": "audio-clips/src/main/java/org/odk/collect/audioclips/AudioPlayerFactory.kt",
"chars": 265,
"preview": "package org.odk.collect.audioclips\n\nimport androidx.activity.ComponentActivity\nimport androidx.lifecycle.LifecycleOwner\n"
},
{
"path": "audio-clips/src/main/java/org/odk/collect/audioclips/Clip.kt",
"chars": 89,
"preview": "package org.odk.collect.audioclips\n\ndata class Clip(val clipID: String, val uRI: String)\n"
},
{
"path": "audio-clips/src/main/java/org/odk/collect/audioclips/PlaybackFailedException.kt",
"chars": 125,
"preview": "package org.odk.collect.audioclips\n\ndata class PlaybackFailedException(val uRI: String, val exceptionMsg: Int) : Excepti"
},
{
"path": "audio-clips/src/main/java/org/odk/collect/audioclips/ThreadSafeMediaPlayerWrapper.kt",
"chars": 2260,
"preview": "package org.odk.collect.audioclips\n\nimport android.media.MediaPlayer\nimport android.os.StrictMode\nimport java.io.IOExcep"
},
{
"path": "audio-clips/src/test/java/org/odk/collect/audioclips/AudioClipViewModelTest.kt",
"chars": 15921,
"preview": "package org.odk.collect.audioclips\n\nimport android.media.MediaPlayer\nimport androidx.arch.core.executor.testing.InstantT"
},
{
"path": "audio-recorder/.gitignore",
"chars": 6,
"preview": "/build"
},
{
"path": "audio-recorder/build.gradle.kts",
"chars": 1821,
"preview": "plugins {\n alias(libs.plugins.androidLibrary)\n alias(libs.plugins.kotlinKsp)\n}\n\napply(from = \"../config/quality.gr"
},
{
"path": "audio-recorder/src/main/AndroidManifest.xml",
"chars": 370,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n <uses-"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/DaggerSetup.kt",
"chars": 2962,
"preview": "package org.odk.collect.audiorecorder\n\nimport android.app.Application\nimport android.media.MediaRecorder\nimport dagger.B"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/mediarecorder/MediaRecorderRecordingResource.kt",
"chars": 2120,
"preview": "package org.odk.collect.audiorecorder.mediarecorder\n\nimport android.media.MediaRecorder\nimport org.odk.collect.audioreco"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/recorder/Recorder.kt",
"chars": 490,
"preview": "package org.odk.collect.audiorecorder.recorder\n\nimport org.odk.collect.audiorecorder.recording.MicInUseException\nimport "
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/recorder/RecordingResource.kt",
"chars": 515,
"preview": "package org.odk.collect.audiorecorder.recorder\n\nimport java.io.IOException\n\n/**\n * Allows faking/stubbing/mocking with o"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/recorder/RecordingResourceRecorder.kt",
"chars": 2139,
"preview": "package org.odk.collect.audiorecorder.recorder\n\nimport org.odk.collect.audiorecorder.recording.MicInUseException\nimport "
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/recording/AudioRecorder.kt",
"chars": 1130,
"preview": "package org.odk.collect.audiorecorder.recording\n\nimport androidx.lifecycle.LiveData\nimport org.odk.collect.androidshared"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/recording/AudioRecorderFactory.kt",
"chars": 519,
"preview": "package org.odk.collect.audiorecorder.recording\n\nimport android.app.Application\nimport org.odk.collect.androidshared.dat"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/recording/AudioRecorderService.kt",
"chars": 4590,
"preview": "package org.odk.collect.audiorecorder.recording\n\nimport android.app.Application\nimport android.app.Service\nimport androi"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/recording/internal/ForegroundServiceAudioRecorder.kt",
"chars": 2798,
"preview": "package org.odk.collect.audiorecorder.recording.internal\n\nimport android.app.Application\nimport android.content.Intent\ni"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/recording/internal/RecordingForegroundServiceNotification.kt",
"chars": 2799,
"preview": "package org.odk.collect.audiorecorder.recording.internal\n\nimport android.app.NotificationChannel\nimport android.app.Noti"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/recording/internal/RecordingRepository.kt",
"chars": 1822,
"preview": "package org.odk.collect.audiorecorder.recording.internal\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.M"
},
{
"path": "audio-recorder/src/main/java/org/odk/collect/audiorecorder/testsupport/StubAudioRecorder.kt",
"chars": 2955,
"preview": "package org.odk.collect.audiorecorder.testsupport\n\nimport androidx.lifecycle.LiveData\nimport androidx.lifecycle.MutableL"
},
{
"path": "audio-recorder/src/test/java/org/odk/collect/audiorecorder/mediarecorder/AMRRecordingResourceTest.kt",
"chars": 1023,
"preview": "package org.odk.collect.audiorecorder.mediarecorder\n\nimport android.media.MediaRecorder\nimport org.junit.Test\nimport org"
},
{
"path": "audio-recorder/src/test/java/org/odk/collect/audiorecorder/recorder/RecordingResourceRecorderTest.kt",
"chars": 7045,
"preview": "package org.odk.collect.audiorecorder.recorder\n\nimport com.google.common.io.Files\nimport org.hamcrest.MatcherAssert.asse"
},
{
"path": "audio-recorder/src/test/java/org/odk/collect/audiorecorder/recording/AudioRecorderTest.kt",
"chars": 4552,
"preview": "package org.odk.collect.audiorecorder.recording\n\nimport org.hamcrest.MatcherAssert.assertThat\nimport org.hamcrest.Matche"
},
{
"path": "audio-recorder/src/test/java/org/odk/collect/audiorecorder/recording/internal/AudioRecorderServiceTest.kt",
"chars": 9920,
"preview": "package org.odk.collect.audiorecorder.recording.internal\n\nimport android.app.Application\nimport android.content.Intent\ni"
},
{
"path": "audio-recorder/src/test/java/org/odk/collect/audiorecorder/recording/internal/ForegroundServiceAudioRecorderTest.kt",
"chars": 4984,
"preview": "package org.odk.collect.audiorecorder.recording.internal\n\nimport android.app.Application\nimport androidx.arch.core.execu"
},
{
"path": "audio-recorder/src/test/java/org/odk/collect/audiorecorder/recording/internal/RecordingForegroundServiceNotificationTest.kt",
"chars": 1778,
"preview": "package org.odk.collect.audiorecorder.recording.internal\n\nimport android.app.Application\nimport android.app.Notification"
},
{
"path": "audio-recorder/src/test/java/org/odk/collect/audiorecorder/support/FakeRecorder.kt",
"chars": 1770,
"preview": "package org.odk.collect.audiorecorder.support\n\nimport org.odk.collect.audiorecorder.recorder.Output\nimport org.odk.colle"
},
{
"path": "audio-recorder/src/test/java/org/odk/collect/audiorecorder/testsupport/RobolectricApplication.kt",
"chars": 1389,
"preview": "package org.odk.collect.audiorecorder.testsupport\n\nimport android.app.Application\nimport org.odk.collect.androidshared.d"
},
{
"path": "audio-recorder/src/test/java/org/odk/collect/audiorecorder/testsupport/StubAudioRecorderTest.kt",
"chars": 836,
"preview": "package org.odk.collect.audiorecorder.testsupport\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.junit"
},
{
"path": "audio-recorder/src/test/resources/robolectric.properties",
"chars": 83,
"preview": "application=org.odk.collect.audiorecorder.testsupport.RobolectricApplication\nsdk=33"
},
{
"path": "benchmark.sh",
"chars": 130,
"preview": "./gradlew collect_app:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.package=org.odk.collect.android."
},
{
"path": "build.gradle",
"chars": 3487,
"preview": "apply from: 'secrets.gradle'\n\n// Top-level build file where you can add configuration options common to all sub-projects"
},
{
"path": "check-size.sh",
"chars": 236,
"preview": "set -e\n\nif [ $(ls -l collect_app/build/outputs/apk/selfSignedRelease/*.apk | awk '{print $5}') -gt $1 ];then\n echo \"APK"
},
{
"path": "codecov.yml",
"chars": 159,
"preview": "codecov:\n notify:\n require_ci_to_pass: no\ncoverage:\n precision: 2\n round: down\n range: \"10...100\"\n status:\n p"
},
{
"path": "collect_app/build.gradle",
"chars": 16044,
"preview": "plugins {\n alias(libs.plugins.androidApplication)\n alias(libs.plugins.kotlinKsp)\n alias(libs.plugins.kotlinParc"
},
{
"path": "collect_app/google-services.json",
"chars": 1069,
"preview": "{\n \"project_info\": {\n \"project_number\": \"640412404956\",\n \"firebase_url\": \"https://opendatakit-forks.firebaseio.co"
},
{
"path": "collect_app/proguard-rules.txt",
"chars": 1422,
"preview": "-dontwarn com.google.**\n-dontwarn au.com.bytecode.**\n-dontwarn org.joda.time.**\n-dontwarn org.osmdroid.**\n-dontwarn org."
},
{
"path": "collect_app/src/androidTest/assets/instances/One Question_2021-06-22_15-55-50.xml",
"chars": 284,
"preview": "<?xml version='1.0' ?><data id=\"one_question\" orx:version=\"1\" xmlns:ev=\"http://www.w3.org/2001/xml-events\" xmlns:orx=\"ht"
},
{
"path": "collect_app/src/androidTest/assets/instances/one-question-google_2023-08-08_14-51-00.xml",
"chars": 308,
"preview": "<?xml version='1.0' encoding='UTF-8' ?><data id=\"one_question_google\" orx:version=\"1\" xmlns:ev=\"http://www.w3.org/2001/x"
},
{
"path": "collect_app/src/androidTest/java/org/odk/collect/android/benchmark/EntitiesBenchmarkTest.kt",
"chars": 4896,
"preview": "package org.odk.collect.android.benchmark\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.hamcrest.Matc"
},
{
"path": "collect_app/src/androidTest/java/org/odk/collect/android/benchmark/FormsUpdateBenchmarkTest.kt",
"chars": 2151,
"preview": "package org.odk.collect.android.benchmark\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.hamcrest.Matc"
},
{
"path": "collect_app/src/androidTest/java/org/odk/collect/android/benchmark/SearchBenchmarkTest.kt",
"chars": 2457,
"preview": "package org.odk.collect.android.benchmark\n\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.hamcrest.Matc"
},
{
"path": "collect_app/src/androidTest/java/org/odk/collect/android/benchmark/support/Benchmarker.kt",
"chars": 1595,
"preview": "package org.odk.collect.android.benchmark.support\n\nimport org.hamcrest.MatcherAssert.assertThat\nimport org.hamcrest.Matc"
},
{
"path": "collect_app/src/androidTest/java/org/odk/collect/android/feature/entitymanagement/ViewEntitiesTest.kt",
"chars": 1814,
"preview": "package org.odk.collect.android.feature.entitymanagement\n\nimport org.junit.Rule\nimport org.junit.Test\nimport org.junit.r"
},
{
"path": "collect_app/src/androidTest/java/org/odk/collect/android/feature/external/AndroidShortcutsTest.kt",
"chars": 2616,
"preview": "package org.odk.collect.android.feature.external\n\nimport android.content.Intent\nimport android.content.Intent.EXTRA_SHOR"
}
]
// ... and 2234 more files (download for full content)
About this extraction
This page contains the full source code of the opendatakit/collect GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 2434 files (11.5 MB), approximately 3.2M tokens, and a symbol index with 5828 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.