facecog-liveness-showcase 0.0.1

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.
Files changed (229) hide show
  1. package/.browserslistrc +15 -0
  2. package/.dockerignore +48 -0
  3. package/.editorconfig +16 -0
  4. package/.eslintrc.json +47 -0
  5. package/.vercelignore +7 -0
  6. package/.vscode/extensions.json +5 -0
  7. package/.vscode/settings.json +3 -0
  8. package/DOCKER.md +221 -0
  9. package/Dockerfile +33 -0
  10. package/README.md +268 -0
  11. package/angular.json +156 -0
  12. package/capacitor.config.ts +9 -0
  13. package/docker-compose.dev.yml +20 -0
  14. package/docker-compose.yml +18 -0
  15. package/ionic.config.json +7 -0
  16. package/jest.config.js +38 -0
  17. package/nginx.conf +50 -0
  18. package/package.json +131 -0
  19. package/patches/ng-packagr+20.3.2.patch +60 -0
  20. package/projects/facecog-liveness-verification/README.md +295 -0
  21. package/projects/facecog-liveness-verification/ng-package.json +7 -0
  22. package/projects/facecog-liveness-verification/package.json +48 -0
  23. package/projects/facecog-liveness-verification/scripts/build-with-wrapper-copy.js +38 -0
  24. package/projects/facecog-liveness-verification/scripts/copy-wrapper-after-ngc.js +35 -0
  25. package/projects/facecog-liveness-verification/sources/FaceLivenessReactWrapper.tsx +320 -0
  26. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.generated.d.ts +28 -0
  27. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.generated.js +247 -0
  28. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.generated.js.map +1 -0
  29. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.js.map +1 -0
  30. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/FaceLivenessReactWrapper.ts +5 -0
  31. package/projects/facecog-liveness-verification/src/lib/components/aws-face-liveness/aws-face-liveness.component.ts +500 -0
  32. package/projects/facecog-liveness-verification/src/lib/components/camera-permission/camera-permission.component.html +41 -0
  33. package/projects/facecog-liveness-verification/src/lib/components/camera-permission/camera-permission.component.scss +234 -0
  34. package/projects/facecog-liveness-verification/src/lib/components/camera-permission/camera-permission.component.spec.ts +158 -0
  35. package/projects/facecog-liveness-verification/src/lib/components/camera-permission/camera-permission.component.ts +58 -0
  36. package/projects/facecog-liveness-verification/src/lib/components/camera-verification/camera-verification.component.html +34 -0
  37. package/projects/facecog-liveness-verification/src/lib/components/camera-verification/camera-verification.component.ts +210 -0
  38. package/projects/facecog-liveness-verification/src/lib/components/dialogs/save-custom-pose-dialog.component.ts +174 -0
  39. package/projects/facecog-liveness-verification/src/lib/components/facetec-scan/facetec-scan.component.html +45 -0
  40. package/projects/facecog-liveness-verification/src/lib/components/facetec-scan/facetec-scan.component.scss +87 -0
  41. package/projects/facecog-liveness-verification/src/lib/components/facetec-scan/facetec-scan.component.ts +182 -0
  42. package/projects/facecog-liveness-verification/src/lib/components/intro/intro.component.html +394 -0
  43. package/projects/facecog-liveness-verification/src/lib/components/intro/intro.component.scss +1567 -0
  44. package/projects/facecog-liveness-verification/src/lib/components/intro/intro.component.spec.ts +699 -0
  45. package/projects/facecog-liveness-verification/src/lib/components/intro/intro.component.ts +721 -0
  46. package/projects/facecog-liveness-verification/src/lib/components/live-preview/live-preview.component.html +120 -0
  47. package/projects/facecog-liveness-verification/src/lib/components/live-preview/live-preview.component.scss +611 -0
  48. package/projects/facecog-liveness-verification/src/lib/components/live-preview/live-preview.component.spec.ts +605 -0
  49. package/projects/facecog-liveness-verification/src/lib/components/live-preview/live-preview.component.ts +524 -0
  50. package/projects/facecog-liveness-verification/src/lib/components/liveness-flow/liveness-flow.component.html +73 -0
  51. package/projects/facecog-liveness-verification/src/lib/components/liveness-flow/liveness-flow.component.scss +19 -0
  52. package/projects/facecog-liveness-verification/src/lib/components/liveness-flow/liveness-flow.component.spec.ts +673 -0
  53. package/projects/facecog-liveness-verification/src/lib/components/liveness-flow/liveness-flow.component.ts +963 -0
  54. package/projects/facecog-liveness-verification/src/lib/components/liveness-verification/liveness-verification.component.html +38 -0
  55. package/projects/facecog-liveness-verification/src/lib/components/liveness-verification/liveness-verification.component.scss +10 -0
  56. package/projects/facecog-liveness-verification/src/lib/components/liveness-verification/liveness-verification.component.ts +233 -0
  57. package/projects/facecog-liveness-verification/src/lib/components/pose-selection/pose-selection.component.html +17 -0
  58. package/projects/facecog-liveness-verification/src/lib/components/pose-selection/pose-selection.component.spec.ts +35 -0
  59. package/projects/facecog-liveness-verification/src/lib/components/pose-selection/pose-selection.component.ts +33 -0
  60. package/projects/facecog-liveness-verification/src/lib/components/processing/processing.component.html +17 -0
  61. package/projects/facecog-liveness-verification/src/lib/components/processing/processing.component.scss +156 -0
  62. package/projects/facecog-liveness-verification/src/lib/components/processing/processing.component.spec.ts +46 -0
  63. package/projects/facecog-liveness-verification/src/lib/components/processing/processing.component.ts +18 -0
  64. package/projects/facecog-liveness-verification/src/lib/components/verification-result/verification-result.component.html +190 -0
  65. package/projects/facecog-liveness-verification/src/lib/components/verification-result/verification-result.component.scss +534 -0
  66. package/projects/facecog-liveness-verification/src/lib/components/verification-result/verification-result.component.spec.ts +286 -0
  67. package/projects/facecog-liveness-verification/src/lib/components/verification-result/verification-result.component.ts +155 -0
  68. package/projects/facecog-liveness-verification/src/lib/interfaces/analyze-response.interface.ts +16 -0
  69. package/projects/facecog-liveness-verification/src/lib/interfaces/aws-face-liveness.interface.ts +46 -0
  70. package/projects/facecog-liveness-verification/src/lib/interfaces/backend-adapter.interface.ts +21 -0
  71. package/projects/facecog-liveness-verification/src/lib/interfaces/backend-http-client.interface.ts +93 -0
  72. package/projects/facecog-liveness-verification/src/lib/interfaces/backend-response.interface.ts +9 -0
  73. package/projects/facecog-liveness-verification/src/lib/interfaces/camera-provider.interface.ts +107 -0
  74. package/projects/facecog-liveness-verification/src/lib/interfaces/category-info.interface.ts +9 -0
  75. package/projects/facecog-liveness-verification/src/lib/interfaces/custom-pose-data.interface.ts +14 -0
  76. package/projects/facecog-liveness-verification/src/lib/interfaces/custom-pose-repository.interface.ts +48 -0
  77. package/projects/facecog-liveness-verification/src/lib/interfaces/custom-pose-response.interface.ts +14 -0
  78. package/projects/facecog-liveness-verification/src/lib/interfaces/index.ts +52 -0
  79. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-action-result.interface.ts +13 -0
  80. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-config.interface.ts +17 -0
  81. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-metadata.interface.ts +17 -0
  82. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-result.interface.ts +24 -0
  83. package/projects/facecog-liveness-verification/src/lib/interfaces/liveness-verification-config.interface.ts +41 -0
  84. package/projects/facecog-liveness-verification/src/lib/interfaces/multi-backend-analyze-response.interface.ts +21 -0
  85. package/projects/facecog-liveness-verification/src/lib/interfaces/multi-backend-liveness-result.interface.ts +14 -0
  86. package/projects/facecog-liveness-verification/src/lib/interfaces/pose-definition.interface.ts +35 -0
  87. package/projects/facecog-liveness-verification/src/lib/interfaces/pose-keypoint.interface.ts +12 -0
  88. package/projects/facecog-liveness-verification/src/lib/interfaces/pose-match-result.interface.ts +9 -0
  89. package/projects/facecog-liveness-verification/src/lib/interfaces/pose-verify-response.interface.ts +8 -0
  90. package/projects/facecog-liveness-verification/src/lib/interfaces/scan-results.interface.ts +29 -0
  91. package/projects/facecog-liveness-verification/src/lib/interfaces/verification-plan.interface.ts +42 -0
  92. package/projects/facecog-liveness-verification/src/lib/interfaces/verification-progress-event.interface.ts +12 -0
  93. package/projects/facecog-liveness-verification/src/lib/interfaces/verification-session.interface.ts +72 -0
  94. package/projects/facecog-liveness-verification/src/lib/interfaces/verification-step-change-event.interface.ts +11 -0
  95. package/projects/facecog-liveness-verification/src/lib/interfaces/video-recording.interface.ts +9 -0
  96. package/projects/facecog-liveness-verification/src/lib/liveness-verification.module.ts +123 -0
  97. package/projects/facecog-liveness-verification/src/lib/models/constants/aws-face-liveness-component.token.ts +23 -0
  98. package/projects/facecog-liveness-verification/src/lib/models/constants/category-info.constant.ts +14 -0
  99. package/projects/facecog-liveness-verification/src/lib/models/constants/default-liveness-config.constant.ts +18 -0
  100. package/projects/facecog-liveness-verification/src/lib/models/constants/index.ts +5 -0
  101. package/projects/facecog-liveness-verification/src/lib/models/constants/liveness-verification-config.token.ts +16 -0
  102. package/projects/facecog-liveness-verification/src/lib/models/constants/pose-definitions.constant.ts +377 -0
  103. package/projects/facecog-liveness-verification/src/lib/models/index.ts +5 -0
  104. package/projects/facecog-liveness-verification/src/lib/models/utils/index.ts +2 -0
  105. package/projects/facecog-liveness-verification/src/lib/models/utils/pose.utils.spec.ts +76 -0
  106. package/projects/facecog-liveness-verification/src/lib/models/utils/pose.utils.ts +59 -0
  107. package/projects/facecog-liveness-verification/src/lib/services/aws-face-liveness.service.ts +49 -0
  108. package/projects/facecog-liveness-verification/src/lib/services/backend-http.service.spec.ts +111 -0
  109. package/projects/facecog-liveness-verification/src/lib/services/backend-http.service.ts +130 -0
  110. package/projects/facecog-liveness-verification/src/lib/services/backends/azure-backend.service.spec.ts +69 -0
  111. package/projects/facecog-liveness-verification/src/lib/services/backends/azure-backend.service.ts +72 -0
  112. package/projects/facecog-liveness-verification/src/lib/services/backends/facetec-backend.service.spec.ts +24 -0
  113. package/projects/facecog-liveness-verification/src/lib/services/backends/facetec-backend.service.ts +35 -0
  114. package/projects/facecog-liveness-verification/src/lib/services/backends/mock-backend.service.spec.ts +36 -0
  115. package/projects/facecog-liveness-verification/src/lib/services/backends/mock-backend.service.ts +39 -0
  116. package/projects/facecog-liveness-verification/src/lib/services/backends/openpose-backend.service.spec.ts +81 -0
  117. package/projects/facecog-liveness-verification/src/lib/services/backends/openpose-backend.service.ts +72 -0
  118. package/projects/facecog-liveness-verification/src/lib/services/backends/rekognition-analysis-backend.service.spec.ts +69 -0
  119. package/projects/facecog-liveness-verification/src/lib/services/backends/rekognition-analysis-backend.service.ts +83 -0
  120. package/projects/facecog-liveness-verification/src/lib/services/camera.service.spec.ts +200 -0
  121. package/projects/facecog-liveness-verification/src/lib/services/camera.service.ts +155 -0
  122. package/projects/facecog-liveness-verification/src/lib/services/custom-poses-api.service.ts +117 -0
  123. package/projects/facecog-liveness-verification/src/lib/services/index.ts +18 -0
  124. package/projects/facecog-liveness-verification/src/lib/services/liveness-backend.service.spec.ts +103 -0
  125. package/projects/facecog-liveness-verification/src/lib/services/liveness-backend.service.ts +61 -0
  126. package/projects/facecog-liveness-verification/src/lib/services/liveness-config.service.spec.ts +109 -0
  127. package/projects/facecog-liveness-verification/src/lib/services/liveness-config.service.ts +70 -0
  128. package/projects/facecog-liveness-verification/src/lib/services/liveness-orchestrator.service.spec.ts +144 -0
  129. package/projects/facecog-liveness-verification/src/lib/services/liveness-orchestrator.service.ts +162 -0
  130. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/hand-gesture-detection.service.ts +315 -0
  131. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/index.ts +5 -0
  132. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/openpose.service.ts +287 -0
  133. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/pose-comparison.service.ts +353 -0
  134. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/pose-matching.service.ts +2370 -0
  135. package/projects/facecog-liveness-verification/src/lib/services/pose-detection/reference-pose.service.ts +271 -0
  136. package/projects/facecog-liveness-verification/src/lib/services/pose-selection.service.spec.ts +183 -0
  137. package/projects/facecog-liveness-verification/src/lib/services/pose-selection.service.ts +179 -0
  138. package/projects/facecog-liveness-verification/src/lib/services/verification-api.service.spec.ts +159 -0
  139. package/projects/facecog-liveness-verification/src/lib/services/verification-api.service.ts +151 -0
  140. package/projects/facecog-liveness-verification/src/lib/services/verification-plan.service.spec.ts +184 -0
  141. package/projects/facecog-liveness-verification/src/lib/services/verification-plan.service.ts +94 -0
  142. package/projects/facecog-liveness-verification/src/lib/services/video-recorder.service.spec.ts +52 -0
  143. package/projects/facecog-liveness-verification/src/lib/services/video-recorder.service.ts +117 -0
  144. package/projects/facecog-liveness-verification/src/lib/types/detection-strategy.type.ts +5 -0
  145. package/projects/facecog-liveness-verification/src/lib/types/index.ts +7 -0
  146. package/projects/facecog-liveness-verification/src/lib/types/liveness-action.type.ts +31 -0
  147. package/projects/facecog-liveness-verification/src/lib/types/liveness-backend.type.ts +5 -0
  148. package/projects/facecog-liveness-verification/src/lib/types/pose-category.type.ts +5 -0
  149. package/projects/facecog-liveness-verification/src/lib/types/pose-difficulty.type.ts +5 -0
  150. package/projects/facecog-liveness-verification/src/lib/types/verification-flow-step.type.ts +5 -0
  151. package/projects/facecog-liveness-verification/src/lib/types/verification-step-kind.type.ts +4 -0
  152. package/projects/facecog-liveness-verification/src/public-api.ts +150 -0
  153. package/projects/facecog-liveness-verification/tsconfig.lib.json +20 -0
  154. package/projects/facecog-liveness-verification/tsconfig.lib.prod.json +11 -0
  155. package/projects/facecog-liveness-verification/tsconfig.spec.json +13 -0
  156. package/projects/facecog-liveness-verification/tsconfig.wrapper.json +15 -0
  157. package/projects/facecog-liveness-verification-test/src/app/app-routing.module.ts +22 -0
  158. package/projects/facecog-liveness-verification-test/src/app/app.component.html +3 -0
  159. package/projects/facecog-liveness-verification-test/src/app/app.component.scss +0 -0
  160. package/projects/facecog-liveness-verification-test/src/app/app.component.ts +11 -0
  161. package/projects/facecog-liveness-verification-test/src/app/app.module.ts +27 -0
  162. package/projects/facecog-liveness-verification-test/src/app/home/home-routing.module.ts +16 -0
  163. package/projects/facecog-liveness-verification-test/src/app/home/home.module.ts +19 -0
  164. package/projects/facecog-liveness-verification-test/src/app/home/home.page.html +39 -0
  165. package/projects/facecog-liveness-verification-test/src/app/home/home.page.scss +97 -0
  166. package/projects/facecog-liveness-verification-test/src/app/home/home.page.spec.ts +24 -0
  167. package/projects/facecog-liveness-verification-test/src/app/home/home.page.ts +92 -0
  168. package/projects/facecog-liveness-verification-test/src/app/home/verification-modal.component.ts +106 -0
  169. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-Bold_0.ttf +0 -0
  170. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-Medium_0.ttf +0 -0
  171. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-Regular_0.ttf +0 -0
  172. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-SemiBold_0.ttf +0 -0
  173. package/projects/facecog-liveness-verification-test/src/assets/fonts/gilroy/Gilroy-Thin_0.ttf +0 -0
  174. package/projects/facecog-liveness-verification-test/src/assets/icon/favicon.png +0 -0
  175. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Five_Fingers_Left.jpg +0 -0
  176. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Left_Palm.jpg +0 -0
  177. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Ok_Sign_Right.jpg +0 -0
  178. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Peace_Sign_Left.jpg +0 -0
  179. package/projects/facecog-liveness-verification-test/src/assets/images/poses/README.md +77 -0
  180. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Right_Palm.jpg +0 -0
  181. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Speak_Phrase.jpg +0 -0
  182. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Three_Fingers_Right.jpg +0 -0
  183. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Thumbs_Up_Left.jpg +0 -0
  184. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Thumbs_Up_Right.jpg +0 -0
  185. package/projects/facecog-liveness-verification-test/src/assets/images/poses/Wave_Right.jpg +0 -0
  186. package/projects/facecog-liveness-verification-test/src/assets/images/poses/blink.jpeg +0 -0
  187. package/projects/facecog-liveness-verification-test/src/assets/images/poses/blink_twice.jpeg +0 -0
  188. package/projects/facecog-liveness-verification-test/src/assets/images/poses/center_face.png +0 -0
  189. package/projects/facecog-liveness-verification-test/src/assets/images/poses/clap.jpeg +0 -0
  190. package/projects/facecog-liveness-verification-test/src/assets/images/poses/cover_mouth.png +0 -0
  191. package/projects/facecog-liveness-verification-test/src/assets/images/poses/cover_right_eye.png +0 -0
  192. package/projects/facecog-liveness-verification-test/src/assets/images/poses/cross_arms.png +0 -0
  193. package/projects/facecog-liveness-verification-test/src/assets/images/poses/face_straight.png +0 -0
  194. package/projects/facecog-liveness-verification-test/src/assets/images/poses/follow_dot.png +0 -0
  195. package/projects/facecog-liveness-verification-test/src/assets/images/poses/look_down.png +0 -0
  196. package/projects/facecog-liveness-verification-test/src/assets/images/poses/look_up.png +0 -0
  197. package/projects/facecog-liveness-verification-test/src/assets/images/poses/move_closer.png +0 -0
  198. package/projects/facecog-liveness-verification-test/src/assets/images/poses/nod.png +0 -0
  199. package/projects/facecog-liveness-verification-test/src/assets/images/poses/open_mouth.png +0 -0
  200. package/projects/facecog-liveness-verification-test/src/assets/images/poses/raise_eyebrow.png +0 -0
  201. package/projects/facecog-liveness-verification-test/src/assets/images/poses/rotate_face.jpeg +0 -0
  202. package/projects/facecog-liveness-verification-test/src/assets/images/poses/shake_head.jpeg +0 -0
  203. package/projects/facecog-liveness-verification-test/src/assets/images/poses/smile.png +0 -0
  204. package/projects/facecog-liveness-verification-test/src/assets/images/poses/tilt_left.png +0 -0
  205. package/projects/facecog-liveness-verification-test/src/assets/images/poses/tilt_right.png +0 -0
  206. package/projects/facecog-liveness-verification-test/src/assets/images/poses/touch_chin_left.jpg +0 -0
  207. package/projects/facecog-liveness-verification-test/src/assets/images/poses/touch_left_cheek.jpeg +0 -0
  208. package/projects/facecog-liveness-verification-test/src/assets/images/poses/touch_nose_right.png +0 -0
  209. package/projects/facecog-liveness-verification-test/src/assets/images/poses/touch_right_cheek.jpeg +0 -0
  210. package/projects/facecog-liveness-verification-test/src/assets/images/poses/turn_left.png +0 -0
  211. package/projects/facecog-liveness-verification-test/src/assets/images/poses/turn_right.png +0 -0
  212. package/projects/facecog-liveness-verification-test/src/assets/images/poses/wink.jpeg +0 -0
  213. package/projects/facecog-liveness-verification-test/src/assets/images/reference-pose.jpg +0 -0
  214. package/projects/facecog-liveness-verification-test/src/assets/shapes.svg +1 -0
  215. package/projects/facecog-liveness-verification-test/src/environments/environment.prod.ts +4 -0
  216. package/projects/facecog-liveness-verification-test/src/environments/environment.ts +17 -0
  217. package/projects/facecog-liveness-verification-test/src/global.scss +288 -0
  218. package/projects/facecog-liveness-verification-test/src/index.html +31 -0
  219. package/projects/facecog-liveness-verification-test/src/main.ts +6 -0
  220. package/projects/facecog-liveness-verification-test/src/polyfills.ts +55 -0
  221. package/projects/facecog-liveness-verification-test/src/theme/nextsapien-theme.scss +174 -0
  222. package/projects/facecog-liveness-verification-test/src/theme/variables.scss +2 -0
  223. package/projects/facecog-liveness-verification-test/src/zone-flags.ts +6 -0
  224. package/projects/facecog-liveness-verification-test/tsconfig.app.json +15 -0
  225. package/projects/facecog-liveness-verification-test/tsconfig.spec.json +14 -0
  226. package/setup-jest.ts +118 -0
  227. package/tsconfig.json +41 -0
  228. package/tsconfig.spec.json +15 -0
  229. package/vercel.json +24 -0
