Load websites in offline mode - android

I want to load websites from cache when internet is not connected. Here's how I setup the webview in onCreate:
browse.getSettings().setJavaScriptEnabled(true);
browse.getSettings().setPluginsEnabled(true);
browse.getSettings().setLoadWithOverviewMode(true);
browse.getSettings().setUseWideViewPort(true);
browse.getSettings().setBuiltInZoomControls(true);
browse.getSettings().setSupportZoom(true);
browse.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);
browse.setScrollbarFadingEnabled(true);
browse.getSettings().setLoadsImagesAutomatically(true);
browse.getSettings().setAppCacheMaxSize(1024 * 1024 * 100);
browse.getSettings().setAppCacheEnabled(true);
if (cm.getActiveNetworkInfo().isConnected())
browse.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
else
browse.getSettings().setCacheMode(
WebSettings.LOAD_CACHE_ELSE_NETWORK);
browse.setWebViewClient(new WebViewClient() {
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// TODO Auto-generated method stub
super.onPageStarted(view, url, favicon);
t.setVisibility(TextView.VISIBLE);
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
t.setVisibility(TextView.GONE);
}
});
try {
browse.loadUrl(address);
} catch (Exception e) {
e.printStackTrace();
}
Here's the tutorial I'm following and I'm trying to load the previously visited webpages even if the browser is restarted. Doesn't work! Please help me.
P.S: I have searched all the related codes on SO, none of them works.

Declare the following permission in your manifest file:
android.permission.ACCESS_NETWORK_STATE

I think you are missing the call to WebSettings.setAppCacheEnabled(true) :)
Also, I think it's probably better to check the connectivity and call setCacheMode() before you call loadUrl() instead of from within your onPageStarted() method (seems like it would be too late).
Lastly, I am not sure whether your override of getCacheDir() is necessary, since I believe you are simply calling the default implementation anyway in your override.
UPDATE:
After looking at the updated code in the question, I can confirm that I have similar code that worked fine. The only notable difference is I have the following code when checking for connectivity:
final NetworkInfo activeNetworkInfo = cm.getActiveNetworkInfo();
if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {
settings.setCacheMode(WebSettings.LOAD_DEFAULT);
} else {
settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
compared to your version:
if (cm.getActiveNetworkInfo().isConnected())
browse.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
else
browse.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
I tested by running in the emulator and toggling airplane mode on and off.
I did however read the tutorial that you linked more closely (and I understand now why you override getCacheDir() in your original code) and am wondering if maybe you are still calling WebView.clearCache(true) in the onDestroy() of your Activity. If you still have that part from the tutorial unmodified, it will definitely cause the cache appear not to be working :)

Have you tried nanoHttpd webserver, it is simple solution to get access webpages in sdcard or local memory.. all you want to do is give path to page as ,http://localhost:8081/[your cache directry path to offline webpage] in webview
https://gist.github.com/1893396
I tried this on pages in sdcard and phone memory, hope this will work for you

Related

Android WebViewClient's ShouldInterceptRequest is never called in MAUI WebView

