
Developer Cristian Peta has a post up on the Embarcadero forum with some sample code showing how he implemented a bar code reader callback in the Android SDK for Delphi XE5 Firemonkey. Basically how Firemonkey applications work on Android is there is an Android SDK shim which is written in Java (and contained within the classes.dex file). This shim calls the actual Android NDK based Firemonkey application which is a .SO file (where your code is located). What Cristian Peta’s code does is implement some Java code and custom events in that Android SDK shim and those events make function calls into the Firemonkey application. This allowed him to implement a Honeywell SDK in Java and use it from Firemonkey. Brian Long pioneered this method in his CodeRage 8 sessions on the topic. Take a look at the BarCode and Menu demos in the AccessingTheAndroidAPI.7z download for complete examples of doing this. I am including Cristian Peta’s full post below with his implementation:
“My solution was to write some Java code then insert this code into classes.dex
Then I use this Java code from Delphi trough Java generic import and JNI.
You can read Brian Long blog about classes.dex
My starting point from Honeywell SDK examples was DecodeSampleActivity. There is ScanDemoMainActivity.java that I modified and merged into classes.dex
I extended com.embarcadero.firemonkey.FMXNativeActivity but I think now it should be enough to extend Activity. When you extend Activity you do not need to decompile classes.dex
The main Delphi part is here:”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
[crayon-600b73f00ec7a883563966 jive-pre ]<code class="jive-code jive-java"><span style="color: darkgreen;">//This is called from the Java activity's onBarCodeComplete() method</span> procedure onBarCodeCompleteNative(PEnv: PJNIEnv; This: JNIObject; BarCode: JNIString); cdecl; begin Log.d(<span style="color: navy;">'+onBarCodeCompleteNative'</span>); Log.d(<span style="color: navy;">'Thread (Main: %.8x, Current: %.8x, Java:%.8d (%2:.8x), POSIX:%.8x)'</span>, [MainThreadID, TThread.CurrentThread.ThreadID, TJThread.JavaClass.currentThread.getId, GetCurrentThreadID]); ARNBarCode := JNIStringToString(PEnv, BarCode); Log.d(<span style="color: navy;">'Calling Synchronize'</span>); TThread.Synchronize(nil, onBarCodeCompleteThreadSwitcher); Log.d(<span style="color: navy;">'Synchronize is over'</span>); Log.d(<span style="color: navy;">'-onBarCodeCompleteNative'</span>); end; Â <span style="color: darkgreen;">//This is called from the Java activity's onBarCodeComplete() method</span> procedure onBarCodeFailNative(PEnv: PJNIEnv; This: JNIObject); cdecl; begin Log.d(<span style="color: navy;">'+onBarCodeFailNative'</span>); Log.d(<span style="color: navy;">'Thread (Main: %.8x, Current: %.8x, Java:%.8d (%2:.8x), POSIX:%.8x)'</span>, [MainThreadID, TThread.CurrentThread.ThreadID, TJThread.JavaClass.currentThread.getId, GetCurrentThreadID]); Log.d(<span style="color: navy;">'Calling Synchronize'</span>); TThread.Synchronize(nil, onBarCodeFailThreadSwitcher); Log.d(<span style="color: navy;">'Synchronize is over'</span>); Log.d(<span style="color: navy;">'-onBarCodeFailNative'</span>); end; Â procedure TFormMain.RegisterDelphiNativeMethods; var PEnv: PJNIEnv; ActivityClass: JNIClass; NativeMethods: array[0..1] of JNINativeMethod; begin Log.d(<span style="color: navy;">'Starting the registration JNI stuff'</span>); Â PEnv := TJNIResolver.GetJNIEnv; Â Log.d(<span style="color: navy;">'Registering interop methods'</span>); Â NativeMethods[0].Name := <span style="color: navy;">'onBarCodeCompleteNative'</span>; NativeMethods[0].Signature := <span style="color: navy;">'(Ljava/lang/String;)V'</span>; NativeMethods[0].FnPtr := @onBarCodeCompleteNative; Â NativeMethods[1].Name := <span style="color: navy;">'onBarCodeFailNative'</span>; NativeMethods[1].Signature := <span style="color: navy;">'()V'</span>; NativeMethods[1].FnPtr := @onBarCodeFailNative; Â ActivityClass := PEnv^.GetObjectClass( PEnv, PANativeActivity(System.DelphiActivity).clazz); Â PEnv^.RegisterNatives(PEnv, ActivityClass, @NativeMethods[0], 2); Â PEnv^.DeleteLocalRef(PEnv, ActivityClass); end; |
[/crayon]
“And Java code:”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
[crayon-600b73f00ec81494166603 jive-pre ]<code class="jive-code jive-java"><span style="color: navy;"><b>package</b></span> com.winarhi.nativeactivitysubclass; Â <span style="color: navy;"><b>import</b></span> android.os.Build; <span style="color: navy;"><b>import</b></span> android.os.Handler; <span style="color: navy;"><b>import</b></span> android.os.Message; <span style="color: navy;"><b>import</b></span> android.os.RemoteException; Â <span style="color: navy;"><b>import</b></span> java.io.IOException; Â <span style="color: navy;"><b>import</b></span> com.honeywell.decodemanager.DecodeManager; <span style="color: navy;"><b>import</b></span> com.honeywell.decodemanager.barcode.DecodeResult; Â <span style="color: navy;"><b>public</b></span> <span style="color: navy;"><b>class</b></span> NativeActivitySubclass <span style="color: navy;"><b>extends</b></span> com.embarcadero.firemonkey.FMXNativeActivity<span style="color: navy;">{</span> Â <span style="color: darkgreen;">//private final int ID_SCANSETTING = 0x12;</span> <span style="color: darkgreen;">//private final int ID_CLEAR_SCREEN = 0x13;</span> Â <span style="color: navy;"><b>private</b></span> DecodeManager mDecodeManager = <span style="color: navy;"><b>null</b></span>; <span style="color: navy;"><b>public</b></span> String strDecodeResult = <span style="color: red;">""</span>; <span style="color: navy;"><b>private</b></span> <span style="color: navy;"><b>final</b></span> <span style="color: navy;"><b>int</b></span> WA_SCANTIMEOUT = 5000; <span style="color: navy;"><b>long</b></span> WA_mScanAccount = 0; Â <span style="color: navy;"><b>private</b></span> <span style="color: navy;"><b>void</b></span> WA_CreateDecodeManager() <span style="color: navy;">{</span> <span style="color: navy;"><b>if</b></span> (mDecodeManager == <span style="color: navy;"><b>null</b></span>) mDecodeManager = <span style="color: navy;"><b>new</b></span> DecodeManager(this, ScanResultHandler); <span style="color: navy;">}</span> Â <span style="color: navy;"><b>private</b></span> <span style="color: navy;"><b>void</b></span> WA_DestroyDecodeManager() <span style="color: navy;">{</span> <span style="color: navy;"><b>if</b></span> (mDecodeManager != <span style="color: navy;"><b>null</b></span>) <span style="color: navy;">{</span> <span style="color: navy;"><b>try</b></span> <span style="color: navy;">{</span> mDecodeManager.release(); mDecodeManager = <span style="color: navy;"><b>null</b></span>; <span style="color: navy;">}</span> <span style="color: navy;"><b>catch</b></span> (IOException e) <span style="color: navy;">{</span> <span style="color: darkgreen;">// TODO Auto-generated catch block</span> e.printStackTrace(); <span style="color: navy;">}</span> <span style="color: navy;">}</span> <span style="color: navy;">}</span> Â <span style="color: navy;"><b>private</b></span> <span style="color: navy;"><b>void</b></span> WA_DoScan() <span style="color: navy;"><b>throws</b></span> Exception <span style="color: navy;">{</span> <span style="color: navy;"><b>if</b></span> (mDecodeManager != <span style="color: navy;"><b>null</b></span>) <span style="color: navy;">{</span> <span style="color: navy;"><b>try</b></span> <span style="color: navy;">{</span> strDecodeResult = <span style="color: red;">""</span>; mDecodeManager.doDecode(WA_SCANTIMEOUT); <span style="color: navy;">}</span> <span style="color: navy;"><b>catch</b></span> (RemoteException e) <span style="color: navy;">{</span> <span style="color: darkgreen;">// TODO Auto-generated catch block</span> e.printStackTrace(); <span style="color: navy;">}</span> <span style="color: navy;">}</span> <span style="color: navy;">}</span> Â <span style="color: navy;"><b>public</b></span> <span style="color: navy;"><b>native</b></span> <span style="color: navy;"><b>void</b></span> onBarCodeCompleteNative(String BarCode); <span style="color: navy;"><b>public</b></span> <span style="color: navy;"><b>native</b></span> <span style="color: navy;"><b>void</b></span> onBarCodeFailNative(); Â <span style="color: navy;"><b>private</b></span> Handler ScanResultHandler = <span style="color: navy;"><b>new</b></span> Handler() <span style="color: navy;">{</span> <span style="color: navy;"><b>public</b></span> <span style="color: navy;"><b>void</b></span> handleMessage(Message msg) <span style="color: navy;">{</span> <span style="color: navy;"><b>switch</b></span> (msg.what) <span style="color: navy;">{</span> <span style="color: navy;"><b>case</b></span> DecodeManager.MESSAGE_DECODER_COMPLETE: WA_mScanAccount++; DecodeResult decodeResult = (DecodeResult) msg.obj; Â <span style="color: darkgreen;">//byte codeid = decodeResult.codeId;</span> <span style="color: darkgreen;">//byte aimid = decodeResult.aimId;</span> <span style="color: darkgreen;">//int iLength = decodeResult.length;</span> Â strDecodeResult = decodeResult.barcodeData; onBarCodeCompleteNative(strDecodeResult); <span style="color: navy;"><b>break</b></span>; Â <span style="color: navy;"><b>case</b></span> DecodeManager.MESSAGE_DECODER_FAIL: <span style="color: navy;">{</span> strDecodeResult = <span style="color: red;">""</span>; onBarCodeFailNative(); <span style="color: navy;">}</span> <span style="color: navy;"><b>break</b></span>; <span style="color: navy;"><b>case</b></span> DecodeManager.MESSAGE_DECODER_READY: <span style="color: navy;">{</span> <span style="color: navy;">}</span> <span style="color: navy;"><b>break</b></span>; <span style="color: navy;"><b>default</b></span>: super.handleMessage(msg); <span style="color: navy;"><b>break</b></span>; <span style="color: navy;">}</span> <span style="color: navy;">}</span> <span style="color: navy;">}</span>; <span style="color: navy;"><b>private</b></span> <span style="color: navy;"><b>void</b></span> WA_cancelScan() <span style="color: navy;"><b>throws</b></span> Exception <span style="color: navy;">{</span> mDecodeManager.cancelDecode(); <span style="color: navy;">}</span> <span style="color: navy;">}</span> |
[/crayon]
One more thing (Java generic import):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
[crayon-600b73f00ec86971978889 jive-pre ]<code class="jive-code jive-java">uses Androidapi.JNIBridge, Androidapi.JNI.App; Â type JNativeActivitySubclassClass = <span style="color: navy;"><b>interface</b></span>(JNativeActivityClass) [<span style="color: navy;">'{829C77FB-08F1-4D19-9782-3C58EECAAAAA}'</span>] <span style="color: navy;">{</span>Methods<span style="color: navy;">}</span> <span style="color: darkgreen;">//function init: JFMXNativeActivity; cdecl;</span> end; Â [JavaSignature(<span style="color: navy;">'com/winarhi/nativeactivitysubclass/NativeActivitySubclass'</span>)] JNativeActivitySubclass = <span style="color: navy;"><b>interface</b></span>(JNativeActivity) [<span style="color: navy;">'{2FA559EC-D1D7-46AA-9C52-FEFC6B3AAAAA}'</span>] <span style="color: navy;">{</span>Methods<span style="color: navy;">}</span> procedure WA_DoScan; procedure WA_CreateDecodeManager; procedure WA_DestroyDecodeManager; end; TJNativeActivitySubclass = <span style="color: navy;"><b>class</b></span>(TJavaGenericImport<JNativeActivitySubclassClass, JNativeActivitySubclass>) end; |
[/crayon]
Best Regards,
Cristian Peta
For implementing existing third party SDKs like MoPub, TapJoy, Millennial Media, Google Play Services, Flurry, Chartboost, Upsight, or even new Bluetooth functionality for Android in Delphi XE5, Delphi XE6, and AppMethod this may be the fastest way.
Head over and download the samples from Brian Long on customizing your Firemonkey classes.dex file.
Hi, can anyone provide a Code Sample here without the faulty HTML-Stuff? Thanks