├── .gitignore
├── LICENSE
├── README.md
├── package.json
├── plugins
├── ios.json
└── org.apache.cordova.core.inappbrowser
│ ├── .fetch.json
│ ├── README.md
│ ├── docs
│ ├── inappbrowser.md
│ └── window.open.md
│ ├── plugin.xml
│ ├── src
│ ├── android
│ │ └── InAppBrowser.java
│ ├── blackberry10
│ │ └── README.md
│ ├── ios
│ │ ├── CDVInAppBrowser.h
│ │ └── CDVInAppBrowser.m
│ └── wp
│ │ └── InAppBrowser.cs
│ ├── test
│ ├── cordova-incl.js
│ ├── inappbrowser
│ │ ├── index.html
│ │ ├── inject.css
│ │ ├── inject.html
│ │ ├── inject.js
│ │ ├── local.html
│ │ └── local.pdf
│ ├── index.html
│ ├── main.js
│ └── master.css
│ └── www
│ └── InAppBrowser.js
└── www
├── config.xml
├── css
└── index.css
├── img
└── logo.png
├── index.html
├── js
├── index.js
└── jquery-1.10.1.min.js
└── res
├── icon
├── android
│ ├── icon-36-ldpi.png
│ ├── icon-48-mdpi.png
│ ├── icon-72-hdpi.png
│ └── icon-96-xhdpi.png
├── blackberry
│ └── icon-80.png
├── ios
│ ├── icon-57-2x.png
│ ├── icon-57.png
│ ├── icon-72-2x.png
│ └── icon-72.png
└── windows-phone
│ ├── icon-173-tile.png
│ ├── icon-48.png
│ └── icon-62-tile.png
└── screen
├── android
├── screen-hdpi-landscape.png
├── screen-hdpi-portrait.png
├── screen-ldpi-landscape.png
├── screen-ldpi-portrait.png
├── screen-mdpi-landscape.png
├── screen-mdpi-portrait.png
├── screen-xhdpi-landscape.png
└── screen-xhdpi-portrait.png
├── blackberry
└── screen-225.png
├── ios
├── screen-ipad-landscape-2x.png
├── screen-ipad-landscape.png
├── screen-ipad-portrait-2x.png
├── screen-ipad-portrait.png
├── screen-iphone-landscape-2x.png
├── screen-iphone-landscape.png
├── screen-iphone-portrait-2x.png
├── screen-iphone-portrait-568h-2x.png
└── screen-iphone-portrait.png
└── windows-phone
└── screen-portrait.jpg
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | platforms
3 | .cordova
4 | .DS_Store
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2103 Michael Dellanoce
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Example demonstrating how to use Google's OAuth with PhoneGap
2 |
3 | # Running the example
4 |
5 | 1. Clone this repo, and run ```npm install```
6 | 1. Add a platform, for example ios ```cordova platform add ios```
7 | 1. Build the platform's project ```cordova build ios```
8 | 1. Launch the platform's simulator ```cordova emulate ios```
9 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "google-api-oauth-phonegap",
3 | "version": "0.0.3",
4 | "dependencies": {
5 | "cordova": "~3.0.0"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/plugins/ios.json:
--------------------------------------------------------------------------------
1 | {"prepare_queue":{"installed":[],"uninstalled":[]},"config_munge":{"config.xml":{"/*":{"":1}}},"installed_plugins":{"org.apache.cordova.core.inappbrowser":{"PACKAGE_NAME":"com.phonegaptips.googleoauth"}},"dependent_plugins":{}}
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/.fetch.json:
--------------------------------------------------------------------------------
1 | {"source":{"type":"git","url":"https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git","subdir":"."}}
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/README.md:
--------------------------------------------------------------------------------
1 | cordova-plugin-inappbrowser
2 | -----------------------------
3 | To install this plugin, follow the [Command-line Interface Guide](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface).
4 |
5 | If you are not using the Cordova Command-line Interface, follow [Using Plugman to Manage Plugins](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html).
6 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/docs/inappbrowser.md:
--------------------------------------------------------------------------------
1 | ---
2 | license: Licensed to the Apache Software Foundation (ASF) under one
3 | or more contributor license agreements. See the NOTICE file
4 | distributed with this work for additional information
5 | regarding copyright ownership. The ASF licenses this file
6 | to you under the Apache License, Version 2.0 (the
7 | "License"); you may not use this file except in compliance
8 | with the License. You may obtain a copy of the License at
9 |
10 | http://www.apache.org/licenses/LICENSE-2.0
11 |
12 | Unless required by applicable law or agreed to in writing,
13 | software distributed under the License is distributed on an
14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | KIND, either express or implied. See the License for the
16 | specific language governing permissions and limitations
17 | under the License.
18 | ---
19 |
20 | InAppBrowser
21 | ============
22 |
23 | > The `InAppBrowser` is a web browser that displays in the app when calling `window.open`.
24 |
25 | var ref = window.open('http://apache.org', '_blank', 'location=yes');
26 |
27 | Description
28 | -----------
29 |
30 | The object returned from a call to `window.open`.
31 |
32 | Methods
33 | ----------
34 |
35 | - addEventListener
36 | - removeEventListener
37 | - close
38 |
39 | Permissions
40 | -----------
41 |
42 | ### Android
43 |
44 | #### app/res/xml/config.xml
45 |
46 |
47 |
48 | ### iOS
49 |
50 | #### config.xml
51 |
52 |
53 |
54 | ### Windows Phone 7 + 8
55 |
56 | #### config.xml
57 |
58 |
59 |
60 | addEventListener
61 | ================
62 |
63 | > Adds a listener for an event from the `InAppBrowser`.
64 |
65 | ref.addEventListener(eventname, callback);
66 |
67 | - __ref__: reference to the `InAppBrowser` window _(InAppBrowser)_
68 | - __eventname__: the event to listen for _(String)_
69 |
70 | - __loadstart__: event fires when the `InAppBrowser` starts to load a URL.
71 | - __loadstop__: event fires when the `InAppBrowser` finishes loading a URL.
72 | - __loaderror__: event fires when the `InAppBrowser` encounters an error when loading a URL.
73 | - __exit__: event fires when the `InAppBrowser` window is closed.
74 |
75 | - __callback__: the function that executes when the event fires. The function is passed an `InAppBrowserEvent` object as a parameter.
76 |
77 | Supported Platforms
78 | -------------------
79 |
80 | - Android
81 | - iOS
82 | - Windows Phone 7 + 8
83 |
84 | Quick Example
85 | -------------
86 |
87 | var ref = window.open('http://apache.org', '_blank', 'location=yes');
88 | ref.addEventListener('loadstart', function() { alert(event.url); });
89 |
90 | Full Example
91 | ------------
92 |
93 |
94 |
95 |
96 | InAppBrowser.addEventListener Example
97 |
98 |
99 |
116 |
117 |
118 |
119 |
120 |
121 | removeEventListener
122 | ===================
123 |
124 | > Removes a listener for an event from the `InAppBrowser`.
125 |
126 | ref.removeEventListener(eventname, callback);
127 |
128 | - __ref__: reference to the `InAppBrowser` window. _(InAppBrowser)_
129 | - __eventname__: the event to stop listening for. _(String)_
130 |
131 | - __loadstart__: event fires when the `InAppBrowser` starts to load a URL.
132 | - __loadstop__: event fires when the `InAppBrowser` finishes loading a URL.
133 | - __loaderror__: event fires when the `InAppBrowser` encounters an error loading a URL.
134 | - __exit__: event fires when the `InAppBrowser` window is closed.
135 |
136 | - __callback__: the function to execute when the event fires.
137 | The function is passed an `InAppBrowserEvent` object.
138 |
139 | Supported Platforms
140 | -------------------
141 |
142 | - Android
143 | - iOS
144 | - Windows Phone 7 + 8
145 |
146 | Quick Example
147 | -------------
148 |
149 | var ref = window.open('http://apache.org', '_blank', 'location=yes');
150 | var myCallback = function() { alert(event.url); }
151 | ref.addEventListener('loadstart', myCallback);
152 | ref.removeEventListener('loadstart', myCallback);
153 |
154 | Full Example
155 | ------------
156 |
157 |
158 |
159 |
160 | InAppBrowser.removeEventListener Example
161 |
162 |
163 |
203 |
204 |
205 |
206 |
207 |
208 | close
209 | =====
210 |
211 | > Closes the `InAppBrowser` window.
212 |
213 | ref.close();
214 |
215 | - __ref__: reference to the `InAppBrowser` window _(InAppBrowser)_
216 |
217 | Supported Platforms
218 | -------------------
219 |
220 | - Android
221 | - iOS
222 | - Windows Phone 7 + 8
223 | - BlackBerry 10
224 |
225 | Quick Example
226 | -------------
227 |
228 | var ref = window.open('http://apache.org', '_blank', 'location=yes');
229 | ref.close();
230 |
231 | Full Example
232 | ------------
233 |
234 |
235 |
236 |
237 | InAppBrowser.close Example
238 |
239 |
240 |
257 |
258 |
259 |
260 |
261 |
262 | executeScript
263 | =============
264 |
265 | > Injects JavaScript code into the `InAppBrowser` window
266 |
267 | ref.executeScript(details, callback);
268 |
269 | - __ref__: reference to the `InAppBrowser` window. _(InAppBrowser)_
270 | - __injectDetails__: details of the script to run, specifying either a `file` or `code` key. _(Object)_
271 | - __file__: URL of the script to inject.
272 | - __code__: Text of the script to inject.
273 | - __callback__: the function that executes after the JavaScript code is injected.
274 | - If the injected script is of type `code`, the callback executes
275 | with a single parameter, which is the return value of the
276 | script, wrapped in an `Array`. For multi-line scripts, this is
277 | the return value of the last statement, or the last expression
278 | evaluated.
279 |
280 | Supported Platforms
281 | -------------------
282 |
283 | - Android
284 | - iOS
285 |
286 | Quick Example
287 | -------------
288 |
289 | var ref = window.open('http://apache.org', '_blank', 'location=yes');
290 | ref.addEventListener('loadstop', function() {
291 | ref.executeSript({file: "myscript.js"});
292 | });
293 |
294 | Full Example
295 | ------------
296 |
297 |
298 |
299 |
300 | InAppBrowser.executeScript Example
301 |
302 |
303 |
336 |
337 |
338 |
339 |
340 |
341 | insertCSS
342 | =========
343 |
344 | > Injects CSS into the `InAppBrowser` window.
345 |
346 | ref.insertCSS(details, callback);
347 |
348 | - __ref__: reference to the `InAppBrowser` window _(InAppBrowser)_
349 | - __injectDetails__: details of the script to run, specifying either a `file` or `code` key. _(Object)_
350 | - __file__: URL of the stylesheet to inject.
351 | - __code__: Text of the stylesheet to inject.
352 | - __callback__: the function that executes after the CSS is injected.
353 |
354 | Supported Platforms
355 | -------------------
356 |
357 | - Android
358 | - iOS
359 |
360 | Quick Example
361 | -------------
362 |
363 | var ref = window.open('http://apache.org', '_blank', 'location=yes');
364 | ref.addEventListener('loadstop', function() {
365 | ref.insertCSS({file: "mystyles.css"});
366 | });
367 |
368 | Full Example
369 | ------------
370 |
371 |
372 |
373 |
374 | InAppBrowser.executeScript Example
375 |
376 |
377 |
410 |
411 |
412 |
413 |
414 |
415 | InAppBrowserEvent
416 | =================
417 |
418 | The object that is passed to the callback function from an
419 | `addEventListener` call on an `InAppBrowser` object.
420 |
421 | Properties
422 | ----------
423 |
424 | - __type__: the eventname, either `loadstart`, `loadstop`, `loaderror`, or `exit`. _(String)_
425 | - __url__: the URL that was loaded. _(String)_
426 | - __code__: the error code, only in the case of `loaderror`. _(Number)_
427 | - __message__: the error message, only in the case of `loaderror`. _(String)_
428 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/docs/window.open.md:
--------------------------------------------------------------------------------
1 | ---
2 | license: Licensed to the Apache Software Foundation (ASF) under one
3 | or more contributor license agreements. See the NOTICE file
4 | distributed with this work for additional information
5 | regarding copyright ownership. The ASF licenses this file
6 | to you under the Apache License, Version 2.0 (the
7 | "License"); you may not use this file except in compliance
8 | with the License. You may obtain a copy of the License at
9 |
10 | http://www.apache.org/licenses/LICENSE-2.0
11 |
12 | Unless required by applicable law or agreed to in writing,
13 | software distributed under the License is distributed on an
14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | KIND, either express or implied. See the License for the
16 | specific language governing permissions and limitations
17 | under the License.
18 | ---
19 |
20 | window.open
21 | ===========
22 |
23 | Opens a URL in a new `InAppBrowser` instance, the current browser
24 | instance, or the system browser.
25 |
26 | var ref = window.open(url, target, options);
27 |
28 | - __ref__: Reference to the `InAppBrowser` window. _(InAppBrowser)_
29 | - __url__: The URL to load _(String)_. Call `encodeURI()` on this if the URL contains Unicode characters.
30 | - __target__: The target in which to load the URL, an optional parameter that defaults to `_self`. _(String)_
31 |
32 | - `_self`: Opens in the Cordova WebView if the URL is in the white list, otherwise it opens in the `InAppBrowser`.
33 | - `_blank`: Opens in the `InAppBrowser`.
34 | - `_system`: Opens in the system's web browser.
35 |
36 | - __options__: Options for the `InAppBrowser`. Optional, defaulting to: `location=yes`. _(String)_
37 |
38 | The `options` string must not contain any blank space, and each feature's name/value pairs must be separated by a comma. Feature names are case insensitive. All platforms support the value below:
39 |
40 | - __location__: Set to `yes` or `no` to turn the `InAppBrowser`'s location bar on or off.
41 |
42 | Android only
43 | ------------
44 | - __closebuttoncaption__ - set to a string that will be the caption for the "Done" button.
45 |
46 | iOS only
47 | --------
48 | - __closebuttoncaption__ - set to a string that will be the caption for the "Done" button. Note that you will have to localize this value yourself.
49 | - __toolbar__ - set to 'yes' or 'no' to turn the toolbar on or off for the InAppBrowser (defaults to 'yes')
50 | - __enableViewportScale__: Set to `yes` or `no` to prevent viewport scaling through a meta tag (defaults to `no`).
51 | - __mediaPlaybackRequiresUserAction__: Set to `yes` or `no` to prevent HTML5 audio or video from autoplaying (defaults to `no`).
52 | - __allowInlineMediaPlayback__: Set to `yes` or `no` to allow inline HTML5 media playback, displaying within the browser window rather than a device-specific playback interface. The HTML's `video` element must also include the `webkit-playsinline` attribute (defaults to `no`)
53 | - __keyboardDisplayRequiresUserAction__: Set to `yes` or `no` to open the keyboard when form elements receive focus via JavaScript's `focus()` call (defaults to `yes`).
54 | - __suppressesIncrementalRendering__: Set to `yes` or `no` to wait until all new view content is received before being rendered (defaults to `no`).
55 | - __presentationstyle__: Set to `pagesheet`, `formsheet` or `fullscreen` to set the [presentation style](http://developer.apple.com/library/ios/documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/modalPresentationStyle) (defaults to `fullscreen`).
56 | - __transitionstyle__: Set to `fliphorizontal`, `crossdissolve` or `coververtical` to set the [transition style](http://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIViewController_Class/Reference/Reference.html#//apple_ref/occ/instp/UIViewController/modalTransitionStyle) (defaults to `coververtical`).
57 |
58 | Supported Platforms
59 | -------------------
60 |
61 | - Android
62 | - iOS
63 | - BlackBerry 10
64 | - Windows Phone 7 + 8
65 |
66 | Quick Example
67 | -------------
68 |
69 | var ref = window.open('http://apache.org', '_blank', 'location=yes');
70 | var ref2 = window.open(encodeURI('http://ja.m.wikipedia.org/wiki/ハングル'), '_blank', 'location=yes');
71 |
72 | Full Example
73 | ------------
74 |
75 |
76 |
77 |
78 | window.open Example
79 |
80 |
81 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 | InAppBrowser
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/src/android/InAppBrowser.java:
--------------------------------------------------------------------------------
1 | /*
2 | Licensed to the Apache Software Foundation (ASF) under one
3 | or more contributor license agreements. See the NOTICE file
4 | distributed with this work for additional information
5 | regarding copyright ownership. The ASF licenses this file
6 | to you under the Apache License, Version 2.0 (the
7 | "License"); you may not use this file except in compliance
8 | with the License. You may obtain a copy of the License at
9 |
10 | http://www.apache.org/licenses/LICENSE-2.0
11 |
12 | Unless required by applicable law or agreed to in writing,
13 | software distributed under the License is distributed on an
14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | KIND, either express or implied. See the License for the
16 | specific language governing permissions and limitations
17 | under the License.
18 | */
19 | package org.apache.cordova.core;
20 |
21 | import java.util.HashMap;
22 | import java.util.StringTokenizer;
23 |
24 |
25 | import org.apache.cordova.Config;
26 | import org.apache.cordova.CordovaWebView;
27 | import org.apache.cordova.CallbackContext;
28 | import org.apache.cordova.CordovaPlugin;
29 | import org.apache.cordova.LOG;
30 | import org.apache.cordova.PluginResult;
31 | import org.json.JSONArray;
32 | import org.json.JSONException;
33 | import org.json.JSONObject;
34 |
35 | import android.annotation.SuppressLint;
36 | import android.app.Dialog;
37 | import android.content.Context;
38 | import android.content.DialogInterface;
39 | import android.content.Intent;
40 | import android.graphics.Bitmap;
41 | import android.net.Uri;
42 | import android.os.Bundle;
43 | import android.text.InputType;
44 | import android.util.Log;
45 | import android.util.TypedValue;
46 | import android.view.Gravity;
47 | import android.view.KeyEvent;
48 | import android.view.View;
49 | import android.view.Window;
50 | import android.view.WindowManager;
51 | import android.view.WindowManager.LayoutParams;
52 | import android.view.inputmethod.EditorInfo;
53 | import android.view.inputmethod.InputMethodManager;
54 | import android.webkit.WebChromeClient;
55 | import android.webkit.GeolocationPermissions.Callback;
56 | import android.webkit.JsPromptResult;
57 | import android.webkit.WebSettings;
58 | import android.webkit.WebStorage;
59 | import android.webkit.WebView;
60 | import android.webkit.WebViewClient;
61 | import android.widget.Button;
62 | import android.widget.EditText;
63 | import android.widget.LinearLayout;
64 | import android.widget.RelativeLayout;
65 |
66 | @SuppressLint("SetJavaScriptEnabled")
67 | public class InAppBrowser extends CordovaPlugin {
68 |
69 | private static final String NULL = "null";
70 | protected static final String LOG_TAG = "InAppBrowser";
71 | private static final String SELF = "_self";
72 | private static final String SYSTEM = "_system";
73 | // private static final String BLANK = "_blank";
74 | private static final String EXIT_EVENT = "exit";
75 | private static final String LOCATION = "location";
76 | private static final String HIDDEN = "hidden";
77 | private static final String LOAD_START_EVENT = "loadstart";
78 | private static final String LOAD_STOP_EVENT = "loadstop";
79 | private static final String LOAD_ERROR_EVENT = "loaderror";
80 | private static final String CLOSE_BUTTON_CAPTION = "closebuttoncaption";
81 | private long MAX_QUOTA = 100 * 1024 * 1024;
82 |
83 | private Dialog dialog;
84 | private WebView inAppWebView;
85 | private EditText edittext;
86 | private CallbackContext callbackContext;
87 | private boolean showLocationBar = true;
88 | private boolean openWindowHidden = false;
89 | private String buttonLabel = "Done";
90 |
91 | /**
92 | * Executes the request and returns PluginResult.
93 | *
94 | * @param action The action to execute.
95 | * @param args JSONArry of arguments for the plugin.
96 | * @param callbackId The callback id used when calling back into JavaScript.
97 | * @return A PluginResult object with a status and message.
98 | */
99 | public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
100 | try {
101 | if (action.equals("open")) {
102 | this.callbackContext = callbackContext;
103 | String url = args.getString(0);
104 | String target = args.optString(1);
105 | if (target == null || target.equals("") || target.equals(NULL)) {
106 | target = SELF;
107 | }
108 | HashMap features = parseFeature(args.optString(2));
109 |
110 | Log.d(LOG_TAG, "target = " + target);
111 |
112 | url = updateUrl(url);
113 | String result = "";
114 |
115 | // SELF
116 | if (SELF.equals(target)) {
117 | Log.d(LOG_TAG, "in self");
118 | // load in webview
119 | if (url.startsWith("file://") || url.startsWith("javascript:")
120 | || Config.isUrlWhiteListed(url)) {
121 | this.webView.loadUrl(url);
122 | }
123 | //Load the dialer
124 | else if (url.startsWith(WebView.SCHEME_TEL))
125 | {
126 | try {
127 | Intent intent = new Intent(Intent.ACTION_DIAL);
128 | intent.setData(Uri.parse(url));
129 | this.cordova.getActivity().startActivity(intent);
130 | } catch (android.content.ActivityNotFoundException e) {
131 | LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
132 | }
133 | }
134 | // load in InAppBrowser
135 | else {
136 | result = this.showWebPage(url, features);
137 | }
138 | }
139 | // SYSTEM
140 | else if (SYSTEM.equals(target)) {
141 | Log.d(LOG_TAG, "in system");
142 | result = this.openExternal(url);
143 | }
144 | // BLANK - or anything else
145 | else {
146 | Log.d(LOG_TAG, "in blank");
147 | result = this.showWebPage(url, features);
148 | }
149 |
150 | PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
151 | pluginResult.setKeepCallback(true);
152 | this.callbackContext.sendPluginResult(pluginResult);
153 | }
154 | else if (action.equals("close")) {
155 | closeDialog();
156 | this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
157 | }
158 | else if (action.equals("injectScriptCode")) {
159 | String jsWrapper = null;
160 | if (args.getBoolean(1)) {
161 | jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
162 | }
163 | injectDeferredObject(args.getString(0), jsWrapper);
164 | }
165 | else if (action.equals("injectScriptFile")) {
166 | String jsWrapper;
167 | if (args.getBoolean(1)) {
168 | 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());
169 | } else {
170 | jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
171 | }
172 | injectDeferredObject(args.getString(0), jsWrapper);
173 | }
174 | else if (action.equals("injectStyleCode")) {
175 | String jsWrapper;
176 | if (args.getBoolean(1)) {
177 | jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
178 | } else {
179 | jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
180 | }
181 | injectDeferredObject(args.getString(0), jsWrapper);
182 | }
183 | else if (action.equals("injectStyleFile")) {
184 | String jsWrapper;
185 | if (args.getBoolean(1)) {
186 | 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());
187 | } else {
188 | jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
189 | }
190 | injectDeferredObject(args.getString(0), jsWrapper);
191 | }
192 | else if (action.equals("show")) {
193 | Runnable runnable = new Runnable() {
194 | @Override
195 | public void run() {
196 | dialog.show();
197 | }
198 | };
199 | this.cordova.getActivity().runOnUiThread(runnable);
200 | this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
201 | }
202 | else {
203 | return false;
204 | }
205 | } catch (JSONException e) {
206 | this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
207 | }
208 | return true;
209 | }
210 |
211 | /**
212 | * Inject an object (script or style) into the InAppBrowser WebView.
213 | *
214 | * This is a helper method for the inject{Script|Style}{Code|File} API calls, which
215 | * provides a consistent method for injecting JavaScript code into the document.
216 | *
217 | * If a wrapper string is supplied, then the source string will be JSON-encoded (adding
218 | * quotes) and wrapped using string formatting. (The wrapper string should have a single
219 | * '%s' marker)
220 | *
221 | * @param source The source object (filename or script/style text) to inject into
222 | * the document.
223 | * @param jsWrapper A JavaScript string to wrap the source string in, so that the object
224 | * is properly injected, or null if the source string is JavaScript text
225 | * which should be executed directly.
226 | */
227 | private void injectDeferredObject(String source, String jsWrapper) {
228 | String scriptToInject;
229 | if (jsWrapper != null) {
230 | org.json.JSONArray jsonEsc = new org.json.JSONArray();
231 | jsonEsc.put(source);
232 | String jsonRepr = jsonEsc.toString();
233 | String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
234 | scriptToInject = String.format(jsWrapper, jsonSourceString);
235 | } else {
236 | scriptToInject = source;
237 | }
238 | // This action will have the side-effect of blurring the currently focused element
239 | this.inAppWebView.loadUrl("javascript:" + scriptToInject);
240 | }
241 |
242 | /**
243 | * Put the list of features into a hash map
244 | *
245 | * @param optString
246 | * @return
247 | */
248 | private HashMap parseFeature(String optString) {
249 | if (optString.equals(NULL)) {
250 | return null;
251 | } else {
252 | HashMap map = new HashMap();
253 | StringTokenizer features = new StringTokenizer(optString, ",");
254 | StringTokenizer option;
255 | while(features.hasMoreElements()) {
256 | option = new StringTokenizer(features.nextToken(), "=");
257 | if (option.hasMoreElements()) {
258 | String key = option.nextToken();
259 | if (key.equalsIgnoreCase(CLOSE_BUTTON_CAPTION)) {
260 | this.buttonLabel = option.nextToken();
261 | } else {
262 | Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE;
263 | map.put(key, value);
264 | }
265 | }
266 | }
267 | return map;
268 | }
269 | }
270 |
271 | /**
272 | * Convert relative URL to full path
273 | *
274 | * @param url
275 | * @return
276 | */
277 | private String updateUrl(String url) {
278 | Uri newUrl = Uri.parse(url);
279 | if (newUrl.isRelative()) {
280 | url = this.webView.getUrl().substring(0, this.webView.getUrl().lastIndexOf("/")+1) + url;
281 | }
282 | return url;
283 | }
284 |
285 | /**
286 | * Display a new browser with the specified URL.
287 | *
288 | * @param url The url to load.
289 | * @param usePhoneGap Load url in PhoneGap webview
290 | * @return "" if ok, or error message.
291 | */
292 | public String openExternal(String url) {
293 | try {
294 | Intent intent = null;
295 | intent = new Intent(Intent.ACTION_VIEW);
296 | intent.setData(Uri.parse(url));
297 | this.cordova.getActivity().startActivity(intent);
298 | return "";
299 | } catch (android.content.ActivityNotFoundException e) {
300 | Log.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString());
301 | return e.toString();
302 | }
303 | }
304 |
305 | /**
306 | * Closes the dialog
307 | */
308 | private void closeDialog() {
309 | try {
310 | this.inAppWebView.loadUrl("about:blank");
311 | JSONObject obj = new JSONObject();
312 | obj.put("type", EXIT_EVENT);
313 |
314 | sendUpdate(obj, false);
315 | } catch (JSONException ex) {
316 | Log.d(LOG_TAG, "Should never happen");
317 | }
318 |
319 | if (dialog != null) {
320 | dialog.dismiss();
321 | }
322 | }
323 |
324 | /**
325 | * Checks to see if it is possible to go back one page in history, then does so.
326 | */
327 | private void goBack() {
328 | if (this.inAppWebView.canGoBack()) {
329 | this.inAppWebView.goBack();
330 | }
331 | }
332 |
333 | /**
334 | * Checks to see if it is possible to go forward one page in history, then does so.
335 | */
336 | private void goForward() {
337 | if (this.inAppWebView.canGoForward()) {
338 | this.inAppWebView.goForward();
339 | }
340 | }
341 |
342 | /**
343 | * Navigate to the new page
344 | *
345 | * @param url to load
346 | */
347 | private void navigate(String url) {
348 | InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
349 | imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0);
350 |
351 | if (!url.startsWith("http") && !url.startsWith("file:")) {
352 | this.inAppWebView.loadUrl("http://" + url);
353 | } else {
354 | this.inAppWebView.loadUrl(url);
355 | }
356 | this.inAppWebView.requestFocus();
357 | }
358 |
359 |
360 | /**
361 | * Should we show the location bar?
362 | *
363 | * @return boolean
364 | */
365 | private boolean getShowLocationBar() {
366 | return this.showLocationBar;
367 | }
368 |
369 | /**
370 | * Display a new browser with the specified URL.
371 | *
372 | * @param url The url to load.
373 | * @param jsonObject
374 | */
375 | public String showWebPage(final String url, HashMap features) {
376 | // Determine if we should hide the location bar.
377 | showLocationBar = true;
378 | openWindowHidden = false;
379 | if (features != null) {
380 | Boolean show = features.get(LOCATION);
381 | if (show != null) {
382 | showLocationBar = show.booleanValue();
383 | }
384 | Boolean hidden = features.get(HIDDEN);
385 | if(hidden != null) {
386 | openWindowHidden = hidden.booleanValue();
387 | }
388 | }
389 |
390 | final CordovaWebView thatWebView = this.webView;
391 |
392 | // Create dialog in new thread
393 | Runnable runnable = new Runnable() {
394 | /**
395 | * Convert our DIP units to Pixels
396 | *
397 | * @return int
398 | */
399 | private int dpToPixels(int dipValue) {
400 | int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,
401 | (float) dipValue,
402 | cordova.getActivity().getResources().getDisplayMetrics()
403 | );
404 |
405 | return value;
406 | }
407 |
408 | public void run() {
409 | // Let's create the main dialog
410 | dialog = new Dialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar);
411 | dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog;
412 | dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
413 | dialog.setCancelable(true);
414 | dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
415 | public void onDismiss(DialogInterface dialog) {
416 | try {
417 | JSONObject obj = new JSONObject();
418 | obj.put("type", EXIT_EVENT);
419 |
420 | sendUpdate(obj, false);
421 | } catch (JSONException e) {
422 | Log.d(LOG_TAG, "Should never happen");
423 | }
424 | }
425 | });
426 |
427 | // Main container layout
428 | LinearLayout main = new LinearLayout(cordova.getActivity());
429 | main.setOrientation(LinearLayout.VERTICAL);
430 |
431 | // Toolbar layout
432 | RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());
433 | toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)));
434 | toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
435 | toolbar.setHorizontalGravity(Gravity.LEFT);
436 | toolbar.setVerticalGravity(Gravity.TOP);
437 |
438 | // Action Button Container layout
439 | RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity());
440 | actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
441 | actionButtonContainer.setHorizontalGravity(Gravity.LEFT);
442 | actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
443 | actionButtonContainer.setId(1);
444 |
445 | // Back button
446 | Button back = new Button(cordova.getActivity());
447 | RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
448 | backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT);
449 | back.setLayoutParams(backLayoutParams);
450 | back.setContentDescription("Back Button");
451 | back.setId(2);
452 | back.setText("<");
453 | back.setOnClickListener(new View.OnClickListener() {
454 | public void onClick(View v) {
455 | goBack();
456 | }
457 | });
458 |
459 | // Forward button
460 | Button forward = new Button(cordova.getActivity());
461 | RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
462 | forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2);
463 | forward.setLayoutParams(forwardLayoutParams);
464 | forward.setContentDescription("Forward Button");
465 | forward.setId(3);
466 | forward.setText(">");
467 | forward.setOnClickListener(new View.OnClickListener() {
468 | public void onClick(View v) {
469 | goForward();
470 | }
471 | });
472 |
473 | // Edit Text Box
474 | edittext = new EditText(cordova.getActivity());
475 | RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
476 | textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1);
477 | textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5);
478 | edittext.setLayoutParams(textLayoutParams);
479 | edittext.setId(4);
480 | edittext.setSingleLine(true);
481 | edittext.setText(url);
482 | edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
483 | edittext.setImeOptions(EditorInfo.IME_ACTION_GO);
484 | edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE
485 | edittext.setOnKeyListener(new View.OnKeyListener() {
486 | public boolean onKey(View v, int keyCode, KeyEvent event) {
487 | // If the event is a key-down event on the "enter" button
488 | if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
489 | navigate(edittext.getText().toString());
490 | return true;
491 | }
492 | return false;
493 | }
494 | });
495 |
496 | // Close button
497 | Button close = new Button(cordova.getActivity());
498 | RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
499 | closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
500 | close.setLayoutParams(closeLayoutParams);
501 | forward.setContentDescription("Close Button");
502 | close.setId(5);
503 | close.setText(buttonLabel);
504 | close.setOnClickListener(new View.OnClickListener() {
505 | public void onClick(View v) {
506 | closeDialog();
507 | }
508 | });
509 |
510 | // WebView
511 | inAppWebView = new WebView(cordova.getActivity());
512 | inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
513 | inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView));
514 | WebViewClient client = new InAppBrowserClient(thatWebView, edittext);
515 | inAppWebView.setWebViewClient(client);
516 | WebSettings settings = inAppWebView.getSettings();
517 | settings.setJavaScriptEnabled(true);
518 | settings.setJavaScriptCanOpenWindowsAutomatically(true);
519 | settings.setBuiltInZoomControls(true);
520 | settings.setPluginState(android.webkit.WebSettings.PluginState.ON);
521 |
522 | //Toggle whether this is enabled or not!
523 | Bundle appSettings = cordova.getActivity().getIntent().getExtras();
524 | boolean enableDatabase = appSettings == null ? true : appSettings.getBoolean("InAppBrowserStorageEnabled", true);
525 | if(enableDatabase)
526 | {
527 | String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath();
528 | settings.setDatabasePath(databasePath);
529 | settings.setDatabaseEnabled(true);
530 | }
531 | settings.setDomStorageEnabled(true);
532 |
533 | inAppWebView.loadUrl(url);
534 | inAppWebView.setId(6);
535 | inAppWebView.getSettings().setLoadWithOverviewMode(true);
536 | inAppWebView.getSettings().setUseWideViewPort(true);
537 | inAppWebView.requestFocus();
538 | inAppWebView.requestFocusFromTouch();
539 |
540 | // Add the back and forward buttons to our action button container layout
541 | actionButtonContainer.addView(back);
542 | actionButtonContainer.addView(forward);
543 |
544 | // Add the views to our toolbar
545 | toolbar.addView(actionButtonContainer);
546 | toolbar.addView(edittext);
547 | toolbar.addView(close);
548 |
549 | // Don't add the toolbar if its been disabled
550 | if (getShowLocationBar()) {
551 | // Add our toolbar to our main view/layout
552 | main.addView(toolbar);
553 | }
554 |
555 | // Add our webview to our main view/layout
556 | main.addView(inAppWebView);
557 |
558 | WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
559 | lp.copyFrom(dialog.getWindow().getAttributes());
560 | lp.width = WindowManager.LayoutParams.MATCH_PARENT;
561 | lp.height = WindowManager.LayoutParams.MATCH_PARENT;
562 |
563 | dialog.setContentView(main);
564 | dialog.show();
565 | dialog.getWindow().setAttributes(lp);
566 | // the goal of openhidden is to load the url and not display it
567 | // Show() needs to be called to cause the URL to be loaded
568 | if(openWindowHidden) {
569 | dialog.hide();
570 | }
571 | }
572 | };
573 | this.cordova.getActivity().runOnUiThread(runnable);
574 | return "";
575 | }
576 |
577 | /**
578 | * Create a new plugin success result and send it back to JavaScript
579 | *
580 | * @param obj a JSONObject contain event payload information
581 | */
582 | private void sendUpdate(JSONObject obj, boolean keepCallback) {
583 | sendUpdate(obj, keepCallback, PluginResult.Status.OK);
584 | }
585 |
586 | /**
587 | * Create a new plugin result and send it back to JavaScript
588 | *
589 | * @param obj a JSONObject contain event payload information
590 | * @param status the status code to return to the JavaScript environment
591 | */ private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
592 | PluginResult result = new PluginResult(status, obj);
593 | result.setKeepCallback(keepCallback);
594 | this.callbackContext.sendPluginResult(result);
595 | }
596 |
597 | public class InAppChromeClient extends WebChromeClient {
598 |
599 | private CordovaWebView webView;
600 |
601 | public InAppChromeClient(CordovaWebView webView) {
602 | super();
603 | this.webView = webView;
604 | }
605 | /**
606 | * Handle database quota exceeded notification.
607 | *
608 | * @param url
609 | * @param databaseIdentifier
610 | * @param currentQuota
611 | * @param estimatedSize
612 | * @param totalUsedQuota
613 | * @param quotaUpdater
614 | */
615 | @Override
616 | public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
617 | long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
618 | {
619 | LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
620 |
621 | if (estimatedSize < MAX_QUOTA)
622 | {
623 | //increase for 1Mb
624 | long newQuota = estimatedSize;
625 | LOG.d(LOG_TAG, "calling quotaUpdater.updateQuota newQuota: %d", newQuota);
626 | quotaUpdater.updateQuota(newQuota);
627 | }
628 | else
629 | {
630 | // Set the quota to whatever it is and force an error
631 | // TODO: get docs on how to handle this properly
632 | quotaUpdater.updateQuota(currentQuota);
633 | }
634 | }
635 |
636 | /**
637 | * Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin.
638 | *
639 | * @param origin
640 | * @param callback
641 | */
642 | @Override
643 | public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
644 | super.onGeolocationPermissionsShowPrompt(origin, callback);
645 | callback.invoke(origin, true, false);
646 | }
647 |
648 | /**
649 | * Tell the client to display a prompt dialog to the user.
650 | * If the client returns true, WebView will assume that the client will
651 | * handle the prompt dialog and call the appropriate JsPromptResult method.
652 | *
653 | * The prompt bridge provided for the InAppBrowser is capable of executing any
654 | * oustanding callback belonging to the InAppBrowser plugin. Care has been
655 | * taken that other callbacks cannot be triggered, and that no other code
656 | * execution is possible.
657 | *
658 | * To trigger the bridge, the prompt default value should be of the form:
659 | *
660 | * gap-iab://
661 | *
662 | * where is the string id of the callback to trigger (something
663 | * like "InAppBrowser0123456789")
664 | *
665 | * If present, the prompt message is expected to be a JSON-encoded value to
666 | * pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid.
667 | *
668 | * @param view
669 | * @param url
670 | * @param message
671 | * @param defaultValue
672 | * @param result
673 | */
674 | @Override
675 | public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
676 | // See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute.
677 | if (defaultValue != null && defaultValue.startsWith("gap-iab://")) {
678 | PluginResult scriptResult;
679 | String scriptCallbackId = defaultValue.substring(10);
680 | if (scriptCallbackId.startsWith("InAppBrowser")) {
681 | if(message == null || message.length() == 0) {
682 | scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray());
683 | } else {
684 | try {
685 | scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray(message));
686 | } catch(JSONException e) {
687 | scriptResult = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
688 | }
689 | }
690 | this.webView.sendPluginResult(scriptResult, scriptCallbackId);
691 | result.confirm("");
692 | return true;
693 | }
694 | }
695 | return false;
696 | }
697 |
698 | }
699 |
700 | /**
701 | * The webview client receives notifications about appView
702 | */
703 | public class InAppBrowserClient extends WebViewClient {
704 | EditText edittext;
705 | CordovaWebView webView;
706 |
707 | /**
708 | * Constructor.
709 | *
710 | * @param mContext
711 | * @param edittext
712 | */
713 | public InAppBrowserClient(CordovaWebView webView, EditText mEditText) {
714 | this.webView = webView;
715 | this.edittext = mEditText;
716 | }
717 |
718 | /**
719 | * Notify the host application that a page has started loading.
720 | *
721 | * @param view The webview initiating the callback.
722 | * @param url The url of the page.
723 | */
724 | @Override
725 | public void onPageStarted(WebView view, String url, Bitmap favicon) {
726 | super.onPageStarted(view, url, favicon);
727 | String newloc = "";
728 | if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
729 | newloc = url;
730 | }
731 | // If dialing phone (tel:5551212)
732 | else if (url.startsWith(WebView.SCHEME_TEL)) {
733 | try {
734 | Intent intent = new Intent(Intent.ACTION_DIAL);
735 | intent.setData(Uri.parse(url));
736 | cordova.getActivity().startActivity(intent);
737 | } catch (android.content.ActivityNotFoundException e) {
738 | LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
739 | }
740 | }
741 |
742 | else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:")) {
743 | try {
744 | Intent intent = new Intent(Intent.ACTION_VIEW);
745 | intent.setData(Uri.parse(url));
746 | cordova.getActivity().startActivity(intent);
747 | } catch (android.content.ActivityNotFoundException e) {
748 | LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString());
749 | }
750 | }
751 | // If sms:5551212?body=This is the message
752 | else if (url.startsWith("sms:")) {
753 | try {
754 | Intent intent = new Intent(Intent.ACTION_VIEW);
755 |
756 | // Get address
757 | String address = null;
758 | int parmIndex = url.indexOf('?');
759 | if (parmIndex == -1) {
760 | address = url.substring(4);
761 | }
762 | else {
763 | address = url.substring(4, parmIndex);
764 |
765 | // If body, then set sms body
766 | Uri uri = Uri.parse(url);
767 | String query = uri.getQuery();
768 | if (query != null) {
769 | if (query.startsWith("body=")) {
770 | intent.putExtra("sms_body", query.substring(5));
771 | }
772 | }
773 | }
774 | intent.setData(Uri.parse("sms:" + address));
775 | intent.putExtra("address", address);
776 | intent.setType("vnd.android-dir/mms-sms");
777 | cordova.getActivity().startActivity(intent);
778 | } catch (android.content.ActivityNotFoundException e) {
779 | LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
780 | }
781 | }
782 | else {
783 | newloc = "http://" + url;
784 | }
785 |
786 | if (!newloc.equals(edittext.getText().toString())) {
787 | edittext.setText(newloc);
788 | }
789 |
790 | try {
791 | JSONObject obj = new JSONObject();
792 | obj.put("type", LOAD_START_EVENT);
793 | obj.put("url", newloc);
794 |
795 | sendUpdate(obj, true);
796 | } catch (JSONException ex) {
797 | Log.d(LOG_TAG, "Should never happen");
798 | }
799 | }
800 |
801 | public void onPageFinished(WebView view, String url) {
802 | super.onPageFinished(view, url);
803 |
804 | try {
805 | JSONObject obj = new JSONObject();
806 | obj.put("type", LOAD_STOP_EVENT);
807 | obj.put("url", url);
808 |
809 | sendUpdate(obj, true);
810 | } catch (JSONException ex) {
811 | Log.d(LOG_TAG, "Should never happen");
812 | }
813 | }
814 |
815 | public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
816 | super.onReceivedError(view, errorCode, description, failingUrl);
817 |
818 | try {
819 | JSONObject obj = new JSONObject();
820 | obj.put("type", LOAD_ERROR_EVENT);
821 | obj.put("url", failingUrl);
822 | obj.put("code", errorCode);
823 | obj.put("message", description);
824 |
825 | sendUpdate(obj, true, PluginResult.Status.ERROR);
826 | } catch (JSONException ex) {
827 | Log.d(LOG_TAG, "Should never happen");
828 | }
829 |
830 | }
831 | }
832 | }
833 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/src/blackberry10/README.md:
--------------------------------------------------------------------------------
1 | # BlackBerry 10 In-App-Browser Plugin
2 |
3 | The in app browser functionality is entirely contained within common js. There is no native implementation required.
4 | To install this plugin, follow the [Command-line Interface Guide](http://cordova.apache.org/docs/en/edge/guide_cli_index.md.html#The%20Command-line%20Interface).
5 |
6 | If you are not using the Cordova Command-line Interface, follow [Using Plugman to Manage Plugins](http://cordova.apache.org/docs/en/edge/guide_plugin_ref_plugman.md.html).
7 | ./cordova-plugin-battery-status/README.md
8 | ./cordova-plugin-camera/README.md
9 | ./cordova-plugin-console/README.md
10 | ./cordova-plugin-contacts/README.md
11 | ./cordova-plugin-device/README.md
12 | ./cordova-plugin-device-motion/README.md
13 | ./cordova-plugin-device-orientation/README.md
14 | ./cordova-plugin-device-orientation/src/blackberry10/README.md
15 | ./cordova-plugin-file/README.md
16 | ./cordova-plugin-file-transfer/README.md
17 | ./cordova-plugin-geolocation/README.md
18 | ./cordova-plugin-globalization/README.md
19 | ./cordova-plugin-inappbrowser/README.md
20 | ./cordova-plugin-inappbrowser/src/blackberry10/README.md
21 | ./cordova-plugin-media/README.md
22 | ./cordova-plugin-media-capture/README.md
23 | ./cordova-plugin-network-information/README.md
24 | ./cordova-plugin-splashscreen/README.md
25 | ./cordova-plugin-vibration/README.md
26 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/src/ios/CDVInAppBrowser.h:
--------------------------------------------------------------------------------
1 | /*
2 | Licensed to the Apache Software Foundation (ASF) under one
3 | or more contributor license agreements. See the NOTICE file
4 | distributed with this work for additional information
5 | regarding copyright ownership. The ASF licenses this file
6 | to you under the Apache License, Version 2.0 (the
7 | "License"); you may not use this file except in compliance
8 | with the License. You may obtain a copy of the License at
9 |
10 | http://www.apache.org/licenses/LICENSE-2.0
11 |
12 | Unless required by applicable law or agreed to in writing,
13 | software distributed under the License is distributed on an
14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | KIND, either express or implied. See the License for the
16 | specific language governing permissions and limitations
17 | under the License.
18 | */
19 |
20 | #import
21 | #import
22 | #import
23 | #import
24 |
25 | @class CDVInAppBrowserViewController;
26 |
27 | @interface CDVInAppBrowser : CDVPlugin {
28 | BOOL _injectedIframeBridge;
29 | }
30 |
31 | @property (nonatomic, retain) CDVInAppBrowserViewController* inAppBrowserViewController;
32 | @property (nonatomic, copy) NSString* callbackId;
33 |
34 | - (void)open:(CDVInvokedUrlCommand*)command;
35 | - (void)close:(CDVInvokedUrlCommand*)command;
36 | - (void)injectScriptCode:(CDVInvokedUrlCommand*)command;
37 | - (void)show:(CDVInvokedUrlCommand*)command;
38 |
39 | @end
40 |
41 | @interface CDVInAppBrowserViewController : UIViewController {
42 | @private
43 | NSString* _userAgent;
44 | NSString* _prevUserAgent;
45 | NSInteger _userAgentLockToken;
46 | CDVWebViewDelegate* _webViewDelegate;
47 | }
48 |
49 | @property (nonatomic, strong) IBOutlet UIWebView* webView;
50 | @property (nonatomic, strong) IBOutlet UIBarButtonItem* closeButton;
51 | @property (nonatomic, strong) IBOutlet UILabel* addressLabel;
52 | @property (nonatomic, strong) IBOutlet UIBarButtonItem* backButton;
53 | @property (nonatomic, strong) IBOutlet UIBarButtonItem* forwardButton;
54 | @property (nonatomic, strong) IBOutlet UIActivityIndicatorView* spinner;
55 | @property (nonatomic, strong) IBOutlet UIToolbar* toolbar;
56 |
57 | @property (nonatomic, weak) id orientationDelegate;
58 | @property (nonatomic, weak) CDVInAppBrowser* navigationDelegate;
59 | @property (nonatomic) NSURL* currentURL;
60 |
61 | - (void)close;
62 | - (void)navigateTo:(NSURL*)url;
63 | - (void)showLocationBar:(BOOL)show;
64 | - (void)showToolBar:(BOOL)show;
65 | - (void)setCloseButtonTitle:(NSString*)title;
66 |
67 | - (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent;
68 |
69 | @end
70 |
71 | @interface CDVInAppBrowserOptions : NSObject {}
72 |
73 | @property (nonatomic, assign) BOOL location;
74 | @property (nonatomic, assign) BOOL toolbar;
75 | @property (nonatomic, copy) NSString* closebuttoncaption;
76 |
77 | @property (nonatomic, copy) NSString* presentationstyle;
78 | @property (nonatomic, copy) NSString* transitionstyle;
79 |
80 | @property (nonatomic, assign) BOOL enableviewportscale;
81 | @property (nonatomic, assign) BOOL mediaplaybackrequiresuseraction;
82 | @property (nonatomic, assign) BOOL allowinlinemediaplayback;
83 | @property (nonatomic, assign) BOOL keyboarddisplayrequiresuseraction;
84 | @property (nonatomic, assign) BOOL suppressesincrementalrendering;
85 | @property (nonatomic, assign) BOOL hidden;
86 |
87 | + (CDVInAppBrowserOptions*)parseOptions:(NSString*)options;
88 |
89 | @end
90 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/src/ios/CDVInAppBrowser.m:
--------------------------------------------------------------------------------
1 | /*
2 | Licensed to the Apache Software Foundation (ASF) under one
3 | or more contributor license agreements. See the NOTICE file
4 | distributed with this work for additional information
5 | regarding copyright ownership. The ASF licenses this file
6 | to you under the Apache License, Version 2.0 (the
7 | "License"); you may not use this file except in compliance
8 | with the License. You may obtain a copy of the License at
9 |
10 | http://www.apache.org/licenses/LICENSE-2.0
11 |
12 | Unless required by applicable law or agreed to in writing,
13 | software distributed under the License is distributed on an
14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | KIND, either express or implied. See the License for the
16 | specific language governing permissions and limitations
17 | under the License.
18 | */
19 |
20 | #import "CDVInAppBrowser.h"
21 | #import
22 | #import
23 | #import
24 |
25 | #define kInAppBrowserTargetSelf @"_self"
26 | #define kInAppBrowserTargetSystem @"_system"
27 | #define kInAppBrowserTargetBlank @"_blank"
28 |
29 | #define TOOLBAR_HEIGHT 44.0
30 | #define LOCATIONBAR_HEIGHT 21.0
31 | #define FOOTER_HEIGHT ((TOOLBAR_HEIGHT) + (LOCATIONBAR_HEIGHT))
32 |
33 | #pragma mark CDVInAppBrowser
34 |
35 | @implementation CDVInAppBrowser
36 |
37 | - (CDVInAppBrowser*)initWithWebView:(UIWebView*)theWebView
38 | {
39 | self = [super initWithWebView:theWebView];
40 | if (self != nil) {
41 | // your initialization here
42 | }
43 |
44 | return self;
45 | }
46 |
47 | - (void)onReset
48 | {
49 | [self close:nil];
50 | }
51 |
52 | - (void)close:(CDVInvokedUrlCommand*)command
53 | {
54 | if (self.inAppBrowserViewController != nil) {
55 | [self.inAppBrowserViewController close];
56 | self.inAppBrowserViewController = nil;
57 | }
58 |
59 | self.callbackId = nil;
60 | }
61 |
62 | - (BOOL) isSystemUrl:(NSURL*)url
63 | {
64 | if ([[url host] isEqualToString:@"itunes.apple.com"]) {
65 | return YES;
66 | }
67 |
68 | return NO;
69 | }
70 |
71 | - (void)open:(CDVInvokedUrlCommand*)command
72 | {
73 | CDVPluginResult* pluginResult;
74 |
75 | NSString* url = [command argumentAtIndex:0];
76 | NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf];
77 | NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]];
78 |
79 | self.callbackId = command.callbackId;
80 |
81 | if (url != nil) {
82 | NSURL* baseUrl = [self.webView.request URL];
83 | NSURL* absoluteUrl = [[NSURL URLWithString:url relativeToURL:baseUrl] absoluteURL];
84 |
85 | if ([self isSystemUrl:absoluteUrl]) {
86 | target = kInAppBrowserTargetSystem;
87 | }
88 |
89 | if ([target isEqualToString:kInAppBrowserTargetSelf]) {
90 | [self openInCordovaWebView:absoluteUrl withOptions:options];
91 | } else if ([target isEqualToString:kInAppBrowserTargetSystem]) {
92 | [self openInSystem:absoluteUrl];
93 | } else { // _blank or anything else
94 | [self openInInAppBrowser:absoluteUrl withOptions:options];
95 | }
96 |
97 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
98 | } else {
99 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"incorrect number of arguments"];
100 | }
101 |
102 | [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
103 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
104 | }
105 |
106 | - (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options
107 | {
108 | if (self.inAppBrowserViewController == nil) {
109 | NSString* originalUA = [CDVUserAgentUtil originalUserAgent];
110 | self.inAppBrowserViewController = [[CDVInAppBrowserViewController alloc] initWithUserAgent:originalUA prevUserAgent:[self.commandDelegate userAgent]];
111 | self.inAppBrowserViewController.navigationDelegate = self;
112 |
113 | if ([self.viewController conformsToProtocol:@protocol(CDVScreenOrientationDelegate)]) {
114 | self.inAppBrowserViewController.orientationDelegate = (UIViewController *)self.viewController;
115 | }
116 | }
117 |
118 |
119 | CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options];
120 | [self.inAppBrowserViewController showLocationBar:browserOptions.location];
121 | [self.inAppBrowserViewController showToolBar:browserOptions.toolbar];
122 | if (browserOptions.closebuttoncaption != nil) {
123 | [self.inAppBrowserViewController setCloseButtonTitle:browserOptions.closebuttoncaption];
124 | }
125 | // Set Presentation Style
126 | UIModalPresentationStyle presentationStyle = UIModalPresentationFullScreen; // default
127 | if (browserOptions.presentationstyle != nil) {
128 | if ([[browserOptions.presentationstyle lowercaseString] isEqualToString:@"pagesheet"]) {
129 | presentationStyle = UIModalPresentationPageSheet;
130 | } else if ([[browserOptions.presentationstyle lowercaseString] isEqualToString:@"formsheet"]) {
131 | presentationStyle = UIModalPresentationFormSheet;
132 | }
133 | }
134 | self.inAppBrowserViewController.modalPresentationStyle = presentationStyle;
135 |
136 | // Set Transition Style
137 | UIModalTransitionStyle transitionStyle = UIModalTransitionStyleCoverVertical; // default
138 | if (browserOptions.transitionstyle != nil) {
139 | if ([[browserOptions.transitionstyle lowercaseString] isEqualToString:@"fliphorizontal"]) {
140 | transitionStyle = UIModalTransitionStyleFlipHorizontal;
141 | } else if ([[browserOptions.transitionstyle lowercaseString] isEqualToString:@"crossdissolve"]) {
142 | transitionStyle = UIModalTransitionStyleCrossDissolve;
143 | }
144 | }
145 | self.inAppBrowserViewController.modalTransitionStyle = transitionStyle;
146 |
147 |
148 | // UIWebView options
149 | self.inAppBrowserViewController.webView.scalesPageToFit = browserOptions.enableviewportscale;
150 | self.inAppBrowserViewController.webView.mediaPlaybackRequiresUserAction = browserOptions.mediaplaybackrequiresuseraction;
151 | self.inAppBrowserViewController.webView.allowsInlineMediaPlayback = browserOptions.allowinlinemediaplayback;
152 | if (IsAtLeastiOSVersion(@"6.0")) {
153 | self.inAppBrowserViewController.webView.keyboardDisplayRequiresUserAction = browserOptions.keyboarddisplayrequiresuseraction;
154 | self.inAppBrowserViewController.webView.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering;
155 | }
156 |
157 | if (! browserOptions.hidden) {
158 | if (self.viewController.modalViewController != self.inAppBrowserViewController) {
159 | [self.viewController presentModalViewController:self.inAppBrowserViewController animated:YES];
160 | }
161 | }
162 | [self.inAppBrowserViewController navigateTo:url];
163 | }
164 |
165 | - (void)show:(CDVInvokedUrlCommand*)command
166 | {
167 | if ([self.inAppBrowserViewController isViewLoaded] && self.inAppBrowserViewController.view.window)
168 | return;
169 | [self.viewController presentModalViewController:self.inAppBrowserViewController animated:YES];
170 | }
171 |
172 | - (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options
173 | {
174 | if ([self.commandDelegate URLIsWhitelisted:url]) {
175 | NSURLRequest* request = [NSURLRequest requestWithURL:url];
176 | [self.webView loadRequest:request];
177 | } else { // this assumes the InAppBrowser can be excepted from the white-list
178 | [self openInInAppBrowser:url withOptions:options];
179 | }
180 | }
181 |
182 | - (void)openInSystem:(NSURL*)url
183 | {
184 | if ([[UIApplication sharedApplication] canOpenURL:url]) {
185 | [[UIApplication sharedApplication] openURL:url];
186 | } else { // handle any custom schemes to plugins
187 | [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
188 | }
189 | }
190 |
191 | // This is a helper method for the inject{Script|Style}{Code|File} API calls, which
192 | // provides a consistent method for injecting JavaScript code into the document.
193 | //
194 | // If a wrapper string is supplied, then the source string will be JSON-encoded (adding
195 | // quotes) and wrapped using string formatting. (The wrapper string should have a single
196 | // '%@' marker).
197 | //
198 | // If no wrapper is supplied, then the source string is executed directly.
199 |
200 | - (void)injectDeferredObject:(NSString*)source withWrapper:(NSString*)jsWrapper
201 | {
202 | if (!_injectedIframeBridge) {
203 | _injectedIframeBridge = YES;
204 | // Create an iframe bridge in the new document to communicate with the CDVInAppBrowserViewController
205 | [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:@"(function(d){var e = _cdvIframeBridge = d.createElement('iframe');e.style.display='none';d.body.appendChild(e);})(document)"];
206 | }
207 |
208 | if (jsWrapper != nil) {
209 | NSString* sourceArrayString = [@[source] JSONString];
210 | if (sourceArrayString) {
211 | NSString* sourceString = [sourceArrayString substringWithRange:NSMakeRange(1, [sourceArrayString length] - 2)];
212 | NSString* jsToInject = [NSString stringWithFormat:jsWrapper, sourceString];
213 | [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:jsToInject];
214 | }
215 | } else {
216 | [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:source];
217 | }
218 | }
219 |
220 | - (void)injectScriptCode:(CDVInvokedUrlCommand*)command
221 | {
222 | NSString* jsWrapper = nil;
223 |
224 | if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
225 | jsWrapper = [NSString stringWithFormat:@"_cdvIframeBridge.src='gap-iab://%@/'+window.escape(JSON.stringify([eval(%%@)]));", command.callbackId];
226 | }
227 | [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
228 | }
229 |
230 | - (void)injectScriptFile:(CDVInvokedUrlCommand*)command
231 | {
232 | NSString* jsWrapper;
233 |
234 | if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
235 | jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('script'); c.src = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
236 | } else {
237 | jsWrapper = @"(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document)";
238 | }
239 | [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
240 | }
241 |
242 | - (void)injectStyleCode:(CDVInvokedUrlCommand*)command
243 | {
244 | NSString* jsWrapper;
245 |
246 | if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
247 | jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('style'); c.innerHTML = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
248 | } else {
249 | jsWrapper = @"(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document)";
250 | }
251 | [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
252 | }
253 |
254 | - (void)injectStyleFile:(CDVInvokedUrlCommand*)command
255 | {
256 | NSString* jsWrapper;
257 |
258 | if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
259 | jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
260 | } else {
261 | jsWrapper = @"(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)";
262 | }
263 | [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
264 | }
265 |
266 | /**
267 | * The iframe bridge provided for the InAppBrowser is capable of executing any oustanding callback belonging
268 | * to the InAppBrowser plugin. Care has been taken that other callbacks cannot be triggered, and that no
269 | * other code execution is possible.
270 | *
271 | * To trigger the bridge, the iframe (or any other resource) should attempt to load a url of the form:
272 | *
273 | * gap-iab:///
274 | *
275 | * where is the string id of the callback to trigger (something like "InAppBrowser0123456789")
276 | *
277 | * If present, the path component of the special gap-iab:// url is expected to be a URL-escaped JSON-encoded
278 | * value to pass to the callback. [NSURL path] should take care of the URL-unescaping, and a JSON_EXCEPTION
279 | * is returned if the JSON is invalid.
280 | */
281 | - (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
282 | {
283 | NSURL* url = request.URL;
284 | BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
285 |
286 | // See if the url uses the 'gap-iab' protocol. If so, the host should be the id of a callback to execute,
287 | // and the path, if present, should be a JSON-encoded value to pass to the callback.
288 | if ([[url scheme] isEqualToString:@"gap-iab"]) {
289 | NSString* scriptCallbackId = [url host];
290 | CDVPluginResult* pluginResult = nil;
291 |
292 | if ([scriptCallbackId hasPrefix:@"InAppBrowser"]) {
293 | NSString* scriptResult = [url path];
294 | NSError* __autoreleasing error = nil;
295 |
296 | // The message should be a JSON-encoded array of the result of the script which executed.
297 | if ((scriptResult != nil) && ([scriptResult length] > 1)) {
298 | scriptResult = [scriptResult substringFromIndex:1];
299 | NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[scriptResult dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
300 | if ((error == nil) && [decodedResult isKindOfClass:[NSArray class]]) {
301 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:(NSArray*)decodedResult];
302 | } else {
303 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION];
304 | }
305 | } else {
306 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]];
307 | }
308 | [self.commandDelegate sendPluginResult:pluginResult callbackId:scriptCallbackId];
309 | return NO;
310 | }
311 | } else if ((self.callbackId != nil) && isTopLevelNavigation) {
312 | // Send a loadstart event for each top-level navigation (includes redirects).
313 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
314 | messageAsDictionary:@{@"type":@"loadstart", @"url":[url absoluteString]}];
315 | [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
316 |
317 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
318 | }
319 |
320 | return YES;
321 | }
322 |
323 | - (void)webViewDidStartLoad:(UIWebView*)theWebView
324 | {
325 | _injectedIframeBridge = NO;
326 | }
327 |
328 | - (void)webViewDidFinishLoad:(UIWebView*)theWebView
329 | {
330 | if (self.callbackId != nil) {
331 | // TODO: It would be more useful to return the URL the page is actually on (e.g. if it's been redirected).
332 | NSString* url = [self.inAppBrowserViewController.currentURL absoluteString];
333 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
334 | messageAsDictionary:@{@"type":@"loadstop", @"url":url}];
335 | [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
336 |
337 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
338 | }
339 | }
340 |
341 | - (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
342 | {
343 | if (self.callbackId != nil) {
344 | NSString* url = [self.inAppBrowserViewController.currentURL absoluteString];
345 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
346 | messageAsDictionary:@{@"type":@"loaderror", @"url":url, @"code": [NSNumber numberWithInt:error.code], @"message": error.localizedDescription}];
347 | [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
348 |
349 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
350 | }
351 | }
352 |
353 | - (void)browserExit
354 | {
355 | if (self.callbackId != nil) {
356 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
357 | messageAsDictionary:@{@"type":@"exit"}];
358 | [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
359 |
360 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
361 | }
362 | // Don't recycle the ViewController since it may be consuming a lot of memory.
363 | // Also - this is required for the PDF/User-Agent bug work-around.
364 | self.inAppBrowserViewController = nil;
365 | }
366 |
367 | @end
368 |
369 | #pragma mark CDVInAppBrowserViewController
370 |
371 | @implementation CDVInAppBrowserViewController
372 |
373 | @synthesize currentURL;
374 |
375 | - (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent
376 | {
377 | self = [super init];
378 | if (self != nil) {
379 | _userAgent = userAgent;
380 | _prevUserAgent = prevUserAgent;
381 | _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self];
382 | [self createViews];
383 | }
384 |
385 | return self;
386 | }
387 |
388 | - (void)createViews
389 | {
390 | // We create the views in code for primarily for ease of upgrades and not requiring an external .xib to be included
391 |
392 | CGRect webViewBounds = self.view.bounds;
393 |
394 | webViewBounds.size.height -= FOOTER_HEIGHT;
395 |
396 | self.webView = [[UIWebView alloc] initWithFrame:webViewBounds];
397 | self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
398 |
399 | [self.view addSubview:self.webView];
400 | [self.view sendSubviewToBack:self.webView];
401 |
402 | self.webView.delegate = _webViewDelegate;
403 | self.webView.backgroundColor = [UIColor whiteColor];
404 |
405 | self.webView.clearsContextBeforeDrawing = YES;
406 | self.webView.clipsToBounds = YES;
407 | self.webView.contentMode = UIViewContentModeScaleToFill;
408 | self.webView.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
409 | self.webView.multipleTouchEnabled = YES;
410 | self.webView.opaque = YES;
411 | self.webView.scalesPageToFit = NO;
412 | self.webView.userInteractionEnabled = YES;
413 |
414 | self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
415 | self.spinner.alpha = 1.000;
416 | self.spinner.autoresizesSubviews = YES;
417 | self.spinner.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
418 | self.spinner.clearsContextBeforeDrawing = NO;
419 | self.spinner.clipsToBounds = NO;
420 | self.spinner.contentMode = UIViewContentModeScaleToFill;
421 | self.spinner.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
422 | self.spinner.frame = CGRectMake(454.0, 231.0, 20.0, 20.0);
423 | self.spinner.hidden = YES;
424 | self.spinner.hidesWhenStopped = YES;
425 | self.spinner.multipleTouchEnabled = NO;
426 | self.spinner.opaque = NO;
427 | self.spinner.userInteractionEnabled = NO;
428 | [self.spinner stopAnimating];
429 |
430 | self.closeButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(close)];
431 | self.closeButton.enabled = YES;
432 |
433 | UIBarButtonItem* flexibleSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
434 |
435 | UIBarButtonItem* fixedSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
436 | fixedSpaceButton.width = 20;
437 |
438 | self.toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, (self.view.bounds.size.height - TOOLBAR_HEIGHT), self.view.bounds.size.width, TOOLBAR_HEIGHT)];
439 | self.toolbar.alpha = 1.000;
440 | self.toolbar.autoresizesSubviews = YES;
441 | self.toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
442 | self.toolbar.barStyle = UIBarStyleBlackOpaque;
443 | self.toolbar.clearsContextBeforeDrawing = NO;
444 | self.toolbar.clipsToBounds = NO;
445 | self.toolbar.contentMode = UIViewContentModeScaleToFill;
446 | self.toolbar.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
447 | self.toolbar.hidden = NO;
448 | self.toolbar.multipleTouchEnabled = NO;
449 | self.toolbar.opaque = NO;
450 | self.toolbar.userInteractionEnabled = YES;
451 |
452 | CGFloat labelInset = 5.0;
453 | self.addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelInset, (self.view.bounds.size.height - FOOTER_HEIGHT), self.view.bounds.size.width - labelInset, LOCATIONBAR_HEIGHT)];
454 | self.addressLabel.adjustsFontSizeToFitWidth = NO;
455 | self.addressLabel.alpha = 1.000;
456 | self.addressLabel.autoresizesSubviews = YES;
457 | self.addressLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
458 | self.addressLabel.backgroundColor = [UIColor clearColor];
459 | self.addressLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
460 | self.addressLabel.clearsContextBeforeDrawing = YES;
461 | self.addressLabel.clipsToBounds = YES;
462 | self.addressLabel.contentMode = UIViewContentModeScaleToFill;
463 | self.addressLabel.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
464 | self.addressLabel.enabled = YES;
465 | self.addressLabel.hidden = NO;
466 | self.addressLabel.lineBreakMode = UILineBreakModeTailTruncation;
467 | self.addressLabel.minimumFontSize = 10.000;
468 | self.addressLabel.multipleTouchEnabled = NO;
469 | self.addressLabel.numberOfLines = 1;
470 | self.addressLabel.opaque = NO;
471 | self.addressLabel.shadowOffset = CGSizeMake(0.0, -1.0);
472 | self.addressLabel.text = @"Loading...";
473 | self.addressLabel.textAlignment = UITextAlignmentLeft;
474 | self.addressLabel.textColor = [UIColor colorWithWhite:1.000 alpha:1.000];
475 | self.addressLabel.userInteractionEnabled = NO;
476 |
477 | NSString* frontArrowString = @"►"; // create arrow from Unicode char
478 | self.forwardButton = [[UIBarButtonItem alloc] initWithTitle:frontArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goForward:)];
479 | self.forwardButton.enabled = YES;
480 | self.forwardButton.imageInsets = UIEdgeInsetsZero;
481 |
482 | NSString* backArrowString = @"◄"; // create arrow from Unicode char
483 | self.backButton = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goBack:)];
484 | self.backButton.enabled = YES;
485 | self.backButton.imageInsets = UIEdgeInsetsZero;
486 |
487 | [self.toolbar setItems:@[self.closeButton, flexibleSpaceButton, self.backButton, fixedSpaceButton, self.forwardButton]];
488 |
489 | self.view.backgroundColor = [UIColor grayColor];
490 | [self.view addSubview:self.toolbar];
491 | [self.view addSubview:self.addressLabel];
492 | [self.view addSubview:self.spinner];
493 | }
494 |
495 | - (void)setCloseButtonTitle:(NSString*)title
496 | {
497 | // the advantage of using UIBarButtonSystemItemDone is the system will localize it for you automatically
498 | // but, if you want to set this yourself, knock yourself out (we can't set the title for a system Done button, so we have to create a new one)
499 | self.closeButton = nil;
500 | self.closeButton = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStyleBordered target:self action:@selector(close)];
501 | self.closeButton.enabled = YES;
502 | self.closeButton.tintColor = [UIColor colorWithRed:60.0 / 255.0 green:136.0 / 255.0 blue:230.0 / 255.0 alpha:1];
503 |
504 | NSMutableArray* items = [self.toolbar.items mutableCopy];
505 | [items replaceObjectAtIndex:0 withObject:self.closeButton];
506 | [self.toolbar setItems:items];
507 | }
508 |
509 | - (void)showLocationBar:(BOOL)show
510 | {
511 | CGRect locationbarFrame = self.addressLabel.frame;
512 |
513 | BOOL toolbarVisible = !self.toolbar.hidden;
514 |
515 | // prevent double show/hide
516 | if (show == !(self.addressLabel.hidden)) {
517 | return;
518 | }
519 |
520 | if (show) {
521 | self.addressLabel.hidden = NO;
522 |
523 | if (toolbarVisible) {
524 | // toolBar at the bottom, leave as is
525 | // put locationBar on top of the toolBar
526 |
527 | CGRect webViewBounds = self.view.bounds;
528 | webViewBounds.size.height -= FOOTER_HEIGHT;
529 | self.webView.frame = webViewBounds;
530 |
531 | locationbarFrame.origin.y = webViewBounds.size.height;
532 | self.addressLabel.frame = locationbarFrame;
533 | } else {
534 | // no toolBar, so put locationBar at the bottom
535 |
536 | CGRect webViewBounds = self.view.bounds;
537 | webViewBounds.size.height -= LOCATIONBAR_HEIGHT;
538 | self.webView.frame = webViewBounds;
539 |
540 | locationbarFrame.origin.y = webViewBounds.size.height;
541 | self.addressLabel.frame = locationbarFrame;
542 | }
543 | } else {
544 | self.addressLabel.hidden = YES;
545 |
546 | if (toolbarVisible) {
547 | // locationBar is on top of toolBar, hide locationBar
548 |
549 | // webView take up whole height less toolBar height
550 | CGRect webViewBounds = self.view.bounds;
551 | webViewBounds.size.height -= TOOLBAR_HEIGHT;
552 | self.webView.frame = webViewBounds;
553 | } else {
554 | // no toolBar, expand webView to screen dimensions
555 |
556 | CGRect webViewBounds = self.view.bounds;
557 | self.webView.frame = webViewBounds;
558 | }
559 | }
560 | }
561 |
562 | - (void)showToolBar:(BOOL)show
563 | {
564 | CGRect toolbarFrame = self.toolbar.frame;
565 | CGRect locationbarFrame = self.addressLabel.frame;
566 |
567 | BOOL locationbarVisible = !self.addressLabel.hidden;
568 |
569 | // prevent double show/hide
570 | if (show == !(self.toolbar.hidden)) {
571 | return;
572 | }
573 |
574 | if (show) {
575 | self.toolbar.hidden = NO;
576 |
577 | if (locationbarVisible) {
578 | // locationBar at the bottom, move locationBar up
579 | // put toolBar at the bottom
580 |
581 | CGRect webViewBounds = self.view.bounds;
582 | webViewBounds.size.height -= FOOTER_HEIGHT;
583 | self.webView.frame = webViewBounds;
584 |
585 | locationbarFrame.origin.y = webViewBounds.size.height;
586 | self.addressLabel.frame = locationbarFrame;
587 |
588 | toolbarFrame.origin.y = (webViewBounds.size.height + LOCATIONBAR_HEIGHT);
589 | self.toolbar.frame = toolbarFrame;
590 | } else {
591 | // no locationBar, so put toolBar at the bottom
592 |
593 | CGRect webViewBounds = self.view.bounds;
594 | webViewBounds.size.height -= TOOLBAR_HEIGHT;
595 | self.webView.frame = webViewBounds;
596 |
597 | toolbarFrame.origin.y = webViewBounds.size.height;
598 | self.toolbar.frame = toolbarFrame;
599 | }
600 | } else {
601 | self.toolbar.hidden = YES;
602 |
603 | if (locationbarVisible) {
604 | // locationBar is on top of toolBar, hide toolBar
605 | // put locationBar at the bottom
606 |
607 | // webView take up whole height less locationBar height
608 | CGRect webViewBounds = self.view.bounds;
609 | webViewBounds.size.height -= LOCATIONBAR_HEIGHT;
610 | self.webView.frame = webViewBounds;
611 |
612 | // move locationBar down
613 | locationbarFrame.origin.y = webViewBounds.size.height;
614 | self.addressLabel.frame = locationbarFrame;
615 | } else {
616 | // no locationBar, expand webView to screen dimensions
617 |
618 | CGRect webViewBounds = self.view.bounds;
619 | self.webView.frame = webViewBounds;
620 | }
621 | }
622 | }
623 |
624 | - (void)viewDidLoad
625 | {
626 | [super viewDidLoad];
627 | }
628 |
629 | - (void)viewDidUnload
630 | {
631 | [self.webView loadHTMLString:nil baseURL:nil];
632 | [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
633 | [super viewDidUnload];
634 | }
635 |
636 | - (void)close
637 | {
638 | [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
639 |
640 | if ([self respondsToSelector:@selector(presentingViewController)]) {
641 | [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
642 | } else {
643 | [[self parentViewController] dismissModalViewControllerAnimated:YES];
644 | }
645 |
646 | self.currentURL = nil;
647 |
648 | if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserExit)]) {
649 | [self.navigationDelegate browserExit];
650 | }
651 | }
652 |
653 | - (void)navigateTo:(NSURL*)url
654 | {
655 | NSURLRequest* request = [NSURLRequest requestWithURL:url];
656 |
657 | if (_userAgentLockToken != 0) {
658 | [self.webView loadRequest:request];
659 | } else {
660 | [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
661 | _userAgentLockToken = lockToken;
662 | [CDVUserAgentUtil setUserAgent:_userAgent lockToken:lockToken];
663 | [self.webView loadRequest:request];
664 | }];
665 | }
666 | }
667 |
668 | - (void)goBack:(id)sender
669 | {
670 | [self.webView goBack];
671 | }
672 |
673 | - (void)goForward:(id)sender
674 | {
675 | [self.webView goForward];
676 | }
677 |
678 | #pragma mark UIWebViewDelegate
679 |
680 | - (void)webViewDidStartLoad:(UIWebView*)theWebView
681 | {
682 | // loading url, start spinner, update back/forward
683 |
684 | self.addressLabel.text = @"Loading...";
685 | self.backButton.enabled = theWebView.canGoBack;
686 | self.forwardButton.enabled = theWebView.canGoForward;
687 |
688 | [self.spinner startAnimating];
689 |
690 | return [self.navigationDelegate webViewDidStartLoad:theWebView];
691 | }
692 |
693 | - (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
694 | {
695 | BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
696 |
697 | if (isTopLevelNavigation) {
698 | self.currentURL = request.URL;
699 | }
700 | return [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
701 | }
702 |
703 | - (void)webViewDidFinishLoad:(UIWebView*)theWebView
704 | {
705 | // update url, stop spinner, update back/forward
706 |
707 | self.addressLabel.text = [self.currentURL absoluteString];
708 | self.backButton.enabled = theWebView.canGoBack;
709 | self.forwardButton.enabled = theWebView.canGoForward;
710 |
711 | [self.spinner stopAnimating];
712 |
713 | // Work around a bug where the first time a PDF is opened, all UIWebViews
714 | // reload their User-Agent from NSUserDefaults.
715 | // This work-around makes the following assumptions:
716 | // 1. The app has only a single Cordova Webview. If not, then the app should
717 | // take it upon themselves to load a PDF in the background as a part of
718 | // their start-up flow.
719 | // 2. That the PDF does not require any additional network requests. We change
720 | // the user-agent here back to that of the CDVViewController, so requests
721 | // from it must pass through its white-list. This *does* break PDFs that
722 | // contain links to other remote PDF/websites.
723 | // More info at https://issues.apache.org/jira/browse/CB-2225
724 | BOOL isPDF = [@"true" isEqualToString :[theWebView stringByEvaluatingJavaScriptFromString:@"document.body==null"]];
725 | if (isPDF) {
726 | [CDVUserAgentUtil setUserAgent:_prevUserAgent lockToken:_userAgentLockToken];
727 | }
728 |
729 | [self.navigationDelegate webViewDidFinishLoad:theWebView];
730 | }
731 |
732 | - (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
733 | {
734 | // log fail message, stop spinner, update back/forward
735 | NSLog(@"webView:didFailLoadWithError - %@", [error localizedDescription]);
736 |
737 | self.backButton.enabled = theWebView.canGoBack;
738 | self.forwardButton.enabled = theWebView.canGoForward;
739 | [self.spinner stopAnimating];
740 |
741 | self.addressLabel.text = @"Load Error";
742 |
743 | [self.navigationDelegate webView:theWebView didFailLoadWithError:error];
744 | }
745 |
746 | #pragma mark CDVScreenOrientationDelegate
747 |
748 | - (BOOL)shouldAutorotate
749 | {
750 | if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) {
751 | return [self.orientationDelegate shouldAutorotate];
752 | }
753 | return YES;
754 | }
755 |
756 | - (NSUInteger)supportedInterfaceOrientations
757 | {
758 | if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) {
759 | return [self.orientationDelegate supportedInterfaceOrientations];
760 | }
761 |
762 | return 1 << UIInterfaceOrientationPortrait;
763 | }
764 |
765 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
766 | {
767 | if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
768 | return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation];
769 | }
770 |
771 | return YES;
772 | }
773 |
774 | @end
775 |
776 | @implementation CDVInAppBrowserOptions
777 |
778 | - (id)init
779 | {
780 | if (self = [super init]) {
781 | // default values
782 | self.location = YES;
783 | self.toolbar = YES;
784 | self.closebuttoncaption = nil;
785 |
786 | self.enableviewportscale = NO;
787 | self.mediaplaybackrequiresuseraction = NO;
788 | self.allowinlinemediaplayback = NO;
789 | self.keyboarddisplayrequiresuseraction = YES;
790 | self.suppressesincrementalrendering = NO;
791 | self.hidden = NO;
792 | }
793 |
794 | return self;
795 | }
796 |
797 | + (CDVInAppBrowserOptions*)parseOptions:(NSString*)options
798 | {
799 | CDVInAppBrowserOptions* obj = [[CDVInAppBrowserOptions alloc] init];
800 |
801 | // NOTE: this parsing does not handle quotes within values
802 | NSArray* pairs = [options componentsSeparatedByString:@","];
803 |
804 | // parse keys and values, set the properties
805 | for (NSString* pair in pairs) {
806 | NSArray* keyvalue = [pair componentsSeparatedByString:@"="];
807 |
808 | if ([keyvalue count] == 2) {
809 | NSString* key = [[keyvalue objectAtIndex:0] lowercaseString];
810 | NSString* value = [keyvalue objectAtIndex:1];
811 | NSString* value_lc = [value lowercaseString];
812 |
813 | BOOL isBoolean = [value_lc isEqualToString:@"yes"] || [value_lc isEqualToString:@"no"];
814 | NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
815 | [numberFormatter setAllowsFloats:YES];
816 | BOOL isNumber = [numberFormatter numberFromString:value_lc] != nil;
817 |
818 | // set the property according to the key name
819 | if ([obj respondsToSelector:NSSelectorFromString(key)]) {
820 | if (isNumber) {
821 | [obj setValue:[numberFormatter numberFromString:value_lc] forKey:key];
822 | } else if (isBoolean) {
823 | [obj setValue:[NSNumber numberWithBool:[value_lc isEqualToString:@"yes"]] forKey:key];
824 | } else {
825 | [obj setValue:value forKey:key];
826 | }
827 | }
828 | }
829 | }
830 |
831 | return obj;
832 | }
833 |
834 | @end
835 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/src/wp/InAppBrowser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Windows;
4 | using System.Windows.Controls;
5 | using System.Windows.Documents;
6 | using System.Windows.Ink;
7 | using System.Windows.Input;
8 | using System.Windows.Media;
9 | using System.Windows.Media.Animation;
10 | using System.Windows.Shapes;
11 | using Microsoft.Phone.Controls;
12 | using System.Diagnostics;
13 | using System.Runtime.Serialization;
14 | using WPCordovaClassLib.Cordova;
15 | using WPCordovaClassLib.Cordova.Commands;
16 | using WPCordovaClassLib.Cordova.JSON;
17 | using Microsoft.Phone.Shell;
18 | using Microsoft.Phone.Tasks;
19 |
20 | namespace WPCordovaClassLib.Cordova.Commands
21 | {
22 | [DataContract]
23 | public class BrowserOptions
24 | {
25 | [DataMember]
26 | public string url;
27 |
28 | [DataMember]
29 | public bool isGeolocationEnabled;
30 | }
31 |
32 | public class InAppBrowser : BaseCommand
33 | {
34 |
35 | private static WebBrowser browser;
36 | private static ApplicationBarIconButton backButton;
37 | private static ApplicationBarIconButton fwdButton;
38 |
39 | public void open(string options)
40 | {
41 | string[] args = JSON.JsonHelper.Deserialize(options);
42 | //BrowserOptions opts = JSON.JsonHelper.Deserialize(options);
43 | string urlLoc = args[0];
44 | string target = args[1];
45 | /*
46 | _self - opens in the Cordova WebView if url is in the white-list, else it opens in the InAppBrowser
47 | _blank - always open in the InAppBrowser
48 | _system - always open in the system web browser
49 | */
50 | switch (target)
51 | {
52 | case "_blank":
53 | ShowInAppBrowser(urlLoc);
54 | break;
55 | case "_self":
56 | ShowCordovaBrowser(urlLoc);
57 | break;
58 | case "_system":
59 | ShowSystemBrowser(urlLoc);
60 | break;
61 | }
62 |
63 |
64 | }
65 |
66 | private void ShowCordovaBrowser(string url)
67 | {
68 | Uri loc = new Uri(url, UriKind.RelativeOrAbsolute);
69 | Deployment.Current.Dispatcher.BeginInvoke(() =>
70 | {
71 | PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
72 | if (frame != null)
73 | {
74 | PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
75 | if (page != null)
76 | {
77 | CordovaView cView = page.FindName("CordovaView") as CordovaView;
78 | if (cView != null)
79 | {
80 | WebBrowser br = cView.Browser;
81 | br.Navigate(loc);
82 | }
83 | }
84 |
85 | }
86 | });
87 | }
88 |
89 | private void ShowSystemBrowser(string url)
90 | {
91 | WebBrowserTask webBrowserTask = new WebBrowserTask();
92 | webBrowserTask.Uri = new Uri(url, UriKind.Absolute);
93 | webBrowserTask.Show();
94 | }
95 |
96 |
97 | private void ShowInAppBrowser(string url)
98 | {
99 | Uri loc = new Uri(url);
100 |
101 | Deployment.Current.Dispatcher.BeginInvoke(() =>
102 | {
103 | if (browser != null)
104 | {
105 | //browser.IsGeolocationEnabled = opts.isGeolocationEnabled;
106 | browser.Navigate(loc);
107 | }
108 | else
109 | {
110 | PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
111 | if (frame != null)
112 | {
113 | PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
114 |
115 | string baseImageUrl = "Images/";
116 |
117 | if (page != null)
118 | {
119 | Grid grid = page.FindName("LayoutRoot") as Grid;
120 | if (grid != null)
121 | {
122 | browser = new WebBrowser();
123 | browser.IsScriptEnabled = true;
124 | browser.LoadCompleted += new System.Windows.Navigation.LoadCompletedEventHandler(browser_LoadCompleted);
125 |
126 | browser.Navigating += new EventHandler(browser_Navigating);
127 | browser.NavigationFailed += new System.Windows.Navigation.NavigationFailedEventHandler(browser_NavigationFailed);
128 | browser.Navigated += new EventHandler(browser_Navigated);
129 | browser.Navigate(loc);
130 | //browser.IsGeolocationEnabled = opts.isGeolocationEnabled;
131 | grid.Children.Add(browser);
132 | }
133 |
134 | ApplicationBar bar = new ApplicationBar();
135 | bar.BackgroundColor = Colors.Gray;
136 | bar.IsMenuEnabled = false;
137 |
138 | backButton = new ApplicationBarIconButton();
139 | backButton.Text = "Back";
140 |
141 | backButton.IconUri = new Uri(baseImageUrl + "appbar.back.rest.png", UriKind.Relative);
142 | backButton.Click += new EventHandler(backButton_Click);
143 | bar.Buttons.Add(backButton);
144 |
145 |
146 | fwdButton = new ApplicationBarIconButton();
147 | fwdButton.Text = "Forward";
148 | fwdButton.IconUri = new Uri(baseImageUrl + "appbar.next.rest.png", UriKind.Relative);
149 | fwdButton.Click += new EventHandler(fwdButton_Click);
150 | bar.Buttons.Add(fwdButton);
151 |
152 | ApplicationBarIconButton closeBtn = new ApplicationBarIconButton();
153 | closeBtn.Text = "Close";
154 | closeBtn.IconUri = new Uri(baseImageUrl + "appbar.close.rest.png", UriKind.Relative);
155 | closeBtn.Click += new EventHandler(closeBtn_Click);
156 | bar.Buttons.Add(closeBtn);
157 |
158 | page.ApplicationBar = bar;
159 | }
160 |
161 | }
162 | }
163 | });
164 | }
165 |
166 | void browser_LoadCompleted(object sender, System.Windows.Navigation.NavigationEventArgs e)
167 | {
168 |
169 | }
170 |
171 | void fwdButton_Click(object sender, EventArgs e)
172 | {
173 | if (browser != null)
174 | {
175 | try
176 | {
177 | #if WP8
178 | browser.GoForward();
179 | #else
180 | browser.InvokeScript("execScript", "history.forward();");
181 | #endif
182 | }
183 | catch (Exception)
184 | {
185 |
186 | }
187 | }
188 | }
189 |
190 | void backButton_Click(object sender, EventArgs e)
191 | {
192 | if (browser != null)
193 | {
194 | try
195 | {
196 | #if WP8
197 | browser.GoBack();
198 | #else
199 | browser.InvokeScript("execScript", "history.back();");
200 | #endif
201 | }
202 | catch (Exception)
203 | {
204 |
205 | }
206 | }
207 | }
208 |
209 | void closeBtn_Click(object sender, EventArgs e)
210 | {
211 | this.close();
212 | }
213 |
214 |
215 | public void close(string options = "")
216 | {
217 | if (browser != null)
218 | {
219 | Deployment.Current.Dispatcher.BeginInvoke(() =>
220 | {
221 | PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
222 | if (frame != null)
223 | {
224 | PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
225 | if (page != null)
226 | {
227 | Grid grid = page.FindName("LayoutRoot") as Grid;
228 | if (grid != null)
229 | {
230 | grid.Children.Remove(browser);
231 | }
232 | page.ApplicationBar = null;
233 | }
234 | }
235 | browser = null;
236 | string message = "{\"type\":\"exit\"}";
237 | PluginResult result = new PluginResult(PluginResult.Status.OK, message);
238 | result.KeepCallback = false;
239 | this.DispatchCommandResult(result);
240 | });
241 | }
242 | }
243 |
244 | void browser_Navigated(object sender, System.Windows.Navigation.NavigationEventArgs e)
245 | {
246 | #if WP8
247 | if (browser != null)
248 | {
249 | backButton.IsEnabled = browser.CanGoBack;
250 | fwdButton.IsEnabled = browser.CanGoForward;
251 |
252 | }
253 | #endif
254 | string message = "{\"type\":\"loadstop\", \"url\":\"" + e.Uri.AbsoluteUri + "\"}";
255 | PluginResult result = new PluginResult(PluginResult.Status.OK, message);
256 | result.KeepCallback = true;
257 | this.DispatchCommandResult(result);
258 | }
259 |
260 | void browser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
261 | {
262 | string message = "{\"type\":\"error\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
263 | PluginResult result = new PluginResult(PluginResult.Status.ERROR, message);
264 | result.KeepCallback = true;
265 | this.DispatchCommandResult(result);
266 | }
267 |
268 | void browser_Navigating(object sender, NavigatingEventArgs e)
269 | {
270 | string message = "{\"type\":\"loadstart\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
271 | PluginResult result = new PluginResult(PluginResult.Status.OK, message);
272 | result.KeepCallback = true;
273 | this.DispatchCommandResult(result);
274 | }
275 |
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/test/cordova-incl.js:
--------------------------------------------------------------------------------
1 | /*
2 | *
3 | * Licensed to the Apache Software Foundation (ASF) under one
4 | * or more contributor license agreements. See the NOTICE file
5 | * distributed with this work for additional information
6 | * regarding copyright ownership. The ASF licenses this file
7 | * to you under the Apache License, Version 2.0 (the
8 | * "License"); you may not use this file except in compliance
9 | * with the License. You may obtain a copy of the License at
10 | *
11 | * http://www.apache.org/licenses/LICENSE-2.0
12 | *
13 | * Unless required by applicable law or agreed to in writing,
14 | * software distributed under the License is distributed on an
15 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | * KIND, either express or implied. See the License for the
17 | * specific language governing permissions and limitations
18 | * under the License.
19 | *
20 | */
21 |
22 | var PLAT;
23 | if (/Android/.exec(navigator.userAgent)) {
24 | PLAT = 'android';
25 | } else if (/(iPad)|(iPhone)|(iPod)/.exec(navigator.userAgent)) {
26 | PLAT = 'ios';
27 | } else if (/(BB10)|(PlayBook)|(BlackBerry)/.exec(navigator.userAgent)) {
28 | PLAT = 'blackberry';
29 | }
30 |
31 | var scripts = document.getElementsByTagName('script');
32 | var currentPath = scripts[scripts.length - 1].src;
33 | var platformCordovaPath = currentPath.replace("cordova-incl.js", "cordova." + PLAT + ".js");
34 | var normalCordovaPath = currentPath.replace("cordova-incl.js", "cordova.js");
35 | var cordovaPath = normalCordovaPath;
36 |
37 | if (PLAT) {
38 | // XHR to local file is an error on some platforms, windowsphone for one
39 | try {
40 | var xhr = new XMLHttpRequest();
41 | xhr.open("GET", platformCordovaPath, false);
42 | xhr.onreadystatechange = function() {
43 |
44 | if (this.readyState == this.DONE && this.responseText.length > 0) {
45 | if(parseInt(this.status) >= 400){
46 | cordovaPath = normalCordovaPath;
47 | }else{
48 | cordovaPath = platformCordovaPath;
49 | }
50 | }
51 | };
52 | xhr.send(null);
53 | }
54 | catch(e){
55 | cordovaPath = normalCordovaPath;
56 | } // access denied!
57 | }
58 |
59 | if (!window._doNotWriteCordovaScript) {
60 | document.write('');
61 | }
62 |
63 | function backHome() {
64 | if (window.device && device.platform && device.platform.toLowerCase() == 'android') {
65 | navigator.app.backHistory();
66 | }
67 | else {
68 | window.history.go(-1);
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/test/inappbrowser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Cordova Mobile Spec
29 |
30 |
31 |
32 |
33 |
200 |
201 |
202 |
203 |
InAppBrowser
204 |
205 | Make sure http://www.google.com is white listed.
206 | Make sure http://www.apple.com is not in the white list. In iOS, starred * tests will leave the app with no way to return.
207 |
User-Agent:
208 |
209 |
Back
210 |
Local URL
211 |
Default: CordovaWebView
212 |
Target=Self: CordovaWebView
213 |
Target=System: Error
214 |
Target=Blank: InAppBrowser
215 |
Target=Random: InAppBrowser
216 |
Target=Random, no location bar: InAppBrowser
217 |
White Listed URL
218 |
Default: CordovaWebView*
219 |
Target=Self: CordovaWebView*
220 |
Target=System: System Browser
221 |
Target=Blank: InAppBrowser
222 |
Target=Random: InAppBrowser
223 |
Target=Random, no location bar: InAppBrowser
224 |
Non White Listed URL
225 |
Default: InAppBrowser
226 |
Target=Self: InAppBrowser
227 |
Target=System: System
228 |
Target=Blank: InAppBrowser
229 |
Target=Random: InAppBrowser
230 |
Target=Random, no location bar: InAppBrowser
231 |
Page with redirect
232 |
http://google.com (should 301)
233 |
http://www.zhihu.com/answer/16714076 (should 302)
234 |
PDF URL
235 |
Remote URL
236 |
Local URL
237 |
INVALID URL
238 |
Invalid Scheme
239 |
Invalid Host
240 |
Missing File
241 |
CSS / JS Injection
242 |
Original Document
243 |
CSS File Injection
244 |
CSS File Injection (CB)
245 |
CSS Literal Injection
246 |
CSS Literal Injection (CB)
247 |
Script File Injection
248 |
Script File Injection (CB)
249 |
Script Literal Injection
250 |
Script Literal Injection (CB)
251 |
Open Hidden
252 |
google.com hidden
253 |
show hidden
254 |
close hidden
255 |
google.com not hidden
256 |
Back
257 |
258 |
259 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/test/inappbrowser/inject.css:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | #style-update-file {
20 | display: block !important;
21 | }
22 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/test/inappbrowser/inject.html:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Cordova Mobile Spec
29 |
30 |
31 |
32 |
InAppBrowser - Script / Style Injection Test
33 |
Style updated from file
34 |
Style updated from literal
35 |
36 |
43 |
44 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/test/inappbrowser/inject.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Licensed to the Apache Software Foundation (ASF) under one
3 | * or more contributor license agreements. See the NOTICE file
4 | * distributed with this work for additional information
5 | * regarding copyright ownership. The ASF licenses this file
6 | * to you under the Apache License, Version 2.0 (the
7 | * "License"); you may not use this file except in compliance
8 | * with the License. You may obtain a copy of the License at
9 | *
10 | * http://www.apache.org/licenses/LICENSE-2.0
11 | *
12 | * Unless required by applicable law or agreed to in writing,
13 | * software distributed under the License is distributed on an
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | * KIND, either express or implied. See the License for the
16 | * specific language governing permissions and limitations
17 | * under the License.
18 | */
19 | var d = document.getElementById("header")
20 | d.innerHTML = "Script file successfully injected";
21 |
--------------------------------------------------------------------------------
/plugins/org.apache.cordova.core.inappbrowser/test/inappbrowser/local.html:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | Cordova Mobile Spec
29 |
30 |
31 |
32 |
33 |
InAppBrowser - Local URL
34 |
35 | You have successfully loaded a local URL
36 |