UPDATE: it's a confirmed bug. Please upvote it here because it doesn't really receive a lot of attention from MS.
I need to override the shouldInterceptRequest method of WebViewClient to load in-app HTML content following that guide.
Here's the repo with the reproducible code: GitHub. I took a sample code from MS Q&A as well:
// ...
.ConfigureMauiHandlers(handlers =>
{
handlers.AddHandler<Microsoft.Maui.Controls.WebView, ProblemHandler2>();
});
// ...
internal class ProblemHandler2 : WebViewHandler
{
protected override Android.Webkit.WebView CreatePlatformView()
{
var wv = new Android.Webkit.WebView(Android.App.Application.Context);
wv.SetWebViewClient(new CustomWebClient());
return wv;
}
}
In the repo, I included 2 custom handlers:
ProblemHandler2 is the exact snippet by the MSFT. I realized a problem: Setting MAUI WebView's Source property no longer navigates the real Android WebView:
WebViewHandler.Mapper.AppendToMapping("MyHandler", (handler, view) =>
{
#if ANDROID
var xWv = handler.PlatformView;
// For ProblemHandler2, this is needed to actually navigate:
xWv.LoadUrl("https://www.google.com/");
#endif
});
this.wv.Source = "https://www.google.com/";
ProblemHandler1 uses the default result and adds a custom handler. This fixes the navigation problem, but, both problem have the same issue:
ShouldInterceptRequest is never called. It is never called on anything even when I manually click a link to navigate. What am I missing? I am sure the CustomWebClient is correctly created and set.
I noticed none of the other callbacks works as well, for example:
public override void OnPageStarted(Android.Webkit.WebView view, string url, Bitmap favicon)
{
Debugger.Break();
Debug.WriteLine(url);
base.OnPageStarted(view, url, favicon);
}
public override void OnPageFinished(Android.Webkit.WebView view, string url)
{
Debugger.Break();
Debug.WriteLine(url);
base.OnPageFinished(view, url);
}
I also tried using WebViewHandler.Mapping but it also does not work:
WebViewHandler.Mapper.AppendToMapping("MyHandler", (handler, _) =>
{
#if ANDROID
handler.PlatformView.SetWebViewClient(new CustomWebClient());
#endif
});
I could be wrong but, I think this might have to do with your overridden version of the CreatePlatform method,
Can you try what the default WebViewHandler is doing:
protected override WebView CreatePlatformView()
{
var platformView = new MauiWebView(this, Context!)
{
LayoutParameters = new LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent)
};
platformView.Settings.JavaScriptEnabled = true;
platformView.Settings.DomStorageEnabled = true;
platformView.Settings.SetSupportMultipleWindows(true);
return platformView;
}
Check this URL for the default handlers CreatePlatform setup :
https://github.com/dotnet/maui/blob/c6250a20d73e1992b4a02e6f3c26a1e6cbcbe988/src/Core/src/Handlers/WebView/WebViewHandler.Android.cs
Also don't use Application Context in Handlers, Handlers have their own Context property you can use.
Yes, it is the case as you said.
And I have created a new issue for this problem, you can follow it up here: https://github.com/dotnet/maui/issues/11004.
Thanks for your support and feedback for maui.
Best Regards.

WebView not working consistently

Edit: Strangely works when duplicating the loadUrl() line
I'm working with WebViews to gather data from a webpage. Sometimes, the webview just does nothing. I tried many proposes, but actually, none of them works....
Strange: webview.loadUrl("url...") doesn't work, but as soon as I call it twice, it works...
checkLogin()
void checkLogin(final Context context) {
Log.d("checkLogin()", "Begin of checkLogin()");
WebView webview = returnNewWebView(context, false);
webview.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url) {
Log.d("url", url);
// GOOGLE.COM ACTUALLY WORKS!
if(url.equals("https://www.google.com/"))
return;
Log.d("checkLogin()", "Loading of checkpage finished");
if(url.contains("loginto.php")) {
Log.d("checkLogin()", "User is logged out");
OnLoginCheckListener.onLoginCheckLoggedOut();
} else {
Log.d("checkLogin()", "User is logged in");
OnLoginCheckListener.onLoginCheckLoggedIn();
}
}
});
String id = sharedPref.getString("id", null);
String transid = sharedPref.getString("transid", null);
if(TextUtils.isEmpty("id") || TextUtils.isEmpty("transid")) {
Log.d("checkLogin()", "No login found, starting LoginActivity()");
Intent myIntent = new Intent(context, LoginActivity.class);
context.startActivity(myIntent);
} else {
Log.d("checkLogin()", "Probably logged in, checking by loading startpage");
webview.loadUrl("https://www.google.com");
// google actually works -.-
webview.loadUrl("https://my.login-page.com/index.php?pageid=1&id=" + id + "&transid=" + transid);
}
}
returnNewWebView()
private WebView returnNewWebView(Context context, Boolean JSInterface) {
// Prepare a webview
WebView WebView = new WebView(context);
if(JSInterface) {
WebView.getSettings().setJavaScriptEnabled(true);
WebView.addJavascriptInterface(new JavaScriptInterface(), "AndroidInterface");
}
// We don't need images for data scraping...
WebView.getSettings().setLoadsImagesAutomatically(false);
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
// Allow cookies
CookieManager.getInstance().acceptCookie();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().acceptThirdPartyCookies(WebView);
}
// Prevent webview not loading
// or try it...
WebView.clearCache(true);
WebView.destroyDrawingCache();
return WebView;
}
As you see, I already tried some stuff in order to prevent it but still, it doesn't work. In that cases, as soon I'm going to put the loadUrl() / postUrl()twice, it works.
What can I do that it works in every single case?
I would appreciate any tip!
Thank you very much in advance
logcat (non-filtered)
I actually see this message then..
03-09 07:36:19.061 4111-24251/? E/ctxmgr: [ProducerActiveIntervalImpl]closeActiveInterval: Error: ongoing, trying to close
and this one:
03-09 07:36:28.864 29862-3249/? E/accs.GcmPush: initializeApp occur error!
java.lang.IllegalStateException: FirebaseApp name [DEFAULT] already exists!
at iv.a(Unknown Source)
at com.google.firebase.FirebaseApp.a(Unknown Source)
at com.google.firebase.FirebaseApp.a(Unknown Source)
at org.android.agoo.gcm.GcmRegister$1.run(GcmRegister.java:32)
and this one too:
E/libEGL: validate_display:99 error 3008 (EGL_BAD_DISPLAY)
But I can't tell you if they are fired by the app....
I have gone through the full code for so long and pointed out this :
You are using this -
webview.loadUrl("https://www.google.com");
// google actually works -.-
webview.loadUrl("https://my.login-page.com/index.php?pageid=1&id=" + id + "&transid=" + transid);
in your checkLogin() method which is simply wrong as because one webview can not display the two urls at same time (as you mentioned it you want to achieve that).
Now according to your code the loading of the first url is overridden by the second url.
In order to achieve the loading of two urls in same webview try to implement this using :
Threads or some sessionTimeout methods.
OR
You can achieve this by using okhttp package.
Hope this helps!
onPageFinished is called when the webpage finished loading, but not after all the javascript on the page finished running. Depending on what the page is doing and what you are trying to achieve, it might be that your code is sometimes being executed before some essential javascript is executed on that page.
It's not a pretty solution but try adding some delay (like a Handler's postDelayed() or SystemClock.sleep() - but never on the UI thread!) to your code to see if that is indeed your problem.

