/* * 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.ui; import com.codename1.ui.events.ActionEvent; import com.codename1.ui.events.ActionListener; import com.codename1.ui.layouts.BorderLayout; import com.codename1.ui.util.EventDispatcher; import com.codename1.ui.events.BrowserNavigationCallback; import com.codename1.ui.plaf.Style; import java.io.IOException; import java.util.Hashtable; /** * <p>The browser component is an interface to an embeddable native platform browser on platforms * that support embedding the native browser in place, if you need wide compatibility and flexibility * you should check out the HTMLComponent which provides a lightweight 100% cross platform * web component.<br> * This component will only work on platforms that support embedding a native browser which * exclude earlier versions of Blackberry devices and J2ME devices.<br> * Its recommended that you place this component in a fixed position (none scrollable) on the screen without other * focusable components to prevent confusion between focus authority and allow the component to scroll * itself rather than CodenameOne making that decision for it.</p> * * <p>On Android this component might show a native progress indicator dialog. You can disable that functionality * using the {@Display.getInstance().setProperty("WebLoadingHidden", "true");} call.</p> * * <p> * The following code shows the basic usage of the {@code BrowserComponent}: * </p> * <script src="https://gist.github.com/codenameone/20b6a17463152f90ebbb.js"></script> * <img src="https://www.codenameone.com/img/developer-guide/components-browsercomponent.png" alt="Simple usage of BrowserComponent" /> * * * @author Shai Almog */ public class BrowserComponent extends Container { private Hashtable listeners; private PeerComponent internal; private boolean pinchToZoom = true; private boolean nativeScrolling = true; private BrowserNavigationCallback browserNavigationCallback = new BrowserNavigationCallback(){ public boolean shouldNavigate(String url) { return true; } }; /** * 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){ this.browserNavigationCallback = 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(){ return this.browserNavigationCallback; } /** * This constructor will work as expected when a browser component is supported, see isNativeBrowserSupported() */ public BrowserComponent() { setUIID("BrowserComponent"); PeerComponent c = Display.impl.createBrowserComponent(this); setLayout(new BorderLayout()); addComponent(BorderLayout.CENTER, c); internal = c; Style s = internal.getUnselectedStyle(); s.setPadding(0, 0, 0, 0); s.setMargin(0, 0, 0, 0); s.setBgTransparency(255); } /** * Returns true if the platform supports embedding a native browser component * * @return true if native browsing is supported */ public static boolean isNativeBrowserSupported() { return Display.impl.isNativeBrowserComponentSupported(); } /** * This method allows customizing the properties of a web view in various ways including platform specific settings. * When a property isn't supported by a specific platform it is just ignored. * * @param key see the documentation with the CodenameOne Implementation for further details * @param value see the documentation with the CodenameOne Implementation for further details */ public void setProperty(String key, Object value) { Display.impl.setBrowserProperty(internal, key, value); } /** * The page title * @return the title */ public String getTitle() { return Display.impl.getBrowserTitle(internal); } /** * The page URL * @return the URL */ public String getURL() { return Display.impl.getBrowserURL(internal); } /** * Sets the page URL, jar: URL's must be supported by the implementation * @param url the URL */ public void setURL(String url) { Display.impl.setBrowserURL(internal, url); } /** * Sets the page URL while respecting the hierarchy of the html * @param url the URL */ public void setURLHierarchy(String url) throws IOException { Display.impl.setBrowserPageInHierarchy(internal, url); } /** * Reload the current page */ public void reload() { Display.impl.browserReload(internal); } /** * Indicates whether back is currently available * @return true if back should work */ public boolean hasBack() { return Display.impl.browserHasBack(internal); } /** * Indicates whether forward is currently available * @return true if forward should work */ public boolean hasForward() { return Display.impl.browserHasForward(internal); } /** * Navigates back in the history */ public void back() { Display.impl.browserBack(internal); } /** * Navigates forward in the history */ public void forward() { Display.impl.browserForward(internal); } /** * Clears navigation history */ public void clearHistory() { Display.impl.browserClearHistory(internal); } /** * Some platforms require that you enable pinch to zoom explicitly. This method has no * effect if pinch to zoom isn't supported by the platform * * @param e true to enable pinch to zoom, false to disable it */ public void setPinchToZoomEnabled(boolean e) { pinchToZoom = e; Display.impl.setPinchToZoomEnabled(internal, e); } /** * This method is unreliable and is only here for consistency with setPinchToZoomEnabled, * it will not return whether the platform supports pinch since this is very hard to detect * properly. * @return the last value for setPinchToZoomEnabled */ public boolean isPinchToZoomEnabled() { return pinchToZoom; } /** * This flag allows disabling the native browser scrolling on platforms that support it * @param b true to enable native scrolling, notice that non-native scrolling might be problematic */ public void setNativeScrollingEnabled(boolean b) { nativeScrolling = b; Display.impl.setNativeBrowserScrollingEnabled(internal, b); } /** * This method is unreliable and is only here for consistency with setNativeScrollingEnabled. * * @return the last value for setNativeScrollingEnabled */ public boolean isNativeScrollingEnabled() { return nativeScrolling; } /** * 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) { Display.impl.setBrowserPage(internal, html, baseUrl); } private EventDispatcher getEventDispatcher(String type, boolean autoCreate) { if(listeners == null) { if(!autoCreate) { return null; } listeners = new Hashtable(); EventDispatcher ev = new EventDispatcher(); listeners.put(type, ev); return ev; } EventDispatcher ev = (EventDispatcher)listeners.get(type); if(ev == null) { if(autoCreate) { ev = new EventDispatcher(); listeners.put(type, ev); } } return ev; } /** * Adds a listener to the given event type name, event type names are platform specific but some * must be fired for all platforms and will invoke the action listener when the appropriate event loads * * @param type platform specific but must support: onStart, onLoad, onError * @param listener callback for the event */ public void addWebEventListener(String type, ActionListener listener) { getEventDispatcher(type, true).addListener(listener); } /** * Removes the listener, see addWebEventListener for details * * @param type see addWebEventListener for details * @param listener see addWebEventListener for details */ public void removeWebEventListener(String type, ActionListener listener) { EventDispatcher e = getEventDispatcher(type, false); if(e != null) { e.removeListener(listener); if(!e.hasListeners()) { listeners.remove(type); } } } /** * Cancel the loading of the current page */ public void stop() { Display.impl.browserStop(internal); } /** * Release native resources of this Browser Component */ public void destroy() { Display.impl.browserDestroy(internal); } /** * Used internally by the implementation to fire an event from the native browser widget * * @param type the type of the event * @param ev the event */ public void fireWebEvent(String type, ActionEvent ev) { EventDispatcher e = getEventDispatcher(type, false); if(e != null) { e.fireActionEvent(ev); } } /** * Executes the given JavaScript string within the current context * * @param javaScript the JavaScript string */ public void execute(String javaScript) { Display.impl.browserExecute(internal, javaScript); } /** * Executes the given JavaScript and returns a result string from the underlying platform * where applicable * @param javaScript the JavaScript code to execute * @return the string returned from the Javascript call */ public String executeAndReturnString(String javaScript){ return Display.impl.browserExecuteAndReturnString(internal, javaScript); } /** * Allows exposing the given object to JavaScript code so the JavaScript code can invoke methods * and access fields on the given object. Notice that on RIM devices which don't support reflection * this object must implement the propriatery Scriptable interface * http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/script/Scriptable.html * * @param o the object to invoke, notice all public fields and methods would be exposed to JavaScript * @param name the name to expose within JavaScript * @deprecated this doesn't work in most platforms see issue 459 for details, use the setBrowserNavigationCallback * method instead */ public void exposeInJavaScript(Object o, String name) { Display.impl.browserExposeInJavaScript(internal, o, name); } /** * Toggles debug mode for the browser component which helps detect coding errors in the JavaScript * bridge logic * @param mode true to debug false otherwise, this might have no effect in some platforms */ public void setDebugMode(boolean mode) { if(mode) { putClientProperty("BrowserComponent.firebug", Boolean.TRUE); } else { putClientProperty("BrowserComponent.firebug", null); } } /** * Indicates if debug mode is set (might have no effect though) * @return true if debug mode was activated */ public boolean isDebugMode() { return getClientProperty("BrowserComponent.firebug") == Boolean.TRUE; } }