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 ??