Recent

Author Topic: Android Module Wizard  (Read 537191 times)

jmpessoa

  • Hero Member
  • *****
  • Posts: 1483
Re: Android Module Wizard
« Reply #1200 on: November 22, 2019, 07:32:41 am »
Ok, kordal!

I will try your solution!

Thank you!

[Edited]
    Commited!!!
« Last Edit: November 23, 2019, 03:57:44 am by jmpessoa »
Lamw: Lazarus Android Module Wizard
https://github.com/jmpessoa/lazandroidmodulewizard

m4u_hoahoctro

  • Full Member
  • ***
  • Posts: 160
Re: Android Module Wizard
« Reply #1201 on: January 06, 2020, 11:48:00 am »
@jmpessoa

Can you write a guide on how to make an own component into lamw ?

I have a while on using flutter and its experience on app seem be more native than lamw's, but making an app with lamw still be easier, hope we can have more components for use :) (May i will contribute for some examples of IoT/Arduino/ARM/AVR :) )

kordal

  • New Member
  • *
  • Posts: 20
Re: Android Module Wizard
« Reply #1202 on: January 09, 2020, 01:26:31 am »
Directories & Files:
...\lazandroidmodulewizard\android_bridges\  - directory to Pascal components realization
register_ ... .pas - component registration files (if uses LAMW wizard, edited automaticly)
*.lrs - icon files of components

..\lazandroidmodulewizard\android_wizard\smartdesigner\java\ - directory to Java components realization
*.create - component initialization code
*.java - component main code
*.native - component event procedures
*.relational - list of *.java files (dependencies), if used in the main module java component

How to start?
  • Lazarus IDE -> Tools -> [LAMW] Android Module Wizard -> New jComponent Create
  • Click right mouse button

Note: When you write bridge functions in Pascal, pay attention to the names of functions in Java code and how they are called in JNI. A prerequisite is the presence of signatures for functions that change depending on the input / output parameters (arguments).

Example (the example is not complete):
jMyComponent.java
Code: Java  [Select]
  1. // package org.lamw...; // changes automatically  
  2. //
  3. // skip two lines
  4.  
  5. import android.os.Bundle;
  6. // other imports
  7. // see https://developer.android.com/reference
  8.  
  9. public class jMyComponent {
  10.     // Java-Pascal Interface
  11.     private long     pascalObj   = 0;    // Pascal Object
  12.     private Controls controls    = null; // Java/Pascal [events] Interface ...
  13.     private Context  context     = null;
  14.  
  15.     // constructor
  16.    public jMyComponent(Controls _Ctrls, long _Self /*, other parameters*/ ) {
  17.                 context   = _Ctrls.activity;
  18.                 controls  = _Ctrls;
  19.                 pascalObj = _Self;
  20.                
  21.    }
  22.  
  23.   // destructor
  24.   public void jFree() {
  25.      // free local objects...
  26.   }
  27.  
  28.   public int Sum(int a, int b) {
  29.      return a + b;
  30.   }
  31.  
  32. }
  33.  

