From c80e74f238d13e4776c7d47a13814082109c3576 Mon Sep 17 00:00:00 2001 From: "Ramo, Davide" <> Date: Tue, 26 Sep 2023 08:13:35 +0200 Subject: [PATCH] feat: Android - add first request headers and cookies as options --- src/android/InAppBrowser.java | 176 ++++++++++++++++++++++++++++------ 1 file changed, 148 insertions(+), 28 deletions(-) diff --git a/src/android/InAppBrowser.java b/src/android/InAppBrowser.java index eed5950..32a2d02 100644 --- a/src/android/InAppBrowser.java +++ b/src/android/InAppBrowser.java @@ -24,6 +24,8 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Handler; +import android.os.HandlerThread; import android.os.Parcelable; import android.provider.Browser; import android.content.res.Resources; @@ -35,6 +37,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.text.InputType; +import android.util.Base64; import android.util.TypedValue; import android.view.Gravity; import android.view.KeyEvent; @@ -63,6 +66,9 @@ import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import org.apache.cordova.CallbackContext; import org.apache.cordova.Config; import org.apache.cordova.CordovaArgs; @@ -80,9 +86,12 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; import java.util.HashMap; +import java.util.Map; import java.util.StringTokenizer; +import java.util.concurrent.CountDownLatch; @SuppressLint("SetJavaScriptEnabled") public class InAppBrowser extends CordovaPlugin { @@ -120,8 +129,10 @@ public class InAppBrowser extends CordovaPlugin { private static final String FULLSCREEN = "fullscreen"; private static final int TOOLBAR_HEIGHT = 48; + private static final String COOKIES = "cookies"; + private static final String HEADERS = "headers"; - private static final List customizableOptions = Arrays.asList(CLOSE_BUTTON_CAPTION, TOOLBAR_COLOR, NAVIGATION_COLOR, CLOSE_BUTTON_COLOR, FOOTER_COLOR); + private static final List customizableOptions = Arrays.asList(CLOSE_BUTTON_CAPTION, TOOLBAR_COLOR, NAVIGATION_COLOR, CLOSE_BUTTON_COLOR, FOOTER_COLOR, COOKIES, HEADERS); private InAppBrowserDialog dialog; private WebView inAppWebView; @@ -152,6 +163,12 @@ public class InAppBrowser extends CordovaPlugin { private String[] allowedSchemes; private InAppBrowserClient currentClient; + @Nullable + private Map headers; + @Nullable + private Map cookies; + + /** * Executes the request and returns PluginResult. * @@ -715,6 +732,16 @@ public class InAppBrowser extends CordovaPlugin { if (fullscreenSet != null) { fullscreen = fullscreenSet.equals("yes") ? true : false; } + + String headersSet = features.get(HEADERS); + if (headersSet != null) { + headers = deserializeMapOption(headersSet); + } + + String cookiesSet = features.get(COOKIES); + if (cookiesSet != null) { + cookies = deserializeMapOption(cookiesSet); + } } final CordovaWebView thatWebView = this.webView; @@ -948,30 +975,30 @@ public class InAppBrowser extends CordovaPlugin { settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setBuiltInZoomControls(showZoomControls); settings.setPluginState(android.webkit.WebSettings.PluginState.ON); - + // download event - + inAppWebView.setDownloadListener( - new DownloadListener(){ - public void onDownloadStart( - String url, String userAgent, String contentDisposition, String mimetype, long contentLength - ){ - try{ - JSONObject succObj = new JSONObject(); - succObj.put("type", DOWNLOAD_EVENT); - succObj.put("url",url); - succObj.put("userAgent",userAgent); - succObj.put("contentDisposition",contentDisposition); - succObj.put("mimetype",mimetype); - succObj.put("contentLength",contentLength); - sendUpdate(succObj, true); - } - catch(Exception e){ - LOG.e(LOG_TAG,e.getMessage()); + new DownloadListener(){ + public void onDownloadStart( + String url, String userAgent, String contentDisposition, String mimetype, long contentLength + ){ + try{ + JSONObject succObj = new JSONObject(); + succObj.put("type", DOWNLOAD_EVENT); + succObj.put("url",url); + succObj.put("userAgent",userAgent); + succObj.put("contentDisposition",contentDisposition); + succObj.put("mimetype",mimetype); + succObj.put("contentLength",contentLength); + sendUpdate(succObj, true); + } + catch(Exception e){ + LOG.e(LOG_TAG,e.getMessage()); + } } } - } - ); + ); // Add postMessage interface class JsObject { @@ -1011,16 +1038,10 @@ public class InAppBrowser extends CordovaPlugin { } settings.setDomStorageEnabled(true); - if (clearAllCache) { - CookieManager.getInstance().removeAllCookie(); - } else if (clearSessionCache) { - CookieManager.getInstance().removeSessionCookie(); - } - // Enable Thirdparty Cookies CookieManager.getInstance().setAcceptThirdPartyCookies(inAppWebView,true); - inAppWebView.loadUrl(url); + inAppWebView.loadUrl(url, headers); inAppWebView.setId(Integer.valueOf(6)); inAppWebView.getSettings().setLoadWithOverviewMode(true); inAppWebView.getSettings().setUseWideViewPort(useWideViewPort); @@ -1071,10 +1092,109 @@ public class InAppBrowser extends CordovaPlugin { } } }; + + setCookies(clearAllCache, clearSessionCache, cookies); + this.cordova.getActivity().runOnUiThread(runnable); return ""; } + private void setCookies(boolean clearAllCache, + boolean clearSessionCache, + @Nullable Map cookies) { + + int operations = 0; + if (clearAllCache) { + operations++; + } + if (clearSessionCache) { + operations++; + } + if (cookies != null) { + operations += cookies.size(); + } + + CountDownLatch cookiesCountdown = new CountDownLatch(operations); + + Runnable cookiesRunnable = cookiesRunnable( + cookiesCountdown, + clearAllCache, + clearSessionCache, + cookies); + + //CookieManager setCookie needs a background Looper to await completion + HandlerThread handlerThread = new HandlerThread("backgroundThread"); + if (!handlerThread.isAlive()) + handlerThread.start(); + Handler threadHandler = new Handler(handlerThread.getLooper()); + threadHandler.post(cookiesRunnable); //runs on main thread + try { + cookiesCountdown.await(); + } catch (InterruptedException e) { + LOG.e(LOG_TAG, "cookies set was interrupted by timeout.", e); + } + handlerThread.quitSafely(); + } + + @NonNull + private Runnable cookiesRunnable( + CountDownLatch cookiesCountdown, + boolean clearAllCache, + boolean clearSessionCache, + @Nullable Map cookies) { + + return () -> { + + if (clearAllCache) { + CookieManager.getInstance().removeAllCookies(value -> { + if (!value) { + LOG.e(LOG_TAG, "unable to removeAllCookies in CookieManager!"); + } + cookiesCountdown.countDown(); + }); + } else if (clearSessionCache) { + CookieManager.getInstance().removeSessionCookies(value -> { + if (!value) { + LOG.e(LOG_TAG, "unable to removeSessionCookies in CookieManager!"); + } + cookiesCountdown.countDown(); + }); + } + + //Set all cookies from options + if (cookies != null && cookies.size() > 0) { + + for (String cookieKey : cookies.keySet()) { + CookieManager.getInstance().setCookie(cookieKey, cookies.get(cookieKey), value -> { + if (!value) { + LOG.e(LOG_TAG, "unable to set the cookie in CookieManager!"); + } + cookiesCountdown.countDown(); + }); + } + } + + }; + } + + @Nullable + private Map deserializeMapOption(@NonNull String serializedMapOption) { + Map result = new HashMap<>(); + String base64 = serializedMapOption.replace("@", "="); + String json = new String(Base64.decode(base64, Base64.DEFAULT)); + try { + JSONObject jsonObject = new JSONObject(json); + for (Iterator keys = jsonObject.keys(); keys.hasNext(); ) { + String key = keys.next(); + result.put(key, jsonObject.getString(key)); + } + } catch (JSONException e) { + LOG.e(LOG_TAG, "headers options are not serialized as a valid json.", e); + return null; + } + return result; + } + /** * Create a new plugin success result and send it back to JavaScript *