Android webview - shouldOverrideUrlLoading() not called in some jsp pages

I've been developing hybrid apps for many companies with mobile websites.
And as a matter of fact, there are some websites made with using jsp.
I already had the knowledge that iframes and javascripts xhr requests will not fire webViewClient's shouldOverrideUrlLoading override function. I'm fine with that.
But today I learned that SOME actions such as:
JSP Page Redirects
Link Clicks within a JSP page
JSP/JS induced URL Loads
will not ALWAYS fire this function.
Hence, shouldOverrideUrlLoading() does not fire, when the webView is asked to load a page that it cannot load(i.e. "intent://...",) it shows an error page.
Has anyone encountered this kind of behaviour and is there any solution to work around it ?
Below is the code I'm using to invoke activities, where urls with 'intent:' protocol (which will fail because this function never gets called when above actions are performed)
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// ... omitted ...
if ( url.startsWith("intent:") ) {
Intent intent = null;
try {
intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
// The following flags launch the app outside the current app
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
try {
getActivity().startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
return true;
}
}
ps. please notice that every other websites' page loads will perfectly call shouldOverrideUrlLoading().
I couldn't find any JSP related bugs on android webViews so I'm asking one.
ps. I am happily willing to provide sample websites that some gracious readers will try on.. but the website's written in Korean so I doubt it will help.
Thank you!
Your problem might not related to JSP, the real problem may be shouldOverrideUrlLoading() itself. In this case, using shouldOverrideUrlLoading() may not be a good idea, so why not try another perspective?
I've encountered many problems when using
shouldOverrideUrlLoading() loading XmlHttpRequest. At the end, I
came up with the idea using onProgressChanged() and it solved all
my problems. I've written a similar answer here.
I tried adding your code into my own webview project and tested it with some JSP sites, and looks like it always work. I also added loadUrl() after other activities are invoked, so after pressing the back button, the loading error page will not be displayed again. So try this one :
First declare a global variable to store last URL.
String strLastUrl = null;
Then override onProgressChanged(WebView view, int progress)
mWebView.setWebChromeClient(new MyWebChromeClient(){
#Override
public void onProgressChanged(WebView view, int progress) {
if (progress == 100) {
//A fully loaded url will come here
String StrNewUrl = view.getUrl();
if(TextUtils.equals(StrNewUrl,strLastUrl)){
//same page was reloaded, not doing anything
}else{
String strOldUrl = null;
//save old url to variable strOldUrl before overwriting it
strOldURL = strLastUrl;
//a new page was loaded,overwrite this new url to variable
strLastUrl = StrNewUrl;
if ( strLastUrl.startsWith("intent:") ) {
Log.d("TAG", "intent triggered");
Intent intent = null;
try {
intent = Intent.parseUri(strLastUrl, Intent.URI_INTENT_SCHEME);
// The following flags launch the app outside the current app
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
e.printStackTrace();
}
//reload the page before invoking other activities
view.loadUrl(strOldURL);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
}
}
super.onProgressChanged(view, progress);
}
});

Android webview onReceivedError display custom error page and reload previous url onResume

We suppose that a url is already loaded (Let's call it the original url).
webView.setWebViewClient(new WebViewClient() {
#Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
webView.loadUrl("file:///android_asset/missing.html");
}
});
I have created my own error page to prevent the "web page not available message" to appear.
The app must reload the webview every time it is resumed. So i have the following lines of code:
#Override
protected void onResume() {
super.onResume();
webView.reload();
}
The problem here is that when the error page is loaded (for example when the user is not connected to the internet), and then a connection is available again and the user resumes the app, the original url is not loaded (which seems logic, the current now is missing.html). But is this a nice approach? Any suggestions for the problem?
There is also a refresh button if the user wants to reload the content. Same issue here.
You should load the wanted url instead of using webView.reload(), like this:
webView.loadUrl("http://yoururl");
or go back to previous page with:
webView.loadUrl("javascript:history.go(-1)");
I'd examine internet connectivity in onResume() like suggested on http://developer.android.com/training/basics/network-ops/managing.html
public static boolean isOnline() {
ConnectivityManager connMgr = (ConnectivityManager) App.instans.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
public void onResume() {
It's not just the mobile connection that can drop but also the page can become unavailable and as you rightly say using webview.loadUrl("someerrorpage.html") will create fake history and cause problems if the user tries to refresh or press back.
My solution was to replace the contents of the default error page with my own custom one e.g.
webView.evaluateJavascript("javascript:document.open();document.write('your custom error html');document.close();", null);

WebView.loadUrl(url) does nothing

I recently wrote up a simple Twitter app for Android to learn the ropes of the Twitter API and OAuth.
The app's main activity simply asks for a username to follow. It then calls another activity which handles the OAuth & Twitter API calls. It redirects the user to an authorization page, which then returns to the app after the user finishes.
It used to work just fine, but now for some reason when I call webview.loadUrl(authorizationURL), NOTHING happens. I never changed anything that would affect the WebView stuff though... Here's my code:
#Override
public void onResume() {
super.onResume();
try {
if(weNeedCredentials()) {
obtainCredentials();
}
follow(mUsername);
} catch (OAuthException oae) {
// omitted
}
}
private boolean weNeedCredentials() {
// assume the method returns true
}
private void obtainCredentials() {
final Token requestToken = mOauthService.getRequestToken();
String authUrl = mOauthService.getAuthorizationUrl(requestToken);
// I verified that authUrl is the correct url (and != null)
final WebView oauthView = (WebView) findViewById(R.id.oauthview);
oauthView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if(someCondition) {
oauthView.setVisibility(View.GONE);
doOtherStuff();
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
oauthView.getSettings().setJavaScriptEnabled(true);
oauthView.loadUrl(authUrl);
}
Here's my layout xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<WebView
android:id="#+id/oauthview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
and also I included the proper Internet permissions in the Manifest.
A WebViewCoreThread is running for the life of the app, and a WebViewWorkerThread pops in later, but no WebView ever comes up (not even a white screen). The app never blocks either. It continues running as if the loadUrl() line were simply commented out.
I've tested on my phone (Droid X2) as well as an emulator, both with the same results. Any help would be greatly appreciated.
Thanks in advance!
It continues running as if the loadUrl() line were simply commented out.
It will always "continue running as if the loadUrl() line were simply commented out". loadUrl() is asynchronous and does not block.
Off the cuff, either:
weNeedCredentials() is returning false, or
follow() is replacing the UI, or
Twitter is doing a redirect, and someCondition is true, so you are making the WebView be GONE right away
there are issues with the URL that you are loading

Categories

Resources