How to call JavaScript from webview in fragment? - javascript

Below code, I am using to load webview in fragment and I want to call JavaScript function from webview but below code is not working.
public class MainActivity extends ActionBarActivity
{
public void loadUrlInWebView(String url,String positon)
{
WebViewFragment fragment = new WebViewFragment();
Bundle data = new Bundle();
data.putString("url",url);
fragment.setArguments(data);
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frame_container, fragment).commit();
}
var url ="javascript:mobileApp.openLoginMenu()";
loadUrlInWebView(url,null);
}
public class WebViewFragment extends Fragment{
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
url = getArguments().getString("url");
View rootView = inflater.inflate(R.layout.activity_main, container, false);
webView = (WebView) rootView.findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new JsInterface(getActivity()),"AndroidJSObject");
webView.loadUrl(url);
webView.setWebViewClient(new WebViewClient()
{
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
view.loadUrl(url);
return true;
}
});
return rootView;
}
}

To call a javascript into android you can call addJavascriptInterface().
For more explanation please follow this link Building Web Apps in WebView.
And the thing to keep in mind is,
When you want to call an android method from javascript method, the method name should be called on the string name you have provided while calling addJavascriptInterface() precisely in your case something like this in your javascript
AndroidJSObject.yourmethod
Inside javascript file.

if android:targetSdkVersion >=17
add #SuppressLint("JavascriptInterface") in onCreated method
and
class InJavaScript {
#JavascriptInterface
public void runOnAndroidJavaScript(String status) {
collectionStatus.setCollectionStatus(status);
}
}
watch out #JavasscriptInterface.

Related

Webview does not load javascript to get content

I want to get all text in body tag of a url but it doesn't work. I have searched many but i could not find anything. I have also added android.permission.INTERNET also.
so what is the problem?
This is my code:
public class Activity_Main extends Activity {
#SuppressLint("JavascriptInterface")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final WebView webView = (WebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
final TextView contentView = (TextView) findViewById(R.id.contentView);
class MyJavaScriptInterface
{
private TextView contentView;
public MyJavaScriptInterface(TextView aContentView)
{
contentView = aContentView;
}
#SuppressWarnings("unused")
public void processContent(String aContent)
{
final String content = aContent;
contentView.setText(content);
}
}
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new MyJavaScriptInterface(contentView), "INTERFACE");
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url)
{
webView.loadUrl("javascript:window.INTERFACE.processContent(document.getElementsByTagName('body')[0].innerText);");
}
});
webView.loadUrl("https://stackoverflow.com");
}
}
update #JavascriptInterface for your interface function and check
class MyJavaScriptInterface
{
private TextView contentView;
public MyJavaScriptInterface(TextView aContentView)
{
contentView = aContentView;
}
#JavascriptInterface
public void processContent(String aContent)
{
final String content = aContent;
contentView.setText(content);
}
}
and there is another way to do this is this for above KITKAT
webView.evaluateJavascript("alert('pass here some ...')", new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
}
});
this is update solution for executing js in android
full code is here
final WebView webView = (WebView) findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
final TextView contentView = (TextView) findViewById(R.id.contentView);
class MyJavaScriptInterface
{
private TextView contentView;
public MyJavaScriptInterface(TextView aContentView)
{
contentView = aContentView;
}
#JavascriptInterface
public void processContent(String aContent)
{
final String content = aContent;
contentView.setText(content);
}
}
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new MyJavaScriptInterface(contentView), "INTERFACE");
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageFinished(WebView view, String url)
{
super.onPageFinished(view,url);
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript("document.getElementsByTagName('body')[0].innerText", new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
contentView.setText(s);
}
});
}
else {
webView.loadUrl("javascript:window.INTERFACE.processContent(document.getElementsByTagName('body')[0].innerText)");
}
}
});
webView.loadUrl("https://stackoverflow.com");
To call a JavaScript function from Android you don't need to add interface prefix:
webView.loadUrl("javascript:processContent(document.getElementsByTagName('body')[0].innerText);");
Note: You must implement the called javascript function in your HTML page. So If you are calling a third party website in your webview, you have only access to the current existing functions in that page.
e.g https://stackoverflow.com has no javascript function named INTERFACE.processContent!
On the contrary when you want to call and Android method from javascript then you need that prefix:
<script>INTERFACE.myAndroidMethod()</script>
Finally if you want to proccess the HTML content of a thirdparty webpage you can not extract the content using a Webview and JavaScript function (that not exists in that page) but you need Android codes to load the webpage content without the WebView mediation.

