LiveCode 9.0.0 DP-6 has just been released, and is the first developer preview containing the Java Foreign Function Interface (FFI). We’re thrilled to be bringing you this exciting first part of the Infinite LiveCode project. It’s been a longish road to get here and as is typical with such large and complex software projects, there have been a number of frustrating walls to be knocked down en route. I’m proud to say we have overcome these roadblocks, and without further ado, let me get stuck in to telling you how you can use it! Foreign handlers can be written in LiveCode Builder (LCB) which bind to calls on Java classes via the Java Native Interface (JNI). In particular this enables binding to classes in the Android API.
This release also contains a command line tool called lc-compile-ffi-java
which can auto-generate LCB wrappers of Java classes, given the appropriate data.
Setup
The JNI is currently available on Mac, Linux and Android.
Android
No additional setup is required on Android.
Mac
In order to be able to create a Java Virtual Machine on Mac, you will need the JAVA_HOME
environment variable set to the appropriate value. Usually if you have a Java Runtime Environment installed you can find the correct path by executing /usr/libexec/java_home
on the command line.
Due to the way the environment works on Mac when the application is launched from the dock, it is not currently possible to call Java APIs on Mac when LiveCode is launched in this way. However the situation is better when you launch LiveCode from the command line:
/Applications/LiveCode Community 9.0.0 (dp 6).app/Contents/Contents/MacOS/LiveCode-Community
If you launch LiveCode like this you can modify the JAVA_HOME
environment variable in the message box by doing (for example)
put "/Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home" into $JAVA_HOME
Linux
In order to be able to create a Java Virtual Machine on Linux, you will need to set the LD_LIBRARY_PATH
environment variable to the location of the libjvm.so
library. This is often located at ${JAVA_HOME}/jre/lib/amd64/server
or ${JAVA_HOME}/jre/lib/i386/server
on 64-bit and 32-bit Linux respectively.
Examples
Android Logger
Here is an example of a library which exposes a public handler OutputToLog
, that can be used for logging from LiveCode Script or Builder, so that the output can be seen for example using adb logcat
.
library com.livecode.library.androidlog
use com.livecode.foreign
use com.livecode.java
metadata title is "AndroidLog"
metadata author is "LiveCode"
metadata version is "1.0.0"
foreign handler AndroidLog(in pTag as JString, in pLog as JString) returns CInt binds to "java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static"
public handler OutputToLog(in pTag as String, in pString as String)
unsafe
AndroidLog(StringToJString(pTag), StringToJString(pString))
end unsafe
end handler
end library
In the foreign handler declaration, the input parameters (what the actual handler is called with) are JString
s. The binding string breaks down as follows:
“java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static”
This indicates the language of the binding, i.e. Java
“java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static”
This is the class containing the method being called
“java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static”
This specified which method defined in the class is being called (in this case, d
)
“java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static”
This is the parameter string. In brackets are the input parameter codes, and after the brackets is the return parameter code. In this case the d method takes two instances of the java.lang.String
class as parameters, and returns an int
“java:android.util.Log>d(Ljava/lang/String;Ljava/lang/String;)I!static”
This is the calling convention. The default is instance
. The android.util.Log
class d
method is static, i.e. no instance of the Log
class is needed to call it (in Java you would call it using Log.d("tag", "msg")
).
Android Battery Level
Here is an example of using the Java FFI to monitor the battery level of an Android device. We jump through a few hoops in order to fetch the application’s Context
– in the future we plan to provide an API for some useful things like this.
The battery changed intent is known as a ‘sticky intent’ – this is why we
can access the relevant values at any time rather than registering a BroadcastReceiver
to respond to changes in the battery state.
All such sticky intents can be obtained in exactly the same way, passing null
as the BroadcastReceiver
(in this case passing nothing as the first parameter in ApplicationRegisterReceiver
).
Then various values can be accessed using the appropriate EXTRA_
constants. For more information see the Android Intent API
library com.livecode.library.androidbattery
use com.livecode.foreign
use com.livecode.java
metadata title is "Android Battery Library"
metadata author is "LiveCode"
metadata version is "1.0.0"
foreign handler ClassForName(in pClass as JString) returns JObject binds to "java:java.lang.Class>forName(Ljava/lang/String;)Ljava/lang/Class;!static"
foreign handler GetCurrentApplicationMethod(in pActivityThread as JObject, in pMethod as JString, in pParams as optional JObject) returns JObject binds to "java:java.lang.Class>getMethod(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"
foreign handler MethodInvoke(in pMethod as JObject, in pInstance as optional JObject, in pParams as optional JObject) returns JObject binds to "java:java.lang.reflect.Method>invoke(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
foreign handler GetIntentFilter(in pAction as JString) returns JObject binds to "java:android.content.IntentFilter>new(Ljava/lang/String;)"
foreign handler ApplicationRegisterReceiver(in pContext as JObject, in pObj as optional JObject, in pFilter as JObject) returns JObject binds to "java:android.content.Context>registerReceiver(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;"
foreign handler GetIntentValueInt(in pIntent as JObject, in pWhich as JString, in pDefault as CInt) returns CInt binds to "java:android.content.Intent>getIntExtra(Ljava/lang/String;I)I"
public handler GetBatteryPercentage() returns Number
unsafe
// Get the application context. We rely on an undocumented
// method to get this 'statically' at the moment.
variable tClass as JObject
put ClassForName(StringToJString("android.app.ActivityThread")) into tClass
variable tMethod as JObject
put GetCurrentApplicationMethod(tClass, StringToJString("currentApplication"), nothing) into tMethod
variable tContext as JObject
put MethodInvoke(tMethod, nothing, nothing) into tContext
// Get an intent filter for the battery changed intent
variable tFilter as JObject
put GetIntentFilter(StringToJString("android.intent.action.BATTERY_CHANGED")) into tFilter
// Get the intent
variable tIntent as JObject
put ApplicationRegisterReceiver(tContext, nothing, tFilter) into tIntent
// Get the relevant values from the intent.
variable tLevel as Integer
put GetIntentValueInt(tIntent, StringToJString("level"), -1) into tLevel
variable tScale as Integer
put GetIntentValueInt(tIntent, StringToJString("scale"), -1) into tScale
return tLevel / tScale * 100
end unsafe
end handler
end library
Generating an LCB wrapper from a class specification
We have also provided a tool which eventually will be integrated into the extension builder to generate an LCB wrapper from a class specification.
Here’s an example of the spec file for the java.math.BigInteger class:
foreign package java.math
class Random
end class
class Object
end class
class BigInteger
constructor BigInteger(val as byte[]) named BigIntegerFromBytes
constructor BigInteger(val as String) named BigIntegerFromString
constructor BigInteger(val as String, radix as int) named BigIntegerFromStringWithRadix
method abs() returns BigInteger
method add(val as BigInteger) returns BigInteger
method and(val as BigInteger) returns BigInteger
method andNot(val as BigInteger) returns BigInteger
method bitCount() returns int
method bitLength() returns int
method clearBit(n as int) returns BigInteger
method compareTo(val as BigInteger) returns int
method divide(val as BigInteger) returns BigInteger
method divideAndRemainder(val as BigInteger) returns BigInteger[]
method doubleValue() returns double
method equals(x as Object) returns boolean
method flipBit(n as int) returns BigInteger
method floatValue() returns float
method gcd(val as BigInteger) returns BigInteger
method getLowestSetBit() returns int
method hashCode() returns int
method intValue() returns int
method isProbablePrime(certainty as int) returns boolean
method longValue() returns long
method max(val as BigInteger) returns BigInteger
method min(val as BigInteger) returns BigInteger
method mod(m as BigInteger) returns BigInteger
method modInverse(m as BigInteger) returns BigInteger
method modPow(exponent as BigInteger, m as BigInteger) returns BigInteger
method multiply(val as BigInteger) returns BigInteger
method negate() returns BigInteger
method nextProbablePrime() returns BigInteger
method not() returns BigInteger
method or(val as BigInteger) returns BigInteger
method pow(exponent as int) returns BigInteger
class method probablePrime(bitLength as int, rnd as Random) returns BigInteger
method remainder(val as BigInteger) returns BigInteger
method setBit(n as int) returns BigInteger
method shiftLeft(n as int) returns BigInteger
method shiftRight(n as int) returns BigInteger
method signum() returns int
method subtract(val as BigInteger) returns BigInteger
method testBit(n as int) returns boolean
method toByteArray() returns byte[]
method toString() returns String
method toString(radix as int) named toStringRadix returns String
class method valueOf(val as long) returns BigInteger
method xor(val as BigInteger) returns BigInteger
end class
end package
From this, an LCB wrapper can be generated using the lc-compile-ffi-java
tool:
/Applications/LiveCode Business 9.0.0 (dp 6).app/Contents/Tools/Toolchain/lc-compile-ffi-java --modulename com.livecode.java.bigint --output /Users/alilloyd/Desktop/BigInteger.lcb /Users/alilloyd/Desktop/BigInteger.lcb-java
This results in a full wrapping of the class as a module, that can be used in LCB.
module com.livecode.java.bigint
use com.livecode.foreign
use com.livecode.java
foreign handler _JNI_BigInteger_BigIntegerFromBytes(in pParam_val as JByteArray) returns JObject binds to "java:java.math.BigInteger>new([B)"
foreign handler _JNI_BigInteger_BigIntegerFromString(in pParam_val as JString) returns JObject binds to "java:java.math.BigInteger>new(Ljava/lang/String;)"
foreign handler _JNI_BigInteger_BigIntegerFromStringWithRadix(in pParam_val as JString, in pParam_radix as CInt) returns JObject binds to "java:java.math.BigInteger>new(Ljava/lang/String;I)"
foreign handler _JNI_BigInteger_abs(in pObj as JObject) returns JObject binds to "java:java.math.BigInteger>abs()Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_add(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>add(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_and(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>and(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_andNot(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>andNot(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_bitCount(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>bitCount()I"
foreign handler _JNI_BigInteger_bitLength(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>bitLength()I"
foreign handler _JNI_BigInteger_clearBit(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>clearBit(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_compareTo(in pObj as JObject, in pParam_val as JObject) returns CInt binds to "java:java.math.BigInteger>compareTo(Ljava/math/BigInteger;)I"
foreign handler _JNI_BigInteger_divide(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>divide(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_divideAndRemainder(in pObj as JObject, in pParam_val as JObject) returns optional JObject binds to "java:java.math.BigInteger>divideAndRemainder(Ljava/math/BigInteger;)[Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_doubleValue(in pObj as JObject) returns CDouble binds to "java:java.math.BigInteger>doubleValue()D"
foreign handler _JNI_BigInteger_equals(in pObj as JObject, in pParam_x as JObject) returns CBool binds to "java:java.math.BigInteger>equals(Ljava/math/BigInteger/Object;)Z"
foreign handler _JNI_BigInteger_flipBit(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>flipBit(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_floatValue(in pObj as JObject) returns CFloat binds to "java:java.math.BigInteger>floatValue()F"
foreign handler _JNI_BigInteger_gcd(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>gcd(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_getLowestSetBit(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>getLowestSetBit()I"
foreign handler _JNI_BigInteger_hashCode(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>hashCode()I"
foreign handler _JNI_BigInteger_intValue(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>intValue()I"
foreign handler _JNI_BigInteger_isProbablePrime(in pObj as JObject, in pParam_certainty as CInt) returns CBool binds to "java:java.math.BigInteger>isProbablePrime(I)Z"
foreign handler _JNI_BigInteger_longValue(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>longValue()J"
foreign handler _JNI_BigInteger_max(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>max(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_min(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>min(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_mod(in pObj as JObject, in pParam_m as JObject) returns JObject binds to "java:java.math.BigInteger>mod(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_modInverse(in pObj as JObject, in pParam_m as JObject) returns JObject binds to "java:java.math.BigInteger>modInverse(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_modPow(in pObj as JObject, in pParam_exponent as JObject, in pParam_m as JObject) returns JObject binds to "java:java.math.BigInteger>modPow(Ljava/math/BigInteger;Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_multiply(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>multiply(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_negate(in pObj as JObject) returns JObject binds to "java:java.math.BigInteger>negate()Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_nextProbablePrime(in pObj as JObject) returns JObject binds to "java:java.math.BigInteger>nextProbablePrime()Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_not(in pObj as JObject) returns JObject binds to "java:java.math.BigInteger>not()Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_or(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>or(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_pow(in pObj as JObject, in pParam_exponent as CInt) returns JObject binds to "java:java.math.BigInteger>pow(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_probablePrime(in pParam_bitLength as CInt, in pParam_rnd as JObject) returns JObject binds to "java:java.math.BigInteger>probablePrime(ILjava/math/BigInteger/Random;)Ljava/math/BigInteger;!static"
foreign handler _JNI_BigInteger_remainder(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>remainder(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_setBit(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>setBit(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_shiftLeft(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>shiftLeft(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_shiftRight(in pObj as JObject, in pParam_n as CInt) returns JObject binds to "java:java.math.BigInteger>shiftRight(I)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_signum(in pObj as JObject) returns CInt binds to "java:java.math.BigInteger>signum()I"
foreign handler _JNI_BigInteger_subtract(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>subtract(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
foreign handler _JNI_BigInteger_testBit(in pObj as JObject, in pParam_n as CInt) returns CBool binds to "java:java.math.BigInteger>testBit(I)Z"
foreign handler _JNI_BigInteger_toByteArray(in pObj as JObject) returns JByteArray binds to "java:java.math.BigInteger>toByteArray()[B"
foreign handler _JNI_BigInteger_toString(in pObj as JObject) returns JString binds to "java:java.math.BigInteger>toString()Ljava/lang/String;"
foreign handler _JNI_BigInteger_toStringRadix(in pObj as JObject, in pParam_radix as CInt) returns JString binds to "java:java.math.BigInteger>toString(I)Ljava/lang/String;"
foreign handler _JNI_BigInteger_valueOf(in pParam_val as CInt) returns JObject binds to "java:java.math.BigInteger>valueOf(J)Ljava/math/BigInteger;!static"
foreign handler _JNI_BigInteger_xor(in pObj as JObject, in pParam_val as JObject) returns JObject binds to "java:java.math.BigInteger>xor(Ljava/math/BigInteger;)Ljava/math/BigInteger;"
public handler BigIntegerFromBytes_Constructor(in pParam_val as Data) returns JObject
variable tParam_val as JByteArray
put DataToJByteArray(pParam_val) into tParam_val
unsafe
return _JNI_BigInteger_BigIntegerFromBytes(tParam_val)
end unsafe
end handler
public handler BigIntegerFromString_Constructor(in pParam_val as String) returns JObject
variable tParam_val as JString
put StringToJString(pParam_val) into tParam_val
unsafe
return _JNI_BigInteger_BigIntegerFromString(tParam_val)
end unsafe
end handler
public handler BigIntegerFromStringWithRadix_Constructor(in pParam_val as String, in pParam_radix as Number) returns JObject
variable tParam_val as JString
put StringToJString(pParam_val) into tParam_val
unsafe
return _JNI_BigInteger_BigIntegerFromStringWithRadix(tParam_val, pParam_radix)
end unsafe
end handler
public handler BigInteger_abs(in pObj as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_abs(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_add(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_add(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_and(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_and(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_andNot(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_andNot(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_bitCount(in pObj as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_bitCount(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_bitLength(in pObj as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_bitLength(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_clearBit(in pObj as JObject, in pParam_n as Number) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_clearBit(pObj, pParam_n) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_compareTo(in pObj as JObject, in pParam_val as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_compareTo(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_divide(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_divide(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_divideAndRemainder(in pObj as JObject, in pParam_val as JObject) returns List
variable tJNIResult as List
unsafe
put _JNI_BigInteger_divideAndRemainder(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_doubleValue(in pObj as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_doubleValue(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_equals(in pObj as JObject, in pParam_x as JObject) returns Boolean
variable tJNIResult as Boolean
unsafe
put _JNI_BigInteger_equals(pObj, pParam_x) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_flipBit(in pObj as JObject, in pParam_n as Number) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_flipBit(pObj, pParam_n) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_floatValue(in pObj as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_floatValue(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_gcd(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_gcd(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_getLowestSetBit(in pObj as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_getLowestSetBit(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_hashCode(in pObj as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_hashCode(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_intValue(in pObj as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_intValue(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_isProbablePrime(in pObj as JObject, in pParam_certainty as Number) returns Boolean
variable tJNIResult as Boolean
unsafe
put _JNI_BigInteger_isProbablePrime(pObj, pParam_certainty) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_longValue(in pObj as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_longValue(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_max(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_max(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_min(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_min(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_mod(in pObj as JObject, in pParam_m as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_mod(pObj, pParam_m) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_modInverse(in pObj as JObject, in pParam_m as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_modInverse(pObj, pParam_m) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_modPow(in pObj as JObject, in pParam_exponent as JObject, in pParam_m as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_modPow(pObj, pParam_exponent, pParam_m) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_multiply(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_multiply(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_negate(in pObj as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_negate(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_nextProbablePrime(in pObj as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_nextProbablePrime(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_not(in pObj as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_not(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_or(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_or(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_pow(in pObj as JObject, in pParam_exponent as Number) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_pow(pObj, pParam_exponent) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_probablePrime(in pParam_bitLength as Number, in pParam_rnd as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_probablePrime(pParam_bitLength, pParam_rnd) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_remainder(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_remainder(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_setBit(in pObj as JObject, in pParam_n as Number) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_setBit(pObj, pParam_n) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_shiftLeft(in pObj as JObject, in pParam_n as Number) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_shiftLeft(pObj, pParam_n) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_shiftRight(in pObj as JObject, in pParam_n as Number) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_shiftRight(pObj, pParam_n) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_signum(in pObj as JObject) returns Number
variable tJNIResult as Number
unsafe
put _JNI_BigInteger_signum(pObj) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_subtract(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_subtract(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_testBit(in pObj as JObject, in pParam_n as Number) returns Boolean
variable tJNIResult as Boolean
unsafe
put _JNI_BigInteger_testBit(pObj, pParam_n) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_toByteArray(in pObj as JObject) returns Data
variable tJNIResult as JByteArray
unsafe
put _JNI_BigInteger_toByteArray(pObj) into tJNIResult
end unsafe
return DataFromJByteArray(tJNIResult)
end handler
public handler BigInteger_toString(in pObj as JObject) returns String
variable tJNIResult as JString
unsafe
put _JNI_BigInteger_toString(pObj) into tJNIResult
end unsafe
return StringFromJString(tJNIResult)
end handler
public handler BigInteger_toStringRadix(in pObj as JObject, in pParam_radix as Number) returns String
variable tJNIResult as JString
unsafe
put _JNI_BigInteger_toStringRadix(pObj, pParam_radix) into tJNIResult
end unsafe
return StringFromJString(tJNIResult)
end handler
public handler BigInteger_valueOf(in pParam_val as Number) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_valueOf(pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
public handler BigInteger_xor(in pObj as JObject, in pParam_val as JObject) returns JObject
variable tJNIResult as JObject
unsafe
put _JNI_BigInteger_xor(pObj, pParam_val) into tJNIResult
end unsafe
return tJNIResult
end handler
end module
This module can then be used to implement a library for performing big integer arithmetic:
use com.livecode.java.bigint
public handler AddBigIntegers(in pLeft as String, in pRight as String) returns String
unsafe
variable tLeftBigInt as JObject
variable tRightBigInt as JObject
put BigIntegerFromString_Constructor(pLeft) into tLeftBigInt
put BigIntegerFromString_Constructor(pRight) into tRightBigInt
variable tSumBigInt as JObject
put BigInteger_add(tLeftBigInt, tRightBigInt) into tSumBigInt
return BigInteger_toString(tSumBigInt)
end unsafe
end handler
I hope you find this new functionality for LiveCode useful. I can’t wait to see all the new Android widgets and addons that it enables starting to be created! I’m really looking forward to bringing you further parts of the Infinite LiveCode project in the future.
17 comments
Join the conversationPaul Dupuis - March 9, 2017
I am delighted that LC9 continues to expand what can be done. I hope LiveCode someday publishes an idiot’s guide to Infinite Livecode, because what I don’t get is the steps to get from this blot post to command/functions usable in scripts.
If I google Java BigInteger Library, I get https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html
When I look at this library I see a bunch of functions, and being barely familiar with Java (I took 1 class nearly 20 years ago), I guess, I could write something like
put BigInteger(“1234567890″) into X — using the BigInteger(String val) constructor to translates the decimal String representation of a BigInteger into a BigInteger.
put add(X,”9876543210”) into X — using add(BigInteger val)
put toString(X) into fld “somefield” — using toString()
and end up with “11111111100”?
Could you perhaps expand your BigInteger example at some point to show how one can get from the blog post to command/functions usable in scripts?
Ali Lloyd - March 9, 2017
Hi Paul,
Essentially what you write is correct, although the foreign bindings are only available within LiveCode Builder (until you provide some sort of LCB library interface to use it in LiveCode Script). So for example, if you just wanted to be able to add two big integers in LCS, you could put some of the above together to write an LCB library as follows:
Once that library is loaded, you could then do
to obtain 11111111100 in the message box (for example).
Does that make things a little clearer?
Michael T Walas - March 9, 2017
Will this new functionality eventually help with further HTML5 development like networking and databases?
Ali Lloyd - March 13, 2017
Unfortunately not, no. As far as I know we have no way of being able to load the Java Runtime Environment from the HTML5 engine.
Paul McClernan - March 10, 2017
Sweet, I can’t wait to have time to delve into this more!
https://developer.android.com/reference/android/os/BatteryManager.html
So looking at the above page we can can add more to this lib like so:
public handler GetBatteryPlugged() returns Number
unsafe
// Get the application context. We rely on an undocumented
// method to get this ‘statically’ at the moment.
variable tClass as JObject
put ClassForName(StringToJString(“android.app.ActivityThread”)) into tClass
variable tMethod as JObject
put GetCurrentApplicationMethod(tClass, StringToJString(“currentApplication”), nothing) into tMethod
variable tContext as JObject
put MethodInvoke(tMethod, nothing, nothing) into tContext
// Get an intent filter for the battery changed intent
variable tFilter as JObject
put GetIntentFilter(StringToJString(“android.intent.action.BATTERY_CHANGED”)) into tFilter
// Get the intent
variable tIntent as JObject
put ApplicationRegisterReceiver(tContext, nothing, tFilter) into tIntent
// Get the relevant values from the intent.
variable tLevel as Integer
put GetIntentValueInt(tIntent, StringToJString(“plugged”), -1) into tPlugBool
return tPlugBool
end unsafe
end handler
Paul McClernan - March 10, 2017
err
forgot to edit :
variable tLevel as Integer
should read
variable tPlugBool as Integer
Paul McClernan - March 10, 2017
Probably be nicer if it was returning a string “Plugged” or “Unplugged”
Ali Lloyd - March 13, 2017
Yep, that looks like it would work! For a library full of battery utilities you would probably want to store the application context in a variable so as not to have to fetch it each time.
MaxV - March 21, 2017
Any RSS of this blog? How can I advertise it with my friends?
Ali Lloyd - March 21, 2017
Hi Max, https://livecode.com/blog/feed/ should work!
Paul McClernan - March 28, 2017
OK, I actually go around to resetting up to build for Android and tried to build the battery library. It seems to compile correctly but then fails building the Android apk package. It’s something to do with the manifest or something (not at home right now). I see there is a manifest file generated in the same folder as the compiled, is there something more that needs to be set so this file overrides the normal default manifest or something?
T.I.A.
Ali Lloyd - March 28, 2017
The manifest is not affected by the inclusion or otherwise of the LCB module, so if you are getting an error to do with manifests I suspect it will happen with a stack with nothing on it too…
Paul McClernan - March 29, 2017
Sorry my bad, it was actually a missing provisioning file for iOS build (obviously not needed here). Thanks.
Paul McClernan - March 29, 2017
Got things to compile, but not getting any functionality, installed apk phone but get no results from GetBatteryPercentage(), also tried the JAVA Big Integer code on Mac OS X but getting domain error when I try AddBigIntegers(“12345″,12345”). My environment is set up correctly and everything compiles installs as library extension fine. “Hint Runtime” isn’t very helpful, is there fully tested & working code on on git hub or somewhere? Should probably move this to a forum discussion.
Thanks Ali
Paul McClernan - March 29, 2017
AddBigIntegers(“12345″,”12345”), that was a typo, not the problem.
Ali Lloyd - March 29, 2017
OK I have started a forum topic here: http://forums.livecode.com/viewtopic.php?f=93&t=29062
Paul McClernan - April 26, 2017
I set up a Git project with the Android Battery example with a few more functions added (I’m not sure if all of the functions work correctly though, I haven’t done much testing).
https://github.com/PaulMcClernan/LCAndroidBatteryLib