Compare commits

...

11 Commits

Author SHA1 Message Date
Steven Gill
496ecc720f [CB-5565] Updated version and RELEASENOTES.md for release 0.2.5 2013-12-04 15:16:31 -08:00
Jesse MacFadyen
30525d545a Merge branch 'dev' of https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser into dev 2013-12-03 13:50:16 -08:00
Jesse MacFadyen
8a549547cb Merge branch 'dev' of https://github.com/Yangtb/cordova-plugin-inappbrowser into dev 2013-12-03 13:42:50 -08:00
Ian Clelland
65ee3d142f Remove merge conflict tag 2013-12-02 20:53:17 -05:00
Jesse MacFadyen
fadb170e50 Merge branch 'dev' of https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser into dev 2013-12-02 17:51:28 -08:00
Jesse MacFadyen
9768ec2ef0 CB-3420 WP feature hidden=yes implemented 2013-12-02 17:50:57 -08:00
Steven Gill
d2b644e5d5 Added ubuntu support 2013-12-02 16:28:32 -08:00
Archana Naik
e5101ba8e1 Added amazon-fireos platform.
Change to use amazon-fireos as the platform if user agent string contains 'cordova-amazon-fireos'
2013-10-30 13:36:04 -07:00
Steven Gill
d49d6ec62f CB-5188: 2013-10-28 12:27:15 -07:00
Maxim Ermilov
a8f79e1fd6 add ubuntu platform 2013-10-16 21:13:03 +04:00
Yangtb
e5862bf820 [CB-4724] fixed UriFormatException
fixed UriFormatException & InvalidOperationException
2013-09-11 16:18:47 +08:00
10 changed files with 1302 additions and 10 deletions

View File

@@ -48,4 +48,10 @@
* CB-3747 Fix back button having different dismiss logic from the close button.
* CB-5021 Expose closeDialog() as a public function and make it safe to call multiple times.
* CB-5021 Make it safe to call close() multiple times
>>>>>>> dev
### 0.2.5 (Dec 4, 2013)
* Remove merge conflict tag
* [CB-4724] fixed UriFormatException
* add ubuntu platform
* CB-3420 WP feature hidden=yes implemented
* Added amazon-fireos platform. Change to use amazon-fireos as the platform if user agent string contains 'cordova-amazon-fireos'

View File

@@ -2,7 +2,7 @@
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
id="org.apache.cordova.inappbrowser"
version="0.2.4">
version="0.2.5">
<name>InAppBrowser</name>
<description>Cordova InAppBrowser Plugin</description>
@@ -31,6 +31,26 @@
<source-file src="src/android/InAppChromeClient.java" target-dir="src/org/apache/cordova/inappbrowser" />
</platform>
<!-- amazon-fireos -->
<platform name="amazon-fireos">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="InAppBrowser">
<param name="android-package" value="org.apache.cordova.inappbrowser.InAppBrowser"/>
</feature>
</config-file>
<source-file src="src/amazon/InAppBrowser.java" target-dir="src/org/apache/cordova/inappbrowser" />
<source-file src="src/amazon/InAppChromeClient.java" target-dir="src/org/apache/cordova/inappbrowser" />
</platform>
<!-- ubuntu -->
<platform name="ubuntu">
<header-file src="src/ubuntu/inappbrowser.h" />
<source-file src="src/ubuntu/inappbrowser.cpp" />
<resource-file src="src/ubuntu/InAppBrowser.qml" />
<resource-file src="src/ubuntu/close.png" />
</platform>
<!-- ios -->
<platform name="ios">
<config-file target="config.xml" parent="/*">

View File

