Merge pull request #266 from deedubbu/childbrowser

Add ChildBrowser plugin for BlackBerry.
This commit is contained in:
Drew Walters
2011-11-30 08:23:42 -08:00
11 changed files with 1353 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
# ChildBrowser plugin for Phonegap #
The child browser allows you to display external web pages within your PhoneGap application in either a custom browser field or in the device's builtin browser. Web pages accessed through the child browser plugin are not restricted by the whitelist entries in the applications config.xml.
The custom browser view provides advantages over the builtin browser. The custom browser provides the ability to be notified of URL location changes and when the browser is closed. Additionally, with the custom browser it is possible to have a chrome-less view or a view with a navigation bar at the top.
Whether the custom browser or builtin browser is used the user is always brought back to the original application when the user backs out or closes the browser.
## Adding the Plugin to your project ##
Using this plugin requires [BlackBerry PhoneGap](http://github.com/callback/callback-blackberry).
1. To install the plugin, move www/childbrowser.js to your project's www folder and include a reference to it in your html file after phonegap.js.
&lt;script type="text/javascript" charset="utf-8" src="phonegap.js"&gt;&lt;/script&gt;<br/>
&lt;script type="text/javascript" charset="utf-8" src="childbrowser.js"&gt;&lt;/script&gt;
2. Copy the image files folder www/childbrowser to your project's www folder. Note, you need the entire folder not just the images.
3. Add the plugin source to your phonegap.jar in your projects ext folder. The phonegap.jar file is a jar of source code. Open phonegap.jar with your favorite archive manager or use the jar command to create a directory called "src/com/phonegap/plugins/childbrowser" and copy the `.java` files into it.
4. In your projects plugins.xml file add the following line:
&lt;plugin name="ChildBrowser" value="com.phonegap.plugins.childbrowser.ChildBrowser"/&gt;
## Using the plugin ##
The plugin creates the object `window.plugins.childBrowser`. To use, call one of the following, available methods:
### Load a URL in a custom browser ###
showWebPage(url, [options])
Where:
url: The URL to load
options: An object that specifies additional options.
The following values are recognized for the `options` parameter.
* showLocationBar - boolean value indicating whether to show a navigation bar or not. The location bar provides UI elements for back, forward, stop/refresh and URL entry.
Sample use:
window.plugins.childBrowser.showWebPage("http://www.google.com", { showLocationBar: true });
### Close an open custom browser ###
close()
Sample use:
window.plugins.childBrowser.close();
### Receive notification that the custom browser closed ###
onClose()
Sample use:
function closed() {
console.log("The browser window was closed");
}
window.plugins.childBrowser.onClose = closed;
### Receive notification when the browser location changes ###
onLocationChange(location)
Sample use:
function locationChanged(newurl) {
console.log("Location was changed to: " + newurl);
}
window.plugins.childBrowser.onLocationChange = locationChanged;
### Receive notification when an error occurs in the custom browser ###
onError(data)
Sample use:
function error(msg) {
console.log("An error occurred while browsing: " + msg);
}
window.plugins.childBrowser.onError = error;
### Load a URL in the device's builtin browser ###
openExternal(url, [usePhoneGap])
Where:
url: The URL to load
usePhoneGap: Ignored. Maintained for API consistency with Android
Sample use:
window.plugins.childBrowser.openExternal("http://www.google.com");
## Known Issues ##
* Severe rendering issue appearing in landscape mode on Torch 9800 running OS 6. Looks like only portrait resolution is being rendered and the rest is clipped. Issue does not appear in simulators. Not sure at this time if it happens on other devices.
* On OS 5 non-touchscreen devices, the navigation bar does not acquire focus when the pointer is within the navigation bar region. This is a known issue on OS 5 with BrowserField elements. On OS 5, once the BrowserField acquires focus it does not release focus to other UI elements like it should. To move focus to the navigation bar, the user must click the trackpad/roller in the navigation bar region. The user will then be able to focus the individual UI elements.
* When editing the URL in the navigation bar the full URL is not always shown. For some reason it appears that the text box is wrapping text.
## RELEASE NOTES ##
### Nov 30, 2011 ###
* Initial release
## BUGS AND CONTRIBUTIONS ##
## LICENSE ##
PhoneGap is available under *either* the terms of the modified BSD license *or* the
MIT License (2008). As a recipient of PhonegGap, you may choose which
license to receive this code under (except as noted in per-module LICENSE
files). Some modules may not be the copyright of Nitobi. These
modules contain explicit declarations of copyright in both the LICENSE files in
the directories in which they reside and in the code itself. No external
contributions are allowed under licenses which are fundamentally incompatible
with the MIT or BSD licenses that PhoneGap is distributed under.
The text of the MIT and BSD licenses is reproduced below.
---
### The "New" BSD License
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Phonegap/Nitobi nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---
### The MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,179 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2011, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
package com.phonegap.plugins.childbrowser;
import net.rim.blackberry.api.browser.Browser;
import net.rim.blackberry.api.browser.BrowserSession;
import net.rim.device.api.ui.UiApplication;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
import com.phonegap.json4j.JSONArray;
import com.phonegap.json4j.JSONException;
import com.phonegap.json4j.JSONObject;
import com.phonegap.util.Logger;
/**
* The ChildBrowser plug-in. This class provides the ability to load external
* web sites in a custom view or the devices browser. URLs loaded by this
* plugin are not restricted by the whitelist.
*
* showWebPage - Open a URL in a custom browser view.
* close - Close a previously opened custom browser.
* openExternal - Load a URL in the device's browser.
*/
public class ChildBrowser extends Plugin {
protected final static String TAG = "ChildBrowser: ";
// Supported actions
private final static String ACTION_SHOW_WEBPAGE = "showWebPage";
private final static String ACTION_CLOSE = "close";
private final static String ACTION_EXTERNAL = "openExternal";
private String callbackId = null;
private static CustomBrowser browser = null;
private UiApplication uiApp = UiApplication.getUiApplication();
/**
* 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 PluginResult execute(String action, JSONArray args, String callbackId) {
PluginResult result;
if (ACTION_SHOW_WEBPAGE.equals(action)) {
this.callbackId = callbackId;
result = showWebPage(args);
} else if (ACTION_CLOSE.equals(action)) {
result = closeBrowser();
} else if (ACTION_EXTERNAL.equals(action)) {
result = openExternal(args);
} else {
result = new PluginResult(PluginResult.Status.INVALID_ACTION, TAG
+ "Invalid action: " + action);
}
return result;
}
static synchronized void clearBrowser() {
browser = null;
}
/**
* Close a custom browser screen.
*
* @return a PluginResult
*/
private synchronized PluginResult closeBrowser() {
JSONObject obj = null;
if (browser != null && browser.isDisplayed()) {
uiApp.invokeAndWait(new Runnable() {
public void run() {
try {
uiApp.popScreen(browser);
} catch (IllegalArgumentException e) {
Logger.log(ChildBrowser.TAG
+ ": Caught illegal argument exception: "
+ e.getMessage());
}
}
});
browser = null;
obj = new JSONObject();
try {
obj.put("type", CustomBrowser.CLOSE_EVENT);
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
return new PluginResult(PluginResult.Status.OK, obj);
}
/**
* Load an URL in the device's browser application.
*
* @param args
* JSONArry of arguments for the action.
* @return a PluginResult
*/
private PluginResult openExternal(JSONArray args) {
try {
String url = args.getString(0);
BrowserSession session = Browser.getDefaultSession();
session.displayPage(url);
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
return new PluginResult(PluginResult.Status.OK, "");
}
/**
* Display the specified URL in a custom browser screen.
*
* @param args
* JSONArry of arguments for the action.
* @return a PluginResult
*/
private synchronized PluginResult showWebPage(JSONArray args) {
PluginResult result;
if (browser == null) {
try {
boolean showLocationBar = true;
String url = args.getString(0);
JSONObject options = args.getJSONObject(1);
// Determine whether to show or hide navigation bar.
if (options != null) {
showLocationBar = options.optBoolean("showLocationBar",
true);
}
browser = new CustomBrowser(callbackId);
if (browser.init(showLocationBar)) {
uiApp.invokeLater(new Runnable() {
public void run() {
uiApp.pushScreen(browser);
}
});
browser.loadURL(url);
// Must keep the callback for browser URL load and close
// events.
result = new PluginResult(PluginResult.Status.OK, "");
result.setKeepCallback(true);
} else {
result = new PluginResult(PluginResult.Status.ERROR, TAG
+ "Failed to initialize CustomBrowser.");
}
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
} else {
result = new PluginResult(PluginResult.Status.ERROR,
"ChildBrowser is already open.");
}
return result;
}
}

View File

@@ -0,0 +1,274 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2011, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
package com.phonegap.plugins.childbrowser;
import net.rim.device.api.browser.field.ContentReadEvent;
import net.rim.device.api.browser.field2.BrowserField;
import net.rim.device.api.browser.field2.BrowserFieldConfig;
import net.rim.device.api.browser.field2.BrowserFieldHistory;
import net.rim.device.api.browser.field2.BrowserFieldListener;
import net.rim.device.api.script.ScriptEngine;
import net.rim.device.api.system.Characters;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
import org.w3c.dom.Document;
import com.phonegap.PhoneGapExtension;
import com.phonegap.api.PluginResult;
import com.phonegap.json4j.JSONException;
import com.phonegap.json4j.JSONObject;
import com.phonegap.util.Logger;
/**
* A custom browser screen. Contains an optional navigation bar at the top of
* the screen with a scrollable browser field below it.
*/
public class CustomBrowser extends MainScreen {
// Event types that can be returned to caller.
static final int CLOSE_EVENT = 0;
static final int LOCATION_CHANGED_EVENT = 1;
private BrowserField browserField = null;
private String callbackId;
private NavigationBar navBar = null;
/**
* Constructor to create a custom browser which uses all the available
* height and width on the device.
*
* @param callbackId
* the call back identifier.
*/
CustomBrowser(String callbackId) {
super(USE_ALL_HEIGHT | USE_ALL_WIDTH);
this.callbackId = callbackId;
}
/**
* On browser close, notify the caller of the close event.
*
* @see net.rim.device.api.ui.Screen#close()
*/
public void close() {
ChildBrowser.clearBrowser();
try {
JSONObject obj = new JSONObject();
obj.put("type", CLOSE_EVENT);
sendUpdate(obj, false);
} catch (JSONException e) {
Logger.log(ChildBrowser.TAG + "JSONException " + e.getMessage());
}
super.close();
}
/**
* OS5 Non-touchscreen devices have an issue where UI elements cannot regain
* focus after a BrowserField acquires focus when NAVIGATION_MODE_POINTER is
* used. On these devices invokeAction() is called when the navigation wheel
* is clicked outside of the BrowserField. So, in order to set focus to the
* navigation bar on these devices, this method is overridden and focus is
* set to the navigation bar should the event occur.
*
* @see net.rim.device.api.ui.Screen#invokeAction(int)
*/
public boolean invokeAction(final int action) {
if (action == ACTION_INVOKE && navBar != null) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
navBar.setFocus();
}
});
return true;
}
return super.invokeAction(action);
}
/**
* Override the handling of the back button to navigate back in history.
* Also, display the stop load button in the navigation bar if it is active.
*
* @see net.rim.device.api.ui.Screen#keyChar(char, int, int)
*/
protected boolean keyChar(char key, int status, int time) {
boolean consumedByChild = super.keyChar(key, status, time);
if (!consumedByChild && key == Characters.ESCAPE && browserField != null) {
BrowserFieldHistory history = browserField.getHistory();
if (history != null && history.canGoBack()) {
if (navBar != null) {
navBar.setRefreshActive(false);
}
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
browserField.setFocus();
browserField.back();
}
});
return true;
}
}
return consumedByChild;
}
/**
* Sets up the custom browser screen. Initializes configuration and adds
* elements to the screen.
*
* @param showNavBar
* boolean value indicating whether to display navigation bar or
* not.
* @return true if init succeeds, false otherwise.
*/
boolean init(boolean showNavBar) {
// Setup a browser configuration which will produce a BrowserField with
// desired features.
BrowserFieldConfig config = new BrowserFieldConfig();
config.setProperty(BrowserFieldConfig.ALLOW_CS_XHR, Boolean.TRUE);
config.setProperty(BrowserFieldConfig.ENABLE_COOKIES, Boolean.TRUE);
config.setProperty(BrowserFieldConfig.ENABLE_GEARS, Boolean.FALSE);
config.setProperty(BrowserFieldConfig.INITIAL_SCALE, new Float(1.0));
config.setProperty(BrowserFieldConfig.JAVASCRIPT_ENABLED, Boolean.TRUE);
config.setProperty(BrowserFieldConfig.NAVIGATION_MODE,
BrowserFieldConfig.NAVIGATION_MODE_POINTER);
config.setProperty(BrowserFieldConfig.MDS_TRANSCODING_ENABLED,
Boolean.FALSE);
// config.setProperty(BrowserFieldConfig.USER_SCALABLE, Boolean.FALSE);
// config.setProperty(BrowserFieldConfig.VIEWPORT_WIDTH, new Integer(
// Display.getWidth()));
browserField = new BrowserField(config);
// Add a listener to the BrowserField which sends LOCATION_CHANGED_EVENT
// on document load and manages the elements in the navigation bar if it
// is active.
browserField.addListener(new BrowserFieldListener() {
public void documentCreated(BrowserField browserField,
ScriptEngine scriptEngine, Document document)
throws Exception {
if ((browserField != null) && (document != null)) {
String url = document.getBaseURI();
if (navBar != null) {
navBar.setURL(url);
navBar.setRefreshActive(false);
}
try {
JSONObject obj = new JSONObject();
obj.put("type", LOCATION_CHANGED_EVENT);
obj.put("location", url);
sendUpdate(obj, true);
} catch (JSONException e) {
Logger.log(ChildBrowser.TAG + "JSONException: "
+ e.getMessage());
}
}
super.documentCreated(browserField, scriptEngine, document);
};
public void documentLoaded(BrowserField browserField,
Document document) throws Exception {
if (navBar != null) {
navBar.setURL(browserField.getDocumentUrl());
navBar.setRefreshActive(true);
}
super.documentLoaded(browserField, document);
};
public void downloadProgress(BrowserField browserField,
ContentReadEvent event) throws Exception {
super.downloadProgress(browserField, event);
};
});
// Create and add the navigation bar if necessary.
if (showNavBar) {
navBar = new NavigationBar(browserField);
if (!navBar.init()) {
return false;
}
add(navBar);
}
// Create a VerticalFieldManager which will contain the BrowserField.
// Enables scrolling in all directions, manages the back button to
// navigate browser history and manages display of elements in
// navigation bar if it is active.
VerticalFieldManager vfm = new VerticalFieldManager(VERTICAL_SCROLL
| HORIZONTAL_SCROLL | HORIZONTAL_SCROLLBAR | VERTICAL_SCROLLBAR) {
protected boolean keyChar(char key, int status, int time) {
if (key == Characters.ESCAPE && browserField != null) {
BrowserFieldHistory history = browserField.getHistory();
if (history != null && history.canGoBack()) {
if (navBar != null) {
navBar.setRefreshActive(false);
}
UiApplication.getUiApplication().invokeLater(
new Runnable() {
public void run() {
browserField.setFocus();
browserField.back();
}
});
return true;
}
}
return super.keyChar(key, status, time);
}
protected void onFocus(int direction) {
if (navBar != null) {
navBar.showFullNav(true);
}
super.onFocus(direction);
}
};
vfm.add(browserField);
add(vfm);
return true;
}
/**
* Load the specified URL in the browser.
*
* @param url
* the URL to load.
*/
void loadURL(String url) {
if (url != null && url.length() > 0) {
if (!url.startsWith("http")) {
url = "http://" + url;
}
if (navBar != null) {
navBar.setURL(url);
}
browserField.requestContent(url);
}
}
/**
* Create a new plugin result and send it back to JavaScript
*
* @param obj
* a JSONObject containing event payload information
*/
private void sendUpdate(JSONObject obj, boolean keepCallback) {
if (callbackId != null) {
PluginResult result = new PluginResult(PluginResult.Status.OK, obj);
result.setKeepCallback(keepCallback);
PhoneGapExtension.invokeSuccessCallback(callbackId, result);
}
}
}

