Is there any documentation regarding the WebView JavaScript Bridge? I am looking for documentation that describes the capabilities and supported data types for methods defined within the "JavascriptInterface".
For example if I define the following:
public class JavaScriptInterface {
public int incrementNumber(int num) {
return num + 1;
}
If I call this method from within JavaScript and run it in the emulator, everything seems to work fine. If I run this on my NexusOne, the passed in "num" argument is always "0".
If I change the above to:
public class JavaScriptInterface {
public int incrementNumber(String num) {
// Leaving out try/catch
int tempNum = newRadius = Integer.parseInt(num);
return tempNum + 1;
}
... everything seems to work. So I am wondering if JavaScriptInterface method arguments should/can only be of type String?
Relevant resources:
http://developer.android.com/reference/android/webkit/WebView.html
http://developer.android.com/reference/android/webkit/WebView.html#addJavascriptInterface(java.lang.Object, java.lang.String)
http://code.google.com/apis/maps/articles/android_v3.html
You can either require String args on the Java side or ensure that numbers are actual numbers (and not text versions of numbers - see about.com - JavaScript: Strings to Numbers) on the JavaScript side.
The only relevant official doc is here:
http://developer.android.com/guide/webapps/webview.html
But no description about the available types
Related
I'm new to Flutter. And I get an error like this: The final variable 'articleC' can't be read because it is potentially unassigned at this point.
Here is where I use:
Navigator.push(
context,
MaterialPageRoute(
builder: (ctx) => DetailScreen(article: articleC)));
I have added an article class on the detail page as required. Here, when I call the detail class, it asks me for an article class, and I give it an article class. But I get an error as above.
I defined the article class as : final Article articleC;
If you can share a little bit of code, it will be easier to trace. Also the error you've got could also be found in the documentation.
read_potentially_unassigned_final
The final variable ‘{0}’ can’t be read because it’s potentially
unassigned at this point.
Description
The analyzer produces this diagnostic when a final local variable that
isn’t initialized at the declaration site is
read at a point where the compiler can’t prove that the variable is
always initialized before it’s referenced.
There could be a possibility that your code read the variable articleC prior to being declared or initialized.
Check the example from the documentation for the possible solution:
Example The following code produces this diagnostic because the final local variable x is read (on line 3) when it’s possible
that it hasn’t yet been initialized:
int f() {
final int x;
return x;
}
Common fixes
Ensure that the variable has been initialized before it’s read:
int f(bool b) {
final int x;
if (b) {
x = 0;
} else {
x = 1;
}
return x;
}
I'm trying to send data back and forth from Flutter to my native platform (in this case Android).
In order to keep some model consistency, I have generated the models for all platforms by using Protocol-Buffers.
When I try to pass data from Android to Flutter I'm not finding any way to do it without shenanigans like serializing to a handcrafted JSON.
There must be a way to use protobuf in order to do so, isn't it?
In order to give context, I have made a minimal app to try to solve this problem:
My Protocol Buffer
syntax = "proto3";
option java_package = "com.test.protobuf_test";
option java_outer_classname = "ProtoModel";
message SimplePerson {
int32 id= 1;
string name= 2;
}
From which I generate my model using:
protoc --java_out and protoc --dart_out
In Dart I get my class
class SimplePerson extends $pb.GeneratedMessage {...}
And in Java
public final class ProtoModel {
...
public static final class SimplePerson extends
com.google.protobuf.GeneratedMessageV3 implements
SimplePersonOrBuilder {...}
}
From Android inside my method channel, I am trying to pass one or many ProtoModel.SimplePerson objects back to Dart.
No success so far.
How would you actually do it?
I'd expect it to be something like
In Java:
ProtoModel.SimplePerson person = ProtoModel.SimplePerson.newBuilder().setId(3).setName("Person Name").build();
result(person);
And in Dart:
var result = await platform.invokeMethod("generatePerson");
if(result is SimplePerson) {
print("Success!");
} else {
print("Failure!");
}
So far I'm only getting Failures or Exceptions.
Thanks!
your very close your using result but i have it working with result.success
when (call.method) {
"getPlatformVersion" -> result.success(getPlatformVersion().toByteArray())
}
private fun getPlatformVersion(): Models.Version {
return Models.Version.newBuilder().setVersionName("Android ${android.os.Build.VERSION.RELEASE}").build()
}
great example here https://www.freecodecamp.org/news/flutter-platform-channels-with-protobuf-e895e533dfb7/
EDIT didnt see how old this post was
I have to use this as Pigeon is sill early access, and although pigeon was generally harder to set up i do prefer it
I am coding in Haxe, Lime, OpenFl. I am trying to set up a Class to store data in a Map, referenced by class instance. The class type is to be passed in the constructor, via inference. But I am quite new to all this and can't quite figure out the syntax, this is what I got so far:
class DynamicStore<A>
{
private var hashA:Map<Class<A>,String>;
public function new<A>(paramA:Class<A>) {
hashA = new Map();
}
}
But this gives me the following error:
Abstract Map has no #:to function that accepts IMap<Class<DynamicStore.A>, String>
Is there a way to do this?
A question first:
do you really want to use classes as key? or objects?
In classes should be the key
It would be much simpler to use the classe's full name as key, like "mypackage.blob.MyClass". It's safer, easier to handle and debug.
Map<String, String>
Would suffice in that case.
If objects should be keys
Then the code would look like:
import haxe.ds.ObjectMap;
class Test<A>
{
static function main() {}
private var hashA :ObjectMap<A,String>;
public function new(paramA:A) {
hashA = new ObjectMap<A,String>();
}
}
The reason "Map" cannot be directly used in this case is that "Map" is a syntactic sugar, being resolved to StringMap, IntMap or others depending on the key type. If it doesn't know what kind of map to be used, it cannot proceed (this is mainly due to cross-compiling issues).
Remark
As a final note, I would mention your construction seems a bit wacky/strange to me. It would be interesting to know what you are trying to achieve and why you structure it the way you do.
I don't think you can use Class as the key of a Map. A good work around it to use a String as a key and the fully qualified names of the types. You can also define an abstract to move from the Type to String easily ... something like the following (code not-tested);
private var hashA : Map<String, String>;
public function addClass(className : ClassId, ...)
And the abstract will look something like this:
abstract ClassId(String) {
inline public function new(name : String) this = name;
#:from public static inline function fromClass(cls : Class<Dynamic>)
return new ClassId(Type.getClassName(cls));
#:to public inline function toClass() : Class<Dynamic>
return Type.resolveClass(this);
#:to public inline function toString() : String
return this;
}
I just switched over from iPhone to Android and am looking for something similar to where in the iPhone SDK, when a class finishes a certain task, it calls delegate methods in objects set as it's delegates.
I don't need too many details. I went through the docs and didn't find anything (the closest I got was "broadcast intents" which seem more like iOS notifications).
Even if someone can point me to the correct documentation, it would be great.
Thanks!
Never mind... found the answer here :)
http://www.javaworld.com/javaworld/javatips/jw-javatip10.html
Pasting from the article so as to preserve it:
Developers conversant in the event-driven programming model of MS-Windows and the X Window System are accustomed to passing function pointers that are invoked (that is, "called back") when something happens. Java's object-oriented model does not currently support method pointers, and thus seems to preclude using this comfortable mechanism. But all is not lost!
Java's support of interfaces provides a mechanism by which we can get the equivalent of callbacks. The trick is to define a simple interface that declares the method we wish to be invoked.
For example, suppose we want to be notified when an event happens. We can define an interface:
public interface InterestingEvent
{
// This is just a regular method so it can return something or
// take arguments if you like.
public void interestingEvent ();
}
This gives us a grip on any objects of classes that implement the interface. So, we need not concern ourselves with any other extraneous type information. This is much nicer than hacking trampoline C functions that use the data field of widgets to hold an object pointer when using C++ code with Motif.
The class that will signal the event needs to expect objects that implement the InterestingEvent interface and then invoke the interestingEvent() method as appropriate.
public class EventNotifier
{
private InterestingEvent ie;
private boolean somethingHappened;
public EventNotifier (InterestingEvent event)
{
// Save the event object for later use.
ie = event;
// Nothing to report yet.
somethingHappened = false;
}
//...
public void doWork ()
{
// Check the predicate, which is set elsewhere.
if (somethingHappened)
{
// Signal the even by invoking the interface's method.
ie.interestingEvent ();
}
//...
}
// ...
}
In that example, I used the somethingHappened predicate to track whether or not the event should be triggered. In many instances, the very fact that the method was called is enough to warrant signaling the interestingEvent().
The code that wishes to receive the event notification must implement the InterestingEvent interface and just pass a reference to itself to the event notifier.
public class CallMe implements InterestingEvent
{
private EventNotifier en;
public CallMe ()
{
// Create the event notifier and pass ourself to it.
en = new EventNotifier (this);
}
// Define the actual handler for the event.
public void interestingEvent ()
{
// Wow! Something really interesting must have occurred!
// Do something...
}
//...
}
That's all there is to it. I hope use this simple Java idiom will make your transition to Java a bit less jittery.
The pendant for kotlin.
Define your interface: In my example I scan a credit card with an external library.
interface ScanIOInterface {
fun onScannedCreditCard(creditCard: CreditCard)
}
Create a class where you can register your Activity / Fragment.
class ScanIOScanner {
var scannerInterface: ScanIOInterface? = null
fun startScanningCreditCard() {
val creditCard = Library.whichScanCreditCard() //returns CreditCard model
scannerInterface?.onScannedCreditCard(creditCard)
}
}
Implement the interface in your Activity / Fragment.
class YourClassActivity extends AppCompatActivity, ScanIOInterface {
//called when credit card was scanned
override fun onScannedCreditCard(creditCard: CreditCard) {
//do stuff with the credit card information
}
//call scanIOScanner to register your interface
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val scanIOScanner = ScanIOScanner()
scanIOScanner.scannerInterface = this
}
}
CreditCard is a model and could be define however you like. In my case it includes brand, digits, expiry date ...
After that you can call scanIOScanner.startScanningCreditCard() wherever you like.
The main content of this video tutorial is to show how to use interfaces to delegate methods / data exchange between different Fragments and activities, but it is great example to learn how delegate pattern can be implemented in Java for Android.
Java callback is not the same thing like ios delegate, in ios you can use a callback almost the same way like in Android. In Android there is startActivityForResult that can help you to implement the tasks for what ios delegate is used.
I believe ListAdapter is a example of delegation pattern in Android.
Kotlin's official Delegation pattern:
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
See: https://kotlinlang.org/docs/delegation.html
I believe that it's possible to call Java methods from (PhoneGap) Javascript.
Anyone knows how to do that?? (I know how to do it by changing the source code of PhoneGap, but I'd avoid that)
I finally made it work.
Create a class with methods you want to use:
public class MyClass {
private WebView mAppView;
private DroidGap mGap;
public MyClass(DroidGap gap, WebView view)
{
mAppView = view;
mGap = gap;
}
public String getTelephoneNumber(){
TelephonyManager tm =
(TelephonyManager) mGap.getSystemService(Context.TELEPHONY_SERVICE);
String number = tm.getLine1Number();
return number;
}
}
In your main activity add a Javascript interface for this class:
public class Main extends DroidGap
{
private MyClass mc;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
super.init();
mc = new MyClass(this, appView);
appView.addJavascriptInterface(mc, "MyCls");
super.loadUrl(getString(R.string.url));
}
}
In Javascript call window.MyCls methods:
<script>
$(function(){
$("#phone").text("My telephone number is: " +
window.MyCls.getTelephoneNumber());
});
</script>
Note:
As mentioned in the comment, for Android version 4.2 and above, add #JavascriptInterface to the method which you want to access from your HTML page. Reference.
addJavaScriptInterface(mc, "MyCls") without Gap init()ed may cause crush of the app, you'd better add super.init() before addJavascriptInterface()
public class Main extends DroidGap
{
private MyClass mc;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
super.init();
mc = new MyClass(this, appView);
appView.addJavascriptInterface(mc, "MyCls");
super.loadUrl(getString(R.string.url));
}
}
PhoneGap has a decent Plugin API. You'd write the plugin in Java by implementing the IPlugin interface. Most of the magic is in the execute() function.
public interface IPlugin {
/**
* Executes the request and returns PluginResult.
*
* #param action The action to execute.
* #param args JSONArry of arguments for the plugin.
* #param callbackId The callback id used when calling back into JavaScript.
* #return A PluginResult object with a status and message.
*/
PluginResult execute(String action, JSONArray args, String callbackId);
// ... more ...
}
The best way to start writing a plugin is by writing the javascript API first. You would typical start by writing a custom javascript class, and in each method on the javascript class, marshal the variables and call into the plugin you developed using the Phonegap.exec() method. Here is the method signature for your reference.
/* src/com/phonegap/api/PluginManager.java */
/**
* Receives a request for execution and fulfills it by finding the appropriate
* Java class and calling it's execute method.
*
* PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded
* string is returned that will indicate if any errors have occurred when trying to find
* or execute the class denoted by the clazz argument.
*
* #param service String containing the service to run
* #param action String containt the action that the class is supposed to perform. This is
* passed to the plugin execute method and it is up to the plugin developer
* how to deal with it.
* #param callbackId String containing the id of the callback that is execute in JavaScript if
* this is an async plugin call.
* #param args An Array literal string containing any arguments needed in the
* plugin execute method.
* #param async Boolean indicating whether the calling JavaScript code is expecting an
* immediate return value. If true, either PhoneGap.callbackSuccess(...) or
* PhoneGap.callbackError(...) is called once the plugin code has executed.
*
* #return JSON encoded string with a response message and status.
*/
#SuppressWarnings("unchecked")
public String exec(final String service, final String action,
final String callbackId, final String jsonArgs,
final boolean async)
You also need to register the Plugin. You do this by adding the registration code at the bottom of your custom javascript library.
In the example below, the author defined a javascript BarcodeScanner class and registers it using the addConstructor method.
Two steps are carried out in the addConstructor:
Create a new instance of BarcodeScanner in javascript and registers it.
This is accessible in javascript as window.plugins.barcodeScanner
Registers the custom Plugin class with a service name. This service name
is passed in as the first argument to PhoneGap.exec so that PhoneGap
can instantiate the java plugin class and call the execute() method on it.
Sample registration code:
PhoneGap.addConstructor(function() {
/* The following registers an instance of BarcodeScanner in window.plugins.barcodeScanner */
PhoneGap.addPlugin('barcodeScanner', new BarcodeScanner());
/* The following associates a service name BarcodeScanner with a class com.beetight.barcodescanner.BarcodeScanner */
/* The service name is the first argument passed into PhoneGap.exec */
PluginManager.addService("BarcodeScanner","com.beetight.barcodescanner.BarcodeScanner");
});
a simpler form:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();
super.appView.getSettings().setJavaScriptEnabled(true);
super.appView.addJavascriptInterface(this, "MyCls");
super.loadUrl("file:///android_asset/www/login.html");
}
If anyone gets nullPointer exception using the code above, do super.oncreate() first and then super..init()
super.onCreate(savedInstanceState);
super.init();
I found this solution here: Phonegap Google Group
Thanks a lot to #zorglub76 for the solution....
Communication from JavaScript to native is achieved by overriding the JavaScript prompt function in the Android native code and the message passed is much like that used in iOS. We used to use WebView.addJavascriptInterface to add Java objects directly to the JavaScript sandbox but that was causing some devices to crash with Android 2.3. To call JavaScript from native we currently use WebView.loadUrl(”javascript:…”) but that has some problems so we are soon moving over to polling a Java message queue calling a local HTTP server via a long-lived XHR connection.
Description by here