I am developing a cross platform app that has a HybridWebView and displays a local html file. I have a wwwroot folder that contains the html file, css files, js files and all other resources. I have built the complete folder as BundleResource. I also start a local web server with EmbedIO. When I launch the app on iPads(iOS 15>), it does not execute the JavaScript files. On iPhones(iOS 15>) the app works fine. Also on iPads with iOS 12 the app works. Also, the app works on Safari no matter what device is used.
I have already added in info.plist NSAppTransportSecurity with NSAllowsArbitraryLoads = true.Also, I have developed a Swift app with a WebView and tried to use the local web server of the Xamarin.iOS app to present the app there. But again JavaScript is not executed (I also set the preferences regarding JavaScript).
My problem:
I don't understand why the application works on Safari, iPhones and old iPads, but not on new iPads. I suspect that you have to enable JavaScript, but can't find a corresponding solution.
To mention:
I load only one js file in the index.html. This js file in turn loads other js files (This works on all devices except the new iPads, as mentioned above).
Below I have added the HybridWebView.cs, the HybridWebViewRenderer.cs, the MainPage.xaml.cs and MainPage.xaml.
MainPage.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:extensions="clr-namespace:Viewer.Extensions;assembly=Viewer"
x:Class="Viewer.MainPage">
<StackLayout>
<extensions:HybridWebView x:Name="HybridWebView" Uri="{Binding WebViewSource}" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" />
</StackLayout>
</ContentPage>
MainPage.xaml.cs:
namespace Viewer
{
[DesignTimeVisible(false)]
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MainPage : ContentPage
{
private readonly LocalWebServer _server = new LocalWebServer();
private string _webViewSource;
public string WebViewSource
{
get => _webViewSource;
set
{
_webViewSource = value;
OnPropertyChanged(nameof(WebViewSource));
}
}
public MainPage()
{
InitializeComponent();
BindingContext = this;
NavigationPage.SetHasNavigationBar(this, false);
HybridWebView.RegisterAction(data =>
{
DisplayAlert("Alert", "Hello " + data, "OK");
});
HybridWebView.RegisterQRAction(() =>
{
try
{
ZXingScannerPage scanPage = new ZXingScannerPage();
scanPage.OnScanResult += (result) =>
{
scanPage.IsScanning = false;
Device.BeginInvokeOnMainThread(async () =>
{
await Navigation.PopAsync();
var barcode = result.Text.ParseBarcode();
switch (barcode.BarcodeType)
{
case BarcodeType.Hotspot:
{
await HybridWebView.EvaluateJavaScriptAsync(
$"javascript:doLoadHS('{barcode.Datamodule}.html', '{barcode.Hotspot.figureId}', '{barcode.Hotspot.hotspotId}');");
break;
}
case BarcodeType.Datamodule:
default:
{
await HybridWebView.EvaluateJavaScriptAsync(
$"javascript:doLoad('{barcode.Datamodule}.html');");
break;
}
}
});
};
Device.BeginInvokeOnMainThread(() =>
{
Navigation.PushAsync(scanPage);
});
}
catch (Exception ex)
{
DisplayAlert("QR", $"Error while reading qr code: {ex.Message}", "OK");
}
});
HybridWebView.RegisterProjectSelectionAction(() =>
{
_server.Dispose();
Navigation.PopToRootAsync();
});
var docpath = Helper.PathAddBackslash(Path.Combine(DependencyService.Get<IApplicationConfigurationService>().DocumentationRootPath, Init.NAME_DIR_WWWROOT));
_server.StartWebServer(docpath, false, false);
WebViewSource = $"{LocalWebServer.Url}/index.html";
NavigationPage.SetHasBackButton(this, false);
}
protected override bool OnBackButtonPressed()
{
return true;
}
}
}
HybridWebView.cs:
using System;
using Xamarin.Forms;
namespace Viewer.Extensions
{
public class HybridWebView : WebView
{
Action<string> action;
Action qrAction;
Action projectSelectionAction;
public static readonly BindableProperty UriProperty = BindableProperty.Create(
propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(HybridWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
public void RegisterAction(Action<string> callback)
{
action = callback;
}
public void RegisterQRAction(Action callback)
{
qrAction = callback;
}
public void RegisterProjectSelectionAction(Action callback)
{
projectSelectionAction = callback;
}
public void Cleanup()
{
action = null;
qrAction = null;
projectSelectionAction = null;
}
public void InvokeAction(string data)
{
if (action == null || data == null)
{
return;
}
action.Invoke(data);
}
public void InvokeQRAction()
{
qrAction?.Invoke();
}
public void InvokeProjectSelectionAction()
{
projectSelectionAction?.Invoke();
}
}
}
HybridWebViewRenderer.cs:
[assembly: ExportRenderer(typeof(HybridWebView), typeof(HybridWebViewRenderer))]
namespace Viewer.iOS.Views
{
public class HybridWebViewRenderer : WkWebViewRenderer, IWKScriptMessageHandler
{
const string JavaScriptFunction = "function invokeCSharpAction(data){window.webkit.messageHandlers.invokeAction.postMessage(data);}" +
"function invokeCSharpQRAction(data){window.webkit.messageHandlers.invokeQRAction.postMessage(data);}";
WKUserContentController userController;
public HybridWebViewRenderer() : this(new WKWebViewConfiguration())
{
}
public HybridWebViewRenderer(WKWebViewConfiguration config) : base(config)
{
userController = config.UserContentController;
var script = new WKUserScript(new NSString(JavaScriptFunction), WKUserScriptInjectionTime.AtDocumentEnd, false);
userController.AddUserScript(script);
userController.AddScriptMessageHandler(this, "invokeAction");
userController.AddScriptMessageHandler(this, "invokeQRAction");
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
userController.RemoveAllUserScripts();
userController.RemoveScriptMessageHandler("invokeAction");
userController.RemoveScriptMessageHandler("invokeQRAction");
HybridWebView hybridWebView = e.OldElement as HybridWebView;
hybridWebView.Cleanup();
}
if (e.NewElement != null)
{
LoadRequest(new NSUrlRequest(new NSUrl(((HybridWebView)Element).Uri)));
}
}
public void DidReceiveScriptMessage(WKUserContentController userContentController, WKScriptMessage message)
{
var eventArgs = message.Body.ToString().ParseEventArgs();
switch (eventArgs.name)
{
case "invokeAction":
((HybridWebView)Element).InvokeAction(eventArgs.payload);
break;
case "invokeQRAction":
((HybridWebView)Element).InvokeQRAction();
break;
}
}
}
}
The problem here is probably with your Javascript.
Mobile and older devices often suppress the console errors of Javascript and instead of an error page or an exception, further Javascript is not executed and it seems as if it is not executed at all.
Especially with complex Javascript with many usings and references it is enough if there is a problem with one reference, so you don't see anything.
Here it can help to simplify the Javascript and to rebuild it bit by bit to localize the error.
I have an architecture where I am having a webview in MainActivity of my Android app. A page having a "Take Photo" button loads whenever user opens the app. On clicking "Take Photo" button, javascript will call dispatchTakePictureIntent() method defined in the Android app, which will redirect user to the Camera. Once, the user clicks the photo and submits it, I want to send the photo back to JS which in turn will make a AJAX call to the server to store the image.
I am facing trouble in developing the flow once user submits the photo. In my current implementation, I am able to store the image file locally and retrieve a file path, but I am unable to figure out how to use this file path in JS to get the file and send it on the server. Kindly help me out with this.
I have taken reference from this article: https://developer.android.com/training/camera/photobasics#TaskPath
MainActivity.java
public class MainActivity extends Activity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String FILE_PATH_PREFIX = "file:";
private String imageFilePath;
private String callbackFunction;
#SuppressLint("SetJavaScriptEnabled")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidPlatform.setUp(this, APPLICATION_NAME);
setContentView(R.layout.activity_main);
webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setGeolocationEnabled(true);
webView.getSettings().setAppCacheEnabled(true);
webView.getSettings().setDatabaseEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.getSettings().setAllowFileAccess(true);
webView.getSettings().setGeolocationDatabasePath(getFilesDir().getPath());
webView.addJavascriptInterface(this, "Android");
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient());
webView.loadUrl("URL to the page having take photo button");
}
#JavascriptInterface
public void dispatchTakePictureIntent(String _callbackFunction) {
this.callbackFunction = _callbackFunction;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
File photoFile = createImageFile();
if (photoFile != null) {
this.imageFilePath = FILE_PATH_PREFIX + photoFile.getAbsolutePath();
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
} else {
Toast.makeText(getApplicationContext(), R.string.please_retry, Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getApplicationContext(), R.string.no_camera_found, Toast.LENGTH_SHORT).show();
}
}
private File createImageFile() {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "storephoto_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = null;
try {
image = File.createTempFile(imageFileName,".jpg", storageDir);
} catch (IOException e) {
Log.e(TAG, "Exception in createImageFile:", e);
}
return image;
}
#TargetApi(Build.VERSION_CODES.KITKAT)
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_IMAGE_CAPTURE) {
if(resultCode == RESULT_OK && callbackFunction != null && imageFilePath != null) {
// What to do with imageFilePath?
//
// How to use it in Javascript to read and send image to backend server?
//
// Sample code if we send imageFilePath to JS:
// String callbackModule = String.format(TAKE_PICTURE_JS_MODULE, callbackFunction, imageFilePath);
// webView.evaluateJavascript(callbackModule, null);
}
}
}
}
Javascript
$takePhotoButton.click(function(e) {
e.preventDefault();
Android && Android.dispatchTakePictureIntent("takePhotoCallbackFn");
});
Note: Here it is necessary that user submits the photo through camera only and not via choosing/uploading an existing file.
So I load a page of a website and its show on my android APP via webview.
The website have several images on it; let's say the website page contains 3 images:
<img src="pic_mountain.jpg">
<img src="random_image.php">
<img src="pic_mountain_second.jpg">
my question is, how do I get each of the images above, so I can access each of it as Bitmap on my Java Android apps?
Here is working example to get HTML as string which is loaded in webview and how you can get image url from that after you can convert image url to bitmap.
set webview client to your webview like
webview.setWebViewClient(new WebClientClass());
The webview client is given below
public class WebClientClass extends WebViewClient {
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (progressDialog != null && progressDialog.isShowing())
progressDialog.dismiss();
wvAboutUs.evaluateJavascript(
"(function() { return ('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>'); })();",
new ValueCallback<String>() {
#Override
public void onReceiveValue(String html) {
JsonReader reader = new JsonReader(new StringReader(html));
reader.setLenient(true);
try {
if (reader.peek() != JsonToken.NULL) {
if (reader.peek() == JsonToken.STRING) {
String msg = reader.nextString();
if (msg != null) {
Log.d("HTML", msg);
Pattern p = Pattern.compile("src=\"(.*?)\"");
Matcher m = p.matcher(msg);
if (m.find()) {
Log.d("HTML", m.group(1)); // This will print your image url
}
}
}
}
} catch (IOException e) {
Log.e("TAG", "MainActivity: IOException", e);
} finally {
try {
reader.close();
} catch (IOException e) {
}
}
}
});
}
}
im trying to parse url generated by Bootstrap`s Bootpage.js which looks like
https://example.com/#page-2
but JSOUP cant parse it and showing main url.
how to get normal link from Bootpage or how to make JSOUP to parse it.
Parsing code:
Jsoup.connect("https://example.com/#page-2").followRedirects(true).get();
(See UPDATE below, first/accepted solution didn't met the android requirement, but is left for reference.)
Desktop Solution
HtmlUnit doesn't seem able to handle this site (often the case, lately). So I don't have a plain java solution either, but you could use PhantomJS: download the binary for your os, create a script file, start the process from within your java code and parse the output with a dom parser like jsoup.
Script file (here called simple.js):
var page = require('webpage').create();
var fs = require('fs');
var system = require('system');
var url = "";
var fileName = "output";
// first parameter: url
// second parameter: filename for output
console.log("args length: " + system.args.length);
if (system.args.length > 1) {
url=system.args[1];
}
if (system.args.length > 2){
fileName=system.args[2];
}
if(url===""){
phantom.exit();
}
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36';
page.settings.loadImages = false;
page.open(url, function(status) {
console.log("Status: " + status);
if(status === "success") {
var path = fileName+'.html';
fs.write(path, page.content, 'w');
}
phantom.exit();
});
Java code (example to get title and cover-url):
try {
//change path to phantomjs binary and your script file
String outputFileName = "srulad";
String phantomJSPath = "phantomjs" + File.separator + "bin" + File.separator + "phantomjs";
String scriptFile = "simple.js";
String urlParameter = "http://srulad.com/#page-2";
new File(outputFileName+".html").delete();
Process process = Runtime.getRuntime().exec(phantomJSPath + " " + scriptFile + " " + urlParameter + " " + outputFileName);
process.waitFor();
Document doc = Jsoup.parse(new File(outputFileName + ".html"),"UTF-8"); // output.html is created by phantom.js, same path as page.js
Elements elements = doc.select("#list_page-2 > div");
for (Element element : elements) {
System.out.println(element.select("div.l-description.float-left > div:nth-child(1) > a").first().attr("title"));
System.out.println(element.select("div.l-image.float-left > a > img.lazy").first().attr("data-original"));
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
Output:
სიყვარული და მოწყალება / Love & Mercy
http://srulad.com/assets/uploads/42410_Love_and_Mercy.jpg
მუზა / The Muse
http://srulad.com/assets/uploads/43164_large_qRzsimNz0eDyFLFJcbVLIxlqii.jpg
...
UPDATE
Parsing of websites with javascript based dynamic content in Android is possible using WebView and jsoup.
The following example app uses a javascript enabled WebView to render a Javascript dependent website. With a JavascriptInterface the html source is returned, parsed with jsoup and as a proof of concept the titles and the urls to the cover-images are used to populate a ListView. The buttons decrement or increment the page number triggering an update of the ListView. Note: tested on an Android 5.1.1/API 22 device.
add internet permission to your AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/page_down"
android:id="#+id/buttonDown"
android:layout_weight="0.5" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/page_up"
android:id="#+id/buttonUp"
android:layout_weight="0.5" />
</LinearLayout>
<ListView
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/listView"
android:layout_gravity="bottom"
android:layout_weight="0.5" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private final Handler uiHandler = new Handler();
private ArrayAdapter<String> adapter;
private ArrayList<String> entries = new ArrayList<>();
private ProgressDialog progressDialog;
private class JSHtmlInterface {
#android.webkit.JavascriptInterface
public void showHTML(String html) {
final String htmlContent = html;
uiHandler.post(
new Runnable() {
#Override
public void run() {
Document doc = Jsoup.parse(htmlContent);
Elements elements = doc.select("#online_movies > div > div");
entries.clear();
for (Element element : elements) {
String title = element.select("div.l-description.float-left > div:nth-child(1) > a").first().attr("title");
String imgUrl = element.select("div.l-image.float-left > a > img.lazy").first().attr("data-original");
entries.add(title + "\n" + imgUrl);
}
adapter.notifyDataSetChanged();
}
}
);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = (ListView) findViewById(R.id.listView);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1, entries);
listView.setAdapter(adapter);
progressDialog = ProgressDialog.show(this, "Loading","Please wait...", true);
progressDialog.setCancelable(false);
try {
final WebView browser = new WebView(this);
browser.setVisibility(View.INVISIBLE);
browser.setLayerType(View.LAYER_TYPE_NONE,null);
browser.getSettings().setJavaScriptEnabled(true);
browser.getSettings().setBlockNetworkImage(true);
browser.getSettings().setDomStorageEnabled(false);
browser.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
browser.getSettings().setLoadsImagesAutomatically(false);
browser.getSettings().setGeolocationEnabled(false);
browser.getSettings().setSupportZoom(false);
browser.addJavascriptInterface(new JSHtmlInterface(), "JSBridge");
browser.setWebViewClient(
new WebViewClient() {
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
progressDialog.show();
super.onPageStarted(view, url, favicon);
}
#Override
public void onPageFinished(WebView view, String url) {
browser.loadUrl("javascript:window.JSBridge.showHTML('<html>'+document.getElementsByTagName('html')[0].innerHTML+'</html>');");
progressDialog.dismiss();
}
}
);
findViewById(R.id.buttonDown).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
uiHandler.post(new Runnable() {
#Override
public void run() {
int page = Integer.parseInt(browser.getUrl().split("-")[1]);
int newPage = page > 1 ? page-1 : 1;
browser.loadUrl("http://srulad.com/#page-" + newPage);
browser.loadUrl(browser.getUrl()); // not sure why this is needed, but doesn't update without it on my device
if(getSupportActionBar()!=null) getSupportActionBar().setTitle(browser.getUrl());
}
});
}
});
findViewById(R.id.buttonUp).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
uiHandler.post(new Runnable() {
#Override
public void run() {
int page = Integer.parseInt(browser.getUrl().split("-")[1]);
int newPage = page+1;
browser.loadUrl("http://srulad.com/#page-" + newPage);
browser.loadUrl(browser.getUrl()); // not sure why this is needed, but doesn't update without it on my device
if(getSupportActionBar()!=null) getSupportActionBar().setTitle(browser.getUrl());
}
});
}
});
browser.loadUrl("http://srulad.com/#page-1");
if(getSupportActionBar()!=null) getSupportActionBar().setTitle(browser.getUrl());
} catch (Exception e) {
e.printStackTrace();
}
}
}
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.