diff options
| author | Rene Ae <aehtyb@gmail.com> | 2015-12-04 20:32:44 -0600 |
|---|---|---|
| committer | Rene Ae <aehtyb@gmail.com> | 2015-12-04 20:32:44 -0600 |
| commit | 10efb0f7b426426057fed757fe3c851a249358dd (patch) | |
| tree | b80e285251d30fbca36220c932ef180c29c55dcf /StoneIsland/platforms/android/src/org/apache | |
| parent | 015b58ff6845b5cb79b13fec109a37b4c10c7813 (diff) | |
android build
Diffstat (limited to 'StoneIsland/platforms/android/src/org/apache')
8 files changed, 2460 insertions, 0 deletions
diff --git a/StoneIsland/platforms/android/src/org/apache/cordova/device/Device.java b/StoneIsland/platforms/android/src/org/apache/cordova/device/Device.java new file mode 100755 index 00000000..5eded907 --- /dev/null +++ b/StoneIsland/platforms/android/src/org/apache/cordova/device/Device.java @@ -0,0 +1,161 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.device; + +import java.util.TimeZone; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaInterface; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.provider.Settings; + +public class Device extends CordovaPlugin { + public static final String TAG = "Device"; + + public static String platform; // Device OS + public static String uuid; // Device UUID + + private static final String ANDROID_PLATFORM = "Android"; + private static final String AMAZON_PLATFORM = "amazon-fireos"; + private static final String AMAZON_DEVICE = "Amazon"; + + /** + * Constructor. + */ + public Device() { + } + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param cordova The context of the main Activity. + * @param webView The CordovaWebView Cordova is running in. + */ + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + Device.uuid = getUuid(); + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback id used when calling back into JavaScript. + * @return True if the action was valid, false if not. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + if (action.equals("getDeviceInfo")) { + JSONObject r = new JSONObject(); + r.put("uuid", Device.uuid); + r.put("version", this.getOSVersion()); + r.put("platform", this.getPlatform()); + r.put("model", this.getModel()); + r.put("manufacturer", this.getManufacturer()); + callbackContext.success(r); + } + else { + return false; + } + return true; + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Get the OS name. + * + * @return + */ + public String getPlatform() { + String platform; + if (isAmazonDevice()) { + platform = AMAZON_PLATFORM; + } else { + platform = ANDROID_PLATFORM; + } + return platform; + } + + /** + * Get the device's Universally Unique Identifier (UUID). + * + * @return + */ + public String getUuid() { + String uuid = Settings.Secure.getString(this.cordova.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); + return uuid; + } + + public String getModel() { + String model = android.os.Build.MODEL; + return model; + } + + public String getProductName() { + String productname = android.os.Build.PRODUCT; + return productname; + } + + public String getManufacturer() { + String manufacturer = android.os.Build.MANUFACTURER; + return manufacturer; + } + /** + * Get the OS version. + * + * @return + */ + public String getOSVersion() { + String osversion = android.os.Build.VERSION.RELEASE; + return osversion; + } + + public String getSDKVersion() { + @SuppressWarnings("deprecation") + String sdkversion = android.os.Build.VERSION.SDK; + return sdkversion; + } + + public String getTimeZoneID() { + TimeZone tz = TimeZone.getDefault(); + return (tz.getID()); + } + + /** + * Function to check if the device is manufactured by Amazon + * + * @return + */ + public boolean isAmazonDevice() { + if (android.os.Build.MANUFACTURER.equals(AMAZON_DEVICE)) { + return true; + } + return false; + } + +} diff --git a/StoneIsland/platforms/android/src/org/apache/cordova/dialogs/Notification.java b/StoneIsland/platforms/android/src/org/apache/cordova/dialogs/Notification.java new file mode 100755 index 00000000..3bc3cee6 --- /dev/null +++ b/StoneIsland/platforms/android/src/org/apache/cordova/dialogs/Notification.java @@ -0,0 +1,483 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.dialogs; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.widget.EditText; +import android.widget.TextView; + + +/** + * This class provides access to notifications on the device. + * + * Be aware that this implementation gets called on + * navigator.notification.{alert|confirm|prompt}, and that there is a separate + * implementation in org.apache.cordova.CordovaChromeClient that gets + * called on a simple window.{alert|confirm|prompt}. + */ +public class Notification extends CordovaPlugin { + + public int confirmResult = -1; + public ProgressDialog spinnerDialog = null; + public ProgressDialog progressDialog = null; + + /** + * Constructor. + */ + public Notification() { + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArray of arguments for the plugin. + * @param callbackContext The callback context used when calling back into JavaScript. + * @return True when the action was valid, false otherwise. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + /* + * Don't run any of these if the current activity is finishing + * in order to avoid android.view.WindowManager$BadTokenException + * crashing the app. Just return true here since false should only + * be returned in the event of an invalid action. + */ + if(this.cordova.getActivity().isFinishing()) return true; + + if (action.equals("beep")) { + this.beep(args.getLong(0)); + } + else if (action.equals("alert")) { + this.alert(args.getString(0), args.getString(1), args.getString(2), callbackContext); + return true; + } + else if (action.equals("confirm")) { + this.confirm(args.getString(0), args.getString(1), args.getJSONArray(2), callbackContext); + return true; + } + else if (action.equals("prompt")) { + this.prompt(args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), callbackContext); + return true; + } + else if (action.equals("activityStart")) { + this.activityStart(args.getString(0), args.getString(1)); + } + else if (action.equals("activityStop")) { + this.activityStop(); + } + else if (action.equals("progressStart")) { + this.progressStart(args.getString(0), args.getString(1)); + } + else if (action.equals("progressValue")) { + this.progressValue(args.getInt(0)); + } + else if (action.equals("progressStop")) { + this.progressStop(); + } + else { + return false; + } + + // Only alert and confirm are async. + callbackContext.success(); + return true; + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Beep plays the default notification ringtone. + * + * @param count Number of times to play notification + */ + public void beep(final long count) { + cordova.getThreadPool().execute(new Runnable() { + public void run() { + Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + Ringtone notification = RingtoneManager.getRingtone(cordova.getActivity().getBaseContext(), ringtone); + + // If phone is not set to silent mode + if (notification != null) { + for (long i = 0; i < count; ++i) { + notification.play(); + long timeout = 5000; + while (notification.isPlaying() && (timeout > 0)) { + timeout = timeout - 100; + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + } + } + } + }); + } + + /** + * Builds and shows a native Android alert with given Strings + * @param message The message the alert should display + * @param title The title of the alert + * @param buttonLabel The label of the button + * @param callbackContext The callback context + */ + public synchronized void alert(final String message, final String title, final String buttonLabel, final CallbackContext callbackContext) { + final CordovaInterface cordova = this.cordova; + + Runnable runnable = new Runnable() { + public void run() { + + AlertDialog.Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(true); + dlg.setPositiveButton(buttonLabel, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0)); + } + }); + dlg.setOnCancelListener(new AlertDialog.OnCancelListener() { + public void onCancel(DialogInterface dialog) + { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0)); + } + }); + + changeTextDirection(dlg); + }; + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Builds and shows a native Android confirm dialog with given title, message, buttons. + * This dialog only shows up to 3 buttons. Any labels after that will be ignored. + * The index of the button pressed will be returned to the JavaScript callback identified by callbackId. + * + * @param message The message the dialog should display + * @param title The title of the dialog + * @param buttonLabels A comma separated list of button labels (Up to 3 buttons) + * @param callbackContext The callback context. + */ + public synchronized void confirm(final String message, final String title, final JSONArray buttonLabels, final CallbackContext callbackContext) { + final CordovaInterface cordova = this.cordova; + + Runnable runnable = new Runnable() { + public void run() { + AlertDialog.Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(true); + + // First button + if (buttonLabels.length() > 0) { + try { + dlg.setNegativeButton(buttonLabels.getString(0), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 1)); + } + }); + } catch (JSONException e) { } + } + + // Second button + if (buttonLabels.length() > 1) { + try { + dlg.setNeutralButton(buttonLabels.getString(1), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 2)); + } + }); + } catch (JSONException e) { } + } + + // Third button + if (buttonLabels.length() > 2) { + try { + dlg.setPositiveButton(buttonLabels.getString(2), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 3)); + } + }); + } catch (JSONException e) { } + } + dlg.setOnCancelListener(new AlertDialog.OnCancelListener() { + public void onCancel(DialogInterface dialog) + { + dialog.dismiss(); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0)); + } + }); + + changeTextDirection(dlg); + }; + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Builds and shows a native Android prompt dialog with given title, message, buttons. + * This dialog only shows up to 3 buttons. Any labels after that will be ignored. + * The following results are returned to the JavaScript callback identified by callbackId: + * buttonIndex Index number of the button selected + * input1 The text entered in the prompt dialog box + * + * @param message The message the dialog should display + * @param title The title of the dialog + * @param buttonLabels A comma separated list of button labels (Up to 3 buttons) + * @param callbackContext The callback context. + */ + public synchronized void prompt(final String message, final String title, final JSONArray buttonLabels, final String defaultText, final CallbackContext callbackContext) { + + final CordovaInterface cordova = this.cordova; + + Runnable runnable = new Runnable() { + public void run() { + final EditText promptInput = new EditText(cordova.getActivity()); + promptInput.setHint(defaultText); + AlertDialog.Builder dlg = createDialog(cordova); // new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(true); + + dlg.setView(promptInput); + + final JSONObject result = new JSONObject(); + + // First button + if (buttonLabels.length() > 0) { + try { + dlg.setNegativeButton(buttonLabels.getString(0), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + try { + result.put("buttonIndex",1); + result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); + } catch (JSONException e) { e.printStackTrace(); } + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); + } + }); + } catch (JSONException e) { } + } + + // Second button + if (buttonLabels.length() > 1) { + try { + dlg.setNeutralButton(buttonLabels.getString(1), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + try { + result.put("buttonIndex",2); + result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); + } catch (JSONException e) { e.printStackTrace(); } + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); + } + }); + } catch (JSONException e) { } + } + + // Third button + if (buttonLabels.length() > 2) { + try { + dlg.setPositiveButton(buttonLabels.getString(2), + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + try { + result.put("buttonIndex",3); + result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); + } catch (JSONException e) { e.printStackTrace(); } + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); + } + }); + } catch (JSONException e) { } + } + dlg.setOnCancelListener(new AlertDialog.OnCancelListener() { + public void onCancel(DialogInterface dialog){ + dialog.dismiss(); + try { + result.put("buttonIndex",0); + result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText()); + } catch (JSONException e) { e.printStackTrace(); } + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result)); + } + }); + + changeTextDirection(dlg); + }; + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Show the spinner. + * + * @param title Title of the dialog + * @param message The message of the dialog + */ + public synchronized void activityStart(final String title, final String message) { + if (this.spinnerDialog != null) { + this.spinnerDialog.dismiss(); + this.spinnerDialog = null; + } + final Notification notification = this; + final CordovaInterface cordova = this.cordova; + Runnable runnable = new Runnable() { + public void run() { + notification.spinnerDialog = createProgressDialog(cordova); // new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + notification.spinnerDialog.setTitle(title); + notification.spinnerDialog.setMessage(message); + notification.spinnerDialog.setCancelable(true); + notification.spinnerDialog.setIndeterminate(true); + notification.spinnerDialog.setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + notification.spinnerDialog = null; + } + }); + notification.spinnerDialog.show(); + } + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Stop spinner. + */ + public synchronized void activityStop() { + if (this.spinnerDialog != null) { + this.spinnerDialog.dismiss(); + this.spinnerDialog = null; + } + } + + /** + * Show the progress dialog. + * + * @param title Title of the dialog + * @param message The message of the dialog + */ + public synchronized void progressStart(final String title, final String message) { + if (this.progressDialog != null) { + this.progressDialog.dismiss(); + this.progressDialog = null; + } + final Notification notification = this; + final CordovaInterface cordova = this.cordova; + Runnable runnable = new Runnable() { + public void run() { + notification.progressDialog = createProgressDialog(cordova); // new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + notification.progressDialog.setTitle(title); + notification.progressDialog.setMessage(message); + notification.progressDialog.setCancelable(true); + notification.progressDialog.setMax(100); + notification.progressDialog.setProgress(0); + notification.progressDialog.setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + notification.progressDialog = null; + } + }); + notification.progressDialog.show(); + } + }; + this.cordova.getActivity().runOnUiThread(runnable); + } + + /** + * Set value of progress bar. + * + * @param value 0-100 + */ + public synchronized void progressValue(int value) { + if (this.progressDialog != null) { + this.progressDialog.setProgress(value); + } + } + + /** + * Stop progress dialog. + */ + public synchronized void progressStop() { + if (this.progressDialog != null) { + this.progressDialog.dismiss(); + this.progressDialog = null; + } + } + + @SuppressLint("NewApi") + private AlertDialog.Builder createDialog(CordovaInterface cordova) { + int currentapiVersion = android.os.Build.VERSION.SDK_INT; + if (currentapiVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { + return new AlertDialog.Builder(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + } else { + return new AlertDialog.Builder(cordova.getActivity()); + } + } + + @SuppressLint("InlinedApi") + private ProgressDialog createProgressDialog(CordovaInterface cordova) { + int currentapiVersion = android.os.Build.VERSION.SDK_INT; + if (currentapiVersion >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { + return new ProgressDialog(cordova.getActivity(), AlertDialog.THEME_DEVICE_DEFAULT_LIGHT); + } else { + return new ProgressDialog(cordova.getActivity()); + } + } + + @SuppressLint("NewApi") + private void changeTextDirection(Builder dlg){ + int currentapiVersion = android.os.Build.VERSION.SDK_INT; + dlg.create(); + AlertDialog dialog = dlg.show(); + if (currentapiVersion >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + TextView messageview = (TextView)dialog.findViewById(android.R.id.message); + messageview.setTextDirection(android.view.View.TEXT_DIRECTION_LOCALE); + } + } +} diff --git a/StoneIsland/platforms/android/src/org/apache/cordova/inappbrowser/InAppBrowser.java b/StoneIsland/platforms/android/src/org/apache/cordova/inappbrowser/InAppBrowser.java new file mode 100755 index 00000000..60437451 --- /dev/null +++ b/StoneIsland/platforms/android/src/org/apache/cordova/inappbrowser/InAppBrowser.java @@ -0,0 +1,869 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.inappbrowser; + +import android.annotation.SuppressLint; +import org.apache.cordova.inappbrowser.InAppBrowserDialog; +import android.content.Context; +import android.content.Intent; +import android.provider.Browser; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.text.InputType; +import android.util.Log; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.KeyEvent; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.webkit.CookieManager; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.Button; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.Config; +import org.apache.cordova.CordovaArgs; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginManager; +import org.apache.cordova.PluginResult; +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.StringTokenizer; + +@SuppressLint("SetJavaScriptEnabled") +public class InAppBrowser extends CordovaPlugin { + + private static final String NULL = "null"; + protected static final String LOG_TAG = "InAppBrowser"; + private static final String SELF = "_self"; + private static final String SYSTEM = "_system"; + private static final String EXIT_EVENT = "exit"; + private static final String LOCATION = "location"; + private static final String ZOOM = "zoom"; + private static final String HIDDEN = "hidden"; + 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 CLEAR_ALL_CACHE = "clearcache"; + private static final String CLEAR_SESSION_CACHE = "clearsessioncache"; + private static final String HARDWARE_BACK_BUTTON = "hardwareback"; + + private InAppBrowserDialog dialog; + private WebView inAppWebView; + private EditText edittext; + private CallbackContext callbackContext; + private boolean showLocationBar = true; + private boolean showZoomControls = true; + private boolean openWindowHidden = false; + private boolean clearAllCache = false; + private boolean clearSessionCache = false; + private boolean hadwareBackButton = true; + + /** + * Executes the request and returns PluginResult. + * + * @param action the action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext the callbackContext used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException { + if (action.equals("open")) { + this.callbackContext = callbackContext; + final String url = args.getString(0); + String t = args.optString(1); + if (t == null || t.equals("") || t.equals(NULL)) { + t = SELF; + } + final String target = t; + final HashMap<String, Boolean> features = parseFeature(args.optString(2)); + + Log.d(LOG_TAG, "target = " + target); + + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + String result = ""; + // SELF + if (SELF.equals(target)) { + Log.d(LOG_TAG, "in self"); + /* This code exists for compatibility between 3.x and 4.x versions of Cordova. + * Previously the Config class had a static method, isUrlWhitelisted(). That + * responsibility has been moved to the plugins, with an aggregating method in + * PluginManager. + */ + Boolean shouldAllowNavigation = null; + if (url.startsWith("javascript:")) { + shouldAllowNavigation = true; + } + if (shouldAllowNavigation == null) { + try { + Method iuw = Config.class.getMethod("isUrlWhiteListed", String.class); + shouldAllowNavigation = (Boolean)iuw.invoke(null, url); + } catch (NoSuchMethodException e) { + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } + } + if (shouldAllowNavigation == null) { + try { + Method gpm = webView.getClass().getMethod("getPluginManager"); + PluginManager pm = (PluginManager)gpm.invoke(webView); + Method san = pm.getClass().getMethod("shouldAllowNavigation", String.class); + shouldAllowNavigation = (Boolean)san.invoke(pm, url); + } catch (NoSuchMethodException e) { + } catch (IllegalAccessException e) { + } catch (InvocationTargetException e) { + } + } + // load in webview + if (Boolean.TRUE.equals(shouldAllowNavigation)) { + Log.d(LOG_TAG, "loading in webview"); + webView.loadUrl(url); + } + //Load the dialer + else if (url.startsWith(WebView.SCHEME_TEL)) + { + try { + Log.d(LOG_TAG, "loading in dialer"); + Intent intent = new Intent(Intent.ACTION_DIAL); + intent.setData(Uri.parse(url)); + cordova.getActivity().startActivity(intent); + } catch (android.content.ActivityNotFoundException e) { + LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString()); + } + } + // load in InAppBrowser + else { + Log.d(LOG_TAG, "loading in InAppBrowser"); + result = showWebPage(url, features); + } + } + // SYSTEM + else if (SYSTEM.equals(target)) { + Log.d(LOG_TAG, "in system"); + result = openExternal(url); + } + // BLANK - or anything else + else { + Log.d(LOG_TAG, "in blank"); + result = showWebPage(url, features); + } + + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result); + pluginResult.setKeepCallback(true); + callbackContext.sendPluginResult(pluginResult); + } + }); + } + else if (action.equals("close")) { + closeDialog(); + } + else if (action.equals("injectScriptCode")) { + String jsWrapper = null; + if (args.getBoolean(1)) { + jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId()); + } + injectDeferredObject(args.getString(0), jsWrapper); + } + else if (action.equals("injectScriptFile")) { + String jsWrapper; + if (args.getBoolean(1)) { + jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId()); + } else { + jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)"; + } + injectDeferredObject(args.getString(0), jsWrapper); + } + else if (action.equals("injectStyleCode")) { + String jsWrapper; + if (args.getBoolean(1)) { + jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId()); + } else { + jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)"; + } + injectDeferredObject(args.getString(0), jsWrapper); + } + else if (action.equals("injectStyleFile")) { + String jsWrapper; + if (args.getBoolean(1)) { + jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId()); + } else { + jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)"; + } + injectDeferredObject(args.getString(0), jsWrapper); + } + else if (action.equals("show")) { + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + dialog.show(); + } + }); + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK); + pluginResult.setKeepCallback(true); + this.callbackContext.sendPluginResult(pluginResult); + } + else { + return false; + } + return true; + } + + /** + * Called when the view navigates. + */ + @Override + public void onReset() { + closeDialog(); + } + + /** + * Called by AccelBroker when listener is to be shut down. + * Stop listener. + */ + public void onDestroy() { + closeDialog(); + } + + /** + * Inject an object (script or style) into the InAppBrowser WebView. + * + * This is a helper method for the inject{Script|Style}{Code|File} API calls, which + * provides a consistent method for injecting JavaScript code into the document. + * + * If a wrapper string is supplied, then the source string will be JSON-encoded (adding + * quotes) and wrapped using string formatting. (The wrapper string should have a single + * '%s' marker) + * + * @param source The source object (filename or script/style text) to inject into + * the document. + * @param jsWrapper A JavaScript string to wrap the source string in, so that the object + * is properly injected, or null if the source string is JavaScript text + * 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); + } 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); + } + } + }); + } + + /** + * Put the list of features into a hash map + * + * @param optString + * @return + */ + private HashMap<String, Boolean> parseFeature(String optString) { + if (optString.equals(NULL)) { + return null; + } else { + HashMap<String, Boolean> map = new HashMap<String, Boolean>(); + 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; + map.put(key, value); + } + } + return map; + } + } + + /** + * Display a new browser with the specified URL. + * + * @param url the url to load. + * @return "" if ok, or error message. + */ + public String openExternal(String url) { + try { + Intent intent = null; + intent = new Intent(Intent.ACTION_VIEW); + // Omitting the MIME type for file: URLs causes "No Activity found to handle Intent". + // Adding the MIME type to http: URLs causes them to not be handled by the downloader. + Uri uri = Uri.parse(url); + if ("file".equals(uri.getScheme())) { + intent.setDataAndType(uri, webView.getResourceApi().getMimeType(uri)); + } else { + intent.setData(uri); + } + intent.putExtra(Browser.EXTRA_APPLICATION_ID, cordova.getActivity().getPackageName()); + this.cordova.getActivity().startActivity(intent); + return ""; + } catch (android.content.ActivityNotFoundException e) { + Log.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString()); + return e.toString(); + } + } + + /** + * Closes the dialog + */ + public void closeDialog() { + final WebView childView = this.inAppWebView; + // The JS protects against multiple calls, so this should happen only when + // closeDialog() is called by other native code. + if (childView == null) { + return; + } + this.cordova.getActivity().runOnUiThread(new Runnable() { + @Override + public void run() { + childView.setWebViewClient(new WebViewClient() { + // NB: wait for about:blank before dismissing + public void onPageFinished(WebView view, String url) { + if (dialog != null) { + dialog.dismiss(); + } + } + }); + // NB: From SDK 19: "If you call methods on WebView from any thread + // other than your app's UI thread, it can cause unexpected results." + // http://developer.android.com/guide/webapps/migrating.html#Threads + childView.loadUrl("about:blank"); + } + }); + + try { + JSONObject obj = new JSONObject(); + obj.put("type", EXIT_EVENT); + sendUpdate(obj, false); + } catch (JSONException ex) { + Log.d(LOG_TAG, "Should never happen"); + } + } + + /** + * Checks to see if it is possible to go back one page in history, then does so. + */ + public void goBack() { + if (this.inAppWebView.canGoBack()) { + this.inAppWebView.goBack(); + } + } + + /** + * Can the web browser go back? + * @return boolean + */ + public boolean canGoBack() { + return this.inAppWebView.canGoBack(); + } + + /** + * Has the user set the hardware back button to go back + * @return boolean + */ + public boolean hardwareBack() { + return hadwareBackButton; + } + + /** + * Checks to see if it is possible to go forward one page in history, then does so. + */ + private void goForward() { + if (this.inAppWebView.canGoForward()) { + this.inAppWebView.goForward(); + } + } + + /** + * Navigate to the new page + * + * @param url to load + */ + private void navigate(String url) { + InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0); + + if (!url.startsWith("http") && !url.startsWith("file:")) { + this.inAppWebView.loadUrl("http://" + url); + } else { + this.inAppWebView.loadUrl(url); + } + this.inAppWebView.requestFocus(); + } + + + /** + * Should we show the location bar? + * + * @return boolean + */ + private boolean getShowLocationBar() { + return this.showLocationBar; + } + + private InAppBrowser getInAppBrowser(){ + return this; + } + + /** + * Display a new browser with the specified URL. + * + * @param url the url to load. + * @param features jsonObject + */ + public String showWebPage(final String url, HashMap<String, Boolean> features) { + // Determine if we should hide the location bar. + showLocationBar = true; + showZoomControls = true; + openWindowHidden = false; + if (features != null) { + Boolean show = features.get(LOCATION); + if (show != null) { + showLocationBar = show.booleanValue(); + } + Boolean zoom = features.get(ZOOM); + if (zoom != null) { + showZoomControls = zoom.booleanValue(); + } + Boolean hidden = features.get(HIDDEN); + if (hidden != null) { + openWindowHidden = hidden.booleanValue(); + } + Boolean hardwareBack = features.get(HARDWARE_BACK_BUTTON); + if (hardwareBack != null) { + hadwareBackButton = hardwareBack.booleanValue(); + } + Boolean cache = features.get(CLEAR_ALL_CACHE); + if (cache != null) { + clearAllCache = cache.booleanValue(); + } else { + cache = features.get(CLEAR_SESSION_CACHE); + if (cache != null) { + clearSessionCache = cache.booleanValue(); + } + } + } + + final CordovaWebView thatWebView = this.webView; + + // Create dialog in new thread + Runnable runnable = new Runnable() { + /** + * Convert our DIP units to Pixels + * + * @return int + */ + private int dpToPixels(int dipValue) { + int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, + (float) dipValue, + cordova.getActivity().getResources().getDisplayMetrics() + ); + + return value; + } + + @SuppressLint("NewApi") + public void run() { + // Let's create the main dialog + 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); + dialog.setCancelable(true); + dialog.setInAppBroswer(getInAppBrowser()); + + // Main container layout + LinearLayout main = new LinearLayout(cordova.getActivity()); + main.setOrientation(LinearLayout.VERTICAL); + + // Toolbar layout + RelativeLayout toolbar = new RelativeLayout(cordova.getActivity()); + //Please, no more black! + toolbar.setBackgroundColor(android.graphics.Color.LTGRAY); + 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); + 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)); + actionButtonContainer.setHorizontalGravity(Gravity.LEFT); + actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL); + actionButtonContainer.setId(1); + + // Back button + Button back = new Button(cordova.getActivity()); + RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT); + back.setLayoutParams(backLayoutParams); + back.setContentDescription("Back Button"); + back.setId(2); + Resources activityRes = cordova.getActivity().getResources(); + int backResId = activityRes.getIdentifier("ic_action_previous_item", "drawable", cordova.getActivity().getPackageName()); + Drawable backIcon = activityRes.getDrawable(backResId); + if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) + { + back.setBackgroundDrawable(backIcon); + } + else + { + back.setBackground(backIcon); + } + back.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + goBack(); + } + }); + + // Forward button + Button forward = new Button(cordova.getActivity()); + RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2); + forward.setLayoutParams(forwardLayoutParams); + forward.setContentDescription("Forward Button"); + forward.setId(3); + int fwdResId = activityRes.getIdentifier("ic_action_next_item", "drawable", cordova.getActivity().getPackageName()); + Drawable fwdIcon = activityRes.getDrawable(fwdResId); + if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) + { + forward.setBackgroundDrawable(fwdIcon); + } + else + { + forward.setBackground(fwdIcon); + } + forward.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + goForward(); + } + }); + + // Edit Text Box + edittext = new EditText(cordova.getActivity()); + RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1); + textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5); + edittext.setLayoutParams(textLayoutParams); + edittext.setId(4); + edittext.setSingleLine(true); + edittext.setText(url); + edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI); + edittext.setImeOptions(EditorInfo.IME_ACTION_GO); + edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE + edittext.setOnKeyListener(new View.OnKeyListener() { + 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; + } + return false; + } + }); + + // Close/Done button + Button close = new Button(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(5); + int closeResId = activityRes.getIdentifier("ic_action_remove", "drawable", cordova.getActivity().getPackageName()); + Drawable closeIcon = activityRes.getDrawable(closeResId); + if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) + { + close.setBackgroundDrawable(closeIcon); + } + else + { + close.setBackground(closeIcon); + } + close.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + closeDialog(); + } + }); + + // WebView + inAppWebView = new WebView(cordova.getActivity()); + inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); + inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView)); + WebViewClient client = new InAppBrowserClient(thatWebView, edittext); + inAppWebView.setWebViewClient(client); + WebSettings settings = inAppWebView.getSettings(); + settings.setJavaScriptEnabled(true); + settings.setJavaScriptCanOpenWindowsAutomatically(true); + settings.setBuiltInZoomControls(showZoomControls); + settings.setPluginState(android.webkit.WebSettings.PluginState.ON); + + //Toggle whether this is enabled or not! + Bundle appSettings = cordova.getActivity().getIntent().getExtras(); + boolean enableDatabase = appSettings == null ? true : appSettings.getBoolean("InAppBrowserStorageEnabled", true); + if (enableDatabase) { + String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath(); + settings.setDatabasePath(databasePath); + settings.setDatabaseEnabled(true); + } + settings.setDomStorageEnabled(true); + + if (clearAllCache) { + CookieManager.getInstance().removeAllCookie(); + } else if (clearSessionCache) { + CookieManager.getInstance().removeSessionCookie(); + } + + inAppWebView.loadUrl(url); + inAppWebView.setId(6); + inAppWebView.getSettings().setLoadWithOverviewMode(true); + inAppWebView.getSettings().setUseWideViewPort(true); + inAppWebView.requestFocus(); + inAppWebView.requestFocusFromTouch(); + + // Add the back and forward buttons to our action button container layout + actionButtonContainer.addView(back); + actionButtonContainer.addView(forward); + + // Add the views to our toolbar + toolbar.addView(actionButtonContainer); + toolbar.addView(edittext); + toolbar.addView(close); + + // Don't add the toolbar if its been disabled + if (getShowLocationBar()) { + // Add our toolbar to our main view/layout + main.addView(toolbar); + } + + // Add our webview to our main view/layout + main.addView(inAppWebView); + + 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); + // 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) { + dialog.hide(); + } + } + }; + this.cordova.getActivity().runOnUiThread(runnable); + return ""; + } + + /** + * Create a new plugin success result and send it back to JavaScript + * + * @param obj a JSONObject contain event payload information + */ + private void sendUpdate(JSONObject obj, boolean keepCallback) { + sendUpdate(obj, keepCallback, PluginResult.Status.OK); + } + + /** + * Create a new plugin result and send it back to JavaScript + * + * @param obj a JSONObject contain event payload information + * @param status the status code to return to the JavaScript environment + */ + private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) { + if (callbackContext != null) { + PluginResult result = new PluginResult(status, obj); + result.setKeepCallback(keepCallback); + callbackContext.sendPluginResult(result); + if (!keepCallback) { + callbackContext = null; + } + } + } + + /** + * The webview client receives notifications about appView + */ + public class InAppBrowserClient extends WebViewClient { + EditText edittext; + CordovaWebView webView; + + /** + * Constructor. + * + * @param webView + * @param mEditText + */ + public InAppBrowserClient(CordovaWebView webView, EditText mEditText) { + this.webView = webView; + this.edittext = mEditText; + } + + /** + * Notify the host application that a page has started loading. + * + * @param view The webview initiating the callback. + * @param url The url of the page. + */ + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + String newloc = ""; + if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) { + newloc = url; + } + // If dialing phone (tel:5551212) + else if (url.startsWith(WebView.SCHEME_TEL)) { + try { + Intent intent = new Intent(Intent.ACTION_DIAL); + intent.setData(Uri.parse(url)); + cordova.getActivity().startActivity(intent); + } catch (android.content.ActivityNotFoundException e) { + LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString()); + } + } + + else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:")) { + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + cordova.getActivity().startActivity(intent); + } catch (android.content.ActivityNotFoundException e) { + LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString()); + } + } + // If sms:5551212?body=This is the message + else if (url.startsWith("sms:")) { + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + + // Get address + String address = null; + int parmIndex = url.indexOf('?'); + if (parmIndex == -1) { + address = url.substring(4); + } + else { + address = url.substring(4, parmIndex); + + // If body, then set sms body + Uri uri = Uri.parse(url); + String query = uri.getQuery(); + if (query != null) { + if (query.startsWith("body=")) { + intent.putExtra("sms_body", query.substring(5)); + } + } + } + intent.setData(Uri.parse("sms:" + address)); + intent.putExtra("address", address); + intent.setType("vnd.android-dir/mms-sms"); + cordova.getActivity().startActivity(intent); + } catch (android.content.ActivityNotFoundException e) { + LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString()); + } + } + else { + newloc = "http://" + url; + } + + if (!newloc.equals(edittext.getText().toString())) { + edittext.setText(newloc); + } + + try { + JSONObject obj = new JSONObject(); + obj.put("type", LOAD_START_EVENT); + obj.put("url", newloc); + + sendUpdate(obj, true); + } catch (JSONException ex) { + Log.d(LOG_TAG, "Should never happen"); + } + } + + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + + try { + JSONObject obj = new JSONObject(); + obj.put("type", LOAD_STOP_EVENT); + obj.put("url", url); + + sendUpdate(obj, true); + } catch (JSONException ex) { + Log.d(LOG_TAG, "Should never happen"); + } + } + + public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { + super.onReceivedError(view, errorCode, description, failingUrl); + + try { + JSONObject obj = new JSONObject(); + obj.put("type", LOAD_ERROR_EVENT); + obj.put("url", failingUrl); + obj.put("code", errorCode); + obj.put("message", description); + + sendUpdate(obj, true, PluginResult.Status.ERROR); + } catch (JSONException ex) { + Log.d(LOG_TAG, "Should never happen"); + } + } + } +} + diff --git a/StoneIsland/platforms/android/src/org/apache/cordova/inappbrowser/InAppBrowserDialog.java b/StoneIsland/platforms/android/src/org/apache/cordova/inappbrowser/InAppBrowserDialog.java new file mode 100755 index 00000000..d7017202 --- /dev/null +++ b/StoneIsland/platforms/android/src/org/apache/cordova/inappbrowser/InAppBrowserDialog.java @@ -0,0 +1,58 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.inappbrowser; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.content.Context; +import android.util.Log; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * Created by Oliver on 22/11/2013. + */ +public class InAppBrowserDialog extends Dialog { + Context context; + InAppBrowser inAppBrowser = null; + + public InAppBrowserDialog(Context context, int theme) { + super(context, theme); + this.context = context; + } + + public void setInAppBroswer(InAppBrowser browser) { + this.inAppBrowser = browser; + } + + public void onBackPressed () { + if (this.inAppBrowser == null) { + this.dismiss(); + } else { + // better to go through the in inAppBrowser + // because it does a clean up + if (this.inAppBrowser.hardwareBack() && this.inAppBrowser.canGoBack()) { + this.inAppBrowser.goBack(); + } else { + this.inAppBrowser.closeDialog(); + } + } + } +} diff --git a/StoneIsland/platforms/android/src/org/apache/cordova/inappbrowser/InAppChromeClient.java b/StoneIsland/platforms/android/src/org/apache/cordova/inappbrowser/InAppChromeClient.java new file mode 100755 index 00000000..a2145e6a --- /dev/null +++ b/StoneIsland/platforms/android/src/org/apache/cordova/inappbrowser/InAppChromeClient.java @@ -0,0 +1,133 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.inappbrowser; + +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; + +import android.webkit.JsPromptResult; +import android.webkit.WebChromeClient; +import android.webkit.WebStorage; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.webkit.GeolocationPermissions.Callback; + +public class InAppChromeClient extends WebChromeClient { + + private CordovaWebView webView; + private String LOG_TAG = "InAppChromeClient"; + private long MAX_QUOTA = 100 * 1024 * 1024; + + public InAppChromeClient(CordovaWebView webView) { + super(); + this.webView = webView; + } + /** + * Handle database quota exceeded notification. + * + * @param url + * @param databaseIdentifier + * @param currentQuota + * @param estimatedSize + * @param totalUsedQuota + * @param quotaUpdater + */ + @Override + public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize, + long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) + { + LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota); + quotaUpdater.updateQuota(MAX_QUOTA); + } + + /** + * Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin. + * + * @param origin + * @param callback + */ + @Override + public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) { + super.onGeolocationPermissionsShowPrompt(origin, callback); + callback.invoke(origin, true, false); + } + + /** + * Tell the client to display a prompt dialog to the user. + * If the client returns true, WebView will assume that the client will + * handle the prompt dialog and call the appropriate JsPromptResult method. + * + * The prompt bridge provided for the InAppBrowser is capable of executing any + * oustanding callback belonging to the InAppBrowser plugin. Care has been + * taken that other callbacks cannot be triggered, and that no other code + * execution is possible. + * + * To trigger the bridge, the prompt default value should be of the form: + * + * gap-iab://<callbackId> + * + * where <callbackId> is the string id of the callback to trigger (something + * like "InAppBrowser0123456789") + * + * If present, the prompt message is expected to be a JSON-encoded value to + * pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid. + * + * @param view + * @param url + * @param message + * @param defaultValue + * @param result + */ + @Override + public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { + // See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute. + if (defaultValue != null && defaultValue.startsWith("gap")) { + if(defaultValue.startsWith("gap-iab://")) { + PluginResult scriptResult; + String scriptCallbackId = defaultValue.substring(10); + if (scriptCallbackId.startsWith("InAppBrowser")) { + if(message == null || message.length() == 0) { + scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray()); + } else { + try { + scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray(message)); + } catch(JSONException e) { + scriptResult = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage()); + } + } + this.webView.sendPluginResult(scriptResult, scriptCallbackId); + result.confirm(""); + return true; + } + } + else + { + // Anything else with a gap: prefix should get this message + LOG.w(LOG_TAG, "InAppBrowser does not support Cordova API calls: " + url + " " + defaultValue); + result.cancel(); + return true; + } + } + return false; + } + +} diff --git a/StoneIsland/platforms/android/src/org/apache/cordova/networkinformation/NetworkManager.java b/StoneIsland/platforms/android/src/org/apache/cordova/networkinformation/NetworkManager.java new file mode 100755 index 00000000..4c85ddab --- /dev/null +++ b/StoneIsland/platforms/android/src/org/apache/cordova/networkinformation/NetworkManager.java @@ -0,0 +1,267 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ +package org.apache.cordova.networkinformation; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.PluginResult; +import org.apache.cordova.CordovaWebView; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; +import android.util.Log; + +public class NetworkManager extends CordovaPlugin { + + public static int NOT_REACHABLE = 0; + public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1; + public static int REACHABLE_VIA_WIFI_NETWORK = 2; + + public static final String WIFI = "wifi"; + public static final String WIMAX = "wimax"; + // mobile + public static final String MOBILE = "mobile"; + + // Android L calls this Cellular, because I have no idea! + public static final String CELLULAR = "cellular"; + // 2G network types + public static final String GSM = "gsm"; + public static final String GPRS = "gprs"; + public static final String EDGE = "edge"; + // 3G network types + public static final String CDMA = "cdma"; + public static final String UMTS = "umts"; + public static final String HSPA = "hspa"; + public static final String HSUPA = "hsupa"; + public static final String HSDPA = "hsdpa"; + public static final String ONEXRTT = "1xrtt"; + public static final String EHRPD = "ehrpd"; + // 4G network types + public static final String LTE = "lte"; + public static final String UMB = "umb"; + public static final String HSPA_PLUS = "hspa+"; + // return type + public static final String TYPE_UNKNOWN = "unknown"; + public static final String TYPE_ETHERNET = "ethernet"; + public static final String TYPE_WIFI = "wifi"; + public static final String TYPE_2G = "2g"; + public static final String TYPE_3G = "3g"; + public static final String TYPE_4G = "4g"; + public static final String TYPE_NONE = "none"; + + private static final String LOG_TAG = "NetworkManager"; + + private CallbackContext connectionCallbackContext; + + ConnectivityManager sockMan; + BroadcastReceiver receiver; + private JSONObject lastInfo = null; + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param cordova The context of the main Activity. + * @param webView The CordovaWebView Cordova is running in. + */ + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + this.sockMan = (ConnectivityManager) cordova.getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); + this.connectionCallbackContext = null; + + // We need to listen to connectivity events to update navigator.connection + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + if (this.receiver == null) { + this.receiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // (The null check is for the ARM Emulator, please use Intel Emulator for better results) + if(NetworkManager.this.webView != null) + updateConnectionInfo(sockMan.getActiveNetworkInfo()); + } + }; + webView.getContext().registerReceiver(this.receiver, intentFilter); + } + + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackContext The callback id used when calling back into JavaScript. + * @return True if the action was valid, false otherwise. + */ + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) { + if (action.equals("getConnectionInfo")) { + this.connectionCallbackContext = callbackContext; + NetworkInfo info = sockMan.getActiveNetworkInfo(); + String connectionType = ""; + try { + connectionType = this.getConnectionInfo(info).get("type").toString(); + } catch (JSONException e) { } + + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, connectionType); + pluginResult.setKeepCallback(true); + callbackContext.sendPluginResult(pluginResult); + return true; + } + return false; + } + + /** + * Stop network receiver. + */ + public void onDestroy() { + if (this.receiver != null) { + try { + webView.getContext().unregisterReceiver(this.receiver); + } catch (Exception e) { + Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e); + } finally { + receiver = null; + } + } + } + + //-------------------------------------------------------------------------- + // LOCAL METHODS + //-------------------------------------------------------------------------- + + /** + * Updates the JavaScript side whenever the connection changes + * + * @param info the current active network info + * @return + */ + private void updateConnectionInfo(NetworkInfo info) { + // send update to javascript "navigator.network.connection" + // Jellybean sends its own info + JSONObject thisInfo = this.getConnectionInfo(info); + if(!thisInfo.equals(lastInfo)) + { + String connectionType = ""; + try { + connectionType = thisInfo.get("type").toString(); + } catch (JSONException e) { } + + sendUpdate(connectionType); + lastInfo = thisInfo; + } + } + + /** + * Get the latest network connection information + * + * @param info the current active network info + * @return a JSONObject that represents the network info + */ + private JSONObject getConnectionInfo(NetworkInfo info) { + String type = TYPE_NONE; + String extraInfo = ""; + if (info != null) { + // If we are not connected to any network set type to none + if (!info.isConnected()) { + type = TYPE_NONE; + } + else { + type = getType(info); + } + extraInfo = info.getExtraInfo(); + } + + Log.d("CordovaNetworkManager", "Connection Type: " + type); + Log.d("CordovaNetworkManager", "Connection Extra Info: " + extraInfo); + + JSONObject connectionInfo = new JSONObject(); + + try { + connectionInfo.put("type", type); + connectionInfo.put("extraInfo", extraInfo); + } catch (JSONException e) { } + + return connectionInfo; + } + + /** + * Create a new plugin result and send it back to JavaScript + * + * @param connection the network info to set as navigator.connection + */ + private void sendUpdate(String type) { + if (connectionCallbackContext != null) { + PluginResult result = new PluginResult(PluginResult.Status.OK, type); + result.setKeepCallback(true); + connectionCallbackContext.sendPluginResult(result); + } + webView.postMessage("networkconnection", type); + } + + /** + * Determine the type of connection + * + * @param info the network info so we can determine connection type. + * @return the type of mobile network we are on + */ + private String getType(NetworkInfo info) { + if (info != null) { + String type = info.getTypeName(); + + if (type.toLowerCase().equals(WIFI)) { + return TYPE_WIFI; + } + else if (type.toLowerCase().equals(MOBILE) || type.toLowerCase().equals(CELLULAR)) { + type = info.getSubtypeName(); + if (type.toLowerCase().equals(GSM) || + type.toLowerCase().equals(GPRS) || + type.toLowerCase().equals(EDGE)) { + return TYPE_2G; + } + else if (type.toLowerCase().startsWith(CDMA) || + type.toLowerCase().equals(UMTS) || + type.toLowerCase().equals(ONEXRTT) || + type.toLowerCase().equals(EHRPD) || + type.toLowerCase().equals(HSUPA) || + type.toLowerCase().equals(HSDPA) || + type.toLowerCase().equals(HSPA)) { + return TYPE_3G; + } + else if (type.toLowerCase().equals(LTE) || + type.toLowerCase().equals(UMB) || + type.toLowerCase().equals(HSPA_PLUS)) { + return TYPE_4G; + } + } + } + else { + return TYPE_NONE; + } + return TYPE_UNKNOWN; + } +} diff --git a/StoneIsland/platforms/android/src/org/apache/cordova/splashscreen/SplashScreen.java b/StoneIsland/platforms/android/src/org/apache/cordova/splashscreen/SplashScreen.java new file mode 100755 index 00000000..75ad724c --- /dev/null +++ b/StoneIsland/platforms/android/src/org/apache/cordova/splashscreen/SplashScreen.java @@ -0,0 +1,328 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +package org.apache.cordova.splashscreen; + +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Configuration; +import android.graphics.Color; +import android.os.Handler; +import android.view.Display; +import android.view.View; +import android.view.ViewGroup.LayoutParams; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.json.JSONArray; +import org.json.JSONException; + +public class SplashScreen extends CordovaPlugin { + private static final String LOG_TAG = "SplashScreen"; + // Cordova 3.x.x has a copy of this plugin bundled with it (SplashScreenInternal.java). + // Enable functionality only if running on 4.x.x. + private static final boolean HAS_BUILT_IN_SPLASH_SCREEN = Integer.valueOf(CordovaWebView.CORDOVA_VERSION.split("\\.")[0]) < 4; + private static Dialog splashDialog; + private static ProgressDialog spinnerDialog; + private static boolean firstShow = true; + + /** + * Displays the splash drawable. + */ + private ImageView splashImageView; + + /** + * Remember last device orientation to detect orientation changes. + */ + private int orientation; + + // Helper to be compile-time compatible with both Cordova 3.x and 4.x. + private View getView() { + try { + return (View)webView.getClass().getMethod("getView").invoke(webView); + } catch (Exception e) { + return (View)webView; + } + } + + @Override + protected void pluginInitialize() { + if (HAS_BUILT_IN_SPLASH_SCREEN || !firstShow) { + return; + } + // Make WebView invisible while loading URL + getView().setVisibility(View.INVISIBLE); + int drawableId = preferences.getInteger("SplashDrawableId", 0); + if (drawableId == 0) { + String splashResource = preferences.getString("SplashScreen", "screen"); + if (splashResource != null) { + drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getClass().getPackage().getName()); + if (drawableId == 0) { + drawableId = cordova.getActivity().getResources().getIdentifier(splashResource, "drawable", cordova.getActivity().getPackageName()); + } + preferences.set("SplashDrawableId", drawableId); + } + } + + // Save initial orientation. + orientation = cordova.getActivity().getResources().getConfiguration().orientation; + + firstShow = false; + loadSpinner(); + showSplashScreen(true); + } + + /** + * Shorter way to check value of "SplashMaintainAspectRatio" preference. + */ + private boolean isMaintainAspectRatio () { + return preferences.getBoolean("SplashMaintainAspectRatio", false); + } + + @Override + public void onPause(boolean multitasking) { + if (HAS_BUILT_IN_SPLASH_SCREEN) { + return; + } + // hide the splash screen to avoid leaking a window + this.removeSplashScreen(); + } + + @Override + public void onDestroy() { + if (HAS_BUILT_IN_SPLASH_SCREEN) { + return; + } + // hide the splash screen to avoid leaking a window + this.removeSplashScreen(); + // If we set this to true onDestroy, we lose track when we go from page to page! + //firstShow = true; + } + + @Override + public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { + if (action.equals("hide")) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.postMessage("splashscreen", "hide"); + } + }); + } else if (action.equals("show")) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.postMessage("splashscreen", "show"); + } + }); + } else if (action.equals("spinnerStart")) { + if (!HAS_BUILT_IN_SPLASH_SCREEN) { + final String title = args.getString(0); + final String message = args.getString(1); + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + spinnerStart(title, message); + } + }); + } + } else { + return false; + } + + callbackContext.success(); + return true; + } + + @Override + public Object onMessage(String id, Object data) { + if (HAS_BUILT_IN_SPLASH_SCREEN) { + return null; + } + if ("splashscreen".equals(id)) { + if ("hide".equals(data.toString())) { + this.removeSplashScreen(); + } else { + this.showSplashScreen(false); + } + } else if ("spinner".equals(id)) { + if ("stop".equals(data.toString())) { + this.spinnerStop(); + getView().setVisibility(View.VISIBLE); + } + } else if ("onReceivedError".equals(id)) { + spinnerStop(); + } + return null; + } + + // Don't add @Override so that plugin still compiles on 3.x.x for a while + public void onConfigurationChanged(Configuration newConfig) { + if (newConfig.orientation != orientation) { + orientation = newConfig.orientation; + + // Splash drawable may change with orientation, so reload it. + if (splashImageView != null) { + int drawableId = preferences.getInteger("SplashDrawableId", 0); + if (drawableId != 0) { + splashImageView.setImageDrawable(cordova.getActivity().getResources().getDrawable(drawableId)); + } + } + } + } + + private void removeSplashScreen() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + if (splashDialog != null && splashDialog.isShowing()) { + splashDialog.dismiss(); + splashDialog = null; + splashImageView = null; + } + } + }); + } + + /** + * Shows the splash screen over the full Activity + */ + @SuppressWarnings("deprecation") + private void showSplashScreen(final boolean hideAfterDelay) { + final int splashscreenTime = preferences.getInteger("SplashScreenDelay", 3000); + final int drawableId = preferences.getInteger("SplashDrawableId", 0); + + // If the splash dialog is showing don't try to show it again + if (splashDialog != null && splashDialog.isShowing()) { + return; + } + if (drawableId == 0 || (splashscreenTime <= 0 && hideAfterDelay)) { + return; + } + + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + // Get reference to display + Display display = cordova.getActivity().getWindowManager().getDefaultDisplay(); + Context context = webView.getContext(); + + // Use an ImageView to render the image because of its flexible scaling options. + splashImageView = new ImageView(context); + splashImageView.setImageResource(drawableId); + LayoutParams layoutParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + splashImageView.setLayoutParams(layoutParams); + + splashImageView.setMinimumHeight(display.getHeight()); + splashImageView.setMinimumWidth(display.getWidth()); + + // TODO: Use the background color of the webView's parent instead of using the preference. + splashImageView.setBackgroundColor(preferences.getInteger("backgroundColor", Color.BLACK)); + + if (isMaintainAspectRatio()) { + // CENTER_CROP scale mode is equivalent to CSS "background-size:cover" + splashImageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + } + else { + // FIT_XY scales image non-uniformly to fit into image view. + splashImageView.setScaleType(ImageView.ScaleType.FIT_XY); + } + + // Create and show the dialog + splashDialog = new Dialog(context, android.R.style.Theme_Translucent_NoTitleBar); + // check to see if the splash screen should be full screen + if ((cordova.getActivity().getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) + == WindowManager.LayoutParams.FLAG_FULLSCREEN) { + splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + splashDialog.setContentView(splashImageView); + splashDialog.setCancelable(false); + splashDialog.show(); + + // Set Runnable to remove splash screen just in case + if (hideAfterDelay) { + final Handler handler = new Handler(); + handler.postDelayed(new Runnable() { + public void run() { + removeSplashScreen(); + } + }, splashscreenTime); + } + } + }); + } + + /* + * Load the spinner + */ + private void loadSpinner() { + // If loadingDialog property, then show the App loading dialog for first page of app + String loading = null; + if (webView.canGoBack()) { + loading = preferences.getString("LoadingDialog", null); + } + else { + loading = preferences.getString("LoadingPageDialog", null); + } + if (loading != null) { + String title = ""; + String message = "Loading Application..."; + + if (loading.length() > 0) { + int comma = loading.indexOf(','); + if (comma > 0) { + title = loading.substring(0, comma); + message = loading.substring(comma + 1); + } + else { + title = ""; + message = loading; + } + } + spinnerStart(title, message); + } + } + + private void spinnerStart(final String title, final String message) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + spinnerStop(); + spinnerDialog = ProgressDialog.show(webView.getContext(), title, message, true, true, + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + spinnerDialog = null; + } + }); + } + }); + } + + private void spinnerStop() { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + if (spinnerDialog != null && spinnerDialog.isShowing()) { + spinnerDialog.dismiss(); + spinnerDialog = null; + } + } + }); + } +} diff --git a/StoneIsland/platforms/android/src/org/apache/cordova/whitelist/WhitelistPlugin.java b/StoneIsland/platforms/android/src/org/apache/cordova/whitelist/WhitelistPlugin.java new file mode 100755 index 00000000..4e4f57e1 --- /dev/null +++ b/StoneIsland/platforms/android/src/org/apache/cordova/whitelist/WhitelistPlugin.java @@ -0,0 +1,161 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +*/ + +package org.apache.cordova.whitelist; + +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.ConfigXmlParser; +import org.apache.cordova.Whitelist; +import org.xmlpull.v1.XmlPullParser; + +import android.content.Context; +import android.util.Log; + +public class WhitelistPlugin extends CordovaPlugin { + private static final String LOG_TAG = "WhitelistPlugin"; + private Whitelist allowedNavigations; + private Whitelist allowedIntents; + private Whitelist allowedRequests; + + // Used when instantiated via reflection by PluginManager + public WhitelistPlugin() { + } + // These can be used by embedders to allow Java-configuration of whitelists. + public WhitelistPlugin(Context context) { + this(new Whitelist(), new Whitelist(), null); + new CustomConfigXmlParser().parse(context); + } + public WhitelistPlugin(XmlPullParser xmlParser) { + this(new Whitelist(), new Whitelist(), null); + new CustomConfigXmlParser().parse(xmlParser); + } + public WhitelistPlugin(Whitelist allowedNavigations, Whitelist allowedIntents, Whitelist allowedRequests) { + if (allowedRequests == null) { + allowedRequests = new Whitelist(); + allowedRequests.addWhiteListEntry("file:///*", false); + allowedRequests.addWhiteListEntry("data:*", false); + } + this.allowedNavigations = allowedNavigations; + this.allowedIntents = allowedIntents; + this.allowedRequests = allowedRequests; + } + @Override + public void pluginInitialize() { + if (allowedNavigations == null) { + allowedNavigations = new Whitelist(); + allowedIntents = new Whitelist(); + allowedRequests = new Whitelist(); + new CustomConfigXmlParser().parse(webView.getContext()); + } + } + + private class CustomConfigXmlParser extends ConfigXmlParser { + @Override + public void handleStartTag(XmlPullParser xml) { + String strNode = xml.getName(); + if (strNode.equals("content")) { + String startPage = xml.getAttributeValue(null, "src"); + allowedNavigations.addWhiteListEntry(startPage, false); + } else if (strNode.equals("allow-navigation")) { + String origin = xml.getAttributeValue(null, "href"); + if ("*".equals(origin)) { + allowedNavigations.addWhiteListEntry("http://*/*", false); + allowedNavigations.addWhiteListEntry("https://*/*", false); + allowedNavigations.addWhiteListEntry("data:*", false); + } else { + allowedNavigations.addWhiteListEntry(origin, false); + } + } else if (strNode.equals("allow-intent")) { + String origin = xml.getAttributeValue(null, "href"); + allowedIntents.addWhiteListEntry(origin, false); + } else if (strNode.equals("access")) { + String origin = xml.getAttributeValue(null, "origin"); + String subdomains = xml.getAttributeValue(null, "subdomains"); + boolean external = (xml.getAttributeValue(null, "launch-external") != null); + if (origin != null) { + if (external) { + Log.w(LOG_TAG, "Found <access launch-external> within config.xml. Please use <allow-intent> instead."); + allowedIntents.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); + } else { + if ("*".equals(origin)) { + allowedRequests.addWhiteListEntry("http://*/*", false); + allowedRequests.addWhiteListEntry("https://*/*", false); + } else { + allowedRequests.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); + } + } + } + } + } + @Override + public void handleEndTag(XmlPullParser xml) { + } + } + + @Override + public Boolean shouldAllowNavigation(String url) { + if (allowedNavigations.isUrlWhiteListed(url)) { + return true; + } + return null; // Default policy + } + + @Override + public Boolean shouldAllowRequest(String url) { + if (Boolean.TRUE == shouldAllowNavigation(url)) { + return true; + } + if (allowedRequests.isUrlWhiteListed(url)) { + return true; + } + return null; // Default policy + } + + @Override + public Boolean shouldOpenExternalUrl(String url) { + if (allowedIntents.isUrlWhiteListed(url)) { + return true; + } + return null; // Default policy + } + + public Whitelist getAllowedNavigations() { + return allowedNavigations; + } + + public void setAllowedNavigations(Whitelist allowedNavigations) { + this.allowedNavigations = allowedNavigations; + } + + public Whitelist getAllowedIntents() { + return allowedIntents; + } + + public void setAllowedIntents(Whitelist allowedIntents) { + this.allowedIntents = allowedIntents; + } + + public Whitelist getAllowedRequests() { + return allowedRequests; + } + + public void setAllowedRequests(Whitelist allowedRequests) { + this.allowedRequests = allowedRequests; + } +} |
