how does the ReadableMap interface in React-native convert JS to JAVA? - javascript

I'm trying to understand how to properly pass options from the js side of react-native to the java side.
The react-native bridge uses ReadableMap to convert but as a Java noob I'm failing to understand how this works.
More specifically I don't understand:
how the actual conversion works?
how to tell what type/format the downstream Java code wants?
how to properly use ReadableMap to do this?
Generally I would like to know how this works but I'll give the specific example I'm looking at to give some context.
A react-native package exposes a datetime picker.
The JS side has a showTimePicker method:
showTimePicker(date, callback) {
date = date || new Date();
console.log(this.props);
debugger
var options = {
...this.props,
hour:date.getHours(),
minute:date.getMinutes()
};
RCTDateTimePicker.showTimePicker(options, function (hour, minute) {
date.setHours(hour);
date.setMinutes(minute);
callback(date);
});
}
RCTDateTimePicker.showTimePicker is bridged to a Java method on the native side:
#ReactMethod
public void showTimePicker(ReadableMap options, Callback callback) {
DialogFragment timePicker = new TimePicker(options, callback);
timePicker.show(activity.getFragmentManager(), "timePicker");
}
which calls
public TimePicker(ReadableMap options, Callback callback) {
final Calendar c = Calendar.getInstance();
this.callback = callback;
hour = options.hasKey("hour") ? options.getInt("hour") : c.get(Calendar.HOUR_OF_DAY);
minute = options.hasKey("minute") ? options.getInt("minute") : c.get(Calendar.MINUTE);
}
which creates and instance of an Android TimePicker. My goal is to change the style of the Timepicker from watch to spinner.
There is a settable XML attribute android:timePickerMode="spinner" but I don't think I can pass an XML attribute through react-native can I?
I also found a suggestion in comments to pass defStyleAttr to do this, but I don't understand how.

First, see the list of Argument Types from the React Native document for Android Native Modules.
Argument Types
The following argument types are supported for methods
annotated with #ReactMethod and they directly map to their JavaScript
equivalents
Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array
So, you can pass the extra field for timePickerMode as a String by updating the options that the JS side passes to include it.
var options = {
...this.props,
hour:date.getHours(),
minute:date.getMinutes(),
timePickerMode:"spinner"
};
And then get that value from the ReadableMap in your custom TimePicker constructor.
String timePickerMode = options.hasKey("timePickerMode") ?
options.getString("timePickerMode") : defaultTimePickerMode;
Now you have the value you wish to set. Unfortunately it does not appear that timePickerMode can be set programmatically. This SO question asks how to do it and #alanv (whose profile indicates they are a Software Engineer at Google) indicates that it is not possible at runtime.

Related

Frida Casting object to List of Strings

I have been trying to print out the contents of a list when hooking an android app with Frida but am not having any luck.
The object I want to hook in Java looks like this
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.List;
public final class Hello extends HelloParent{
#JsonIgnore
public final List sampleList;
}
There aren't any getters for this public object so I have to resort to using another object (Let's call the object "Bye")'s method (byeMethodB) to monitor this value.
This is what my frida-script looks like:
setTimeout(function() {
Java.perform(function(){
Java.use("Bye").byeMethodA.implementation = function(){
try{
//Returns a Hello object
var helloObject = Java.cast(this.byeMethodB(),Java.use("Hello"))
printListContent(Java.cast(helloObject.sampleList,Java.use("java.util.List"))))
}catch(err){
console.log(err)
}
}
})
},1000)
function printListContent(list){
var listIter = list.iterator()
while(listIter.hasNext()){
console.log(listIter.next())
}
}
Without casting the "helloObject.sampleList" object to a list, the output looks like this:
[object Object]
So I am sure it is not null
If I cast using Java.cast(helloObject.sampleList,Java.use("java.util.List")),
I get the following error:
I have also tried:
Java.cast(helloObject.sampleList,Java.use("java.util.List<>"))
(I am pretty sure its a String)
Java.cast(helloObject.sampleList,Java.use("java.util.List<String>"))
Java.cast(helloObject.sampleList,Java.use("java.util.List<java.lang.String>"))
Java.cast(helloObject.sampleList,Java.use("[String"))
Java.cast(helloObject.sampleList,Java.use("[Ljava.lang.String"))
It is not going well at all. Would appreciate some help
In Frida accessing fields is not identical as in Java. If you execute helloObject.sampleList in Frida you are getting the JavaScript object that describes the field, not the field value itself.
If you want the field value you have to execute helloObject.sampleList.value.
Therefore the following code should work:
Java.cast(helloObject.sampleList.value, Java.use("java.util.List"));
Generics only exists at compile time but frida is working at run-time. Therefore java.util.List<> and the other class names with angle bracket will never work.

