CameraManager.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. using Android;
  2. using Android.Content;
  3. using Android.Graphics;
  4. using AndroidX.Camera.Core;
  5. using AndroidX.Camera.Core.ResolutionSelector;
  6. using AndroidX.Camera.View;
  7. using AndroidX.Camera.View.Transform;
  8. using AndroidX.Core.Content;
  9. using AndroidX.Lifecycle;
  10. using Avalonia.Media;
  11. using Avalonia.Threading;
  12. using InABox.Avalonia.Platform.Barcodes;
  13. using Java.Util.Concurrent;
  14. using Microsoft.Maui.Devices;
  15. using static Android.Views.ViewGroup;
  16. using Color = Android.Graphics.Color;
  17. using AvColor = Avalonia.Media.Color;
  18. using Avalonia;
  19. namespace InABox.Avalonia.Platform.Android.Barcodes;
  20. internal class CameraManager : IDisposable
  21. {
  22. internal BarcodeView BarcodeView { get => _barcodeView; }
  23. internal CameraView? CameraView { get => _cameraView; }
  24. internal PreviewView PreviewView { get => _previewView; }
  25. internal CameraState? OpenedCameraState { get; set; }
  26. private readonly BarcodeAnalyzer _barcodeAnalyzer;
  27. private readonly BarcodeView _barcodeView;
  28. private readonly Context _context;
  29. private readonly IExecutorService _analyzerExecutor;
  30. private readonly ImageView _imageView;
  31. private readonly LifecycleCameraController _cameraController;
  32. private readonly ILifecycleOwner _lifecycleOwner;
  33. private readonly PreviewView _previewView;
  34. private readonly RelativeLayout _relativeLayout;
  35. private readonly CameraStateObserver _cameraStateObserver;
  36. private readonly CameraView? _cameraView;
  37. // Caches of CameraView properties to avoid calling on non-main threads.
  38. internal bool _pauseScanning;
  39. internal bool _forceInverted;
  40. internal bool _forceFrameCapture;
  41. internal bool _captureNextFrame;
  42. internal bool _aimMode;
  43. internal bool _viewFinderMode;
  44. private ICameraInfo? _currentCameraInfo;
  45. private const int aimRadius = 25;
  46. static CameraManager()
  47. {
  48. }
  49. internal CameraManager(CameraView cameraView, Context context)
  50. {
  51. _context = context;
  52. _cameraView = cameraView;
  53. ILifecycleOwner? owner = null;
  54. if (_context is ILifecycleOwner)
  55. owner = _context as ILifecycleOwner;
  56. else if ((_context as ContextWrapper)?.BaseContext is ILifecycleOwner)
  57. owner = (_context as ContextWrapper)?.BaseContext as ILifecycleOwner;
  58. // else if (Platform.CurrentActivity is ILifecycleOwner)
  59. // owner = Platform.CurrentActivity as ILifecycleOwner;
  60. var executor = Executors.NewSingleThreadExecutor();
  61. ArgumentNullException.ThrowIfNull(owner);
  62. ArgumentNullException.ThrowIfNull(executor);
  63. _lifecycleOwner = owner;
  64. _analyzerExecutor = executor;
  65. _barcodeAnalyzer = new BarcodeAnalyzer(this);
  66. _cameraStateObserver = new CameraStateObserver(this, _cameraView);
  67. _cameraController = new LifecycleCameraController(_context)
  68. {
  69. TapToFocusEnabled = _cameraView?.TapToFocusEnabled ?? false,
  70. ImageAnalysisBackpressureStrategy = ImageAnalysis.StrategyKeepOnlyLatest
  71. };
  72. _cameraController.SetEnabledUseCases(CameraController.ImageAnalysis);
  73. _cameraController.ZoomState.ObserveForever(_cameraStateObserver);
  74. _cameraController.InitializationFuture.AddListener(new Java.Lang.Runnable(() =>
  75. {
  76. _currentCameraInfo?.CameraState.RemoveObserver(_cameraStateObserver);
  77. _currentCameraInfo = _cameraController.CameraInfo;
  78. _currentCameraInfo?.CameraState.ObserveForever(_cameraStateObserver);
  79. }), ContextCompat.GetMainExecutor(_context));
  80. ArgumentNullException.ThrowIfNull(PreviewView.ImplementationMode.Compatible);
  81. ArgumentNullException.ThrowIfNull(PreviewView.ScaleType.FillCenter);
  82. ArgumentNullException.ThrowIfNull(Bitmap.Config.Argb8888);
  83. _previewView = new PreviewView(_context)
  84. {
  85. LayoutParameters = new RelativeLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent)
  86. };
  87. var background = (_cameraView?.Background as SolidColorBrush)?.Color ?? Colors.Transparent;
  88. _previewView.SetBackgroundColor(new Color(background.R, background.G, background.B, background.A));
  89. _previewView.SetImplementationMode(PreviewView.ImplementationMode.Compatible);
  90. _previewView.SetScaleType(PreviewView.ScaleType.FillCenter);
  91. using var layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WrapContent, LayoutParams.WrapContent);
  92. layoutParams.AddRule(LayoutRules.CenterInParent);
  93. using var circleBitmap = Bitmap.CreateBitmap(2 * aimRadius, 2 * aimRadius, Bitmap.Config.Argb8888);
  94. using var canvas = new Canvas(circleBitmap);
  95. canvas.DrawCircle(aimRadius, aimRadius, aimRadius, new Paint
  96. {
  97. AntiAlias = true,
  98. Color = Color.Red,
  99. Alpha = 150
  100. });
  101. _imageView = new ImageView(_context)
  102. {
  103. LayoutParameters = layoutParams
  104. };
  105. _imageView.SetImageBitmap(circleBitmap);
  106. _relativeLayout = new RelativeLayout(_context)
  107. {
  108. LayoutParameters = new RelativeLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent)
  109. };
  110. _relativeLayout.AddView(_previewView);
  111. _barcodeView = new BarcodeView(_context);
  112. _barcodeView.AddView(_relativeLayout);
  113. DeviceDisplay.Current.MainDisplayInfoChanged += MainDisplayInfoChangedAsync;
  114. }
  115. internal void Start()
  116. {
  117. if (_previewView is not null)
  118. _previewView.Controller = null;
  119. if (OpenedCameraState?.GetType() != CameraState.Type.Closed)
  120. _cameraController?.Unbind();
  121. _cameraController?.ClearImageAnalysisAnalyzer();
  122. if (_barcodeAnalyzer is not null && _analyzerExecutor is not null)
  123. _cameraController?.SetImageAnalysisAnalyzer(_analyzerExecutor, _barcodeAnalyzer);
  124. UpdateResolution();
  125. UpdateCamera();
  126. UpdateSymbologies();
  127. UpdateTorch();
  128. if (_lifecycleOwner is not null)
  129. _cameraController?.BindToLifecycle(_lifecycleOwner);
  130. if (_previewView is not null && _cameraController is not null)
  131. _previewView.Controller = _cameraController;
  132. }
  133. internal void Stop()
  134. {
  135. _cameraController?.Unbind();
  136. }
  137. internal void UpdateAimMode()
  138. {
  139. if (_aimMode)
  140. _relativeLayout?.AddView(_imageView);
  141. else
  142. _relativeLayout?.RemoveView(_imageView);
  143. }
  144. internal void UpdateBackgroundColor()
  145. {
  146. var background = (_cameraView?.Background as SolidColorBrush)?.Color ?? Colors.Transparent;
  147. _previewView.SetBackgroundColor(new Color(background.R, background.G, background.B, background.A));
  148. }
  149. internal void UpdateCamera()
  150. {
  151. if (_cameraController is not null)
  152. {
  153. if (_cameraView?.CameraFacing == CameraFacing.Front)
  154. _cameraController.CameraSelector = CameraSelector.DefaultFrontCamera;
  155. else
  156. _cameraController.CameraSelector = CameraSelector.DefaultBackCamera;
  157. }
  158. }
  159. internal void UpdateCameraEnabled()
  160. {
  161. if (_cameraView?.CameraEnabled ?? false)
  162. Start();
  163. else
  164. Stop();
  165. }
  166. internal void UpdateResolution()
  167. {
  168. using var analysisStrategy = new ResolutionStrategy(Methods.TargetResolution(_cameraView?.CaptureQuality), ResolutionStrategy.FallbackRuleClosestHigherThenLower);
  169. using var resolutionBuilder = new ResolutionSelector.Builder();
  170. resolutionBuilder.SetAllowedResolutionMode(ResolutionSelector.PreferHigherResolutionOverCaptureRate);
  171. resolutionBuilder.SetResolutionStrategy(analysisStrategy);
  172. resolutionBuilder.SetAspectRatioStrategy(AspectRatioStrategy.Ratio169FallbackAutoStrategy);
  173. var selector = resolutionBuilder.Build();
  174. if (_cameraController is not null)
  175. {
  176. _cameraController.ImageAnalysisResolutionSelector = selector;
  177. _cameraController.PreviewResolutionSelector = selector;
  178. }
  179. }
  180. internal void UpdateSymbologies()
  181. {
  182. _barcodeAnalyzer?.UpdateSymbologies();
  183. }
  184. internal void UpdateTapToFocus()
  185. {
  186. if (_cameraController is not null)
  187. _cameraController.TapToFocusEnabled = _cameraView?.TapToFocusEnabled ?? false;
  188. }
  189. internal void UpdateTorch()
  190. {
  191. _cameraController?.EnableTorch(_cameraView?.TorchOn ?? false);
  192. }
  193. internal void UpdateVibration()
  194. {
  195. // if ((_cameraView?.VibrationOnDetected ?? false) &&
  196. // !Permissions.IsDeclaredInManifest(Manifest.Permission.Vibrate))
  197. // _cameraView.VibrationOnDetected = false;
  198. }
  199. internal void UpdateZoomFactor()
  200. {
  201. if (_cameraView is not null && (_cameraController?.ZoomState?.IsInitialized ?? false))
  202. {
  203. var factor = _cameraView.RequestZoomFactor;
  204. if (factor > 0)
  205. {
  206. factor = Math.Max(factor, _cameraView.MinZoomFactor);
  207. factor = Math.Min(factor, _cameraView.MaxZoomFactor);
  208. if (factor != _cameraView.CurrentZoomFactor)
  209. _cameraController.SetZoomRatio(factor);
  210. }
  211. }
  212. }
  213. internal CoordinateTransform? GetCoordinateTransform(IImageProxy proxy)
  214. {
  215. var imageOutputTransform = new ImageProxyTransformFactory().GetOutputTransform(proxy);
  216. var previewOutputTransform = Dispatcher.UIThread.InvokeAsync(() => _previewView?.OutputTransform).GetAwaiter().GetResult();
  217. if (imageOutputTransform is not null && previewOutputTransform is not null)
  218. return new CoordinateTransform(imageOutputTransform, previewOutputTransform);
  219. else
  220. return null;
  221. }
  222. private async void MainDisplayInfoChangedAsync(object? sender, DisplayInfoChangedEventArgs e)
  223. {
  224. if (OpenedCameraState?.GetType() == CameraState.Type.Open)
  225. {
  226. if (_previewView is not null)
  227. _previewView.Controller = null;
  228. await Task.Delay(100);
  229. if (_previewView is not null)
  230. _previewView.Controller = _cameraController;
  231. }
  232. }
  233. public void Dispose()
  234. {
  235. Dispose(true);
  236. GC.SuppressFinalize(this);
  237. }
  238. protected virtual void Dispose(bool disposing)
  239. {
  240. if (disposing)
  241. {
  242. Stop();
  243. DeviceDisplay.Current.MainDisplayInfoChanged -= MainDisplayInfoChangedAsync;
  244. if (_cameraStateObserver is not null)
  245. {
  246. _cameraController?.ZoomState.RemoveObserver(_cameraStateObserver);
  247. _currentCameraInfo?.CameraState.RemoveObserver(_cameraStateObserver);
  248. }
  249. _cameraController?.ClearImageAnalysisAnalyzer();
  250. _barcodeView?.RemoveAllViews();
  251. _relativeLayout?.RemoveAllViews();
  252. _barcodeView?.Dispose();
  253. _relativeLayout?.Dispose();
  254. _imageView?.Dispose();
  255. _previewView?.Dispose();
  256. _cameraController?.Dispose();
  257. _currentCameraInfo?.Dispose();
  258. _cameraStateObserver?.Dispose();
  259. _barcodeAnalyzer?.Dispose();
  260. _analyzerExecutor?.Dispose();
  261. }
  262. }
  263. }