使用 Android Webview 使 facebook 登录工作
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12648099/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Making facebook login work with an Android Webview
提问by Adrian Avendano
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.
我只是想在 Android 的 WebView 上实现 Facebook 登录。问题是在我单击 HTML 页面上的 facebook 按钮并在 Facebook 对话框中插入用户名和密码之后。url 重定向只是给了我一个黑页。
@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.
这是我的 HTML 页面上的 Facebook 常规 javascript API,单击 facebook 按钮时会调用此函数。
$("#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.
关于如何在 Facebook 对话页面重定向后获得响应的任何想法?谢谢。
回答by Ken You
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.
我在我的 android 应用程序上遇到了同样的问题。问题的原因是 FB 登录 javascript 在新窗口上打开一个新页面。然后它尝试关闭它并在登录成功后发送一些 javascript 身份验证代码。WebView 通常是“仅单个窗口”,因此它无处可返回,因此是空白屏幕。
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.
id 为“webview”的 Webview 是我的内容的主要视图。下面是我的活动代码。
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.
这个问题的关键是 onCreateWindow。创建一个新窗口并将其插入框架布局并在成功后删除。我在 shouldOverrideUrlLoading 中添加了删除。
回答by Haider
Here is an example Android project: Github: Android_Popup_Webview_handler_example
下面是一个 Android 项目示例: 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.
这是一个 Android Studio 项目,展示了如何在 Android Webview 中处理弹出窗口。大多数开源浏览器不支持打开弹出窗口。
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).
弹出窗口在许多网站(例如www.feedly.com)中使用的 OAuth 登录中尤为重要。此项目中的弹出窗口在对话框中打开,可以通过关闭按钮或按“返回”或弹出窗口自行关闭(如大多数登录身份验证流程中发生的情况)来关闭。
回答by Yulian
Regarding the best answerto this question, you just need to implement the onPageFinished method of the WebViewClient class that you use.
关于最佳答案到这个问题,你只需要实现您使用WebViewClient类的onPageFinished方法。
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 :)
它对我来说效果很好。希望它也能帮助你:)
回答by praneetloke
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.
覆盖shouldOverrideUrlLoading()
在你的WebViewClient
。在shouldOverrideUrlLoading
这里搜索。此外,还有一个参数可以传递给 facebook 的登录 API;我认为是redirect_uri。这应该可以帮助您识别登录成功的时间,并且在您的shouldOVerrideUrlLoading()
.登录成功。
回答by Savitru
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.
上面的答案太旧了,它不适用于最新的 Facebook sdk 2.7 版。在花了 4 个小时之后,我发现它几乎没有变化。以下代码适用于最新的 SDK。
Below mentioned is the XML layout file.
下面提到的是 XML 布局文件。
<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
这是 Android Activity 代码片段
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");
}
}
}
回答by Mark McClelland
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.
我的答案与这里的其他一些答案基本相似,因为我创建了第二个WebView
来托管 Facebook 登录页面,而不是尝试通过重定向来解决问题。但是,我选择将登录WebView
放在它自己的 中Fragment
,并给它自己的专用WebViewClient
和WebChromeClient
子类。我认为这可以更容易地了解每个组件所扮演的角色,以及哪些对象需要哪些设置和行为。
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.
我还利用WebChromeClient.onCloseWindow()
来检测 Facebook 的 JavaScript 何时想要关闭登录窗口。这比我最初采用的方法更强大,来自不同的答案。
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.
在您的Activity
布局中,您将拥有WebView
托管评论的 "primary"和一个用于FacebookWebLoginFragment
. 登录名Fragment
是在需要时即时创建的,然后在 Facebook 的登录 JavaScript 请求关闭其窗口时删除。
My Activity
layout looks like this:
我的Activity
布局是这样的:
<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.)
在您的 中Activity
,您需要代码来显示和隐藏 Facebook 网络登录片段。我使用 Otto 事件总线,所以我有如下所示的事件处理程序。(这里没有什么是针对这个问题的;我包含这个代码只是为了让你了解登录是如何Fragment
适应整体结构的。)
@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:
当FacebookWebLoginFragment
存在时,它应该被授予处理设备后退按钮按下的权限。这很重要,因为 Facebook 登录流程包括导航离开登录页面的能力,并且用户会期望后退按钮使他们返回登录。所以,在我的Activity
,我有这个:
@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:
的布局FacebookWebLoginFragment
非常简单:
<?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
.
这是FacebookWebLoginFragment
代码。请注意,它依赖于 的子类WebChromeClient
来检测 Facebook 登录 JavaScript 何时准备好关闭窗口(即删除片段)。另请注意,此登录名WebView
与WebView
包含评论 UI的主用户之间没有直接通信;身份验证令牌通过第三方 cookie 传递,这就是为什么您必须确保在主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.
现在,最棘手的一点是对现有的 进行必要的更改WebView
,因为您可能已经有了大量的代码,并且您需要了解需要更改的内容。
First, make sure you have JavaScript enabled, and that it supports multiple windows.
首先,确保您启用了 JavaScript,并且它支持多个窗口。
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setSupportMultipleWindows(true);
You do notneed to call setJavaScriptCanOpenWindowsAutomatically(true)
.
你不会需要调用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()
.
查看其他一些答案,您可能认为您需要使用WebViewClient
分配给您的WebView
, 并覆盖shouldOverrideUrlLoading()
。这是没有必要的。重要的是WebChromeClient
,它需要覆盖onCreateWindow()
.
So... next, assign a custom WebChromeClient
subclass to your WebView
:
所以......接下来,WebChromeClient
为您的分配一个自定义子类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:
您会注意到这是对 的第二次调用UriHelper.isFacebookHost()
。我没有防弹方法来确定这一点,但这是我所做的:
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:
您还会注意到对 的调用CookieHelper.setAcceptThirdPartyCookies()
。这是代码:
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:
另一件让一些人失望的事情是 Facebook 开发设置中“有效的 OAuth 重定向 URI”的配置。如果您在日志中看到这样的错误:
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.
URL 已阻止:此重定向失败,因为重定向 URI 未在应用的客户端 OAuth 设置中列入白名单。确保客户端和 Web OAuth 登录已打开,并将您的所有应用程序域添加为有效的 OAuth 重定向 URI。
... then you'll want to check out this answer: https://stackoverflow.com/a/37009374
...然后你会想看看这个答案: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.
玩得开心!一个看似非常简单的问题的复杂解决方案。从积极的方面来说,Android 在这里为开发人员提供了大量控制权。
回答by rogerdpack
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.
可能不是一个总是可行的答案,但另一种选择是从“popup then JS”样式的 OAuth 登录切换到非弹出的“redirect_uri”OAUth 样式,将它们发送到登录页面,然后在成功/失败后发送到完成身份验证的“一些 uri 错误其他”(例如:您自己网站上的另一个页面)。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...
FWIW 哪里 facebook 说“如果你正在做 WebView 将它们重定向到 https://www.facebook.com/connect/login_success.html”我的预感是(只有一个 WebView 和)使用 OAuth 的情况下登录,所以他们会向 login_success.html 添加一些身份验证参数,然后您可以收集这些信息,所以不是正常的网络流程......
Another possibility might be to overridethe javascript postMessage
function so you can capture what it was about toreturn to the parent window.
另一种可能性可能是覆盖javascriptpostMessage
函数,以便您可以捕获即将返回到父窗口的内容。
回答by Prakhs
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.
尝试将您的 webview 移动到 xml 布局文件。空白页错误是由于在将 oAuth 登录重定向到授权接受页面时 js 脚本失败引起的。您可以通过将 webview 移动到 xml 布局来解决这个问题。