Scandit

Scandit. Integração em Xamarin Forms do SDK para a captura de dados a partir da câmara

O Scandit é um SDK para capturar códigos de barras e/ou texto da câmara do dispositivo. Está disponível para Android, iOS, Windows, Web, Cordova, Xamarin (Android, iOS e Forms), React Native, Flutter e outros SDK’s híbridos.

Reconhece praticamente todas as simbologias de códigos de barras existentes, tanto unidimensionais (EAN, Code128, UPCE...) como bidimensionais (QR, Pdf417, DataMatrix...).

Oferece a capacidade de detetar múltiplos códigos de barras num único frame com o seu modo Matrix Scan. Este último também nos permite desenhar sobreposições na interface da câmara, ou seja, a realidade aumentada.

O que precisamos?

  • Visual Studio (óbvio).
  • Destino API 21 (5.0 Lollipop) ou superior no nosso projeto Android.
  • Destino iOS 11.0 ou superior no nosso projeto iOS.
  • Uma licença Scandit.

Note-se que estas licenças variam de acordo com as funcionalidades requeridas pelo que numa dada licença, algumas características podem estar "indisponíveis".

Acrescentar os pacotes NuGet

Devem ser adicionados tanto ao projeto partilhado, como aos projetos nativos.

  • Scandit.DataCapture.Core.Xamarin.Forms
  • Scandit.DataCapture.Barcode.Xamarin.Forms

Criar um serviço de dependência

Consoante o teu projeto utilize um motor IoC, ou utilize DepedencyService, ou nenhum dos dois, a melhor implementação pode variar e pode requerer uma inversão de dependência, ou não. Seja qual for o caso, precisamos de um serviço para gerir internamente os objetos necessários para trabalhar com Scandit. Para simplificar, utilizaremos o Padrão Singleton neste exemplo, uma vez que necessitamos apenas de uma única instância.

public sealed class ScanditManager {
    private static readonly Lazy<ScanditManager> lazyInstance = new(
        () =>
new ScanditManager(),
        LazyThreadSafetyMode.PublicationOnly

    );

    public static ScanditManager Instance => lazyInstance.Value;
    private ScanditManager() {}
}

Nesta fase, temos de decidir se queremos que o Scandit utilize o Barcode Scanning (Detecção de código sequencial), o Matrix Scan (Detecção de n códigos de barras por frame), Matrix Scan + AR ou todos de uma só vez. Neste artigo abordamos apenas o Barcode Scanning, o mais básico, caso contrário ficaria demasiado extenso.

Implementar o Barcode Scanning

Os passos a seguir são:

  • Acrescentar uma propriedade DataCaptureContext.
  • Acrescentar uma propriedade Camera.
  • Acrescentar uma propriedade CameraSettings.
  • Acrescentar uma propriedade BarcodeCapture.
  • Acrescentar uma propriedade BarcodeCaptureSettings.
  • Definir as simbologias suportadas (quanto menos, melhor desempenho).
  • Iniciar Scandit.

Ficaríamos com algo assim:

public sealed class ScanditManager

{

    #region singleton stuff

    private static readonly Lazy<ScanditManager> lazyInstance = new(

        () => new ScanditManager(),

        LazyThreadSafetyMode.PublicationOnly

    );

    public static ScanditManager Instance => lazyInstance.Value;

    private ScanditManager() { }

    #endregion

    private readonly ISet<Symbology> symbologies = new HashSet<Symbology>

    {

        Symbology.Code128,

        Symbology.Gs1Databar,

        Symbology.Qr

    };

    public bool IsInitialized { get; private set; } = false;

    public DataCaptureContext Context { get; private set; }

    public Camera Camera { get; private set; } = Camera.GetCamera(CameraPosition.WorldFacing);

    public CameraSettings CameraSettings { get; } = BarcodeCapture.RecommendedCameraSettings;

    public BarcodeCapture BarcodeCapture { get; private set; }

    public BarcodeCaptureSettings BarcodeCaptureSettings { get; private set; }

    //Must be initialized before navigating a page with scandit dependency

    public async Task InitializeAsync(string scanditLicence)

    {

        Context = DataCaptureContext.ForLicenseKey(scanditLicence);

        await Context.SetFrameSourceAsync(Camera);

        BarcodeCaptureSettings = BarcodeCaptureSettings.Create();

        BarcodeCaptureSettings.CodeDuplicateFilter = TimeSpan.FromSeconds(4);

        BarcodeCaptureSettings.EnableSymbologies(symbologies);

        BarcodeCapture = BarcodeCapture.Create(Context, BarcodeCaptureSettings);

        BarcodeCapture.Enabled = false; //Starts disabled for performance purposes.

        BarcodeCapture.Feedback.Success = new Feedback(null, null); //No feedback for this example.

        IsInitialized = true;

    }

}

