Welcome to Software Development on Codidact!
Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.
Android CameraPreview not rendering for MLKit barcode analyis on MAUI custom control
I have a QR code which needs to read through Google's ML kit for Android since Zxing.MAUI doesnt read it. I followed the code in ml-kit/vision/barcode-scanning/android and VinayByte/mlkit-qr-code-scan-android-kotlin and also hjam40/Camera.MAUI I was not able to use Camera.MAUI since it doesnt work for .net 9
Here is my custom control code, if anyone has an idea why the preview is not showing up that would be a great help!
builder.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler<QRCodeReaderView, QRCodeReaderViewHandler>();
});
Custom control
public class QRCodeReaderView : View {}
public partial class QRCodeReaderViewHandler
{
public static IPropertyMapper<QRCodeReaderView, QRCodeReaderViewHandler> PropertyMapper = new PropertyMapper<QRCodeReaderView, QRCodeReaderViewHandler>(ViewMapper)
{
};
public static CommandMapper<QRCodeReaderView, QRCodeReaderViewHandler> CommandMapper = new CommandMapper<QRCodeReaderView, QRCodeReaderViewHandler>(ViewCommandMapper)
{
};
public QRCodeReaderViewHandler() : base(PropertyMapper, CommandMapper) { }
}
using System.Diagnostics;
using AndroidX.Camera.Core;
using AndroidX.Camera.Core.ResolutionSelector;
using AndroidX.Camera.Lifecycle;
using AndroidX.Camera.View;
using AndroidX.Core.Content;
using AndroidX.Lifecycle;
using Java.Lang;
using Java.Util.Concurrent;
using Microsoft.Maui.Handlers;
using Exception = System.Exception;
using Size = Android.Util.Size;
namespace QRCodeScanner;
public partial class QRCodeReaderViewHandler : ViewHandler<QRCodeReaderView, AndroidQRCodeReaderView>
{
private Preview? _cameraPreview;
private PreviewView _previewView;
private IExecutorService? cameraExecutor;
private ICamera? _camera;
protected override AndroidQRCodeReaderView CreatePlatformView()
{
var context = MauiContext?.Context ?? Platform.AppContext;
_previewView = new PreviewView(context);
return new AndroidQRCodeReaderView(context, _previewView);
}
protected override void ConnectHandler(AndroidQRCodeReaderView platformView)
{
base.ConnectHandler(platformView);
if (MauiContext is not null)
{
Connect(MauiContext, platformView.PreviewView);
}
}
public void Connect(IMauiContext mauiContext, PreviewView previewView)
{
try
{
if (mauiContext.Context is null)
{
return;
}
cameraExecutor = Executors.NewSingleThreadExecutor();
var cameraProviderFuture = ProcessCameraProvider.GetInstance(mauiContext.Context);
if (cameraExecutor is null)
return;
var cameraProvider = cameraProviderFuture.Get() as ProcessCameraProvider;
if (cameraProvider is null)
return;
cameraProviderFuture.AddListener(new Runnable(() =>
{
var resolutionSelector = new ResolutionSelector.Builder().SetResolutionStrategy(new ResolutionStrategy(boundSize: new Size(1280, 700),
fallbackRule: ResolutionStrategy.FallbackRuleClosestHigherThenLower))
.Build();
_cameraPreview = new Preview.Builder().SetResolutionSelector(resolutionSelector).Build();
_cameraPreview.SetSurfaceProvider(cameraExecutor, previewView.SurfaceProvider);
var imageAnalyzer = new ImageAnalysis.Builder()
.SetBackpressureStrategy(ImageAnalysis.StrategyKeepOnlyLatest)
.SetResolutionSelector(resolutionSelector)
.Build();
var mLkitQrCodeAnalyzer = new MLkitQRCodeAnalyzer();
imageAnalyzer.SetAnalyzer(cameraExecutor, mLkitQrCodeAnalyzer);
cameraProvider.UnbindAll();
var cameraSelector = CameraSelector.DefaultBackCamera;
var hasCamera = cameraProvider.HasCamera(cameraSelector);
Debug.WriteLine($"hasCamera: {hasCamera}");
if (previewView.Context is ILifecycleOwner lifecycleOwner)
{
_camera = cameraProvider.BindToLifecycle(lifecycleOwner, cameraSelector, _cameraPreview, imageAnalyzer);
}
else if (Platform.CurrentActivity is ILifecycleOwner maLifecycleOwner)
{
_camera = cameraProvider.BindToLifecycle(maLifecycleOwner, cameraSelector, _cameraPreview, imageAnalyzer);
}
}),
ContextCompat.GetMainExecutor(mauiContext.Context));
}
catch (Exception ex)
{
Debug.WriteLine(ex.StackTrace);
}
}
}
Camera permissions
<uses-permission android:name="android.permission.CAMERA"/>
private async Task CheckAndRequestCameraPermission()
{
var status = await Permissions.CheckStatusAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
{
status = await Permissions.RequestAsync<Permissions.Camera>();
if (status != PermissionStatus.Granted)
{
await DisplayAlert("Camera Permission Denied", "This app needs access to the camera to scan QR codes.", "OK");
}
}
}
protected override async void OnAppearing()
{
base.OnAppearing();
await CheckAndRequestCameraPermission();
}
UI
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:local="clr-namespace:QRCodeScanner"
x:DataType="{x:Type local:MainViewModel}"
x:Class="QRCodeScanner.MainPage">
<ContentPage.Resources>
<toolkit:BoolToObjectConverter x:TypeArguments="x:String"
x:Key="BoolToStringConverter"
TrueObject="On"
FalseObject="Off" />
</ContentPage.Resources>
<VerticalStackLayout Spacing="10"
Padding="20">
<Label Text="Below is the scanner"
HorizontalOptions="Center" />
<local:QRCodeReaderView HorizontalOptions="Fill"
VerticalOptions="Fill"
HeightRequest="400"
WidthRequest="400"
ResultCommand="{Binding QRResultCommand}"
IsFlashOn="{Binding IsFlashOn}"
IsVisible="{Binding IsVisible}"
x:Name="qrCodeReaderView" />
<HorizontalStackLayout HorizontalOptions="Center"
Spacing="20"
IsVisible="{Binding IsVisible}">
<Label Text="{Binding IsFlashOn, Source={Reference qrCodeReaderView}, Converter={StaticResource BoolToStringConverter}, StringFormat='Flash {0}'}"
VerticalOptions="Center"
VerticalTextAlignment="Center" />
<Switch IsToggled="{Binding IsFlashOn}" />
</HorizontalStackLayout>
<Label HorizontalOptions="Center"
VerticalOptions="End"
Margin="0,0,0,20"
Text="{Binding Result}" />
</VerticalStackLayout>
</ContentPage>
Edit:
On further analysis through Logcat I found the issue to be as following
Unable to configure camera Camera@461d7c2[id=0]
java.util.concurrent.TimeoutException: Future[androidx.camera.core.impl.utils.futures.ListFuture@4c0973e] is not done within 5000 ms.
The possible solution to set android:hardwareAccelerated="true"
application level or MainActivity level is not working too.
1 answer
Answering my own question in case anyone else stumbles upon this.
Changing native platform implementation from Android.Views.View
to AndroidX.CoordinatorLayout.Widget.CoordinatorLayout
like following fixed the issue of preview not showing.
protected override AndroidQRCodeReaderView CreatePlatformView()
{
var context = MauiContext?.Context ?? Platform.AppContext;
_previewView = new PreviewView(context)
{
LayoutParameters = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent)
};
var _relativeLayout = new RelativeLayout(context)
{
LayoutParameters = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.MatchParent)
};
_relativeLayout.AddView(_previewView);
var platformview = new AndroidQRCodeReaderView(context);
platformview.AddView(_relativeLayout);
return platformview;
}
Reference: github/afriscic/BarcodeScanning.Native.Maui
0 comment threads