I am using WebView to turn my responsive website into a native app. Almost everything seems to be working, but the one thing I cannot get to work is my "Login with Facebook" and "Login with Google" buttons.
Google One Tap Sign-in works fine by the way (after the WebView app has logged in to Google on a different page, i.e. Gmail), but the "Login with Facebook" and "Login with Google" buttons don’t work.
Please note that I have in fact configured my intent correctly (i.e. any returnURL or callbackURL with my domain name that would get called, would still be called through the app), but I believe these login API’s from both Google and Facebook don’t return the user to any returnURL, but rather just pass their results back to the javascript that initially opened the login popup.
The problem is the following (I think): At first, both buttons opened the link in a separate browser window (Chrome). When I complete the sign-in process (with either Google or Facebook), this browser window is closed automatically, but nothing is returned to my app (thus, my app doesn’t know the user completed the OAuth process).
So I decided to set webSettings.setSupportMultipleWindows(false). This at least fixed my problem with the Instagram API (because that also wasn’t working), and it does also make the Google and Facebook authentication windows now open within the app (so I think I’m a little closer to getting this to work), but the problem is, the return data/result from the OAuth process is still not returned to my app. In fact, the Facebook login screen manages to close my app entirely once it completes (I guess the window.close() affects my WebView app as well, or something like that). For Google on the other hand, after signing in, the page stays white. Pressing back does allow me to go back to my app’s Login screen.
Here are examples of what’s happening:
Facebook:
Google (first time, the login process works, but after completing the signing in, we end up with the same white page as below):
Google (every time after that, once you’re signed in to Google):
The javascript that handles the response in the website version of my app all works just fine! It captures the result from the OAuth call to Google or Facebook and handles the returned e-mail address and Google/Facebook ID’s, and then logs the user in to my app.
My problem is probably related to:
Android Google login not working inside WebView
But I couldn’t get that to work. If anyone has a better understanding of this WebView technique or knows how to apply the solution mentioned in that topic to my situation, any help would be greatly appreciated!
I’ve also read: Google sign in not working android webview app
And several people in that topic have stated that I have to override the popup handling, but I have no idea how to do that…
Here’s my MainActivity file:
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);
}
Please note, the Config.VARIABLES in the code above are defined in a config file. I’m using a fake useragent (one that doesn’t contain wv- or webview-) as many others have already suggested in those topics (because that would result in UserAgent Not Allowed errors with Google). Also, the OPEN_SPECIAL_URLS_IN_NEW_TAB is set to true, and as you can see from the Facebook demo, despite the setSupportMultipleWindows() being turned off, the app can still open new tabs on top of the app itself (As opposed to opening an external browser window).
Any help would be greatly appreciated!!
2
Answers
Google has blocked non-native WebView integrations from using oAuth as discussed in this thread, and changing the user agent no longer works. Your best bet here is to use the native integration.
The Facebook issue seems similar to the one discussed in this thread.
I can’t find any way to fix this error either. I chose the solution of redirecting google login link to default browser, my new solution is: