Android webview local file setting root - javascript

I am loading a local web application using a webview in Android:
view.loadUrl("https://appassets.androidplatform.net/assets/dist/index.html")
However when I check the current route in my JavaScript it says https://appassets.androidplatform.net/assets/dist/index.html
This makes stuff like routing and file loading kind of a pain - is there a way to set the root of the loaded application, so I can avoid the whole /assets/ ?

Have a look at WebViewAssetLoader the code snippet there does exactly what you need. So you basically just need to forward all the requests which can be handled by the assetsLoader to it, and use the response that it returns.
final WebViewAssetLoader assetLoader = new WebViewAssetLoader.Builder()
.addPathHandler("/assets/", new AssetsPathHandler(this))
.build();
webView.setWebViewClient(new WebViewClient() {
#Override
#RequiresApi(21)
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return assetLoader.shouldInterceptRequest(request.getUrl());
}
#Override
#SuppressWarnings("deprecation") // for API < 21
public WebResourceResponse shouldInterceptRequest(WebView view,
WebResourceRequest request) {
return assetLoader.shouldInterceptRequest(Uri.parse(request));
}
});
WebSettings webViewSettings = webView.getSettings();
// Setting this off for security. Off by default for SDK versions >= 16.
webViewSettings.setAllowFileAccessFromFileURLs(false);
// Off by default, deprecated for SDK versions >= 30.
webViewSettings.setAllowUniversalAccessFromFileURLs(false);
// Keeping these off is less critical but still a good idea, especially if your app is not
// using file:// or content:// URLs.
webViewSettings.setAllowFileAccess(false);
webViewSettings.setAllowContentAccess(false);
// Assets are hosted under http(s)://appassets.androidplatform.net/assets/... .
// If the application's assets are in the "main/assets" folder this will read the file
// from "main/assets/www/index.html" and load it as if it were hosted on:
// https://appassets.androidplatform.net/assets/www/index.html
webview.loadUrl("https://appassets.androidplatform.net/assets/www/index.html");

Related

opening link in external browser from page in WebView

I'm developing an angular website which is loading in an app from a WebView, and there is only one of the links in it that has to be opened outside of the app (external browser)
I need a way to handle this from JavaScript not putting extra work to the android side.
and i have already tried some ways including:
window.open("url","_system")
(navigator as any).app.loadUrl("http://google.com", {openExternal : true});
Well, there is no such thing
instead it must be handled from android application code. you can add a parameter to the url when u need it to open in external browser, ( here it is external=true ) and then check for that parameter in your webview url loading as below:
webView.setWebViewClient(new WebViewClient(){
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
if((String.valueOf(request.getUrl())).contains("external=true")) {
Intent intent = new Intent(Intent.ACTION_VIEW, request.getUrl());
view.getContext().startActivity(intent);
return true;
} else {
view.loadUrl(String.valueOf(request.getUrl()));
}
return true;
}
});

How should I handle caching?

I have a SPA application which is written in Websocket, Polymer, ES6, HTML5. This is served by a Jetty 9 backend which is packaged as a runnable jar with absolutely everything in the JAR.
I'd like to have it such that when a new version of the JAR is deployed, I push a message down to the client to force it to do a cache-less refresh of all the resources.
I have a custom HttpServlet to serve my SPA so I can handle the URL "rewriting":
private static final Path ROOT = getDevelopmentWebRoot();
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String requestPath = req.getPathInfo();
if (requestPath.equals("/")) {
requestPath = "index.html";
} else {
requestPath = requestPath.substring(1);
}
Path resource = ROOT.resolve(requestPath);
resp.setHeader("Cache-Control", "max-age=31536000");
resp.setContentType(MimeTypes.getDefaultMimeByExtension(resource.toString()));
try {
Files.copy(resource, resp.getOutputStream());
} catch (NoSuchFileException e) {
Files.copy(ROOT.resolve("index.html"), resp.getOutputStream());
}
}
I guess my question is, how can I force a cache-less refresh in javascript?
You can set up a cache with the headers "Last-Modified" and "Cache-Control" with "must-revalidate". This should force the browser to do a "If-Modified-Since" request everytime.
Then in your frontend you can just reload the page every X seconds, and if nothing as been modified you should get it from the cache (instantaneous) else it will load the new page.
I haven't tested it but I think it should work like you want it to.

Webview shouldOverrideUrlLoading not getting called

I am making an ebook reader which uses epub format to load books into webviews.
In some of the books there is an anchor link to some portions in the same chapter. Each chapter is loaded as html. This is how the link look like
file:///storage/sdcard0/Android/data/com.abc.reader/files/Download/498935/epub/resources/498935/OEBPS/#footnote-165093-1-backlink
I tried using shouldOverrideUrlLoading() method to get the call back , but it's not getting called and when I press the links in onPageFinished the url shown as about:blank
reader.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.w("TESTTESTOVERRIDE "+url);
view.loadUrl(url);
return false;
}
#Override
public void onPageFinished(WebView view, String url) {
// after the data has been loaded, the following is executed
// TODO Auto-generated method stub
super.onPageFinished(view, url);
System.out.println("check.... onPageFinishedEntered.."
+ url.toString());
view.loadUrl(jsfileloadurl);
}
Any ideas?
EDIT: In 4.1 devices I get the anchor links correctly,but in 4.4 or 5.0 it is about:blank. (in both cases shouldOverrideUrlLoading is not called)
I haven't tested this programmatically but I believe you are facing this issue because there was major changes in how webview works post OS 4.4 . You should check this link
https://developer.android.com/guide/webapps/migrating.html#URLs
Under section 'Custom Url Handling' it says that shouldOverrideUrlLoading() will not be invoked for invalid url. Ideally file:// should be treated as valid url but seems like it's not happening here.
One possible solution is to load you main webview content with loadDataWithBaseURL
and provide baseurl as some test url e.g. http://mytestap.testurl , it will guarantee shouldOverrideUrlLoading will get invoked all time. As a next step you need to remove prefix 'http://mytestap.testurl' if exist in the received url in shouldOverrideUrlLoading callback.
In my case it didn't work because of POST requests on web page. shouldOverrideUrlLoading:
Note: This method is not called for POST requests.
Note: This method may be called for subframes and with non-HTTP(S)
schemes; calling WebView#loadUrl(String) with such a URL will fail.
Override shouldInterceptRequest instead (one or both versions). See also https://medium.com/#madmuc/intercept-all-network-traffic-in-webkit-on-android-9c56c9262c85.
Yes. Mr. androgeek answered it rightly.
From Android OS 4.4(KK), if you implement callbacks such as shouldOverrideUrlLoading() or shouldInterceptRequest(), then WebView invokes them only for valid URLs.
If you are using Custom URL and under your control then you need to follow RFC 3986 standard to above methods called. Kindly check RFC 3986 related file:// and correct your URL
I am not sure whether the below will resolve your problem or not.
Please add below code before setting the WebViewClient
reader.getSettings().setLoadWithOverviewMode(true);
reader.getSettings().setUseWideViewPort(true);
/*This makes the layout/page rendering independent of the devices.
I use this to display local HTML pages.*/
reader.getSettings().setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
In addition I have zoom controls enabled. Please note that I have tested my code from API-10 onwards with multiple devices and brands (HTC, Samsung, Nexus etc.) and found that the shouldOverrideUrlLoading works all the time.
If things do not work well, try extending the WebViewClient and Override the shouldOverrideUrlLoading method
class MyWebView extends WebViewClient{
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return false; //THis should be false always
}
}
Now set the WebViewClient as reader.setWebViewClient(new MyWebView());

Downloading web pages and refreshing it when connection available