Android webview href not support in html page

I want to load to a about.html page inside asset folder by clicking a button in index.html inside android webview.
Here's the code:
Button:
About
Java:
WebView browser = (WebView) findViewById(webview);
browser.getSettings().setJavaScriptEnabled(true);
browser.setWebChromeClient(new WebChromeClient());
browser.loadUrl("file:///android_asset/www/index.html");
browser.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return false;
}
});
But the problem is when i click on button it doesn't load about.html but if I add a javascript function with below code it works.. but i want to use href.
Code:
<button onclick="about()">About</button>
<script>
function about() {
location.href = "about.html";
}
</script>
How do I solve it?
write an interface for TagHandler and in your fromHtml method check for the tag and handle it by loading the page
https://developer.android.com/reference/android/text/Html.html
try this code:
private void loadWebView(WebView webView,String htmlString)
{
try {
final String mimeType = "text/html";
final String encoding = "UTF-8";
WebViewClient yourWebClient = new WebViewClient()
{
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url)
{
// This line we let me load only pages inside Webpage
if ( url.contains("") == true )
// Load new URL Don't override URL Link
return false;
// Return true to override url loading (In this case do nothing).
return true;
}
};
// Get Web view
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setSupportZoom(true);
webView.getSettings().setBuiltInZoomControls(true);
webView.setWebViewClient(yourWebClient);
// Load URL
webView.loadDataWithBaseURL("", htmlString, mimeType, encoding, "");
} catch (Exception e) {
e.printStackTrace();
}
}
use it as:
loadWebView(browser, "file:///android_asset/www/index.html") //pass url

Making facebook login work with an Android Webview

