-1

I am trying to use java API in an Delphi android application to access and control android PDT devices scanner service. Some time ago, I already had a similar problem for a different device, but then it was much simpler and straightforward. All functions and properties where exposed in the interface part. This time its different, and my knowledge using java and interfaces is too poor to figure this out. This time, all procedures are exposed in class definition and none on the interface part, I cant figure out how to access and use them. I have spent several days searching for clues and solutions for similar problems and other devices or api's. There are some, but all had procedures already exposed in the interface part or had to write custom java class to expose functionality of that api based on android studio demo app.

with other device I needed just this code to access the scanner

var 
   ScanDevice: JScanDevice;
begin
      ScanDevice := TJScanDevice.Create;
      ScanDevice.setScanCodeEnterKey;
      ScanDevice.setOutScanMode(0);
      ScanDevice.openScan;
.......

with bridge file containing interface:

type
JScanDevice = interface; { android/device/ScanDevice }

  JScanDeviceClass = interface(JObjectClass)
    ['{58979737-4A26-4134-A604-C6FAB0D224CA}']
    { Methods }
    function init: JScanDevice; cdecl;
    function toHexString(s: JString): JString; cdecl;
  end;

  [JavaSignature('android/device/ScanDevice')]
  JScanDevice = interface(JObject)
    ['{A11B33C8-394C-4180-9003-9C66326B4A2B}']
    { Methods }
    function closeScan: Boolean; cdecl;
    .......
     function stopScan: Boolean; cdecl;
     function openScan: Boolean; cdecl;
     end;
TJScanDevice = class(TJavaGenericImport<JScanDeviceClass, JScanDevice>)

But AUTOID6L is different, with bridge file like this:

  Jscanner_ScannerClass = interface(JObjectClass)
      ['{84F85EBE-57DD-48C5-B2AE-38D3A5E31355}']
      { class } procedure close; cdecl;
........
      { class } procedure stopScan; cdecl;
   end;

   [JavaSignature('com/seuic/scanner/Scanner')]
   Jscanner_Scanner = interface(JObject)
      ['{692FF5AE-6C4B-4222-B858-DA029630F7A5}']
   end;

   TJscanner_Scanner = class(TJavaGenericImport<Jscanner_ScannerClass, Jscanner_Scanner>)
   end;

I don't know how to use that interface and class properly, cant access class functions.

PDT device is Seuic AUTOID6L, it comes with universal SDK used for several of their devices. For android scanner application there is ScannerAPI.jar library with com\seuic\scannerScanner.class , com\seuic\ScannerFactory.class classfiles.

java2op.exe to generated bridge file:

unit SeuicScanner;

interface

uses
   Androidapi.JNIBridge,
   Androidapi.JNI.GraphicsContentViewText,
   Androidapi.JNI.JavaTypes,
   Androidapi.JNI.Os;

