@smileid/usesmileid_mlkit_face 12.0.2 → 12.1.0
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/CHANGELOG.md +7 -0
- package/README.md +7 -3
- package/android/build.gradle +21 -21
- package/android/src/main/java/com/usesmileid/mlkit/face/UseSmileIDMlkitFaceKameraAnalysisTick.kt +42 -0
- package/android/src/main/java/com/usesmileid/mlkit/face/UseSmileIDMlkitFaceModule.kt +6 -28
- package/android/src/main/java/com/usesmileid/mlkit/face/UseSmileIDMlkitFaceSessionHolder.kt +2 -3
- package/android/src/main/java/com/usesmileid/rn/analyzer/mlkitface/UseSmileIDAnalyzerExecutor.kt +28 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -38
- package/package.json +12 -13
- package/plugin/build/index.js +1 -27
- package/plugin/build/lib/mlkit_manifest.js +1 -54
- package/plugin/build/lib/root_project.d.ts +11 -3
- package/plugin/build/lib/root_project.js +4 -109
- package/android/src/main/java/com/usesmileid/mlkit/face/UseSmileIDMlkitFaceFrameProcessorPlugin.kt +0 -79
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/plugin/build/index.js.map +0 -1
- package/plugin/build/lib/mlkit_manifest.js.map +0 -1
- package/plugin/build/lib/root_project.js.map +0 -1
- package/plugin/src/__tests__/mirrored_sources.test.ts +0 -57
- package/plugin/src/__tests__/mlkit_manifest.test.ts +0 -117
- package/plugin/src/__tests__/root_project.test.ts +0 -195
- package/plugin/src/index.ts +0 -47
- package/plugin/src/lib/mlkit_manifest.ts +0 -88
- package/plugin/src/lib/root_project.ts +0 -160
- package/plugin/tsconfig.json +0 -24
- package/src/__tests__/use_smile_id_mlkit_face.test.ts +0 -101
- package/src/index.ts +0 -46
package/android/src/main/java/com/usesmileid/mlkit/face/UseSmileIDMlkitFaceFrameProcessorPlugin.kt
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
package com.usesmileid.mlkit.face
|
|
2
|
-
|
|
3
|
-
import androidx.camera.core.ExperimentalGetImage
|
|
4
|
-
import com.mrousavy.camera.core.FrameInvalidError
|
|
5
|
-
import com.mrousavy.camera.frameprocessors.Frame
|
|
6
|
-
import com.mrousavy.camera.frameprocessors.FrameProcessorPlugin
|
|
7
|
-
import com.usesmileid.bridge.model.YuvFrame as NativeYuvFrame
|
|
8
|
-
import kotlin.coroutines.cancellation.CancellationException
|
|
9
|
-
import kotlinx.coroutines.runBlocking
|
|
10
|
-
import kotlinx.coroutines.withContext
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* VisionCamera Frame Processor Plugin for the ML Kit face backend.
|
|
14
|
-
* Registered under [UseSmileIDMlkitFaceModule.FRAME_PROCESSOR_PLUGIN_NAME].
|
|
15
|
-
* Reads raw [ImageProxy] planes directly (no JPEG encoding) and runs the
|
|
16
|
-
* native session pipeline. Blocks the FP thread via [runBlocking] —
|
|
17
|
-
* VisionCamera drops frames that arrive while a scan is in flight.
|
|
18
|
-
*/
|
|
19
|
-
@OptIn(ExperimentalGetImage::class)
|
|
20
|
-
internal class UseSmileIDMlkitFaceFrameProcessorPlugin : FrameProcessorPlugin() {
|
|
21
|
-
|
|
22
|
-
override fun callback(frame: Frame, arguments: Map<String, Any>?): Any? {
|
|
23
|
-
val imageProxy = try {
|
|
24
|
-
frame.imageProxy
|
|
25
|
-
} catch (e: FrameInvalidError) {
|
|
26
|
-
return mapOf("error" to "frame_conversion_failed")
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
val image = imageProxy.image
|
|
30
|
-
?: return mapOf("error" to "frame_conversion_failed")
|
|
31
|
-
|
|
32
|
-
// Pixel-orientation for ML Kit — always from ImageProxy metadata, never from JS.
|
|
33
|
-
val sensorRotation = imageProxy.imageInfo.rotationDegrees
|
|
34
|
-
|
|
35
|
-
// Device-tilt context (0 = upright portrait) for head-pose math.
|
|
36
|
-
// Must not fall back to sensorRotation — sensor angle != device tilt.
|
|
37
|
-
val jsRotation = (arguments?.get("rotation") as? Number)?.toInt() ?: 0
|
|
38
|
-
|
|
39
|
-
val planes = image.planes
|
|
40
|
-
if (planes.size < 3) return mapOf("error" to "frame_conversion_failed")
|
|
41
|
-
|
|
42
|
-
fun extractPlane(index: Int): ByteArray {
|
|
43
|
-
val buffer = planes[index].buffer
|
|
44
|
-
buffer.rewind()
|
|
45
|
-
return ByteArray(buffer.remaining()).also { buffer.get(it) }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
val yuvFrame = NativeYuvFrame(
|
|
49
|
-
planeY = extractPlane(0),
|
|
50
|
-
planeU = extractPlane(1),
|
|
51
|
-
planeV = extractPlane(2),
|
|
52
|
-
yRowStride = planes[0].rowStride,
|
|
53
|
-
uRowStride = planes[1].rowStride,
|
|
54
|
-
vRowStride = planes[2].rowStride,
|
|
55
|
-
uPixelStride = planes[1].pixelStride,
|
|
56
|
-
vPixelStride = planes[2].pixelStride,
|
|
57
|
-
width = image.width,
|
|
58
|
-
height = image.height,
|
|
59
|
-
rotation = sensorRotation,
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
return runBlocking {
|
|
63
|
-
withContext(UseSmileIDMlkitFaceSessionHolder.sessionDispatcher) {
|
|
64
|
-
val activeSession = UseSmileIDMlkitFaceSessionHolder.session
|
|
65
|
-
?: return@withContext mapOf("error" to "session_not_configured")
|
|
66
|
-
|
|
67
|
-
try {
|
|
68
|
-
val scanResult = activeSession.scan(yuvFrame, jsRotation)
|
|
69
|
-
val events = activeSession.drainCaptureReadyEvents()
|
|
70
|
-
UseSmileIDMlkitFaceModule.buildWireResult(scanResult, events)
|
|
71
|
-
} catch (cancellation: CancellationException) {
|
|
72
|
-
throw cancellation
|
|
73
|
-
} catch (e: Exception) {
|
|
74
|
-
mapOf("error" to ("scan_failed: " + (e.message ?: e.javaClass.simpleName)))
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAG7E;;;;;;;;;;;;;;;;;;;GAmBG;AACH;;;;GAIG;AACH,eAAO,MAAM,8BAA8B;;;CAGjC,CAAC;AAEX,eAAO,MAAM,mBAAmB,+DAM9B,CAAC;AAKH,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAEhE,OAAO,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAC;AAEpF;;;;;;;;;;;;;;;;;;;GAmBG;AACH;;;;GAIG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,gBAAgB,EAAE,qBAAqB;IACvC,wBAAwB,EAAE,mCAAmC;CACrD,CAAC;AAEX,MAAM,CAAC,MAAM,mBAAmB,GAAG,0BAA0B,CAAC;IAC5D,gBAAgB,EAAE,8BAA8B,CAAC,gBAAgB;IACjE,wBAAwB,EACtB,8BAA8B,CAAC,wBAAwB;IACzD,IAAI,EAAE,MAAM;IACZ,GAAG,EAAE,kBAAkB,CAAC,SAAS;CAClC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,6EAA6E;AAC7E,gFAAgF;AAChF,0EAA0E;;AAI1E,qDAAgE;AAChE,yDAG8B;AAE9B,iEAAiE;AACjE,yEAAyE;AACzE,qDAAqD;AACrD,MAAM,sBAAsB,GAAG,QAAQ,CAAC;AACxC,MAAM,mBAAmB,GAAG,EAAE,CAAC;AAC/B,MAAM,mBAAmB,GAAG;IAC1B,2BAA2B;IAC3B,+BAA+B;CAChC,CAAC;AASF,MAAM,aAAa,GAA0C,CAC3D,MAAM,EACN,OAAO,EACP,EAAE;IACF,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,GAAG,eAAe,EAAE,GAAG,OAAO,IAAI,EAAE,CAAC;IACxE,OAAO,IAAA,uCAAsB,EAC3B,IAAA,yCAA0B,EAAC,MAAM,EAAE;QACjC,aAAa,EAAE,aAAa,IAAI,sBAAsB;QACtD,GAAG,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,iBAAiB,EAAE,mBAAmB;QACtC,kBAAkB,EAAE,mBAAmB;KACxC,CAAC,EACF,eAAe,CAChB,CAAC;AACJ,CAAC,CAAC;AAEF,kBAAe,aAAa,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mlkit_manifest.js","sourceRoot":"","sources":["../../src/lib/mlkit_manifest.ts"],"names":[],"mappings":";AAAA,4IAA4I;;;AAgC5I,0DAUC;AAeD,0DA8BC;AArFD,wDAI6B;AAW7B,MAAM,cAAc,GAAG,sCAAsC,CAAC;AAC9D,MAAM,eAAe,GAAG,kCAAkC,CAAC;AAE3D,6EAA6E;AAC7E,MAAM,yBAAyB,GAAG,MAAM,CAAC;AAEzC,kEAAkE;AAClE,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAOxC,6EAA6E;AAC7E,SAAgB,uBAAuB,CAAC,UAA6B;IACnE,KAAK,MAAM,UAAU,IAAI,UAAU,EAAE,CAAC;QACpC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CACb,sBAAsB,UAAU,4CAA4C;gBAC1E,0FAA0F,CAC7F,CAAC;QACJ,CAAC;IACH,CAAC;IACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,yBAAyB,EAAE,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC5E,CAAC;AAED,mHAAmH;AAC5G,MAAM,sBAAsB,GAE/B,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CACtB,IAAA,oCAAmB,EAAC,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE;IACrC,MAAM,CAAC,UAAU,GAAG,uBAAuB,CACzC,MAAM,CAAC,UAAU,EACjB,uBAAuB,CAAC,OAAO,EAAE,2BAA2B,IAAI,EAAE,CAAC,CACpE,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC,CAAC;AATQ,QAAA,sBAAsB,0BAS9B;AAEL,0EAA0E;AAC1E,SAAgB,uBAAuB,CACrC,QAAyB,EACzB,KAAa;IAEb,MAAM,WAAW,GACf,8BAAa,CAAC,QAAQ,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IAC7D,WAAW,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;IAChC,MAAM,OAAO,GAAG,WAAW,CAAC,WAAW,CAA0B,CAAC;IAElE,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAC3B,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,cAAc,CAAC,KAAK,cAAc,CACzD,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,KAAK,CAAC;QACpC,QAAQ,CAAC,CAAC,CAAC,eAAe,CAAC,GAAG,eAAe,CAAC;IAChD,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,IAAI,CAAC;YACX,CAAC,EAAE;gBACD,cAAc,EAAE,cAAc;gBAC9B,eAAe,EAAE,KAAK;gBACtB,eAAe,EAAE,eAAe;aACjC;SACF,CAAC,CAAC;IACL,CAAC;IAED,uFAAuF;IACvF,QAAQ,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAsC,CAAC;IAC/D,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,eAAe,CAAC;IAEvD,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"root_project.js","sourceRoot":"","sources":["../../src/lib/root_project.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { describe, expect, it } from "vitest";
|
|
4
|
-
|
|
5
|
-
// Both packages write the same marked region of `android/build.gradle`, so if
|
|
6
|
-
// their pins diverge the plugin Expo runs last silently wins.
|
|
7
|
-
|
|
8
|
-
const workspaceRoot = fileURLToPath(new URL("../../../../", import.meta.url));
|
|
9
|
-
|
|
10
|
-
function read(relativePath: string): string {
|
|
11
|
-
return readFileSync(`${workspaceRoot}${relativePath}`, "utf8");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
describe("mirrored plugin sources", () => {
|
|
15
|
-
// `plugin/src/index.ts` is deliberately absent: only the face package composes the manifest-merge plugin.
|
|
16
|
-
it.each([
|
|
17
|
-
"plugin/src/lib/root_project.ts",
|
|
18
|
-
"plugin/tsconfig.json",
|
|
19
|
-
"app.plugin.js",
|
|
20
|
-
])("keeps the mlkit packages' %s in step", (relativePath) => {
|
|
21
|
-
const face = read(`usesmileid_mlkit_face/${relativePath}`);
|
|
22
|
-
const document = read(`usesmileid_mlkit_document/${relativePath}`);
|
|
23
|
-
|
|
24
|
-
// The entry points differ only in the analyzer they name.
|
|
25
|
-
expect(normalise(face)).toBe(normalise(document));
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it.each([
|
|
29
|
-
"plugin/src/lib/podfile.ts",
|
|
30
|
-
"plugin/src/lib/deployment_target.ts",
|
|
31
|
-
"plugin/tsconfig.json",
|
|
32
|
-
"app.plugin.js",
|
|
33
|
-
])("keeps the vision packages' %s in step", (relativePath) => {
|
|
34
|
-
expect(read(`usesmileid_vision_face/${relativePath}`)).toBe(
|
|
35
|
-
read(`usesmileid_vision_document/${relativePath}`),
|
|
36
|
-
);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("pins the same Android versions in both mlkit plugins", () => {
|
|
40
|
-
const pins =
|
|
41
|
-
/const (DEFAULT_KOTLIN_VERSION|COMPILE_SDK_VERSION|FORCED_DEPENDENCIES)[\s\S]*?;/g;
|
|
42
|
-
const face = read("usesmileid_mlkit_face/plugin/src/index.ts").match(pins);
|
|
43
|
-
const document = read(
|
|
44
|
-
"usesmileid_mlkit_document/plugin/src/index.ts",
|
|
45
|
-
).match(pins);
|
|
46
|
-
|
|
47
|
-
expect(face).not.toBeNull();
|
|
48
|
-
expect(face).toHaveLength(3);
|
|
49
|
-
expect(face).toStrictEqual(document);
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
function normalise(source: string): string {
|
|
54
|
-
return source
|
|
55
|
-
.replace(/face/gi, "<analyzer>")
|
|
56
|
-
.replace(/document/gi, "<analyzer>");
|
|
57
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { AndroidConfig } from "expo/config-plugins";
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
applyMlkitManifestMerge,
|
|
6
|
-
mergedModelDependencies,
|
|
7
|
-
} from "../lib/mlkit_manifest";
|
|
8
|
-
|
|
9
|
-
type AndroidManifest = AndroidConfig.Manifest.AndroidManifest;
|
|
10
|
-
|
|
11
|
-
const META_DATA_NAME = "com.google.mlkit.vision.DEPENDENCIES";
|
|
12
|
-
|
|
13
|
-
// xml2js form, as `withAndroidManifest` hands the prebuild manifest over.
|
|
14
|
-
function template(): AndroidManifest {
|
|
15
|
-
return {
|
|
16
|
-
manifest: {
|
|
17
|
-
$: { "xmlns:android": "http://schemas.android.com/apk/res/android" },
|
|
18
|
-
application: [
|
|
19
|
-
{
|
|
20
|
-
$: { "android:name": ".MainApplication" },
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
},
|
|
24
|
-
} as unknown as AndroidManifest;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function metaData(manifest: AndroidManifest) {
|
|
28
|
-
const application =
|
|
29
|
-
AndroidConfig.Manifest.getMainApplicationOrThrow(manifest);
|
|
30
|
-
return (application["meta-data"] ?? []).find(
|
|
31
|
-
(entry) => entry?.$?.["android:name"] === META_DATA_NAME,
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe("mergedModelDependencies", () => {
|
|
36
|
-
it("always declares the hint this package's AAR carries", () => {
|
|
37
|
-
expect(mergedModelDependencies([])).toBe("face");
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it("appends the host app's other ML Kit hints", () => {
|
|
41
|
-
expect(mergedModelDependencies(["barcode_ui"])).toBe("face,barcode_ui");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("deduplicates so a caller repeating the provider hint is harmless", () => {
|
|
45
|
-
expect(mergedModelDependencies(["face", "barcode_ui", "barcode_ui"])).toBe(
|
|
46
|
-
"face,barcode_ui",
|
|
47
|
-
);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("rejects a value that could not be an ML Kit hint", () => {
|
|
51
|
-
expect(() => mergedModelDependencies(['face" />'])).toThrow(
|
|
52
|
-
/not a valid ML Kit model dependency/,
|
|
53
|
-
);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe("applyMlkitManifestMerge", () => {
|
|
58
|
-
it("claims the attribute for every ML Kit library in the app", () => {
|
|
59
|
-
const manifest = applyMlkitManifestMerge(template(), "face,barcode_ui");
|
|
60
|
-
const entry = metaData(manifest);
|
|
61
|
-
|
|
62
|
-
expect(entry?.$["android:value"]).toBe("face,barcode_ui");
|
|
63
|
-
expect(entry?.$["tools:replace"]).toBe("android:value");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it("declares the tools namespace the tools:replace attribute needs", () => {
|
|
67
|
-
const manifest = applyMlkitManifestMerge(template(), "face");
|
|
68
|
-
|
|
69
|
-
expect(manifest.manifest.$["xmlns:tools"]).toBe(
|
|
70
|
-
"http://schemas.android.com/tools",
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it("leaves a namespace the host already declared alone", () => {
|
|
75
|
-
const withNamespace = template();
|
|
76
|
-
withNamespace.manifest.$["xmlns:tools"] =
|
|
77
|
-
"http://schemas.android.com/tools";
|
|
78
|
-
|
|
79
|
-
const manifest = applyMlkitManifestMerge(withNamespace, "face");
|
|
80
|
-
|
|
81
|
-
expect(manifest.manifest.$["xmlns:tools"]).toBe(
|
|
82
|
-
"http://schemas.android.com/tools",
|
|
83
|
-
);
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("replaces a stale value instead of declaring the key twice", () => {
|
|
87
|
-
const once = applyMlkitManifestMerge(template(), "face");
|
|
88
|
-
const twice = applyMlkitManifestMerge(once, "face,barcode_ui");
|
|
89
|
-
|
|
90
|
-
const application = AndroidConfig.Manifest.getMainApplicationOrThrow(twice);
|
|
91
|
-
const entries = (application["meta-data"] ?? []).filter(
|
|
92
|
-
(entry) => entry?.$?.["android:name"] === META_DATA_NAME,
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
expect(entries).toHaveLength(1);
|
|
96
|
-
expect(entries[0]?.$["android:value"]).toBe("face,barcode_ui");
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it("keeps unrelated meta-data the host declared", () => {
|
|
100
|
-
const withOther = template();
|
|
101
|
-
const application =
|
|
102
|
-
AndroidConfig.Manifest.getMainApplicationOrThrow(withOther);
|
|
103
|
-
application["meta-data"] = [
|
|
104
|
-
{ $: { "android:name": "com.example.OTHER", "android:value": "keep" } },
|
|
105
|
-
];
|
|
106
|
-
|
|
107
|
-
const manifest = applyMlkitManifestMerge(withOther, "face");
|
|
108
|
-
const merged =
|
|
109
|
-
AndroidConfig.Manifest.getMainApplicationOrThrow(manifest)["meta-data"];
|
|
110
|
-
|
|
111
|
-
expect(merged).toHaveLength(2);
|
|
112
|
-
expect(
|
|
113
|
-
merged?.find((entry) => entry.$["android:name"] === "com.example.OTHER")
|
|
114
|
-
?.$["android:value"],
|
|
115
|
-
).toBe("keep");
|
|
116
|
-
});
|
|
117
|
-
});
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
applyAndroidRootProjectPins,
|
|
5
|
-
type AndroidRootProjectPins,
|
|
6
|
-
} from "../lib/root_project";
|
|
7
|
-
|
|
8
|
-
const PINS: AndroidRootProjectPins = {
|
|
9
|
-
kotlinVersion: "2.4.10",
|
|
10
|
-
kspVersion: "2.3.10",
|
|
11
|
-
compileSdkVersion: 37,
|
|
12
|
-
forcedDependencies: [
|
|
13
|
-
"androidx.core:core:1.18.0",
|
|
14
|
-
"androidx.core:core-ktx:1.18.0",
|
|
15
|
-
],
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// Shaped like the `android/build.gradle` Expo prebuild generates.
|
|
19
|
-
const TEMPLATE = `buildscript {
|
|
20
|
-
repositories {
|
|
21
|
-
google()
|
|
22
|
-
mavenCentral()
|
|
23
|
-
}
|
|
24
|
-
dependencies {
|
|
25
|
-
classpath('com.android.tools.build:gradle')
|
|
26
|
-
classpath('com.facebook.react:react-native-gradle-plugin')
|
|
27
|
-
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
allprojects {
|
|
32
|
-
repositories {
|
|
33
|
-
google()
|
|
34
|
-
mavenCentral()
|
|
35
|
-
maven { url 'https://www.jitpack.io' }
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
apply plugin: "expo-root-project"
|
|
40
|
-
apply plugin: "com.facebook.react.rootproject"
|
|
41
|
-
`;
|
|
42
|
-
|
|
43
|
-
describe("applyAndroidRootProjectPins", () => {
|
|
44
|
-
it("declares every pin before expo-root-project applies", () => {
|
|
45
|
-
const result = applyAndroidRootProjectPins(TEMPLATE, PINS);
|
|
46
|
-
|
|
47
|
-
expect(result).toContain("ext.kotlinVersion = '2.4.10'");
|
|
48
|
-
expect(result).toContain("ext.kspVersion = '2.3.10'");
|
|
49
|
-
expect(result).toContain("ext.compileSdkVersion = 37");
|
|
50
|
-
expect(result.indexOf("ext.kotlinVersion")).toBeLessThan(
|
|
51
|
-
result.indexOf('apply plugin: "expo-root-project"'),
|
|
52
|
-
);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("pins the Kotlin Gradle Plugin classpath so the compiler moves too", () => {
|
|
56
|
-
const result = applyAndroidRootProjectPins(TEMPLATE, PINS);
|
|
57
|
-
|
|
58
|
-
expect(result).toContain(
|
|
59
|
-
"classpath('org.jetbrains.kotlin:kotlin-gradle-plugin:2.4.10')",
|
|
60
|
-
);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// A template using Groovy's space form must not abort prebuild.
|
|
64
|
-
it.each([
|
|
65
|
-
[
|
|
66
|
-
"space-call, double quotes",
|
|
67
|
-
'classpath "org.jetbrains.kotlin:kotlin-gradle-plugin"',
|
|
68
|
-
],
|
|
69
|
-
[
|
|
70
|
-
"space-call, single quotes",
|
|
71
|
-
"classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin'",
|
|
72
|
-
],
|
|
73
|
-
[
|
|
74
|
-
"already versioned",
|
|
75
|
-
"classpath('org.jetbrains.kotlin:kotlin-gradle-plugin:2.1.20')",
|
|
76
|
-
],
|
|
77
|
-
])("pins the classpath written as %s", (_label, line) => {
|
|
78
|
-
const template = TEMPLATE.replace(
|
|
79
|
-
"classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')",
|
|
80
|
-
line,
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const result = applyAndroidRootProjectPins(template, PINS);
|
|
84
|
-
|
|
85
|
-
expect(result).toContain(
|
|
86
|
-
"classpath('org.jetbrains.kotlin:kotlin-gradle-plugin:2.4.10')",
|
|
87
|
-
);
|
|
88
|
-
expect(result).not.toContain("kotlin-gradle-plugin:2.1.20");
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it("is idempotent across prebuilds", () => {
|
|
92
|
-
const once = applyAndroidRootProjectPins(TEMPLATE, PINS);
|
|
93
|
-
|
|
94
|
-
expect(applyAndroidRootProjectPins(once, PINS)).toBe(once);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it("replaces stale versions instead of appending a second block", () => {
|
|
98
|
-
const stale = applyAndroidRootProjectPins(TEMPLATE, {
|
|
99
|
-
kotlinVersion: "2.3.20",
|
|
100
|
-
kspVersion: "2.3.6",
|
|
101
|
-
compileSdkVersion: 36,
|
|
102
|
-
forcedDependencies: ["androidx.core:core:1.17.0"],
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
const result = applyAndroidRootProjectPins(stale, PINS);
|
|
106
|
-
|
|
107
|
-
expect(result).not.toContain("2.3.20");
|
|
108
|
-
expect(result).not.toContain("ext.compileSdkVersion = 36");
|
|
109
|
-
expect(result).not.toContain("androidx.core:core:1.17.0");
|
|
110
|
-
expect(result.match(/ext\.kotlinVersion/g)).toHaveLength(1);
|
|
111
|
-
expect(result.match(/resolutionStrategy/g)).toHaveLength(1);
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("forces the pinned dependency versions across every module", () => {
|
|
115
|
-
const result = applyAndroidRootProjectPins(TEMPLATE, PINS);
|
|
116
|
-
|
|
117
|
-
expect(result).toContain("allprojects {");
|
|
118
|
-
expect(result).toContain("force 'androidx.core:core:1.18.0'");
|
|
119
|
-
expect(result).toContain("force 'androidx.core:core-ktx:1.18.0'");
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it("emits no resolution block when nothing is forced", () => {
|
|
123
|
-
const result = applyAndroidRootProjectPins(TEMPLATE, {
|
|
124
|
-
...PINS,
|
|
125
|
-
forcedDependencies: [],
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
expect(result).not.toContain("resolutionStrategy");
|
|
129
|
-
expect(result).toContain("ext.compileSdkVersion = 37");
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("leaves the template's own allprojects block intact", () => {
|
|
133
|
-
const result = applyAndroidRootProjectPins(TEMPLATE, PINS);
|
|
134
|
-
|
|
135
|
-
expect(result).toContain("mavenCentral()");
|
|
136
|
-
expect(result.match(/allprojects \{/g)).toHaveLength(2);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it("rejects a coordinate that could break out of the Groovy literal", () => {
|
|
140
|
-
expect(() =>
|
|
141
|
-
applyAndroidRootProjectPins(TEMPLATE, {
|
|
142
|
-
...PINS,
|
|
143
|
-
forcedDependencies: [
|
|
144
|
-
"androidx.core:core:1.18.0'; new File('/').delete()",
|
|
145
|
-
],
|
|
146
|
-
}),
|
|
147
|
-
).toThrow(/unsafe character/);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("fails loudly when the Kotlin Gradle Plugin classpath is absent", () => {
|
|
151
|
-
const withoutKotlin = TEMPLATE.replace(
|
|
152
|
-
" classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')\n",
|
|
153
|
-
"",
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
expect(() => applyAndroidRootProjectPins(withoutKotlin, PINS)).toThrow(
|
|
157
|
-
/kotlin-gradle-plugin classpath/,
|
|
158
|
-
);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
it("leaves the host's Kotlin untouched when no kotlinVersion is given", () => {
|
|
162
|
-
const result = applyAndroidRootProjectPins(TEMPLATE, {
|
|
163
|
-
compileSdkVersion: 37,
|
|
164
|
-
forcedDependencies: PINS.forcedDependencies,
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
expect(result).not.toContain("ext.kotlinVersion");
|
|
168
|
-
expect(result).not.toContain("ext.kspVersion");
|
|
169
|
-
expect(result).toContain(
|
|
170
|
-
"classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')",
|
|
171
|
-
);
|
|
172
|
-
expect(result).toContain("ext.compileSdkVersion = 37");
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
it("rejects kspVersion without kotlinVersion", () => {
|
|
176
|
-
expect(() =>
|
|
177
|
-
applyAndroidRootProjectPins(TEMPLATE, {
|
|
178
|
-
kspVersion: "2.3.10",
|
|
179
|
-
compileSdkVersion: 37,
|
|
180
|
-
forcedDependencies: [],
|
|
181
|
-
}),
|
|
182
|
-
).toThrow(/kspVersion/);
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
it("fails loudly when expo-root-project is absent", () => {
|
|
186
|
-
const withoutRootProject = TEMPLATE.replace(
|
|
187
|
-
'apply plugin: "expo-root-project"\n',
|
|
188
|
-
"",
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
expect(() => applyAndroidRootProjectPins(withoutRootProject, PINS)).toThrow(
|
|
192
|
-
/expo-root-project/,
|
|
193
|
-
);
|
|
194
|
-
});
|
|
195
|
-
});
|
package/plugin/src/index.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// The module is autolinked as a Gradle subproject, so it inherits the host's
|
|
2
|
-
// Kotlin compiler and `compileSdk`; Expo's defaults for both are lower than the
|
|
3
|
-
// AARs accept and a library cannot raise a setting the root project owns.
|
|
4
|
-
|
|
5
|
-
import { type ConfigPlugin } from "expo/config-plugins";
|
|
6
|
-
|
|
7
|
-
import { withAndroidRootProjectPins } from "./lib/root_project";
|
|
8
|
-
import {
|
|
9
|
-
withMlkitManifestMerge,
|
|
10
|
-
type MlkitManifestMergeOptions,
|
|
11
|
-
} from "./lib/mlkit_manifest";
|
|
12
|
-
|
|
13
|
-
// Lowest Kotlin that reads the AARs' 2.4.x metadata AND has Expo
|
|
14
|
-
// compiler-plugin (pika) artifacts; higher pins break expo-modules-core.
|
|
15
|
-
// KSP is left to Expo's own kotlin→ksp lookup table.
|
|
16
|
-
const DEFAULT_KOTLIN_VERSION = "2.3.20";
|
|
17
|
-
const COMPILE_SDK_VERSION = 37;
|
|
18
|
-
const FORCED_DEPENDENCIES = [
|
|
19
|
-
"androidx.core:core:1.18.0",
|
|
20
|
-
"androidx.core:core-ktx:1.18.0",
|
|
21
|
-
];
|
|
22
|
-
|
|
23
|
-
export interface MlkitFaceOptions extends MlkitManifestMergeOptions {
|
|
24
|
-
/** Overrides the default Kotlin pin (must read the AARs' metadata). */
|
|
25
|
-
kotlinVersion?: string;
|
|
26
|
-
/** Opt-in: only when Expo's lookup table lacks the pinned Kotlin. */
|
|
27
|
-
kspVersion?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const withMlkitFace: ConfigPlugin<MlkitFaceOptions | void> = (
|
|
31
|
-
config,
|
|
32
|
-
options,
|
|
33
|
-
) => {
|
|
34
|
-
const { kotlinVersion, kspVersion, ...manifestOptions } = options ?? {};
|
|
35
|
-
return withMlkitManifestMerge(
|
|
36
|
-
withAndroidRootProjectPins(config, {
|
|
37
|
-
kotlinVersion: kotlinVersion ?? DEFAULT_KOTLIN_VERSION,
|
|
38
|
-
...(kspVersion !== undefined ? { kspVersion } : {}),
|
|
39
|
-
compileSdkVersion: COMPILE_SDK_VERSION,
|
|
40
|
-
forcedDependencies: FORCED_DEPENDENCIES,
|
|
41
|
-
}),
|
|
42
|
-
manifestOptions,
|
|
43
|
-
);
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
export default withMlkitFace;
|
|
47
|
-
export type { MlkitManifestMergeOptions };
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
// The AAR declares this meta-data without `tools:replace`, so only the host `<application>` can claim it for every ML Kit consumer at once.
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
AndroidConfig,
|
|
5
|
-
type ConfigPlugin,
|
|
6
|
-
withAndroidManifest,
|
|
7
|
-
} from "expo/config-plugins";
|
|
8
|
-
|
|
9
|
-
type AndroidManifest = AndroidConfig.Manifest.AndroidManifest;
|
|
10
|
-
|
|
11
|
-
// Expo's meta-data attribute type covers only the `android:*` names.
|
|
12
|
-
type ReplaceableMetaData = {
|
|
13
|
-
$: AndroidConfig.Manifest.ManifestMetaData["$"] & {
|
|
14
|
-
"tools:replace"?: string;
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const META_DATA_NAME = "com.google.mlkit.vision.DEPENDENCIES";
|
|
19
|
-
const TOOLS_NAMESPACE = "http://schemas.android.com/tools";
|
|
20
|
-
|
|
21
|
-
/** The hint this package's AAR declares; always part of the merged value. */
|
|
22
|
-
const PROVIDER_MODEL_DEPENDENCY = "face";
|
|
23
|
-
|
|
24
|
-
// ML Kit's own hint vocabulary — lowercase words and underscores.
|
|
25
|
-
const MODEL_DEPENDENCY = /^[a-z0-9_]+$/;
|
|
26
|
-
|
|
27
|
-
export interface MlkitManifestMergeOptions {
|
|
28
|
-
/** Hints your app's other ML Kit consumers declare, so their models prefetch at install time rather than on first use. */
|
|
29
|
-
readonly additionalModelDependencies?: readonly string[];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Provider hint first, then the caller's, deduplicated and order-stable. */
|
|
33
|
-
export function mergedModelDependencies(additional: readonly string[]): string {
|
|
34
|
-
for (const dependency of additional) {
|
|
35
|
-
if (!MODEL_DEPENDENCY.test(dependency)) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
`[smileid-android] '${dependency}' is not a valid ML Kit model dependency. ` +
|
|
38
|
-
"Expected a lowercase hint such as 'barcode_ui', as printed in the manifest-merger error.",
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return [...new Set([PROVIDER_MODEL_DEPENDENCY, ...additional])].join(",");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Declares the merged `com.google.mlkit.vision.DEPENDENCIES` on the host `<application>` with `tools:replace`. */
|
|
46
|
-
export const withMlkitManifestMerge: ConfigPlugin<
|
|
47
|
-
MlkitManifestMergeOptions | void
|
|
48
|
-
> = (config, options) =>
|
|
49
|
-
withAndroidManifest(config, (config) => {
|
|
50
|
-
config.modResults = applyMlkitManifestMerge(
|
|
51
|
-
config.modResults,
|
|
52
|
-
mergedModelDependencies(options?.additionalModelDependencies ?? []),
|
|
53
|
-
);
|
|
54
|
-
return config;
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
/** Pure form of the edit. Idempotent; a re-run replaces a stale value. */
|
|
58
|
-
export function applyMlkitManifestMerge(
|
|
59
|
-
manifest: AndroidManifest,
|
|
60
|
-
value: string,
|
|
61
|
-
): AndroidManifest {
|
|
62
|
-
const application =
|
|
63
|
-
AndroidConfig.Manifest.getMainApplicationOrThrow(manifest);
|
|
64
|
-
application["meta-data"] ??= [];
|
|
65
|
-
const entries = application["meta-data"] as ReplaceableMetaData[];
|
|
66
|
-
|
|
67
|
-
const existing = entries.find(
|
|
68
|
-
(entry) => entry?.$?.["android:name"] === META_DATA_NAME,
|
|
69
|
-
);
|
|
70
|
-
if (existing) {
|
|
71
|
-
existing.$["android:value"] = value;
|
|
72
|
-
existing.$["tools:replace"] = "android:value";
|
|
73
|
-
} else {
|
|
74
|
-
entries.push({
|
|
75
|
-
$: {
|
|
76
|
-
"android:name": META_DATA_NAME,
|
|
77
|
-
"android:value": value,
|
|
78
|
-
"tools:replace": "android:value",
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Without the namespace on <manifest>, `tools:replace` merges as an unknown attribute.
|
|
84
|
-
manifest.manifest.$ ??= {} as AndroidManifest["manifest"]["$"];
|
|
85
|
-
manifest.manifest.$["xmlns:tools"] ??= TOOLS_NAMESPACE;
|
|
86
|
-
|
|
87
|
-
return manifest;
|
|
88
|
-
}
|