Implementing the camera in AL

You can access the camera of a device from the Business Central Web client in the browser and from the Business Central Mobile App. This allows the user to take pictures and handle them directly from the same device, and in that way, improve accuracy of capturing data closest to the source, and reduce end-to-end time to perform tasks.

You can also add access to the camera to a specific page from the AL Language development environment. For a Dynamics 365 Business Central existing implementation of this, see the Picture factbox on the Item Card, which lets you take a picture of a specific item and store it together with the item.

Important

The camera access is only available on devices that have a camera.

Example

This example illustrates how to implement the camera capability on a page in AL. The example implements three actions to take a picture: Take Picture, Take Picture High Quality, and Take Picture Low Quality. However, it does not include code that saves the picture to the database.

The example also shows how to specify options for the camera functionality such as picture quality or source type. For more information about the different options that can be set for the camera, see CameraOptions Overview.

Note

To enable the camera functionality, it is required that you add the path of the folder containing the "Microsoft.Dynamics.Nav.ClientExtensions" assembly on the Al: Assembly Probing Paths setting on the User Settings or Workspace Settings so the compiler can access it. For more information, see Getting started with Microsoft .NET Interoperability from AL.

The following code will create two variables; the CameraAvailable variable is a Boolean that checks whether the current device has a camera. The Camera variable is a DotNet type that gets instantiated by adding code to the OnOpenPage trigger. Then, it will add the actions to the page that lets the user start the camera. Finally, the trigger Camera::PictureAvailable is defined to handle the incoming picture.

page 50101 "Card with Camera Capability"
{

    Caption = 'Card Page';
    PageType = Card;
    RefreshOnActivate = true;
    SourceTable = "Test Table";

    layout
    {
        area(content)
        {
            //...
        }
    }

    actions
    {
        
        area(Processing)
        {
            action(TakePicture)
            {
                Visible = CameraAvailable;
                Promoted = true;
                PromotedCategory = Process;
                PromotedIsBig = true;
                Image = Camera;

                trigger OnAction()
                begin
                    Camera.RequestPictureAsync();
                end;
            }

            action(TakePictureHigh)
            {
                Visible = CameraAvailable;
                Promoted = true;
                PromotedCategory = Process;
                PromotedIsBig = true;
                Image = Camera;

                trigger OnAction()
                begin
                    CameraOptions := CameraOptions.CameraOptions();
                    CameraOptions.Quality := 100;
                    Camera.RequestPictureAsync(CameraOptions);
                end;
            }

            action(TakePictureLow)
            {
                Visible = CameraAvailable;
                Promoted = true;
                PromotedCategory = Process;
                PromotedIsBig = true;
                Image = Camera;

                trigger OnAction()
                begin
                    CameraOptions := CameraOptions.CameraOptions();
                    CameraOptions.Quality := 10;
                    Camera.RequestPictureAsync(CameraOptions);
                end;
            }
        }

    }

    trigger OnOpenPage()
    begin
        // The IsAvailable() enables the camera functionality based on its presence.
        if Camera.IsAvailable() then begin
            Camera := Camera.Create();
            CameraAvailable := True;
        end;
    end;

    // The PictureName contains the name of the file including its extension on the device. 
    // The naming scheme depends on the device platform. 
    // The PictureFilePath contains the path to the picture in a temporary folder on the server for the current user.
    // The PictureAvailable trigger handles the picture for when the camera has captured it and it has been uploaded.
    trigger Camera::PictureAvailable(PictureName: Text; PictureFilePath: Text) 
    begin
        IncomingFile.Open(PictureFilePath);
        Message('Picture size: %1', IncomingFile.Len());
        IncomingFile.Close();
        // It is important to clean up by using the File.Erase command to avoid accumulating image files.
        File.Erase(PictureFilePath);
    end;

    var
        [RunOnClient]
        [WithEvents]
        Camera: DotNet UT_CameraProvider;
        CameraOptions: DotNet UT_CameraOptions;
        // Checks whether the current device has a camera.
        CameraAvailable: Boolean;
        IncomingFile: File;
}

dotnet
{
    assembly("Microsoft.Dynamics.Nav.ClientExtensions")
    {

        type("Microsoft.Dynamics.Nav.Client.Capabilities.CameraProvider"; "UT_CameraProvider")
        {

        }

        type("Microsoft.Dynamics.Nav.Client.Capabilities.CameraOptions"; "UT_CameraOptions")
        {

        }
    }
}

For information about troubleshooting access to camera, see Troubleshooting: Camera and Location.

Getting started with Microsoft .NET Interoperability from AL
Implementing Location in AL
CameraOptions Overview
RunOnClient property
WithEvents property