Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -264,9 +264,9 @@ jobs:
name: Check APK size hasn't increased
command: |
if [[ -n "$GOOGLE_MAPS_API_KEY" ]]; then \
./check-size.sh 23117869
./check-size.sh 23300000
else
./check-size.sh 13863000
./check-size.sh 14000000
fi

- run:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
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()
}
}
1 change: 1 addition & 0 deletions collect_app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ dependencies {
implementation project(':db')
implementation project(':open-rosa')
implementation project(':mobile-device-management')
implementation project(':timedgrid')

if (getSecrets().getProperty('MAPBOX_DOWNLOADS_TOKEN', '') != '') {
implementation project(':mapbox')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@
import org.odk.collect.androidshared.system.ProcessRestoreDetector;
import org.odk.collect.androidshared.ui.DialogFragmentUtils;
import org.odk.collect.androidshared.ui.FragmentFactoryBuilder;
import org.odk.collect.androidshared.ui.DialogUtils;
import org.odk.collect.androidshared.ui.SnackbarUtils;
import org.odk.collect.androidshared.ui.ToastUtils;
import org.odk.collect.async.Scheduler;
Expand All @@ -199,6 +200,7 @@
import org.odk.collect.settings.SettingsProvider;
import org.odk.collect.settings.keys.ProjectKeys;
import org.odk.collect.strings.localization.LocalizedActivity;
import org.odk.collect.timedgrid.NavigationWarning;

import java.io.File;
import java.util.HashMap;
Expand Down Expand Up @@ -388,7 +390,10 @@ public void allowSwiping(boolean doSwipe) {
private final OnBackPressedCallback onBackPressedCallback = new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
if (audioRecorder.isRecording() && !backgroundAudioViewModel.isBackgroundRecording()) {
NavigationWarning navigationWarning = odkView != null ? odkView.isNavigationBlocked() : null;
if (navigationWarning != null) {
DialogUtils.show(FormFillingActivity.this, navigationWarning.getTitleRes(), navigationWarning.getMessageRes());
} else if (audioRecorder.isRecording() && !backgroundAudioViewModel.isBackgroundRecording()) {
// We want the user to stop recording before changing screens
DialogFragmentUtils.showIfNotShowing(RecordingWarningDialogFragment.class, getSupportFragmentManager());
} else {
Expand Down Expand Up @@ -496,6 +501,15 @@ public void changeLanguage() {
public void save() {
saveForm(false, InstancesDaoHelper.isInstanceComplete(getFormController()), null, true);
}
},
() -> {
NavigationWarning navigationWarning = odkView != null ? odkView.isNavigationBlocked() : null;
if (navigationWarning != null) {
DialogUtils.show(this, navigationWarning.getTitleRes(), navigationWarning.getMessageRes());
swipeHandler.setBeenSwiped(false);
return false;
}
return true;
}
);

Expand Down Expand Up @@ -1203,6 +1217,13 @@ private void moveScreen(Direction direction) {
return;
}

NavigationWarning navigationWarning = odkView != null ? odkView.isNavigationBlocked() : null;
if (navigationWarning != null) {
DialogUtils.show(this, navigationWarning.getTitleRes(), navigationWarning.getMessageRes());
swipeHandler.setBeenSwiped(false);
return;
}

if (audioRecorder.isRecording() && !backgroundAudioViewModel.isBackgroundRecording()) {
// We want the user to stop recording before changing screens
DialogFragmentUtils.showIfNotShowing(RecordingWarningDialogFragment.class, getSupportFragmentManager());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ class FormEntryMenuProvider(
private val backgroundLocationViewModel: BackgroundLocationViewModel,
private val backgroundAudioViewModel: BackgroundAudioViewModel,
private val settingsProvider: SettingsProvider,
private val formEntryMenuClickListener: FormEntryMenuClickListener
private val formEntryMenuClickListener: FormEntryMenuClickListener,
private val beforeMenuItemClick: () -> Boolean
) : MenuProvider {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.form_menu, menu)
Expand Down Expand Up @@ -96,6 +97,10 @@ class FormEntryMenuProvider(
return true
}

if (!beforeMenuItemClick()) {
return true
}

return when (item.itemId) {
R.id.menu_add_repeat -> {
if (audioRecorder.isRecording() && !backgroundAudioViewModel.isBackgroundRecording) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@
import org.odk.collect.permissions.PermissionListener;
import org.odk.collect.permissions.PermissionsProvider;
import org.odk.collect.settings.SettingsProvider;
import org.odk.collect.timedgrid.NavigationAwareWidget;
import org.odk.collect.timedgrid.NavigationWarning;

import java.io.File;
import java.io.Serializable;
Expand All @@ -97,6 +99,7 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import javax.inject.Inject;
Expand Down Expand Up @@ -804,4 +807,15 @@ private void updateQuestions(FormEntryPrompt[] prompts) {
this.questions.add(new ImmutableDisplayableQuestion(questionAfterSave));
}
}

@Nullable
public NavigationWarning isNavigationBlocked() {
return widgets.stream()
.filter(widget -> widget instanceof NavigationAwareWidget)
.map(widget -> (NavigationAwareWidget) widget)
.map(NavigationAwareWidget::shouldBlockNavigation)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ object Appearances {
const val MASKED = "masked"
const val COUNTER = "counter"
const val MULTILINE = "multiline"
const val X_TIMED_GRID = "x-timed-grid"

// Get appearance hint and clean it up so it is lower case, without the search function and never null.
@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package org.odk.collect.android.widgets

import android.annotation.SuppressLint
import android.content.Context
import org.javarosa.core.model.FormIndex
import org.javarosa.core.model.IFormElement
import org.javarosa.core.model.SelectChoice
import org.javarosa.core.model.data.IAnswerData
import org.javarosa.form.api.FormEntryPrompt
import org.odk.collect.android.activities.FormFillingActivity
import org.odk.collect.android.formentry.FormEntryViewModel
import org.odk.collect.android.formentry.questions.QuestionDetails
import org.odk.collect.android.widgets.items.ItemsWidgetUtils
import org.odk.collect.timedgrid.FormAnswerRefresher
import org.odk.collect.timedgrid.FormControllerFacade
import org.odk.collect.timedgrid.NavigationAwareWidget
import org.odk.collect.timedgrid.NavigationWarning
import org.odk.collect.timedgrid.TimedGridWidgetDelegate

@SuppressLint("ViewConstructor")
class TimedGridWidget(
context: Context,
questionDetails: QuestionDetails,
dependencies: Dependencies,
formEntryViewModel: FormEntryViewModel
) : QuestionWidget(context, dependencies, questionDetails), NavigationAwareWidget {
private val widgetDelegate = TimedGridWidgetDelegate(
context,
questionDetails.prompt,
object : FormControllerFacade {
override fun getFormElements(): List<IFormElement>? {
return formEntryViewModel.formController.getFormDef()?.children
}

override fun getItems(): List<SelectChoice> {
return ItemsWidgetUtils.loadItemsAndHandleErrors(
this@TimedGridWidget, questionDetails.prompt, formEntryViewModel
)
}

override fun saveAnswer(index: FormIndex, answer: IAnswerData) {
formEntryViewModel.formController.saveOneScreenAnswer(index, answer, false)
}
},
object : FormAnswerRefresher {
override fun refreshAnswer(index: FormIndex) {
val activity = context as? FormFillingActivity ?: return
val odkView = activity.currentViewIfODKView ?: return

val widget = odkView.widgets
.filterIsInstance<StringWidget>()
.firstOrNull { it.formEntryPrompt.index == index }
?: return

widget.apply {
setDisplayValueFromModel()
widgetValueChanged()
showAnswerContainer()
}
}
}
) {
widgetValueChanged()
}

init {
render()
}

override fun onCreateWidgetView(context: Context, prompt: FormEntryPrompt, answerFontSize: Int) = widgetDelegate.onCreateWidgetView(this)

override fun getAnswer() = widgetDelegate.getAnswer()

override fun clearAnswer() {}

override fun setOnLongClickListener(l: OnLongClickListener?) {}

override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
widgetDelegate.onDetachedFromWindow()
}

override fun shouldBlockNavigation(): NavigationWarning? = widgetDelegate.shouldBlockNavigation()
}
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,8 @@ public QuestionWidget createWidgetFromPrompt(FormEntryPrompt prompt, Permissions
questionWidget = new LabelWidget(activity, questionDetails, formEntryViewModel, dependencies);
} else if (appearance.contains(Appearances.IMAGE_MAP)) {
questionWidget = new SelectMultiImageMapWidget(activity, questionDetails, formEntryViewModel, dependencies);
} else if (appearance.startsWith(Appearances.X_TIMED_GRID)) {
questionWidget = new TimedGridWidget(activity, questionDetails, dependencies, formEntryViewModel);
} else {
questionWidget = new SelectMultiWidget(activity, questionDetails, formEntryViewModel, dependencies);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class FormEntryMenuProviderTest {
backgroundAudioViewModel,
settingsProvider,
formEntryMenuClickListener
)
) { true }

@Test
fun onPrepare_inRepeatQuestion_showsAddRepeat() {
Expand Down
1 change: 1 addition & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ include ':web-page'
include ':db'
include ':open-rosa'
include ':mobile-device-management'
include ':timedgrid'

apply from: 'secrets.gradle'
if (getSecrets().getProperty('MAPBOX_DOWNLOADS_TOKEN', '') != '') {
Expand Down
1 change: 1 addition & 0 deletions timedgrid/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
59 changes: 59 additions & 0 deletions timedgrid/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
plugins {
alias(libs.plugins.androidLibrary)
alias(libs.plugins.kotlinAndroid)
}

apply(from = "../config/quality.gradle")

android {
namespace = "org.odk.collect.timedgrid"

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"))
}
}

compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}

testOptions {
unitTests {
isIncludeAndroidResources = true
}
}

buildFeatures {
viewBinding = true
buildConfig = true
}
}

dependencies {
coreLibraryDesugaring(libs.desugar)

implementation(project(":androidshared"))
implementation(project(":strings"))

implementation(libs.androidxFragmentKtx)
implementation(libs.androidMaterial)
implementation(libs.androidxAppcompat)
implementation(libs.androidFlexbox)
implementation(libs.androidxLifecycleViewmodelKtx)
implementation(libs.javarosa) {
exclude(group = "joda-time")
exclude(group = "org.hamcrest", module = "hamcrest-all")
}
}
4 changes: 4 additions & 0 deletions timedgrid/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.odk.collect.timedgrid

enum class AssessmentType(
private val rendererFactory: () -> TimedGridRenderer
) {
LETTERS(::CommonTimedGridRenderer),
WORDS(::CommonTimedGridRenderer),
NUMBERS(::CommonTimedGridRenderer),
ARITHMETIC(::CommonTimedGridRenderer),
READING({ CommonTimedGridRenderer(showRowNumbers = false) });

fun createRenderer(): TimedGridRenderer = rendererFactory()
}
Loading