@@ -0,0 +1,769 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova.inappbrowser;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import com.amazon.android.webkit.AmazonWebChromeClient;
import com.amazon.android.webkit.AmazonGeolocationPermissions.Callback;
import com.amazon.android.webkit.AmazonJsPromptResult;
import com.amazon.android.webkit.AmazonWebSettings;
import com.amazon.android.webkit.AmazonWebStorage;
import com.amazon.android.webkit.AmazonWebView;
import com.amazon.android.webkit.AmazonWebViewClient;
import com.amazon.android.webkit.AmazonCookieManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.Config;
import org.apache.cordova.CordovaArgs;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.LOG;
import org.apache.cordova.PluginResult;
import org.apache.cordova.CordovaActivity;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.StringTokenizer;
@SuppressLint("SetJavaScriptEnabled")
public class InAppBrowser extends CordovaPlugin {
private static final String NULL = "null";
protected static final String LOG_TAG = "InAppBrowser";
private static final String SELF = "_self";
private static final String SYSTEM = "_system";
// private static final String BLANK = "_blank";
private static final String EXIT_EVENT = "exit";
private static final String LOCATION = "location";
private static final String HIDDEN = "hidden";
private static final String LOAD_START_EVENT = "loadstart";
private static final String LOAD_STOP_EVENT = "loadstop";
private static final String LOAD_ERROR_EVENT = "loaderror";
private static final String CLOSE_BUTTON_CAPTION = "closebuttoncaption";
private static final String CLEAR_ALL_CACHE = "clearcache";
private static final String CLEAR_SESSION_CACHE = "clearsessioncache";
private Dialog dialog;
private AmazonWebView inAppWebView;
private EditText edittext;
private CallbackContext callbackContext;
private boolean showLocationBar = true;
private boolean openWindowHidden = false;
private String buttonLabel = "Done";
private boolean clearAllCache= false;
private boolean clearSessionCache=false;
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackId The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
if (action.equals("open")) {
this.callbackContext = callbackContext;
final String url = args.getString(0);
String t = args.optString(1);
if (t == null || t.equals("") || t.equals(NULL)) {
t = SELF;
}
final String target = t;
final HashMap<String, Boolean> features = parseFeature(args.optString(2));
Log.d(LOG_TAG, "target = " + target);
this.cordova.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
String result = "";
// SELF
if (SELF.equals(target)) {
Log.d(LOG_TAG, "in self");
// load in webview
if (url.startsWith("file://") || url.startsWith("javascript:")
|| Config.isUrlWhiteListed(url)) {
webView.loadUrl(url);
}
//Load the dialer
else if (url.startsWith(AmazonWebView.SCHEME_TEL))
{
try {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(url));
cordova.getActivity().startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
}
}
// load in InAppBrowser
else {
result = showWebPage(url, features);
}
}
// SYSTEM
else if (SYSTEM.equals(target)) {
Log.d(LOG_TAG, "in system");
result = openExternal(url);
}
// BLANK - or anything else
else {
Log.d(LOG_TAG, "in blank");
result = showWebPage(url, features);
}
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
pluginResult.setKeepCallback(true);
callbackContext.sendPluginResult(pluginResult);
}
});
}
else if (action.equals("close")) {
closeDialog();
}
else if (action.equals("injectScriptCode")) {
String jsWrapper = null;
if (args.getBoolean(1)) {
jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
}
injectDeferredObject(args.getString(0), jsWrapper);
}
else if (action.equals("injectScriptFile")) {
String jsWrapper;
if (args.getBoolean(1)) {
jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId());
} else {
jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
}
injectDeferredObject(args.getString(0), jsWrapper);
}
else if (action.equals("injectStyleCode")) {
String jsWrapper;
if (args.getBoolean(1)) {
jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
} else {
jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
}
injectDeferredObject(args.getString(0), jsWrapper);
}
else if (action.equals("injectStyleFile")) {
String jsWrapper;
if (args.getBoolean(1)) {
jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
} else {
jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
}
injectDeferredObject(args.getString(0), jsWrapper);
}
else if (action.equals("show")) {
this.cordova.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
dialog.show();
}
});
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
pluginResult.setKeepCallback(true);
this.callbackContext.sendPluginResult(pluginResult);
}
else {
return false;
}
return true;
}
/**
* Called when the view navigates.
*/
@Override
public void onReset() {
closeDialog();
}
/**
* Called by AccelBroker when listener is to be shut down.
* Stop listener.
*/
public void onDestroy() {
closeDialog();
}
/**
* Inject an object (script or style) into the InAppBrowser AmazonWebView.
*
* This is a helper method for the inject{Script|Style}{Code|File} API calls, which
* provides a consistent method for injecting JavaScript code into the document.
*
* If a wrapper string is supplied, then the source string will be JSON-encoded (adding
* quotes) and wrapped using string formatting. (The wrapper string should have a single
* '%s' marker)
*
* @param source The source object (filename or script/style text) to inject into
* the document.
* @param jsWrapper A JavaScript string to wrap the source string in, so that the object
* is properly injected, or null if the source string is JavaScript text
* which should be executed directly.
*/
private void injectDeferredObject(String source, String jsWrapper) {
final String scriptToInject;
if (jsWrapper != null) {
org.json.JSONArray jsonEsc = new org.json.JSONArray();
jsonEsc.put(source);
String jsonRepr = jsonEsc.toString();
String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
scriptToInject = String.format(jsWrapper, jsonSourceString);
} else {
scriptToInject = source;
}
final String finalScriptToInject = scriptToInject;
// This action will have the side-effect of blurring the currently focused element
this.cordova.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
inAppWebView.loadUrl("javascript:" + finalScriptToInject);
}
});
}
/**
* Put the list of features into a hash map
*
* @param optString
* @return
*/
private HashMap<String, Boolean> parseFeature(String optString) {
if (optString.equals(NULL)) {
return null;
} else {
HashMap<String, Boolean> map = new HashMap<String, Boolean>();
StringTokenizer features = new StringTokenizer(optString, ",");
StringTokenizer option;
while(features.hasMoreElements()) {
option = new StringTokenizer(features.nextToken(), "=");
if (option.hasMoreElements()) {
String key = option.nextToken();
if (key.equalsIgnoreCase(CLOSE_BUTTON_CAPTION)) {
this.buttonLabel = option.nextToken();
} else {
Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE;
map.put(key, value);
}
}
}
return map;
}
}
/**
* Display a new browser with the specified URL.
*
* @param url The url to load.
* @param usePhoneGap Load url in PhoneGap webview
* @return "" if ok, or error message.
*/
public String openExternal(String url) {
try {
Intent intent = null;
intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
this.cordova.getActivity().startActivity(intent);
return "";
} catch (android.content.ActivityNotFoundException e) {
Log.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString());
return e.toString();
}
}
/**
* Closes the dialog
*/
public void closeDialog() {
this.cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
if (dialog != null) {
dialog.dismiss();
}
}
});
}
/**
* Checks to see if it is possible to go back one page in history, then does so.
*/
private void goBack() {
this.cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
if (InAppBrowser.this.inAppWebView.canGoBack()) {
InAppBrowser.this.inAppWebView.goBack();
}
}
});
}
/**
* Checks to see if it is possible to go forward one page in history, then does so.
*/
private void goForward() {
this.cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
if (InAppBrowser.this.inAppWebView.canGoForward()) {
InAppBrowser.this.inAppWebView.goForward();
}
}
});
}
/**
* Navigate to the new page
*
* @param url to load
*/
private void navigate(final String url) {
InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0);
this.cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
if (!url.startsWith("http") && !url.startsWith("file:")) {
InAppBrowser.this.inAppWebView.loadUrl("http://" + url);
} else {
InAppBrowser.this.inAppWebView.loadUrl(url);
}
InAppBrowser.this.inAppWebView.requestFocus();
}
});
}
/**
* Should we show the location bar?
*
* @return boolean
*/
private boolean getShowLocationBar() {
return this.showLocationBar;
}
/**
* Display a new browser with the specified URL.
*
* @param url The url to load.
* @param jsonObject
*/
public String showWebPage(final String url, HashMap<String, Boolean> features) {
// Determine if we should hide the location bar.
showLocationBar = true;
openWindowHidden = false;
if (features != null) {
Boolean show = features.get(LOCATION);
if (show != null) {
showLocationBar = show.booleanValue();
}
Boolean hidden = features.get(HIDDEN);
if (hidden != null) {
openWindowHidden = hidden.booleanValue();
}
Boolean cache = features.get(CLEAR_ALL_CACHE);
if (cache != null) {
clearAllCache = cache.booleanValue();
} else {
cache = features.get(CLEAR_SESSION_CACHE);
if (cache != null) {
clearSessionCache = cache.booleanValue();
}
}
}
final CordovaWebView thatWebView = this.webView;
// Create dialog in new thread
Runnable runnable = new Runnable() {
/**
* Convert our DIP units to Pixels
*
* @return int
*/
private int dpToPixels(int dipValue) {
int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,
(float) dipValue,
cordova.getActivity().getResources().getDisplayMetrics()
);
return value;
}
public void run() {
// Let's create the main dialog
dialog = new Dialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar);
dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog;
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setCancelable(true);
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
closeDialog();
}
});
// Main container layout
LinearLayout main = new LinearLayout(cordova.getActivity());
main.setOrientation(LinearLayout.VERTICAL);
// Toolbar layout
RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());
//Please, no more black!
toolbar.setBackgroundColor(android.graphics.Color.LTGRAY);
toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)));
toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
toolbar.setHorizontalGravity(Gravity.LEFT);
toolbar.setVerticalGravity(Gravity.TOP);
// Action Button Container layout
RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity());
actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
actionButtonContainer.setHorizontalGravity(Gravity.LEFT);
actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
actionButtonContainer.setId(1);
// Back button
Button back = new Button(cordova.getActivity());
RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT);
back.setLayoutParams(backLayoutParams);
back.setContentDescription("Back Button");
back.setId(2);
back.setText("<");
back.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
goBack();
}
});
// Forward button
Button forward = new Button(cordova.getActivity());
RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2);
forward.setLayoutParams(forwardLayoutParams);
forward.setContentDescription("Forward Button");
forward.setId(3);
forward.setText(">");
forward.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
goForward();
}
});
// Edit Text Box
edittext = new EditText(cordova.getActivity());
RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1);
textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5);
edittext.setLayoutParams(textLayoutParams);
edittext.setId(4);
edittext.setSingleLine(true);
edittext.setText(url);
edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
edittext.setImeOptions(EditorInfo.IME_ACTION_GO);
edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE
edittext.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
navigate(edittext.getText().toString());
return true;
}
return false;
}
});
// Close button
Button close = new Button(cordova.getActivity());
RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
close.setLayoutParams(closeLayoutParams);
forward.setContentDescription("Close Button");
close.setId(5);
close.setText(buttonLabel);
close.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
closeDialog();
}
});
// WebView
inAppWebView = new AmazonWebView(cordova.getActivity());
CordovaActivity app = (CordovaActivity) cordova.getActivity();
cordova.getFactory().initializeWebView(inAppWebView, 0x00FF00, false, null);
inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView));
AmazonWebViewClient client = new InAppBrowserClient(thatWebView, edittext);
inAppWebView.setWebViewClient(client);
AmazonWebSettings settings = inAppWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setBuiltInZoomControls(true);
settings.setPluginState(com.amazon.android.webkit.AmazonWebSettings.PluginState.ON);
//Toggle whether this is enabled or not!
Bundle appSettings = cordova.getActivity().getIntent().getExtras();
boolean enableDatabase = appSettings == null ? true : appSettings.getBoolean("InAppBrowserStorageEnabled", true);
if (enableDatabase) {
String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath();
settings.setDatabasePath(databasePath);
settings.setDatabaseEnabled(true);
}
settings.setDomStorageEnabled(true);
if (clearAllCache) {
AmazonCookieManager.getInstance().removeAllCookie();
} else if (clearSessionCache) {
AmazonCookieManager.getInstance().removeSessionCookie();
}
inAppWebView.loadUrl(url);
inAppWebView.setId(6);
inAppWebView.getSettings().setLoadWithOverviewMode(true);
inAppWebView.getSettings().setUseWideViewPort(true);
inAppWebView.requestFocus();
inAppWebView.requestFocusFromTouch();
// Add the back and forward buttons to our action button container layout
actionButtonContainer.addView(back);
actionButtonContainer.addView(forward);
// Add the views to our toolbar
toolbar.addView(actionButtonContainer);
toolbar.addView(edittext);
toolbar.addView(close);
// Don't add the toolbar if its been disabled
if (getShowLocationBar()) {
// Add our toolbar to our main view/layout
main.addView(toolbar);
}
// Add our webview to our main view/layout
main.addView(inAppWebView);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.MATCH_PARENT;
dialog.setContentView(main);
dialog.show();
dialog.getWindow().setAttributes(lp);
// the goal of openhidden is to load the url and not display it
// Show() needs to be called to cause the URL to be loaded
if(openWindowHidden) {
dialog.hide();
}
}
};
this.cordova.getActivity().runOnUiThread(runnable);
return "";
}
/**
* Create a new plugin success result and send it back to JavaScript
*
* @param obj a JSONObject contain event payload information
*/
private void sendUpdate(JSONObject obj, boolean keepCallback) {
sendUpdate(obj, keepCallback, PluginResult.Status.OK);
}
/**
* Create a new plugin result and send it back to JavaScript
*
* @param obj a JSONObject contain event payload information
* @param status the status code to return to the JavaScript environment
*/
private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
if (callbackContext != null) {
PluginResult result = new PluginResult(status, obj);
result.setKeepCallback(keepCallback);
callbackContext.sendPluginResult(result);
if (!keepCallback) {
callbackContext = null;
}
}
}
/**
* The webview client receives notifications about appView
*/
public class InAppBrowserClient extends AmazonWebViewClient {
EditText edittext;
CordovaWebView webView;
/**
* Constructor.
*
* @param mContext
* @param edittext
*/
public InAppBrowserClient(CordovaWebView webView, EditText mEditText) {
this.webView = webView;
this.edittext = mEditText;
}
/**
* Notify the host application that a page has started loading.
*
* @param view The webview initiating the callback.
* @param url The url of the page.
*/
@Override
public void onPageStarted(AmazonWebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
String newloc = "";
if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
newloc = url;
}
// If dialing phone (tel:5551212)
else if (url.startsWith(AmazonWebView.SCHEME_TEL)) {
try {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(url));
cordova.getActivity().startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
}
}
else if (url.startsWith("geo:") || url.startsWith(AmazonWebView.SCHEME_MAILTO) || url.startsWith("market:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
cordova.getActivity().startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString());
}
}
// If sms:5551212?body=This is the message
else if (url.startsWith("sms:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
// Get address
String address = null;
int parmIndex = url.indexOf('?');
if (parmIndex == -1) {
address = url.substring(4);
}
else {
address = url.substring(4, parmIndex);
// If body, then set sms body
Uri uri = Uri.parse(url);
String query = uri.getQuery();
if (query != null) {
if (query.startsWith("body=")) {
intent.putExtra("sms_body", query.substring(5));
}
}
}
intent.setData(Uri.parse("sms:" + address));
intent.putExtra("address", address);
intent.setType("vnd.android-dir/mms-sms");
cordova.getActivity().startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
}
}
else {
newloc = "http://" + url;
}
if (!newloc.equals(edittext.getText().toString())) {
edittext.setText(newloc);
}
try {
JSONObject obj = new JSONObject();
obj.put("type", LOAD_START_EVENT);
obj.put("url", newloc);
sendUpdate(obj, true);
} catch (JSONException ex) {
Log.d(LOG_TAG, "Should never happen");
}
}
public void onPageFinished(AmazonWebView view, String url) {
super.onPageFinished(view, url);
try {
JSONObject obj = new JSONObject();
obj.put("type", LOAD_STOP_EVENT);
obj.put("url", url);
sendUpdate(obj, true);
} catch (JSONException ex) {
Log.d(LOG_TAG, "Should never happen");
}
}
public void onReceivedError(AmazonWebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
try {
JSONObject obj = new JSONObject();
obj.put("type", LOAD_ERROR_EVENT);
obj.put("url", failingUrl);
obj.put("code", errorCode);
obj.put("message", description);
sendUpdate(obj, true, PluginResult.Status.ERROR);
} catch (JSONException ex) {
Log.d(LOG_TAG, "Should never happen");
}
}
}
}