@@ -0,0 +1,234 @@
1
+ :host {
2
+ display: flex;
3
+ flex-direction: column;
4
+ height: 100%;
5
+ width: 100%;
6
+ }
7
+
8
+ .camera-permission-wrapper {
9
+ flex: 1;
10
+ overflow-y: auto;
11
+ padding: 16px;
12
+ background: var(--ion-background-color, #fff);
13
+ }
14
+
15
+ .permission-container {
16
+ display: flex;
17
+ flex-direction: column;
18
+ align-items: center;
19
+ justify-content: center;
20
+ min-height: 100%;
21
+ padding: 24px;
22
+ }
23
+
24
+ .content-wrapper {
25
+ display: flex;
26
+ flex-direction: column;
27
+ align-items: center;
28
+ width: 100%;
29
+ max-width: 480px;
30
+ }
31
+
32
+ .icon-container {
33
+ width: 140px;
34
+ height: 140px;
35
+ border-radius: 50%;
36
+ background: linear-gradient(135deg, rgba(254, 60, 114, 0.2) 0%, rgba(254, 60, 114, 0.1) 100%);
37
+ display: flex;
38
+ align-items: center;
39
+ justify-content: center;
40
+ margin: 0 0 32px;
41
+ box-shadow: 0 12px 32px rgba(254, 60, 114, 0.2), 0 4px 12px rgba(0, 0, 0, 0.1);
42
+ position: relative;
43
+ animation: pulseGlow 2s ease-in-out infinite;
44
+
45
+ &::before {
46
+ content: '';
47
+ position: absolute;
48
+ width: 100%;
49
+ height: 100%;
50
+ border-radius: 50%;
51
+ border: 2px solid rgba(254, 60, 114, 0.3);
52
+ animation: ripple 2s ease-out infinite;
53
+ }
54
+
55
+ .camera-icon {
56
+ font-size: 70px;
57
+ color: var(--ion-color-primary);
58
+ }
59
+ }
60
+
61
+ .permission-text {
62
+ text-align: center;
63
+ margin: 0 0 40px;
64
+ width: 100%;
65
+
66
+ h2 {
67
+ color: var(--ion-color-primary);
68
+ margin-bottom: 24px;
69
+ font-size: 28px;
70
+ font-weight: 700;
71
+ letter-spacing: -0.5px;
72
+ }
73
+
74
+ p {
75
+ color: var(--ion-color-medium);
76
+ line-height: 1.7;
77
+ margin: 12px 0;
78
+ font-size: 16px;
79
+ font-weight: 400;
80
+
81
+ &:first-of-type {
82
+ font-size: 17px;
83
+ color: var(--ion-text-color);
84
+ font-weight: 500;
85
+ margin-bottom: 16px;
86
+ }
87
+
88
+ &:last-of-type {
89
+ font-size: 14px;
90
+ opacity: 0.9;
91
+ margin-top: 20px;
92
+ }
93
+ }
94
+ }
95
+
96
+ .tips-container {
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: 12px;
100
+ margin: 20px 0 8px;
101
+ width: 100%;
102
+ text-align: left;
103
+
104
+ .tip-item {
105
+ display: flex;
106
+ align-items: flex-start;
107
+ gap: 10px;
108
+ padding: 12px 16px;
109
+ border-radius: 10px;
110
+ background: rgba(var(--ion-color-medium-rgb, 146, 148, 156), 0.08);
111
+ font-size: 14px;
112
+ line-height: 1.5;
113
+ color: var(--ion-text-color);
114
+
115
+ ion-icon {
116
+ font-size: 20px;
117
+ flex-shrink: 0;
118
+ margin-top: 1px;
119
+ color: var(--ion-color-medium);
120
+ }
121
+
122
+ &.blocking-notice {
123
+ background: rgba(var(--ion-color-warning-rgb, 255, 196, 9), 0.12);
124
+ border: 1px solid rgba(var(--ion-color-warning-rgb, 255, 196, 9), 0.3);
125
+ font-weight: 600;
126
+
127
+ ion-icon {
128
+ color: var(--ion-color-warning-shade, #e0ac08);
129
+ }
130
+ }
131
+ }
132
+ }
133
+
134
+ .button-container {
135
+ width: 100%;
136
+ display: flex;
137
+ flex-direction: column;
138
+ align-items: center;
139
+ gap: 16px;
140
+
141
+ ion-button {
142
+ width: 100%;
143
+ }
144
+
145
+ .cancel-link {
146
+ background: none;
147
+ border: none;
148
+ color: var(--ion-color-medium);
149
+ font-size: 15px;
150
+ font-weight: 500;
151
+ padding: 12px 16px;
152
+ cursor: pointer;
153
+ transition: color 0.2s ease;
154
+ text-decoration: none;
155
+
156
+ &:hover {
157
+ color: var(--ion-color-primary);
158
+ text-decoration: underline;
159
+ }
160
+
161
+ &:active {
162
+ opacity: 0.7;
163
+ }
164
+ }
165
+ }
166
+
167
+ @keyframes pulseGlow {
168
+ 0%, 100% {
169
+ transform: scale(1);
170
+ box-shadow: 0 12px 32px rgba(254, 60, 114, 0.2), 0 4px 12px rgba(0, 0, 0, 0.1);
171
+ }
172
+ 50% {
173
+ transform: scale(1.05);
174
+ box-shadow: 0 16px 40px rgba(254, 60, 114, 0.3), 0 6px 16px rgba(0, 0, 0, 0.15);
175
+ }
176
+ }
177
+
178
+ @keyframes ripple {
179
+ 0% {
180
+ transform: scale(1);
181
+ opacity: 1;
182
+ }
183
+ 100% {
184
+ transform: scale(1.3);
185
+ opacity: 0;
186
+ }
187
+ }
188
+
189
+ @media (max-width: 768px) {
190
+ .icon-container {
191
+ width: 140px;
192
+ height: 140px;
193
+
194
+ .camera-icon {
195
+ font-size: 70px;
196
+ }
197
+ }
198
+
199
+ .permission-text {
200
+ h2 {
201
+ font-size: 24px;
202
+ }
203
+
204
+ p {
205
+ font-size: 15px;
206
+
207
+ &:first-of-type {
208
+ font-size: 16px;
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ @media (max-width: 480px) {
215
+ .permission-container {
216
+ padding: 32px 20px 24px;
217
+ }
218
+
219
+ .icon-container {
220
+ width: 120px;
221
+ height: 120px;
222
+ margin: 24px 0 32px;
223
+
224
+ .camera-icon {
225
+ font-size: 64px;
226
+ }
227
+ }
228
+
229
+ .permission-text {
230
+ h2 {
231
+ font-size: 22px;
232
+ }
233
+ }
234
+ }
@@ -0,0 +1,158 @@
1
+ import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+ import { ChangeDetectorRef, NgZone } from '@angular/core';
3
+ import { IonicModule } from '@ionic/angular';
4
+ import { CommonModule } from '@angular/common';
5
+
6
+ import { CameraPermissionComponent } from './camera-permission.component';
7
+ import { CAMERA_PROVIDER } from '../../interfaces/camera-provider.interface';
8
+
9
+ describe('CameraPermissionComponent', () => {
10
+ let component: CameraPermissionComponent;
11
+ let fixture: ComponentFixture<CameraPermissionComponent>;
12
+ let mockCdr: { markForCheck: jasmine.Spy; detectChanges: jasmine.Spy };
13
+ let ngZoneRunSpy: jasmine.Spy;
14
+ let getUserMediaSpy: jasmine.Spy;
15
+
16
+ beforeEach(async () => {
17
+ mockCdr = {
18
+ markForCheck: jasmine.createSpy('markForCheck'),
19
+ detectChanges: jasmine.createSpy('detectChanges')
20
+ };
21
+
22
+ const mockStream = {
23
+ getTracks: () => [{ stop: jasmine.createSpy('stop') }]
24
+ };
25
+ getUserMediaSpy = jasmine.createSpy('getUserMedia').and.returnValue(Promise.resolve(mockStream));
26
+
27
+ Object.defineProperty(navigator, 'mediaDevices', {
28
+ value: { getUserMedia: getUserMediaSpy },
29
+ configurable: true
30
+ });
31
+
32
+ await TestBed.configureTestingModule({
33
+ imports: [CommonModule, IonicModule.forRoot(), CameraPermissionComponent],
34
+ providers: [
35
+ { provide: ChangeDetectorRef, useValue: mockCdr },
36
+ { provide: CAMERA_PROVIDER, useValue: null }
37
+ ]
38
+ }).compileComponents();
39
+
40
+ const ngZone = TestBed.inject(NgZone);
41
+ ngZoneRunSpy = spyOn(ngZone, 'run').and.callFake((fn: (...args: any[]) => any) => fn());
42
+
43
+ fixture = TestBed.createComponent(CameraPermissionComponent);
44
+ component = fixture.componentInstance;
45
+ });
46
+
47
+ it('should create', () => {
48
+ expect(component).toBeTruthy();
49
+ });
50
+
51
+ it('requestPermission calls navigator.mediaDevices.getUserMedia when no cameraProvider', async () => {
52
+ let emitted = false;
53
+ component.permissionGranted.subscribe(() => (emitted = true));
54
+
55
+ await component.requestPermission();
56
+
57
+ expect(getUserMediaSpy).toHaveBeenCalledWith({
58
+ video: { facingMode: 'user', width: { ideal: 1280 }, height: { ideal: 720 } },
59
+ audio: false
60
+ });
61
+ expect(ngZoneRunSpy).toHaveBeenCalled();
62
+ expect(emitted).toBe(true);
63
+ });
64
+
65
+ it('requestPermission emits permissionGranted on success inside ngZone.run', async () => {
66
+ let grantedEmitted = false;
67
+ component.permissionGranted.subscribe(() => (grantedEmitted = true));
68
+
69
+ await component.requestPermission();
70
+
71
+ expect(ngZoneRunSpy).toHaveBeenCalled();
72
+ expect(grantedEmitted).toBe(true);
73
+ });
74
+
75
+ it('requestPermission emits permissionDenied on error inside ngZone.run', async () => {
76
+ getUserMediaSpy.and.returnValue(Promise.reject(new Error('Permission denied')));
77
+
78
+ let deniedEmitted = false;
79
+ component.permissionDenied.subscribe(() => (deniedEmitted = true));
80
+
81
+ await component.requestPermission();
82
+
83
+ expect(ngZoneRunSpy).toHaveBeenCalled();
84
+ expect(deniedEmitted).toBe(true);
85
+ });
86
+
87
+ it('requestPermission uses cameraProvider.requestPermissions() when available', async () => {
88
+ const mockCameraProvider = {
89
+ requestPermissions: jasmine.createSpy('requestPermissions').and.returnValue(Promise.resolve(true))
90
+ };
91
+
92
+ await TestBed.configureTestingModule({
93
+ imports: [CommonModule, IonicModule.forRoot(), CameraPermissionComponent],
94
+ providers: [
95
+ { provide: ChangeDetectorRef, useValue: mockCdr },
96
+ { provide: CAMERA_PROVIDER, useValue: mockCameraProvider }
97
+ ]
98
+ }).compileComponents();
99
+
100
+ const ngZone = TestBed.inject(NgZone);
101
+ spyOn(ngZone, 'run').and.callFake((fn: (...args: any[]) => any) => fn());
102
+
103
+ fixture = TestBed.createComponent(CameraPermissionComponent);
104
+ component = fixture.componentInstance;
105
+
106
+ let grantedEmitted = false;
107
+ component.permissionGranted.subscribe(() => (grantedEmitted = true));
108
+
109
+ await component.requestPermission();
110
+
111
+ expect(mockCameraProvider.requestPermissions).toHaveBeenCalled();
112
+ expect(getUserMediaSpy).not.toHaveBeenCalled();
113
+ expect(grantedEmitted).toBe(true);
114
+ });
115
+
116
+ it('requestPermission emits permissionDenied when cameraProvider returns false', async () => {
117
+ const mockCameraProvider = {
118
+ requestPermissions: jasmine.createSpy('requestPermissions').and.returnValue(Promise.resolve(false))
119
+ };
120
+
121
+ await TestBed.configureTestingModule({
122
+ imports: [CommonModule, IonicModule.forRoot(), CameraPermissionComponent],
123
+ providers: [
124
+ { provide: ChangeDetectorRef, useValue: mockCdr },
125
+ { provide: CAMERA_PROVIDER, useValue: mockCameraProvider }
126
+ ]
127
+ }).compileComponents();
128
+
129
+ const ngZone = TestBed.inject(NgZone);
130
+ spyOn(ngZone, 'run').and.callFake((fn: (...args: any[]) => any) => fn());
131
+
132
+ fixture = TestBed.createComponent(CameraPermissionComponent);
133
+ component = fixture.componentInstance;
134
+
135
+ let deniedEmitted = false;
136
+ component.permissionDenied.subscribe(() => (deniedEmitted = true));
137
+
138
+ await component.requestPermission();
139
+
140
+ expect(mockCameraProvider.requestPermissions).toHaveBeenCalled();
141
+ expect(deniedEmitted).toBe(true);
142
+ });
143
+
144
+ it('onLater emits permissionDenied', () => {
145
+ let emitted = false;
146
+ component.permissionDenied.subscribe(() => (emitted = true));
147
+ component.onLater();
148
+ expect(emitted).toBe(true);
149
+ });
150
+
151
+ it('hideCancel input defaults to false', () => {
152
+ expect(component.hideCancel).toBe(false);
153
+ });
154
+
155
+ it('poseSpecificMode input defaults to false', () => {
156
+ expect(component.poseSpecificMode).toBe(false);
157
+ });
158
+ });
@@ -0,0 +1,58 @@
1
+ import { ChangeDetectorRef, Component, EventEmitter, Input, NgZone, Output, Inject, Optional } from '@angular/core';
2
+ import { IonicModule } from '@ionic/angular';
3
+ import { CommonModule } from '@angular/common';
4
+ import { ICameraProvider, CAMERA_PROVIDER } from '../../interfaces/camera-provider.interface';
5
+
6
+ @Component({
7
+ selector: 'lib-camera-permission',
8
+ standalone: true,
9
+ imports: [CommonModule, IonicModule],
10
+ templateUrl: './camera-permission.component.html',
11
+ styleUrls: ['./camera-permission.component.scss']
12
+ })
13
+ export class CameraPermissionComponent {
14
+ @Input() hideCancel = false;
15
+ @Input() poseSpecificMode = false;
16
+ @Output() permissionGranted = new EventEmitter<void>();
17
+ @Output() permissionDenied = new EventEmitter<void>();
18
+
19
+ constructor(
20
+ private cdr: ChangeDetectorRef,
21
+ private ngZone: NgZone,
22
+ @Optional() @Inject(CAMERA_PROVIDER) private cameraProvider: ICameraProvider | null
23
+ ) {}
24
+
25
+ async requestPermission(): Promise<void> {
26
+ try {
27
+ if (this.cameraProvider) {
28
+ const granted = await this.cameraProvider.requestPermissions();
29
+ this.ngZone.run(() => {
30
+ if (granted) {
31
+ this.permissionGranted.emit();
32
+ } else {
33
+ this.permissionDenied.emit();
34
+ }
35
+ });
36
+ } else {
37
+ const stream = await navigator.mediaDevices.getUserMedia({
38
+ video: { facingMode: 'user', width: { ideal: 1280 }, height: { ideal: 720 } },
39
+ audio: false
40
+ });
41
+
42
+ stream.getTracks().forEach(track => track.stop());
43
+ this.ngZone.run(() => {
44
+ this.permissionGranted.emit();
45
+ });
46
+ }
47
+ } catch (error) {
48
+ console.error('Camera permission denied:', error);
49
+ this.ngZone.run(() => {
50
+ this.permissionDenied.emit();
51
+ });
52
+ }
53
+ }
54
+
55
+ onLater(): void {
56
+ this.permissionDenied.emit();
57
+ }
58
+ }
@@ -0,0 +1,34 @@
1
+ <!-- Permission Step -->
2
+ <div *ngIf="currentStep === 'permission'" class="step-permission">
3
+ <ion-card>
4
+ <ion-card-header>
5
+ <ion-card-title>Camera Access Required</ion-card-title>
6
+ </ion-card-header>
7
+ <ion-card-content>
8
+ <p>Please allow camera access to continue with verification.</p>
9
+ <ion-button expand="block" (click)="requestCameraPermission()">
10
+ <ion-icon name="videocam" slot="start"></ion-icon>
11
+ Allow Camera
12
+ </ion-button>
13
+ <ion-button expand="block" fill="outline" (click)="onCancel()">
14
+ Cancel
15
+ </ion-button>
16
+ </ion-card-content>
17
+ </ion-card>
18
+ </div>
19
+
20
+ <!-- Preview Step -->
21
+ <div *ngIf="currentStep === 'preview'" class="step-preview">
22
+ <div class="camera-container">
23
+ <video #videoElement autoplay playsinline muted class="camera-video"></video>
24
+ <div class="overlay">
25
+ <div class="status-message">{{ statusMessage }}</div>
26
+ <div class="progress-bar">
27
+ <div class="progress-fill" [style.width.%]="matchProgress"></div>
28
+ </div>
29
+ </div>
30
+ </div>
31
+ <ion-button expand="block" fill="outline" (click)="onCancel()">
32
+ Cancel
33
+ </ion-button>
34
+ </div>
@@ -0,0 +1,210 @@
1
+ import { Component, Input, Output, EventEmitter, OnDestroy, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
2
+ import { CommonModule } from '@angular/common';
3
+ import { IonicModule } from '@ionic/angular';
4
+ import { CameraService } from '../../services/camera.service';
5
+ import { VideoRecorderService } from '../../services/video-recorder.service';
6
+ import { LivenessConfigService } from '../../services/liveness-config.service';
7
+ import { PoseDefinition } from '../../interfaces/pose-definition.interface';
8
+ import { LivenessVerificationConfig } from '../../interfaces/liveness-verification-config.interface';
9
+
10
+ export interface CapturedVerificationData {
11
+ frames: string[];
12
+ videoBlob?: Blob;
13
+ attemptNumber: number;
14
+ }
15
+
16
+ type CameraStep = 'permission' | 'preview';
17
+
18
+ @Component({
19
+ selector: 'facecog-camera-verification',
20
+ standalone: true,
21
+ imports: [CommonModule, IonicModule],
22
+ templateUrl: './camera-verification.component.html',
23
+ styles: [`
24
+ .camera-container {
25
+ position: relative;
26
+ width: 100%;
27
+ aspect-ratio: 4/3;
28
+ background: #000;
29
+ border-radius: 12px;
30
+ overflow: hidden;
31
+ margin-bottom: 16px;
32
+ }
33
+ .camera-video {
34
+ width: 100%;
35
+ height: 100%;
36
+ object-fit: cover;
37
+ transform: scaleX(-1);
38
+ }
39
+ .overlay {
40
+ position: absolute;
41
+ bottom: 0;
42
+ left: 0;
43
+ right: 0;
44
+ padding: 16px;
45
+ background: linear-gradient(transparent, rgba(0,0,0,0.7));
46
+ }
47
+ .status-message {
48
+ color: white;
49
+ text-align: center;
50
+ margin-bottom: 8px;
51
+ font-weight: 500;
52
+ }
53
+ .progress-bar {
54
+ height: 4px;
55
+ background: rgba(255,255,255,0.3);
56
+ border-radius: 2px;
57
+ overflow: hidden;
58
+ }
59
+ .progress-fill {
60
+ height: 100%;
61
+ background: var(--ion-color-success);
62
+ transition: width 0.3s ease;
63
+ }
64
+ `]
65
+ })
66
+ export class CameraVerificationComponent implements OnDestroy, AfterViewInit {
67
+ @ViewChild('videoElement') videoElementRef?: ElementRef<HTMLVideoElement>;
68
+
69
+ @Input() selectedPose?: PoseDefinition;
70
+ @Input() config?: Partial<LivenessVerificationConfig>;
71
+
72
+ @Output() verificationComplete = new EventEmitter<CapturedVerificationData>();
73
+ @Output() cancelled = new EventEmitter<void>();
74
+ @Output() error = new EventEmitter<Error>();
75
+
76
+ currentStep: CameraStep = 'permission';
77
+ statusMessage = 'Position yourself in the frame...';
78
+ matchProgress = 0;
79
+
80
+ private capturedFrames: string[] = [];
81
+ private videoBlob?: Blob;
82
+ private attemptCount = 0;
83
+ private matchingInterval?: ReturnType<typeof setInterval>;
84
+
85
+ constructor(
86
+ private cameraService: CameraService,
87
+ private videoRecorder: VideoRecorderService,
88
+ private configService: LivenessConfigService
89
+ ) {}
90
+
91
+ ngAfterViewInit(): void {
92
+ // Component is ready, wait for permission step
93
+ }
94
+
95
+ ngOnDestroy(): void {
96
+ this.cleanup();
97
+ }
98
+
99
+ async requestCameraPermission(): Promise<void> {
100
+ try {
101
+ const hasPermission = await this.cameraService.requestPermissions();
102
+ if (hasPermission) {
103
+ this.currentStep = 'preview';
104
+ setTimeout(() => this.startCameraPreview(), 100);
105
+ } else {
106
+ this.handleError(new Error('Camera permission denied'));
107
+ }
108
+ } catch (err) {
109
+ this.handleError(err as Error);
110
+ }
111
+ }
112
+
113
+ onCancel(): void {
114
+ this.cleanup();
115
+ this.cancelled.emit();
116
+ }
117
+
118
+ private async startCameraPreview(): Promise<void> {
119
+ const videoElement = this.videoElementRef?.nativeElement;
120
+ if (!videoElement) {
121
+ this.handleError(new Error('Video element not found'));
122
+ return;
123
+ }
124
+
125
+ try {
126
+ const started = await this.cameraService.startCamera(videoElement);
127
+ if (!started) {
128
+ this.handleError(new Error('Failed to start camera'));
129
+ return;
130
+ }
131
+
132
+ const config = this.configService.getConfig();
133
+ if (config.enableVideoRecording) {
134
+ const stream = this.cameraService.getStream();
135
+ if (stream) {
136
+ await this.videoRecorder.startRecording(stream);
137
+ }
138
+ }
139
+
140
+ this.simulatePoseMatching();
141
+ } catch (err) {
142
+ this.handleError(err as Error);
143
+ }
144
+ }
145
+
146
+ private simulatePoseMatching(): void {
147
+ let progress = 0;
148
+ this.matchingInterval = setInterval(() => {
149
+ progress += Math.random() * 10 + 5;
150
+ this.matchProgress = Math.min(progress, 100);
151
+ this.statusMessage = progress < 50
152
+ ? 'Position yourself in the frame...'
153
+ : progress < 80
154
+ ? 'Hold steady...'
155
+ : 'Almost there...';
156
+
157
+ if (progress >= 100) {
158
+ if (this.matchingInterval) {
159
+ clearInterval(this.matchingInterval);
160
+ }
161
+ this.captureAndComplete();
162
+ }
163
+ }, 500);
164
+ }
165
+
166
+ private async captureAndComplete(): Promise<void> {
167
+ const videoElement = this.videoElementRef?.nativeElement;
168
+ if (!videoElement) return;
169
+
170
+ this.capturedFrames = await this.cameraService.captureFrames(videoElement, 5, 100);
171
+
172
+ const config = this.configService.getConfig();
173
+ if (config.enableVideoRecording && this.videoRecorder.isRecording()) {
174
+ const recording = await this.videoRecorder.stopRecording();
175
+ this.videoBlob = recording.blob;
176
+ }
177
+
178
+ this.cameraService.stopCamera();
179
+ this.attemptCount++;
180
+
181
+ this.verificationComplete.emit({
182
+ frames: this.capturedFrames,
183
+ videoBlob: this.videoBlob,
184
+ attemptNumber: this.attemptCount
185
+ });
186
+ }
187
+
188
+ private handleError(err: Error): void {
189
+ console.error('[CameraVerification] Error:', err);
190
+ this.cleanup();
191
+ this.error.emit(err);
192
+ }
193
+
194
+ private cleanup(): void {
195
+ if (this.matchingInterval) {
196
+ clearInterval(this.matchingInterval);
197
+ }
198
+ this.cameraService.stopCamera();
199
+ this.videoRecorder.cleanup();
200
+ }
201
+
202
+ reset(): void {
203
+ this.cleanup();
204
+ this.currentStep = 'permission';
205
+ this.statusMessage = 'Position yourself in the frame...';
206
+ this.matchProgress = 0;
207
+ this.capturedFrames = [];
208
+ this.videoBlob = undefined;
209
+ }
210
+ }