type
   // ===== Forward declarations =====

   JAccount             = interface; // android.accounts.Account
   Jscanner_BuildConfig = interface; // com.seuic.scanner.BuildConfig
   JDecodeInfo          = interface; // com.seuic.scanner.DecodeInfo
   JDecodeInfoCallBack  = interface; // com.seuic.scanner.DecodeInfoCallBack
   Jscanner_Scanner     = interface; // com.seuic.scanner.Scanner
   JScannerFactory      = interface; // com.seuic.scanner.ScannerFactory
   JScannerKey          = interface; // com.seuic.scanner.ScannerKey
   JStatusCallBack      = interface; // com.seuic.scanner.StatusCallBack
   JVideoCallBack       = interface; // com.seuic.scanner.VideoCallBack
   // JStringBuffer = interface;//java.lang.StringBuffer
   // JStringBuilder = interface;//java.lang.StringBuilder

   // ===== Interface declarations =====

   JAccountClass = interface(JObjectClass)
      ['{94EE6861-F326-489F-8919-E20B39E3D9C1}']
      { class } function _GetCREATOR: JParcelable_Creator; cdecl;
      { class } function _Getname: JString; cdecl;
      { class } function _Gettype: JString; cdecl;
      { class } function init(name: JString; type_: JString): JAccount; cdecl; overload; // Deprecated
      { class } function init(in_: JParcel): JAccount; cdecl; overload; // Deprecated
      { class } function describeContents: Integer; cdecl; // Deprecated
      { class } function equals(o: JObject): Boolean; cdecl; // Deprecated
      { class } property CREATOR: JParcelable_Creator read _GetCREATOR;
      { class } property name: JString read _Getname;
      { class } property &type: JString read _Gettype;
   end;

   [JavaSignature('android/accounts/Account')]
   JAccount = interface(JObject)
      ['{71476381-8B6E-471F-9189-9857ECD7508C}']
      function hashCode: Integer; cdecl; // Deprecated
      function toString: JString; cdecl; // Deprecated
      procedure writeToParcel(dest: JParcel; flags: Integer); cdecl; // Deprecated
   end;

   TJAccount = class(TJavaGenericImport<JAccountClass, JAccount>)
   end;

   Jscanner_BuildConfigClass = interface(JObjectClass)
      ['{6AD54B95-FC70-418E-AA52-5846F17E981F}']
      { class } function _GetAPPLICATION_ID: JString; cdecl;
      { class } function _GetBUILD_TYPE: JString; cdecl;
      { class } function _GetDEBUG: Boolean; cdecl;
      { class } function _GetFLAVOR: JString; cdecl;
      { class } function _GetVERSION_CODE: Integer; cdecl;
      { class } function _GetVERSION_NAME: JString; cdecl;
      { class } function init: Jscanner_BuildConfig; cdecl;
      { class } property APPLICATION_ID: JString read _GetAPPLICATION_ID;
      { class } property BUILD_TYPE: JString read _GetBUILD_TYPE;
      { class } property DEBUG: Boolean read _GetDEBUG;
      { class } property FLAVOR: JString read _GetFLAVOR;
      { class } property VERSION_CODE: Integer read _GetVERSION_CODE;
      { class } property VERSION_NAME: JString read _GetVERSION_NAME;
   end;

   [JavaSignature('com/seuic/scanner/BuildConfig')]
   Jscanner_BuildConfig = interface(JObject)
      ['{1584DFE9-2B25-481A-8AF3-0E512A80A042}']
   end;

   TJscanner_BuildConfig = class(TJavaGenericImport<Jscanner_BuildConfigClass, Jscanner_BuildConfig>)
   end;

   JDecodeInfoClass = interface(JObjectClass)
      ['{D37439A1-F763-4798-8291-30E01196985A}']
      { class } function _Getbarcode: JString; cdecl;
      { class } function init: JDecodeInfo; cdecl;
      { class } property barcode: JString read _Getbarcode;
   end;

   [JavaSignature('com/seuic/scanner/DecodeInfo')]
   JDecodeInfo = interface(JObject)
      ['{0169284C-55E4-45BF-B648-87471A0AB873}']
      function _Getcodetype: JString; cdecl;
      function _Getlength: Integer; cdecl;
      property codetype: JString read _Getcodetype;
      property length: Integer read _Getlength;
   end;

   TJDecodeInfo = class(TJavaGenericImport<JDecodeInfoClass, JDecodeInfo>)
   end;

   JDecodeInfoCallBackClass = interface(IJavaClass)
      ['{81EA2AA6-F12D-41D0-ADA4-61EEB0F3FB9D}']
      { class } procedure onDecodeComplete(P1: JDecodeInfo); cdecl;
   end;

   [JavaSignature('com/seuic/scanner/DecodeInfoCallBack')]
   JDecodeInfoCallBack = interface(IJavaInstance)
      ['{CE9270B9-CD79-4FE7-8C29-7BFA9C2D8804}']
   end;

   TJDecodeInfoCallBack = class(TJavaGenericImport<JDecodeInfoCallBackClass, JDecodeInfoCallBack>)
   end;

   Jscanner_ScannerClass = interface(JObjectClass)
      ['{84F85EBE-57DD-48C5-B2AE-38D3A5E31355}']
      { class } procedure close; cdecl;
      { class } procedure disable; cdecl;
      { class } procedure enable; cdecl;
      { class } function getLastImage: TJavaArray<Byte>; cdecl;
      { class } function getParams(P1: Integer): Integer; cdecl;
      { class } function open: Boolean; cdecl;
      { class } procedure setDecodeInfoCallBack(P1: JDecodeInfoCallBack); cdecl;
      { class } function setParams(P1: Integer; P2: Integer): Boolean; cdecl;
      { class } procedure setStatusCallBack(P1: JStatusCallBack); cdecl;
      { class } procedure setVideoCallBack(P1: JVideoCallBack); cdecl;
      { class } procedure startScan; cdecl;
      { class } function startVideo(P1: Integer): Integer; cdecl;
      { class } procedure stopScan; cdecl;
      { class } procedure stopVideo; cdecl;
   end;

   [JavaSignature('com/seuic/scanner/Scanner')]
   Jscanner_Scanner = interface(JObject)
      ['{692FF5AE-6C4B-4222-B858-DA029630F7A5}']
   end;

   TJscanner_Scanner = class(TJavaGenericImport<Jscanner_ScannerClass, Jscanner_Scanner>)
   end;

   JScannerFactoryClass = interface(JObjectClass)
      ['{00796D3B-F41D-4FC1-9F68-475CE7DB738E}'] { class }
      function init: JScannerFactory; cdecl;
      function getScanner(P1: JContext): Jscanner_Scanner; cdecl;

   end;

   [JavaSignature('com/seuic/scanner/ScannerFactory')]
   JScannerFactory = interface(JObject)
      ['{77AE2625-EEFB-4EF2-838E-D8178AF6A9BD}']

   end;

   TJScannerFactory = class(TJavaGenericImport<JScannerFactoryClass, JScannerFactory>)
   end;

   JScannerKeyClass = interface(JObjectClass)
      ['{00D92230-CBC1-42EF-A1BA-7A84E4E70AB9}']
      { class } function _GetKEY_DOWN: Integer; cdecl;
      { class } function _GetKEY_UP: Integer; cdecl;
      { class } procedure close; cdecl;
      { class } function getKeyEvent: Integer; cdecl;
      { class } function init: JScannerKey; cdecl;
      { class } function open: Integer; cdecl;
      { class } property KEY_DOWN: Integer read _GetKEY_DOWN;
      { class } property KEY_UP: Integer read _GetKEY_UP;
   end;

   [JavaSignature('com/seuic/scanner/ScannerKey')]
   JScannerKey = interface(JObject)
      ['{CE5ADF91-7ED1-46B5-B51D-694277A743AD}']
   end;

   TJScannerKey = class(TJavaGenericImport<JScannerKeyClass, JScannerKey>)
   end;

   JStatusCallBackClass = interface(IJavaClass)
      ['{58DA1A43-E11A-4F47-82FF-15DB0D65750E}']
      { class } procedure onStatusCallBack(P1: Integer; P2: Integer); cdecl;
   end;

   [JavaSignature('com/seuic/scanner/StatusCallBack')]
   JStatusCallBack = interface(IJavaInstance)
      ['{75C0B88C-71DC-4645-A11D-76CFE299F28C}']
   end;

   TJStatusCallBack = class(TJavaGenericImport<JStatusCallBackClass, JStatusCallBack>)
   end;

   JVideoCallBackClass = interface(IJavaClass)
      ['{2C57576F-B3EC-4784-B190-C36924D62BAC}']
      { class } function onVideoCallBack(P1: Integer; P2: Integer; P3: TJavaArray<Byte>): Boolean; cdecl;
   end;

   [JavaSignature('com/seuic/scanner/VideoCallBack')]
   JVideoCallBack = interface(IJavaInstance)
      ['{A8CB0355-4451-4FCD-B6D6-9E718CA500D0}']
   end;

   TJVideoCallBack = class(TJavaGenericImport<JVideoCallBackClass, JVideoCallBack>)
   end;

   // java.lang.StringBuffer
   // java.lang.StringBuilder
