Java Question No implementation found

DonManfred

Expert
Licensed User
Longtime User
I am doing a wrap for the Scandit SDK which came with several AARs. Based on their example code i did created a wrapper for the - in my eyes - needed Objects.

Java:
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.scandit.datacapture.barcodecapturesimplesample;

import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import com.scandit.datacapture.barcode.capture.*;
import com.scandit.datacapture.barcode.data.Barcode;
import com.scandit.datacapture.barcode.data.Symbology;
import com.scandit.datacapture.barcode.data.SymbologyDescription;
import com.scandit.datacapture.barcode.ui.overlay.BarcodeCaptureOverlay;
import com.scandit.datacapture.core.capture.DataCaptureContext;
import com.scandit.datacapture.core.data.FrameData;
import com.scandit.datacapture.core.source.Camera;
import com.scandit.datacapture.core.source.FrameSourceState;
import com.scandit.datacapture.core.ui.DataCaptureView;
import com.scandit.datacapture.core.ui.viewfinder.RectangularViewfinder;

import java.util.Arrays;
import java.util.HashSet;

import static android.content.DialogInterface.OnClickListener;

public class BarcodeScanActivity
        extends CameraPermissionActivity implements BarcodeCaptureListener {

    // Enter your Scandit License key here.
    // Your Scandit License key is available via your Scandit SDK web account.
    public static final String SCANDIT_LICENSE_KEY = "-- ENTER YOUR SCANDIT LICENSE KEY HERE --";

    private DataCaptureContext dataCaptureContext;
    private BarcodeCapture barcodeCapture;
    private Camera camera;
    private DataCaptureView dataCaptureView;

    private AlertDialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Initialize and start the barcode recognition.
        initializeAndStartBarcodeScanning();
    }

    private void initializeAndStartBarcodeScanning() {
        // Create data capture context using your license key.
        dataCaptureContext = DataCaptureContext.forLicenseKey(SCANDIT_LICENSE_KEY);

        // Use the default camera with the recommended camera settings for the BarcodeCapture mode
        // and set it as the frame source of the context. The camera is off by default and must be
        // turned on to start streaming frames to the data capture context for recognition.
        // See resumeFrameSource and pauseFrameSource below.
        camera = Camera.getDefaultCamera(BarcodeCapture.createRecommendedCameraSettings());
        if (camera != null) {
            dataCaptureContext.setFrameSource(camera);
        } else {
            throw new IllegalStateException("Sample depends on a camera, which failed to initialize.");
        }

        // The barcode capturing process is configured through barcode capture settings
        // which are then applied to the barcode capture instance that manages barcode recognition.
        BarcodeCaptureSettings barcodeCaptureSettings = new BarcodeCaptureSettings();

        // The settings instance initially has all types of barcodes (symbologies) disabled.
        // For the purpose of this sample we enable a very generous set of symbologies.
        // In your own app ensure that you only enable the symbologies that your app requires as
        // every additional enabled symbology has an impact on processing times.
        HashSet<Symbology> symbologies = new HashSet<>();
        symbologies.add(Symbology.EAN13_UPCA);
        symbologies.add(Symbology.EAN8);
        symbologies.add(Symbology.UPCE);
        symbologies.add(Symbology.QR);
        symbologies.add(Symbology.DATA_MATRIX);
        symbologies.add(Symbology.CODE39);
        symbologies.add(Symbology.CODE128);
        symbologies.add(Symbology.INTERLEAVED_TWO_OF_FIVE);

        barcodeCaptureSettings.enableSymbologies(symbologies);

        // Some linear/1d barcode symbologies allow you to encode variable-length data.
        // By default, the Scandit Data Capture SDK only scans barcodes in a certain length range.
        // If your application requires scanning of one of these symbologies, and the length is
        // falling outside the default range, you may need to adjust the "active symbol counts"
        // for this symbology. This is shown in the following few lines of code for one of the
        // variable-length symbologies.
        SymbologySettings symbologySettings =
                barcodeCaptureSettings.getSymbologySettings(Symbology.CODE39);

        HashSet<Short> activeSymbolCounts = new HashSet<>(
                Arrays.asList(new Short[] { 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 }));

        symbologySettings.setActiveSymbolCounts(activeSymbolCounts);

        // Create new barcode capture mode with the settings from above.
        barcodeCapture = BarcodeCapture.forDataCaptureContext(dataCaptureContext, barcodeCaptureSettings);

        // Register self as a listener to get informed whenever a new barcode got recognized.
        barcodeCapture.addListener(this);

        // To visualize the on-going barcode capturing process on screen, setup a data capture view
        // that renders the camera preview. The view must be connected to the data capture context.
        dataCaptureView = DataCaptureView.newInstance(this, dataCaptureContext);

        // Add a barcode capture overlay to the data capture view to render the location of captured
        // barcodes on top of the video preview.
        // This is optional, but recommended for better visual feedback.
        BarcodeCaptureOverlay overlay = BarcodeCaptureOverlay.newInstance(barcodeCapture, dataCaptureView);
        overlay.setViewfinder(new RectangularViewfinder());

        setContentView(dataCaptureView);
    }

    @Override
    protected void onPause() {
        pauseFrameSource();
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        barcodeCapture.removeListener(this);
        dataCaptureContext.removeMode(barcodeCapture);
        super.onDestroy();
    }

    private void pauseFrameSource() {
        // Switch camera off to stop streaming frames.
        // The camera is stopped asynchronously and will take some time to completely turn off.
        // Until it is completely stopped, it is still possible to receive further results, hence
        // it's a good idea to first disable barcode capture as well.
        barcodeCapture.setEnabled(false);
        camera.switchToDesiredState(FrameSourceState.OFF, null);
    }

    @Override
    protected void onResume() {
        super.onResume();

        // Check for camera permission and request it, if it hasn't yet been granted.
        // Once we have the permission the onCameraPermissionGranted() method will be called.
        requestCameraPermission();
    }

    @Override
    public void onCameraPermissionGranted() {
        resumeFrameSource();
    }

    private void resumeFrameSource() {
        dismissScannedCodesDialog();

        // Switch camera on to start streaming frames.
        // The camera is started asynchronously and will take some time to completely turn on.
        barcodeCapture.setEnabled(true);
        camera.switchToDesiredState(FrameSourceState.ON, null);
    }

    private void dismissScannedCodesDialog() {
        if (dialog != null) {
            dialog.dismiss();
            dialog = null;
        }
    }

    private void showResult(String result) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        dialog = builder.setCancelable(false)
                .setTitle(result)
                .setPositiveButton(android.R.string.ok,
                        new OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                barcodeCapture.setEnabled(true);
                            }
                        })
                .create();
        dialog.show();
    }

    @Override
    public void onBarcodeScanned(
            @NonNull BarcodeCapture barcodeCapture,
            @NonNull BarcodeCaptureSession session,
            @NonNull FrameData frameData
    ) {
        if (session.getNewlyRecognizedBarcodes().isEmpty()) return;

        Barcode barcode = session.getNewlyRecognizedBarcodes().get(0);

        // Stop recognizing barcodes for as long as we are displaying the result. There won't be any
        // new results until the capture mode is enabled again. Note that disabling the capture mode
        // does not stop the camera, the camera continues to stream frames until it is turned off.
        barcodeCapture.setEnabled(false);

        // If you are not disabling barcode capture here and want to continue scanning, consider
        // setting the codeDuplicateFilter when creating the barcode capture settings to around 500
        // or even -1 if you do not want codes to be scanned more than once.

        // Get the human readable name of the symbology and assemble the result to be shown.
        String symbology = SymbologyDescription.create(barcode.getSymbology()).getReadableName();
        final String result = "Scanned: " + barcode.getData() + " (" + symbology + ")";

        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                showResult(result);
            }
        });
    }

    @Override
    public void onSessionUpdated(@NonNull BarcodeCapture barcodeCapture,
            @NonNull BarcodeCaptureSession session, @NonNull FrameData data) {}

    @Override
    public void onObservationStarted(@NonNull BarcodeCapture barcodeCapture) {}

    @Override
    public void onObservationStopped(@NonNull BarcodeCapture barcodeCapture) {}
}
Basically one need to start with a simple object
B4X:
BarcodeCaptureSettings barcodeCaptureSettings = new BarcodeCaptureSettings();
which i did in my wrapper.
Even like this but even in a AbsObjectWrapper it crashes with

Service started in the background. Trying to start again in foreground mode.
*** Service (starter) Create ***
** Service (starter) Start **
Service started in foreground mode.
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
main$ResumableSub_Activity_Createresume (java line: 391)
java.lang.UnsatisfiedLinkError: No implementation found for com.scandit.datacapture.core.internal.sdk.capture.NativeDataCaptureContextSettings com.scandit.datacapture.core.internal.sdk.capture.NativeDataCaptureContextSettings$CppProxy.create() (tried Java_com_scandit_datacapture_core_internal_sdk_capture_NativeDataCaptureContextSettings_00024CppProxy_create and Java_com_scandit_datacapture_core_internal_sdk_capture_NativeDataCaptureContextSettings_00024CppProxy_create__)
at com.scandit.datacapture.core.internal.sdk.capture.NativeDataCaptureContextSettings$CppProxy.create(Native Method)
at com.scandit.datacapture.core.internal.sdk.capture.NativeDataCaptureContextSettings.create(SourceFile:33)
at com.scandit.datacapture.core.capture.DataCaptureContextSettings.<init>(SourceFile:33)
at com.scandit.datacapture.core.capture.DataCaptureContextBuilder.<init>(SourceFile:13)
at de.donmanfred.DataCaptureContextBuilderwrapper.Initialize(DataCaptureContextBuilderwrapper.java:47)
at de.donmanfred.scanditsdktest.main$ResumableSub_Activity_Create.resume(main.java:391)
at anywheresoftware.b4a.BA.checkAndRunWaitForEvent(BA.java:267)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:207)
at anywheresoftware.b4a.BA$2.run(BA.java:387)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:241)
at android.app.ActivityThread.main(ActivityThread.java:7582)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:941)

