Android WebView with AngularJS application route changes not being detected in shouldOverrideUrlLoading - android

I have been working on a hybrid android application. Currently a WebView in our application is pointing to an AngularJS 1.5.7 application. When the user hits a button inside of the application that changes the route I was expecting the shouldOverrideUrlLoading function to be called inside of my WebViewClient. However, this is not the case. It looks like shouldOverrideUrlLoading does not get hit on Angualar route changes.
This being the case I have gone down the following rabbit holes:
onPageFinished - Overriding this function in the WebViewClient works, however, it is not being called until after the new route is getting hit. Which is adding to the application loading time and creating a choppy experience. ` #Override
public void onPageFinished(WebView view, String url) {
if (url.endsWith("/#/")) {
signOut();
} else if (url.endsWith("/login")) {
// TODO: show some sort of failure message?
Log.i("Login Route", "The webview just attempted to go to the login route.");
signOut();
} else if (url.endsWith("/security")) {
Intent intent = new Intent(getApplicationContext(), SecurityActivity.class);
startActivity(intent);
}
}`
shouldInterceptRequest - Overriding this function allows you to watch for requests. However, by the time the requests go out from the AngularJS application the web view is showing a new route once again providing a choppy user experience.
onLoadResource - same
JavaScriptInterface - Currently I have set up a JavaScript interface to watch for window.location changes. This seems to catch the route changes quicker than any of the above options, however, there is still a glimpse quick flicker of the web page I do not want to do go to. You can find how to do Javascript bridging on this post
Any suggestions would be greatly appreciated! Thanks.

Related

Is default behavior of Android's WebView changed to open internally all links?

I noticed that with the last update of Google System WebView, all the links in my WebViews are opened in the view itself. But according to the documentation from google:
public boolean shouldOverrideUrlLoading (WebView view, String url)
Added in API level 1
Give the host application a chance to take over the control when a new url is about to be loaded in the current WebView. If WebViewClient is not provided, by default WebView will ask Activity Manager to choose the proper handler for the url. If WebViewClient is provided, return true means the host application handles the url, while return false means the current WebView handles the url. This method is not called for requests using the POST "method".
I did not provide custom WebViewClient.
NOTE: The device that I noticed the problem was HTC One with the latest Google System WebView from June 8, 2015
I can reproduce the findings. Android System WebView 43.0.2357.121 exhibits the behavior that you describe, while the version I had on before upgrading to it did not.
I have filed a bug report on this and now need to do more testing and warn the world.
Thanks for pointing this out!
UPDATE
Here is a blog post that I wrote on this subject. Quoting myself from it:
My recommendation at the moment is:
Always attach a WebViewClient to your WebView
Always implement shouldOverrideUrlLoading() on the WebViewClient
Always return true to indicate that you are handling the event
Always do what your app needs to have done, whether that is loading
the URL into the WebView or launching a browser on the URL (rather
than returning false and relying on stock behavior)
Something like this static inner class appears to do the trick —
create an instance and pass it to setWebViewClient() on your
WebView:
private static class URLHandler extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (shouldKeepInWebView(url)) {
view.loadUrl(url);
}
else {
Intent i=new Intent(Intent.ACTION_VIEW, Uri.parse(url))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
view.getContext().startActivity(i);
}
return(true);
}
private boolean shouldKeepInWebView(String url) {
return(true); // or false, or use regex, or whatever
}
}
(where you would put your business logic in shouldKeepInWebView() to
determine whether or not a given URL should stay in the WebView
or launch a browser)
Seems to me this issue was resolved in 44.0.240.54.

How to achieve interaction between Native and Hybrid Applications in Worklight?

