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
Related
I'm a newbie and I want to use CameraView library, the "toFile" function has two parameters. What does "callback" mean here?
camera.addCameraListener(new CameraListener() {
#Override
public void onPictureTaken(PictureResult result) {
result.toFile(file, **callback**);
}
});
camera.takePicture();
It will notify you when the file has been written.
Extract from the source code:
/**
* Receives callbacks about a file saving operation.
*/
public interface FileCallback {
/**
* Notifies that the data was succesfully written to file.
* This is run on the UI thread.
* Returns a null object if an exception was encountered, for example
* if you don't have permissions to write to file.
*
* #param file the written file, or null
*/
#UiThread
void onFileReady(#Nullable File file);
}
I'm looking for an example of how to use the CustomTabs "warm up" functionality to preload Chrome when the host application is initialized. Documentation around using Chrome CustomTabs for loading URLs is woefully incomplete, and all the examples out there are from 5 years ago and use ugly callback mechanisms to accomplish this.
In the latest version (2.2.0), I was able to find this method that looked promising:
* Connects to the Custom Tabs warmup service, and initializes the browser.
*
* This convenience method connects to the service, and immediately warms up the Custom Tabs
* implementation. Since service connection is asynchronous, the return code is not the return
* code of warmup.
* This call is optional, and clients are encouraged to connect to the service, call
* <code>warmup()</code> and create a session. In this case, calling this method is not
* necessary.
*
* #param context {#link Context} to use to connect to the remote service.
* #param packageName Package name of the target implementation.
* #return Whether the binding was successful.
*/
public static boolean connectAndInitialize(#NonNull Context context,
#NonNull String packageName) {
if (packageName == null) return false;
final Context applicationContext = context.getApplicationContext();
CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
#Override
public final void onCustomTabsServiceConnected(
#NonNull ComponentName name, #NonNull CustomTabsClient client) {
client.warmup(0);
// Unbinding immediately makes the target process "Empty", provided that it is
// not used by anyone else, and doesn't contain any Activity. This makes it
// likely to get killed, but is preferable to keeping the connection around.
applicationContext.unbindService(this);
}
#Override
public void onServiceDisconnected(ComponentName componentName) { }
};
try {
return bindCustomTabsService(applicationContext, packageName, connection);
} catch (SecurityException e) {
return false;
}
}
But the warmup method doesn't ever get called and performance doesn't seem to be improved. I tried calling this in the Application class as well as the Activity classes that I'd be loading URLs from.
I'm loading URLs using CustomTabsClient.Builder()..build().launchUrl(url)
In my LogCat while debugging my app, I often get:
E/TelephonyManager(5382): Hidden constructor called more than once per process!
I've been Googling around a bit, and while I noticed other mentions of the error (in other logs), I cannot identify what it means.
So what is this error? Why am I getting it? And what is its significance?
This is from the Android source code:
/**
* Provides access to information about the telephony services on
* the device. Applications can use the methods in this class to
* determine telephony services and states, as well as to access some
* types of subscriber information. Applications can also register
* a listener to receive notification of telephony state changes.
*
* You do not instantiate this class directly; instead, you retrieve
* a reference to an instance through
* {#link android.content.Context#getSystemService
* Context.getSystemService(Context.TELEPHONY_SERVICE)}.
*
* Note that access to some telephony information is
* permission-protected. Your application cannot access the protected
* information unless it has the appropriate permissions declared in
* its manifest file. Where permissions apply, they are noted in the
* the methods through which you access the protected information.
*/
public class TelephonyManager {
private static final String TAG = "TelephonyManager";
private static Context sContext;
private static ITelephonyRegistry sRegistry;
/** #hide */
public TelephonyManager(Context context) {
context = context.getApplicationContext();
if (sContext == null) {
sContext = context;
sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService(
"telephony.registry"));
} else if (sContext != context) {
Log.e(TAG, "Hidden constructor called more than once per process!");
Log.e(TAG, "Original: " + sContext.getPackageName() + ", new: " +
context.getPackageName());
}
}
The TelephonyManager seems to put the "Hidden constructor called more than once per process!" into the Log when your application calls the constructor more than once, as the message suggests. The constructor is called using the getSystemService as per the comments on the constructor.
Do you have more than one instance of:
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
or something similar in your code? This could possibly be causing the error.
EDIT: If it's not your code causing the message then it's the program running with PID 5382 I think.
I am facing problem in android-ndk. When i try to call a java nan-static member function from cpp, i am not getting any runtime error as well, but function is not getting called.
But when i try calling a java static member function from cpp i am able to call succesfully, the member function definition is getting executed sucessfully.
/********** For static member function */
/* This is the c code */
jmethodID method = env->GetStaticMethodID(interfaceClass, "callBack", "(Ljava/lang/String;)V");
if(!method) {
LOGE("Callback_handler: Failed to get the callback method");
return;
}
env->CallStaticVoidMethod(interfaceClass, method, js);
/* This is the function in the java */
public static void callBack(String s) {
Bundle b = new Bundle();
b.putString("callback_string", s);
Message m = Message.obtain();
m.setData(b);
//Sending to the handler
h.sendMessage(m);
}
The above code works well, but the below code is not working
/********** For member function */
/* This is the c code */
jmethodID method = env->GetMethodID(interfaceClass, "callBack", "(Ljava/lang/String;)V");
LOGE("callback_handler: method %d", method);
if(!method) {
LOGE("Callback_handler: Failed to get the callback method");
return;
}
/* Call the callback function */
env->CallVoidMethod(interfaceClass, method, js);
/* This is the function in the java */
public void callBack(String s) {
Bundle b = new Bundle();
b.putString("callback_string", s);
Message m = Message.obtain();
m.setData(b);
//Sending to the handler
h.sendMessage(m);
}
Please let me know if i am missing anything.
Thanks & Regards,
SSuman185
During calling the member function, dont use the class instance as a the class parameter. Use the instance of the class as cls parameter. then we will be able to call the member function as well.
The answer is given by Selvin and it is working fine, but instead of answering he added the comments. So, i am updating the answer. Please give the credit to him.
Thanks & Regards,
SSuman185
This has to do with using the Robolectric framework for unit testing on android. I'm getting a null pointer exception on code which has no problem when running normally. I'm just starting on the roboelectric, so it's probably pretty simple.
Here is the calling code for Testing :
#Test
public void testInitUtilsInitSequenceNumberIsRandom() {
// create an activity for reference
InitUtils initUtils = new InitUtils();
// do static initialization to parse questions into memory
InitUtils.initialize(initUtils); // <============ the call from roboelectric framework
// retreive app state
AppState appState = (AppState) initUtils.getApplicationContext();
// fill in later
fail("not implemented");
}
Here is the method called within in InitUtils which crashes
/**
* Loads the XML into the {#see mQuestions} class member variable
*
*/
public static void initializeQuestions(Activity activity, AppState appState) {
/* create XML Parser */
XmlResourceParser questionBatch;
/* local question variable */
Question question = null;
/* retrieve the XML for parsing */
// =============== This returns null ==============================
questionBatch = activity.getResources().getXml(R.xml.questions);
/* Parse the XML */
int eventType = -1;
/* iterate through XML */
while (eventType != XmlResourceParser.END_DOCUMENT) {
if (eventType == XmlResourceParser.START_TAG) {
/* Get the questions */
// ================================= NPE exception ======================
String strName = questionBatch.getName();
...etc
Is there something special I need to do for this to retrieve the resource?
I don't know anything about this Robolectric thing, but getResources() returning null means it is being called before the framework has called Activity.onCreate(). I don't know where you got this Activity from, but if you are doing unit testing on top of Instrumentation you need to make sure that your instrumentation thread blocks until the main thread has finished executing, using something like:
http://developer.android.com/reference/android/app/Instrumentation.html#waitForIdleSync()
If you are using startActivitySync this will be done for you:
http://developer.android.com/reference/android/app/Instrumentation.html#startActivitySync(android.content.Intent)