Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Location co-ordinates on questionnaire submission #2997

Merged
merged 77 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
563fca0
Add location permissions to get location details on submission
DebbieArita Jan 18, 2024
6c52152
Edit current location
DebbieArita Jan 18, 2024
dcfbe3c
Append precise and approximate location to the questionnaireResponse
DebbieArita Jan 22, 2024
6a9307e
Merge branch 'main' into questionnaire-location
DebbieArita Jan 22, 2024
093138c
Add application config checker
DebbieArita Jan 23, 2024
cf61cc8
Add logQuestionnaireLocation config to toggle functionality
DebbieArita Jan 23, 2024
ca395b2
Merge branch 'main' into questionnaire-location
DebbieArita Jan 23, 2024
eabd1be
Run SpotlessApply
DebbieArita Jan 23, 2024
13c170d
Merge branch 'main' into questionnaire-location
DebbieArita Feb 1, 2024
2427914
Run location calculation in background and add user checks
DebbieArita Feb 1, 2024
d2dd2f0
Update build gradle
DebbieArita Feb 2, 2024
e7babb2
Code refactors
qiarie Feb 6, 2024
57ee61e
Code cleanup
qiarie Feb 6, 2024
6d0a310
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Feb 6, 2024
16285f7
Refactor location implementation
qiarie Feb 6, 2024
ba0fc6f
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Feb 6, 2024
8d8df47
Rename location setup
qiarie Feb 6, 2024
66ec65f
Fix crash on opening location service settings after permissions
qiarie Feb 6, 2024
a910980
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Feb 6, 2024
06c6e7c
Merge branch 'main' into questionnaire-location
brandyodhiambo Feb 8, 2024
52bea17
Resolve merge conflict
brandyodhiambo Feb 8, 2024
c59f335
Add tests
DebbieArita Feb 9, 2024
8431a58
Allow location picking when location service & permissions are alread…
qiarie Feb 9, 2024
4c39e81
add service location test on QuestionnaireActivityTest
brandyodhiambo Feb 13, 2024
ae10339
Merge branch 'main' into questionnaire-location
brandyodhiambo Feb 13, 2024
7cbbac0
Merge remote-tracking branch 'origin/questionnaire-location' into que…
brandyodhiambo Feb 13, 2024
642542d
Merge branch 'main' into questionnaire-location
DebbieArita Feb 19, 2024
a686ce0
Add tests and Run SpotlessApply
DebbieArita Feb 19, 2024
1a8e82c
Merge remote-tracking branch 'origin/questionnaire-location' into que…
DebbieArita Feb 19, 2024
94a01e0
Merge branch 'main' into questionnaire-location
DebbieArita Feb 19, 2024
150c64f
Empty commit
DebbieArita Feb 19, 2024
3b46ada
Update RegisterCardList tests
DebbieArita Feb 20, 2024
7493e33
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Feb 21, 2024
2f47f93
Resolve merge conflicts and WIP questionnaire improvements
DebbieArita Feb 22, 2024
1a9ed26
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Feb 23, 2024
a06ae76
Log GPS location fetch failure
qiarie Feb 23, 2024
4e12927
Update QuestionnaireActivityTests
DebbieArita Feb 23, 2024
baae9b3
Merge branch 'main' into questionnaire-location
DebbieArita Feb 23, 2024
5e4dee6
Merge branch 'main' into questionnaire-location
DebbieArita Feb 23, 2024
2423fc5
Merge remote-tracking branch 'origin/questionnaire-location' into que…
DebbieArita Feb 23, 2024
516f484
Remove comments
DebbieArita Feb 23, 2024
8f63da1
Merge branch 'main' into questionnaire-location
Lentumunai-Mark Feb 26, 2024
554eb83
Update tests WIP
DebbieArita Feb 26, 2024
fe035a6
Merge branch 'main' into questionnaire-location
dubdabasoduba Feb 26, 2024
066d1b9
Merge branch 'main' into questionnaire-location
DebbieArita Feb 27, 2024
a107cc5
Merge branch 'main' into questionnaire-location
DebbieArita Feb 28, 2024
1797620
Update LocationUtils tests
DebbieArita Feb 28, 2024
d30b3bf
Run spotlessApply
DebbieArita Feb 28, 2024
5d2d057
Refactor PermissionsUtilsTest
DebbieArita Feb 28, 2024
4b054df
Refactor ResourceUtilsTest
DebbieArita Feb 28, 2024
56c26b3
Run SpotlessApply
DebbieArita Feb 28, 2024
14e18fe
Merge branch 'main' into questionnaire-location
pld Mar 4, 2024
1210019
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Mar 5, 2024
5623799
Update ApplicationConfiguration docs
qiarie Mar 5, 2024
da25d8d
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Mar 7, 2024
e3719a6
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Mar 7, 2024
b148c88
Merge branch 'main' into questionnaire-location
DebbieArita Mar 7, 2024
ed31f2d
Merge branch 'main' into questionnaire-location
dubdabasoduba Mar 8, 2024
e2857a2
Make logGpsLocations config a list in ApplicationConfiguration
qiarie Mar 11, 2024
cc8727a
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Mar 11, 2024
02e818b
Merge branch 'questionnaire-location' of github.com:opensrp/fhircore …
qiarie Mar 11, 2024
7f8a65c
Update tests
qiarie Mar 11, 2024
aebe8cf
Update test configs
qiarie Mar 11, 2024
76908ef
Merge branch 'main' into questionnaire-location
DebbieArita Mar 12, 2024
ebdd7bb
Update docs on logGpsLocation config option
qiarie Mar 12, 2024
e5577fe
Merge branch 'questionnaire-location' of github.com:opensrp/fhircore …
qiarie Mar 12, 2024
a6587f8
Merge remote-tracking branch 'origin/main' into questionnaire-location
qiarie Mar 12, 2024
1b4aaa4
Merge branch 'main' into questionnaire-location
dubdabasoduba Mar 12, 2024
f5a9f1a
Update android/quest/src/main/java/org/smartregister/fhircore/quest/u…
qiarie Mar 13, 2024
8568ea7
Merge branch 'main' into questionnaire-location
DebbieArita Mar 13, 2024
b4a0513
Run SpotlessApply
DebbieArita Mar 13, 2024
9cc8d0c
Update fhir Location from GPS and remove null safety
DebbieArita Mar 14, 2024
e5d3a9f
Run SpotlessApply
DebbieArita Mar 14, 2024
eeee521
Update test
DebbieArita Mar 14, 2024
6b009f2
Merge branch 'main' into questionnaire-location
dubdabasoduba Mar 15, 2024
34fffda
Update application config docs
DebbieArita Mar 15, 2024
ac82739
Update location permission denied string
DebbieArita Mar 15, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,5 @@ data class ApplicationConfiguration(
val showLogo: Boolean = true,
val taskBackgroundWorkerBatchSize: Int = 500,
val eventWorkflows: List<EventWorkflow> = emptyList(),
val logQuestionnaireLocation: Boolean = false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we know that we will (at some point) want to log location at multiple points, can we change this data structure to reflect that?

Suggested change
val logQuestionnaireLocation: Boolean = false,
val logLocationInClass: List<Class<T>> = emptyList(),

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pld Should we move the setting to QuestionnaireConfig?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is appropriate here, because in the future we plan to do location logging outside of the Questionnaire flow

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pld Did you have another use case in mind?

In cases where we would need to log the location in multiple places, I'd go with a location config with all options explicitly listed as below:

@Serializable
data class LocationConfig(
  val logQuestionnaire: Boolean = false,
  val logOtherPlace: Boolean = false,
) : Configuration()

Usage:

val logLocation: LocationConfig = LocationConfig(logQuestionnaire = true),

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pld Using reflection will not be possible in the application configuration. Kotlin Serialization will be unable to deserialize the type. We could opt for a list of enums instead. Every place that needs location logging will be identified by an enum type.

Example

enum class LocationLogArea { 
   QUESTIONNAIRE, 
   ANOTHER_PLACE,
   AND_ANOTHER_PLACE
}

ApplicationConfiguration

{
  "locationLogAreas" : [
     "QUESTIONNAIRE"
   ]
}

Suggest an appropriate name for the property/class.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK yea if we can't use class names, and maybe that won't map to the semantic anyhow, that's fine, enum seems straightforward.

The other use case I had in mind is that we might want to log the location every 5 minutes. Or if you could complete a task by clicking a button in the profile, we might want to log the location on click

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pld This is noted. We had discussed with @marklosh the need to have a periodic logger for their distance from current location use case since picking the location takes time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have created this issue for the addition of enums to configure location logging

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok let's make the change in this PR before we merge it

) : Configuration()
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Ona Systems, Inc
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Ona Systems, Inc
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Ona Systems, Inc
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Ona Systems, Inc
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Ona Systems, Inc
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Ona Systems, Inc
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Ona Systems, Inc
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2023 Ona Systems, Inc
* Copyright 2021-2024 Ona Systems, Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down
2 changes: 2 additions & 0 deletions android/quest/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ dependencies {
implementation(libs.dagger.hilt.android)
implementation(libs.hilt.work)
implementation(libs.cql.measure.evaluator)
// implementation("com.google.android.gms:play-services-location:{version}")
DebbieArita marked this conversation as resolved.
Show resolved Hide resolved
implementation("com.google.android.gms:play-services-location:21.0.1")
DebbieArita marked this conversation as resolved.
Show resolved Hide resolved

// Annotation processors
kapt(libs.hilt.compiler)
Expand Down
5 changes: 5 additions & 0 deletions android/quest/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
xmlns:tools="http://schemas.android.com/tools"
package="org.smartregister.fhircore.quest">

<!-- <uses-permission android:name="android.permission.INTERNET"></uses-permission>-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />

<application
android:name=".QuestApplication"
android:allowBackup="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,33 @@

package org.smartregister.fhircore.quest.ui.questionnaire

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Location
import android.os.Bundle
import android.os.Parcelable
import android.provider.Settings
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.core.app.ActivityCompat
import androidx.core.os.bundleOf
import androidx.fragment.app.commit
import androidx.lifecycle.lifecycleScope
import com.google.android.fhir.datacapture.QuestionnaireFragment
import com.google.android.fhir.logicalId
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.location.Priority
import com.google.android.gms.tasks.CancellationToken
import com.google.android.gms.tasks.CancellationTokenSource
import com.google.android.gms.tasks.OnTokenCanceledListener
import dagger.hilt.android.AndroidEntryPoint
import java.io.Serializable
import java.util.LinkedList
Expand Down Expand Up @@ -62,8 +76,19 @@ class QuestionnaireActivity : BaseMultiLanguageActivity() {
private var questionnaire: Questionnaire? = null
private var alertDialog: AlertDialog? = null

private lateinit var fusedLocationClient: FusedLocationProviderClient
private val loc = org.hl7.fhir.r4.model.Location()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val applicationConfiguration = viewModel.applicationConfiguration

if (applicationConfiguration.logQuestionnaireLocation) {
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
getLocation()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this running in the foreground or background? If there is not a GPS fix, what happens?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pld There are some updates to the flow.

The GPS location request is made in the background when a questionnaire is launched. This is done after config, permissions and location services checks are complete. On submit, the GPS location - if available - is used to create the location resource.

}

setTheme(R.style.AppTheme_Questionnaire)
viewBinding = QuestionnaireActivityBinding.inflate(layoutInflater)
setContentView(viewBinding.root)
Expand Down Expand Up @@ -231,6 +256,9 @@ class QuestionnaireActivity : BaseMultiLanguageActivity() {
if (questionnaireResponse != null && questionnaire != null) {
viewModel.run {
setProgressState(QuestionnaireProgressState.ExtractionInProgress(true))

questionnaireResponse.contained.add(loc)

handleQuestionnaireSubmission(
questionnaire = questionnaire!!,
currentQuestionnaireResponse = questionnaireResponse,
Expand All @@ -251,11 +279,112 @@ class QuestionnaireActivity : BaseMultiLanguageActivity() {
)
finish()
}
// do background location processing
// check here that "logQuestionnaireLocation": true
DebbieArita marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}

private fun getLocation() {
DebbieArita marked this conversation as resolved.
Show resolved Hide resolved
if (
ActivityCompat.checkSelfPermission(
this@QuestionnaireActivity,
Manifest.permission.ACCESS_FINE_LOCATION,
) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(
this@QuestionnaireActivity,
Manifest.permission.ACCESS_COARSE_LOCATION,
) != PackageManager.PERMISSION_GRANTED
) {
val locationPermissionRequest =
[email protected](
ActivityResultContracts.RequestMultiplePermissions(),
) { permissions ->
when {
permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) -> {
getCurrentLocation()
}
permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) -> {
// request approx permissions
getCurrentApproximateLocation()
}
else -> {
// No location access granted. Notify and fail
Toast.makeText(
this,
"Location Access Permission not granted",
Toast.LENGTH_LONG,
)
.show()
}
}
}
locationPermissionRequest.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
),
)
} else {
getCurrentLocation()
}
}

