Skip to content

Commit

Permalink
Device Motion Orientation (#263)
Browse files Browse the repository at this point in the history
* code formatting.

* revert max pitch threshold

* correct instructions copy

* use device motion to determine device orientation and fall back to unlocked device orientation if device motion is not available.

* code formatting

* update to new lottie animation designs.
  • Loading branch information
tobitech authored Dec 12, 2024
1 parent 304fe7f commit 8b7279e
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 66 deletions.
94 changes: 47 additions & 47 deletions Example/SmileID.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class LivenessCheckManager: ObservableObject {
/// The minimum threshold for pitch (up-down head movement)
private let minPitchAngleThreshold: CGFloat = 0.15
/// The maximum threshold for pitch (up-down head movement)
private let maxPitchAngleThreshold: CGFloat = 0.25
private let maxPitchAngleThreshold: CGFloat = 0.30
/// The timeout duration for each task in seconds.
private let taskTimeoutDuration: TimeInterval = 120

Expand Down
8 changes: 3 additions & 5 deletions Sources/SmileID/Classes/Networking/Models/v2/Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public struct Metadata: Codable {
.clientIP,
.fingerprint,
.deviceModel,
.deviceOS,
.deviceOS
])
}

Expand Down Expand Up @@ -170,17 +170,15 @@ func getIPAddress(useIPv4: Bool) -> String {
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) {
let name = String(cString: interface.ifa_name)
if name == "en0" || name == "en1" || name == "pdp_ip0"
|| name == "pdp_ip1" || name == "pdp_ip2" || name == "pdp_ip3"
{
|| name == "pdp_ip1" || name == "pdp_ip2" || name == "pdp_ip3" {
var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len),
&hostname, socklen_t(hostname.count),
nil, socklen_t(0), NI_NUMERICHOST)
address = String(cString: hostname)

if (useIPv4 && addrFamily == UInt8(AF_INET)) ||
(!useIPv4 && addrFamily == UInt8(AF_INET6))
{
(!useIPv4 && addrFamily == UInt8(AF_INET6)) {
if !useIPv4 {
if let percentIndex = address.firstIndex(of: "%") {
address = String(address[..<percentIndex]).uppercased()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ enum CaptureGuideAnimation: Equatable {
case .goodLight:
return "light_animation_with_bg"
case .headInFrame:
return "positioning"
return "positioning_with_bg"
case .moveBack:
return "positioning"
return "positioning_with_bg"
case .moveCloser:
return "positioning"
return "positioning_with_bg"
case .lookRight:
return "liveness_guides_with_bg"
return "headdirection_with_bg"
case .lookLeft:
return "liveness_guides_with_bg"
return "headdirection_with_bg"
case .lookUp:
return "liveness_guides_with_bg"
return "headdirection_with_bg"
case .turnPhoneUp:
return "device_orientation"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import ARKit
import CoreMotion
import Combine
import SwiftUI

public class EnhancedSmartSelfieViewModel: ObservableObject {
// MARK: Dependencies
private let motionManager = CMMotionManager()
let cameraManager = CameraManager.shared
let faceDetector = EnhancedFaceDetector()
private let faceValidator = FaceValidator()
Expand All @@ -13,8 +15,9 @@ public class EnhancedSmartSelfieViewModel: ObservableObject {
private let metadataTimerStart = MonotonicTime()

// MARK: Private Properties
private var deviceOrientation: UIDeviceOrientation {
return UIDevice.current.orientation
private var motionDeviceOrientation: UIDeviceOrientation = UIDevice.current.orientation
private var unlockedDeviceOrientation: UIDeviceOrientation {
UIDevice.current.orientation
}
private var faceLayoutGuideFrame = CGRect(x: 0, y: 0, width: 250, height: 350)
private var elapsedGuideAnimationDelay: TimeInterval = 0
Expand Down Expand Up @@ -109,6 +112,7 @@ public class EnhancedSmartSelfieViewModel: ObservableObject {
subscribers.removeAll()
stopGuideAnimationDelayTimer()
invalidateSubmissionTask()
motionManager.stopDeviceMotionUpdates()
}

private func initialSetup() {
Expand Down Expand Up @@ -153,13 +157,29 @@ public class EnhancedSmartSelfieViewModel: ObservableObject {
self?.handleCameraImageBuffer(imageBuffer)
}
.store(in: &subscribers)

if motionManager.isDeviceMotionAvailable {
motionManager.startDeviceMotionUpdates(to: OperationQueue()) {[weak self] deviceMotion, _ in
guard let gravity = deviceMotion?.gravity else { return }
if abs(gravity.y) < abs(gravity.x) {
self?.motionDeviceOrientation = gravity.x > 0 ? .landscapeRight : .landscapeLeft
} else {
self?.motionDeviceOrientation = gravity.y > 0 ? .portraitUpsideDown : .portrait
}
}
}
}

private func handleCameraImageBuffer(_ imageBuffer: CVPixelBuffer) {
if deviceOrientation == .portrait {
let currentOrientation: UIDeviceOrientation = motionManager.isDeviceMotionAvailable
? motionDeviceOrientation : unlockedDeviceOrientation
if currentOrientation == .portrait {
analyzeFrame(imageBuffer: imageBuffer)
} else {
publishUserInstruction(.turnPhoneUp)
DispatchQueue.main.async {
self.faceInBounds = false
self.publishUserInstruction(.turnPhoneUp)
}
}
}

Expand Down Expand Up @@ -205,7 +225,10 @@ extension EnhancedSmartSelfieViewModel {
elapsedGuideAnimationDelay = 0
showGuideAnimation = false
guard guideAnimationDelayTimer == nil else { return }
guideAnimationDelayTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
guideAnimationDelayTimer = Timer.scheduledTimer(
withTimeInterval: 1,
repeats: true
) { _ in
self.elapsedGuideAnimationDelay += 1
if self.elapsedGuideAnimationDelay == self.guideAnimationDelayTime {
self.showGuideAnimation = true
Expand Down
2 changes: 1 addition & 1 deletion Sources/SmileID/Classes/SmileID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ public class SmileID {
LocalStorage.getFileByType(jobId: jobId, fileType: .selfie),
LocalStorage.getFileByType(jobId: jobId, fileType: .documentFront),
LocalStorage.getFileByType(jobId: jobId, fileType: .documentBack),
LocalStorage.getInfoJsonFile(jobId: jobId),
LocalStorage.getInfoJsonFile(jobId: jobId)
].compactMap { $0 }
allFiles = livenessFiles + additionalFiles
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"Instructions.Quality" = "Move to well lit area and clear face of obstructions";
"Instructions.Brightness" = "Move to a well lit room";
"Instructions.Start" = "Put your face inside the oval frame and wait until it turns green";
"Instructions.SelfieCapture" = "Position your head in camera frame. \nThen move in the direction that is indicated.";
"Instructions.SelfieCapture" = "Position your head in the camera view. Then move in the direction that is indicated.";
"Instructions.PositionHeadInView" = "Position your head in view";
"Instructions.TurnHeadLeft" = "Turn your head to the left";
"Instructions.TurnHeadRight" = "Turn your head to the right";
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 8b7279e

Please sign in to comment.