MyComponent.pas
Code: Pascal  [Select]
  1.   // ...
  2. type  
  3.   jMyComponent = class(...)
  4.     // initialization code
  5.     // ...
  6.     function Sum(a, b: Integer): Integer;
  7.   end;
  8.  
  9.   function jMyComponent.Sum(a, b: Integer): Integer;
  10.   begin
  11.     if FInitialized then
  12.       Result := jMyComponent_Sum(FjEnv, FjObject, a, b);
  13.   end;
  14.  
  15.   // JNI implementation
  16.   function jMyComponent_Sum(env: PJNIEnv; _jmycomponent: JObject; _a, _b: Integer): Integer;
  17.   var
  18.     jParams: array[0..1] of JValue;
  19.     jMethod: JMethodID = nil;
  20.     jCls   : JClass = nil;      
  21.   begin
  22.     jParams[0].i := _a;
  23.     jParams[1].i := _b;
  24.     jCls := env^.GetObjectClass(env, _jmycomponent);
  25.     jMethod := env^.GetMethodID(env, jCls, 'Sum', '(II)I'); // function name 'Sum', signature (I - integer, I - integer) result Integer  
  26.     Result := env^.CallIntMethodA(env, _jmycomponent, jMethod, @jParams);
  27.     // env^.CallVoidMethodA(env, _jmycomponent, jMethod, @jParams);  // no result
  28.     // env^.CallVoidMethod(env, _jmycomponent, jMethod);  // no result, no params
  29.     // env^.CallFloatMethodA(env, _jmycomponent, jMethod, @jParams);  // result float, with params
  30.    
  31.    // See more And_jni.pas & other components
  32.     env^.DeleteLocalRef(env, jCls);                                  
  33.   end;
  34.  
  35.   // signatures
  36. (*  
  37.  
  38.   Z - Boolean
  39.   B - Byte
  40.   I - Integer
  41.   F - Float (Single)
  42.   V - Void (no result)
  43.   L - java Object
  44.   [ - array, ex [I - integer array, [F - float array, etc.
  45.  
  46. *)
  47.  

To view signatures of compiled java files, you can use, for example, JCSign tool.

Official help:

Quote
"LAMW: Lazarus Android Module Wizard"

   Author: Jose Marques Pessoa

      https://github.com/jmpessoa/lazandroidmodulewizard

:: How to Create JNI Bridge Component code::
 
1. LAMW Component Model: 

   ::  jControl: No GUI Control     

       hint: you can use composition or inheritance model here....

   ::  jVisualControl: GUI Control

       warning:  you MUST use inheritance model here.....

2. Creating a new component:
     
   2.1. Right click Java tab:

   2.1. Insert a new [wrapper] java class  jControl/jVisualControl from template;

        hint1. Alternatively, you can paste you java code [template compatible] from clipboard!

        hint2. Alternatively, you can import a "JAR" file and selec a class [at the moment, only for no visual  control]!

        hint3: You can also use the demo AppTryCode3 to produce a complete [wrapper]  java class !

       hint4. you do not need to build the "perfect" component now .... later you can add more methods, events and properties! [sections 3 e 4 bellow]

        warning/important: All "imports" from "Controls.java" template  and new "imports" from you java class code
                   will  be researched to try write the correct JNI methods signatures.....

                     
   2.2. Rename [and extends] the new class [and constructor], write some custom field, methods, etc...
        [for "JAR" imported class this task is automated! ]
       
        guideline: please, preferentially init your news params names with "_", ex: int _flag, String _hello ...
         
        hint1: Only public methods will be translated to pascal [bridge] code.
        hint2:  use the  mask  /*.*/   to make a public method invisible for the parse [ex.  /*.*/public myfunctio() {....} ] 

   2.3. Select all  the new java class code and right click it [java tab] .
     
       hint1:  if have only one java class code you do not need select it! just right click it!
       hint2. save your java class source code in LAMW folder "......\android_wizard\smartdesigner\java" :

        warning:  the  LAMW java to pascal convert dont support complex/generic types like  "java.util.Map<,>" ,  "java.util.Set<>" etc..
       So, we need re-write  the method signature [by hand] for primitives data/array type....

      But simple java Object param [or return]  is ok   [including array!!!]!
     
      public void SetImage(Bitmap _image) {     //<<----  OK!
             this.setImage(_image);
      }
 
     public Bitmap GetImage(Bitmap _image) {     //<<---- OK!
             return this.getImage();
      }

       warning/important: All "imports" from "Controls.java" template  and new "imports" from you java class code
                   will  be researched to try write the correct JNI methods signatures.....
     
   2.4. Popup Menu: Select "Write [Draft] Pascal jControl/jVisualControl Interface".
 
   2.5. Right click Pascal Tab  and select "Register Component..." from  Popup Menu;

          warning: Mac users: please, you must copy the console app "lazres" [lazarus/tools] to the folder
          ".../LazAndroidWizard/ide_tools";

   2.6. [Dialog]:  select a 16x16 PNG icon file;

   2.7.  [Dialog]:  open a "register_*.pas" file (ex. "register_extra.pas"); **

      hint: you can select "register_template.pas" to write a custom register file,
              but, then,  you must insert it into  "tfpandroidbridge_pack.lpk".

   2.8. Ok. You got you DRAFT pascal component JNI stuff! Please, look in the folder   "..../LazAndroidWizard/android_bridges"
         directory:
         
        :: *.pas           <--- open and fix/complete the pascal code with your others events/properties/methods/variables/overload!

        :: *_icon.lrs    <--- new icon resource
        :: register_extras.pas   <--- updated!  by default, the new component will be include there!

   2.9. [Lazarus IDE Menu]: Package -> Open Package File (*.lpk) --> "tfpandroidbridge_pack.pas" in folder 
         "....../LazAndroidWizard/android_bridges".

   2.10. [Package Wizard]: Compile --->> Use ---> Install.

            Warning: to compile/install/reinstall a LAMW package in  Lazarus/Laz4Android [windows],
               please, open a "dummy" pure windows project.... you MUST always close the cross compile project!
   
   2.11. Wait .... Lazarus re-building ...

   2.12. The new component  is on "Android Bridges Extra" [or other...] palette!.
   
   3. Added more methods to your component   

   3.1.  write new method to  your ".java" code
           ex.
                 public void SetFoo(int _foo) {
                       mFoo = _foo;
                 }
                  public int GetFoo() {
                        return mFoo;
                  }

   3.1.  In java tab write a draft java class code to wrapper the [new] methods;

           class jMyComponent {

                 public void SetFoo(int _foo) {
                       mFoo = _foo;
                 }
                  public int GetFoo() {
                        return mFoo;
                  }
          }                 
           
    3.2   Right click the Java tab and select "Write [draft] complementary Pascal Interface"

    3.4   Copy the the pascal interface code to your component unit!!!
       
    4. Added "native" [events] methods to your component

    4.1.  write the native method "call" to to  your ".java" code    [study some LAMW code as example!]

               ex1:   [from jSpinner.java]
 
                controls.pOnSpinnerItemSelected(pascalObj,position,caption);   //return void

              ex2:   [fiction...]
                int  i = controls.pOnSpinnerItemSelected(pascalObj,position,caption);   //return int             
 
              ex3:   [fiction...]
                int[] v;
                 v = controls.pOnSpinnerItemSelected(pascalObj,position,caption);   //return int[]     
                 for (int i = 0; i < v.length; i++) {
                     //Log.i("v["+i+"]", ""+v);     //do some use...
                 }         
               
              ex4:   [fiction...]
                String[] s;
                 s = controls.pOnSpinnerItemSelected(pascalObj,position,caption);   //return String[]               
                for (int i = 0; i < s.length; i++) {
                     //Log.i("s["+i+"]", ""+s);     //do some use...
                 }         
 
    4.2.  write the native method signature to your [component] ".native" file    [study some LAMW code as example!]
           ex1.    [from jSpinner.native]       
              public native void pOnSpinnerItemSelected(long pasobj, int position, String caption);

           ex2.    [fiction...]       
              public native int pOnSpinnerItemSelected(long pasobj, int position, String caption);

           ex3.    [fiction...]       
              public native int[] pOnSpinnerItemSelected(long pasobj, int position, String caption);

           ex4.    [fiction...]       
              public native String[] pOnSpinnerItemSelected(long pasobj, int position, String caption);

    4.3.  In java tab write a draft java class code to wrapper the the native [one line!] method;
         
           class jMyComponent {

                public native void pOnMyComponentItemSelected(long pasobj, int position, String caption); //MyComponent

          }                 

         NOTE: the  "//MyComponent" signs the part of the method name that will be overshadowed in the nomination of the property.
                            ex.  FOnItemSelected   [ not FOnMyComponentItemSelected  ]   

    4.4   Right click the Java tab and select "Write [draft] complementary Native Method Pascal Interface"

    4.5   Copy the the pascal interface code [generated] to your component unit [mycomponent.pas] and to "Laz_And_Controls_Events.pas"!!!

    Congratulations!!!     

« Last Edit: January 10, 2020, 12:20:13 am by kordal »

Mongkey

  • New Member
  • *
  • Posts: 12
Re: Android Module Wizard
« Reply #1203 on: January 10, 2020, 11:36:35 am »
Thanks bro, your tools are awesome!

cant wait for new advanced cool bridge :D
« Last Edit: January 10, 2020, 12:04:01 pm by Mongkey »

kordal

  • New Member
  • *
  • Posts: 20
Re: Android Module Wizard
« Reply #1204 on: January 23, 2020, 11:49:40 am »
Hi @jmpessoa, @TR3E  :)
1. There is an error in the
Code: Pascal  [Select]
  1. function GetTimeInMilliseconds: Longint
function, it returns a negative value! The fact is that the Integer type does not have enough bit depth for the correct operation of the time counter.

The problem arises is data types on the Pascal side. GetTimeInMilliseconds is of type Longint = Integer = jInt = Int (java), and should be Int64 = jLong = Long (java). As a result, the function calls another jni_func_out_j, where there is also a problem with data types.

So, we have at least several problematic calling functions
Code: Pascal  [Select]
  1. function jni_func_out_j: Longint; // change Longint to Int64
  2. function jni_func_j_out_t(..: Longint) ...; // change Longint to Int64
, where the types Longint and Int64 are mixed up.

2. I`am updated the jDrawingView component. The OnClick and OnDoubleClick events have been added.
So, the first modification:
Controls.java, added to line 165, 166
Code: Java  [Select]
  1. class Const {
  2.   public static final int TouchDown            =  0;
  3.   public static final int TouchMove            =  1;
  4.   public static final int TouchUp              =  2;
  5.   public static final int Click                =  3; // new
  6.   public static final int DoubleClick          =  4;  // new
  7.   public static final int Click_Default        =  0;
  8. }
  9.  
AndroidWidget.pas, added to line 71, 72 and two new functions
Code: Pascal  [Select]
  1. // Event id for Pascal & Java
  2.   cTouchDown            = 0;
  3.   cTouchMove            = 1;
  4.   cTouchUp              = 2;
  5.   cClick                = 3;
  6.   cDoubleClick          = 4;
  7.  
  8. // ...
  9.   procedure jni_proc_s(env: PJNIEnv; _jobject: JObject; javaFuncion : string; _short: smallint);
  10.   procedure jni_proc_ss(env: PJNIEnv; _jobject: JObject; javaFuncion : string; _short1, _short2: smallint);
  11.  
  12. // ...
  13. implementation
  14. // ...
  15.   procedure jni_proc_s(env: PJNIEnv; _jobject: JObject; javaFuncion : string; _short: smallint);
  16.   var
  17.     jParams: array[0..0] of jValue;
  18.     jMethod: jMethodID = nil;
  19.     jCls   : jClass = nil;
  20.   begin
  21.     jParams[0].s:= _short;
  22.  
  23.     jCls := env^.GetObjectClass(env, _jobject);
  24.     jMethod := env^.GetMethodID(env, jCls, PChar(javaFuncion), '(S)V');
  25.     env^.CallVoidMethodA(env, _jobject, jMethod, @jParams);
  26.     env^.DeleteLocalRef(env, jCls);
  27.   end;
  28.  
  29.   procedure jni_proc_ss(env: PJNIEnv; _jobject: JObject; javaFuncion : string; _short1, _short2: smallint);
  30.   var
  31.     jParams: array[0..1] of jValue;
  32.     jMethod: jMethodID = nil;
  33.     jCls   : jClass = nil;
  34.   begin
  35.     jParams[0].s:= _short1;
  36.     jParams[1].s:= _short2;
  37.  
  38.     jCls := env^.GetObjectClass(env, _jobject);
  39.     jMethod := env^.GetMethodID(env, jCls, PChar(javaFuncion), '(SS)V');
  40.     env^.CallVoidMethodA(env, _jobject, jMethod, @jParams);
  41.     env^.DeleteLocalRef(env, jCls);
  42.   end;    
  43.  

The following modification affected JDrawingView.java and DrawingView.pas, modified files in the attached.
« Last Edit: January 24, 2020, 05:17:56 am by kordal »

TR3E

  • Jr. Member
  • **
  • Posts: 61
    • ADiV Software
Re: Android Module Wizard
« Reply #1205 on: January 24, 2020, 09:29:08 am »
Updated "jDrawingView" [thanks to Kordal]

Fixed "GetTimeInMilliseconds" long to int64 and the corresponding "jni_func".

I have changed the variables that were in capital letters, since in programming the constants are understood as capital, when it will not be so.

And I have changed the FTimeClick and FTimeDoubleClick variables from short to int.

Already uploaded to LAMW as "Pull request", it is necessary that jmpessoa accept it.


kordal

  • New Member
  • *
  • Posts: 20
Re: Android Module Wizard
« Reply #1206 on: January 24, 2020, 01:26:30 pm »
Thank you) I forgot to add line in the Init procedure. This is necessary to change property values ​​in component design time.
Code: Pascal  [Select]
  1. procedure jDrawingView.Init(refApp: jApp);
  2. // ...
  3. begin
  4.   // ...
  5.   if not FInitialized then
  6.   begin
  7.     // ...
  8.     jni_proc_ii(FjEnv, FjObject, 'SetTimeClicks', FTimeClick, FTimeDoubleClick);
  9.   end;
  10. end;
  11.  

TR3E

  • Jr. Member
  • **
  • Posts: 61
    • ADiV Software
Re: Android Module Wizard
« Reply #1207 on: January 24, 2020, 02:10:32 pm »
Ok, it's already uploaded.