implementation

procedure RegisterTypes;
begin
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JAccount', TypeInfo(SeuicScanner.JAccount));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.Jscanner_BuildConfig', TypeInfo(SeuicScanner.Jscanner_BuildConfig));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JDecodeInfo', TypeInfo(SeuicScanner.JDecodeInfo));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JDecodeInfoCallBack', TypeInfo(SeuicScanner.JDecodeInfoCallBack));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.Jscanner_Scanner', TypeInfo(SeuicScanner.Jscanner_Scanner));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JScannerFactory', TypeInfo(SeuicScanner.JScannerFactory));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JScannerKey', TypeInfo(SeuicScanner.JScannerKey));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JStatusCallBack', TypeInfo(SeuicScanner.JStatusCallBack));
TRegTypes.RegisterType('Android.JNI.SeuicScanner.JVideoCallBack', TypeInfo(SeuicScanner.JVideoCallBack));
// TRegTypes.RegisterType('Android.JNI.SeuicScanner.JStringBuffer', TypeInfo(Android.JNI.SeuicScanner.JStringBuffer));
// TRegTypes.RegisterType('Android.JNI.SeuicScanner.JStringBuilder', TypeInfo(Android.JNI.SeuicScanner.JStringBuilder));
end;

initialization

RegisterTypes;

end.

I did successfully try and run demo app provided in sdk for android studio.

service script:

package com.seuic.scannerapitest.service;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;

