@capgo/camera-preview 8.11.5 → 8.11.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,6 +40,9 @@ android {
40
40
  lintOptions {
41
41
  abortOnError = false
42
42
  }
43
+ testOptions {
44
+ unitTests.includeAndroidResources = true
45
+ }
43
46
  }
44
47
 
45
48
  repositories {
@@ -65,10 +68,12 @@ dependencies {
65
68
  implementation "androidx.camera:camera-lifecycle:${camerax_version}"
66
69
  implementation "androidx.camera:camera-view:${camerax_version}"
67
70
  implementation "androidx.camera:camera-extensions:${camerax_version}"
68
- implementation 'com.google.guava:guava:33.6.0-android'
71
+ implementation 'com.google.guava:guava:33.7.2-android'
69
72
  implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1'
70
73
 
71
74
  testImplementation "junit:junit:$junitVersion"
75
+ testImplementation 'org.robolectric:robolectric:4.14.1'
76
+ testImplementation 'androidx.test:core:1.7.0'
72
77
  androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
73
78
  androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
74
79
  }
@@ -29,7 +29,6 @@ import android.view.ViewGroup;
29
29
  import android.webkit.WebView;
30
30
  import androidx.annotation.RequiresPermission;
31
31
  import androidx.appcompat.app.AlertDialog;
32
- import androidx.core.app.ActivityCompat;
33
32
  import androidx.core.graphics.Insets;
34
33
  import androidx.core.view.ViewCompat;
35
34
  import androidx.core.view.WindowInsetsCompat;
@@ -456,10 +455,13 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
456
455
  final boolean withExifLocation = Boolean.TRUE.equals(call.getBoolean("withExifLocation", false));
457
456
 
458
457
  if (withExifLocation) {
459
- if (getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) != PermissionState.GRANTED) {
458
+ if (LocationPermissionHelper.canCaptureWithExifLocation(getContext())) {
459
+ getLocationAndCapture(call);
460
+ } else if (getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) != PermissionState.GRANTED) {
460
461
  requestPermissionForAlias(CAMERA_WITH_LOCATION_PERMISSION_ALIAS, call, "captureWithLocationPermission");
461
462
  } else {
462
- getLocationAndCapture(call);
463
+ Logger.warn("Location permission unavailable. Capturing photo without location data.");
464
+ captureWithoutLocation(call);
463
465
  }
464
466
  } else {
465
467
  captureWithoutLocation(call);
@@ -469,35 +471,40 @@ public class CameraPreview extends Plugin implements CameraXView.CameraXViewList
469
471
  @SuppressLint("MissingPermission")
470
472
  @PermissionCallback
471
473
  private void captureWithLocationPermission(PluginCall call) {
472
- if (getPermissionState(CAMERA_WITH_LOCATION_PERMISSION_ALIAS) == PermissionState.GRANTED) {
473
- if (
474
- ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) !=
475
- PackageManager.PERMISSION_GRANTED ||
476
- ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) !=
477
- PackageManager.PERMISSION_GRANTED
478
- ) {
479
- return;
480
- }
474
+ if (LocationPermissionHelper.canCaptureWithExifLocation(getContext())) {
481
475
  getLocationAndCapture(call);
482
476
  } else {
483
- Logger.warn("Location permission denied. Capturing photo without location data.");
477
+ Logger.warn("Location permission denied or unavailable. Capturing photo without location data.");
484
478
  captureWithoutLocation(call);
485
479
  }
486
480
  }
487
481
 