So I'm building an app with a lot of web content I plan to release it using Phone Gap build but will host all the content online and will link to it. I was wondering if there is a way that the web pages can be downloaded when there is an active internet connection for offline use and when there is a connection again for the data to be refreshed preferably when the user is using a wifi connection. The site will mostly be in html, js, and php. I will be hosting with bluehost
Is there any way of doing this? Thanks in advance! Littleswany!
PhoneGap apps ARE downloaded to the device, when they are downloaded from the store. They are basically a wrapper around an index.html file, but the app is actually programmed in JavaScript, which is responsible for creating and displaying views etc. The only time you need to check for an internet connection is when you are communicating with your back end (PHP)... If the ajax request fails, the best solution is to provide the user with a button/link to try again when they have regained their internet connection, or set a timer which fires intermittently to keep trying again... NEVER use a while(true) loop in your Phone Gap app - it will just hang.
I am not familiar with java, but i think i can provide the logic to get the job done.
You want to do an infinite loop that checks if the user is on wifi. Then if true, use wget, rsync, or scp to download the website. Something like this.:
while (true){
// do an if statement that checks if user is on wifi. Then do a then statement that uses rsync or wget.
}
Info on how to nest if statements in while loops in java: java loop, if else
I do not know if wget, rsync, or scp can be ran from java. You'll need to look more into it or write your own alternative function to do it. Something like:
function download_file() {
var url = "http://www.example.com/file.doc"
window.location = url;
}
You should be able to do it from your java like this:
String whatToRun = "/usr/local/bin/wget http://insitu.fruitfly.org/insitu_image_storage/img_dir_38/insitu38795.jpe";
Sources:
1. What is the equivalent of wget in javascript to download a file from a given url?
2. Call a command in terminal using Java (OSX)
First Create an Connection filter class
public class Connection_Status{
private static ConnectivityManager connectivityManager;
static boolean connected = false;
public static Boolean isOnline(Context ctx) {
try {
connectivityManager = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
connected = networkInfo != null && networkInfo.isAvailable()&& networkInfo.isConnected();
return connected;
} catch (Exception e) {
System.out.println("CheckConnectivity Exception: " + e.getMessage());
}
return connected;
}
}
And in your Main class
public class Main extends Activity{
private WebView mWebView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webview);
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setBuiltInZoomControls(true);
if(Connection_Status.isOnline(Main.this)){
HttpClient httpclient = new DefaultHttpClient(); // Create HTTP Client
HttpGet httpget = new HttpGet("http://yoururl.com"); // Set the action you want to do
HttpResponse response = httpclient.execute(httpget); // Executeit
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent(); // Create an InputStream with the response
BufferedReader reader = new BufferedReader(new InputStreamReader(is, "iso-8859-1"), 8);
StringBuilder sb = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) // Read line by line
sb.append(line + "\n");
String resString = sb.toString(); //
is.close(); // Close the stream
}
}
}
Or you can use cache on it e.g
mWebView.getSettings().setAppCacheMaxSize(1024*1024*8);
mWebView.getSettings().setAppCachePath(""+this.getCacheDir());
mWebView.getSettings().setAppCacheEnabled(true);
mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
Don't forget to add the following permissions
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- for the connection status-->
Sources:
https://stackoverflow.com/a/6503817/1309629

Android/WebView doesn't load javascript gallery images when navigated to

I have a website/web app built with jquery mobile that I am trying to package into a webview for android; I am able to load up the pages locally by putting the whole site into the assets folder and loading the url like so:
mWebView.loadUrl("file:///android_asset/www/btec/index.html");
One of the pages is a gallery/slideshow of images that flips through automatically using javascript. However, if I navigate to my gallery from index.html, the images dont load. If I load it directly (i.e. load it directly into the webview with:
mWebView.loadUrl("file:///android_asset/www/btec/gallery.html");
the images appear and scroll no problem! I'm not sure why this is happening. Is there a setting I need to enable/disable? I have the following settings enabled already:
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setAllowFileAccess(true);
mWebView.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
mWebView.getSettings().setLoadsImagesAutomatically(true);
mWebView.getSettings().setPluginsEnabled(true);
This a trick that probably solves your problem.
1) Create a WebViewClient class, overriding shouldOverrideUrlLoading method:
private class MyClient extends WebViewClient {
private WebView mwv;
public MyClient(WebView v) {
mwv = v;
}
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(url.equals("file:///android_asset/www/btec/gallery.html")) {
mwv.loadUrl(url);
return true;
}
return false;
}
}
2) Associate this client to your webview:
mWebView.setWebViewClient(new MyClient(mWebView));
Probably not the best way; but I think it works... ;)
EDIT: for other details, you can see this: http://developer.android.com/reference/android/webkit/WebViewClient.html

Categories