View File

@@ -0,0 +1,363 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2011, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
package com.phonegap.plugins.childbrowser;
import javax.microedition.io.InputConnection;
import net.rim.device.api.browser.field2.BrowserField;
import net.rim.device.api.browser.field2.BrowserFieldConfig;
import net.rim.device.api.browser.field2.BrowserFieldHistory;
import net.rim.device.api.browser.field2.BrowserFieldRequest;
import net.rim.device.api.browser.field2.ProtocolController;
import net.rim.device.api.system.Bitmap;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.VerticalFieldManager;
import net.rim.device.api.ui.decor.BackgroundFactory;
import com.phonegap.util.Logger;
/**
* Implements a navigation bar for the custom browser. Provides UI elements to
* navigation back or forward in history, to stop or refresh loading and a field
* to display and navigate to a URL.
*/
public class NavigationBar extends HorizontalFieldManager {
// Images used in the navigation bar. Must exist in the compiled
// application.
private final static String BUTTON_BACK = "icon_arrow_left.png";
private final static String BUTTON_FORWARD = "icon_arrow_right.png";
private final static String BUTTON_REFRESH = "icon_refresh.png";
private final static String BUTTON_STOP = "icon_stop.png";
private BrowserField browserField = null;
/** Tracks whether the full navigation bar is displayed. */
private boolean fullNav = false;
/** The fields and their order in the navigation bar. */
private Field[] navFields = new Field[4];
private BitmapField refreshField = null;
private BitmapField stopField = null;
private UiApplication uiApp = UiApplication.getUiApplication();
private URLLabelField urlLabel = null;
/**
* Create a HorizontalFieldManager with no scrolling and a black background.
*
* @param browserField
*/
NavigationBar(BrowserField browserField) {
super(NO_HORIZONTAL_SCROLL | NO_VERTICAL_SCROLL | NON_FOCUSABLE);
setBackground(BackgroundFactory.createSolidBackground(Color.BLACK));
this.browserField = browserField;
}
/**
* Initialize the navigation bar. Load the images used in the UI and
* configure the layout.
*
* @return true if init succeeds, otherwise false.
*/
boolean init() {
final long style = Field.FIELD_VCENTER | Field.FOCUSABLE;
// Load a custom protocol controller which provides the ability to
// stop a load of a web page.
final CustomProtocolController controller = new CustomProtocolController(
browserField);
browserField.getConfig().setProperty(BrowserFieldConfig.CONTROLLER,
controller);
// Load the images used in the navigation bar.
Bitmap back = Bitmap.getBitmapResource(BUTTON_BACK);
Bitmap forward = Bitmap.getBitmapResource(BUTTON_FORWARD);
Bitmap stop = Bitmap.getBitmapResource(BUTTON_STOP);
Bitmap refresh = Bitmap.getBitmapResource(BUTTON_REFRESH);
if (back == null || forward == null || stop == null || refresh == null) {
Logger.log(ChildBrowser.TAG
+ "Failed to load navigation bar image.\n" + BUTTON_BACK
+ ": " + back + "\n" + BUTTON_FORWARD + ": " + forward
+ "\n" + BUTTON_STOP + ": " + stop + "\n" + BUTTON_REFRESH
+ ": " + refresh);
return false;
}
// Setup the back button.
BitmapField backField = new BitmapField(back, style) {
protected boolean navigationClick(int status, int time) {
goBack();
return true;
}
};
backField.setSpace(5, 3);
backField.setMargin(2, 2, 2, 2);
// Setup the forward button.
BitmapField forwardField = new BitmapField(forward, style) {
protected boolean navigationClick(int status, int time) {
goForward();
return true;
}
};
forwardField.setSpace(5, 3);
forwardField.setMargin(2, 0, 2, 2);
// Setup the stop button.
stopField = new BitmapField(stop, style) {
protected boolean navigationClick(int status, int time) {
controller.stopLoad();
setRefreshActive(true);
return true;
}
};
stopField.setSpace(5, 3);
stopField.setMargin(2, 2, 2, 0);
// Setup the refresh button.
refreshField = new BitmapField(refresh, style) {
protected boolean navigationClick(int status, int time) {
setRefreshActive(false);
uiApp.invokeLater(new Runnable() {
public void run() {
browserField.setFocus();
browserField.refresh();
}
});
return true;
}
};
refreshField.setSpace(5, 3);
refreshField.setMargin(2, 2, 2, 0);
// Calculate the total width used by the buttons (images + padding +
// margin) in order to determine the text field size.
int buttonWidth = back.getWidth() + forward.getWidth() + refresh.getWidth()
+ 50;
// Setup the URL label text field.
urlLabel = new URLLabelField(this, buttonWidth);
urlLabel.setBackground(BackgroundFactory
.createSolidBackground(Color.WHITE));
urlLabel.setFont(Font.getDefault());
urlLabel.setPadding(5, 5, 5, 5);
urlLabel.setMargin(5, 2, 5, 2);
// Update the field array with the initial setup for the navigation bar.
navFields[0] = backField;
navFields[1] = forwardField;
navFields[2] = urlLabel;
navFields[3] = refreshField;
showFullNav(true);
return true;
}
/**
* Initiates a browser request to a specified URL and updates the navigation
* bar to the proper state for browser loading.
*
* @param address
* the URL to load.
*/
void navigate(final String address) {
if (browserField != null && address != null && address.length() > 0) {
final String url = setURL(address);
setRefreshActive(false);
showFullNav(true);
uiApp.invokeLater(new Runnable() {
public void run() {
try {
browserField.setFocus();
browserField.requestContent(url);
} catch (Exception e) {
Logger.log("Caught exception navigating to: " + address
+ " message: " + e.getMessage());
}
}
});
}
}
/**
* Activates either the refresh or close button in the navigation bar.
*
* @param refresh
* if true activate refresh, otherwise activate close.
*/
synchronized void setRefreshActive(boolean refresh) {
navFields[3] = refresh ? refreshField : stopField;
// If the full navigation bar is currently displayed, make sure the
// correct button is active.
if (fullNav && getFieldCount() == 4) {
if ((refresh && getField(3) == stopField)
|| (!refresh && getField(3) == refreshField)) {
final Field deleteField = refresh ? stopField : refreshField;
final Field showField = refresh ? refreshField : stopField;
uiApp.invokeLater(new Runnable() {
public void run() {
replace(deleteField, showField);
}
});
}
}
}
/**
* Sets the URL to display in the navigation bar URL label field.
*
* @param url
* the URL to display.
* @return the URL.
*/
String setURL(String url) {
if (url != null && url.length() > 0) {
if (!url.startsWith("http")) {
url = "http://" + url;
}
}
final String address = url;
uiApp.invokeLater(new Runnable() {
public void run() {
urlLabel.setText(address);
}
});
return url;
}
/**
* Switches the navigation bar to either the full navigation bar or a
* navigation bar for URL editing.
*
* @param showFull
* if true show full navigation bar otherwise show URL edit bar.
*/
synchronized void showFullNav(boolean showFull) {
if (showFull && !fullNav) {
uiApp.invokeLater(new Runnable() {
public void run() {
deleteAll();
addAll(navFields);
invalidate();
fullNav = true;
}
});
} else if (!showFull && fullNav) {
uiApp.invokeLater(new Runnable() {
public void run() {
deleteAll();
// Use a VerticalFieldManager to restrict vertical scrolling
// but allow horizontal scrolling of the text field.
VerticalFieldManager vfm = new VerticalFieldManager(
FIELD_VCENTER | NO_VERTICAL_SCROLL
| HORIZONTAL_SCROLL);
URLEditField urlEdit = new URLEditField(NavigationBar.this);
urlEdit.setText(urlLabel.getText());
vfm.add(urlEdit);
add(vfm);
invalidate();
urlEdit.setFocus();
fullNav = false;
}
});
}
}
/**
* Checks to see if it is possible to go back one page in history, then does
* so.
*/
private void goBack() {
if (browserField != null) {
BrowserFieldHistory history = browserField.getHistory();
if (history != null && history.canGoBack()) {
setRefreshActive(false);
uiApp.invokeLater(new Runnable() {
public void run() {
browserField.setFocus();
browserField.back();
}
});
}
}
}
/**
* Checks to see if it is possible to go forward one page in history, then
* does so.
*/
private void goForward() {
if (browserField != null) {
BrowserFieldHistory history = browserField.getHistory();
if (history != null && history.canGoForward()) {
setRefreshActive(false);
uiApp.invokeLater(new Runnable() {
public void run() {
browserField.setFocus();
browserField.forward();
}
});
}
}
}
/**
* This is a very simple override of the ProtocolController which provides a
* hack to stop a load of a web page.
*/
private class CustomProtocolController extends ProtocolController {
private boolean stop = false;
public CustomProtocolController(BrowserField browserField) {
super(browserField);
}
/**
* @see net.rim.device.api.browser.field2.ProtocolController#handleNavigationRequest(net.rim.device.api.browser.field2.BrowserFieldRequest)
*/
public void handleNavigationRequest(BrowserFieldRequest request)
throws Exception {
// A new navigation request resets the stop variable.
stop = false;
super.handleNavigationRequest(request);
}
/**
* @see net.rim.device.api.browser.field2.ProtocolController#handleResourceRequest(net.rim.device.api.browser.field2.BrowserFieldRequest)
*/
public InputConnection handleResourceRequest(BrowserFieldRequest request)
throws Exception {
// If a stop request was issued just return null so the content
// isn't downloaded.
if (stop) {
return null;
} else {
return super.handleResourceRequest(request);
}
}
/**
* Stop a navigation request.
*/
void stopLoad() {
stop = true;
}
}
}

