Skip to content

Commit 662acfd

Browse files
authored
Fix app crash on app launch with location permissions request (#3478)
* Fix app crash on app launch with location permissions request Signed-off-by: Elly Kitoto <[email protected]> * Fix black screen Signed-off-by: Elly Kitoto <[email protected]> --------- Signed-off-by: Elly Kitoto <[email protected]>
1 parent 92b9fb6 commit 662acfd

File tree

10 files changed

+156
-234
lines changed

10 files changed

+156
-234
lines changed

android/engine/src/main/java/org/smartregister/fhircore/engine/util/location/LocationUtils.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ object LocationUtils {
3434

3535
fun isLocationEnabled(context: Context): Boolean {
3636
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
37-
3837
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) ||
3938
locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
4039
}

android/engine/src/main/java/org/smartregister/fhircore/engine/util/location/PermissionUtils.kt

Lines changed: 25 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,63 +18,48 @@ package org.smartregister.fhircore.engine.util.location
1818

1919
import android.Manifest
2020
import android.content.Context
21-
import android.content.Intent
2221
import android.content.pm.PackageManager
23-
import androidx.activity.result.ActivityResultLauncher
24-
import androidx.activity.result.contract.ActivityResultContracts
25-
import androidx.appcompat.app.AppCompatActivity
2622
import androidx.core.content.ContextCompat
2723

2824
object PermissionUtils {
2925

30-
fun checkPermissions(context: Context, permissions: List<String>): Boolean {
31-
for (permission in permissions) {
32-
if (
33-
ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED
34-
) {
35-
return false
36-
}
26+
fun checkPermissions(context: Context, permissions: List<String>): Boolean =
27+
permissions.none {
28+
ContextCompat.checkSelfPermission(
29+
context,
30+
it,
31+
) != PackageManager.PERMISSION_GRANTED
3732
}
38-
return true
39-
}
4033

4134
fun getLocationPermissionLauncher(
42-
activity: AppCompatActivity,
35+
permissions: Map<String, Boolean>,
4336
onFineLocationPermissionGranted: () -> Unit,
4437
onCoarseLocationPermissionGranted: () -> Unit,
4538
onLocationPermissionDenied: () -> Unit,
46-
): ActivityResultLauncher<Array<String>> {
47-
return activity.registerForActivityResult(
48-
ActivityResultContracts.RequestMultiplePermissions(),
49-
) { permissions ->
50-
if (permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true) {
39+
) {
40+
when {
41+
permissions[Manifest.permission.ACCESS_FINE_LOCATION] == true ->
5142
onFineLocationPermissionGranted()
52-
} else if (permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true) {
43+
permissions[Manifest.permission.ACCESS_COARSE_LOCATION] == true ->
5344
onCoarseLocationPermissionGranted()
54-
} else {
55-
onLocationPermissionDenied()
56-
}
57-
}
58-
}
59-
60-
fun getStartActivityForResultLauncher(
61-
activity: AppCompatActivity,
62-
onResult: (resultCode: Int, data: Intent?) -> Unit,
63-
): ActivityResultLauncher<Intent> {
64-
return activity.registerForActivityResult(
65-
ActivityResultContracts.StartActivityForResult(),
66-
) { result ->
67-
onResult(result.resultCode, result.data)
45+
else -> onLocationPermissionDenied()
6846
}
6947
}
7048

71-
fun hasFineLocationPermissions(context: Context): Boolean {
72-
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) ==
49+
fun hasFineLocationPermissions(context: Context): Boolean =
50+
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) ==
7351
PackageManager.PERMISSION_GRANTED
74-
}
7552

76-
fun hasCoarseLocationPermissions(context: Context): Boolean {
77-
return ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) ==
53+
fun hasCoarseLocationPermissions(context: Context): Boolean =
54+
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) ==
7855
PackageManager.PERMISSION_GRANTED
79-
}
56+
57+
fun hasLocationPermissions(context: Context) =
58+
checkPermissions(
59+
context,
60+
listOf(
61+
Manifest.permission.ACCESS_COARSE_LOCATION,
62+
Manifest.permission.ACCESS_FINE_LOCATION,
63+
),
64+
)
8065
}

android/engine/src/main/res/values/strings.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@
150150
<string name="weeks">Week(s)</string>
151151
<string name="days">Days(s)</string>
152152
<string name="initializing">Initializing settings &#8230;</string>
153+
<string name="initializing_application">Initializing application &#8230;</string>
153154
<string name="username_sample" translatable="false">e.g JohnDoe</string>
154155
<string name="percentage_progress" translatable="false">%1$d%%</string>
155156
<string name="error_occurred">Something went wrong…</string>

