com.codename1.components.WebBrowser Maven / Gradle / Ivy
/*
* Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores
* CA 94065 USA or visit www.oracle.com if you need additional information or
* have any questions.
*/
package com.codename1.components;
import com.codename1.io.ConnectionRequest;
import com.codename1.io.Log;
import com.codename1.ui.*;
import com.codename1.ui.animations.Animation;
import com.codename1.ui.events.ActionEvent;
import com.codename1.ui.html.IOCallback;
import com.codename1.ui.html.DocumentInfo;
import com.codename1.ui.html.HTMLComponent;
import com.codename1.ui.html.AsyncDocumentRequestHandlerImpl;
import com.codename1.ui.events.ActionListener;
import com.codename1.ui.html.DefaultHTMLCallback;
import com.codename1.ui.html.HTMLCallback;
import com.codename1.ui.layouts.BorderLayout;
import com.codename1.util.Base64;
import com.codename1.ui.events.BrowserNavigationCallback;
import com.codename1.ui.geom.Rectangle;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
/**
* A simple browser view that encapsulates a usable version of HTMLComponent or BrowserComponent and
* automatically picks the right component for the platform preferring BrowserComponent whenever it
* is supported.
*
* On Android this component might show a native progress indicator dialog. You can disable that functionality
* using the {@Display.getInstance().setProperty("WebLoadingHidden", "true");} call.
*
* @author Shai Almog
* @deprecated Use{@link com.codename1.ui.BrowserComponent} instead. The original purpose of this class was to work as an interim solution for platforms where {@link com.codename1.ui.BrowserComponent} isn't supported however all currently supported platforms work with {@code BrowserComponent} so there is no real reason to use this class
*/
public class WebBrowser extends Container {
private Component internal;
private boolean isNative;
private String page;
private Loading loading;
/**
* Constructor with a URL
*
* @param url the url
*/
public WebBrowser(String url) {
this();
setURL(url);
}
/**
* Default constructor
*/
public WebBrowser() {
super(new BorderLayout());
try {
if (BrowserComponent.isNativeBrowserSupported()) {
isNative = true;
BrowserComponent b = new BrowserComponent();
b.addWebEventListener("onStart", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
onStart((String) evt.getSource());
}
});
b.addWebEventListener("onLoad", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
onLoad((String) evt.getSource());
}
});
b.addWebEventListener("onError", new ActionListener() {
public void actionPerformed(ActionEvent evt) {
onError((String) evt.getSource(), evt.getKeyEvent());
}
});
internal = b;
addComponent(BorderLayout.CENTER, internal);
return;
}
} catch(Throwable t) {
// workaround for issue in the designer related to JavaFX, fallback to lightweight mode...
Log.e(t);
}
isNative = false;
HTMLComponent h = new HTMLComponent(new AsyncDocumentRequestHandlerImpl() {
protected ConnectionRequest createConnectionRequest(final DocumentInfo docInfo,
final IOCallback callback, final Object[] response) {
return new ConnectionRequest() {
protected void buildRequestBody(OutputStream os) throws IOException {
if (isPost()) {
if (docInfo.getParams() != null) {
String enc = docInfo.getEncoding();
if(enc.indexOf('/') > -1) {
if(enc.indexOf("charset=") > -1) {
enc = enc.substring(enc.indexOf("charset=") + 8);
} else {
enc = DocumentInfo.ENCODING_UTF8;
}
}
OutputStreamWriter w = new OutputStreamWriter(os, enc);
w.write(docInfo.getParams());
w.flush();
}
}
}
protected void handleIOException(IOException err) {
if (callback == null) {
response[0] = err;
}
super.handleIOException(err);
}
protected boolean shouldAutoCloseResponse() {
return callback != null;
}
protected void readResponse(InputStream input) throws IOException {
if (callback != null) {
callback.streamReady(input, docInfo);
} else {
response[0] = input;
synchronized (LOCK) {
LOCK.notify();
}
}
}
protected void handleErrorResponseCode(int code, String message) {
onError(message, code);
}
protected void handleException(Exception err) {
System.out.println("Error occured");
Log.e(err);
if(loading != null){
loading.unInstall();
}
}
public boolean onRedirect(String url) {
onStart(url);
if(((HTMLComponent)internal).getPageStatus() == HTMLCallback.STATUS_CANCELLED){
return true;
}
return super.onRedirect(url);
}
};
}
});
h.setIgnoreCSS(true);
h.setHTMLCallback(new DefaultHTMLCallback() {
public void pageStatusChanged(HTMLComponent htmlC, int status, String url) {
Form f = htmlC.getComponentForm();
if(f != null){
if(status == STATUS_REQUESTED || (loading == null && status == STATUS_CONNECTED)){
loading = new Loading(f);
loading.install();
}else{
if(loading != null && ( status == STATUS_DISPLAYED ||
status == STATUS_ERROR || status == STATUS_CANCELLED)){
loading.unInstall();
}
}
}
if (status == STATUS_REQUESTED && url != null) {
onStart(url);
} else if (status == STATUS_DISPLAYED && url != null) {
onLoad(url);
} else if (status == STATUS_ERROR) {
onError("error on page", -1);
}
}
});
internal = h;
addComponent(BorderLayout.CENTER, internal);
}
/**
* This method creates a data URI
* which allows developers creating HTML for local use to embed local images into the HTML by
* appending them as a URI. E.g. instead of referencing a file or URL just load the image data
* and place the contents of this string into the src attribute.
* This is the easiest way to get an HTML with local images to work on all mobile platforms.
* @param data data of an image
* @param mime the mime type of the image e.g. image/png
* @return a data URL that can be placed into the img src attribute in HTML e.g. data:image/png;base64,encodedData
*/
public static String createDataURI(byte[] data, String mime) {
return "data:" + mime + ";base64," + Base64.encodeNoNewline(data);
}
/**
* This is a callback method, this method is called before the url has been
* loaded
* @param url
*/
public void onStart(String url) {
}
/**
* This is a callback method, this method is called after the url has been
* loaded
* @param url
*/
public void onLoad(String url) {
}
/**
* Set the browser navigation callback which allows handling a case where
* a URL invocation can be delegated to Java code. This allows binding
* Java side functionality to JavaScript functionality in the same
* way PhoneGap/Cordova work
* @param callback the callback interface
*/
public void setBrowserNavigationCallback(BrowserNavigationCallback callback){
if(BrowserComponent.isNativeBrowserSupported()) {
((BrowserComponent)this.getInternal()).setBrowserNavigationCallback(callback);
}
}
/**
* The browser navigation callback interface allows handling a case where
* a URL invocation can be delegated to Java code. This allows binding
* Java side functionality to JavaScript functionality in the same
* way PhoneGap/Cordova work
*
* @return the callback interface
*/
public BrowserNavigationCallback getBrowserNavigationCallback(){
if(BrowserComponent.isNativeBrowserSupported()) {
return ((BrowserComponent)this.getInternal()).getBrowserNavigationCallback();
} else {
return null;
}
}
/**
* This is a callback method to inform on an error.
* @param message
* @param errorCode
*/
public void onError(String message, int errorCode) {
}
/**
* Since the internal component can be either an HTMLComponent or a BrowserComponent one of them
* will be returned. If you are targeting modern smartphones only you can rely on this method
* returning a BrowserComponent instance.
* @return BrowserComponent or HTMLComponent
*/
public Component getInternal() {
return internal;
}
/**
* Returns the title for the browser page
* @return the title
*/
public String getTitle() {
if (isNative) {
return ((BrowserComponent) internal).getTitle();
} else {
return ((HTMLComponent) internal).getTitle();
}
}
/**
* The page URL
* @return the URL
*/
public String getURL() {
if (isNative) {
return ((BrowserComponent) internal).getURL();
} else {
return ((HTMLComponent) internal).getPageURL();
}
}
/**
* Sets the page URL, jar: URL's must be supported by the implementation
* @param url the URL
*/
public void setURL(String url) {
if (isNative) {
((BrowserComponent) internal).setURL(url);
} else {
((HTMLComponent) internal).setPage(url);
}
}
/**
* Reload the current page
*/
public void reload() {
if (isNative) {
((BrowserComponent) internal).reload();
} else {
((HTMLComponent) internal).refreshDOM();
}
}
/**
* Stop loading the current page
*/
public void stop() {
if (isNative) {
((BrowserComponent) internal).stop();
} else {
((HTMLComponent) internal).cancel();
}
}
/**
* Release WebBrowser native resources.
*/
public void destroy() {
if (isNative) {
// workaround for issue 827
((BrowserComponent) internal).setPage("
", null);
((BrowserComponent) internal).destroy();
}
internal = null;
}
/**
* Shows the given HTML in the native viewer
*
* @param html HTML web page
* @param baseUrl base URL to associate with the HTML
*/
public void setPage(String html, String baseUrl) {
page = html;
if (isNative) {
((BrowserComponent) internal).setPage(html, baseUrl);
} else {
((HTMLComponent) internal).setHTML(html, "UTF-8", null, true);
}
}
/**
* Returns the page set by getPage for the GUI builder
*
* @return the HTML page set manually
*/
public String getPage() {
return page;
}
/**
* {@inheritDoc}
*/
public String[] getPropertyNames() {
return new String[]{"url", "html"};
}
/**
* {@inheritDoc}
*/
public Class[] getPropertyTypes() {
return new Class[]{String.class, String.class};
}
/**
* {@inheritDoc}
*/
public String[] getPropertyTypeNames() {
return new String[] {"String", "String"};
}
/**
* {@inheritDoc}
*/
public Object getPropertyValue(String name) {
if (name.equals("url")) {
return getURL();
}
if (name.equals("html")) {
return getPage();
}
return null;
}
/**
* {@inheritDoc}
*/
public String setPropertyValue(String name, Object value) {
if (name.equals("url")) {
setURL((String) value);
return null;
}
if (name.equals("html")) {
setPage((String) value, null);
return null;
}
return super.setPropertyValue(name, value);
}
class Loading implements Painter, Animation{
private Form f;
private InfiniteProgress progress = new InfiniteProgress();
Loading(Form f){
this.f = f;
}
public void paint(Graphics g, Rectangle rect) {
int x = f.getWidth()/2 - progress.getPreferredW()/2;
int y = f.getHeight()/2 - progress.getPreferredH()/2;
progress.setX(x);
progress.setY(y);
progress.setWidth(progress.getPreferredW());
progress.setHeight(progress.getPreferredH());
progress.paintComponent(g, true);
}
public boolean animate() {
return true;
}
public void paint(Graphics g) {
paint(g, null);
}
void install(){
f.setGlassPane(this);
f.registerAnimated(this);
}
void unInstall(){
f.setGlassPane(null);
f.deregisterAnimated(this);
}
}
}