View File

@@ -0,0 +1,181 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2011, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
package com.phonegap.plugins.childbrowser;
import net.rim.device.api.system.Characters;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.Keypad;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.VirtualKeyboard;
import net.rim.device.api.ui.component.BasicEditField;
import net.rim.device.api.ui.decor.BackgroundFactory;
/**
* A BasicEditField which manages display of the virtual keyboard when required
* and provides native behavior for text selection and input.
*/
public class URLEditField extends BasicEditField {
private NavigationBar navBar;
private boolean selectAll = false;
private final int PADDING = 20;
/**
* Create a one line BasicEditField with limited character input, no auto
* correction and a white background.
*
* @param navigation
* @param width
*/
URLEditField(NavigationBar navigation) {
super(NO_NEWLINE | FOCUSABLE | EDITABLE | FILTER_URL);
navBar = navigation;
setBackground(BackgroundFactory.createSolidBackground(Color.WHITE));
setFont(Font.getDefault());
setPadding(5, 5, 5, 5);
setMargin(5, 5, 5, 5);
}
/**
* Override the enter key to initiate a navigation and override the back key
* to clear all text selection then revert to full navigation bar.
*
* @see net.rim.device.api.ui.component.BasicEditField#keyChar(char, int,
* int)
*/
protected boolean keyChar(char key, int status, int time) {
if (key == Keypad.KEY_ENTER) {
// On enter key, navigate to specified URL.
navBar.navigate(getText());
return true;
} else if (key == Characters.ESCAPE) {
// If all text is currently selected, clear selection and set cursor
// to end of text.
if (selectAll) {
selectAll = false;
select(false);
String text = getText();
if (text != null) {
setCursorPosition(text.length());
} else {
setCursorPosition(0);
}
return true;
} else {
// User is backing out of URL edit, so display full navigation
// bar.
navBar.showFullNav(true);
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
navBar.setFocus();
}
});
return true;
}
}
return super.keyChar(key, status, time);
}
/**
* Adjust width to the desired width on orientation change.
*
* @see net.rim.device.api.ui.component.BasicEditField#layout(int, int)
*/
protected void layout(int maxWidth, int maxHeight) {
int width = Display.getWidth() - PADDING;
super.layout(width, maxHeight);
setExtent(width, getPreferredHeight());
}
/**
* If the user clicks the trackpad in the field, navigate to the URL
* specified.
*
* @see net.rim.device.api.ui.component.TextField#navigationClick(int, int)
*/
protected boolean navigationClick(int status, int time) {
String text = getText();
if (text != null && text.length() > 0) {
navBar.navigate(text);
return true;
}
return navigationClick(status, time);
}
/**
* Disable text selection with the trackpad upon movement.
*
* @see net.rim.device.api.ui.Field#navigationMovement(int, int, int, int)
*/
protected boolean navigationMovement(int dx, int dy, int status, int time) {
if (isSelecting()) {
select(false);
}
return super.navigationMovement(dx, dy, status, time);
}
/**
* When focus is acquired, highlight all the text in the field and display
* the virtual keyboard if it is needed.
*
* @see net.rim.device.api.ui.component.TextField#onFocus(int)
*/
protected void onFocus(int direction) {
if (!selectAll) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
// Display the virtual keyboard if required.
if (VirtualKeyboard.isSupported()) {
VirtualKeyboard vk = UiApplication.getUiApplication()
.getActiveScreen().getVirtualKeyboard();
if (vk != null
&& vk.getVisibility() == VirtualKeyboard.HIDE) {
vk.setVisibility(VirtualKeyboard.SHOW);
}
}
// Highlight all the text in the field.
setCursorPosition(0);
select(true);
String text = getText();
if (text != null && text.length() > 0) {
moveFocus(text.length(), 0, 0);
}
selectAll = true;
}
});
return;
} else {
// On refocus, use default behavior.
selectAll = false;
}
super.onFocus(direction);
}
/**
* When focus is lost, hide the virtual keyboard if it is shown.
*
* @see net.rim.device.api.ui.component.BasicEditField#onUnfocus()
*/
protected void onUnfocus() {
if (VirtualKeyboard.isSupported()) {
VirtualKeyboard vk = UiApplication.getUiApplication()
.getActiveScreen().getVirtualKeyboard();
if (vk != null && vk.getVisibility() == VirtualKeyboard.SHOW) {
vk.setVisibility(VirtualKeyboard.HIDE);
}
}
selectAll = false;
super.onUnfocus();
}
}