android/quest/src/main/java/org/smartregister/fhircore/quest/ui/main/AppMainActivity.kt

Lines changed: 62 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import androidx.activity.result.ActivityResultLauncher
2929
import androidx.activity.result.contract.ActivityResultContracts
3030
import androidx.activity.viewModels
3131
import androidx.compose.material.ExperimentalMaterialApi
32-
import androidx.compose.runtime.getValue
3332
import androidx.lifecycle.lifecycleScope
3433
import androidx.navigation.findNavController
3534
import androidx.navigation.fragment.NavHostFragment
@@ -40,6 +39,7 @@ import dagger.hilt.android.AndroidEntryPoint
4039
import io.sentry.android.navigation.SentryNavigationListener
4140
import java.time.Instant
4241
import javax.inject.Inject
42+
import kotlinx.coroutines.async
4343
import kotlinx.coroutines.launch
4444
import kotlinx.coroutines.withContext
4545
import org.hl7.fhir.r4.model.IdType
@@ -62,6 +62,8 @@ import org.smartregister.fhircore.quest.R
6262
import org.smartregister.fhircore.quest.event.AppEvent
6363
import org.smartregister.fhircore.quest.event.EventBus
6464
import org.smartregister.fhircore.quest.ui.questionnaire.QuestionnaireActivity
65+
import org.smartregister.fhircore.quest.ui.shared.ActivityOnResultType
66+
import org.smartregister.fhircore.quest.ui.shared.ON_RESULT_TYPE
6567
import org.smartregister.fhircore.quest.ui.shared.QuestionnaireHandler
6668
import org.smartregister.fhircore.quest.ui.shared.models.QuestionnaireSubmission
6769
import timber.log.Timber
@@ -81,13 +83,34 @@ open class AppMainActivity : BaseMultiLanguageActivity(), QuestionnaireHandler,
8183
val appMainViewModel by viewModels<AppMainViewModel>()
8284
private val sentryNavListener =
8385
SentryNavigationListener(enableNavigationBreadcrumbs = true, enableNavigationTracing = true)
84-
private lateinit var locationPermissionLauncher: ActivityResultLauncher<Array<String>>
85-
private lateinit var activityResultLauncher: ActivityResultLauncher<Intent>
86+
87+
private val locationPermissionLauncher: ActivityResultLauncher<Array<String>> =
88+
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
89+
permissions: Map<String, Boolean> ->
90+
PermissionUtils.getLocationPermissionLauncher(
91+
permissions = permissions,
92+
onFineLocationPermissionGranted = { fetchLocation() },
93+
onCoarseLocationPermissionGranted = { fetchLocation() },
94+
onLocationPermissionDenied = {
95+
showToast(
96+
getString(R.string.location_permissions_denied),
97+
Toast.LENGTH_SHORT,
98+
)
99+
},
100+
)
101+
}
102+
86103
private lateinit var fusedLocationClient: FusedLocationProviderClient
87104

88105
override val startForResult =
89-
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult ->
90-
if (activityResult.resultCode == Activity.RESULT_OK) {
106+
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
107+
activityResult: ActivityResult ->
108+
val onResultType = activityResult.data?.extras?.getString(ON_RESULT_TYPE)
109+
if (
110+
activityResult.resultCode == Activity.RESULT_OK &&
111+
!onResultType.isNullOrBlank() &&
112+
ActivityOnResultType.valueOf(onResultType) == ActivityOnResultType.QUESTIONNAIRE
113+
) {
91114
lifecycleScope.launch { onSubmitQuestionnaire(activityResult) }
92115
}
93116
}
@@ -127,6 +150,7 @@ open class AppMainActivity : BaseMultiLanguageActivity(), QuestionnaireHandler,
127150
retrieveAppMainUiState()
128151
withContext(dispatcherProvider.io()) { schedulePeriodicJobs(this@AppMainActivity) }
129152
}
153+
130154
setupLocationServices()
131155

132156
findViewById<View>(R.id.mainScreenProgressBar).apply { visibility = View.GONE }
@@ -188,92 +212,61 @@ open class AppMainActivity : BaseMultiLanguageActivity(), QuestionnaireHandler,
188212
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
189213

190214
if (!LocationUtils.isLocationEnabled(this)) {
191-
openLocationServicesSettings()
215+
showLocationSettingsDialog(
216+
Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS).apply {
217+
putExtra(ON_RESULT_TYPE, ActivityOnResultType.LOCATION.name)
218+
},
219+
)
192220
}
193221