@SuppressLint("MissingPermission")
private fun getCurrentLocation() {
fusedLocationClient
.getCurrentLocation(
Priority.PRIORITY_HIGH_ACCURACY,
object : CancellationToken() {
override fun onCanceledRequested(p0: OnTokenCanceledListener) =
CancellationTokenSource().token

override fun isCancellationRequested() = false
},
)
.addOnSuccessListener { location: Location? ->
if (location != null) {
Timber.d("current location${location.latitude} ${location.longitude}")
loc.position.altitude = location.altitude.toBigDecimal()
loc.position.latitude = location.latitude.toBigDecimal()
loc.position.longitude = location.longitude.toBigDecimal()
}
}
.addOnFailureListener {
// Location services are not enabled, prompt the user to enable them
val settingsIntent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
this.startActivity(settingsIntent)
}
}

@SuppressLint("MissingPermission")
private fun getCurrentApproximateLocation() {
fusedLocationClient
.getCurrentLocation(
Priority.PRIORITY_BALANCED_POWER_ACCURACY,
object : CancellationToken() {
override fun onCanceledRequested(p0: OnTokenCanceledListener) =
CancellationTokenSource().token

override fun isCancellationRequested() = false
},
)
.addOnSuccessListener { approxLocation: Location? ->
if (approxLocation != null) {
Timber.d("current location${approxLocation.latitude} ${approxLocation.longitude}")
loc.position.altitude = approxLocation.altitude.toBigDecimal()
loc.position.latitude = approxLocation.latitude.toBigDecimal()
loc.position.longitude = approxLocation.longitude.toBigDecimal()
}
}
.addOnFailureListener {
// Location services are not enabled, prompt the user to enable them
val settingsIntent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
this.startActivity(settingsIntent)
}
}

