@@ -0,0 +1,113 @@
4+open System.Threading.Tasks
10+type private ScanDelegate(tcs: TaskCompletionSource<string option>, session: AVCaptureSession) =
11+ inherit AVCaptureMetadataOutputObjectsDelegate()
13+ let mutable handled = false
15+ override _.DidOutputMetadataObjects(output, metadataObjects, connection) =
16+ if not handled && metadataObjects.Length > 0 then
17+ match metadataObjects.[0] with
18+ | :? AVMetadataMachineReadableCodeObject as code when code.Type = AVMetadataObjectType.QRCode ->
20+ session.StopRunning()
22+ DispatchQueue.MainQueue.DispatchAsync(fun () ->
23+ let window = UIApplication.SharedApplication.KeyWindow
25+ if not (isNull window) && not (isNull window.RootViewController) then
26+ window.RootViewController.DismissViewController(true, fun () ->
27+ tcs.TrySetResult(Some code.StringValue) |> ignore)
29+ tcs.TrySetResult(Some code.StringValue) |> ignore)
33+ let mutable private prevent_gc: obj = null
35+ let private presentScanner (tcs: TaskCompletionSource<string option>) =
36+ let session = new AVCaptureSession()
38+ match AVCaptureDevice.GetDefaultDevice(AVMediaTypes.Video) with
39+ | null -> tcs.TrySetResult None |> ignore
41+ let mutable error: NSError = null
42+ let input = AVCaptureDeviceInput.FromDevice(device, &error)
45+ tcs.TrySetResult None |> ignore
47+ session.AddInput(input)
49+ let output = new AVCaptureMetadataOutput()
50+ session.AddOutput(output)
52+ let del = new ScanDelegate(tcs, session)
53+ output.SetDelegate(del, DispatchQueue.MainQueue)
54+ output.MetadataObjectTypes <- AVMetadataObjectType.QRCode
56+ let preview = AVCaptureVideoPreviewLayer.FromSession(session)
57+ preview.VideoGravity <- AVLayerVideoGravity.ResizeAspectFill
58+ preview.Frame <- UIScreen.MainScreen.Bounds
60+ let vc = new UIViewController()
61+ vc.View.BackgroundColor <- UIColor.Black
62+ vc.View.Layer.AddSublayer(preview)
64+ let closeBtn = UIButton.FromType(UIButtonType.System)
65+ closeBtn.SetTitle("Cancel", UIControlState.Normal)
66+ closeBtn.SetTitleColor(UIColor.White, UIControlState.Normal)
67+ closeBtn.Frame <- CoreGraphics.CGRect(20., 60., 100., 44.)
69+ closeBtn.TouchUpInside.Add(fun _ ->
70+ session.StopRunning()
72+ vc.DismissViewController(true, fun () ->
73+ tcs.TrySetResult None |> ignore))
75+ vc.View.AddSubview(closeBtn)
77+ prevent_gc <- box (session, del, preview, vc)
79+ tcs.Task.ContinueWith(fun (_: Task<string option>) -> prevent_gc <- null)
82+ session.StartRunning()
84+ let window = UIApplication.SharedApplication.KeyWindow
86+ if not (isNull window) && not (isNull window.RootViewController) then
87+ vc.ModalPresentationStyle <- UIModalPresentationStyle.FullScreen
88+ window.RootViewController.PresentViewController(vc, true, null)
90+ session.StopRunning()
91+ tcs.TrySetResult None |> ignore
93+ let scan () : Task<string option> =
94+ let tcs = TaskCompletionSource<string option>()
96+ Avalonia.Threading.Dispatcher.UIThread.Post(fun () ->
97+ let status = AVCaptureDevice.GetAuthorizationStatus(AVAuthorizationMediaType.Video)
100+ | AVAuthorizationStatus.Denied
101+ | AVAuthorizationStatus.Restricted -> tcs.TrySetResult None |> ignore
102+ | AVAuthorizationStatus.NotDetermined ->
103+ AVCaptureDevice.RequestAccessForMediaType(
104+ AVAuthorizationMediaType.Video,
107+ DispatchQueue.MainQueue.DispatchAsync(fun () -> presentScanner tcs)
109+ tcs.TrySetResult None |> ignore
111+ | _ -> presentScanner tcs)