I need to detect from a native JAVA (non phonegap plugin) class if phonegap's web view has finished loading. Is this possible, or will I need to rely on structuring my code as a plugin?
I finally found a solution (tested and works).
It's a tricky workaround but does the job.
Step 1:
Create a new class in the same package as your MainActivity like this:
public class MyClass{
private WebView appView;
public MyClass(DroidGap gap, WebView view){
appView = view;
}
public void onLoaded(){
// This will be called when webview loaded
System.out.println("WORKED!!");
}
}
Step 2:
Add the following lines to the very end of onCreate() in your MainActivity:
MyClass ai = new MyClass(this, super.appView);
super.appView.addJavascriptInterface(ai, "MyClass");
It should look something like this:
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
super.loadUrl("file:///android_asset/www/index.html");
MyClass ai = new MyClass(this, super.appView);
super.appView.addJavascriptInterface(ai, "MyClass");
}
Step 3:
In your JavaScript code in assets/www add this:
document.addEventListener("deviceready", function(){
try{
// Might throw an error but onLoaded() gets called correctly
window.MyClass.onLoaded();
}
catch(e){
console.error(e)
}
});
Important: Make sure that cordova.js is correctly included in your index.html
This did the job for me.
Related
I am very new to Cordova framework. I am trying to access HTML file from Droidgap activity, but getting exception as,
Cross-origin requests are only supported for protocol schemes: HTTP, data, chrome, chrome-extension, https.
For this I found one link, maybe you will find this question as a duplicate, but I am looking for Android solution, they are saying to install local server and then try to access this HTML, but in my case I want it to be accessible in MainActivity.
following is code..
public class MainActivity extends DroidGap {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.appView.getSettings().setAllowFileAccess(true); //this line throws nullPointerException for appView
super.appView.getSettings().setAllowFileAccessFromFileURLs(true);
super.appView.getSettings().setAllowUniversalAccessFromFileURLs(true);
super.loadUrl("file:///android_asset/www/MyHtml.html");
}
}
My HTML is located at asset/www/MyHtml.html
I am really struggling with this issue, can anyone help to get out of this?
You say:
Cross-origin requests are only supported for protocol schemes: HTTP,
data, chrome, chrome-extension, https.
You also say (in comment):
In this case I am getting appView variable as null,
NullpointerException
First get the appView with the getView() method, and use a local variable:
WebView myappView = getView();//getView is a method from DroidGap super class
WebSettings settings = myappView.getSettings();
Your are not initializing DroidGap so add super.init(); as follows:
public class MainActivity extends DroidGap {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();//you were missing this
WebView myappView = getView();//getView is a method from DroidGap super class
WebSettings settings = myappView.getSettings();
settings.setAllowFileAccess(true); //this line throws nullPointerException for appView
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);
super.loadUrl("file:///android_asset/www/MyHtml.html");
}
}
You should be fine now but you can also try one (or all) of these (see WebSettings):
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);
settings.setAllowFileAccess(true);
settings.setAllowContentAccess(true);
See DroidGap.java source code.
I developed a simple game and I want to give an external URL to my web page. The problem is that, I'm using setContentView in the onCreate method of my main activity. My game content is dynamic and I cannot use intents instead of setContentView. However, as you know, onCreate is called once and the only way that I found on web to call an URL is "using intents". It is not working because setContentView is the only thing running. Here is my codes:
GameActivity.java
private static GameContent gameContent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
gameContent = new GameContent(this);
setContentView(gameContent);
} // on.c.ends
My GameContent class is extends SurfaceView and Runnable. I call all draw(), pause() and resume() methods here. I have a class BasicButton and I added it to GameContent and draw it. It works. Here is the part that I want to use:
BasicButton.java
#Override
public void update() {
if (xx > (rate.getX()) && xx < (rate.getX() + rate.getPlayButton('x')))
if (yy > (rate.getY()) && yy < (rate.getY() + rate.getPlayButton('y'))) {
UserInput.setXY(0, 0);
if (UserInput.getAction() == true) {
// I want to add a code here to open a web page
} // fi
} // fi
} // update ends
My solution was to create a boolean in GameActivity and change it from BasicButton class and using an intent and startActivity, to open a web page. However it is not working.
Do you have any suggestion?
You can't. setContentView only works with pre-compiled XML files in your APK. You can't even write a new one on the fly and call setContentView on it. I don't think you really understand what setContentView does.
How can I call a native function from cordova/phonegap webview for example for showing an ad.
EDIT: OK I FUNALLY GOT IT and I'm gonna write some steps for all of you who don't know how to do that (just to spare 2 days of your lifetime :D)
A) if you have just cordova/phonegap and and wish to call from js do this:
1) Replace the following code with your existing DroidGap Activity.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init(); // Calling this is necessary to make this work
appView.addJavascriptInterface(this, "MainActivity");
/* "this" points the to the object of the current activity. "MainActivity" is used to refer "this" object in JavaScript as in Step 3. */
super.loadUrl("file:///android_asset/www/index.html");
}
2) Add the custom function in current (this) activity as following.
#JavascriptInterface
public void customFunctionCalled() {
Log.e("Custom Function Called", "Custom Function Called");
}
3) Now call this function from your HTML/JavaScript code as following.
window.MainActivity.customFunctionCalled();
B.1) if you have cordova/phonegap implemented in a webview and wish to call from js do this:
(and want to call normal function)
1) Add this to the main java file:
JavaScriptInterface jsInterface = new JavaScriptInterface(this);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(jsInterface, "JSInterface");
2) Declare the class JavaScriptInterface:
public class JavaScriptInterface {
private Activity activity;
public JavaScriptInterface(Activity activiy) {
this.activity = activiy;
}
#JavascriptInterface
public void showLog(){
Log.v("blah", "blah blah");
}
}
3) Call it from js with `window.JSInterface.showLog();
B.2) if you have cordova/phonegap implemented in a webview and wish to call from js (and want to call UI function, like a toast) do this:
1) Add this to the main java file:
JavaScriptInterface jsInterface = new JavaScriptInterface(this);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(jsInterface, "JSInterface");
2) Declare the class JavaScriptInterface:
public class JavaScriptInterface {
private Activity activity;
public JavaScriptInterface(Activity activiy) {
this.activity = activiy;
}
#JavascriptInterface
public void myFunction()
{
activity.runOnUiThread(new Runnable() {
public void run() {
//Code that interact with UI
showToast();
}
});
}
}
3) Add the toast function underneath:
public void showToast(){
Toast.makeText(getApplicationContext(), "this is my Toast message!!! =)",
Toast.LENGTH_LONG).show();
}
4) Call it with `window.JSInterface.myFunction();
As you see if you need a function that uses the UI you need to wrap your function into activity.runOnUiThread so that it can get called from js.
*If you want to call from java a jquery method do this:
Java:
cordova_webview.loadUrl("javascript:window.functionn()");
Javascript:
window.function = punish;
Have a nice day!
In the .java file
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
super.init(); // Calling this is necessary to make this work
appView.addJavascriptInterface(this, "MainActivity");
super.loadUrl(Config.getStartUrl());
}
In the javascript
window.MainActivity.customFunctionCalled();
This will only work on TargetSDK<17 . An androidManifest.xml targetSDK need to be set < 17. And for TargetSDK >=17 , I guess you'll need to create some custom plugin. I use this process lowering my target SDK for now.
1. Javascript file ------ calls -------> CORDOVA PLUGIN(plugin.java)
2. CORDOVA PLUGIN(plugin.java) file -------calls-------> Android BroadcastReceiver file(Broadcast_Receiver.java)
Broadcast_Receiver.java contains onResume.
How do I call/initiate it from Javascript file?
Please see sample code at the bottom
Problem #1: I can't combine plugin.java & Broadcast_Receiver.java because they are extending CordovaPlugin & WLDroidGap respectively.
Problem #2: When I navigate between pages of my worklight application; onResume of Broadcast_Receiver.java never gets triggered because onResume is in native Android code
Possible solution which i was unable to implement:
use cordova onResume i.e.: document.addEventListener("resume", function() { });
merge plugin.java & Broadcast_Receiver.java
In short: how to initiate android lifecycle using a Cordova plug-in?
Sample code
Javascript file calls plugin
//some code
currentPage.myfunction= function() {
cordova.exec(Success, Failure, "plugin","someMETHOD", []);
}
plugin.java
public class plugin extends CordovaPlugin
{
//some code
float value= Broadcast_Receiver.Variable_Name;
//some code
}
Broadcast_Receiver.java
public class roamingadvisor extends WLDroidGap {
//some code
onCreate(){}
onResume(){}
onPause(){}
//some code
}
What I am trying to achieve
My application starts calculating data usage once it receives broadcast that data got activated
I am able to calculate data usage
It gets updated once i press home button & relaunch my app(i.e. trigger native onResume in android)
But when i keep using my application (i.e navigate between UI screen) native android onResume never gets triggered
Or
should i do it using entirely different approach?
*******R&D *******
I used below two link
https://apache.googlesource.com/cordova-android/+/2.6.x/framework/src/org/apache/cordova/Device.java
https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java
and wrote follwing code for my need
public class Myclass extends CordovaPlugin {
IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
boolean multitasking=true;
BroadcastReceiver mReceiver=null;
//constructor
public MyDataUsage(){
}
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
//Device.uuid = getUuid();
this.initReceiver();
}
#Override
public void onPause(boolean multitasking) {
super.onPause(multitasking);
this.cordova.getActivity().unregisterReceiver(this.mReceiver);
}
#Override
public void onResume(boolean multitasking) {
super.onResume(multitasking);
this.cordova.getActivity().registerReceiver(this.mReceiver, intentFilter);
}
public void onDestroy() {
this.cordova.getActivity().unregisterReceiver(this.mReceiver);
}
private void initReceiver() {
this.mReceiver=new BroadcastReceiver(){
#Override
public void onReceive(Context context, Intent intent) {
ctb=context;
if (intent.getAction().equals("android.net.conn.CONNECTIVITY_CHANGE"))
{
//My code
}
}
}
};
this.cordova.getActivity().registerReceiver(this.mReceiver, intentFilter);
}
public boolean execute(String action, JSONArray arr,
final CallbackContext callbackcontext) throws JSONException {
if (action.equals("getDataUsage")) {
try {
//code
callbackcontext.success(""+dataUsed+ ":" +timeElapsed+":"+StartTime);
} catch (Exception e) {
callbackcontext.error("ERROR :");
}
return true;
}
return false;
}
}
But onPause & onResume still gets called only when i press HOME key & RELAUNCH my APP
Should i CONCLUDE that " I can not implement ACTIVITY LIFECYCLE while naviagting in my UI/html SCREEN/pages" ???
By default Cordova application consist of a single Activity which hosts a single WebView. When you're navigating pages/changing content in web you will not get any lifecycle events from Activity because technically you're still in a context of a same Activity.
Note that CordovaPlugin class has callbacks for many Activity lifecycle methods, you can leverage them (https://github.com/apache/cordova-android/blob/master/framework/src/org/apache/cordova/CordovaPlugin.java). For example use initialize() method for starting your calculation and onDestroy() method to detect when your app is being closed. You can also use other CordovaPlugin methods such as onResume() and onPause() according to the logic you're trying to implement.
I am using the Android SDK and PhoneGap to create a native Android App. Now I want to use Java methods in the view, calling by JS methods.
In the Main Class I called the "addJavascriptInterface" method to bind a java class with the view.
public class App extends DroidGap {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
super.loadUrl("file:///android_asset/www/index.html");
}
}
The problem is I get an InvocationTarget Exception when the programm executes the line "appView.addJavascript..." and the program crashes on the device.
Any solutions here?
Thanks!
Calling Java methods from JavaScript is exactly what PhoneGap plugins enable. Checkout several examples here
Using addJavascriptInterface before super.init(); can make the application crash.
You should try the following :
public class App extends DroidGap {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.init();
appView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
super.loadUrl("file:///android_asset/www/index.html");
}
}