private fun handleBackPress() {
if (questionnaireConfig.isReadOnly()) {
finish()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,11 @@ import org.hl7.fhir.r4.model.RelatedPerson
import org.hl7.fhir.r4.model.Resource
import org.hl7.fhir.r4.model.ResourceType
import org.hl7.fhir.r4.model.StringType
import org.smartregister.fhircore.engine.configuration.ConfigType
import org.smartregister.fhircore.engine.configuration.ConfigurationRegistry
import org.smartregister.fhircore.engine.configuration.GroupResourceConfig
import org.smartregister.fhircore.engine.configuration.QuestionnaireConfig
import org.smartregister.fhircore.engine.configuration.app.ApplicationConfiguration
import org.smartregister.fhircore.engine.data.local.DefaultRepository
import org.smartregister.fhircore.engine.domain.model.ActionParameter
import org.smartregister.fhircore.engine.domain.model.ActionParameterType
Expand Down Expand Up @@ -95,6 +98,7 @@ constructor(
val sharedPreferencesHelper: SharedPreferencesHelper,
val fhirOperator: FhirOperator,
val fhirPathDataExtractor: FhirPathDataExtractor,
val configurationRegistry: ConfigurationRegistry,
) : ViewModel() {

private val authenticatedOrganizationIds by lazy {
Expand All @@ -111,6 +115,10 @@ constructor(
val questionnaireProgressStateLiveData: LiveData<QuestionnaireProgressState?>
get() = _questionnaireProgressStateLiveData

val applicationConfiguration: ApplicationConfiguration by lazy {
configurationRegistry.retrieveConfiguration(ConfigType.Application)
}

/**
* This function retrieves the [Questionnaire] as configured via the [QuestionnaireConfig]. The
* retrieved [Questionnaire] can be pre-populated with computed values from the Rules engine.
Expand Down
Loading