Trying to redirect local html page in android webview using Javascript redirect, gets denied starting an intent in Logcat:
Testing on android 5.1.1
document.location = "index.html";
Denied starting an intent without a user gesture, URI:
file:///android_asset/index.html
I read the documentation in 1,000 attempts Android.developer and this was my solution
I do not know if you understand, I speak Spanish
webView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
});
This worked for me:
webView.setWebViewClient(new WebViewClient());
There are few issues here.
From newest androids, the WebView and Chrome Client is separated application which can be automatically updated without user intention.
From Chrome x >= 25 version, they changed how loading url is working in android application which is using webview component. https://developer.chrome.com/multidevice/android/intents Looks like they are blocking changing url without user gesture and launched from JavaScript timers
Solution here is to force user to activate URL change, for example on button click.
Also, you can override method mentioned above "shouldOverrideUrlLoading" in WebView client.
As alternate, i figured out was to add addJavascriptInterface each button click event fire action to JavascriptInterface
webView.addJavascriptInterface(new java2JSAgent(), "java2JSAgentVar"); //webView webview object
public class java2JSAgent
{
#JavascriptInterface
public String getContacts()
{
String jsonResponse = "{result:'redirected'}";
runOnUiThread(new Runnable() {
#Override
public void run() {
webView.loadUrl("file:///android_asset/index.html");
}
});
return jsonResponse;
}
}
might not be a good approach but atleast its working :-)
Thanks
Related
In my project I have a WebView in a fragment which loads a page from the internet. I want to set a listener on a button in that page so that when the user clicks on it I am able to move to a different fragment. I don't have the code for the webpage that is being loaded.
I was able to implement an onClick listener for the WebView itself by using this answer, but I can't think of a way to intercept the button press specifically inside the WebView.
Any suggestions on how to implement something like this? Thanks.
You can use WebViewClient.shouldOverrideUrlLoading to detect every URL changes on your WebViewClient and according to that you can open the particular fragment instead of setting click listener of a button in webview
You should set up WebViewClient for your WebView . Here is a simple snippet:
WebView webView;
webView.setWebViewClient(webClient);
WebViewClient webClient = new WebViewClient(){
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url){
return true;
}
#Override
public void onLoadResource(WebView view, String url){
if( url.equals("http://cnn.com") ){
// Do the job here
}
}
}
Add a function to your button that calls alert("abc") in your web page.
Afterwards add following in your android code. The code will intercept your alert and you can implement your own code.
webView.setWebChromeClient(new WebChromeClient() {
public boolean onJsAlert(WebView view, String url, final String message, JsResult result) {
if (message.equals("abc"))
{
result.cancel();
return true;
}else
return false;
}
});
All the above answers were good workarounds, however, I ended up using this solution and followed the guide on Android Developers page to enable JavaScript Binding. Note that Android interface needs to be used in the JavaScript file in order for the interception to work.
The problem is rather simple.
In the application we want to keep track of the current url being displayed. For that we use shouldOverrideUrlLoading callback from the WebViewClient by saving the url into a class field for every update. Here is the relevant code:
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.getSettings().setDomStorageEnabled(true);
mWebView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
mCurrentUrl = url;
// If we don't return false then any redirect (like redirecting to the mobile
// version of the page) or any link click will open the web browser (like an
// implicit intent).
return false;
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
...
}
});
mWebView.loadUrl(mInitialUrl);
However, there is at least one scenario, where the callback never gets triggered and the mCurrentUrl field doesnt get updated.
The url: https://m.pandora.net/es-es/products/bracelets/556000
Last updated url (shouldOverrideUrlLoading never gets called when clicking the product): https://m.pandora.net/es-es/products/bracelets
I have tried with callbacks like onPageStarted(), but the url also gets filtered and there doesn't seem to be an accessible one upstream since its protected code.
Reading android documentation about WebView I found this:
https://developer.android.com/guide/webapps/migrating.html#URLs
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.
But still doesnt make sense since the above url is generic and should meet the standard.
Any alternative or solution to this?
When you click a product on that web page, it loads the new content in with JavaScript and updates the visible URL in the address bar using the HTML5 History APIs.
From the above MDN article:
This will cause the URL bar to display http://mozilla.org/bar.html, but won't cause the browser to load bar.html or even check that bar.html exists.
These are sometimes called single-page applications. Since the actual loaded page doesn’t change, the WebView callback for page loads isn’t called.
In case you know precisely what kind of HTTP request you want to intercept, you could use the shouldInterceptRequest callback that gets called for each request. It’s likely that the web application loads some data from an API, for example when a product is shown, which you could then detect.
If detecting this isn’t possible, but you’re in control of the web application, you could use the Android JavaScript interface to invoke methods within the Android application directly from the web page.
If you’re not in control of the loaded page, you could still try to inject a local JavaScript file into the web page and observe when the history APIs are used, then call methods in your Android application over the JS interface. I tried observing these events in Chrome with the method described in the previous link and it seems to work fine.
Maybe this helps someone, although the signature in the question is correct, but Android Studio suggests the following method signature:
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
which then never called. It took me a while to notice that the right signature is:
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Sorry if this not 100% fit the question, but I believe this may help someone in the same situation. It's not always easy to notice that the second parameter is different.
Please omit mWebView.getSettings().setDomStorageEnabled(true);
Then again try, if a new url found then will invoke shouldOverrideUrl()
I had the same problem like you, and I've finished with extending of WebViewChromeClient with listening for callback to
public void onReceivedTitle(WebView view, String title)
mWebView.setWebChromeClient(mSWWebChromeClient);
private WebChromeClient mSWWebChromeClient = new WebChromeClient() {
#Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (!view.getUrl().equals(mCurrentUrl)) {
mCurrentUrl = view.getUrl();
//make something
}
}
};
For me the problem was below line -
mWebView.getSettings().setSupportMultipleWindows(true);
After removing it shouldOverrideUrlLoading was being called.
after stumbling on this problem and searching for solutions, I've found the one that worked perfectly for me
https://stackoverflow.com/a/56395424/10506087
override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) {
// your code here
super.doUpdateVisitedHistory(view, url, isReload)
}
Another approach you can try: Catch the url by javascript side. Initialize your webView with this:
webView.addJavascriptInterface(new WebAppInterface(getActivity()), "Android");
After page is completely loaded (You can use an algorithm to check this like this https://stackoverflow.com/a/6199854/4198633), then:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript("(function() {return window.location.href;})", new ValueCallback<String>() {
#Override
public void onReceiveValue(String url) {
//do your scheme with variable "url"
}
});
} else {
webView.loadUrl("javascript:Android.getURL(window.location.href);");
}
And declare your WebAppInterface:
public class WebAppInterface {
Activity mContext;
public WebAppInterface(Activity c) {
mContext = c;
}
#JavascriptInterface
public void getURL(final String url) {
mContext.runOnUiThread(new Runnable() {
#Override
public void run() {
//do your scheme with variable "url" in UIThread side. Over here you can call any method inside your activity/fragment
}
});
}
}
You can do something like that to get url, or anything else inside the page.
Add
webView.getSetting().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
then shouldOverrideUrl will be triggered.
onProgressChanged is always triggered when reloading, loading new page with userclick or XmlHttpRequest.
Compare the URL of previous load and the current load, you'll know it's reloading or loading a new page. This works perfect in my single page Web App.
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{
//a new page was loaded,write this new url to variable
strLastUrl = StrNewUrl;
//do your work here
Log.d("TAG", "A new page or xhr loaded, the new url is : " + strLastUrl);
}
}
super.onProgressChanged(view, progress);
}
});
I've also tried above solutions, but most of them have issue in my case:
doUpdateVisitedHistory sometimes can not return correct url after "#" made by XmlHttpRequest.
My case is a single page web App. The web App uses javascript with
xhr to display new page when user click an item. For example, user is
currently at http://example.com/myapp/index.php , after clicking, the
browser url becomes
http://example.com/myapp/index.php#/myapp/query.php?info=1, but in
this case, doUpdateVisitedHistory returns
http://example.com/myapp//myapp/
onReceivedTitle doesn't work in my case because the response retrieved by XMLHttpRequest does not have <title></title> tag.
The JavascriptInterface method also works, but I'm afraid it will cause
security related issues with javascript.
public class AndroidMobileAppSampleActivity extends Activity {
/** Called when the activity is first created. */
String mCurrentUrl="";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
WebView mWebView = (WebView) findViewById(R.id.mainWebView);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.setWebViewClient(new MyCustomWebViewClient());
mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
mWebView.loadUrl("https://m.pandora.net/es-es/products/bracelets/556000");
}
private class MyCustomWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
mCurrentUrl = url;
Log.i("mCurrentUrl",""+mCurrentUrl);
view.loadUrl(url);
return true;
}
}
}
try this one...
I have two Applications with two web-views (a web-view in each). to simply explain my requirement with an example consider a user adds few items in his shopping trolley in first web-view in first app and then he press checkout and this will launched second web-view (which is in second app), result : he needs to see same shopping cart (in second web-view too).
I know sharing cookies between two webviews in a single app is not a problem and it is done in background by Android (CookieManager) however my case is different, I need to achieve the same in two different App.
Perhaps what I am looking for is how to share cookies/ session cookies between two webviews in different apps?
Note: I am restricted to targeted API 18
What I tried so far (apart of lots of useless reading) is :
getting cookie of the URL from the first webview and send it through
intent to second APP's activity
mWebView.setWebChromeClient(new WebChromeClient() {
});
mWebView.loadUrl(getPreferedURL().toString());
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setAppCacheEnabled(true);
mWebView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (!url.contains("openCheckoutPage")) {
return false;
} else {
mCookie = CookieManager.getInstance().getCookieurl);
Intent fullScreenIntent = new Intent("com.bastami1982.webview.app.WebviewPanel_ACTION");
fullScreenIntent.putExtra("mUrl", mUrl);
fullScreenIntent.putExtra("mCookie", mCookie);
startActivity(fullScreenIntent);
return true;
}
}
});
set the cookie in second app webview
private void loadFullWebview() {
mLink = getIntent().getStringExtra("mUrl");
mLinkCookie = getIntent().getStringExtra("mCookie");
mFullScreenWebView = (WebView) findViewById(R.id.webView);
mFullScreenWebView.setWebChromeClient(new WebChromeClient() {
});
WebSettings settings = mFullScreenWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setAppCacheEnabled(true);
if (mLinkCookie!=null) {
mCoockieManager.setAcceptCookie(true);
mCoockieManager.setCookie(mLink, mLinkCookie);
mCoockieManager.acceptCookie();
CookieSyncManager.getInstance().sync();
}
mFullScreenWebView.loadUrl(mLink);
mFullScreenWebView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
});
}
I can confirm that I will get the cookie in second app but it seems webview is not using the cookie because the shopping cart is still empty.
I hope I explained it good enough to have some feedback , having said that please ask me for more info if it is needed
I checked the logcat in second App and I think this means that items in trolley can not be find (getItem) (I used Argos.co.uk) to test their shopping Trolley in another word I think cookie is not attached to the url correctly at loading ?! just guessing...
12-18 16:18:11.754 14675-14675 I/Web Console: Error while sending custom tracking data at https://d1af033869koo7.cloudfront.net/psp/argos-v1-001/default/20151216103020/pxfwk.gz.js:7
12-18 16:18:11.904 14675-14675 E/Web Console: Uncaught TypeError: Cannot call method 'getItem' of null at https://argospsp.px.247inc.net/pspxd.html#parentdomain=https://www.argos.co.uk&clientkey=argos-v1-001&version=20151216103020&pspv=default&n=1&caller=refloaded&l=1000&s=cookie:5
Try this...
private void loadFullWebview() {
mLink = getIntent().getStringExtra("mUrl");
mLinkCookie = getIntent().getStringExtra("mCookie");
mFullScreenWebView = (WebView) findViewById(R.id.webView);
WebSettings settings = mFullScreenWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setAppCacheEnabled(true);
if (mLinkCookie!=null) {
mCoockieManager.removeSessionCookie();
CookieSyncManager.createInstance(this);
mCoockieManager.acceptThirdPartyCookies(mFullScreenWebView,true);
mCoockieManager.setAcceptCookie(true);
mCoockieManager.setCookie(mLink, mLinkCookie);
CookieSyncManager.getInstance().sync();
}
mFullScreenWebView.loadUrl(mLink);
mFullScreenWebView.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
});
}
It's been a while since this was asked, but I just had to do something similar (for different reasons)...
What worked for me was to create a service to share the values between the apps.
In my particular case, it was for mobile devices that were managed - so we controlled what is installed on the devices. Since we controlled things, I created a separate app install to have a service which was independent from both of the apps that each had an embedded webview.
The webviews would push cookies to the service when they were updated via monitoring the webview's page loads and querying for cookies at that point. They would load cookies up from the service when the app was foregrounded so they would get anything that had been set while they were backgrounded and the other app was active - and those cookies were added to the webview via the CookieManager.
In the case where there is a family of apps on the PlayStore, it could be done similarly, but there would need to be a service in each app for the other app to connect to and listen from.... If there are more than 2 apps, it could get kind of complex - so might need some creative patterns to handle pulling from all the apps when foregrounded, and keeping track of timestamps to sort out which ones were more recently saved so they overwrite the others....
Or, if one app will always be present, it can be the "main" app that contains the "service", and things will work exactly as what I've done.
Sorry no code here, figured I'd share the approach in case anyone else needs the idea.
I realized from working today that there's an easier way to do this, so answer has been edited to give the most complete answer I could.
Granted, this is Xamarin.Android, but it should work the same for Android Java (maybe kotlin too?):
I recommend doing the cookie getting in OnPageFinished override, but you could theoretically do it anywhere you have an Android.Webkit.CookieManager.Instance context. The reason I recommmend doing it in OnPageFinished() is because you should have the full set of cookies after your page loads.
Set your WebView to use a customized WebViewClient as such, which will allow you to override the OnPageFinished() method, thus getting the cookies at just the right time. If you don't want this overhead after you've got the cookies then you can always switch the WebViewClient or WebView to a vanilla unmodified WebViewClient.
Here's setting this client (optional)
FragmentContainerLayout = inflater.Inflate(Resource.Layout.Tab0FragLayout, container, false); }
Wv = FragmentContainerLayout.FindViewById<WebView>(Resource.Id.webView1);
Wv.SetWebViewClient(new LoginWebViewClient());
and here's getting the cookie from
APP SENDING COOKIE:
public class LoginWebViewClient : WebViewClient
{
public override void OnPageFinished(WebView view, string url)
{
RunPageCommands(view);
base.OnPageFinished(view, url);
}
public static void RunPageCommands(WebView w)
{
try
{
//replace this with yourDomain
string exampleDomain = "https://www." + "example.com"+ #"/"; //or .net or .tv etc
// get the cookie header in context
string cookieHeader = Android.Webkit.CookieManager.Instance.GetCookie(exampleDomain);
}
catch { return; /*cookie getting failed, nothing to do*/ }
ShareCookieWithAnotherApp(cookieHeader, exampleDomain);
}
}
if you don't need to get the cookie every time then you would not want to use an extended WebViewClient after you've got the cookie .. as it will slow down your app. You can alternatively just get the cookie header anywhere in the app with the CookieManager and pass it as an argument to any
You can reset the WebViewClient to the stock one by nulling it out WebView.SetWebViewClient(null) then set it back to the unextended version
then, in the context of an Activity, do something like this, which creates an Intent for another app to respond, and be careful here if your cookie has security tokens in it.
App SENDING cookie:
public void ShareCookieWithAnotherApp(string cookie, string domain){
Intent intent = new Intent();
intent.SetClassName("com.appstore", "com.appstore.MyBroadcastReceiver");
intent.SetAction("com.appstore.MyBroadcastReceiver");
intent.PutExtra("CookieSyncExtra", cookie);
intent.PutExtra("CookieDomainExtra", domain);
SendBroadcast(intent);
}
in the app receiving cookie, extend broadcast receiver:
public class MyBroadcastReceiver : BroadcastReceiver {
//this fires when it receives the broadcast from the sending app
public override void OnReceive(Context context, Intent intent) {
// set the CM to accept cookies
CookieManager.Instance.SetAcceptCookie(true);
//and set the cookie from your extras
CookieManager.Instance.SetCookie(intent.GetStringExtra("CookieDomainExtra"), intent.GetStringExtra("CookieSyncExtra"));
}
}
Manifest (receiving app), set the intent-filter :
<receiver
android:name=".MyBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="first_app_packagename" />
</intent-filter>
</receiver>
and in your Activity of the receiving app:
//OnCreate
MyBroadcastReceiver MyReceiver = new MyBroadcastReceiver();
//I put this OnResume() but you can experiment with different placements
RegisterReceiver(MyReceiver , new IntentFilter("first_app_packagename"));
then you can use the WebView.LoadUrl(string url) after you've SetCookie in the receiving app
you can also attach the cookie header manually using the Dictionary<string, string> overload
Dictionary<string, string> cookieHeaderDictionary = new Dictionary<string, string>();
cookieHeaderDictionary.Add("Cookie", cookieString);
LoadUrl(url, cookieHeaderDictionary);
source: How to send data between one application to other application in android?
I have an Android app that is redirected users to a webpage that contains a reCAPTCHA question. Previously, I was implementing this simply by opening a browser window, and directing the user there. Recently, I changed it to use a webview instead for a better user experience, but the problem is that now for some reason the reCAPTCHA question is not rendered on the page; everything else functions normally. Why would this be, and how might I fix it? I assume this must have something to do with accessing a different domain from the webview (www.google.com), but not sure how to configure things differently that it's not an issue. Here is how I am setting up the Webview. Note that the overridden method is for handling some OAuth authorization process that can happen in this Webview. Even if I comment that out, I have the same problem.
WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.loadUrl(this.url);
myWebView.setWebViewClient(new WebViewClient()
{
#Override
public boolean shouldOverrideUrlLoading(WebView webView, String url){
Uri uri = Uri.parse(url);
if (url != null && uri.getScheme().equals(NNApplication.CALLBACK_SCHEME)) {
SharedPreferences shPref = getSharedPreferences("NN_PREFS", Activity.MODE_PRIVATE);
new OAuthAccessTokenTask(Authorization.this, consumer, provider, shPref).execute(uri);
webView.setVisibility(View.GONE);
finish();
return true;
}else{
return false;
}
}
#Override
public void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error) {
handler.proceed() ;
}
});
Likewise, you can view the reCAPTCHA question at the URL below. I already checked, and it's behaving the same between our development site and our live site:
https://www-dev.usanpn.org/user/register
Captcha requires javascript:
myWebView.getSettings().setJavaScriptEnabled(true);
I am trying to load a wiki web page into a native Android WebView with WebView.loadUrl(String url). It works and displays all of the web content, except for the images. However, I have not found a reason for this. Does anyone know why image resources would not load automatically? Is there a method I need to overwrite or a parameter that needs to be set? I can't find anything in the documentation - it looks like the default settings should allow for automatic image loading. Any information would be appreciated. Thanks!
mWebView = (WebView)findViewById(R.id.webView);
setWebViewClient(mWebView);
setWebSettings(mWebView);
mWebView.loadUrl("http://my_wiki_url_goes_here/");
private void setWebViewClient(WebView mWebView) {
WebViewClient client = new WikiWebViewClient();
mWebView.setWebViewClient(client);
}
private void setWebSettings(WebView mWebView) {
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
}
public class WikiWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
}
Printing the url onLoadResource(), I see that the image urls generally start with "data:image/png;base64,". I don't think that should makes a difference or not - it works in the web browser.
Huh, looks like simply enabling DOM storage did the trick.
settings.setDomStorageEnabled(true);
If I don't receive a better explanation or solution, I will accept this answer once I am able to