Skip to content
This repository has been archived by the owner on Oct 11, 2023. It is now read-only.

Commit

Permalink
Add custom PopupBridge class from popup-bridge-android containing cra…
Browse files Browse the repository at this point in the history
…shfix: Wait for Window to Load Before Executing Javascript Callback.

Patched from v4 commit: braintree/popup-bridge-android@4bff178
Resolves: braintree/popup-bridge-android#26
  • Loading branch information
dpa99c committed Aug 6, 2021
1 parent 7ba3a8c commit dd65aba
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 1 deletion.
1 change: 1 addition & 0 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<source-file src="src/android/InAppBrowser.java" target-dir="src/org/apache/cordova/inappbrowser" />
<source-file src="src/android/InAppBrowserDialog.java" target-dir="src/org/apache/cordova/inappbrowser" />
<source-file src="src/android/InAppChromeClient.java" target-dir="src/org/apache/cordova/inappbrowser" />
<source-file src="src/android/PopupBridge.java" target-dir="src/org/apache/cordova/inappbrowser" />

<!-- drawable src/android/resources -->
<resource-file src="src/android/res/drawable-hdpi/ic_action_next_item.png" target="res/drawable-hdpi/ic_action_next_item.png" />
Expand Down
2 changes: 1 addition & 1 deletion src/android/InAppBrowser.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.util.List;
import java.util.HashMap;
import java.util.StringTokenizer;
import com.braintreepayments.popupbridge.PopupBridge;
import org.apache.cordova.inappbrowser.PopupBridge;

