CameraManager.cs 11 KB

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