I am just trying to implement the facebook login on a WebView in Android. The problem is after I click the facebook button on my HTML page and insert the username and password on Facebook dialog. The url redirect is just giving me a black page.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView webview = new WebView(this);
webview.setWebChromeClient(new WebChromeClient());
webview.getSettings().setPluginState(PluginState.ON);
webview.getSettings().setJavaScriptEnabled(true);
webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
webview.setWebViewClient(new WebViewClient());
webview.loadUrl("http://peoplehunt.crowdscanner.com/hunt");
setContentView(webview);
This is the Facebook regular javascript API on my HTML page and this function gets called when the facebook button is clicked.
$("#login_facebook").click(function() {
FB.login(function(response) {
//This function should be called
if (response.authResponse) {
FB.api('/me?fields=name,email,picture,id&type=large', function(response) {
//console.log("email "+response.email);
$("#submitHandle").hide();
$("#loader").show();
//console.log('Good to see you, ' + response.picture + '.');
var theUsername = response.name;
theUsername = theUsername.replace(/ /g, '_')+"_"+response.id+"#meetforeal.com";
//console.log(theUsername);
$("input[name=email]").val(encodeURIComponent(response.email));
$("input[name=lastName]").val(encodeURIComponent(response.name));
$("input[name=avatarImage]").val(response.picture);
$("input[name=userName]").val(encodeURIComponent(theUsername));
$("#msg_twitter").fadeIn("slow");
$("#submitHandle").show();
$("#loader").hide();
$("#user").attr("action","/crowdmodule/auth/registerattendeefacebook");
$("#user").submit();
});
} else {
//console.log('User cancelled login or did not fully authorize.');
}
}, {scope: 'email'});
ANY ideas on how to get the response back after the redirect on the Facebook dialog page? THANKS.
I had the same issue on my android application.
The cause of the issue is FB login javascript opens a new page on a new window. Then it tries to close it and send some javascript auth codes back, after login success. WebView is typically "single window only" so it has no place to go back to, hence the blank screen.
Please follow flowing example from my working codes.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0099cc"
tools:context=".MyActivity"
android:id="#+id/webview_frame">
<WebView
android:id="#+id/webview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
The Webview of id "webview" is the main view for my content.
Below is my activity codes.
public class MyActivity extends Activity {
/* URL saved to be loaded after fb login */
private static final String target_url="http://www.example.com";
private static final String target_url_prefix="www.example.com";
private Context mContext;
private WebView mWebview;
private WebView mWebviewPop;
private FrameLayout mContainer;
private long mLastBackPressTime = 0;
private Toast mToast;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_urimalo);
// final View controlsView =
// findViewById(R.id.fullscreen_content_controls);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
mWebview = (WebView) findViewById(R.id.webview);
//mWebviewPop = (WebView) findViewById(R.id.webviewPop);
mContainer = (FrameLayout) findViewById(R.id.webview_frame);
WebSettings webSettings = mWebview.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAppCacheEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setSupportMultipleWindows(true);
mWebview.setWebViewClient(new UriWebViewClient());
mWebview.setWebChromeClient(new UriChromeClient());
mWebview.loadUrl(target_url);
mContext=this.getApplicationContext();
}
private class UriWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
String host = Uri.parse(url).getHost();
//Log.d("shouldOverrideUrlLoading", url);
if (host.equals(target_url_prefix))
{
// This is my web site, so do not override; let my WebView load
// the page
if(mWebviewPop!=null)
{
mWebviewPop.setVisibility(View.GONE);
mContainer.removeView(mWebviewPop);
mWebviewPop=null;
}
return false;
}
if(host.equals("m.facebook.com"))
{
return false;
}
// Otherwise, the link is not for a page on my site, so launch
// another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
#Override
public void onReceivedSslError(WebView view, SslErrorHandler handler,
SslError error) {
Log.d("onReceivedSslError", "onReceivedSslError");
//super.onReceivedSslError(view, handler, error);
}
}
class UriChromeClient extends WebChromeClient {
#Override
public boolean onCreateWindow(WebView view, boolean isDialog,
boolean isUserGesture, Message resultMsg) {
mWebviewPop = new WebView(mContext);
mWebviewPop.setVerticalScrollBarEnabled(false);
mWebviewPop.setHorizontalScrollBarEnabled(false);
mWebviewPop.setWebViewClient(new UriWebViewClient());
mWebviewPop.getSettings().setJavaScriptEnabled(true);
mWebviewPop.getSettings().setSavePassword(false);
mWebviewPop.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mContainer.addView(mWebviewPop);
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(mWebviewPop);
resultMsg.sendToTarget();
return true;
}
#Override
public void onCloseWindow(WebView window) {
Log.d("onCloseWindow", "called");
}
}
}
The key for this issue is onCreateWindow. A new window is created and inserted to the frame layout and removed upon success. I added the removal at shouldOverrideUrlLoading.
Here is an example Android project:
Github: Android_Popup_Webview_handler_example
This is an Android Studio project showing how to handle popups in Android Webview.
Most open Source browsers do not support opening popups.
Popups are especially important in OAuth login used in a lot of websites (e.g., www.feedly.com).
The popups in this project opened in a dialog and can be dismissed by a close button or pressing Back or if the popup window closes itself (like what happens on most login authentication flow).
Regarding the best answer to this question, you just need to implement the onPageFinished method of the WebViewClient class that you use.
public void onPageFinished(WebView view, String url) {
// First, get the URL that Facebook's login button is actually redirecting you to.
// It should be something simulator to https://www.facebook.com/dialog/return/arbiter?relation=opener&close=true
String webUrl = webView.getUrl();
// Pass it to the LogCat so that you can then use it in the if statement below.
Log.d.println(TAG, webUrl);
if (url.startsWith("The url that you copied from the LogCat")) {
// Check whether the current URL is the URL that Facebook's redirecting you to.
// If it is - that's it - do what you want to after the logging process has finished.
return;
}
super.onPageFinished(view, url);
}
It worked just fine for me. Hope it helps you too :)
Override shouldOverrideUrlLoading() in your WebViewClient. Search for shouldOverrideUrlLoading here. Also, there is a parameter you can pass to facebook's login API; I think it's redirect_uri. That should help you recognize when the login is successful and in your shouldOVerrideUrlLoading(), you would just have to detect the url being loaded and if it is the redirect_uri you specified, then just return true in that method and close the webview or whatever you want to when the login is successful.
Above answer is too old and it does not work with latest Facebook sdk version 2.7. After spending 4 hours on it, I have found out few changes in it. Following code will work fine with latest SDK.
Below mentioned is the XML layout file.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0099cc"
tools:context=".MainActivity"
android:id="#+id/webview_frame">
<WebView
android:id="#+id/webView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</FrameLayout>
This is the Android Activity code snippet
public class MainActivity extends AppCompatActivity {
private WebView webView;
private WebView mWebviewPop;
private FrameLayout mContainer;
private Context mContext;
private String url = "https://www.YourWebsiteAddress.com";
private String target_url_prefix = "www.YourWebsiteAddress.com";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Get outer container
mContainer = (FrameLayout) findViewById(R.id.webview_frame);
webView = (WebView)findViewById(R.id.webView);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setAppCacheEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webSettings.setSupportMultipleWindows(true);
//These two lines are specific for my need. These are not necessary
if (Build.VERSION.SDK_INT >= 21) {
webSettings.setMixedContentMode( WebSettings.MIXED_CONTENT_ALWAYS_ALLOW );
}
//Cookie manager for the webview
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
webView.setWebViewClient(new MyCustomWebViewClient());
webView.setWebChromeClient(new UriWebChromeClient());
webView.loadUrl("https://www.YourWebsiteAddress.com");
mContext=this.getApplicationContext();
}
#Override
public void onBackPressed() {
if(webView.isFocused() && webView.canGoBack()) {
webView.goBack();
} else {
super.onBackPressed();
}
}
private class MyCustomWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
String host = Uri.parse(url).getHost();
if( url.startsWith("http:") || url.startsWith("https:") ) {
if(Uri.parse(url).getPath().equals("/connection-compte.html")) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.YourWebsiteAddress.com"));
startActivity(browserIntent);
return true ;
}
if (host.equals(target_url_prefix)) {
if (mWebviewPop != null) {
mWebviewPop.setVisibility(View.GONE);
mContainer.removeView(mWebviewPop);
mWebviewPop = null;
}
return false;
}
if (host.equals("m.facebook.com") || host.equals("www.facebook.com") || host.equals("facebook.com")) {
return false;
}
// Otherwise, the link is not for a page on my site, so launch
// another Activity that handles URLs
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
return true;
}
// Otherwise allow the OS to handle it
else if (url.startsWith("tel:")) {
Intent tel = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
startActivity(tel);
return true;
}
//This is again specific for my website
else if (url.startsWith("mailto:")) {
Intent mail = new Intent(Intent.ACTION_SEND);
mail.setType("application/octet-stream");
String AdressMail = new String(url.replace("mailto:" , "")) ;
mail.putExtra(Intent.EXTRA_EMAIL, new String[]{ AdressMail });
mail.putExtra(Intent.EXTRA_SUBJECT, "");
mail.putExtra(Intent.EXTRA_TEXT, "");
startActivity(mail);
return true;
}
return true;
}
#Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
Log.d("onReceivedSslError", "onReceivedSslError");
//super.onReceivedSslError(view, handler, error);
}
#Override
public void onPageFinished(WebView view, String url) {
if(url.startsWith("https://m.facebook.com/v2.7/dialog/oauth")){
if(mWebviewPop!=null)
{
mWebviewPop.setVisibility(View.GONE);
mContainer.removeView(mWebviewPop);
mWebviewPop=null;
}
view.loadUrl("https://www.YourWebsiteAddress.com");
return;
}
super.onPageFinished(view, url);
}
}
private class UriWebChromeClient extends WebChromeClient {
#Override
public boolean onCreateWindow(WebView view, boolean isDialog,
boolean isUserGesture, Message resultMsg) {
mWebviewPop = new WebView(mContext);
mWebviewPop.setVerticalScrollBarEnabled(false);
mWebviewPop.setHorizontalScrollBarEnabled(false);
mWebviewPop.setWebViewClient(new MyCustomWebViewClient());
mWebviewPop.getSettings().setJavaScriptEnabled(true);
mWebviewPop.getSettings().setSavePassword(false);
mWebviewPop.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mContainer.addView(mWebviewPop);
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(mWebviewPop);
resultMsg.sendToTarget();
return true;
}
#Override
public void onCloseWindow(WebView window) {
Log.d("onCloseWindow", "called");
}
}
}
My answer is essentially similar to some others here, in that I create a second WebView to host the Facebook login page, rather than attempting to solve the problem with redirects. However, I chose to place the login WebView in its own Fragment, and give it is own dedicated WebViewClient and WebChromeClient subclasses. I think this makes it a little easier to see what role each component plays, and which objects need which settings and behaviors.
I also make use of WebChromeClient.onCloseWindow() to detect when Facebook's JavaScript wants to close the login window. This is much more robust than the approach I originally pursued, from a different answer.
In your Activity layout, you'll have the "primary" WebView, which hosts the comments, and a container for the FacebookWebLoginFragment. The login Fragment is created on-the-fly, when it's needed, and then removed when Facebook's login JavaScript requests that its window be closed.
My Activity layout looks like this:
<include layout="#layout/toolbar_common" />
<FrameLayout
android:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/web_view_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
/>
<!-- Used for Facebook login associated with comments -->
<FrameLayout
android:id="#+id/facebook_web_login_fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never"
android:visibility="gone"
/>
</FrameLayout>
Within your Activity, you'll need code to show and hide the Facebook web login fragment. I use the Otto event bus, so I have event handlers like the ones below. (Nothing here is specific to this problem; I include this code just to give you a sense of how the login Fragment fits into the overall structure.)
#Subscribe
public void onShowFacebookWebLoginEvent(ShowFacebookWebLoginEvent event) {
FacebookWebLoginFragment existingFragment = getFacebookWebLoginFragment();
if (existingFragment == null) {
mFacebookWebLoginFragmentContainer.setVisibility(View.VISIBLE);
createFacebookWebLoginFragment(event);
}
}
#Subscribe
public void onHideFacebookWebLoginEvent(HideFacebookWebLoginEvent event) {
FacebookWebLoginFragment existingFragment = getFacebookWebLoginFragment();
if (existingFragment != null) {
mFacebookWebLoginFragmentContainer.setVisibility(View.GONE);
FragmentManager fm = getSupportFragmentManager();
fm.beginTransaction()
.remove(existingFragment)
.commit();
}
}
#Nullable
private FacebookWebLoginFragment getFacebookWebLoginFragment() {
FragmentManager fm = getSupportFragmentManager();
return (FacebookWebLoginFragment) fm.findFragmentById(R.id.facebook_web_login_fragment_container);
}
private void createFacebookWebLoginFragment(ShowFacebookWebLoginEvent event) {
FragmentManager fm = getSupportFragmentManager();
FacebookWebLoginFragment fragment = (FacebookWebLoginFragment) fm.findFragmentById(R.id.facebook_web_login_fragment_container);
if (fragment == null) {
fragment = FacebookWebLoginFragment.newInstance(event.getOnCreateWindowResultMessage());
fm.beginTransaction()
.add(R.id.facebook_web_login_fragment_container, fragment)
.commit();
}
}
While the FacebookWebLoginFragment is around, it should be given authority to handle device back-button presses. This is important because the Facebook login flow includes the ability to navigate away from the login page, and the user will expect the back button to return them to login. So, in my Activity, I have this:
#Override
public void onBackPressed() {
boolean handled = false;
FacebookWebLoginFragment facebookWebLoginFragment = getFacebookWebLoginFragment();
if (facebookWebLoginFragment != null) {
handled = facebookWebLoginFragment.onBackPressed();
}
if (!handled) {
WebViewFragment fragment = getWebViewFragment();
if (fragment != null) {
handled = fragment.onBackPressed();
}
}
if (!handled) {
finish();
}
}
The layout for FacebookWebLoginFragment is extremely simple:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="#+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
Here's the FacebookWebLoginFragment code. Note that it relies on a subclass of WebChromeClient to detect when the Facebook login JavaScript is ready to close the window (i.e., remove the fragment). Note also that there is no direct communication between this login WebView and the primary WebView, which contains the comments UI; the auth token is passed along via a third-party cookie, which is why you have to be sure to enable third-party cookie support on your primary WebView.
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
import android.os.Bundle;
import android.os.Message;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.SslErrorHandler;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import butterknife.Bind;
import butterknife.ButterKnife;
/**
* Hosts WebView used by Facebook web login.
*/
public class FacebookWebLoginFragment extends BaseFragment {
private static final String LOGTAG = LogHelper.getLogTag(FacebookWebLoginFragment.class);
#Bind(R.id.web_view) WebView mFacebookLoginWebView;
private WebChromeClient mFacebookLoginWebChromeClient;
private Message onCreateWindowResultMessage;
public static FacebookWebLoginFragment newInstance(Message onCreateWindowResultMessage) {
FacebookWebLoginFragment fragment = new FacebookWebLoginFragment();
fragment.onCreateWindowResultMessage = onCreateWindowResultMessage;
return fragment;
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.frag_facebook_web_login, container, false);
ButterKnife.bind(this, rootView);
return rootView;
}
#Override
public void onViewCreated(View v, #Nullable Bundle savedInstanceState) {
super.onViewCreated(v, savedInstanceState);
mFacebookLoginWebView.setVerticalScrollBarEnabled(false);
mFacebookLoginWebView.setHorizontalScrollBarEnabled(false);
mFacebookLoginWebView.setWebViewClient(new FacebookLoginWebViewClient());
mFacebookLoginWebView.getSettings().setJavaScriptEnabled(true);
mFacebookLoginWebView.getSettings().setSavePassword(false);
mFacebookLoginWebView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
mFacebookLoginWebChromeClient = makeFacebookLoginWebChromeClient();
mFacebookLoginWebView.setWebChromeClient(mFacebookLoginWebChromeClient);
WebView.WebViewTransport transport = (WebView.WebViewTransport) onCreateWindowResultMessage.obj;
transport.setWebView(mFacebookLoginWebView);
onCreateWindowResultMessage.sendToTarget();
onCreateWindowResultMessage = null; // This seems to eliminate a mysterious crash
}
#Override
public void onDestroy() {
mFacebookLoginWebChromeClient = null;
super.onDestroy();
}
/**
* Performs fragment-specific behavior for back button, and returns true if the back press
* has been fully handled.
*/
public boolean onBackPressed() {
if (mFacebookLoginWebView.canGoBack()) {
mFacebookLoginWebView.goBack();
} else {
closeThisFragment();
}
return true;
}
private void closeThisFragment() {
EventBusHelper.post(new HideFacebookWebLoginEvent());
}
class FacebookLoginWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Only allow content from Facebook
Uri uri = Uri.parse(url);
String scheme = uri.getScheme();
if (scheme != null && (TextUtils.equals(scheme, "http") || TextUtils.equals(scheme, "https"))) {
if (UriHelper.isFacebookHost(uri)) {
return false;
}
}
return true;
}
}
private WebChromeClient makeFacebookLoginWebChromeClient() {
return new WebChromeClient() {
#Override
public void onCloseWindow(WebView window) {
closeThisFragment();
}
};
}
}
Now, the trickiest bit is making the changes necessary to your existing WebView, since it's likely you've already got a fair amount of code in place around it, and you'll need to make sense of what needs to change.
First, make sure you have JavaScript enabled, and that it supports multiple windows.
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportMultipleWindows(true);
You do not need to call setJavaScriptCanOpenWindowsAutomatically(true).
Looking at some of the other answers, you might think you need to monkey with the WebViewClient that's assigned to your WebView, and override shouldOverrideUrlLoading(). This is not necessary. What's important is the WebChromeClient, which needs to override onCreateWindow().
So... next, assign a custom WebChromeClient subclass to your WebView:
mWebView.setWebChromeClient(new WebChromeClient() {
#Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
String url = null;
Message href = view.getHandler().obtainMessage();
if (href != null) {
view.requestFocusNodeHref(href);
url = href.getData().getString("url");
}
LogHelper.d(LOGTAG, "onCreateWindow: " + url);
// Unfortunately, url is null when "Log In to Post" button is pressed
if (url == null || UriHelper.isFacebookHost(Uri.parse(url))) {
// Facebook login requires cookies to be enabled, and on more recent versions
// of Android, it's also necessary to enable acceptance of 3rd-party cookies
// on the WebView that hosts Facebook comments
CookieHelper.setAcceptThirdPartyCookies(mWebView, true);
EventBusHelper.post(new ShowFacebookWebLoginEvent(resultMsg));
} else {
LogHelper.d(LOGTAG, "Ignoring request from js to open new window for URL: " + url);
}
return true;
}
});
You'll notice this is the second call to UriHelper.isFacebookHost(). I don't have a bullet-proof approach to determining this, but here's what I do:
public static boolean isFacebookHost(Uri uri) {
if (uri != null && !TextUtils.isEmpty(uri.getHost())) {
String host = uri.getHost().toLowerCase();
return host.endsWith("facebook.com") || host.endsWith("facebook.net");
}
return false;
}
You'll also notice the call to CookieHelper.setAcceptThirdPartyCookies(). Here's the code for that:
public static void setAcceptThirdPartyCookies(WebView webView, boolean accept) {
CookieManager cookieManager = CookieManager.getInstance();
// This is a safeguard, in case you've disabled cookies elsewhere
if (accept && !cookieManager.acceptCookie()) {
cookieManager.setAcceptCookie(true);
}
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.setAcceptThirdPartyCookies(webView, accept);
}
}
One more thing that trips some people up is the configuration of "Valid OAuth redirect URIs" in the Facebook dev settings. If you see an error like this in your logs:
URL Blocked: This redirect failed because the redirect URI is not whitelisted in the app’s Client OAuth Settings. Make sure Client and Web OAuth Login are on and add all your app domains as Valid OAuth Redirect URIs.
... then you'll want to check out this answer: https://stackoverflow.com/a/37009374
Have fun! A complicated solution to what seems like a pretty simple problem. On the positive side, Android has given developers a ton of control here.
This code works for me! I have a LIKE, SHARE, Comments button on my page.
private lateinit var myWebView: WebView;
private val target_url = "https://www.webkomph.com"
private val target_url_prefix = "webkomph.com"
private var mContext: Context? = null
private var mWebviewPop: WebView? = null
private var mContainer: ConstraintLayout? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
val cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true)
myWebView = findViewById(R.id.webkom_site);
mContainer = findViewById(R.id.webview_frame);
val webkomBlue = Color.parseColor("#1c71bc")
myWebView.setBackgroundColor(webkomBlue);
myWebView.background
val webSettings = myWebView.settings;
webSettings.setJavaScriptEnabled(true);
webSettings.setAppCacheEnabled(true);
webSettings.javaScriptCanOpenWindowsAutomatically = true;
webSettings.setSupportMultipleWindows(true)
myWebView.setWebViewClient(UriWebViewClient())
myWebView.setWebChromeClient(UriChromeClient())
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.setAcceptThirdPartyCookies(myWebView, true)
}
myWebView.loadUrl(target_url);
mContext = this.applicationContext
}
override fun onBackPressed() {
if (myWebView.canGoBack()) {
myWebView.goBack()
} else {
super.onBackPressed()
}
}
inner class UriWebViewClient : WebViewClient() {
override fun shouldOverrideUrlLoading(
view: WebView,
url: String
): Boolean {
val host = Uri.parse(url).host
//Log.d("shouldOverrideUrlLoading", url);
if(host == null || host == target_url_prefix) {
// This is my web site, so do not override; let my WebView load
// the page
if (mWebviewPop != null) {
mWebviewPop!!.setVisibility(View.GONE)
mContainer?.removeView(mWebviewPop)
mWebviewPop = null
}
return false
}
if (host == "m.facebook.com" || host == "www.facebook.com" || url.contains("ret=login")) {
return false
}
// Otherwise, the link is not for a page on my site, so launch
// another Activity that handles URLs
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
return true
}
override fun onPageFinished(view: WebView?, url: String) {
// Facebook redirects to this url once a user has logged in, this is a blank page so we override this
// http://www.facebook.com/connect/connect_to_external_page_widget_loggedin.php?............
if (url.contains("plugins/close_popup.php?reload") || url.contains("dialog/close_window") || url.contains("facebook.com/dialog/plugin.optin?")) {
if(mWebviewPop != null){
mWebviewPop!!.visibility = View.GONE
mContainer!!.removeView(mWebviewPop)
mWebviewPop = null
if(myWebView != null && (url.contains("dialog/close_window") || url.contains("facebook.com/dialog/plugin.optin?"))){
myWebView?.reload();
}
}
return
}
super.onPageFinished(view, url)
}
override fun onReceivedSslError(
view: WebView, handler: SslErrorHandler,
error: SslError
) {
Log.d("onReceivedSslError", "onReceivedSslError")
//super.onReceivedSslError(view, handler, error);
}
}
inner class UriChromeClient : WebChromeClient() {
override fun onCreateWindow(
view: WebView, isDialog: Boolean,
isUserGesture: Boolean, resultMsg: Message
): Boolean {
mWebviewPop = WebView(mContext)
mWebviewPop!!.setVerticalScrollBarEnabled(false)
mWebviewPop!!.setHorizontalScrollBarEnabled(false)
mWebviewPop!!.setWebViewClient(UriWebViewClient())
mWebviewPop!!.getSettings().setJavaScriptEnabled(true)
mWebviewPop!!.getSettings().setSavePassword(false)
mWebviewPop!!.layoutParams = FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
mContainer?.addView(mWebviewPop)
val transport = resultMsg.obj as WebViewTransport
transport.webView = mWebviewPop
resultMsg.sendToTarget()
return true
}
override fun onCloseWindow(window: WebView) {
Log.d("onCloseWindow", "called")
}
}
Try moving your webview to xml layout file. The blank page error was caused due to js script fail while redirecting oAuth login to authorization acceptance page. You can overcome this issue by moving your webview into xml layout.
Probably not an always viable answer, but another option is to switch from "popup then JS" style OAuth login to the non-popup "redirect_uri" OAUth style, where it sends them to the login page, then after success/failure they get sent to "some uri err other" (ex: another page on your own website) that completes the authentication. FWIW.
FWIW where facebook says "if you're doing WebView redirect them to https://www.facebook.com/connect/login_success.html" my hunch is that that's for the case of (only having a single WebView and) using OAuth to login, so they'd add some auth params to the login_success.html that you could then glean, so not the normal web flow...
Another possibility might be to override the javascript postMessage function so you can capture what it was about to return to the parent window.