import com.seuic.scanner.DecodeInfo;
import com.seuic.scanner.DecodeInfoCallBack;
import com.seuic.scanner.Scanner;
import com.seuic.scanner.ScannerFactory;
import com.seuic.scanner.ScannerKey;
import com.seuic.scanner.VideoCallBack;
import com.seuic.scannerapitest.activity.MainActivity;

@SuppressWarnings("unused")
public class ScannerService extends Service implements DecodeInfoCallBack,VideoCallBack {
    static final String TAG = "ScannerApiTest";
    Scanner scanner;
    private static  MainActivity mcontext = null;
    private boolean mScanRunning = false;

    private  void log(String  string){
        Log.i(TAG, string);
    }

    public static  void MyService(Context context){
        mcontext = (MainActivity)context;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        scanner = ScannerFactory.getScanner(this);
        scanner.open();
        scanner.setDecodeInfoCallBack(this);
        scanner.setVideoCallBack(this);
        scanner.enable();
        mScanRunning = true;
        new Thread(runnable).start();
    };

    Runnable runnable = new Runnable() {

        @Override
        public void run() {
            int ret1 = ScannerKey.open();
            if (ret1 > -1) {
                while (mScanRunning) {
                    int ret = ScannerKey.getKeyEvent();
                    if (ret > -1) {
                        switch (ret) {
                        case ScannerKey.KEY_DOWN:
                            if (scanner != null && mScanRunning) {
                                scanner.startScan();
                            }
                            break;
                        case ScannerKey.KEY_UP:
                            if (scanner != null && mScanRunning) {
                                scanner.stopScan();
                            }
                            break;
                        }
                    }
                }
            }
        }
    };

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        return Service.START_STICKY;
    };

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        mScanRunning = false;
        ScannerKey.close();
        scanner.setDecodeInfoCallBack(null);
        scanner.setVideoCallBack(null);
        scanner.close();
        scanner = null;
        super.onDestroy();
    }

    public static final String BAR_CODE = "barcode";
    public static final String CODE_TYPE = "codetype";
    public static final String LENGTH = "length";

    // this is a custom broadcast receiver action
    public static final String ACTION = "seuic.android.scanner.scannertestreciever";

    @Override
    public void onDecodeComplete(DecodeInfo info) {
        Intent intent = new Intent(ACTION);
        Bundle bundle = new Bundle();
        bundle.putString(BAR_CODE, info.barcode);
        bundle.putString(CODE_TYPE, info.codetype);
        bundle.putInt(LENGTH, info.length);
        intent.putExtras(bundle);
        sendBroadcast(intent);
    }

    @Override
    public boolean onVideoCallBack(int width, int height, byte[] img) {
        if (img == null||width == 0||height == 0||img.length == 0){
            return false;
        }

        log("onVideCallBack E");
        Message video_msg = mcontext.mHandler.obtainMessage(img.length, width, height, img);
        mcontext.mHandler.sendMessage(video_msg);

        try {
            Thread.sleep(80);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        log("onVideCallBack X");

        return false;
    }

}

mainactivity:

package scannertest.test2;

import android.content.Intent;

import android.os.Bundle;

import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;


import com.seuic.scanner.Scanner;
import com.seuic.scanner.ScannerFactory;


public class MainActivity extends AppCompatActivity {
    public static final String TAG;
    static final int SCANNER_KEYCODE;

    static {
        TAG = "ScannerApiTest";
        SCANNER_KEYCODE = 142;
    }

    Scanner scanner;

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


