summaryrefslogtreecommitdiff
path: root/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java
diff options
context:
space:
mode:
Diffstat (limited to 'StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java')
-rw-r--r--StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java739
1 files changed, 641 insertions, 98 deletions
diff --git a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java
index 8f4f3d97..b3e0e612 100644
--- a/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java
+++ b/StoneIsland/plugins/cordova-plugin-inappbrowser/src/android/InAppBrowser.java
@@ -19,12 +19,21 @@
package org.apache.cordova.inappbrowser;
import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Parcelable;
import android.provider.Browser;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.Color;
+import android.net.http.SslError;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -41,6 +50,12 @@ import android.view.inputmethod.InputMethodManager;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.HttpAuthHandler;
+import android.webkit.JavascriptInterface;
+import android.webkit.SslErrorHandler;
+import android.webkit.ValueCallback;
+import android.webkit.WebChromeClient;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebResourceResponse;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@@ -49,6 +64,7 @@ import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.TextView;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.Config;
@@ -65,6 +81,9 @@ import org.json.JSONObject;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.HashMap;
import java.util.StringTokenizer;
@@ -82,11 +101,27 @@ public class InAppBrowser extends CordovaPlugin {
private static final String LOAD_START_EVENT = "loadstart";
private static final String LOAD_STOP_EVENT = "loadstop";
private static final String LOAD_ERROR_EVENT = "loaderror";
+ private static final String MESSAGE_EVENT = "message";
private static final String CLEAR_ALL_CACHE = "clearcache";
private static final String CLEAR_SESSION_CACHE = "clearsessioncache";
private static final String HARDWARE_BACK_BUTTON = "hardwareback";
private static final String MEDIA_PLAYBACK_REQUIRES_USER_ACTION = "mediaPlaybackRequiresUserAction";
private static final String SHOULD_PAUSE = "shouldPauseOnSuspend";
+ private static final Boolean DEFAULT_HARDWARE_BACK = true;
+ private static final String USER_WIDE_VIEW_PORT = "useWideViewPort";
+ private static final String TOOLBAR_COLOR = "toolbarcolor";
+ private static final String CLOSE_BUTTON_CAPTION = "closebuttoncaption";
+ private static final String CLOSE_BUTTON_COLOR = "closebuttoncolor";
+ private static final String LEFT_TO_RIGHT = "lefttoright";
+ private static final String HIDE_NAVIGATION = "hidenavigationbuttons";
+ private static final String NAVIGATION_COLOR = "navigationbuttoncolor";
+ private static final String HIDE_URL = "hideurlbar";
+ private static final String FOOTER = "footer";
+ private static final String FOOTER_COLOR = "footercolor";
+ private static final String BEFORELOAD = "beforeload";
+ private static final String FULLSCREEN = "fullscreen";
+
+ private static final List customizableOptions = Arrays.asList(CLOSE_BUTTON_CAPTION, TOOLBAR_COLOR, NAVIGATION_COLOR, CLOSE_BUTTON_COLOR, FOOTER_COLOR);
private InAppBrowserDialog dialog;
private WebView inAppWebView;
@@ -100,6 +135,24 @@ public class InAppBrowser extends CordovaPlugin {
private boolean hadwareBackButton = true;
private boolean mediaPlaybackRequiresUserGesture = false;
private boolean shouldPauseInAppBrowser = false;
+ private boolean useWideViewPort = true;
+ private ValueCallback<Uri> mUploadCallback;
+ private ValueCallback<Uri[]> mUploadCallbackLollipop;
+ private final static int FILECHOOSER_REQUESTCODE = 1;
+ private final static int FILECHOOSER_REQUESTCODE_LOLLIPOP = 2;
+ private String closeButtonCaption = "";
+ private String closeButtonColor = "";
+ private boolean leftToRight = false;
+ private int toolbarColor = android.graphics.Color.LTGRAY;
+ private boolean hideNavigationButtons = false;
+ private String navigationButtonColor = "";
+ private boolean hideUrlBar = false;
+ private boolean showFooter = false;
+ private String footerColor = "";
+ private String beforeload = "";
+ private boolean fullscreen = true;
+ private String[] allowedSchemes;
+ private InAppBrowserClient currentClient;
/**
* Executes the request and returns PluginResult.
@@ -118,7 +171,7 @@ public class InAppBrowser extends CordovaPlugin {
t = SELF;
}
final String target = t;
- final HashMap<String, Boolean> features = parseFeature(args.optString(2));
+ final HashMap<String, String> features = parseFeature(args.optString(2));
LOG.d(LOG_TAG, "target = " + target);
@@ -207,6 +260,25 @@ public class InAppBrowser extends CordovaPlugin {
else if (action.equals("close")) {
closeDialog();
}
+ else if (action.equals("loadAfterBeforeload")) {
+ if (beforeload == null) {
+ LOG.e(LOG_TAG, "unexpected loadAfterBeforeload called without feature beforeload=yes");
+ }
+ final String url = args.getString(0);
+ this.cordova.getActivity().runOnUiThread(new Runnable() {
+ @SuppressLint("NewApi")
+ @Override
+ public void run() {
+ if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.O) {
+ currentClient.waitForBeforeload = false;
+ inAppWebView.setWebViewClient(currentClient);
+ } else {
+ ((InAppBrowserClient)inAppWebView.getWebViewClient()).waitForBeforeload = false;
+ }
+ inAppWebView.loadUrl(url);
+ }
+ });
+ }
else if (action.equals("injectScriptCode")) {
String jsWrapper = null;
if (args.getBoolean(1)) {
@@ -245,7 +317,22 @@ public class InAppBrowser extends CordovaPlugin {
this.cordova.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
- dialog.show();
+ if (dialog != null && !cordova.getActivity().isFinishing()) {
+ dialog.show();
+ }
+ }
+ });
+ PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
+ pluginResult.setKeepCallback(true);
+ this.callbackContext.sendPluginResult(pluginResult);
+ }
+ else if (action.equals("hide")) {
+ this.cordova.getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (dialog != null && !cordova.getActivity().isFinishing()) {
+ dialog.hide();
+ }
}
});
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
@@ -311,29 +398,33 @@ public class InAppBrowser extends CordovaPlugin {
* which should be executed directly.
*/
private void injectDeferredObject(String source, String jsWrapper) {
- String scriptToInject;
- if (jsWrapper != null) {
- org.json.JSONArray jsonEsc = new org.json.JSONArray();
- jsonEsc.put(source);
- String jsonRepr = jsonEsc.toString();
- String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
- scriptToInject = String.format(jsWrapper, jsonSourceString);
+ if (inAppWebView!=null) {
+ String scriptToInject;
+ if (jsWrapper != null) {
+ org.json.JSONArray jsonEsc = new org.json.JSONArray();
+ jsonEsc.put(source);
+ String jsonRepr = jsonEsc.toString();
+ String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
+ scriptToInject = String.format(jsWrapper, jsonSourceString);
+ } else {
+ scriptToInject = source;
+ }
+ final String finalScriptToInject = scriptToInject;
+ this.cordova.getActivity().runOnUiThread(new Runnable() {
+ @SuppressLint("NewApi")
+ @Override
+ public void run() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
+ // This action will have the side-effect of blurring the currently focused element
+ inAppWebView.loadUrl("javascript:" + finalScriptToInject);
+ } else {
+ inAppWebView.evaluateJavascript(finalScriptToInject, null);
+ }
+ }
+ });
} else {
- scriptToInject = source;
+ LOG.d(LOG_TAG, "Can't inject code into the system browser");
}
- final String finalScriptToInject = scriptToInject;
- this.cordova.getActivity().runOnUiThread(new Runnable() {
- @SuppressLint("NewApi")
- @Override
- public void run() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- // This action will have the side-effect of blurring the currently focused element
- inAppWebView.loadUrl("javascript:" + finalScriptToInject);
- } else {
- inAppWebView.evaluateJavascript(finalScriptToInject, null);
- }
- }
- });
}
/**
@@ -342,18 +433,21 @@ public class InAppBrowser extends CordovaPlugin {
* @param optString
* @return
*/
- private HashMap<String, Boolean> parseFeature(String optString) {
+ private HashMap<String, String> parseFeature(String optString) {
if (optString.equals(NULL)) {
return null;
} else {
- HashMap<String, Boolean> map = new HashMap<String, Boolean>();
+ HashMap<String, String> map = new HashMap<String, String>();
StringTokenizer features = new StringTokenizer(optString, ",");
StringTokenizer option;
while(features.hasMoreElements()) {
option = new StringTokenizer(features.nextToken(), "=");
if (option.hasMoreElements()) {
String key = option.nextToken();
- Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE;
+ String value = option.nextToken();
+ if (!customizableOptions.contains(key)) {
+ value = value.equals("yes") || value.equals("no") ? value : "yes";
+ }
map.put(key, value);
}
}
@@ -380,15 +474,57 @@ public class InAppBrowser extends CordovaPlugin {
intent.setData(uri);
}
intent.putExtra(Browser.EXTRA_APPLICATION_ID, cordova.getActivity().getPackageName());
- this.cordova.getActivity().startActivity(intent);
+ // CB-10795: Avoid circular loops by preventing it from opening in the current app
+ this.openExternalExcludeCurrentApp(intent);
return "";
- } catch (android.content.ActivityNotFoundException e) {
+ // not catching FileUriExposedException explicitly because buildtools<24 doesn't know about it
+ } catch (java.lang.RuntimeException e) {
LOG.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString());
return e.toString();
}
}
/**
+ * Opens the intent, providing a chooser that excludes the current app to avoid
+ * circular loops.
+ */
+ private void openExternalExcludeCurrentApp(Intent intent) {
+ String currentPackage = cordova.getActivity().getPackageName();
+ boolean hasCurrentPackage = false;
+
+ PackageManager pm = cordova.getActivity().getPackageManager();
+ List<ResolveInfo> activities = pm.queryIntentActivities(intent, 0);
+ ArrayList<Intent> targetIntents = new ArrayList<Intent>();
+
+ for (ResolveInfo ri : activities) {
+ if (!currentPackage.equals(ri.activityInfo.packageName)) {
+ Intent targetIntent = (Intent)intent.clone();
+ targetIntent.setPackage(ri.activityInfo.packageName);
+ targetIntents.add(targetIntent);
+ }
+ else {
+ hasCurrentPackage = true;
+ }
+ }
+
+ // If the current app package isn't a target for this URL, then use
+ // the normal launch behavior
+ if (hasCurrentPackage == false || targetIntents.size() == 0) {
+ this.cordova.getActivity().startActivity(intent);
+ }
+ // If there's only one possible intent, launch it directly
+ else if (targetIntents.size() == 1) {
+ this.cordova.getActivity().startActivity(targetIntents.get(0));
+ }
+ // Otherwise, show a custom chooser without the current app listed
+ else if (targetIntents.size() > 0) {
+ Intent chooser = Intent.createChooser(targetIntents.remove(targetIntents.size()-1), null);
+ chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetIntents.toArray(new Parcelable[] {}));
+ this.cordova.getActivity().startActivity(chooser);
+ }
+ }
+
+ /**
* Closes the dialog
*/
public void closeDialog() {
@@ -405,7 +541,7 @@ public class InAppBrowser extends CordovaPlugin {
childView.setWebViewClient(new WebViewClient() {
// NB: wait for about:blank before dismissing
public void onPageFinished(WebView view, String url) {
- if (dialog != null) {
+ if (dialog != null && !cordova.getActivity().isFinishing()) {
dialog.dismiss();
dialog = null;
}
@@ -488,7 +624,7 @@ public class InAppBrowser extends CordovaPlugin {
return this.showLocationBar;
}
- private InAppBrowser getInAppBrowser(){
+ private InAppBrowser getInAppBrowser() {
return this;
}
@@ -498,7 +634,7 @@ public class InAppBrowser extends CordovaPlugin {
* @param url the url to load.
* @param features jsonObject
*/
- public String showWebPage(final String url, HashMap<String, Boolean> features) {
+ public String showWebPage(final String url, HashMap<String, String> features) {
// Determine if we should hide the location bar.
showLocationBar = true;
showZoomControls = true;
@@ -506,38 +642,84 @@ public class InAppBrowser extends CordovaPlugin {
mediaPlaybackRequiresUserGesture = false;
if (features != null) {
- Boolean show = features.get(LOCATION);
+ String show = features.get(LOCATION);
if (show != null) {
- showLocationBar = show.booleanValue();
+ showLocationBar = show.equals("yes") ? true : false;
+ }
+ if(showLocationBar) {
+ String hideNavigation = features.get(HIDE_NAVIGATION);
+ String hideUrl = features.get(HIDE_URL);
+ if(hideNavigation != null) hideNavigationButtons = hideNavigation.equals("yes") ? true : false;
+ if(hideUrl != null) hideUrlBar = hideUrl.equals("yes") ? true : false;
}
- Boolean zoom = features.get(ZOOM);
+ String zoom = features.get(ZOOM);
if (zoom != null) {
- showZoomControls = zoom.booleanValue();
+ showZoomControls = zoom.equals("yes") ? true : false;
}
- Boolean hidden = features.get(HIDDEN);
+ String hidden = features.get(HIDDEN);
if (hidden != null) {
- openWindowHidden = hidden.booleanValue();
+ openWindowHidden = hidden.equals("yes") ? true : false;
}
- Boolean hardwareBack = features.get(HARDWARE_BACK_BUTTON);
+ String hardwareBack = features.get(HARDWARE_BACK_BUTTON);
if (hardwareBack != null) {
- hadwareBackButton = hardwareBack.booleanValue();
+ hadwareBackButton = hardwareBack.equals("yes") ? true : false;
+ } else {
+ hadwareBackButton = DEFAULT_HARDWARE_BACK;
}
- Boolean mediaPlayback = features.get(MEDIA_PLAYBACK_REQUIRES_USER_ACTION);
+ String mediaPlayback = features.get(MEDIA_PLAYBACK_REQUIRES_USER_ACTION);
if (mediaPlayback != null) {
- mediaPlaybackRequiresUserGesture = mediaPlayback.booleanValue();
+ mediaPlaybackRequiresUserGesture = mediaPlayback.equals("yes") ? true : false;
}
- Boolean cache = features.get(CLEAR_ALL_CACHE);
+ String cache = features.get(CLEAR_ALL_CACHE);
if (cache != null) {
- clearAllCache = cache.booleanValue();
+ clearAllCache = cache.equals("yes") ? true : false;
} else {
cache = features.get(CLEAR_SESSION_CACHE);
if (cache != null) {
- clearSessionCache = cache.booleanValue();
+ clearSessionCache = cache.equals("yes") ? true : false;
}
}
- Boolean shouldPause = features.get(SHOULD_PAUSE);
+ String shouldPause = features.get(SHOULD_PAUSE);
if (shouldPause != null) {
- shouldPauseInAppBrowser = shouldPause.booleanValue();
+ shouldPauseInAppBrowser = shouldPause.equals("yes") ? true : false;
+ }
+ String wideViewPort = features.get(USER_WIDE_VIEW_PORT);
+ if (wideViewPort != null ) {
+ useWideViewPort = wideViewPort.equals("yes") ? true : false;
+ }
+ String closeButtonCaptionSet = features.get(CLOSE_BUTTON_CAPTION);
+ if (closeButtonCaptionSet != null) {
+ closeButtonCaption = closeButtonCaptionSet;
+ }
+ String closeButtonColorSet = features.get(CLOSE_BUTTON_COLOR);
+ if (closeButtonColorSet != null) {
+ closeButtonColor = closeButtonColorSet;
+ }
+ String leftToRightSet = features.get(LEFT_TO_RIGHT);
+ leftToRight = leftToRightSet != null && leftToRightSet.equals("yes");
+
+ String toolbarColorSet = features.get(TOOLBAR_COLOR);
+ if (toolbarColorSet != null) {
+ toolbarColor = android.graphics.Color.parseColor(toolbarColorSet);
+ }
+ String navigationButtonColorSet = features.get(NAVIGATION_COLOR);
+ if (navigationButtonColorSet != null) {
+ navigationButtonColor = navigationButtonColorSet;
+ }
+ String showFooterSet = features.get(FOOTER);
+ if (showFooterSet != null) {
+ showFooter = showFooterSet.equals("yes") ? true : false;
+ }
+ String footerColorSet = features.get(FOOTER_COLOR);
+ if (footerColorSet != null) {
+ footerColor = footerColorSet;
+ }
+ if (features.get(BEFORELOAD) != null) {
+ beforeload = features.get(BEFORELOAD);
+ }
+ String fullscreenSet = features.get(FULLSCREEN);
+ if (fullscreenSet != null) {
+ fullscreen = fullscreenSet.equals("yes") ? true : false;
}
}
@@ -552,13 +734,61 @@ public class InAppBrowser extends CordovaPlugin {
*/
private int dpToPixels(int dipValue) {
int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,
- (float) dipValue,
- cordova.getActivity().getResources().getDisplayMetrics()
+ (float) dipValue,
+ cordova.getActivity().getResources().getDisplayMetrics()
);
return value;
}
+ private View createCloseButton(int id) {
+ View _close;
+ Resources activityRes = cordova.getActivity().getResources();
+
+ if (closeButtonCaption != "") {
+ // Use TextView for text
+ TextView close = new TextView(cordova.getActivity());
+ close.setText(closeButtonCaption);
+ close.setTextSize(20);
+ if (closeButtonColor != "") close.setTextColor(android.graphics.Color.parseColor(closeButtonColor));
+ close.setGravity(android.view.Gravity.CENTER_VERTICAL);
+ close.setPadding(this.dpToPixels(10), 0, this.dpToPixels(10), 0);
+ _close = close;
+ }
+ else {
+ ImageButton close = new ImageButton(cordova.getActivity());
+ int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName());
+ Drawable closeIcon = activityRes.getDrawable(closeResId);
+ if (closeButtonColor != "") close.setColorFilter(android.graphics.Color.parseColor(closeButtonColor));
+ close.setImageDrawable(closeIcon);
+ close.setScaleType(ImageView.ScaleType.FIT_CENTER);
+ if (Build.VERSION.SDK_INT >= 16)
+ close.getAdjustViewBounds();
+
+ _close = close;
+ }
+
+ RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
+ if (leftToRight) closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+ else closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+ _close.setLayoutParams(closeLayoutParams);
+
+ if (Build.VERSION.SDK_INT >= 16)
+ _close.setBackground(null);
+ else
+ _close.setBackgroundDrawable(null);
+
+ _close.setContentDescription("Close Button");
+ _close.setId(Integer.valueOf(id));
+ _close.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ closeDialog();
+ }
+ });
+
+ return _close;
+ }
+
@SuppressLint("NewApi")
public void run() {
@@ -571,6 +801,9 @@ public class InAppBrowser extends CordovaPlugin {
dialog = new InAppBrowserDialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar);
dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog;
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ if (fullscreen) {
+ dialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
dialog.setCancelable(true);
dialog.setInAppBroswer(getInAppBrowser());
@@ -581,18 +814,25 @@ public class InAppBrowser extends CordovaPlugin {
// Toolbar layout
RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());
//Please, no more black!
- toolbar.setBackgroundColor(android.graphics.Color.LTGRAY);
+ toolbar.setBackgroundColor(toolbarColor);
toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)));
toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
- toolbar.setHorizontalGravity(Gravity.LEFT);
+ if (leftToRight) {
+ toolbar.setHorizontalGravity(Gravity.LEFT);
+ } else {
+ toolbar.setHorizontalGravity(Gravity.RIGHT);
+ }
toolbar.setVerticalGravity(Gravity.TOP);
// Action Button Container layout
RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity());
- actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
+ RelativeLayout.LayoutParams actionButtonLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ if (leftToRight) actionButtonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+ else actionButtonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+ actionButtonContainer.setLayoutParams(actionButtonLayoutParams);
actionButtonContainer.setHorizontalGravity(Gravity.LEFT);
actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
- actionButtonContainer.setId(Integer.valueOf(1));
+ actionButtonContainer.setId(leftToRight ? Integer.valueOf(5) : Integer.valueOf(1));
// Back button
ImageButton back = new ImageButton(cordova.getActivity());
@@ -604,6 +844,7 @@ public class InAppBrowser extends CordovaPlugin {
Resources activityRes = cordova.getActivity().getResources();
int backResId = activityRes.getIdentifier("ic_action_previous_item", "drawable", cordova.getActivity().getPackageName());
Drawable backIcon = activityRes.getDrawable(backResId);
+ if (navigationButtonColor != "") back.setColorFilter(android.graphics.Color.parseColor(navigationButtonColor));
if (Build.VERSION.SDK_INT >= 16)
back.setBackground(null);
else
@@ -629,6 +870,7 @@ public class InAppBrowser extends CordovaPlugin {
forward.setId(Integer.valueOf(3));
int fwdResId = activityRes.getIdentifier("ic_action_next_item", "drawable", cordova.getActivity().getPackageName());
Drawable fwdIcon = activityRes.getDrawable(fwdResId);
+ if (navigationButtonColor != "") forward.setColorFilter(android.graphics.Color.parseColor(navigationButtonColor));
if (Build.VERSION.SDK_INT >= 16)
forward.setBackground(null);
else
@@ -661,53 +903,112 @@ public class InAppBrowser extends CordovaPlugin {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
- navigate(edittext.getText().toString());
- return true;
+ navigate(edittext.getText().toString());
+ return true;
}
return false;
}
});
- // Close/Done button
- ImageButton close = new ImageButton(cordova.getActivity());
- RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
- closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
- close.setLayoutParams(closeLayoutParams);
- forward.setContentDescription("Close Button");
- close.setId(Integer.valueOf(5));
- int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName());
- Drawable closeIcon = activityRes.getDrawable(closeResId);
- if (Build.VERSION.SDK_INT >= 16)
- close.setBackground(null);
- else
- close.setBackgroundDrawable(null);
- close.setImageDrawable(closeIcon);
- close.setScaleType(ImageView.ScaleType.FIT_CENTER);
- back.setPadding(0, this.dpToPixels(10), 0, this.dpToPixels(10));
- if (Build.VERSION.SDK_INT >= 16)
- close.getAdjustViewBounds();
- close.setOnClickListener(new View.OnClickListener() {
- public void onClick(View v) {
- closeDialog();
- }
- });
+ // Header Close/Done button
+ int closeButtonId = leftToRight ? 1 : 5;
+ View close = createCloseButton(closeButtonId);
+ toolbar.addView(close);
+
+ // Footer
+ RelativeLayout footer = new RelativeLayout(cordova.getActivity());
+ int _footerColor;
+ if(footerColor != "") {
+ _footerColor = Color.parseColor(footerColor);
+ } else {
+ _footerColor = android.graphics.Color.LTGRAY;
+ }
+ footer.setBackgroundColor(_footerColor);
+ RelativeLayout.LayoutParams footerLayout = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44));
+ footerLayout.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
+ footer.setLayoutParams(footerLayout);
+ if (closeButtonCaption != "") footer.setPadding(this.dpToPixels(8), this.dpToPixels(8), this.dpToPixels(8), this.dpToPixels(8));
+ footer.setHorizontalGravity(Gravity.LEFT);
+ footer.setVerticalGravity(Gravity.BOTTOM);
+
+ View footerClose = createCloseButton(7);
+ footer.addView(footerClose);
+
// WebView
inAppWebView = new WebView(cordova.getActivity());
inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
inAppWebView.setId(Integer.valueOf(6));
- inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView));
- WebViewClient client = new InAppBrowserClient(thatWebView, edittext);
- inAppWebView.setWebViewClient(client);
+ // File Chooser Implemented ChromeClient
+ inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView) {
+ // For Android 5.0+
+ public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
+ {
+ LOG.d(LOG_TAG, "File Chooser 5.0+");
+ // If callback exists, finish it.
+ if(mUploadCallbackLollipop != null) {
+ mUploadCallbackLollipop.onReceiveValue(null);
+ }
+ mUploadCallbackLollipop = filePathCallback;
+
+ // Create File Chooser Intent
+ Intent content = new Intent(Intent.ACTION_GET_CONTENT);
+ content.addCategory(Intent.CATEGORY_OPENABLE);
+ content.setType("*/*");
+
+ // Run cordova startActivityForResult
+ cordova.startActivityForResult(InAppBrowser.this, Intent.createChooser(content, "Select File"), FILECHOOSER_REQUESTCODE_LOLLIPOP);
+ return true;
+ }
+
+ // For Android 4.1+
+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
+ {
+ LOG.d(LOG_TAG, "File Chooser 4.1+");
+ // Call file chooser for Android 3.0+
+ openFileChooser(uploadMsg, acceptType);
+ }
+
+ // For Android 3.0+
+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType)
+ {
+ LOG.d(LOG_TAG, "File Chooser 3.0+");
+ mUploadCallback = uploadMsg;
+ Intent content = new Intent(Intent.ACTION_GET_CONTENT);
+ content.addCategory(Intent.CATEGORY_OPENABLE);
+
+ // run startActivityForResult
+ cordova.startActivityForResult(InAppBrowser.this, Intent.createChooser(content, "Select File"), FILECHOOSER_REQUESTCODE);
+ }
+
+ });
+ currentClient = new InAppBrowserClient(thatWebView, edittext, beforeload);
+ inAppWebView.setWebViewClient(currentClient);
WebSettings settings = inAppWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setBuiltInZoomControls(showZoomControls);
settings.setPluginState(android.webkit.WebSettings.PluginState.ON);
+ // Add postMessage interface
+ class JsObject {
+ @JavascriptInterface
+ public void postMessage(String data) {
+ try {
+ JSONObject obj = new JSONObject();
+ obj.put("type", MESSAGE_EVENT);
+ obj.put("data", new JSONObject(data));
+ sendUpdate(obj, true);
+ } catch (JSONException ex) {
+ LOG.e(LOG_TAG, "data object passed to postMessage has caused a JSON error.");
+ }
+ }
+ }
+
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
settings.setMediaPlaybackRequiresUserGesture(mediaPlaybackRequiresUserGesture);
+ inAppWebView.addJavascriptInterface(new JsObject(), "cordova_iab");
}
String overrideUserAgent = preferences.getString("OverrideUserAgent", null);
@@ -736,10 +1037,15 @@ public class InAppBrowser extends CordovaPlugin {
CookieManager.getInstance().removeSessionCookie();
}
+ // Enable Thirdparty Cookies on >=Android 5.0 device
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
+ CookieManager.getInstance().setAcceptThirdPartyCookies(inAppWebView,true);
+ }
+
inAppWebView.loadUrl(url);
inAppWebView.setId(Integer.valueOf(6));
inAppWebView.getSettings().setLoadWithOverviewMode(true);
- inAppWebView.getSettings().setUseWideViewPort(true);
+ inAppWebView.getSettings().setUseWideViewPort(useWideViewPort);
inAppWebView.requestFocus();
inAppWebView.requestFocusFromTouch();
@@ -747,10 +1053,9 @@ public class InAppBrowser extends CordovaPlugin {
actionButtonContainer.addView(back);
actionButtonContainer.addView(forward);
- // Add the views to our toolbar
- toolbar.addView(actionButtonContainer);
- toolbar.addView(edittext);
- toolbar.addView(close);
+ // Add the views to our toolbar if they haven't been disabled
+ if (!hideNavigationButtons) toolbar.addView(actionButtonContainer);
+ if (!hideUrlBar) toolbar.addView(edittext);
// Don't add the toolbar if its been disabled
if (getShowLocationBar()) {
@@ -759,19 +1064,28 @@ public class InAppBrowser extends CordovaPlugin {
}
// Add our webview to our main view/layout
- main.addView(inAppWebView);
+ RelativeLayout webViewLayout = new RelativeLayout(cordova.getActivity());
+ webViewLayout.addView(inAppWebView);
+ main.addView(webViewLayout);
+
+ // Don't add the footer unless it's been enabled
+ if (showFooter) {
+ webViewLayout.addView(footer);
+ }
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.MATCH_PARENT;
- dialog.setContentView(main);
- dialog.show();
- dialog.getWindow().setAttributes(lp);
+ if (dialog != null) {
+ dialog.setContentView(main);
+ dialog.show();
+ dialog.getWindow().setAttributes(lp);
+ }
// the goal of openhidden is to load the url and not display it
// Show() needs to be called to cause the URL to be loaded
- if(openWindowHidden) {
+ if (openWindowHidden && dialog != null) {
dialog.hide();
}
}
@@ -807,11 +1121,49 @@ public class InAppBrowser extends CordovaPlugin {
}
/**
+ * Receive File Data from File Chooser
+ *
+ * @param requestCode the requested code from chromeclient
+ * @param resultCode the result code returned from android system
+ * @param intent the data from android file chooser
+ */
+ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ // For Android >= 5.0
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ LOG.d(LOG_TAG, "onActivityResult (For Android >= 5.0)");
+ // If RequestCode or Callback is Invalid
+ if(requestCode != FILECHOOSER_REQUESTCODE_LOLLIPOP || mUploadCallbackLollipop == null) {
+ super.onActivityResult(requestCode, resultCode, intent);
+ return;
+ }
+ mUploadCallbackLollipop.onReceiveValue(WebChromeClient.FileChooserParams.parseResult(resultCode, intent));
+ mUploadCallbackLollipop = null;
+ }
+ // For Android < 5.0
+ else {
+ LOG.d(LOG_TAG, "onActivityResult (For Android < 5.0)");
+ // If RequestCode or Callback is Invalid
+ if(requestCode != FILECHOOSER_REQUESTCODE || mUploadCallback == null) {
+ super.onActivityResult(requestCode, resultCode, intent);
+ return;
+ }
+
+ if (null == mUploadCallback) return;
+ Uri result = intent == null || resultCode != cordova.getActivity().RESULT_OK ? null : intent.getData();
+
+ mUploadCallback.onReceiveValue(result);
+ mUploadCallback = null;
+ }
+ }
+
+ /**
* The webview client receives notifications about appView
*/
public class InAppBrowserClient extends WebViewClient {
EditText edittext;
CordovaWebView webView;
+ String beforeload;
+ boolean waitForBeforeload;
/**
* Constructor.
@@ -819,27 +1171,97 @@ public class InAppBrowser extends CordovaPlugin {
* @param webView
* @param mEditText
*/
- public InAppBrowserClient(CordovaWebView webView, EditText mEditText) {
+ public InAppBrowserClient(CordovaWebView webView, EditText mEditText, String beforeload) {
this.webView = webView;
this.edittext = mEditText;
+ this.beforeload = beforeload;
+ this.waitForBeforeload = beforeload != null;
}
/**
* Override the URL that should be loaded
*
- * This handles a small subset of all the URIs that would be encountered.
+ * Legacy (deprecated in API 24)
+ * For Android 6 and below.
*
* @param webView
* @param url
*/
+ @SuppressWarnings("deprecation")
@Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
+ return shouldOverrideUrlLoading(url, null);
+ }
+
+ /**
+ * Override the URL that should be loaded
+ *
+ * New (added in API 24)
+ * For Android 7 and above.
+ *
+ * @param webView
+ * @param request
+ */
+ @TargetApi(Build.VERSION_CODES.N)
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView webView, WebResourceRequest request) {
+ return shouldOverrideUrlLoading(request.getUrl().toString(), request.getMethod());
+ }
+
+ /**
+ * Override the URL that should be loaded
+ *
+ * This handles a small subset of all the URIs that would be encountered.
+ *
+ * @param url
+ * @param method
+ */
+ public boolean shouldOverrideUrlLoading(String url, String method) {
+ boolean override = false;
+ boolean useBeforeload = false;
+ String errorMessage = null;
+
+ if (beforeload.equals("yes") && method == null) {
+ useBeforeload = true;
+ } else if(beforeload.equals("yes")
+ //TODO handle POST requests then this condition can be removed:
+ && !method.equals("POST"))
+ {
+ useBeforeload = true;
+ } else if(beforeload.equals("get") && (method == null || method.equals("GET"))) {
+ useBeforeload = true;
+ } else if(beforeload.equals("post") && (method == null || method.equals("POST"))) {
+ //TODO handle POST requests
+ errorMessage = "beforeload doesn't yet support POST requests";
+ }
+
+ // On first URL change, initiate JS callback. Only after the beforeload event, continue.
+ if (useBeforeload && this.waitForBeforeload) {
+ if(sendBeforeLoad(url, method)) {
+ return true;
+ }
+ }
+
+ if(errorMessage != null) {
+ try {
+ LOG.e(LOG_TAG, errorMessage);
+ JSONObject obj = new JSONObject();
+ obj.put("type", LOAD_ERROR_EVENT);
+ obj.put("url", url);
+ obj.put("code", -1);
+ obj.put("message", errorMessage);
+ sendUpdate(obj, true, PluginResult.Status.ERROR);
+ } catch(Exception e) {
+ LOG.e(LOG_TAG, "Error sending loaderror for " + url + ": " + e.toString());
+ }
+ }
+
if (url.startsWith(WebView.SCHEME_TEL)) {
try {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(url));
cordova.getActivity().startActivity(intent);
- return true;
+ override = true;
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
}
@@ -848,7 +1270,7 @@ public class InAppBrowser extends CordovaPlugin {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
cordova.getActivity().startActivity(intent);
- return true;
+ override = true;
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString());
}
@@ -879,15 +1301,89 @@ public class InAppBrowser extends CordovaPlugin {
intent.putExtra("address", address);
intent.setType("vnd.android-dir/mms-sms");
cordova.getActivity().startActivity(intent);
- return true;
+ override = true;
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
}
}
+ // Test for whitelisted custom scheme names like mycoolapp:// or twitteroauthresponse:// (Twitter Oauth Response)
+ else if (!url.startsWith("http:") && !url.startsWith("https:") && url.matches("^[A-Za-z0-9+.-]*://.*?$")) {
+ if (allowedSchemes == null) {
+ String allowed = preferences.getString("AllowedSchemes", null);
+ if(allowed != null) {
+ allowedSchemes = allowed.split(",");
+ }
+ }
+ if (allowedSchemes != null) {
+ for (String scheme : allowedSchemes) {
+ if (url.startsWith(scheme)) {
+ try {
+ JSONObject obj = new JSONObject();
+ obj.put("type", "customscheme");
+ obj.put("url", url);
+ sendUpdate(obj, true);
+ override = true;
+ } catch (JSONException ex) {
+ LOG.e(LOG_TAG, "Custom Scheme URI passed in has caused a JSON error.");
+ }
+ }
+ }
+ }
+ }
+
+ if (useBeforeload) {
+ this.waitForBeforeload = true;
+ }
+ return override;
+ }
+
+ private boolean sendBeforeLoad(String url, String method) {
+ try {
+ JSONObject obj = new JSONObject();
+ obj.put("type", BEFORELOAD);
+ obj.put("url", url);
+ if(method != null) {
+ obj.put("method", method);
+ }
+ sendUpdate(obj, true);
+ return true;
+ } catch (JSONException ex) {
+ LOG.e(LOG_TAG, "URI passed in has caused a JSON error.");
+ }
return false;
}
+ /**
+ * Legacy (deprecated in API 21)
+ * For Android 4.4 and below.
+ * @param view
+ * @param url
+ * @return
+ */
+ @SuppressWarnings("deprecation")
+ @Override
+ public WebResourceResponse shouldInterceptRequest (final WebView view, String url) {
+ return shouldInterceptRequest(url, super.shouldInterceptRequest(view, url), null);
+ }
+
+ /**
+ * New (added in API 21)
+ * For Android 5.0 and above.
+ *
+ * @param webView
+ * @param request
+ */
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ @Override
+ public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
+ return shouldInterceptRequest(request.getUrl().toString(), super.shouldInterceptRequest(view, request), request.getMethod());
+ }
+
+ public WebResourceResponse shouldInterceptRequest(String url, WebResourceResponse response, String method) {
+ return response;
+ }
+
/*
* onPageStarted fires the LOAD_START_EVENT
*
@@ -913,7 +1409,7 @@ public class InAppBrowser extends CordovaPlugin {
// Update the UI if we haven't already
if (!newloc.equals(edittext.getText().toString())) {
edittext.setText(newloc);
- }
+ }
try {
JSONObject obj = new JSONObject();
@@ -925,11 +1421,14 @@ public class InAppBrowser extends CordovaPlugin {
}
}
-
-
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
+ // Set the namespace for postMessage()
+ if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ injectDeferredObject("window.webkit={messageHandlers:{cordova_iab:cordova_iab}}", null);
+ }
+
// CB-10395 InAppBrowser's WebView not storing cookies reliable to local device storage
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
CookieManager.getInstance().flush();
@@ -937,6 +1436,10 @@ public class InAppBrowser extends CordovaPlugin {
CookieSyncManager.getInstance().sync();
}
+ // https://issues.apache.org/jira/browse/CB-11248
+ view.clearFocus();
+ view.requestFocus();
+
try {
JSONObject obj = new JSONObject();
obj.put("type", LOAD_STOP_EVENT);
@@ -964,6 +1467,46 @@ public class InAppBrowser extends CordovaPlugin {
}
}
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+ super.onReceivedSslError(view, handler, error);
+ try {
+ JSONObject obj = new JSONObject();
+ obj.put("type", LOAD_ERROR_EVENT);
+ obj.put("url", error.getUrl());
+ obj.put("code", 0);
+ obj.put("sslerror", error.getPrimaryError());
+ String message;
+ switch (error.getPrimaryError()) {
+ case SslError.SSL_DATE_INVALID:
+ message = "The date of the certificate is invalid";
+ break;
+ case SslError.SSL_EXPIRED:
+ message = "The certificate has expired";
+ break;
+ case SslError.SSL_IDMISMATCH:
+ message = "Hostname mismatch";
+ break;
+ default:
+ case SslError.SSL_INVALID:
+ message = "A generic error occurred";
+ break;
+ case SslError.SSL_NOTYETVALID:
+ message = "The certificate is not yet valid";
+ break;
+ case SslError.SSL_UNTRUSTED:
+ message = "The certificate authority is not trusted";
+ break;
+ }
+ obj.put("message", message);
+
+ sendUpdate(obj, true, PluginResult.Status.ERROR);
+ } catch (JSONException ex) {
+ LOG.d(LOG_TAG, "Should never happen");
+ }
+ handler.cancel();
+ }
+
/**
* On received http auth request.
*/