View File

@@ -0,0 +1,81 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2011, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
package com.phonegap.plugins.childbrowser;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.decor.BackgroundFactory;
/**
* Label field used to display the current web page URL. Draws a highlight of
* the field on focus and displays an editable text field when clicked.
*/
public class URLLabelField extends LabelField {
private NavigationBar parentManager;
private int padding;
/**
* Creates a label field of a defined size with ellipsis for long text.
*
* @param manager
* @param padding
*/
URLLabelField(NavigationBar manager, int padding) {
super("", LabelField.ELLIPSIS | LabelField.FIELD_LEFT
| LabelField.FIELD_VCENTER | LabelField.USE_ALL_WIDTH
| LabelField.FOCUSABLE);
this.padding = padding;
parentManager = manager;
}
/**
* Draws a light blue background across entire text label when element is
* focussed.
*
* @see net.rim.device.api.ui.Field#drawFocus(net.rim.device.api.ui.Graphics,
* boolean)
*/
protected void drawFocus(Graphics graphics, boolean on) {
setBackground(BackgroundFactory.createSolidBackground(Color.LIGHTBLUE));
invalidate();
}
/**
* Enforces the custom width for the layout.
*
* @see net.rim.device.api.ui.component.LabelField#layout(int, int)
*/
protected void layout(int maxWidth, int maxHeight) {
int width = Display.getWidth() - padding;
super.layout(width, maxHeight);
setExtent(width, getPreferredHeight());
}
/**
* Shows the editable text field when the label field is clicked.
*
* @see net.rim.device.api.ui.Field#navigationClick(int, int)
*/
protected boolean navigationClick(int status, int time) {
parentManager.showFullNav(false);
return true;
}
/**
* Resets the background color on unfocus.
*
* @see net.rim.device.api.ui.Field#onUnfocus()
*/
protected void onUnfocus() {
setBackground(BackgroundFactory.createSolidBackground(Color.WHITE));
invalidate();
super.onUnfocus();
}
}