194-
if (!hasLocationPermissions()) {
195-
launchLocationPermissionsDialog()
222+
if (!PermissionUtils.hasLocationPermissions(this)) {
223+
locationPermissionLauncher.launch(
224+
arrayOf(
225+
Manifest.permission.ACCESS_FINE_LOCATION,
226+
Manifest.permission.ACCESS_COARSE_LOCATION,
227+
),
228+
)
196229
}
197230

198-
if (LocationUtils.isLocationEnabled(this) && hasLocationPermissions()) {
231+
if (LocationUtils.isLocationEnabled(this) && PermissionUtils.hasLocationPermissions(this)) {
199232
fetchLocation()
200233
}
201234
}
202235
}
203236

204-
fun hasLocationPermissions(): Boolean {
205-
return PermissionUtils.checkPermissions(
206-
this,
207-
listOf(
208-
Manifest.permission.ACCESS_COARSE_LOCATION,
209-
Manifest.permission.ACCESS_FINE_LOCATION,
210-
),
211-
)
212-
}
213-
214-
private fun openLocationServicesSettings() {
215-
activityResultLauncher =
216-
PermissionUtils.getStartActivityForResultLauncher(this) { resultCode, _ ->
217-
if (resultCode == RESULT_OK || hasLocationPermissions()) {
218-
Timber.d("Location or permissions successfully enabled")
219-
}
220-
}
221-
222-
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
223-
showLocationSettingsDialog(intent)
224-
}
225-
226237
private fun showLocationSettingsDialog(intent: Intent) {
227238
AlertDialog.Builder(this)
228239
.setMessage(getString(R.string.location_services_disabled))
229240
.setCancelable(true)
230-
.setPositiveButton(getString(R.string.yes)) { _, _ -> activityResultLauncher.launch(intent) }
241+
.setPositiveButton(getString(R.string.yes)) { _, _ -> startForResult.launch(intent) }
231242
.setNegativeButton(getString(R.string.no)) { dialog, _ -> dialog.cancel() }
232243
.show()
233244
}
234245

235-
fun launchLocationPermissionsDialog() {
236-
locationPermissionLauncher =
237-
PermissionUtils.getLocationPermissionLauncher(
238-
this,
239-
onFineLocationPermissionGranted = { fetchLocation() },
240-
onCoarseLocationPermissionGranted = { fetchLocation() },
241-
onLocationPermissionDenied = {
242-
Toast.makeText(
243-
this,
244-
getString(R.string.location_permissions_denied),
245-
Toast.LENGTH_SHORT,
246-
)
247-
.show()
248-
},
249-
)
250-
251-
locationPermissionLauncher.launch(
252-
arrayOf(
253-
Manifest.permission.ACCESS_FINE_LOCATION,
254-
Manifest.permission.ACCESS_COARSE_LOCATION,
255-
),
256-
)
257-
}
258-
259246
private fun fetchLocation() {
260247
val context = this
261248
lifecycleScope.launch {
262249
val retrievedLocation =
263-
if (PermissionUtils.hasFineLocationPermissions(context)) {
264-
LocationUtils.getAccurateLocation(fusedLocationClient)
265-
} else if (PermissionUtils.hasCoarseLocationPermissions(context)) {
266-
LocationUtils.getApproximateLocation(fusedLocationClient)
267-
} else {
268-
null
269-
}
270-
retrievedLocation?.let {
271-
protoDataStore.writeLocationCoordinates(
272-
LocationCoordinate(it.latitude, it.longitude, it.altitude, Instant.now()),
273-
)
274-
}
250+
async(dispatcherProvider.io()) {
251+
when {
252+
PermissionUtils.hasFineLocationPermissions(context) ->
253+
LocationUtils.getAccurateLocation(fusedLocationClient)
254+
PermissionUtils.hasCoarseLocationPermissions(context) ->
255+
LocationUtils.getApproximateLocation(fusedLocationClient)
256+
else -> null
257+
}
258+
}
259+
.await()
260+
?.also {
261+
protoDataStore.writeLocationCoordinates(
262+
LocationCoordinate(it.latitude, it.longitude, it.altitude, Instant.now()),
263+
)
264+
}
265+
275266
if (retrievedLocation == null) {
276-
this@AppMainActivity.showToast("Failed to get GPS location", Toast.LENGTH_LONG)
267+
withContext(dispatcherProvider.main()) {
268+
showToast(getString(R.string.failed_to_get_gps_location), Toast.LENGTH_LONG)
269+
}
277270
}
278271
}
279272
}

0 commit comments

Comments
 (0)