@SuppressLint("SetJavaScriptEnabled")
public class InAppBrowser extends CordovaPlugin {
Expand Down
218 changes: 218 additions & 0 deletions src/android/PopupBridge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package org.apache.cordova.inappbrowser;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.net.Uri;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;

import com.braintreepayments.browserswitch.BrowserSwitchFragment;
import com.braintreepayments.popupbridge.PopupBridgeMessageListener;
import com.braintreepayments.popupbridge.PopupBridgeNavigationListener;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.Set;

public class PopupBridge extends BrowserSwitchFragment {

public static final String POPUP_BRIDGE_NAME = "popupBridge";
public static final String POPUP_BRIDGE_URL_HOST = "popupbridgev1";

private static final String TAG = "com.braintreepayments.popupbridge";

private WebView mWebView;
private PopupBridgeNavigationListener mNavigationListener;
private PopupBridgeMessageListener mMessageListener;

public PopupBridge() {}

/**
* Create a new instance of {@link PopupBridge} and add it to the {@link Activity}'s {@link FragmentManager}.
*
* This will enable JavaScript in your WebView.
*
* @param activity The {@link Activity} to add the {@link Fragment} to.
* @param webView The {@link WebView} to enable for PopupBridge.
* @return {@link PopupBridge}
* @throws IllegalArgumentException If the activity is not valid or the fragment cannot be added.
*/
public static PopupBridge newInstance(Activity activity, WebView webView) throws IllegalArgumentException {
if (activity == null) {
throw new IllegalArgumentException("Activity is null");
}

if (webView == null) {
throw new IllegalArgumentException("WebView is null");
}

FragmentManager fm = activity.getFragmentManager();
PopupBridge popupBridge = (PopupBridge) fm.findFragmentByTag(TAG);
if (popupBridge == null) {
popupBridge = new PopupBridge();
Bundle bundle = new Bundle();

popupBridge.setArguments(bundle);

try {
if (VERSION.SDK_INT >= VERSION_CODES.N) {
try {
fm.beginTransaction().add(popupBridge, TAG).commitNow();
} catch (IllegalStateException | NullPointerException e) {
fm.beginTransaction().add(popupBridge, TAG).commit();
try {
fm.executePendingTransactions();
} catch (IllegalStateException ignored) {}
}
} else {
fm.beginTransaction().add(popupBridge, TAG).commit();
try {
fm.executePendingTransactions();
} catch (IllegalStateException ignored) {}
}
} catch (IllegalStateException e) {
throw new IllegalArgumentException(e.getMessage());
}
}

webView.getSettings().setJavaScriptEnabled(true);

popupBridge.mContext = activity.getApplicationContext();
popupBridge.mWebView = webView;
popupBridge.mWebView.addJavascriptInterface(popupBridge, POPUP_BRIDGE_NAME);

return popupBridge;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}

private void runJavaScriptInWebView(final String script) {
mWebView.post(new Runnable() {
@Override
public void run() {
mWebView.evaluateJavascript(script, null);
}
});
}

@Override
public void onBrowserSwitchResult(int requestCode, BrowserSwitchResult result, Uri returnUri) {
String error = null;
String payload = null;

if (result == BrowserSwitchResult.CANCELED) {
runJavaScriptInWebView(""
+ "function notifyCanceled() {"
+ " if (typeof window.popupBridge.onCancel === 'function') {"
+ " window.popupBridge.onCancel();"
+ " } else {"
+ " window.popupBridge.onComplete(null, null);"
+ " }"
+ "}"
+ ""
+ "if (document.readyState === 'complete') {"
+ " notifyCanceled();"
+ "} else {"
+ " window.addEventListener('load', function () {"
+ " notifyCanceled();"
+ " });"
+ "}");
return;
} else if (result == BrowserSwitchResult.OK) {
if (returnUri == null || !returnUri.getScheme().equals(getReturnUrlScheme()) ||
!returnUri.getHost().equals(POPUP_BRIDGE_URL_HOST)) {
return;
}

JSONObject json = new JSONObject();
JSONObject queryItems = new JSONObject();

Set<String> queryParams = returnUri.getQueryParameterNames();
if (queryParams != null && !queryParams.isEmpty()) {
for (String queryParam : queryParams) {
try {
queryItems.put(queryParam, returnUri.getQueryParameter(queryParam));
} catch (JSONException e) {
error = "new Error('Failed to parse query items from return URL. " +
e.getLocalizedMessage() + "')";
}
}
}

try {
json.put("path", returnUri.getPath());
json.put("queryItems", queryItems);
json.put("hash", returnUri.getFragment());
} catch (JSONException ignored) {}

payload = json.toString();
} else if (result == BrowserSwitchResult.ERROR) {
error = "new Error('" + result.getErrorMessage() + "')";
}

String successJavascript = String.format(""
+ "function notifyComplete() {"
+ " window.popupBridge.onComplete(%s, %s);"
+ "}"
+ ""
+ "if (document.readyState === 'complete') {"
+ " notifyComplete();"
+ "} else {"
+ " window.addEventListener('load', function () {"
+ " notifyComplete();"
+ " });"
+ "}", error, payload);

runJavaScriptInWebView(successJavascript);
}

@Override
public String getReturnUrlScheme() {
return mContext.getPackageName().toLowerCase().replace("_", "") + ".popupbridge";
}

@JavascriptInterface
public String getReturnUrlPrefix() {
return String.format("%s://%s/", getReturnUrlScheme(), POPUP_BRIDGE_URL_HOST);
}

@JavascriptInterface
public void open(String url) {
browserSwitch(1, url);

if (mNavigationListener != null) {
mNavigationListener.onUrlOpened(url);
}
}

@JavascriptInterface
public void sendMessage(String messageName) {
if (mMessageListener != null) {
mMessageListener.onMessageReceived(messageName, null);
}
}

@JavascriptInterface
public void sendMessage(String messageName, String data) {
if (mMessageListener != null) {
mMessageListener.onMessageReceived(messageName, data);
}
}

public void setNavigationListener(PopupBridgeNavigationListener listener) {
mNavigationListener = listener;
}

public void setMessageListener(PopupBridgeMessageListener listener) {
mMessageListener = listener;
}
}

0 comments on commit dd65aba

Please sign in to comment.