View File

@@ -0,0 +1,103 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2011, IBM Corporation
*/
/**
* window.plugins.childBrowser
*
* Provides
*/
var ChildBrowser = ChildBrowser || (function() {
/**
* Constructor
*/
function ChildBrowser() {
};
ChildBrowser.CLOSE_EVENT = 0;
ChildBrowser.LOCATION_CHANGED_EVENT = 1;
/**
* Display a new browser with the specified URL. This method loads up a new
* custom browser field.
*
* @param url
* The url to load
* @param options
* An object that specifies additional options
*/
ChildBrowser.prototype.showWebPage = function(url, options) {
if (options === null || options === "undefined") {
var options = new Object();
options.showLocationBar = true;
}
PhoneGap.exec(this._onEvent, this._onError, "ChildBrowser",
"showWebPage", [url, options ]);
};
/**
* Close the browser opened by showWebPage.
*/
ChildBrowser.prototype.close = function() {
PhoneGap.exec(null, this._onError, "ChildBrowser", "close", []);
};
/**
* Display a new browser with the specified URL. This method starts a new
* web browser activity.
*
* @param url
* The url to load
* @param usePhoneGap
* Load url in PhoneGap webview [ignored]
*/
ChildBrowser.prototype.openExternal = function(url, usePhoneGap) {
// if (usePhoneGap === true) {
// navigator.app.loadUrl(url);
// } else {
PhoneGap.exec(null, null, "ChildBrowser", "openExternal", [ url,
usePhoneGap ]);
// }
};
/**
* Method called when the child browser is closed.
*/
ChildBrowser.prototype._onEvent = function(data) {
if (data.type == ChildBrowser.CLOSE_EVENT
&& typeof window.plugins.childBrowser.onClose === "function") {
window.plugins.childBrowser.onClose();
}
if (data.type == ChildBrowser.LOCATION_CHANGED_EVENT
&& typeof window.plugins.childBrowser.onLocationChange === "function") {
window.plugins.childBrowser.onLocationChange(data.location);
}
};
/**
* Method called when the child browser has an error.
*/
ChildBrowser.prototype._onError = function(data) {
if (typeof window.plugins.childBrowser.onError === "function") {
window.plugins.childBrowser.onError(data);
}
};
/**
* Maintain API consistency with iOS
*/
ChildBrowser.prototype.install = function() {
};
/**
* Load ChildBrowser
*/
PhoneGap.addConstructor(function() {
PhoneGap.addPlugin("childBrowser", new ChildBrowser());
});
})();

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B