I will start with explaining the use case I am trying to implement. I have two different applications:
Native android application, and
Worklight-based hybrid application
The use case starts with opening native android application. On a particular event I am opening the Hybrid application with some parameters.
In the Hybrid application, I am getting the passed parameters in the native side it, and now I want to use that data in the webview of the application (JavaScript, HTML). How can I achieve that?
For example:
I opened the first android application. Which have one text box and a button. I entered my mobile number in text box and hit the button. On the button click I have code which starts the other hybrid application and passes the mobile number with it.
I am able to extract that mobile number parameter on native side of code. How to pass that to the Web (JavaScript) part of it?
Any help will be appreciated.
If you are using Worklight 6.2, you can achieve this in 2 ways.
Use the Simple Data Sharing API
With this API I don't think you'll even need to try to get the data from the native view and move it back to the webview in your Hybrid app, it will just be available in the webview.
Explaining the concept and execution in this answer will make it too long; I suggest to first review the documentation and see whether it fits your needs.
But I suggest:
Use the Action Sender API
With this API you can easily move data from web to native or native to web.
In your case, you say you already have the data in the native code after opening the Hybrid application, and you only need to move it to the webview, so what that is required is to:
Add a sender in the native code
Add a receiver in the JavaScript code
Unfortunately at this time there is no training module available to demonstrate specifically this feature, but there will be.
This is the basic premise for what you'll need to do:
In the JavaScript you implement a receiver:
function wlCommonInit(){
WL.App.addActionReceiver ("doSomething", actionReceiver);
}
function actionReceiver(received){
// Do something with the received data.
alert (received.data.someProperty);
}
In the main Java class of the Hybrid application (or elsewhere, depending on your application) you implement the following in onInitWebFrameworkComplete after the else closing bracket:
public void onInitWebFrameworkComplete(WLInitWebFrameworkResult result){
...
...
else {
handleWebFrameworkInitFailure(result);
}
JSONObject data = new JSONObject();
try {
data.put("someProperty", 12345);
} catch (JSONException e) {
// handle it...
}
WL.getInstance().sendActionToJS("doSomething", data);
}
The end result would be that once you open the app, you'll be welcomed with an alert showing "12345".
I will describe the solution using code snippets.
First Open the hybrid application from a native application.
Intent intent = getPackageManager().getLaunchIntentForPackage(“URI Of Target Application”);
intent.putExtra("someData", someData);
startActivity(intent);
Now Worklight based hybrid application will start and from native part we will extract that passed data and store it in shared preferences:
Bundle dataBundle = getIntent().getExtras();
String someData = dataBundle.getString("someData");
sharedpreferences = getSharedPreferences(MyPREFERENCES, MODE_PRIVATE);
sharedpreferences.edit().putString("someData", someData);
sharedpreferences.commit();
Now make a plugin which you can call after the web part is ready for use.
SharedPreferences sharedpreferences = cordova.getActivity().getSharedPreferences(MyPREFERENCES,cordova.getActivity().MODE_PRIVATE);
if(sharedpreferences!=null) {
String param = sharedpreferences.getString("someData", "-1");
sharedpreferences.edit().remove("someData").commit();
callbackContext.success(param);
}
Call that plugin on web side of Worklight based hybrid application.
function onSuccessSharedData (param) {
Param is the passed parameter
}
Cordova.exec(onSuccessSharedData, onFailure, "pluginName", "action", []);

Android 4.4 giving ERR_CACHE_MISS error in onReceivedError for WebView back