Send Object from Javascript to Kotlin using Webview

I have loaded a webpage using WebView component and added a JavascriptInterface. Please check the code below,
val webview = WebView(this)
setContentView(webview)
webview.settings.javaScriptEnabled = true
webview.loadUrl(HOME_PAGE_URL)
webview.addJavascriptInterface(JavascriptInterface(),”javascript_bridge”)
And when I call the invoke from Javascript using window.javascript_bridge.showToast(“Information Saved”);
private inner class JavascriptInterface
{
#android.webkit.JavascriptInterface
fun showToast(text: String?)
{
Log.d("WEBVIEW", text);
}
}
I am able to call the method from Javascript to Kotlin without any trouble.
But now I want to pass an Object from Javascript to Kotlin like below,
var info = {
message: “Information Saved”,
ID: 123456
}
And when I call the invoke from Javascript using window.javascript_bridge.showToast(info);
I tried to change to the data type to Any, but the value passed from Javascript is null
private inner class JavascriptInterface
{
#android.webkit.JavascriptInterface
fun showToast(text: Any?)
{
Log.d("WEBVIEW", text.toString());
}
}
As far as i know, the javaScript interface methods only accepts primitive types of data as parameters (as discussed on this question).
If you still want to achieve that, you may serialize the object (to JSON format, for instance) in the javaScript and then Deserialize it in Java.
Hope it helps :)

How can I invoke a class method in Swift if I only have the string?