the provided example comes with a gradle script (parts of it)
dependencies {
api(name:'ScanditCaptureCore', ext:'aar')
api(name:'ScanditBarcodeCapture', ext:'aar')

implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"

implementation "androidx.appcompat:appcompat:${versions.androidx_appcompat}"
implementation "com.google.android.material:material:${versions.material}"

implementation "androidx.constraintlayout:constraintlayout:${versions.androidx_constraintlayout}"

implementation "com.squareup.okhttp3:eek:khttp:${versions.okhttp}"
}

Do i need to implement some code in my wrapper for this?

Or is it still something stupid like a missing dependy i forget to add?
ScanditCaptureCore.aar and ScanditBarcodeCapture.aar are added via #additionaljar
Even Kotlin-Library

Every hint into the right direction will be helpful ;-)

Thank you all ;-)


What does "implementation" in a gradle-project do? Does it add some code automatically?
 

DonManfred

Expert
Licensed User
Longtime User
You are testing it on a real device, right?
yes. I tested it on my Samsung Note 10 (Android 11) and Motorola G (Android 10)
Looks like a missing native binary (so).
should i maybe add them to my wrapper and remove them from the AARs?
I can see different .so inside the AARs.
 

DonManfred

Expert
Licensed User
Longtime User
You should be able to see them inside the APK. Check the APK. Maybe the structure is different.
I can not see any difference from the AARs provided (and their .so files)
and the .so files in the APK. The names, the amount of .so and the .so filenames and the foldername are identically.


If you want to check the files (and APK) i added them to Dropbox.
 

DonManfred

Expert
Licensed User
Longtime User
Have you tried to run an example project with Android Studio?
yes but unfortunately i did not get it working to compile (at all) and run on my device.

I´ll restart with Android Studio and try to run one of the examples provided.

Thank you. I´ll report back.
 

DonManfred

Expert
Licensed User
Longtime User
If it works then you can compare the APKs.
it did work in AS at work.
I found out that the example does have
A code similar to this inside the manifest.
B4X:
AddApplicationText(
<provider android:name="com.scandit.datacapture.core.internal.sdk.init.CoreLibraryLoaderContentProvider"
android:exported="false"
android:authorities="${applicationId}.corelibraryloadercontentprovider"/>
<provider
android:name="com.scandit.datacapture.barcode.internal.sdk.init.BarcodeLibraryLoaderContentProvider"
android:exported="false"
android:authorities="${applicationId}.barcodelibraryloadercontentprovider"/>
)

Could be the missing of these "Providers" in my example be the cause of the error? Anyway i did run the B4A Example over RemoteDesktop at home.
I now see another error (i need to further investigate)... But seems that this lines were mandatory.
 

DonManfred

Expert
Licensed User
Longtime User
Examine the APK and see if there are more .so files inside it.
No there are no more .so files.

But further tryings i now get a first event raised (so the basic initialisation seems to work).
Still jumping in a problem though. But i expected it at this point.
 
Top