View File

@@ -0,0 +1,128 @@
package org.apache.cordova.inappbrowser;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.LOG;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import com.amazon.android.webkit.AmazonWebChromeClient;
import com.amazon.android.webkit.AmazonGeolocationPermissions.Callback;
import com.amazon.android.webkit.AmazonJsPromptResult;
import com.amazon.android.webkit.AmazonWebStorage;
import com.amazon.android.webkit.AmazonWebView;
import com.amazon.android.webkit.AmazonWebViewClient;
public class InAppChromeClient extends AmazonWebChromeClient {
private CordovaWebView webView;
private String LOG_TAG = "InAppChromeClient";
private long MAX_QUOTA = 100 * 1024 * 1024;
public InAppChromeClient(CordovaWebView webView) {
super();
this.webView = webView;
}
/**
* Handle database quota exceeded notification.
*
* @param url
* @param databaseIdentifier
* @param currentQuota
* @param estimatedSize
* @param totalUsedQuota
* @param quotaUpdater
*/
@Override
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
long totalUsedQuota, AmazonWebStorage.QuotaUpdater quotaUpdater)
{
LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
if (estimatedSize < MAX_QUOTA)
{
//increase for 1Mb
long newQuota = estimatedSize;
LOG.d(LOG_TAG, "calling quotaUpdater.updateQuota newQuota: %d", newQuota);
quotaUpdater.updateQuota(newQuota);
}
else
{
// Set the quota to whatever it is and force an error
// TODO: get docs on how to handle this properly
quotaUpdater.updateQuota(currentQuota);
}
}
/**
* Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin.
*
* @param origin
* @param callback
*/
@Override
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
super.onGeolocationPermissionsShowPrompt(origin, callback);
callback.invoke(origin, true, false);
}
/**
* Tell the client to display a prompt dialog to the user.
* If the client returns true, WebView will assume that the client will
* handle the prompt dialog and call the appropriate JsPromptResult method.
*
* The prompt bridge provided for the InAppBrowser is capable of executing any
* oustanding callback belonging to the InAppBrowser plugin. Care has been
* taken that other callbacks cannot be triggered, and that no other code
* execution is possible.
*
* To trigger the bridge, the prompt default value should be of the form:
*
* gap-iab://<callbackId>
*
* where <callbackId> is the string id of the callback to trigger (something
* like "InAppBrowser0123456789")
*
* If present, the prompt message is expected to be a JSON-encoded value to
* pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid.
*
* @param view
* @param url
* @param message
* @param defaultValue
* @param result
*/
@Override
public boolean onJsPrompt(AmazonWebView view, String url, String message, String defaultValue, AmazonJsPromptResult result) {
// See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute.
if (defaultValue != null && defaultValue.startsWith("gap")) {
if(defaultValue.startsWith("gap-iab://")) {
PluginResult scriptResult;
String scriptCallbackId = defaultValue.substring(10);
if (scriptCallbackId.startsWith("InAppBrowser")) {
if(message == null || message.length() == 0) {
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray());
} else {
try {
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray(message));
} catch(JSONException e) {
scriptResult = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
}
}
this.webView.sendPluginResult(scriptResult, scriptCallbackId);
result.confirm("");
return true;
}
}
else
{
// Anything else with a gap: prefix should get this message
LOG.w(LOG_TAG, "InAppBrowser does not support Cordova API calls: " + url + " " + defaultValue);
result.cancel();
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,69 @@
/*
*
* Copyright 2013 Canonical Ltd.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
import QtQuick 2.0
import QtWebKit 3.0
import Ubuntu.Components.Popups 0.1
import Ubuntu.Components 0.1
Rectangle {
anchors.fill: parent
id: inappbrowser
property string url1
Rectangle {
border.color: "black"
width: parent.width
height: urlEntry.height
color: "gray"
TextInput {
id: urlEntry
width: parent.width - closeButton.width
text: url1
activeFocusOnPress: false
}
Image {
id: closeButton
width: height
x: parent.width - width
height: parent.height
source: "close.png"
MouseArea {
anchors.fill: parent
onClicked: {
root.exec("InAppBrowser", "close", [0, 0])
}
}
}
}
WebView {
width: parent.width
y: urlEntry.height
height: parent.height - y
url: url1
onLoadingChanged: {
if (loadRequest.status) {
root.exec("InAppBrowser", "loadFinished", [loadRequest.status])
}
}
}
}

BIN
src/ubuntu/close.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 461 B

106
src/ubuntu/inappbrowser.cpp Normal file
View File

@@ -0,0 +1,106 @@
/*
*
* Copyright 2013 Canonical Ltd.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#include <QQuickView>
#include <QQuickItem>
#include "inappbrowser.h"
#include <cordova.h>
Inappbrowser::Inappbrowser(Cordova *cordova): CPlugin(cordova), _eventCb(0) {
}
const char code[] = "\
var component, object; \
function createObject() { \
component = Qt.createComponent(%1); \
if (component.status == Component.Ready) \
finishCreation(); \
else \
component.statusChanged.connect(finishCreation); \
} \
function finishCreation() { \
CordovaWrapper.object = component.createObject(root, \
{root: root, cordova: cordova, url1: %2}); \
} \
createObject()";
const char EXIT_EVENT[] = "'exit'";
const char LOADSTART_EVENT[] = "'loadstart'";
const char LOADSTOP_EVENT[] = "'loadstop'";
const char LOADERROR_EVENT[] = "'loaderror'";
void Inappbrowser::open(int cb, int, const QString &url, const QString &windowName, const QString &windowFeatures) {
assert(_eventCb == 0);
_eventCb = cb;
QString path = m_cordova->get_app_dir() + "/../qml/InAppBrowser.qml";
// TODO: relative url
QString qml = QString(code)
.arg(CordovaInternal::format(path)).arg(CordovaInternal::format(url));
m_cordova->execQML(qml);
}
void Inappbrowser::show(int, int) {
m_cordova->execQML("CordovaWrapper.object.visible = true");
}
void Inappbrowser::close(int, int) {
m_cordova->execQML("CordovaWrapper.object.destroy()");
this->callbackWithoutRemove(_eventCb, EXIT_EVENT);
_eventCb = 0;
}
void Inappbrowser::injectStyleFile(int cb, int, const QString&, bool) {
// TODO:
qCritical() << "unimplemented " << __PRETTY_FUNCTION__;
}
void Inappbrowser::injectStyleCode(int cb, int, const QString&, bool) {
// TODO:
qCritical() << "unimplemented " << __PRETTY_FUNCTION__;
}
void Inappbrowser::injectScriptFile(int cb, int, const QString&, bool) {
// TODO:
qCritical() << "unimplemented " << __PRETTY_FUNCTION__;
}
void Inappbrowser::injectScriptCode(int cb, int, const QString&, bool) {
// TODO:
qCritical() << "unimplemented " << __PRETTY_FUNCTION__;
}
void Inappbrowser::loadFinished(int status) {
if (status == 2) {
this->callbackWithoutRemove(_eventCb, LOADERROR_EVENT);
}
if (status == 0) {
this->callbackWithoutRemove(_eventCb, LOADSTART_EVENT);
}
if (status == 3) {
this->callbackWithoutRemove(_eventCb, LOADSTOP_EVENT);
}
}

61
src/ubuntu/inappbrowser.h Normal file
View File

@@ -0,0 +1,61 @@
/*
*
* Copyright 2013 Canonical Ltd.
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#ifndef INAPPBROWSER_H
#define INAPPBROWSER_H
#include <QtCore>
#include <cplugin.h>
class Inappbrowser: public CPlugin {
Q_OBJECT
public:
Inappbrowser(Cordova *cordova);
virtual const QString fullName() override {
return Inappbrowser::fullID();
}
virtual const QString shortName() override {
return "InAppBrowser";
}
static const QString fullID() {
return "InAppBrowser";
}
public slots:
void open(int cb, int, const QString &url, const QString &windowName, const QString &windowFeatures);
void show(int, int);
void close(int, int);
void injectStyleFile(int cb, int, const QString&, bool);
void injectStyleCode(int cb, int, const QString&, bool);
void injectScriptFile(int cb, int, const QString&, bool);
void injectScriptCode(int cb, int, const QString&, bool);
void loadFinished(int status);
private:
int _eventCb;
};
#endif

View File

@@ -36,12 +36,45 @@ namespace WPCordovaClassLib.Cordova.Commands
private static ApplicationBarIconButton backButton;
private static ApplicationBarIconButton fwdButton;
protected ApplicationBar AppBar;
protected bool ShowLocation {get;set;}
protected bool StartHidden {get;set;}
public void open(string options)
{
// reset defaults on ShowLocation + StartHidden features
ShowLocation = true;
StartHidden = false;
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
//BrowserOptions opts = JSON.JsonHelper.Deserialize<BrowserOptions>(options);
string urlLoc = args[0];
string target = args[1];
string featString = args[2];
string[] features = featString.Split(',');
foreach (string str in features)
{
try
{
string[] split = str.Split('=');
switch (split[0])
{
case "location":
ShowLocation = split[1].ToLower().StartsWith("yes");
break;
case "hidden":
StartHidden = split[1].ToLower().StartsWith("yes");
break;
}
}
catch(Exception)
{
// some sort of invalid param was passed, moving on ...
}
}
/*
_self - opens in the Cordova WebView if url is in the white-list, else it opens in the InAppBrowser
_blank - always open in the InAppBrowser
@@ -59,10 +92,99 @@ namespace WPCordovaClassLib.Cordova.Commands
ShowSystemBrowser(urlLoc);
break;
}
}
public void show(string options)
{
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
if (browser != null)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
browser.Visibility = Visibility.Visible;
AppBar.IsVisible = true;
});
}
}
public void injectScriptCode(string options)
{
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
bool bCallback = false;
if (bool.TryParse(args[1], out bCallback)) { };
string callbackId = args[2];
if (browser != null)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
var res = browser.InvokeScript("eval", new string[] { args[0] });
if (bCallback)
{
PluginResult result = new PluginResult(PluginResult.Status.OK, res.ToString());
result.KeepCallback = false;
this.DispatchCommandResult(result);
}
});
}
}
public void injectScriptFile(string options)
{
Debug.WriteLine("Error : Windows Phone org.apache.cordova.inappbrowser does not currently support executeScript");
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
// throw new NotImplementedException("Windows Phone does not currently support 'executeScript'");
}
public void injectStyleCode(string options)
{
Debug.WriteLine("Error : Windows Phone org.apache.cordova.inappbrowser does not currently support insertCSS");
return;
//string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
//bool bCallback = false;
//if (bool.TryParse(args[1], out bCallback)) { };
//string callbackId = args[2];
//if (browser != null)
//{
//Deployment.Current.Dispatcher.BeginInvoke(() =>
//{
// if (bCallback)
// {
// string cssInsertString = "try{(function(doc){var c = '<style>body{background-color:#ffff00;}</style>'; doc.head.innerHTML += c;})(document);}catch(ex){alert('oops : ' + ex.message);}";
// //cssInsertString = cssInsertString.Replace("_VALUE_", args[0]);
// Debug.WriteLine("cssInsertString = " + cssInsertString);
// var res = browser.InvokeScript("eval", new string[] { cssInsertString });
// if (bCallback)
// {
// PluginResult result = new PluginResult(PluginResult.Status.OK, res.ToString());
// result.KeepCallback = false;
// this.DispatchCommandResult(result);
// }
// }
//});
//}
}
public void injectStyleFile(string options)
{
Debug.WriteLine("Error : Windows Phone org.apache.cordova.inappbrowser does not currently support insertCSS");
return;
//string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
//throw new NotImplementedException("Windows Phone does not currently support 'insertCSS'");
}
private void ShowCordovaBrowser(string url)
{
Uri loc = new Uri(url, UriKind.RelativeOrAbsolute);
@@ -96,7 +218,7 @@ namespace WPCordovaClassLib.Cordova.Commands
private void ShowInAppBrowser(string url)
{
Uri loc = new Uri(url);
Uri loc = new Uri(url, UriKind.RelativeOrAbsolute);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
@@ -127,6 +249,12 @@ namespace WPCordovaClassLib.Cordova.Commands
browser.NavigationFailed += new System.Windows.Navigation.NavigationFailedEventHandler(browser_NavigationFailed);
browser.Navigated += new EventHandler<System.Windows.Navigation.NavigationEventArgs>(browser_Navigated);
browser.Navigate(loc);
if (StartHidden)
{
browser.Visibility = Visibility.Collapsed;
}
//browser.IsGeolocationEnabled = opts.isGeolocationEnabled;
grid.Children.Add(browser);
}
@@ -156,6 +284,9 @@ namespace WPCordovaClassLib.Cordova.Commands
bar.Buttons.Add(closeBtn);
page.ApplicationBar = bar;
bar.IsVisible = !StartHidden;
AppBar = bar;
}
}
@@ -251,7 +382,7 @@ namespace WPCordovaClassLib.Cordova.Commands
}
#endif
string message = "{\"type\":\"loadstop\", \"url\":\"" + e.Uri.AbsoluteUri + "\"}";
string message = "{\"type\":\"loadstop\", \"url\":\"" + e.Uri.OriginalString + "\"}";
PluginResult result = new PluginResult(PluginResult.Status.OK, message);
result.KeepCallback = true;
this.DispatchCommandResult(result);
@@ -259,7 +390,7 @@ namespace WPCordovaClassLib.Cordova.Commands
void browser_NavigationFailed(object sender, System.Windows.Navigation.NavigationFailedEventArgs e)
{
string message = "{\"type\":\"error\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
string message = "{\"type\":\"error\",\"url\":\"" + e.Uri.OriginalString + "\"}";
PluginResult result = new PluginResult(PluginResult.Status.ERROR, message);
result.KeepCallback = true;
this.DispatchCommandResult(result);
@@ -267,7 +398,7 @@ namespace WPCordovaClassLib.Cordova.Commands
void browser_Navigating(object sender, NavigatingEventArgs e)
{
string message = "{\"type\":\"loadstart\",\"url\":\"" + e.Uri.AbsoluteUri + "\"}";
string message = "{\"type\":\"loadstart\",\"url\":\"" + e.Uri.OriginalString + "\"}";
PluginResult result = new PluginResult(PluginResult.Status.OK, message);
result.KeepCallback = true;
this.DispatchCommandResult(result);

View File

@@ -20,7 +20,9 @@
*/
var PLAT;
if (/Android/.exec(navigator.userAgent)) {
if (/cordova-amazon-fireos/.exec(navigator.userAgent)) {
PLAT = 'amazon-fireos';
}else if (/Android/.exec(navigator.userAgent)) {
PLAT = 'android';
} else if (/(iPad)|(iPhone)|(iPod)/.exec(navigator.userAgent)) {
PLAT = 'ios';
@@ -61,7 +63,7 @@ if (!window._doNotWriteCordovaScript) {
}
function backHome() {
if (window.device && device.platform && device.platform.toLowerCase() == 'android') {
if (window.device && device.platform && (device.platform.toLowerCase() == 'android' || device.platform.toLowerCase() == 'amazon-fireos')) {
navigator.app.backHistory();
}
else {