@capgo/camera-preview 8.11.4 → 8.11.6
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.
- package/android/build.gradle +5 -0
- package/android/src/main/java/app/capgo/capacitor/camera/preview/CameraPreview.java +27 -20
- package/android/src/main/java/app/capgo/capacitor/camera/preview/LocationPermissionHelper.java +32 -0
- package/android/src/test/java/app/capgo/capacitor/camera/preview/LocationPermissionHelperContextTest.java +38 -0
- package/android/src/test/java/app/capgo/capacitor/camera/preview/LocationPermissionHelperTest.java +25 -0
- package/dist/esm/web.js +18 -15
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +18 -15
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +18 -15
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/CapgoCameraPreviewPlugin/Plugin.swift +1 -1
- package/package.json +5 -7
package/android/build.gradle
CHANGED
|
@@ -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 {
|
|
@@ -69,6 +72,8 @@ dependencies {
|
|
|
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.6.1'
|
|
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 (
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
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
|
-
.
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
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
|
}
|
package/android/src/main/java/app/capgo/capacitor/camera/preview/LocationPermissionHelper.java
ADDED
|
@@ -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
|
+
}
|
package/android/src/test/java/app/capgo/capacitor/camera/preview/LocationPermissionHelperTest.java
ADDED
|
@@ -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
|
+
}
|
package/dist/esm/web.js
CHANGED
|
@@ -67,7 +67,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
67
67
|
try {
|
|
68
68
|
stream === null || stream === void 0 ? void 0 : stream.getTracks().forEach((t) => t.stop());
|
|
69
69
|
}
|
|
70
|
-
catch (
|
|
70
|
+
catch (_c) {
|
|
71
71
|
/* no-op */
|
|
72
72
|
}
|
|
73
73
|
}
|
|
@@ -177,16 +177,13 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
177
177
|
const leftInset = parseInset(styles.paddingLeft);
|
|
178
178
|
const rightInset = parseInset(styles.paddingRight);
|
|
179
179
|
probe.remove();
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
else {
|
|
188
|
-
top = Math.max(topInset, leftInset, rightInset);
|
|
189
|
-
}
|
|
180
|
+
const top = orientation === 1
|
|
181
|
+
? topInset
|
|
182
|
+
: orientation === 2
|
|
183
|
+
? leftInset > 0
|
|
184
|
+
? leftInset
|
|
185
|
+
: rightInset
|
|
186
|
+
: Math.max(topInset, leftInset, rightInset);
|
|
190
187
|
return { orientation, top };
|
|
191
188
|
}
|
|
192
189
|
async getZoomButtonValues() {
|
|
@@ -382,7 +379,7 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
382
379
|
}
|
|
383
380
|
this.videoElement.style.setProperty('top', `${y}px`, 'important');
|
|
384
381
|
// Force a style recalculation
|
|
385
|
-
this.videoElement.offsetHeight;
|
|
382
|
+
void this.videoElement.offsetHeight;
|
|
386
383
|
console.log('Positioning video:', {
|
|
387
384
|
positioning,
|
|
388
385
|
viewportHeight: window.innerHeight,
|
|
@@ -873,7 +870,9 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
873
870
|
await video.play();
|
|
874
871
|
}
|
|
875
872
|
catch (error) {
|
|
876
|
-
|
|
873
|
+
const err = new Error(`Failed to flip camera: ${String(error)}`);
|
|
874
|
+
err.cause = error;
|
|
875
|
+
throw err;
|
|
877
876
|
}
|
|
878
877
|
}
|
|
879
878
|
async setOpacity(_options) {
|
|
@@ -1064,7 +1063,9 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
1064
1063
|
});
|
|
1065
1064
|
}
|
|
1066
1065
|
catch (error) {
|
|
1067
|
-
|
|
1066
|
+
const err = new Error(`Failed to set zoom: ${String(error)}`);
|
|
1067
|
+
err.cause = error;
|
|
1068
|
+
throw err;
|
|
1068
1069
|
}
|
|
1069
1070
|
}
|
|
1070
1071
|
async getFlashMode() {
|
|
@@ -1110,7 +1111,9 @@ export class CameraPreviewWeb extends WebPlugin {
|
|
|
1110
1111
|
await video.play();
|
|
1111
1112
|
}
|
|
1112
1113
|
catch (error) {
|
|
1113
|
-
|
|
1114
|
+
const err = new Error(`Failed to swap to device ${options.deviceId}: ${String(error)}`);
|
|
1115
|
+
err.cause = error;
|
|
1116
|
+
throw err;
|
|
1114
1117
|
}
|
|
1115
1118
|
}
|
|
1116
1119
|
async getAspectRatio() {
|