I have a webview in my Layout. By default, a search form is opened in it. On search, a listing section appears below the search form. If any link in the list is clicked, the details page opened. Now I want to controlled the back navigation for the webview. I placed this code in Activity.
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.d("TYPE", TYPE);
WebView myWebView = null;
if (TYPE.equalsIgnoreCase("REPORT_ACTIVITY"))
myWebView = reportView;
if (TYPE.equalsIgnoreCase("FEEDBACK_ACTIVITY"))
myWebView = feedbackView;
if (myWebView != null)
// Check if the key event was the Back button and if there's history
if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) {
myWebView.goBack();
return true;
}
// If it wasn't the Back key or there's no web page history, bubble up
// to the default
// system behavior (probably exit the activity)
return super.onKeyDown(keyCode, event);
}
private WebViewClient webViewClient = new WebViewClient() {
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Log.d("onPageStarted", "onPageStarted");
loadProgressBarBox.setVisibility(View.VISIBLE);
//view.setVisibility(View.GONE);
}
public void onPageFinished(WebView view, String url) {
Log.d("onPageFinished", "onPageFinished");
loadProgressBarBox.setVisibility(View.GONE);
}
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
Log.d("Error", "Error code: " + errorCode + "/" + description);
}
}
I have also set a WebViewClient with the WebView. When I going back using back button it is working fine for any version 4.4. But when I am trying in Android 4.4, it is coming back fine from details page to listing page. But as soon as I am trying to go back again, its throwing error code -1 and ERR_CACHE_MISS in description. No page is displayed.
09-04 06:59:05.666: D/Error(1102): Error code: -1/net::ERR_CACHE_MISS
How to solve this problem in Android 4.4?
This error actually stems from outside of your application in most cases (occasionally it's just a missing INTERNET permission, but that doesn't sound like the case here).
I was typing out an explanation, but found a much more straightforward example that doubles as an explanation in this answer to another question. Here's the relevant bits, re-hashed a little:
Joe fills in an order form with his credit card information
The server processes that information and returns a confirmation/receipt page that's marked with no-cache in the header, meaning it will always be requested from the server.
Joe goes to another page.
Joe clicks back because he wants to double check something, taking him to the confirmation page.
The problem arises from that last step. The confirmation page was marked with no-cache, so it has to be requested from the server again. But to show the same page correctly, the same data that was passed the first time needs to get sent again.
This results in Joe getting billed twice, since a new request is being made with the same information as last time. Joe will not be a happy camper when he finds two charges on his account and an extra pair of tents on his doorstep.
It seems this situation was common enough that it is now a standard error across most browsers, and apparently, newer versions of Android. The error actually originates from Chromium, which is why you'll see the same error in Google Chrome, and why you only see it in 4.4 (which introduced a new version of the WebView based on Chromium).
In fact, you have actually probably seen it before, it's the message that shows up in most browsers warning you with something along the lines of "To refresh this page, the browser will have to resend data...yada yada yada".
This is Android 4.4's way warning you of what's going on. How to fix it really depends on what you're connecting to, but if you search for this situation, you'll find that it's fairly common, and has fixes. The exact trigger of the error is actually when the request can't be serviced from cache (in this case, no-cache is causing that).
Depending on the nature of the request, maybe no-cache isn't actually needed.
But from your application's perspective, the main problem is, onReceiveError is a sort of "last resort" for the WebView. Errors you get there have propagated from underlying system. And once you end up there, you can't continue the page load as it stands. So you don't have a chance to allow that resend, and you can't give the user that option, unlike, say Google Chrome does.
I ran into the same issue because in my manifest folder I had the Internet permission capitalized:
I had (error)
<uses-permission android:name="ANDROID.PERMISSION.INTERNET"/>
Should have (no error)
<uses-permission android:name="android.permission.INTERNET"/>
Use
if (Build.VERSION.SDK_INT >= 19) {
mWebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
It will fix ERR_CACHE_MISS in the WebView.
Maybe you will need to change it to SDK_INT == 19 after some Lollipop WebView updates, but it works for now.
this permission in your andriodManifest.xml file
<uses-permission android:name="android.permission.INTERNET"/>

android phonegap custom "Web page not found"

Problem:
I'm trying to avoid the "web page not found", or at least to display a customized error page.
Context:
I use the cordova trick:
if (navigator.network.connection.type == Connection.NONE)
{
window.location="offline/index.html";
}
else
{
window.location="http://myurl.com";
}
But in my tablet, if there's no connection, I have the ugly "WebPage not found".
There's maybe something wrong in my code, but in all case I would like to find a way to avoid this page and to show my own.
I will be very happy if somebody here can tell me where to give a look.
Stef
PS : the "Page Not found" appears when the website is down. It's not related to the event offline. You can have internet, and the server can be down. In that case, I want to display my own error page. Thanks!
There is an event offline available in Cordova. You could add event listeners to this and do required changes in its callback. If you are using deviceready event it will be called only once when your app is done loading. But if you add offline & online listeners you can alert user each time network goes down/up.
document.addEventListener("offline", onOffline, false);
function onOffline() {
// Handle the offline event
}
http://docs.phonegap.com/en/2.9.0/cordova_events_events.md.html#offline

How to handle a binary data response in WebView

My activity has an intent filter to pick up a specific url and open it in a WebView control, which brings user to an auth page (user name/password). After authentication is done user will get a binary stream response (file). Is there a way to handle that response and read data from the stream?
I tried to setup a custom WebViewClient with the overridden shouldOverrideUrlLoading method, but app doesn't get there.
#Override
mMyWebView.setWebViewClient(new CustomWebClient());
mMyWebView.loadUrl("http://xxx.xx.x.xx:xxxx/getCert");
...
private class CustomWebClient extends WebViewClient
{
#Override
public boolean shouldOverrideUrlLoading (WebView view, String urlConection)
{
// break point here doesn't stop debugger
return true;
}
}
neither works
mMyWebView.setWebViewClient(new WebViewClient(){
#Override
public boolean shouldOverrideUrlLoading (WebView view, String urlConection)
....
});
Server reacts on requests in the same way from both My app and build-in browser. Build-in browser starts to download file received in response, but my app doesn't do anything and doesn't hit breakpoint inside CustomWebClient.
This is a separate app just to test this piece nothing else interferes with it. INTERNET & WRITE_EXTERNAL_STORAGE permissions added.
EDIT: Mar 8
Gave up. Will go with httpClient.
Did you implement this method of WebView?
void setDownloadListener(DownloadListener listener)
Register the interface to be used when content can not be handled by the rendering engine, and should be downloaded instead.

Categories

Resources