488
- @RequiresPermission(allOf = { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION })
482
+ @RequiresPermission(anyOf = { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION })
489
483
  private void getLocationAndCapture(PluginCall call) {
484
+ if (getActivity() == null) {
485
+ Logger.warn("Activity unavailable for location lookup. Capturing photo without location data.");
486
+ captureWithoutLocation(call);
487
+ return;
488
+ }
489
+
490
490
  if (fusedLocationClient == null) {
491
491
  fusedLocationClient = LocationServices.getFusedLocationProviderClient(getContext());
492
492
  }
493
493
  fusedLocationClient
494
494
  .getLastLocation()
495
- .addOnSuccessListener(getActivity(), (location) -> {
496
- lastLocation = location;
497
- proceedWithCapture(call, lastLocation);
498
- })
499
- .addOnFailureListener((e) -> {
500
- Logger.error("Failed to get location: " + e.getMessage());
495
+ .addOnCompleteListener((task) -> {
496
+ if (task.isCanceled()) {
497
+ Logger.warn("Location lookup cancelled. Capturing photo without location data.");
498
+ proceedWithCapture(call, null);
499
+ return;
500
+ }
501
+ if (task.isSuccessful()) {
502
+ lastLocation = task.getResult();
503
+ proceedWithCapture(call, lastLocation);
504
+ return;
505
+ }
506
+ Exception error = task.getException();
507
+ Logger.error("Failed to get location: " + (error != null ? error.getMessage() : "unknown error"));
501
508
  proceedWithCapture(call, null);
502
509
  });
503
510
  }
@@ -0,0 +1,32 @@
1
+ package app.capgo.capacitor.camera.preview;
2
+
3
+ import android.Manifest;
4
+ import android.content.Context;
5
+ import android.content.pm.PackageManager;
6
+ import androidx.core.app.ActivityCompat;
7
+
8
+ /**
9
+ * Runtime location permission checks for EXIF capture.
10
+ * <p>
11
+ * Capacitor's cached {@link PermissionState} can diverge from the OS grant state; always
12
+ * verify both before requesting location and before settling a capture call.
13
+ */
14
+ public final class LocationPermissionHelper {
15
+
16
+ private LocationPermissionHelper() {}
17
+
18
+ public static boolean hasRuntimeLocationPermissions(Context context) {
19
+ return (
20
+ ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
21
+ ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
22
+ );
23
+ }
24
+
25
+ public static boolean canCaptureWithExifLocation(Context context) {
26
+ return canCaptureWithExifLocation(hasRuntimeLocationPermissions(context));
27
+ }
28
+
29
+ static boolean canCaptureWithExifLocation(boolean hasRuntimeLocationPermissions) {
30
+ return hasRuntimeLocationPermissions;
31
+ }
32
+ }
@@ -0,0 +1,38 @@
1
+ package app.capgo.capacitor.camera.preview;
2
+
3
+ import static org.junit.Assert.assertFalse;
4
+ import static org.junit.Assert.assertTrue;
5
+ import static org.robolectric.Shadows.shadowOf;
6
+
7
+ import android.Manifest;
8
+ import android.app.Application;
9
+ import androidx.test.core.app.ApplicationProvider;
10
+ import org.junit.Test;
11
+ import org.junit.runner.RunWith;
12
+ import org.robolectric.RobolectricTestRunner;
13
+ import org.robolectric.annotation.Config;
14
+
15
+ @RunWith(RobolectricTestRunner.class)
16
+ @Config(sdk = 31)
17
+ public class LocationPermissionHelperContextTest {
18
+
19
+ @Test
20
+ public void canCaptureWithExifLocationContextOverloadAllowsFineOnly() {
21
+ Application application = (Application) ApplicationProvider.getApplicationContext();
22
+ shadowOf(application).grantPermissions(Manifest.permission.ACCESS_FINE_LOCATION);
23
+ assertTrue(LocationPermissionHelper.canCaptureWithExifLocation(application));
24
+ }
25
+
26
+ @Test
27
+ public void canCaptureWithExifLocationContextOverloadAllowsCoarseOnly() {
28
+ Application application = (Application) ApplicationProvider.getApplicationContext();
29
+ shadowOf(application).grantPermissions(Manifest.permission.ACCESS_COARSE_LOCATION);
30
+ assertTrue(LocationPermissionHelper.canCaptureWithExifLocation(application));
31
+ }
32
+
33
+ @Test
34
+ public void canCaptureWithExifLocationContextOverloadDeniesWhenNeitherGranted() {
35
+ Application application = (Application) ApplicationProvider.getApplicationContext();
36
+ assertFalse(LocationPermissionHelper.canCaptureWithExifLocation(application));
37
+ }
38
+ }
@@ -0,0 +1,25 @@
1
+ package app.capgo.capacitor.camera.preview;
2
+
3
+ import static org.junit.Assert.assertFalse;
4
+ import static org.junit.Assert.assertTrue;
5
+
6
+ import org.junit.Test;
7
+
8
+ public class LocationPermissionHelperTest {
9
+
10
+ @Test
11
+ public void canCaptureWithExifLocationUsesRuntimePermissionOnly() {
12
+ assertTrue(LocationPermissionHelper.canCaptureWithExifLocation(true));
13
+ assertFalse(LocationPermissionHelper.canCaptureWithExifLocation(false));
14
+ }
15
+
16
+ @Test
17
+ public void runtimeAccessAllowsExifCaptureRegardlessOfCapacitorCache() {
18
+ assertTrue(LocationPermissionHelper.canCaptureWithExifLocation(true));
19
+ }
20
+
21
+ @Test
22
+ public void missingRuntimePermissionsFallsBackToGpsLessCapture() {
23
+ assertFalse(LocationPermissionHelper.canCaptureWithExifLocation(false));
24
+ }
25
+ }
@@ -35,7 +35,7 @@ extension UIWindow {
35
35
  */
36
36
  @objc(CameraPreview)
37
37
  public class CameraPreview: CAPPlugin, CAPBridgedPlugin, CLLocationManagerDelegate {
38
- private let pluginVersion: String = "8.11.5"
38
+ private let pluginVersion: String = "8.11.8"
39
39
  public let identifier = "CameraPreviewPlugin"
40
40
  public let jsName = "CameraPreview"
41
41
  public let pluginMethods: [CAPPluginMethod] = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/camera-preview",
3
- "version": "8.11.5",
3
+ "version": "8.11.8",
4
4
  "description": "Camera preview",
5
5
  "license": "MPL-2.0",
6
6
  "repository": {