I am writing a cordova plugin for HealthKit and my task is to make sure that we can dynamically read more or less data from HK. Im doing the native part in swift. But I mostly write JavaScript so I am a bit lost in with the Swift part.
I want to be able to dynamically invoke methods having only a string.
let store = HKHealthStore()
do {
let bloodType: HKBloodTypeObject = try store.bloodType()
...
That is an example to read blood type. I am not a Swift developer, but is there any way I can do this dynamically, like I would do in javascript:
... // assuming this function receives a string as paramater
let param[0] = try store[param[0]]() // <-- how to do this in Swift?
then i can talk to a server and get a list of characteristics and load them dynamically from healthkit without having to update my code and hardcode for each possible characteristic.
Create a mapping from your parameter strings to an API enumeration.
enum API: String {
case function1 = "function1"
case function2 = "functionTwo"
case function3 = "threethreethree"
}
Create a relay function that maps the API enumeration to the Swift function.
func callTheCorrectFunction(_ api: API) {
switch api {
case .function1: updateBlood()
case .function2: spinAround()
case .function3: jump()
}
}
Construct your function call using enum's rawValue initializer.
let param = fromSomeJsonStrings[0]
let api = API(rawValue: param)
callTheCorrectFunction(api)
Alternatively, you could use a dictionary mapping from [String: Function] in much the same way.
typealias Function = () -> ()
let dict = {
"function1": updateBlood,
"function2": spinAround,
}
dict[param[0]]()

Supply JavaScript date to Nashorn script

I am working on an API in Java that allows users to write scripts and access a specific set of methods that are passed in (in the form of an API object) by the Nashorn script engine.
I want to, in the JavaScript, call a function getDate(), which will return some arbitrary date (as a native JavaScript date) that's provided from the Java side.
I have tried simply putting an org.java.util.Date on the API object, but that won't behave like a JS date. The goal is to make this as simple as possible for end-users who are experienced with JS.
Java Example:
public class MyAPI {
public void log(String text){
System.out.println(text);
}
public Date getDate(){
// Return something that converts to a native-JS date
}
public static void main(){
// MyClassFilter implements Nashorn's ClassFilter
ScriptEngine engine = new NashornScriptEngineFactory().getScriptEngine(new MyClassFilter());
((Invokable) engine).invokeFunction("entryPoint", new MyAPI());
}
JavaScript example
function entryPoint(myApi){
var date = myApi.getDate();
myApi.log(date.getMinutes());
}
jdk.nashorn.internal.* packages are nashorn internal implementation classes. There is no guarantee that these won't be changed or be removed between JDK versions. Besides with a security manager around, accessing these packages from java code directly would result in SecurityException being thrown! With jdk9 modular jdk, these packages are not exported packages from nashorn module and so javac won't even compile your code in jdk9!
I'd recommend using JS wrapper (solution 1) in the answer by user "ug_". If you do have to call from Java, you can use API exported from jdk.nashorn.api.scripting package.
If "engine" is your javax.script.ScriptEngine of nashorn, then you can do something like the following:
import jdk.nashorn.api.scripting.*;
..
public Object getDate() {
// get JS Date constructor object - you can get once and store
// as well/
JSObject dateConstructor = (JSObject) engine.eval("Date");
// now do "new" on it
return dateConstructor.newObject();
}
With that, your JS script can call "getDate()" on your API object and get a JS
Date object. Note that you can also pass constructor arguments to newObject
method call (it is a Java variadic method).
The Nashorn engine has objects it uses internally which represent the Javascript objects. As you have guessed the java.util.Date != new Date() (in javascript). The engine uses a class called jdk.nashorn.internal.objects.NativeDate to represent its JS date.
If I were building this out I would not have the NativeDate constructed in the Java but instead have a wrapper in Javascript for the MyApi object which would contain a few other native JS methods, such as getDate().
var MYAPI_JAVASCRIPT = {
log: function() {
print(arguments);
},
getDate: function() {
return new Date();
}
}
You could then pass that object as the method parameter.
However if your really set on using the NativeDate in your Java code then you can construct one like so:
public NativeDate getDate() {
return (NativeDate) NativeDate.construct(true, null);
}

How to create an C#.Net DLL to use in JavaScript

I have created a .Net DLL with few simple classes. I have registered the DLL using RegAsm and I got a message that types were registered successfully.
RegAsm Syntax Used :
C:\Windows\Microsoft.NET\Framework\v4.0.30319>RegAsm.exe "D:\Projects\TestDLL.Core.dll"
C# Code:
namespace MyTestDLL.Core
{
public class PacketInfo
{
// constructor
public PacketInfo()
{
}
public string HostName { get; set; }
// and so on ......
}
}
I have set the ComVisible property to true in AssemblyInfo.cs file of this DLL.
// [assembly: ComVisible(true)]
However when I create an object out of it in JavaScript and run the script in Command prompt , I'm getting either it is not an object or null.
JS Code :
var testObj = new ActiveXObject(MyTestDLL.Core.PacketInfo);
testObj.HostName = "Test";
Can anyone advise me how to resolve this ?
You need to add a ProgId attribute to the class, something like this:
[Guid("some guid that you will never change")]
[ProgId("MyLib.PacketInfo")] // this is arbitrary
public class PacketInfo
{
....
}
The guid is optional, but if you don't set it yourself, it will be something you won't control, so it's better to define it. And of course I did not add the ComVisible because you already defined it at assembly level (I personnally prefer to set it at class level).
Then, once registered, you shall be able to use it like this (use the progid as a string):
var testObj = new ActiveXObject('MyLib.PacketInfo');
testObj.HostName = "Test";
I was able to achieve that by adding the following line to My DLL just above the class,
[Guid("A32AB771-9967-4604-B193-57FAA25557D4"), ClassInterface(ClassInterfaceType.None)]
Earlier I wasn't having the ClassInterfaceType part in my code.
Also ensure that each of your classes are having unique GUID.
FYI : You can create GUID using Visual Studio Tools GUID Creator GUI.

Categories

Resources