android - Webview App 中的 Google 和 Facebook 登录 (OAuth)(需要覆盖弹出功能吗?)
问题描述
我正在使用 WebView 将我的响应式网站变成原生应用程序。几乎一切似乎都在工作,但我无法工作的一件事是我的“使用 Facebook 登录”和“使用 Google 登录”按钮。顺便说一句,谷歌一键登录工作正常(在 WebView 应用程序在不同的页面上登录到谷歌之后,即 Gmail),但“用 Facebook 登录”和“用谷歌登录”按钮不起作用。请注意,我实际上已经正确配置了我的意图(即,任何带有我的域名的 returnURL 或 callbackURL 都会被调用,仍然会通过应用程序调用),但我相信来自 Google 和 Facebook 的这些登录 API 不会返回用户到任何 returnURL,而只是将他们的结果传递回最初打开登录弹出窗口的 javascript。
问题如下(我认为):起初,两个按钮都在单独的浏览器窗口(Chrome)中打开了链接。当我完成登录过程(使用 Google 或 Facebook)时,此浏览器窗口会自动关闭,但没有任何内容返回到我的应用程序(因此,我的应用程序不知道用户完成了 OAuth 过程)。所以我决定设置 webSettings.setSupportMultipleWindows(false)。这至少解决了我的 Instagram API 问题(因为那也不起作用),并且它也确实使 Google 和 Facebook 身份验证窗口现在在应用程序中打开(所以我认为我更接近于得到这个工作),但问题是,OAuth 过程的返回数据/结果仍未返回到我的应用程序。事实上,Facebook 登录屏幕一旦完成就会完全关闭我的应用程序(我猜是窗口。close() 也会影响我的 WebView 应用程序,或者类似的东西)。另一方面,对于 Google,在登录后,页面保持白色。按返回确实可以让我返回到我的应用程序的登录屏幕。
以下是正在发生的事情的示例:
Facebook:
谷歌(第一次,登录过程有效,但完成登录后,我们最终得到如下相同的白页):
Google(之后每次登录 Google 后):
在我的应用程序的网站版本中处理响应的 javascript 一切正常!它从对 Google 或 Facebook 的 OAuth 调用捕获结果并处理返回的电子邮件地址和 Google/Facebook ID,然后将用户登录到我的应用程序。
我的问题可能与: Android Google login not working inside WebView
但我无法让它发挥作用。如果有人对这种 WebView 技术有更好的理解,或者知道如何将该主题中提到的解决方案应用于我的情况,我们将不胜感激!
我还阅读了:Google 登录无法正常工作的 android webview 应用程序
该主题中的几个人表示我必须覆盖弹出处理,但我不知道该怎么做......
这是我的 MainActivity 文件:
webView.setWebViewClient(new MyWebViewClient() {
private Handler notificationHandler;
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String url) {
if (Config.FALLBACK_USE_LOCAL_HTML_FOLDER_IF_OFFLINE) {
loadLocal(INDEX_FILE);
} else {
webView.setVisibility(View.GONE);
offlineLayout.setVisibility(View.VISIBLE);
}
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//Basic Overriding part here (1/2)
if (url.startsWith("mailto:")) {
startActivity(new Intent(Intent.ACTION_SENDTO, Uri.parse(url)));
return true;
}
if (url.startsWith("share:") || url.contains("api.whatsapp.com")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
if (url.startsWith("whatsapp:")) {
Intent i = new Intent();
i.setPackage("com.whatsapp");
i.setAction(Intent.ACTION_SEND);
i.setType("text/plain");
startActivity(i);
return true;
}
if (url.startsWith("geo:") || url.contains("maps:")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
if (url.startsWith("market:")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
if (url.startsWith("maps.app.goo.gl")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
if (url.contains("maps.google.com")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
if (url.startsWith("intent:")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
if (url.startsWith("tel:")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
if (url.startsWith("sms:")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
if (url.startsWith("play.google.com")) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(i);
return true;
}
if (OPEN_SPECIAL_URLS_IN_NEW_TAB) {
WebView.HitTestResult result = view.getHitTestResult();
String data = result.getExtra();
Log.i(TAG, " data :" + data);
if ((data != null && data.endsWith("#")) || url.startsWith("newtab:")) {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setToolbarColor(getResources().getColor(R.color.colorPrimaryDark));
CustomTabsIntent customTabsIntent = builder.build();
String finalUrl = url;
if (url.startsWith("newtab:")) {
finalUrl = url.substring(7);
}
customTabsIntent.launchUrl(MainActivity.this, Uri.parse(finalUrl));
webView.stopLoading();
return false;
}
}
return super.shouldOverrideUrlLoading(view, url);
}
});
webView.getSettings().setSupportMultipleWindows(false);
webView.setWebChromeClient(new MyWebChromeClient() {
private Handler notificationHandler;
@Override
public void onCloseWindow(WebView window) {
super.onCloseWindow(window);
Log.i(TAG, "onCloseWindow url " + window.getUrl());
Log.i(TAG, "onCloseWindow url " + window.getOriginalUrl());
}
@Override
public boolean onCreateWindow(WebView view, boolean dialog, boolean userGesture, Message resultMsg) {
Bundle extras = getIntent().getExtras();
String URL = null;
if (extras != null) {
URL = extras.getString("ONESIGNAL_URL");
}
if (URL != null && !URL.equalsIgnoreCase("")) {
isNotificationURL = true;
deepLinkingURL = URL;
} else isNotificationURL = false;
Log.i(TAG, " LOG24 " + deepLinkingURL);
if (!OPEN_SPECIAL_URLS_IN_NEW_TAB) {
Log.i(TAG, "if ");
WebView.HitTestResult result = view.getHitTestResult();
String data = result.getExtra();
Context context = view.getContext();
if (data == null) {
Log.i(TAG, "else true ");
WebView newWebView = new WebView(view.getContext());
newWebView.setWebChromeClient(new MyWebChromeClient());
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(newWebView);
resultMsg.sendToTarget();
return true;
} else {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(data));
context.startActivity(browserIntent);
}
} else {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
builder.setToolbarColor(getResources().getColor(R.color.colorPrimaryDark));
CustomTabsIntent customTabsIntent = builder.build();
WebView.HitTestResult result = view.getHitTestResult();
String data = result.getExtra();
Log.i("TAG", " data " + data);
String url = "";
WebView newWebView = new WebView(view.getContext());
newWebView.setWebChromeClient(new WebChromeClient());
WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
transport.setWebView(newWebView);
resultMsg.sendToTarget();
}
Log.i("TAG", " running this main activity ");
return true;
}
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.i(TAG, " onJsalert");
return super.onJsAlert(view, url, message, result);
}
public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
callback.invoke(origin, true, false);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mUM = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "Upload"), FCR);
}
@SuppressLint("InlinedApi")
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
if (Config.requireStorage && Config.requireCamera) {
String[] perms = {Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA};
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED
&& ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, perms, FCR);
} else if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, FCR);
} else if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, FCR);
} else if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.RECORD_AUDIO}, FCR);
}
if (mUMA != null) {
mUMA.onReceiveValue(null);
}
mUMA = filePathCallback;
if (Arrays.asList(fileChooserParams.getAcceptTypes()).contains("audio/*")) {
Intent chooserIntent = fileChooserParams.createIntent();
startActivityForResult(chooserIntent, CODE_AUDIO_CHOOSER);
return true;
}
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {
File photoFile = null;
try {
photoFile = createImageFile();
takePictureIntent.putExtra("PhotoPath", mCM);
} catch (IOException ex) {
Log.e(TAG, "Image file creation failed", ex);
}
if (photoFile != null) {
mCM = "file:" + photoFile.getAbsolutePath();
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
FileProvider.getUriForFile(MainActivity.this, getPackageName() + ".provider", photoFile));
} else {
takePictureIntent = null;
}
}
Intent takeVideoIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
if (takeVideoIntent.resolveActivity(MainActivity.this.getPackageManager()) != null) {
File videoFile = null;
try {
videoFile = createVideoFile();
takeVideoIntent.putExtra("PhotoPath", mVM);
} catch (IOException ex) {
Log.e(TAG, "Video file creation failed", ex);
}
if (videoFile != null) {
mVM = "file:" + videoFile.getAbsolutePath();
takeVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT,
FileProvider.getUriForFile(MainActivity.this, getPackageName() + ".provider", videoFile));
} else {
takeVideoIntent = null;
}
}
Intent contentSelectionIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
contentSelectionIntent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
contentSelectionIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/* video/*");
contentSelectionIntent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/*", "video/*"});
Intent[] intentArray;
if (takePictureIntent != null && takeVideoIntent != null) {
intentArray = new Intent[]{takePictureIntent, takeVideoIntent};
} else if (takePictureIntent != null) {
intentArray = new Intent[]{takePictureIntent};
} else if (takeVideoIntent != null) {
intentArray = new Intent[]{takeVideoIntent};
} else {
intentArray = new Intent[0];
}
Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
chooserIntent.putExtra(Intent.EXTRA_TITLE, "Upload");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
startActivityForResult(chooserIntent, FCR);
}
return true;
}
});
final WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setDomStorageEnabled(true);
webSettings.setGeolocationEnabled(true);
webSettings.setBuiltInZoomControls(false);
webSettings.setSupportZoom(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
if (Config.CLEAR_CACHE_ON_STARTUP) {
webSettings.setAppCacheEnabled(false);
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
} else {
webSettings.setAppCacheEnabled(true);
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
}
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setAllowFileAccessFromFileURLs(true);
webSettings.setAllowFileAccess(true);
//webSettings.setLoadWithOverviewMode(true);
webSettings.setUseWideViewPort(true);
webSettings.setAllowContentAccess(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setDatabaseEnabled(true);
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
if (!Config.USER_AGENT.isEmpty()) {
webSettings.setUserAgentString(Config.USER_AGENT);
}
请注意,上面代码中的 Config.VARIABLES 是在配置文件中定义的。我正在使用一个假的用户代理(一个不包含 wv- 或 webview- 的用户代理),正如许多其他人已经在这些主题中建议的那样(因为这会导致 Google 出现 UserAgent Not Allowed 错误)。此外,OPEN_SPECIAL_URLS_IN_NEW_TAB 设置为 true,正如您从 Facebook 演示中看到的那样,尽管 setSupportMultipleWindows() 已关闭,但该应用程序仍然可以在应用程序本身之上打开新标签(而不是打开外部浏览器窗口)。任何帮助将不胜感激!!
解决方案
推荐阅读
- css - 如何使用引导程序设置列元素的高度?
- javascript - 如何在 Google Apps 脚本的前端 HTML 中向表单添加验证?
- flutter - Flutter Riverpod - 在构建方法中使用 read()
- flutter - Flutter - auto_route _CustomNavigatorState 错误
- php - 如何根据 WHERE 条件使用 mysqli 进行 SELECT 和 DELETE 和 RETURN 单行
- c# - 获取远程服务器返回错误:(403)禁止。使用 SSIS 将文件上传到共享点文档库时出错
- ios - 在 SwiftUI 中释放拖动时,ScrollView 正在跳跃
- javascript - 如何每 5 秒刷新一次页面并单击一个按钮?
- c# - 将数据添加到二维数组和声明的字符串
- spring - 跨多个 Azure 服务部署架构是不好的做法吗?