Passing and returning value from javascript to android

Dear all i am just passing and returrn some value from javascript and android. I could able to pass value javascript to android. My problem is i could not able to return the value again. This is my snippet. can any body help me out
HTML and Script
<html>
<head>
<script src="phonegap-1.3.0.js" type="text/javascript"></script>
<script type="text/javascript">
function invoke(param1,param2)
{
alert('Hai');
//invoking the JavascriptBridge registered under the 'jb' namespace
var result = jb.callMe(param1,param2);
//doing something with the return value, it should be concatenation
//of the two input parameters
alert(result);
}
</script>
</head>
<body>
<form id = "returning">
<h2>Demonstrating Android Javascript-To-Java Bridge</h2>
<input type="button" value="Invoke Bridge" onclick="invoke('Hello','World');"/>
</form>
</body>
Android:
public class ReturnAndroidValActivity extends Activity
{
private WebView webView;
public ReturnAndroidValActivity()
{
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
protected void onStart()
{
super.onStart();
}
#Override
protected void onResume()
{
try
{
super.onResume();
//render the main screen
// this.setContentView(ViewHelper.findLayoutId(this, "main"));
//Find the WebView control
//this.webView = (WebView)ViewHelper.findViewById(this, "webview");
setContentView(R.layout.main);
this.webView = (WebView)findViewById(R.id.mybrowser);
//Enable Javascript...This is needed so that Javascript is allowed to execute
//inside the WebView
WebSettings webSettings = this.webView.getSettings();
webSettings.setJavaScriptEnabled(true);
//Register the 'Javascript Bridge' class under the 'jb' namespace
//this class can be invoked from the HTML/Javascript side
this.webView.addJavascriptInterface(new JavascriptBridge(), "jb");
//Register the WebChromeClient to assist with alerts/debugging
this.webView.setWebChromeClient(new MyWebChromeClient());
//Load assets/html/index.html resource into the WebView control
this.webView.loadUrl("file:///android_asset/www/index.html");
}
catch(Exception e)
{
e.printStackTrace(System.out);
}
}
final class JavascriptBridge
{
public String callMe(String param1, String param2)
{
//Generate the returnValue from the bridge
String toastValue = param1 + "," + param2;
//Setup the Toast
Toast toast = Toast.makeText(ReturnAndroidValActivity.this, toastValue, Toast.LENGTH_LONG);
//Show the Toast
toast.show();
return toastValue;
}
}
/**
* Provides a hook for calling "alert" from javascript. Useful for
* debugging your javascript.
*/
final class MyWebChromeClient extends WebChromeClient
{
#Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result)
{
Log.d("JavascriptBridge", message);
resu lt.confirm();
return true;
}
}
}
Define one more function in the javascript:
<script>
function my_callback_function(param){
alert("Called with value: " + param);
}
</script>
Then you call this function through the WebView in the native code like that:
webView.loadUrl("javascript:my_callback_function('TheValue')");

Android WebView

With reference to this WebView tutorial, in particular this method
private void setupWebView(){
String MAP_URL = "http://gmaps-samples.googlecode.com/svn/trunk/articles-android-webmap/simple-android-map.html";
String centerURL = "javascript:centerAt(" + mostRecentLocation.getLatitude() + ","+ mostRecentLocation.getLongitude()+ ")";
webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
//Wait for the page to load then send the location information
webView.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url){
webView.loadUrl(centerURL);
}
});
webView.loadUrl(MAP_URL);
}
I've noticed that if I place the webView.loadUrl(centerURL); directly after
webView.loadUrl(MAP_URL); like this
private void setupWebView(){
String MAP_URL = "http://gmaps-samples.googlecode.com/svn/trunk/articles-android-webmap/simple-android-map.html";
String centerURL = "javascript:centerAt(" + mostRecentLocation.getLatitude() + "," + mostRecentLocation.getLongitude()+ ")";
webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
//Wait for the page to load then send the location information
webView.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url){
//DO NOTHING
}
});
webView.loadUrl(MAP_URL);
webView.loadUrl(centerURL);
}
it no longer works. So the centreAt(..) javascript method is contained int the MAP_URL.
I'm wondering if the webView.loadUrl(..) method returns before the url has actually been loaded.
It looks that way since the top method waits for it to load fully before running the javascript
Yes, webView.loadUrl() is asynchronous: it returns immediately and WebView keeps working in it's own thread.
To monitor WebView page loading use WebViewClient.onPageFinished(..):
webview.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
// do something here
}
});

Categories

Resources