Agora vamos criar um ViewModel base para herdar quando precisarmos de uma vista que utilize Scandit. Precisamos que este ViewModel implemente o IbarcodeCaptureListener.

public class ScanditViewModelBase : YourViewModelBase, IBarcodeCaptureListener

{

    private static readonly Brush brush = new Brush(Color.Transparent, Color.White, 2);

    public Camera Camera => ScanditManager.Instance.Camera;

    public DataCaptureContext Context => ScanditManager.Instance.Context;

    public BarcodeCapture BarcodeCapture => ScanditManager.Instance.BarcodeCapture;

    protected virtual Brush Brush => brush;

    protected void StartCameraScanner()

    {

        BarcodeCapture.AddListener(this);

    }

    protected void StopCameraScanner()

    {

        BarcodeCapture.RemoveListener(this);

    }

    protected async Task<bool> StartCameraScannerReadingAsync()

    {

        PermissionStatus permissionStatus = await Permissions.CheckStatusAsync<Permissions.Camera>();

        bool readingStarted = false;

        if (permissionStatus != PermissionStatus.Granted)

        {

            permissionStatus = await Permissions.RequestAsync<Permissions.Camera>();

            if (permissionStatus == PermissionStatus.Granted)

                readingStarted = await Camera.SwitchToDesiredStateAsync(FrameSourceState.On);

        }

        else

        {

            readingStarted = await Camera.SwitchToDesiredStateAsync(FrameSourceState.On);

        }

        if (readingStarted)

            ScanditManager.Instance.BarcodeCapture.Enabled = true;

        return readingStarted;

    }

    public async Task<bool> StopCameraScannerReadingAsync()

    {

        ScanditManager.Instance.BarcodeCapture.Enabled = false;

        return await Camera.SwitchToDesiredStateAsync(FrameSourceState.Off);

    }

    protected void OnScanditBarcodeScanned(Barcode scannedBarcode) { }

    #region IBarcodeCaptureListener implementation

    public void OnObservationStarted(BarcodeCapture barcodeCapture) { }

    public void OnObservationStopped(BarcodeCapture barcodeCapture) { }

    public void OnSessionUpdated(

        BarcodeCapture barcodeCapture,

        BarcodeCaptureSession session,

        IFrameData frameData) {}

    public void OnBarcodeScanned(

        BarcodeCapture barcodeCapture,

        BarcodeCaptureSession session,

        IFrameData frameData)

    {

        if (session == null || !session.NewlyRecognizedBarcodes.Any())

            return;

        barcodeCapture.Enabled = false;

        Barcode scannedBarcode = session.NewlyRecognizedBarcodes.First();

        try

        {

            OnScanditBarcodeScanned(scannedBarcode);

        }

        finally

        {

            barcodeCapture.Enabled = true;

        }

    }

    #endregion

}

Criamos uma nova vista cujo ViewModel deve herdar do ViewModel base anterior. Criamos a sua ContentPage e definimos o seguinte no xaml:

<scanditCore:DataCaptureView

    DataCaptureContext="{Binding Context}" >

    <scanditBarcode:BarcodeCaptureOverlay

        BarcodeCapture="{Binding BarcodeCapture}"

        Brush="{Binding Brush}"/>

</scanditCore:DataCaptureView>

Não devemos esquecer de acrescentar a referência ao scanditCore e scanditBarcode:

xmlns:scanditCore="clr-namespace:Scandit.DataCapture.Core.UI.Unified;assembly=ScanditCaptureCoreUnified"

xmlns:scanditBarcode="clr-namespace:Scandit.DataCapture.Barcode.UI.Unified;assembly=ScanditBarcodeCaptureUnified"

Já só precisamos de escrever sobre OnScanditBarcodeScanned e invocar os métodos para iniciar/parar a câmara e a leitura quando é necessário. Não o incluo aqui, mas é interessante fazer isto quando a app passa para segundo plano ou volta para primeiro plano. Não te esqueças de iniciar o serviço de dependência com a licença scandit antes de entrar na vista.

O Scandit oferece grandes funcionalidades que são muito úteis para os clientes envolvidos em logística ou que, por qualquer razão, utilizam a leitura de códigos de barras na sua lógica de negócio.

Contudo, observa-se que a deteção de códigos não é tão eficiente quando as condições não são perfeitas (iluminação, sombras, etc.). Por exemplo, basta imprimir um código de barras em papel e comparar a sua deteção com o seu equivalente num monitor.

O Matrix Scan, que não tivemos tempo de abordar, é o grande valor do produto. Por exemplo, numa típica operação de descarga de encomendas, o Matrix Scan detetará todos os códigos de uma só vez e exibirá o feedback em tempo real, inclusive com realidade aumentada. Sem esta ferramenta, o utilizador teria de efetuar a leitura sequencial a laser de cada encomenda descarregada.

Como programadores C#, o uso do padrão listener torna-se raro, pois dispomos de eventos, mas o Scandit pretende melhorar isto em versões futuras.