       // Intent intent = new Intent(this, ScannerService.  class);
       // startService(intent);
        scanner = ScannerFactory.getScanner(this);

        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();

            }
        });

    }

    void init() {
        scanner = ScannerFactory.getScanner(this);
        if (scanner == null){
            log("scanner(NULL)");
        }




    }
    private  void log(String  string){
        Log.i(TAG, string);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Edit:

  scanner = TJScannerFactory.JavaClass.getScanner(TAndroidHelper.Context)

raises exception, call stack:

System._DbgExcNotify(-506670960,0xe0015d30,0xe20e463d,0xe0015d30,0xe20e463d)
System.NotifyReRaise(0xe0015d30,0xe20e463d)
System._RaiseAtExcept(0xe0015d30,0xe20e463d)
System._RaiseExcept(0xe0015d30)
System.Internal.Excutils.DoRaiseJNIExceptionCallBack('class java.lang.RuntimeException','java.lang.RuntimeException: Stub!')
Androidapi.Jni.HandleJNIException(0xef826b20)
Androidapi.Jnimarshal.ExecJNI(0xe1ccd540,0xe13fa138)
:E27BFA70 DispatchToImport
:E27CBE5C dispatch_first_stage_intercept
Unit1.TForm1.Button1Click(0xef89d800,0xef8b4a00)
Fmx.Controls.TControl.Click(0xef8b4a00)
Fmx.Stdctrls.TCustomButton.Click(0xef8b4a00)
Fmx.Controls.TControl.MouseClick(0xef8b4a00,System.Uitypes.mbLeft,0x430a0088,66.3783722,5.73217773)
:E25182D2 __stub_in864v88__ZN3Fmx8Controls8TControl10MouseClickEN6System7Uitypes12TMouseButtonENS2_3SetINS2_7Classes17System_Classes__1ELS7_0ELS7_10EEEff
Fmx.Forms.TCommonCustomForm.MouseUp(0xef89d800,System.Uitypes.mbLeft,0xe1cc0088,138.378372,359.732178,true)
Fmx.Platform.Android.TWindowManager.MouseUp(0xe1698a20,System.Uitypes.mbLeft,0x88,138.378372,384.732178,true)
Fmx.Platform.Android.TPlatformAndroid.ProcessAndroidMouseEvents(0xefa59740)
Fmx.Platform.Android.TPlatformAndroid.HandleAndroidMotionEvent(0xefa59740,0xf4a48d00)
Fmx.Platform.Android.TPlatformAndroid.HandleAndroidInputEvent(0xefa59740,0xf4a48d00)
Fmx.Platform.Android.HandleAndroidInputEvent(@0xf4acbec0: {userData = nil, onAppCmd = 0xe264cf69 <Fmx.Platform.Android.HandleAndroidCmd(Androidapi.Appglue.TAndroid_app&, int)>, onInputEvent = 0xe264cf31 <Fmx.Platform.Android.HandleAndroidInputEvent(Androidapi.Appglue.TAndroid_app&, AInputEvent*)>, activity = 0xf4acb4c0, config = 0xef82f550, savedState = nil, savedStateSize = 0, looper = 0xef82e2e0, inputQueue = 0xf4ae6200, window = 0xf49a8e08, contentRect = {left = 0, top = -496709783, right = -496709839, bottom = -190008128}, activityState = 11, destroyRequested = 0, mutex = {value = 0}, cond = {value = 0}, msgread = 29, msgwrite = 30, thread = 0, cmdPollSource = {id = 0, app = 0xe264cf69, process = 0xe264cf31 <Fmx.Platform.Android.HandleAndroidInputEvent(Androidapi.Appglue.TAndroid_app&, AInputEvent*)>}, inputPollSource = {id = 0, app = 0xe264cf69, process = 0xe264cf31 <Fmx.Platform.Android.HandleAndroidInputEvent(Androidapi.Appglue.TAndroid_app&, AInputEvent*)>}, running = 1, stateSaved = 0, destroyed = 0, redrawNeeded = 0, pendingInputQueue = 0xf4ae6200, pendingWindow = 0xf49a8e08, pendingContentRect = {left = 0, top = -496709783, right = -496709839, bottom = -190008128}},0xf4a48d00)
Androidapi.Appglue.process_input(0xf4acbec0,0xf4acbf20)
Fmx.Platform.Android.TPlatformAndroid.InternalProcessMessages(0xefa59740)
Fmx.Platform.Android.TPlatformAndroid.Run(0xefa59740)
:E2637FF2 __stub_in272s__ZN3Fmx8Platform7Android16TPlatformAndroid3RunEv
Fmx.Forms.TApplication.Run(0xefac7940)
_NativeMain
Androidapi.Appglue.android_app_entry(void*).SystemEntry(void*)(@0x0: {})
Androidapi.Appglue.android_app_entry(0xf4acbec0)
:F71950C4 __pthread_start(void*)
:F71930B0 __start_thread
:00000000 ??
LongBeard_Boldy
  • 802
  • 8
  • 20

1 Answers1

0

Class methods are accessed like this:

TJScannerKey.JavaClass.open 

By the looks of the Java code, I'd say that Java2OP has once again imported some methods incorrectly, particularly in the Scanner class, because this code:

@Override
public void onCreate() {
    super.onCreate();
    scanner = ScannerFactory.getScanner(this);
    scanner.open();
    scanner.setDecodeInfoCallBack(this);
    scanner.setVideoCallBack(this);
    scanner.enable();
    mScanRunning = true;
    new Thread(runnable).start();
};

is calling a bunch of methods on the instance (i.e. scanner), that are declared in the class (Jscanner_ScannerClass). You should refer to the documentation (or maybe just the example Java code) to determine which methods are class methods, and which are instance methods

Dave Nottage
  • 3,411
  • 1
  • 20
  • 57