fix(android): handle location permission races and optional camera
This commit is contained in:
@@ -1,75 +0,0 @@
|
|||||||
name: F6.1 apply Android lint fix
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'release/f6-1-presentation-ready'
|
|
||||||
paths:
|
|
||||||
- '.github/workflows/f6-1-apply-android-lint-fix.yml'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
apply:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
ref: release/f6-1-presentation-ready
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Apply targeted runtime fixes
|
|
||||||
run: |
|
|
||||||
python - <<'PY'
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
replacements = {
|
|
||||||
'android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/DhApp.kt': (
|
|
||||||
''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n else continuation.resume(GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n continuation.invokeOnCancellation { source.cancel() }''',
|
|
||||||
''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n try {\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n else continuation.resume(GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n } catch (error: SecurityException) {\n if (continuation.isActive) continuation.resumeWithException(error)\n }\n continuation.invokeOnCancellation { source.cancel() }''',
|
|
||||||
),
|
|
||||||
'android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/F3VisitRoot.kt': (
|
|
||||||
''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) {\n continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n } else {\n continuation.resume(F3GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n continuation.invokeOnCancellation { source.cancel() }''',
|
|
||||||
''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n try {\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) {\n continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n } else {\n continuation.resume(F3GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n } catch (error: SecurityException) {\n if (continuation.isActive) continuation.resumeWithException(error)\n }\n continuation.invokeOnCancellation { source.cancel() }''',
|
|
||||||
),
|
|
||||||
'android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/FieldFindingScreen.kt': (
|
|
||||||
''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n else continuation.resume(FindingGeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n continuation.invokeOnCancellation { source.cancel() }''',
|
|
||||||
''' val source = CancellationTokenSource()\n val client = LocationServices.getFusedLocationProviderClient(context)\n try {\n client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))\n else continuation.resume(FindingGeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n } catch (error: SecurityException) {\n if (continuation.isActive) continuation.resumeWithException(error)\n }\n continuation.invokeOnCancellation { source.cancel() }''',
|
|
||||||
),
|
|
||||||
'android-app/app/src/main/java/com/korexlabs/dhinspeccion/ui/MobileActsScreen.kt': (
|
|
||||||
''' val source = CancellationTokenSource()\n LocationServices.getFusedLocationProviderClient(context)\n .getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("Ubicación no disponible"))\n else continuation.resume(ActSignatureGeo(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n continuation.invokeOnCancellation { source.cancel() }''',
|
|
||||||
''' val source = CancellationTokenSource()\n try {\n LocationServices.getFusedLocationProviderClient(context)\n .getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)\n .addOnSuccessListener { location ->\n if (!continuation.isActive) return@addOnSuccessListener\n if (location == null) continuation.resumeWithException(IllegalStateException("Ubicación no disponible"))\n else continuation.resume(ActSignatureGeo(location.latitude, location.longitude, location.accuracy.toDouble()))\n }\n .addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }\n } catch (error: SecurityException) {\n if (continuation.isActive) continuation.resumeWithException(error)\n }\n continuation.invokeOnCancellation { source.cancel() }''',
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
for file_name, (before, after) in replacements.items():
|
|
||||||
path = Path(file_name)
|
|
||||||
text = path.read_text()
|
|
||||||
count = text.count(before)
|
|
||||||
if count != 1:
|
|
||||||
raise SystemExit(f'{file_name}: expected exactly one GPS block, found {count}')
|
|
||||||
path.write_text(text.replace(before, after))
|
|
||||||
|
|
||||||
manifest = Path('android-app/app/src/main/AndroidManifest.xml')
|
|
||||||
text = manifest.read_text()
|
|
||||||
before = ' <uses-permission android:name="android.permission.CAMERA" />\n'
|
|
||||||
after = before + ' <uses-feature android:name="android.hardware.camera" android:required="false" />\n'
|
|
||||||
if text.count(before) != 1:
|
|
||||||
raise SystemExit('AndroidManifest.xml: CAMERA permission block not unique')
|
|
||||||
if 'android.hardware.camera' not in text:
|
|
||||||
manifest.write_text(text.replace(before, after))
|
|
||||||
|
|
||||||
# Self-delete so this one-shot workflow never ships in the release branch.
|
|
||||||
Path('.github/workflows/f6-1-apply-android-lint-fix.yml').unlink()
|
|
||||||
PY
|
|
||||||
|
|
||||||
- name: Commit targeted fixes
|
|
||||||
run: |
|
|
||||||
set -Eeuo pipefail
|
|
||||||
git config user.name 'github-actions[bot]'
|
|
||||||
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
|
||||||
git add android-app .github/workflows/f6-1-apply-android-lint-fix.yml
|
|
||||||
git diff --cached --check
|
|
||||||
git commit -m 'fix(android): handle location permission races and optional camera'
|
|
||||||
git push origin HEAD:release/f6-1-presentation-ready
|
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.CAMERA" />
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<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_COARSE_LOCATION" />
|
||||||
|
|
||||||
|
|||||||
@@ -547,13 +547,17 @@ private suspend fun currentGeo(context: Context): GeoSnapshot = suspendCancellab
|
|||||||
}
|
}
|
||||||
val source = CancellationTokenSource()
|
val source = CancellationTokenSource()
|
||||||
val client = LocationServices.getFusedLocationProviderClient(context)
|
val client = LocationServices.getFusedLocationProviderClient(context)
|
||||||
client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)
|
try {
|
||||||
.addOnSuccessListener { location ->
|
client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)
|
||||||
if (!continuation.isActive) return@addOnSuccessListener
|
.addOnSuccessListener { location ->
|
||||||
if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))
|
if (!continuation.isActive) return@addOnSuccessListener
|
||||||
else continuation.resume(GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))
|
if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))
|
||||||
}
|
else continuation.resume(GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))
|
||||||
.addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }
|
}
|
||||||
|
.addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }
|
||||||
|
} catch (error: SecurityException) {
|
||||||
|
if (continuation.isActive) continuation.resumeWithException(error)
|
||||||
|
}
|
||||||
continuation.invokeOnCancellation { source.cancel() }
|
continuation.invokeOnCancellation { source.cancel() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -727,16 +727,20 @@ private suspend fun currentF3Geo(context: Context): F3GeoSnapshot = suspendCance
|
|||||||
}
|
}
|
||||||
val source = CancellationTokenSource()
|
val source = CancellationTokenSource()
|
||||||
val client = LocationServices.getFusedLocationProviderClient(context)
|
val client = LocationServices.getFusedLocationProviderClient(context)
|
||||||
client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)
|
try {
|
||||||
.addOnSuccessListener { location ->
|
client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)
|
||||||
if (!continuation.isActive) return@addOnSuccessListener
|
.addOnSuccessListener { location ->
|
||||||
if (location == null) {
|
if (!continuation.isActive) return@addOnSuccessListener
|
||||||
continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))
|
if (location == null) {
|
||||||
} else {
|
continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))
|
||||||
continuation.resume(F3GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))
|
} else {
|
||||||
|
continuation.resume(F3GeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
.addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }
|
||||||
.addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }
|
} catch (error: SecurityException) {
|
||||||
|
if (continuation.isActive) continuation.resumeWithException(error)
|
||||||
|
}
|
||||||
continuation.invokeOnCancellation { source.cancel() }
|
continuation.invokeOnCancellation { source.cancel() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -389,13 +389,17 @@ private suspend fun currentFindingGeo(context: Context): FindingGeoSnapshot = su
|
|||||||
}
|
}
|
||||||
val source = CancellationTokenSource()
|
val source = CancellationTokenSource()
|
||||||
val client = LocationServices.getFusedLocationProviderClient(context)
|
val client = LocationServices.getFusedLocationProviderClient(context)
|
||||||
client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)
|
try {
|
||||||
.addOnSuccessListener { location ->
|
client.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)
|
||||||
if (!continuation.isActive) return@addOnSuccessListener
|
.addOnSuccessListener { location ->
|
||||||
if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))
|
if (!continuation.isActive) return@addOnSuccessListener
|
||||||
else continuation.resume(FindingGeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))
|
if (location == null) continuation.resumeWithException(IllegalStateException("No se pudo obtener una ubicación GPS actual."))
|
||||||
}
|
else continuation.resume(FindingGeoSnapshot(location.latitude, location.longitude, location.accuracy.toDouble()))
|
||||||
.addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }
|
}
|
||||||
|
.addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }
|
||||||
|
} catch (error: SecurityException) {
|
||||||
|
if (continuation.isActive) continuation.resumeWithException(error)
|
||||||
|
}
|
||||||
continuation.invokeOnCancellation { source.cancel() }
|
continuation.invokeOnCancellation { source.cancel() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -431,13 +431,17 @@ private suspend fun currentActSignatureGeo(context: Context): ActSignatureGeo =
|
|||||||
return@suspendCancellableCoroutine
|
return@suspendCancellableCoroutine
|
||||||
}
|
}
|
||||||
val source = CancellationTokenSource()
|
val source = CancellationTokenSource()
|
||||||
LocationServices.getFusedLocationProviderClient(context)
|
try {
|
||||||
.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)
|
LocationServices.getFusedLocationProviderClient(context)
|
||||||
.addOnSuccessListener { location ->
|
.getCurrentLocation(Priority.PRIORITY_HIGH_ACCURACY, source.token)
|
||||||
if (!continuation.isActive) return@addOnSuccessListener
|
.addOnSuccessListener { location ->
|
||||||
if (location == null) continuation.resumeWithException(IllegalStateException("Ubicación no disponible"))
|
if (!continuation.isActive) return@addOnSuccessListener
|
||||||
else continuation.resume(ActSignatureGeo(location.latitude, location.longitude, location.accuracy.toDouble()))
|
if (location == null) continuation.resumeWithException(IllegalStateException("Ubicación no disponible"))
|
||||||
}
|
else continuation.resume(ActSignatureGeo(location.latitude, location.longitude, location.accuracy.toDouble()))
|
||||||
.addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }
|
}
|
||||||
|
.addOnFailureListener { if (continuation.isActive) continuation.resumeWithException(it) }
|
||||||
|
} catch (error: SecurityException) {
|
||||||
|
if (continuation.isActive) continuation.resumeWithException(error)
|
||||||
|
}
|
||||||
continuation.invokeOnCancellation { source.cancel() }
|
continuation.invokeOnCancellation { source.cancel() }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user