we recently received a request as following -
While loading a webapp (https://192.1.../poc/test.jsp) in android or iOS webview. It's taking some time while loading 1st time because of js, image, css etc files takes time to download including some APIs.
Client want to improve the experience and wanted 1st time even takes time and load the .js, images etc offline. When user launch app 2nd time onwards, use the loaded .js, images etc and make only API call from server.
We would like to check if what is the best way to achieving it?
Any out of frame suggestion is also appreciated.
While digging too much, it was solved as following
iOS having in-build property by WKWebView to store supporting web files like (js, css etc) and reuse if needed for further commmunication. Logic to clear cache etc we can manage it manually when it needed.
Android it was webviewclient. Which is having a callback name shouldInterceptRequest(WebView view, WebResourceRequest request)
Everytime any webview page needs css, js etc, this callback to be invoked. So put the logic be like
if (if url extension is js|png|css){
if (response for url is available within app cache) {
return WebResouceResposne object from cache data
} else {
//Save file response in cache &
return to super.shouldInterceptRequest() using interceptor WebResourceResponse
}
}
Hope, it will help someone in anyways. Thank you!
Related
Google has asked me to address https://support.google.com/faqs/answer/9095419 in my Android app, which basically means not to use the JavaScript injection mechanism for a web page loaded via HTTP.
Not using this mechanism (option 1) doesn't work for me. Setting android:usesCleartextTraffic to false also doesn't work, as the app uses non-HTTPS traffic elsewhere. So that leaves me with "you can ensure that any affected WebViews do not load any URLs with HTTP schemes via loadUrl" - which I'm happy to do, as my app only uses file:/// URLs to load content into the WebView, which should be fine security-wise. But how do I need to code the shouldOverrideUrlLoading method so that Google's checker recognizes that I'm using only file:/// URLs?
Note that the question is different from both Remediation for JavaScript Interface Injection Vulnerability (because I'm clear what is being asked) and In Android, JavaScript Interface Injection Vulnerability (because I'm not using HTTP, but file:/// URLs).
Edit: Adding my shouldOverrideUrlLoading method. (This isn't the entire method, but the salient part of it.)
#Override
public boolean shouldOverrideUrlLoading (WebView browser, String url) {
if (url.startsWith("file:///")) {
// This is my web site, so do not override; let my WebView load the page
browser.loadUrl(url);
return true;
}
// Otherwise, the link is not for a page on my site, or is an entirely different kind of URI
// (like tel:, geo: or mailto:), so launch another Activity that handles URLs
act.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
}
I have not found a way to use file:// URLs with assets in a way that satisfies Google code checker. While this would solve the issue, I'm still not clear how one might need to code it.
What I ended up doing -which solves my immediate problem- is to call a JavaScript method via the WebView.evaluateJavascript method. When called from within WebViewClient.onPageFinished the page has finished loading, so all elements are accessible. While not important for my case, this method can also return a value to the Java code. So while it's not a general replacement for a JavascriptInterface, it addresses some of its uses cases.
I need to catch malformed links (without "http://") in a webpage loaded by the app (through a WebView container) and format them correctly (adding "http://") which I do by Overriding
public boolean shouldOverrideUrlLoading(WebView view, String url).
if(url.startsWith("/")){
url = "http://www.domain.com" + url;
}
Everything was nice but I realize that in API 19 it fails to call shouldOverrideUrlLoading method according to this guide.
But they do not say which method gets to be call to decide if it is a valid or not valid url.
Solutions like get all web content from the page to load, find and replace the bad formatted links and then load that "new" page instead seems to much effort for something so little.
Does anyone knows what method to override (intercepting clicks from the WebView)?
Google has some documentation regarding shouldOverrideUrlLoading() in KitKat:
The new WebView applies additional restrictions when requesting resources and resolving links that use a custom URL scheme. For
example, if you implement callbacks such as shouldOverrideUrlLoading()
or shouldInterceptRequest(), then WebView invokes them only for valid
URLs.
If you are using a custom URL scheme or a base URL and notice that your app is receiving fewer calls to these callbacks or failing to
load resources on Android 4.4, ensure that the requests specify valid
URLs that conform to RFC 3986.
This might seem to be a weird problem, but I am curious to know if it would work. I am working on a POC, and hence have to either prove or disprove that this works or not.
The UI in the Android app would be native (Java + XML layouts) + some other device features access like (Camera/File system etc).
There is a JS library that I have built, that has a few functions which do Ajax post and get requests.
In the app, I have an invisible Webview, where I load a blank HTML (referencing this JS library). And into that WebView, I have injected a JavascripInterface. So, essentially, the UI would be native, and you would never see the Webview. That's just a host which provides access to my JS library to the native code.
Now, on some action on my UI, I call the JS functions on the Webview, which in turn tries to make an ajax call (loadUrl calls ex. javascipt:functionName()). But, those calls fail, without any visible errors.
Note: This same HTML file works, if I load it up on my desktop browser. The AJAX calls succeed.
But, when I initiate Ajax calls through the JavascriptInterface(or webview.loadUrl() calls), they fail, with a reponse status 0.
Things apart from AJAX, like simple function calls, alerts, and callbacks through javascript interface work fine though.
Q: I know this is a weird and an unpractical way to do things. But, would it/should it work?
Update: Even after setting the setBlockNetworkLoads(false), it still doesn't work.
I tried logging the JS calls and errors, and got this error.
Request header field X-Requested-With is not allowed by
Access-Control-Allow-Headers.
Any idea how to solve this?
It seems that your are trying to do a cross domain ajax request.
Cross domain requests are not allowed by same origin policy and so the requests will be blocked. If you are loading a local file in webView and then sending ajax requests from it to other domains, this will be the case.
You if that is the case and it is the same origin policy causing you trouble then you might want to look at Cross-origin Resource Sharing (CORS) or JSONP to workaround it.
Given the error you get it seems that your problem is similar to one discussed here:
Cross-Domain AJAX doesn't send X-Requested-With header
You might want to change server settings to allow X-Requested-With header.
Also it seems that from API level 16, webSettings added a method setAllowFileAccessFromFileURLs(). Setting this to true for the webView might solve the problem as well.
I had a similar issue where I was loading a "web-app" locally into a WebView, just doing Ajax remotely. I observed a similar problem where Javascript alerts etc worked fine, but AJAX calls didn't. It turned out that by default the WebView blocks "network loads".
Make sure you do this:
webView.getSettings().setBlockNetworkLoads(false);
That did it for me. Just to clarify, I wasn't using a Javascriptinterface - just loading a web-app as-is using webView.loadDataWithBaseUrl() - the baseUrl parameter passed to this method was where I perform all my AJAX calls (since this method respects the same origin policy)
In my previous question, i asked "how to download Facebook profile picture" and i got answer that "Use an AsyncTask to download it and save it to your app's disk space".
i am using sencha touch (its all about java script and html) for creating views with phonegap. i need to download image from web (from this url https://graph.facebook.com/username/picture).
so, when i was learning AsyncTask, i had a doubt that "can i use AsyncTask in sencha android application?", because i didn't create views (i mean button, panel, etc..) using activity xml.
is there any other solution to download picture from web in sencha touch application (Android) ?
Update for the solution
i used below facebook api to get the profile picture url and Phonegap file Api for downloading picture as #Lukas K said.
FB.api('/'+fbusername+'/picture?redirect=false', function(response) {
localStorage.fbpicurl = response.data.url;
});
You can convert it into base64 and store it in the localStorage.
But the space of the LocalStorage is limited to 5MB. After reaching the Limit the device will prompt the user to give more space.
Do you use phonegap in addition to Sencha Touch? Phonegap provides access to the phone's file system, where you can store your pictures.
Phonegap File Api: http://docs.phonegap.com/en/2.3.0/cordova_file_file.md.html#File
You can use sencha's delayed task like this:
var task = Ext.create(
'Ext.util.DelayedTask',
function() {
Ext.getCmp("imageId").setSrc( 'https://graph.facebook.com/username/picture' );
}
);
task.delay(100);
This will start downloading the image after 100ms.
I hope this is what you wanted to do, If not then do whatever as delayed task in the function.
I am working on a mobile application with phonegap and jquery mobile for android. It is working properly but it takes so much time to get data from server.
I want to cache its pages so if user come back on same page he can view same page.
I am using jquery mobile caching code but it is not working.
I am using this code data-dom-cache="true" for cache
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.appView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
super.loadUrl("file:///android_asset/www/find.html");
}
Cache is not working in my app so I stored all json data in localstorage by doing this
window.localStorage.setItem("ALL_USERS",JSON.stringify(data));
where data is json object I retrieve from server.
And next time i retrieve data from localstorage
window.localStorage.setItem("ALL_USERS",JSON.stringify(data));
users = JSON.parse(users);
This solution solved my problem hope it will help others.
Thanks
Call below code in onCreate() method of your Android application activity file:
super.appView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
The above method uses cache if content is there, even if expired (eg, history nav). If it is not in the cache, load from network
Please look WebSettings constants for cache settings :
http://developer.android.com/reference/android/webkit/WebSettings.html