From 459cd2df0820dbf17f273333239be8a96415bc14 Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Thu, 6 Nov 2014 14:22:31 -0800 Subject: [PATCH 01/12] Only load tests.jar when actually testing This restrictes the loading of tests.jar to when the Java tests are actually being run. --- main.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/main.js b/main.js index f87a8085..aac174bd 100644 --- a/main.js +++ b/main.js @@ -41,7 +41,12 @@ if ("gamepad" in urlParams && !/no|0/.test(urlParams.gamepad)) { document.documentElement.classList.add('gamepad'); } -var jars = ["java/classes.jar", "tests/tests.jar"]; +var jars = ["java/classes.jar"]; + +if (MIDP.midletClassName == "RunTests") { + jars.push("tests/tests.jar"); +} + if (urlParams.jars) { jars = jars.concat(urlParams.jars.split(":")); } From 3f8cee975649583c52f6602d85353264c1990e1c Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Thu, 6 Nov 2014 15:54:28 -0800 Subject: [PATCH 02/12] Switch to |fs.close| calling |fs.flush| Issue #388. - Adds a dirty flag to our FD, updates it on write/truncate/flush - Only flush if the dirty flag is set - |fs.close| calls flush --- libs/fs.js | 27 +++++++++++++++++++++++---- midp/fs.js | 33 +++++++-------------------------- tests/automation.js | 2 +- tests/fstests.js | 26 +++++++++++++++++++++++++- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/libs/fs.js b/libs/fs.js index e9d7112b..092ff708 100644 --- a/libs/fs.js +++ b/libs/fs.js @@ -99,6 +99,7 @@ var fs = (function() { var reader = new FileReader(); reader.addEventListener("loadend", function() { var fd = openedFiles.push({ + dirty: false, path: path, buffer: new FileBuffer(new Uint8Array(reader.result)), position: 0, @@ -110,11 +111,20 @@ var fs = (function() { }); } - function close(fd) { + function close(fd, cb) { if (fd >= 0 && openedFiles[fd]) { - // Replace descriptor object with null value instead of removing it from - // the array so we don't change the indexes of the other objects. - openedFiles.splice(fd, 1, null); + flush(fd, function() { + // Replace descriptor object with null value instead of removing it from + // the array so we don't change the indexes of the other objects. + openedFiles.splice(fd, 1, null); + if (cb) { + cb(); + } + }); + } else { + if (cb) { + cb(); + } } } @@ -160,6 +170,7 @@ var fs = (function() { openedFiles[fd].position = from + data.byteLength; openedFiles[fd].stat = { mtime: Date.now(), isDir: false }; + openedFiles[fd].dirty = true; } function getpos(fd) { @@ -179,8 +190,15 @@ var fs = (function() { } function flush(fd, cb) { + // Bail early if the file has not been modified. + if (!openedFiles[fd].dirty) { + cb(); + return; + } + var blob = new Blob([openedFiles[fd].buffer.getContent()]); asyncStorage.setItem(openedFiles[fd].path, blob, function() { + openedFiles[fd].dirty = false; if (openedFiles[fd].stat) { setStat(openedFiles[fd].path, openedFiles[fd].stat, cb); } else { @@ -228,6 +246,7 @@ var fs = (function() { if (size != openedFiles[fd].buffer.contentSize) { openedFiles[fd].buffer.setSize(size); openedFiles[fd].stat = { mtime: Date.now(), isDir: false }; + openedFiles[fd].dirty = true; } } diff --git a/midp/fs.js b/midp/fs.js index 3810c3c3..9d9dc781 100644 --- a/midp/fs.js +++ b/midp/fs.js @@ -116,10 +116,7 @@ Native.create("com/sun/midp/rms/RecordStoreFile.commitWrite.(I)V", function(hand Native.create("com/sun/midp/rms/RecordStoreFile.closeFile.(I)V", function(handle) { return new Promise(function(resolve, reject) { - fs.flush(handle, function() { - fs.close(handle); - resolve(); - }); + fs.close(handle, resolve); }); }, true); @@ -438,17 +435,12 @@ Native.create("com/ibm/oti/connection/file/Connection.renameImpl.([B[B)V", funct Native.create("com/ibm/oti/connection/file/Connection.truncateImpl.([BJ)V", function(path, newLength, _) { return new Promise(function(resolve, reject) { - fs.open(util.decodeUtf8(path), function(fd) { - if (fd == -1) { + fs.truncate(util.decodeUtf8(path), function(success) { + if (!success) { reject(new JavaException("java/io/IOException", "truncate failed")); - return; + } else { + resolve(); } - - fs.ftruncate(fd, newLength.toNumber()); - fs.flush(fd, function() { - fs.close(fd); - resolve(); - }); }); }); }, true); @@ -507,15 +499,7 @@ Native.create("com/ibm/oti/connection/file/FCInputStream.closeImpl.(I)V", functi Native.create("com/ibm/oti/connection/file/FCOutputStream.closeImpl.(I)V", function(fd) { return new Promise(function(resolve, reject) { - if (fd <= -1) { - resolve(); - return; - } - - fs.flush(fd, function() { - fs.close(fd); - resolve(); - }); + fs.close(fd, resolve); }); }, true); @@ -665,9 +649,6 @@ Native.create("com/sun/midp/io/j2me/storage/RandomAccessStream.sizeOf.(I)I", fun Native.create("com/sun/midp/io/j2me/storage/RandomAccessStream.close.(I)V", function(handle) { return new Promise(function(resolve, reject) { - fs.flush(handle, function() { - fs.close(handle); - resolve(); - }); + fs.close(handle, resolve); }); }, true); diff --git a/tests/automation.js b/tests/automation.js index e62fba6a..32f48dc7 100644 --- a/tests/automation.js +++ b/tests/automation.js @@ -79,7 +79,7 @@ casper.test.begin("unit tests", 7 + gfxTests.length, function(test) { casper .thenOpen("http://localhost:8000/tests/fstests.html") .waitForText("DONE", function() { - test.assertTextExists("DONE: 124 PASS, 0 FAIL", "run fs.js unit tests"); + test.assertTextExists("DONE: 126 PASS, 0 FAIL", "run fs.js unit tests"); }); casper diff --git a/tests/fstests.js b/tests/fstests.js index 5b6ac828..94e92281 100644 --- a/tests/fstests.js +++ b/tests/fstests.js @@ -556,7 +556,7 @@ tests.push(function() { tests.push(function() { fs.size("/tmp/tmp.txt", function(size) { - is(size, 12, "file's size is 12"); + is(size, 131079, "file's size after closing is 131079"); next(); }); }); @@ -676,6 +676,18 @@ tests.push(function() { }, 1); }); + tests.push(function() { + window.setTimeout(function() { + fs.flush(fd, function() { + fs.stat("/tmp/stat.txt", function(stat) { + is(stat.mtime, lastTime, "flush on just opened file doesn't update mtime"); + lastTime = stat.mtime; + next(); + }); + }); + }, 1); + }); + tests.push(function() { window.setTimeout(function() { fs.write(fd, new TextEncoder().encode("mi")); @@ -700,6 +712,18 @@ tests.push(function() { }, 1); }); + tests.push(function() { + window.setTimeout(function() { + fs.flush(fd, function() { + fs.stat("/tmp/stat.txt", function(stat) { + is(stat.mtime, lastTime, "flush on non-dirty file doesn't change mtime"); + lastTime = stat.mtime; + next(); + }); + }); + }, 1); + }); + tests.push(function() { window.setTimeout(function() { fs.ftruncate(fd, 4); From ddcfb6e3f79fa90e8261a50514e01fb1d87c1c6f Mon Sep 17 00:00:00 2001 From: Pin Zhang Date: Thu, 6 Nov 2014 18:10:45 +0800 Subject: [PATCH 03/12] Fix TextEditor position issue. When using LWUIT library, the position of TextEditor set by TextField is incorrect when the TITLE_LAYER of MIDPWindow is shown. --- java/custom/com/nokia/mid/ui/CanvasItem.java | 22 +- .../com/sun/midp/chameleon/MIDPWindow.java | 908 +++++++++++++++ .../javax/microedition/lcdui/Canvas.java | 1037 +++++++++++++++++ .../javax/microedition/lcdui/Display.java | 18 + midp/gfx.js | 2 +- 5 files changed, 1985 insertions(+), 2 deletions(-) create mode 100644 java/custom/com/sun/midp/chameleon/MIDPWindow.java create mode 100644 java/custom/javax/microedition/lcdui/Canvas.java diff --git a/java/custom/com/nokia/mid/ui/CanvasItem.java b/java/custom/com/nokia/mid/ui/CanvasItem.java index e0ef3d97..c6d378e6 100644 --- a/java/custom/com/nokia/mid/ui/CanvasItem.java +++ b/java/custom/com/nokia/mid/ui/CanvasItem.java @@ -1,5 +1,8 @@ package com.nokia.mid.ui; +import javax.microedition.lcdui.Canvas; +import javax.microedition.lcdui.Display; + public abstract class CanvasItem { public static final int SCALE_NOT_ALLOWED = 0; public static final int SCALE_NEAREST = 1; @@ -46,7 +49,24 @@ public abstract class CanvasItem { native public int getHeight(); // Sets the rendering position of this CanvasItem. - native public void setPosition(int x, int y); + native public void setPosition0(int x, int y); + + public void setPosition(int x, int y) { + // The passed coordinate is relative to the parent canvas, we need to add up + // the anchor value of the parent to get the right coordinate in HTML. + if (!(parent instanceof Canvas)) { + setPosition0(x, y); + return; + } + + Display display = ((Canvas)parent).getCurrentDisplay(); + if (display == null) { + setPosition0(x, y); + return; + } + + setPosition0(display.getDisplayableAnchorX() + x, display.getDisplayableAnchorY() + y); + } // Sets the Z-position, or the elevation, of the item. public void setZPosition(int z) throws IllegalArgumentException { diff --git a/java/custom/com/sun/midp/chameleon/MIDPWindow.java b/java/custom/com/sun/midp/chameleon/MIDPWindow.java new file mode 100644 index 00000000..a96d36f3 --- /dev/null +++ b/java/custom/com/sun/midp/chameleon/MIDPWindow.java @@ -0,0 +1,908 @@ +/* + * + * + * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER + * + * This program 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. + * + * This program 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 at /legal/license.txt). + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 or visit www.sun.com if you need additional + * information or have any questions. + */ + +package com.sun.midp.chameleon; + +import com.sun.midp.chameleon.layers.*; +import com.sun.midp.chameleon.skins.*; +import com.sun.midp.log.Logging; +import com.sun.midp.log.LogChannels; + +import javax.microedition.lcdui.*; + +/** + * The MIDPWindow class is a concrete instance of a CWindow which + * implements the MIDP specification and its graphical elements, + * such as a title bar, soft buttons, ticker, etc. + */ +public class MIDPWindow extends CWindow { + + // The order of layers id is impotant during creation and updating + + /** Id of layer containing the alert wash */ + public static final int ALERT_WASH_LAYER = 0; + + /** Id of layer containing the alert displayable */ + public static final int ALERT_LAYER = 1; + + /** Id of layer containing the mail */ + public static final int WASH_LAYER = 2; + + /** Id of layer rendering the soft button controls */ + public static final int BTN_LAYER = 3; + + /** Id of layer containing the ticker of the current displayable */ + public static final int TICKER_LAYER = 4; + + /** Id of layer containing the title of the current displayable */ + public static final int TITLE_LAYER = 5; + + /** Id of layer containing the pti contents */ + public static final int PTI_LAYER = 6; + + /** Id of layer containing the virtual keyboard contents */ + public static final int KEYBOARD_LAYER = 7; + + /** Id of layer containing the current displayable's contents */ + public static final int BODY_LAYER = 8; + + /** Number of main layers*/ + public static final int LAST_LAYER = 9; + + /** Used to call back into the Display class from this package */ + ChamDisplayTunnel tunnel; + + /** Cached typed references to the namded layers */ + private WashLayer washLayer; + private WashLayer alertWashLayer; + private AlertLayer alertLayer; + private TitleLayer titleLayer; + private TickerLayer tickerLayer; + private SoftButtonLayer buttonLayer; + private PTILayer ptiLayer; + private VirtualKeyboardLayer keyboardLayer; + private BodyLayer bodyLayer; + + // layout modes + /** + * Normal screen mode + */ + private static final int NORMAL_MODE = 0; + + /** + * Full screen mode when the current displayable + * is occupying as much screen as possible + */ + private static final int FULL_SCR_MODE = 1; + + /** + * Current screen mode + */ + int screenMode; + + /** Cache of screen commands */ + Command[] scrCmdCache; + + /** Number of screen commands in the cache */ + int scrCmdCount; + + /** Listener to notify when a screen command is selected */ + CommandListener scrCmdListener; + + /** Cache of selected item commands */ + Command[] itemCmdCache; + + /** Number of item commands in the cache */ + int itemCmdCount; + + /** Listener to notify when an item command is selected */ + ItemCommandListener itemCmdListener; + + CLayer[] mainLayers = new CLayer[LAST_LAYER]; + + /** Determines whether area of the window has been changed */ + boolean sizeChangedOccured = false; + + /** Indicates if body layer was checked for optimized Canvas painting */ + boolean bodyChecked = false; + + /** Indicates wheher body layer is overlapped with a visible layer */ + boolean bodyOverlapped = false; + + /** + * Construct a new MIDPWindow given the tunnel to the desired + * MIDP Display instance + * + * @param tunnel the "tunnel" to make calls from this java package + * back into the Display object in another package + */ + public MIDPWindow(ChamDisplayTunnel tunnel) { + super(ScreenSkin.IMAGE_BG, ScreenSkin.COLOR_BG, + tunnel.getDisplayWidth(), tunnel.getDisplayHeight()); + + this.tunnel = tunnel; + + for (int i = LAST_LAYER - 1; i >= 0; i-- ) { + createLayer(i); + } + } + + /** + * Request a repaint. This method does not require any bounds + * information as it is contained in each of the Chameleon layers. + * This method simply results in a repaint event being placed in + * the event queue for a future callback. + */ + public void requestRepaint() { + if (tunnel != null) { + tunnel.scheduleRepaint(); + } + } + + /** + * Set the title of this MIDPWindow. This would typically + * correspond to the title of the current displayable, and + * may result in the title layer appearing or disappearing. + * + * @param title the value of the title. null indicates there + * is no title. + */ + public void setTitle(String title) { + if (titleLayer.setTitle(title)) { + resize(); + } + requestRepaint(); + } + + /** + * Set the ticker of this MIDPWindow. This would typically + * correspond to the ticker of the current displayable, and + * may result in the ticker layer appearing or disappearing. + * + * @param ticker the current Ticker object. null indicates there + * is no ticker. + */ + public void setTicker(Ticker ticker) { + if (tickerLayer.setText((ticker != null) ? ticker.getString() : null)) { + resize(); + } + requestRepaint(); + } + + /** + * Alert this MIDPWindow that the given displayable is now current + * and should be shown on the screen. + * + * This will establish the given displayable on the screen, + * as well as reflect the displayable's title and ticker (if any). + * Special circumstances may occur if the displayable is an Alert, + * such as maintaining the current screen contents and showing the + * Alert in a popup. + * + * @param displayable the newly current displayable to show + * @param height the preferred height of the new displayable + */ + public void showDisplayable(Displayable displayable, int height) { + bodyLayer.opaque = (displayable instanceof Canvas); + + Ticker t = displayable.getTicker(); + tickerLayer.setText((t != null) ? t.getString() : null); + + if (displayable instanceof Alert) { + tickerLayer.toggleAlert(true); + buttonLayer.toggleAlert(true); + + // alert does not use title layer. The title is a part of content + titleLayer.setTitle(null); + + alertLayer.setAlert(true, (Alert)displayable, height); + + paintWash(false); + addLayer(alertLayer); + } else { + titleLayer.setTitle(displayable.getTitle()); + bodyLayer.setVisible(true); + } + addLayer(tickerLayer); + + resize(); + requestRepaint(); + } + + /** + * Alert this MIDPWindow that the given displayable is no longer + * current and should be removed from the screen. + * + * Special circumstances may occur if the displayable is an Alert, + * such as removing the popup and re-instating the previous + * displayable which was visible before the Alert popped up. + * + * @param displayable the newly current displayable to show + */ + public void hideDisplayable(Displayable displayable) { + if (displayable instanceof Alert) { + buttonLayer.toggleAlert(false); + tickerLayer.toggleAlert(false); + + paintWash(false); + alertLayer.setAlert(false, null, 0); + removeLayer(alertLayer); + } else { + bodyLayer.setVisible(false); + } + + removeLayer(tickerLayer); + + buttonLayer.dismissMenu(); + + // Make sure that not of the popups are shown + clearPopups(); + } + + /** + * Determines if the system menu is currently visible. This can be useful + * in determining the current isShown() status of the displayable. + * + * @return true if the system menu is up + */ + public boolean systemMenuUp() { + return buttonLayer.systemMenuUp(); + } + + /** + * Request a repaint of a region of the current displayable. + * This method specifically marks a region of the body layer + * (which renders the displayable's contents) as dirty and + * results in a repaint request being scheduled. The coordinates + * are in the space of the displayable itself - that is, 0,0 + * represents the top left corner of the body layer. + * + * @param x the x coordinate of the dirty region + * @param y the y coordinate of the dirty region + * @param w the width of the dirty region + * @param h the height of the dirty region + */ + public void repaintDisplayable(int x, int y, int w, int h) { + // We mark the body layer as dirty + if (alertLayer.visible) { + alertLayer.addDirtyRegion(x, y, w, h); + } else { + bodyLayer.addDirtyRegion(x, y, w, h); + } + requestRepaint(); + } + + /** + * Add the given layer to this window. This method is + * overridden from CWindow in order to special case + * popup layers. Popup layers can have their own commands + * which supercede those of the current displayable. + * + * @param layer the CLayer to add to this window + * @return true if new layer was added, false otherwise + */ + public boolean addLayer(CLayer layer) { + boolean added = super.addLayer(layer); + if (added) { + if (layer instanceof PopupLayer) { + PopupLayer popup = (PopupLayer)layer; + popup.setDirty(); + popup.visible = true; + + Command[] cmds = popup.getCommands(); + if (cmds != null) { + buttonLayer.updateCommandSet( + null, 0, null, cmds, cmds.length, + popup.getCommandListener()); + } + } + + if (layer instanceof PTILayer) { + ptiLayer = (PTILayer)layer; + mainLayers[PTI_LAYER] = layer; + resize(); + } else if (layer instanceof VirtualKeyboardLayer) { + keyboardLayer = (VirtualKeyboardLayer)layer; + mainLayers[KEYBOARD_LAYER] = layer; + resize(); + } else { + layer.update(mainLayers); + } + } + if (added && layer instanceof VirtualKeyboardLayer) { + keyboardLayer = (VirtualKeyboardLayer)layer; + mainLayers[KEYBOARD_LAYER] = layer; + resize(); + } + + return added; + } + + /** + * Remove the given layer from this window. This method is + * overridden from CWindow in order to special case popup + * layers. Popup layers can have their own commands which + * supercede those of the current displayable. In this case, + * the popup is removed and the commands in the soft button + * bar are restored to either the next top-most popup layer + * or the current displayable itself. + * + * @param layer the CLayer to remove from this window + * @return true if the layer was able to be removed + */ + public boolean removeLayer(CLayer layer) { + if (super.removeLayer(layer)) { + if (layer instanceof PopupLayer) { + if (layer == mainLayers[PTI_LAYER]) { + ptiLayer = null; + mainLayers[PTI_LAYER] = null; + resize(); + } + if (layer == mainLayers[KEYBOARD_LAYER]) { + keyboardLayer = null; + mainLayers[KEYBOARD_LAYER] = null; + resize(); + } + + // Now we update the command set with either the + // next top most popup or the original cached commands + PopupLayer p = getTopMostPopup(); + if (p != null && p.getCommands() != null) { + Command[] cmds = p.getCommands(); + buttonLayer.updateCommandSet( + null, 0, null, cmds, cmds.length, p.getCommandListener()); + } else { + buttonLayer.updateCommandSet( + itemCmdCache, itemCmdCount, itemCmdListener, + scrCmdCache, scrCmdCount, scrCmdListener); + } + } // instanceof + return true; + } // removeLayer + return false; + } + + /** + * Return bounds of BodyLayer currently + * @return array of bounds + */ + public int[] getBodyLayerBounds() { + int[] innerBounds = new int[4]; + System.arraycopy(bodyLayer.bounds,0,innerBounds,0,4); + return innerBounds; + + } + + /** + * Update this MIDPWindow's current command set to match the + * current displayable and possibly item selection. + * + * @param itemCommands the set of item specific commands + * @param itemCmdCount the number of item commands + * @param itemCmdListener the notification listener for item commands + * @param scrCommands the set of screen specific commands + * @param scrCmdCount the number of screen commands + * @param scrCmdListener the notification listener for screen commands + */ + public void updateCommandSet(Command[] itemCommands, + int itemCmdCount, + ItemCommandListener itemCmdListener, + Command[] scrCommands, + int scrCmdCount, + CommandListener scrCmdListener) + { + // We cache commands to easily reset them when a + // popup takes precedence and then is dismissed + this.itemCmdCache = itemCommands; + this.itemCmdCount = itemCmdCount; + this.itemCmdListener = itemCmdListener; + this.scrCmdCache = scrCommands; + this.scrCmdCount = scrCmdCount; + this.scrCmdListener = scrCmdListener; + + buttonLayer.updateCommandSet(itemCommands, itemCmdCount, + itemCmdListener, + scrCommands, scrCmdCount, + scrCmdListener); + resize(); + } + + /** + * Set this MIDPWindow's displayable to "fullscreen" mode. This + * will expand the region occupied by the current displayable to + * include the area previously occupied by the title and ticker + * if present + * + * @param onOff true if the displayable should be in fullscreen mode + */ + public void setFullScreen(boolean onOff) { + if (onOff) { + setMode(FULL_SCR_MODE); + } else { + setMode(NORMAL_MODE); + } + } + + /** + * Update the current layout + */ + public void updateLayout() { + resize(); + requestRepaint(); + } + + /** + * Changes layout mode. + * + * @param mode the mode to be set + */ + private void setMode(int mode) { + screenMode = mode; + updateLayout(); + } + + /** + * Determines if window is in full screen mode. + * + * @return true if in full screen mode + */ + public boolean isInFullScreenMode() { + return screenMode == FULL_SCR_MODE; + } + + + + + /** + * Called to paint a wash over the background of this window. + * Used by SoftButtonLayer when the system menu pops up, and + * internally when an Alert is shown. + * + * @param onOff A flag indicating if the wash should be on or off + */ + public void paintWash(boolean onOff) { + if (alertLayer.visible) { + addLayer(washLayer); + if (onOff) { + addLayer(alertWashLayer); + } else { + removeLayer(alertWashLayer); + + tickerLayer.addDirtyRegion(); + alertLayer.addDirtyRegion(); + } + } else { + removeLayer(alertWashLayer); + if (onOff) { + addLayer(washLayer); + } else { + removeLayer(washLayer); + + tickerLayer.addDirtyRegion(); + titleLayer.addDirtyRegion(); + + if (ptiLayer != null) { + ptiLayer.addDirtyRegion(); + } + } + } + } + + /** + * Returns the left soft button (one). + * + * @return the command that's tied to the left soft button + */ + public Command getSoftOne() { + return buttonLayer.getSoftOne(); + } + + /** + * Returns the command array tied to the right soft button (two). + * + * @return the command array that's tied to the right soft button + */ + public Command[] getSoftTwo() { + return buttonLayer.getSoftTwo(); + } + + /** + * Called by soft button layer when interactive state of it + * has been changed + * + * @param interactive if soft buttons are currently interactive. + */ + public void onSoftButtonInteractive(boolean interactive) { + if (FULL_SCR_MODE == screenMode) { + // IMPL NOTES: in full screen mode we hide/show soft button layer + // depending on its interactiveness, so we should update layout + updateLayout(); + } + } + + /** + * Returns true if the point lies in the bounds of commnad layer + * @param x the "x" coordinate of the point + * @param y the "y" coordinate of the point + * @return true if the point lies in the bounds of commnad layer + */ + public boolean belongToCmdLayers(int x, int y) { + return buttonLayer.belongToCmdLayers(x,y); + } + + /** + * Set the current vertical scroll position and proportion. + * + * @param scrollPosition vertical scroll position. + * @param scrollProportion vertical scroll proportion. + * @return true if set vertical scroll occues + */ + public boolean setVerticalScroll(int scrollPosition, int scrollProportion) { + + BodyLayer layer = null; + if (alertLayer.isVisible()) { + layer = alertLayer; + } else if (bodyLayer.isVisible()) { + layer = bodyLayer; + } + + if (layer != null && layer.setVerticalScroll(scrollPosition, scrollProportion)) { + setDirty(); + sizeChangedOccured = true; + return true; + } + + return false; + } + + /** + * Get the current x anchor coordinate for the body layer (the body + * layer renders the contents of the current displayable). + * + * @return the x anchor coordinate of the body layer + */ + public int getBodyAnchorX() { + return bodyLayer.bounds[X]; + } + + /** + * Get the current y anchor coordinate for the body layer (the body + * layer renders the contents of the current displayable). + * + * @return the y anchor coordinate of the body layer + */ + public int getBodyAnchorY() { + return bodyLayer.bounds[Y]; + } + + /** + * Get the current x anchor coordinate for the alert layer (the body + * layer renders the contents of the current displayable). + * + * @return the x anchor coordinate of the alert layer + */ + public int getAlertAnchorX() { + return alertLayer.bounds[X]; + } + + /** + * Get the current y anchor coordinate for the alert layer (the body + * layer renders the contents of the current displayable). + * + * @return the y anchor coordinate of the alert layer + */ + public int getAlertAnchorY() { + return alertLayer.bounds[Y]; + } + + /** + * Get the current width of the body layer (the body + * layer renders the contents of the current displayable). + * + * @return the width of the body layer + */ + public int getBodyWidth() { + return bodyLayer.bounds[W]; + } + + /** + * Get the current height of the body layer (the body + * layer renders the contents of the current displayable). + * + * @return the height of the body layer + */ + public int getBodyHeight() { + return bodyLayer.bounds[H]; + } + + /** + * Calculate the width of some default Body layer wich is still not rendered on the screen + * depending on the screen mode and the layers attached to the screen + * @param width screen width + * @param isFullScn true if the full scren is set for the body layer + * @param scrollBarIsVisible true if the scroll bar is in use for the body layer + * @return width of the paticular body layer + */ + public static int getDefaultBodyWidth(int width, + boolean isFullScn, + boolean scrollBarIsVisible) { + int w = width; + // TODO: scroll arrows (bar? ) indicator has to be hidden? + if (scrollBarIsVisible) { + w -= ScrollIndSkin.WIDTH; + } + return w; + } + + /** + * Calculate the height of some default Body layer wich is still not rendered on the screen + * depending on the screen mode and the layers attached to the screen + * param height scren height + * @param isFullScn true if the full scren is set for the body layer + * @param titleIsVisible true if the title is attached + * @param tickerIsVisible true if the ticker is attached + * @param softBtnLayerIsVisible true if command layer is visible + * @return height of the paticular body layer + */ + public static int getDefaultBodyHeight(int height, + boolean isFullScn, + boolean titleIsVisible, + boolean tickerIsVisible, + boolean softBtnLayerIsVisible) { + int h = height; + if (!isFullScn) { + if (titleIsVisible) { + h -= TitleSkin.HEIGHT; + } + if (tickerIsVisible) { + h -= TickerSkin.HEIGHT; + } + if (softBtnLayerIsVisible) { + h -= SoftButtonSkin.HEIGHT; + } + } + return h; + } + + + /** + * Get the current width of the alert layer (the body + * layer renders the contents of the current displayable). + * + * @return the width of the alert layer + */ + public int getAlertWidth() { + return alertLayer.bounds[W]; + } + + /** + * Get the current height of the alert layer (the body + * layer renders the contents of the current displayable). + * + * @return the height of the alert layer + */ + public int getAlertHeight() { + return alertLayer.bounds[H]; + } + + /** + * Utility method to determine if the given point lies within + * the bounds of body layer. The point should be in the coordinate + * space of this layer's containing CWindow. + * + * @param x the "x" coordinate of the point + * @param y the "y" coordinate of the point + * @return true if the coordinate lies in the bounds of this layer + */ + public boolean bodyContainsPoint(int x, int y) { + return bodyLayer.containsPoint(x, y); + } + + /** + * MIDPWindow overrides the parent paint method in order to + * do special effects such as paint a "wash" over the background + * when a dialog is up. Also in an effort to call + * {@link javax.microedition.lcdui.Displayable#sizeChanged } + * method before painting. This implementation determine whether size + * has been changed and calls sizeChanged() if it's so. + * Anyway it invokes the base class's {@link CWindow#paint} method. + * + * @param g The graphics object to use to paint this MIDP window. + * @param refreshQ The chameleon graphics queue. + */ + public void callPaint(Graphics g, CGraphicsQ refreshQ) { + if (sizeChangedOccured) { + if (tunnel != null) { + int w = getBodyWidth(); + int h = getBodyHeight(); + tunnel.callSizeChanged(w, h); + sizeChangedOccured = false; + } + } + super.paint(g, refreshQ); + } + + /** + * This method is an optimization which allows Display to bypass + * the Chameleon paint engine logic and directly paint an animating + * canvas. Display will call this method with the graphics context + * and this method will either return false, indicating the Chameleon + * paint engine should not be bypassed, or will return true and will + * setup the graphics context for the canvas to be painted directly. + * + * @param g the graphics context to setup + * @return true if Chameleon's paint logic can be bypassed and the + * canvas can be rendered directly. + */ + public boolean setGraphicsForCanvas(Graphics g) { + // IMPL_NOTE: Only Canvas painting specially doesn't change dirty + // state of the owner window, however it is not enough to bypass + // the Chameleon paint engine. Body layer holding the Canvas + // should be not overlapped by a visible layer also. + if (super.dirty) { + // Schedule next overlapping check + bodyChecked = false; + return false; + } + if (!bodyChecked) { + bodyOverlapped = !bodyLayer.opaque || + isOverlapped(bodyLayer); + bodyChecked = true; + } + if (!bodyOverlapped) { + bodyLayer.setGraphicsForCanvas(g); + return true; + } + return false; + } + + /** + * Internal method to resize window and its content layers + * according to a size changes in the loaded skins. + * This is important to re-calculate whenever things such as + * titles, tickers, fullscreen mode, etc. change state. + */ + public void resize() { + super.resize(tunnel.getDisplayWidth(), tunnel.getDisplayHeight()); + + int oldHeight = bodyLayer.bounds[H]; + int oldWidth = bodyLayer.bounds[W]; + switch (screenMode) { + case FULL_SCR_MODE: + // TODO: scroll arrows (bar? ) indicator has to be hidden? + titleLayer.visible = false; + tickerLayer.visible = false; + buttonLayer.visible = + buttonLayer.isInteractive(); + break; + case NORMAL_MODE: + titleLayer.visible = + (titleLayer.getTitle() != null); + tickerLayer.visible = + (tickerLayer.getText() != null); + buttonLayer.visible = true; + break; + default: + Logging.report(Logging.ERROR, LogChannels.LC_HIGHUI, + "MIDPWindow: screenMode=" + screenMode); + return; + } + + for (int i = 0; i < LAST_LAYER; i++) { + CLayer l = mainLayers[i]; + if (l != null && l.visible) { + l.update(mainLayers); + } + } + + if (bodyLayer.bounds[W] != oldWidth || + bodyLayer.bounds[H] != oldHeight) { + setDirty(); + sizeChangedOccured = true; + } + } + + /** + * Internal method to clear all current popups. This occurs if a + * change of displayable occurs, as all popups are treated as belonging + * to the current displayable. + */ + protected void clearPopups() { + synchronized (super.layers) { + for (CLayerElement le = super.layers.getTop(); + le != null; le = le.getLower()) { + CLayer l = le.getLayer(); + if (l instanceof PopupLayer) { + removeLayer(l); + } + } + } + } + + /** + * Gets the "top" most Popup layer added to this body layer. + * If there are no popups, this method returns null. + * + * @return the top most popup layer, or null if there are none. + */ + public PopupLayer getTopMostPopup() { + synchronized (super.layers) { + for (CLayerElement le = super.layers.getTop(); + le != null; le = le.getLower()) { + CLayer l = le.getLayer(); + if (l instanceof PopupLayer) { + return (PopupLayer)l; + } + } + } + return null; + } + + /** + * create new layer by id and launch addLayer() + * @param id - layer id + */ + private void createLayer(int id) { + switch (id) { + case PTI_LAYER: + break; + case TITLE_LAYER: + titleLayer = new TitleLayer(); + mainLayers[id] = titleLayer; + addLayer(titleLayer); + break; + case TICKER_LAYER: + tickerLayer = new TickerLayer(); + mainLayers[id] = tickerLayer ; + break; + case BTN_LAYER: + buttonLayer = new SoftButtonLayer(tunnel); + mainLayers[id] = buttonLayer; + addLayer(buttonLayer); + break; + case ALERT_LAYER: + alertLayer = new AlertLayer(tunnel); + mainLayers[id] = alertLayer; + break; + case WASH_LAYER: + washLayer = new WashLayer(); + mainLayers[id] = washLayer; + break; + case ALERT_WASH_LAYER: + alertWashLayer = new WashLayer(); + mainLayers[id] = alertWashLayer; + break; + case KEYBOARD_LAYER: + break; + case BODY_LAYER: + bodyLayer = new BodyLayer(tunnel); + mainLayers[id] = bodyLayer; + addLayer(bodyLayer); + break; + } + } +} + diff --git a/java/custom/javax/microedition/lcdui/Canvas.java b/java/custom/javax/microedition/lcdui/Canvas.java new file mode 100644 index 00000000..85fddc29 --- /dev/null +++ b/java/custom/javax/microedition/lcdui/Canvas.java @@ -0,0 +1,1037 @@ +/* + * + * + * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER + * + * This program 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. + * + * This program 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 at /legal/license.txt). + * + * 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 Sun Microsystems, Inc., 4150 Network Circle, Santa + * Clara, CA 95054 or visit www.sun.com if you need additional + * information or have any questions. + */ + +package javax.microedition.lcdui; + +/* import javax.microedition.lcdui.KeyConverter; */ + +import com.sun.midp.configurator.Constants; + +/** + * The Canvas class is a base class for writing + * only. + * applications that need to + * handle low-level events and to issue graphics calls for drawing to the + * display. Game applications will likely make heavy use of the + * Canvas class. + * From an application development perspective, the Canvas class is + * interchangeable with standard Screen classes, so an + * application may mix and + * match Canvas with high-level screens as needed. For + * example, a List screen + * may be used to select the track for a racing game, and a + * Canvas subclass + * would implement the actual game. + * + *

The Canvas provides the developer with methods to + * handle game actions, + * key events, and + * pointer events (if supported by the device). Methods are + * also provided to identify the device's capabilities and mapping of + * keys to game actions. + * The key events are reported with respect to key codes, which + * are directly bound to concrete keys on the device, use of which may hinder + * portability. Portable applications should use game actions instead of key + * codes.

+ * + *

Like other subclasses of Displayable, the + * Canvas class allows the + * application to register a listener for commands. Unlike other + * Displayables, + * however, the Canvas class requires applications to + * subclass it in order to + * use it. The paint() method is declared + * abstract, and so the + * application must provide an implementation in its subclass. Other + * event-reporting methods are not declared abstract, and their + * default implementations are empty (that is, they do nothing). This allows + * the application to override only the methods that report events in which the + * application has interest.

+ * + *

This is in contrast to the {@link Screen Screen} classes, which allow + * the application to define listeners and to register them with instances of + * the Screen classes. This style is not used for the + * Canvas class, because + * several new listener interfaces would need to be created, one for each kind + * of event that might be delivered. An alternative would be to have fewer + * listener interfaces, but this would require listeners to filter out events + * in which they had no interest.

+ * + * + *

Key Events

+ * + *

Applications receive keystroke events in which the individual keys are + * named within a space of key codes. Every key for which events are + * reported to MIDP applications is assigned a key code. + * The key code values are unique for each hardware key unless two keys are + * obvious synonyms for each other. + * MIDP defines the following key codes: + * {@link #KEY_NUM0 KEY_NUM0}, + * {@link #KEY_NUM1 KEY_NUM1}, + * {@link #KEY_NUM2 KEY_NUM2}, + * {@link #KEY_NUM3 KEY_NUM3}, + * {@link #KEY_NUM4 KEY_NUM4}, + * {@link #KEY_NUM5 KEY_NUM5}, + * {@link #KEY_NUM6 KEY_NUM6}, + * {@link #KEY_NUM7 KEY_NUM7}, + * {@link #KEY_NUM8 KEY_NUM8}, + * {@link #KEY_NUM9 KEY_NUM9}, + * {@link #KEY_STAR KEY_STAR}, and + * {@link #KEY_POUND KEY_POUND}. + * + * (These key codes correspond to keys on a ITU-T standard telephone keypad.) + * Other keys may be present on the keyboard, and they will generally have key + * codes distinct from those list above. In order to guarantee portability, + * applications should use only the standard key codes.

+ * + *

The standard key codes' values are equal to the Unicode encoding for the + * character that represents the key. If the device includes any other keys + * that have an obvious correspondence to a Unicode character, their key code + * values should equal the Unicode encoding for that character. For keys that + * have no corresponding Unicode character, the implementation must use + * negative values. Zero is defined to be an invalid key code. It is thus + * possible for an application to convert a keyCode into a Unicode character + * using the following code:

+ * + * + * + * + * + *
+ *

+ *    if (keyCode > 0) {
+ *        char ch = (char)keyCode;    
+ *        // ...
+ *    }    
+ *
+ *

This technique is useful only in certain limited cases. In particular, + * it is not sufficient for full textual input, because it does not handle + * upper and lower case, keyboard shift states, and characters that require + * more than one keystroke to enter. For textual input, applications should + * always use {@link TextBox TextBox} or {@link TextField TextField} + * objects.

+ * + *

It is sometimes useful to find the name of a key in order to + * display a message about this key. In this case the application may use the + * {@link #getKeyName(int) getKeyName()} method to find a key's name.

+ * + * + *

Game Actions

+ *

+ * Portable applications that need arrow key events and gaming-related events + * should use game actions in preference to key codes and key names. + * MIDP defines the following game actions: + * {@link #UP UP}, + * {@link #DOWN DOWN}, + * {@link #LEFT LEFT}, + * {@link #RIGHT RIGHT}, + * {@link #FIRE FIRE}, + * {@link #GAME_A GAME_A}, + * {@link #GAME_B GAME_B}, + * {@link #GAME_C GAME_C}, and + * {@link #GAME_D GAME_D}. + *

+ * + *

Each key code may be mapped to at most one game action. However, a game + * action may be associated with more than one key code. The application can + * translate a key code into a game action using the {@link #getGameAction(int) + * getGameAction(int keyCode)} method, and it can translate a game action into + * a key code using the {@link #getKeyCode(int) getKeyCode(int gameAction)} + * method. There may be multiple keycodes associated with a particular game + * action, but getKeyCode returns only one of them. Supposing + * that g is a valid game action and k + * is a valid key code for a key associated with a game action, consider + * the following expressions:

+ * + * + * + * + * + *
+ *

+ *    g == getGameAction(getKeyCode(g))     // (1)
+ *    k == getKeyCode(getGameAction(k))     // (2)    
+ *
+ *

Expression (1) is always true. However, expression (2) + * might be true but is not necessarily true.

+ * + *

The implementation is not allowed to change the mapping of game + * actions and key codes during execution of the application.

+ * + *

Portable applications that are interested in using game actions should + * translate every key event into a game action by calling the {@link + * #getGameAction getGameAction()} method and then testing the result. For + * example, on some devices the game actions UP, + * DOWN, LEFT and RIGHT may be + * mapped to 4-way navigation arrow keys. In this case, + * getKeyCode(UP) would + * return a device-dependent code for the up-arrow key. On other devices, a + * possible mapping would be on the number keys 2, + * 4, 6 and 8. In this case, + * getKeyCode(UP) would return KEY_NUM2. In + * both cases, the getGameAction() + * method would return the LEFT game action when the user + * presses the key that + * is a "natural left" on her device.

+ * + * + *

Commands

+ * + *

It is also possible for the user to issue {@link Command commands} when + * a canvas is current. Commands are mapped to keys and menus in a + * device-specific fashion. For some devices the keys used for commands may + * overlap with the keys that will deliver key code events to the canvas. If + * this is the case, the device will provide a means transparent to the + * application that enables the user to select a mode that determines whether + * these keys will deliver commands or key code events to the application. + * When the Canvas is in normal mode (see below), + * the set of key code events available to a canvas will not change depending + * upon the number of commands present or the presence of a command listener. + * When the Canvas is in full-screen mode, if there is no + * command listener + * present, the device may choose to deliver key code events for keys that + * would otherwise be reserved for delivery of commands. Game developers + * should be aware that access to commands will vary greatly across devices, + * and that requiring the user to issue commands during game play may have a + * great impact on the ease with which the game can be played.

+ * + * + *

Event Delivery

+ * + *

The Canvas object defines several methods that are + * called by the + * implementation. These methods are primarily for the purpose of delivering + * events to the application, and so they are referred to as + * event delivery methods. The set of methods is:

+ * + *
    + *
  • showNotify()
  • + *
  • hideNotify()
  • + *
  • keyPressed()
  • + *
  • keyRepeated()
  • + *
  • keyReleased()
  • + *
  • pointerPressed()
  • + *
  • pointerDragged()
  • + *
  • pointerReleased()
  • + *
  • paint()
  • + *
+ * + *

These methods are all called serially. That is, the implementation will + * never call an event delivery method before a prior call to any of + * the event delivery methods has returned. The + * serviceRepaints() method is an exception to this rule, as it + * blocks until paint() is called and returns. This will occur + * even if the application is in the midst of one of the event delivery + * methods when it calls serviceRepaints().

+ * + *

The {@link Display#callSerially Display.callSerially()} method can be + * used to serialize some application-defined work with the event stream. + * For further information, see the + * Event Handling and + * Concurrency + * sections of the package summary.

+ * + *

The key-related, pointer-related, and paint() methods + * will only be called while the Canvas is actually + * visible on the output + * device. These methods will therefore only be called on this + * Canvas object + * only after a call to showNotify() and before a call to + * hideNotify(). After + * hideNotify() has been called, none of the key, + * pointer, and paint + * methods will be called until after a + * subsequent call to + * showNotify() has returned. A call to a + * run() method resulting from + * callSerially() may occur irrespective of calls to + * showNotify() and + * hideNotify().

+ * + *

The {@link #showNotify() showNotify()} method is called prior to the + * Canvas actually being made visible on the display, and + * the {@link + * #hideNotify() hideNotify()} method is called after the + * Canvas has been + * removed from the display. The visibility state of a + * Canvas (or any other + * Displayable object) may be queried through the use of the {@link + * Displayable#isShown() Displayable.isShown()} method. The change in + * visibility state of a Canvas may be caused by the + * application management + * software moving MIDlets between foreground and + * background states, or by the + * system obscuring the Canvas with system screens. + * Thus, the calls to + * showNotify() and hideNotify() are not + * under the control of the MIDlet and + * may occur fairly frequently. Application developers are encouraged to + * perform expensive setup and teardown tasks outside the + * showNotify() and + * hideNotify() methods in order to make them as + * lightweight as possible.

+ * + * + *

A Canvas can be in normal mode or in full-screen + * mode. In normal mode, + * space on the display may be occupied by command labels, a title, and a + * ticker. By setting a Canvas into full-screen mode, + * the application is + * requesting that the Canvas occupy as much of the + * display space as is + * possible. In full-screen mode, the title and ticker are not displayed even + * if they are present on the Canvas, and + * Commands may be presented using some + * alternative means (such as through a pop-up menu). Note that the + * implementation may still consume a portion of the display for things like + * status indicators, even if the displayed Canvas is in + * full-screen mode. In + * full-screen mode, although the title is not displayed, its text may still + * be used for other purposes, such as for the title of a pop-up menu of + * Commands.

+ * + *

Canvas objects are in normal mode by default. The normal vs. + * full-screen mode setting is controlled through the use of the {@link + * #setFullScreenMode} method.

+ * + *

Calling {@link #setFullScreenMode} may result in + * {@link #sizeChanged(int, int) sizeChanged()} being called. + * The default implementation of this method does nothing. + * The application can override this method to handle changes + * in size of available drawing area. + *

+ * + *

Note: As mentioned in the "Specification + * Requirements" section + * of the overview, implementations must provide the user with an indication + * of network usage. If the indicator is rendered on screen, + * it must be visible when network activity occurs, even when + * the Canvas is in full-screen mode.

+ * + * @since MIDP 1.0 + */ + +public abstract class Canvas extends Displayable { + + /** + * Constant for the UP game action. + * + *

Constant value 1 is set to UP.

+ */ + public static final int UP = 1; + + /** + * Constant for the DOWN game action. + * + *

Constant value 6 is set to DOWN.

+ */ + public static final int DOWN = 6; + + + /** + * Constant for the LEFT game action. + * + *

Constant value 2 is set to LEFT.

+ */ + public static final int LEFT = 2; + + /** + * Constant for the RIGHT game action. + * + *

Constant value 5 is set to RIGHT.

+ */ + public static final int RIGHT = 5; + + /** + * Constant for the FIRE game action. + * + *

Constant value 8 is set to FIRE.

+ */ + public static final int FIRE = 8; + + + /** + * Constant for the general purpose "A" game action. + * + *

Constant value 9 is set to GAME_A.

+ */ + public static final int GAME_A = 9; + + /** + * Constant for the general purpose "B" game action. + * + *

Constant value 10 is set to GAME_B.

+ */ + public static final int GAME_B = 10; + + /** + * Constant for the general purpose "C" game action. + * + *

Constant value 11 is set to GAME_C.

+ */ + public static final int GAME_C = 11; + + /** + * Constant for the general purpose "D" game action. + * + *

Constant value 12 is set to GAME_D.

+ */ + public static final int GAME_D = 12; + + /** + * keyCode for ITU-T key 0. + * + *

Constant value 48 is set to KEY_NUM0.

+ */ + public static final int KEY_NUM0 = 48; + + /** + * keyCode for ITU-T key 1. + * + *

Constant value 49 is set to KEY_NUM1.

+ */ + public static final int KEY_NUM1 = 49; + + /** + * keyCode for ITU-T key 2. + * + *

Constant value 50 is set to KEY_NUM2.

+ */ + public static final int KEY_NUM2 = 50; + + /** + * keyCode for ITU-T key 3. + * + *

Constant value 51 is set to KEY_NUM3.

+ */ + public static final int KEY_NUM3 = 51; + + /** + * keyCode for ITU-T key 4. + * + *

Constant value 52 is set to KEY_NUM4.

+ */ + public static final int KEY_NUM4 = 52; + + /** + * keyCode for ITU-T key 5. + * + *

Constant value 53 is set to KEY_NUM5.

+ */ + public static final int KEY_NUM5 = 53; + + /** + * keyCode for ITU-T key 6. + * + *

Constant value 54 is set to KEY_NUM6.

+ */ + public static final int KEY_NUM6 = 54; + + /** + * keyCode for ITU-T key 7. + * + *

Constant value 55 is set to KEY_NUM7.

+ */ + public static final int KEY_NUM7 = 55; + + /** + * keyCode for ITU-T key 8. + * + *

Constant value 56 is set to KEY_NUM8.

+ */ + public static final int KEY_NUM8 = 56; + + /** + * keyCode for ITU-T key 9. + * + *

Constant value 57 is set to KEY_NUM09.

+ */ + public static final int KEY_NUM9 = 57; + + /** + * keyCode for ITU-T key "star" (*). + * + *

Constant value 42 is set to KEY_STAR.

+ */ + public static final int KEY_STAR = 42; + + /** + * keyCode for ITU-T key "pound" (#). + * + *

Constant value 35 is set to KEY_POUND.

+ */ + public static final int KEY_POUND = 35; + + /** + * Constructs a new Canvas object. + */ + protected Canvas() { + synchronized (Display.LCDUILock) { + displayableLF = canvasLF = LFFactory.getFactory().getCanvasLF(this); + } + } + + /** + * Checks if the Canvas is double buffered by the + * implementation. + * @return true if double buffered, + * false otherwise + */ + public boolean isDoubleBuffered() { + // SYNC NOTE: return of atomic value, no locking necessary + return Constants.IS_DOUBLE_BUFFERED; + } + + /** + * Checks if the platform supports pointer press and release events. + * @return true if the device supports pointer events + */ + public boolean hasPointerEvents() { + // SYNC NOTE: return of atomic value, no locking necessary + return Constants.POINTER_SUPPORTED; + } + + /** + * Checks if the platform supports pointer motion events (pointer dragged). + * Applications may use this method to determine if the platform is capable + * of supporting motion events. + * @return true if the device supports pointer motion events + */ + public boolean hasPointerMotionEvents() { + // SYNC NOTE: return of atomic value, no locking necessary + return Constants.MOTION_SUPPORTED; + } + + /** + * Checks if the platform can generate repeat events when key + * is kept down. + * @return true if the device supports repeat events + */ + public boolean hasRepeatEvents() { + // SYNC NOTE: return of atomic value, no locking necessary + return Constants.REPEAT_SUPPORTED; + } + + /** + * Gets a key code that corresponds to the specified game action on the + * device. The implementation is required to provide a mapping for every + * game action, so this method will always return a valid key code for + * every game action. See above for further + * discussion of game actions. There may be multiple keys associated + * with the same game action; however, this method will return only one of + * them. Applications should translate the key code of every key event + * into a game action using {@link #getGameAction} and then interpret the + * resulting game action, instead of generating a table of key codes at + * using this method during initialization. + * + *

The mapping between key codes and game actions + * will not change during the execution of the application.

+ * + * @param gameAction the game action + * @return a key code corresponding to this game action + * @throws IllegalArgumentException if gameAction + * is not a valid game action + */ + public int getKeyCode(int gameAction) { + // SYNC NOTE: no locking necessary as we are doing a static + // table lookup and getKeyCode() is implemented natively + int n = KeyConverter.getKeyCode(gameAction); + + if (n == 0) { + throw new IllegalArgumentException(); + } + + return n; + } + + /** + * Gets an informative key string for a key. The string returned will + * resemble the text physically printed on the key. This string is + * suitable for displaying to the user. For example, on a device + * with function keys F1 through F4, + * calling this method on the keyCode for + * the F1 key will return the string + * "F1". A typical use for this string + * will be to compose help text such as "Press + * F1 to proceed." + * + *

This method will return a non-empty string for every valid key code. + *

+ * + *

There is no direct mapping from game actions to key names. To get + * the string name for a game action GAME_A, the + * application must call

+ * + * + * + * + * + *
+ *

+     *    getKeyName(getKeyCode(GAME_A));    
+ *
+ * @param keyCode the key code being requested + * @return a string name for the key + * @throws IllegalArgumentException if keyCode + * is not a valid key code + */ + public String getKeyName(int keyCode) { + // SYNC NOTE: no locking necessary as we are doing a static + // table lookup and getKeyName() is implemented natively + String s = KeyConverter.getKeyName(keyCode); + + if (s == null) { + throw new IllegalArgumentException(); + } + + return s; + } + + /** + * Gets the game action associated with the given key code of the + * device. Returns zero if no game action is associated with this key + * code. See above for further discussion of + * game actions. + * + *

The mapping between key codes and game actions + * will not change during the execution of the application.

+ * + * @param keyCode the key code + * @return the game action corresponding to this key, or + * 0 if none + * @throws IllegalArgumentException if keyCode + * is not a valid key code + */ + public int getGameAction(int keyCode) { + // SYNC NOTE: no locking necessary as we are doing a static + // table lookup and getGameAction() is implemented natively + int n = KeyConverter.getGameAction(keyCode); + + if (n == -1) { + throw new IllegalArgumentException(); + } + + return n; + } + + /** + * Controls whether the Canvas is in full-screen mode + * or in normal mode. + * + * @param mode true if the Canvas + * is to be in full screen mode, false otherwise + * + */ + public void setFullScreenMode(boolean mode) { + + // Do not do everything if already in the mode given + if (mode == isInFullScreenMode) { + return; + } + + // Ask the Displayable to set fullscrn natively + // To handle it better (and optimize correctly), + // it should sent an event to the event queue. + synchronized (Display.LCDUILock) { + isInFullScreenMode = mode; + } + canvasLF.uSetFullScreenMode(mode); + } + + /** + * Called when a key is pressed. + * + *

The getGameAction() method can be called to + * determine what game action, if any, is mapped to the key. + * Class Canvas has an empty implementation of this method, and + * the subclass has to redefine it if it wants to listen this method. + * @param keyCode the key code of the key that was pressed + */ + protected void keyPressed(int keyCode) { + } + + /** + * Called when a key is repeated (held down). + * + *

The getGameAction() method can + * be called to determine what game action, + * if any, is mapped to the key. + * Class Canvas has an empty implementation of this method, and + * the subclass has to redefine it if it wants to listen this method. + *

+ * @param keyCode the key code of the key that was repeated + * @see #hasRepeatEvents() + */ + protected void keyRepeated(int keyCode) { + } + + /** + * Called when a key is released. + *

+ * The getGameAction() method can be called to + * determine what game action, if any, is mapped to the key. + * Class Canvas has an empty implementation of this method, and + * the subclass has to redefine it if it wants to listen this method. + *

+ * @param keyCode the key code of the key that was released + */ + protected void keyReleased(int keyCode) { + } + + /** + * Called when the pointer is pressed. + * + *

+ * The {@link #hasPointerEvents() hasPointerEvents()} + * method may be called to determine if the device supports pointer events. + * Class Canvas has an empty implementation of this method, and + * the subclass has to redefine it if it wants to listen this method. + *

+ * @param x the horizontal location where the pointer was pressed (relative + * to the Canvas) + * @param y the vertical location where the pointer was pressed + * (relative to the Canvas) + */ + protected void pointerPressed(int x, int y) { + } + + /** + * Called when the pointer is released. + * + *

+ * The {@link #hasPointerEvents() hasPointerEvents()} + * method may be called to determine if the device supports pointer events. + * Class Canvas has an empty implementation of this method, and + * the subclass has to redefine it if it wants to listen this method. + *

+ * @param x the horizontal location where the pointer was released + * (relative to the Canvas) + * @param y the vertical location where the pointer was released + * (relative to the Canvas) + */ + protected void pointerReleased(int x, int y) { + } + + /** + * Called when the pointer is dragged. + * + *

+ * The {@link #hasPointerMotionEvents() hasPointerMotionEvents()} + * method may be called to determine if the device supports pointer events. + * Class Canvas has an empty implementation of this method, and + * the subclass has to redefine it if it wants to listen this method. + *

+ * @param x the horizontal location where the pointer was dragged + * (relative to the Canvas) + * @param y the vertical location where the pointer was dragged + * (relative to the Canvas) + */ + protected void pointerDragged(int x, int y) { + } + + /** + * Requests a repaint for the specified region of the + * Canvas. Calling + * this method may result in subsequent call to + * paint(), where the passed + * Graphics object's clip region will include at + * least the specified + * region. + * + *

If the canvas is not visible, or if width and height are zero or + * less, or if the rectangle does not specify a visible region of + * the display, this call has no effect.

+ * + *

The call to paint() occurs asynchronously of + * the call to repaint(). + * That is, repaint() will not block waiting for + * paint() to finish. The + * paint() method will either be called after the + * caller of repaint() + * returns + * to the implementation (if the caller is a callback) or on another thread + * entirely.

+ * + *

To synchronize with its paint() routine, + * applications can use either + * {@link Display#callSerially(Runnable) Display.callSerially()} or + * {@link #serviceRepaints() serviceRepaints()}, or they can code explicit + * synchronization into their paint() routine.

+ * + *

The origin of the coordinate system is above and to the left of the + * pixel in the upper left corner of the displayable area of the + * Canvas. + * The X-coordinate is positive right and the Y-coordinate is + * positive downwards. + *

+ * + * @param x the x coordinate of the rectangle to be repainted + * @param y the y coordinate of the rectangle to be repainted + * @param width the width of the rectangle to be repainted + * @param height the height of the rectangle to be repainted + * + * @see Display#callSerially(Runnable) + * @see #serviceRepaints() + */ + public final void repaint(int x, int y, int width, int height) { + synchronized (Display.LCDUILock) { + if (width > 0 && height > 0) { + canvasLF.lRepaint(x, y, width, height, null); + } + } + } + + /** + * Requests a repaint for the entire Canvas. The + * effect is identical to + *

repaint(0, 0, getWidth(), getHeight()); + */ + public final void repaint() { + synchronized (Display.LCDUILock) { + canvasLF.lRepaint(); + } + } + + /** + * Forces any pending repaint requests to be serviced immediately. This + * method blocks until the pending requests have been serviced. If + * there are + * no pending repaints, or if this canvas is not visible on the display, + * this call does nothing and returns immediately. + * + *

Warning: This method blocks until the call to the + * application's paint() method returns. The + * application has no + * control over + * which thread calls paint(); it may vary from + * implementation to + * implementation. If the caller of serviceRepaints() + * holds a lock that the + * paint() method acquires, this may result in + * deadlock. Therefore, callers + * of serviceRepaints() must not hold any + * locks that might be + * acquired within the paint() method. The + * {@link Display#callSerially(Runnable) Display.callSerially()} + * method provides a facility where an application can be called back after + * painting has completed, avoiding the danger of deadlock. + *

+ * + * @see Display#callSerially(Runnable) + */ + public final void serviceRepaints() { + // SYNC NOTE: unlike most public API methods, no locking is done + // here. This is necessary because Display.serviceRepaints() + // needs to handle its own locking. + canvasLF.uServiceRepaints(); + } + + /** + * The implementation calls showNotify() + * immediately prior to this Canvas being made + * visible on the display. + * Canvas subclasses may override + * this method to perform tasks before being shown, such + * as setting up animations, starting timers, etc. + * The default implementation of this method in class + * Canvas is empty. + */ + protected void showNotify() { + } + + /** + * The implementation calls hideNotify() shortly + * after the Canvas has been + * removed from the display. + * Canvas subclasses may override this method in + * order to pause + * animations, + * revoke timers, etc. The default implementation of this + * method in class Canvas is empty. + */ + protected void hideNotify() { + } + + /** + * Renders the Canvas. The application must implement + * this method in + * order to paint any graphics. + * + *

The Graphics object's clip region defines the + * area of the screen + * that is considered to be invalid. A correctly-written + * paint() routine + * must paint every pixel within this region. This is necessary + * because the implementation is not required to clear the region prior to + * calling paint() on it. Thus, failing to paint + * every pixel may result + * in a portion of the previous screen image remaining visible.

+ * + *

Applications must not assume that + * they know the underlying source of the paint() + * call and use this + * assumption + * to paint only a subset of the pixels within the clip region. The + * reason is + * that this particular paint() call may have + * resulted from multiple + * repaint() + * requests, some of which may have been generated from outside the + * application. An application that paints only what it thinks is + * necessary to + * be painted may display incorrectly if the screen contents had been + * invalidated by, for example, an incoming telephone call.

+ * + *

Operations on this graphics object after the paint() + * call returns are + * undefined. Thus, the application must not cache this + * Graphics + * object for later use or use by another thread. It must only be + * used within + * the scope of this method.

+ * + *

The implementation may postpone visible effects of + * graphics operations until the end of the paint method.

+ * + *

The contents of the Canvas are never saved if + * it is hidden and then + * is made visible again. Thus, shortly after + * showNotify() is called, + * paint() will always be called with a + * Graphics object whose clip region + * specifies the entire displayable area of the + * Canvas. Applications + * must not rely on any contents being preserved from a previous + * occasion when the Canvas was current. This call to + * paint() will not + * necessarily occur before any other key or pointer + * methods are called on the Canvas. Applications + * whose repaint + * recomputation is expensive may create an offscreen + * Image, paint into it, + * and then draw this image on the Canvas when + * paint() is called.

+ * + *

The application code must never call paint(); + * it is called only by + * the implementation.

+ * + *

The Graphics object passed to the + * paint() method has the following + * properties:

+ *
    + *
  • the destination is the actual display, or if double buffering is in + * effect, a back buffer for the display;
  • + *
  • the clip region includes at least one pixel + * within this Canvas;
  • + *
  • the current color is black;
  • + *
  • the font is the same as the font returned by + * {@link Font#getDefaultFont() Font.getDefaultFont()};
  • + *
  • the stroke style is {@link Graphics#SOLID SOLID};
  • + *
  • the origin of the coordinate system is located at the upper-left + * corner of the Canvas; and
  • + *
  • the Canvas is visible, that is, a call to + * isShown() will return + * true.
  • + *
+ * + * @param g the Graphics object to be used for + * rendering the Canvas + */ + protected abstract void paint(Graphics g); + + /** + * Called when the drawable area of the Canvas has + * been changed. This + * method has augmented semantics compared to {@link + * Displayable#sizeChanged(int, int) Displayable.sizeChanged}. + * + *

In addition to the causes listed in + * Displayable.sizeChanged, a size change can occur on a + * Canvas because of a change between normal and + * full-screen modes.

+ * + *

If the size of a Canvas changes while it is + * actually visible on the + * display, it may trigger an automatic repaint request. If this occurs, + * the call to sizeChanged will occur prior to the call to + * paint. If the Canvas has become smaller, the + * implementation may choose not to trigger a repaint request if the + * remaining contents of the Canvas have been + * preserved. Similarly, if + * the Canvas has become larger, the implementation + * may choose to trigger + * a repaint only for the new region. In both cases, the preserved + * contents must remain stationary with respect to the origin of the + * Canvas. If the size change is significant to the + * contents of the + * Canvas, the application must explicitly issue a + * repaint request for the + * changed areas. Note that the application's repaint request should not + * cause multiple repaints, since it can be coalesced with repaint + * requests that are already pending.

+ * + *

If the size of a Canvas changes while it is not + * visible, the + * implementation may choose to delay calls to sizeChanged + * until immediately prior to the call to showNotify. In + * that case, there will be only one call to sizeChanged, + * regardless of the number of size changes.

+ * + *

An application that is sensitive to size changes can update instance + * variables in its implementation of sizeChanged. These + * updated values will be available to the code in the + * showNotify, hideNotify, and + * paint methods.

+ * + * @param w the new width in pixels of the drawable area of the + * Canvas + * @param h the new height in pixels of the drawable area of + * the Canvas + */ + protected void sizeChanged(int w, int h) { + // this method is intended to be overridden by the application + } + + + /** + * Used by GameCanvas to suppress Game action key events + */ + boolean suppressKeyEvents; // = false + + /** The Canvas look&feel object associated with this Canvas */ + CanvasLF canvasLF; + + /** Get current display in which this Canvas displays. */ + public Display getCurrentDisplay() { + if (canvasLF == null) { + return null; + } + + return canvasLF.lGetCurrentDisplay(); + } +} diff --git a/java/custom/javax/microedition/lcdui/Display.java b/java/custom/javax/microedition/lcdui/Display.java index c7dc4457..6e16aebc 100644 --- a/java/custom/javax/microedition/lcdui/Display.java +++ b/java/custom/javax/microedition/lcdui/Display.java @@ -2564,6 +2564,24 @@ public class Display { return (current instanceof AlertLF) ? window.getAlertHeight() : window.getBodyHeight(); } + /** + * Get the anchor x of the current displayable layer. + * + * @return the anchor x of the current displayable layer + */ + public int getDisplayableAnchorX() { + return (current instanceof AlertLF) ? window.getAlertAnchorX() : window.getBodyAnchorX(); + } + + /** + * Get the anchor y of the current displayable layer. + * + * @return the anchor y of the current displayable layer + */ + public int getDisplayableAnchorY() { + return (current instanceof AlertLF) ? window.getAlertAnchorY() : window.getBodyAnchorY(); + } + /** Get the width of some default displayable depending on the screen mode and * the layers attached to the screen * @param isFullScn true if the full screen is set for the displayable diff --git a/midp/gfx.js b/midp/gfx.js index 1aff2bf3..e2a38e54 100644 --- a/midp/gfx.js +++ b/midp/gfx.js @@ -950,7 +950,7 @@ return parseInt(this.textEditor.style.height); }); - Native.create("com/nokia/mid/ui/CanvasItem.setPosition.(II)V", function(x, y) { + Native.create("com/nokia/mid/ui/CanvasItem.setPosition0.(II)V", function(x, y) { this.textEditor.style.left = x + "px"; this.textEditor.style.top = y + "px"; }); From 7a8c5165b7232e81400d24f59ed7295652f008ed Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Fri, 7 Nov 2014 10:39:55 +0100 Subject: [PATCH 04/12] Enable image.processing to work also if aspect is "LockToPartialView" --- midp/localmsg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/midp/localmsg.js b/midp/localmsg.js index 77749f7b..cbe20fdd 100644 --- a/midp/localmsg.js +++ b/midp/localmsg.js @@ -593,7 +593,7 @@ NokiaImageProcessingLocalMsgConnection.prototype.sendMessageToServer = function( var aspect = decoder.getValue(DataType.STRING); var quality = decoder.getValue(DataType.BYTE); - if (aspect != "FullImage") { + if (aspect != "FullImage" && aspect != "LockToPartialView") { console.error("(nokia.image-processing) event " + name + " with aspect != 'FullImage' not implemented " + util.decodeUtf8(new Uint8Array(message.data.buffer, message.offset, message.length))); return; From 24f8fb439ab76a82a476b214fd0def16e1901176 Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Fri, 7 Nov 2014 09:34:18 -0800 Subject: [PATCH 05/12] File.Connection.truncateImpl should use size param |fs.truncate| is too simple for this case, we need to use |fs.ftruncate| like it was originally using. --- midp/fs.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/midp/fs.js b/midp/fs.js index 9d9dc781..2b556ad1 100644 --- a/midp/fs.js +++ b/midp/fs.js @@ -435,12 +435,14 @@ Native.create("com/ibm/oti/connection/file/Connection.renameImpl.([B[B)V", funct Native.create("com/ibm/oti/connection/file/Connection.truncateImpl.([BJ)V", function(path, newLength, _) { return new Promise(function(resolve, reject) { - fs.truncate(util.decodeUtf8(path), function(success) { - if (!success) { + fs.open(util.decodeUtf8(path), function(fd) { + if (fd == -1) { reject(new JavaException("java/io/IOException", "truncate failed")); - } else { - resolve(); + return; } + + fs.ftruncate(fd, newLength.toNumber()); + fs.close(fd, resolve); }); }); }, true); From 26bc1f97029fef89a57e10b1967e6caa36df6beb Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Fri, 7 Nov 2014 22:27:58 +0100 Subject: [PATCH 06/12] Update nokia.image-processing Scale error message --- midp/localmsg.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/midp/localmsg.js b/midp/localmsg.js index cbe20fdd..0ff29eb4 100644 --- a/midp/localmsg.js +++ b/midp/localmsg.js @@ -594,7 +594,7 @@ NokiaImageProcessingLocalMsgConnection.prototype.sendMessageToServer = function( var quality = decoder.getValue(DataType.BYTE); if (aspect != "FullImage" && aspect != "LockToPartialView") { - console.error("(nokia.image-processing) event " + name + " with aspect != 'FullImage' not implemented " + + console.error("(nokia.image-processing) event " + name + " with aspect != 'FullImage' or 'LockToPartialView' not implemented " + util.decodeUtf8(new Uint8Array(message.data.buffer, message.offset, message.length))); return; } From f8c8564f90dffa8b7759873ed46fcb802aac057a Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Fri, 7 Nov 2014 15:24:23 -0800 Subject: [PATCH 07/12] Add packaging helpers - tools/package.sh - generates an output dir suitable for packaging as a WebApp - tools/explode.sh - converts a compressed jar to an uncompressed jar --- tools/explode.sh | 17 +++++++++++++++++ tools/package.sh | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100755 tools/explode.sh create mode 100755 tools/package.sh diff --git a/tools/explode.sh b/tools/explode.sh new file mode 100755 index 00000000..976388fa --- /dev/null +++ b/tools/explode.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +DIR=`mktemp -d -t explode` +PWD=`pwd` +IN_FILE=$PWD/$1 +OUT_FILE=$PWD/$2 + +echo "DIR: $DIR" +echo "IN_FILE: $IN_FILE" +echo "OUT_FILE: $OUT_FILE" + +pushd $DIR +jar xf $IN_FILE +jar cfM0 $OUT_FILE * +popd + +rm -rf "$DIR" diff --git a/tools/package.sh b/tools/package.sh new file mode 100755 index 00000000..b0da7ba1 --- /dev/null +++ b/tools/package.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +# This sets up a directory suitable for use as WebApp. It is expected to be +# run from your root j2me.js checkout after you've run |make test|. +# NB: I'm making no effort for this to be efficient or clever. + +PACKAGE_DIR="output" + +rm -rf $PACKAGE_DIR/ +mkdir $PACKAGE_DIR + +# setup the root +cp *.js *.html *.webapp $PACKAGE_DIR/. + +# copy over jars/jads that are used for the webapp +# NB: we could be smart about this and parse the manifest, patches welcome! +# grep 'launch_path' manifest.webapp | sed -E 's/.*jars=([^&]+)&.*$/\1/' +cp *.jar *.jad $PACKAGE_DIR/. + +# setup java dir +mkdir $PACKAGE_DIR/java +cp java/*.jar $PACKAGE_DIR/java + +# copy entire certs dir, it's possible we just need the ks files +cp -R certs $PACKAGE_DIR/. + +# copy entire classfile dir +cp -R classfile $PACKAGE_DIR/. + +# copy entire contents of libs dir +cp -R libs $PACKAGE_DIR/. + +# copy entire contents of midp dir +cp -R midp $PACKAGE_DIR/. + +# copy entire contents of style dir +cp -R style $PACKAGE_DIR/. + +# setup tests dir, for now just the jar and js files +mkdir $PACKAGE_DIR/tests +cp tests/tests.jar $PACKAGE_DIR/tests/. +cp tests/*.js $PACKAGE_DIR/tests/. + From f13b4cec45a5f3cc50bd489f760876a6ece31055 Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Thu, 6 Nov 2014 14:22:31 -0800 Subject: [PATCH 08/12] Only load tests.jar when actually testing This restrictes the loading of tests.jar to when the Java tests are actually being run. --- main.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/main.js b/main.js index f87a8085..aac174bd 100644 --- a/main.js +++ b/main.js @@ -41,7 +41,12 @@ if ("gamepad" in urlParams && !/no|0/.test(urlParams.gamepad)) { document.documentElement.classList.add('gamepad'); } -var jars = ["java/classes.jar", "tests/tests.jar"]; +var jars = ["java/classes.jar"]; + +if (MIDP.midletClassName == "RunTests") { + jars.push("tests/tests.jar"); +} + if (urlParams.jars) { jars = jars.concat(urlParams.jars.split(":")); } From d625a2cedc8ec9d0498cfe2d7bd5013bb3c4238d Mon Sep 17 00:00:00 2001 From: Eric Rahm Date: Fri, 7 Nov 2014 15:34:22 -0800 Subject: [PATCH 09/12] Specify jars=tets/test.jar for tests that need it --- tests/automation.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/automation.js b/tests/automation.js index 2e13983d..22e0b3ca 100644 --- a/tests/automation.js +++ b/tests/automation.js @@ -69,7 +69,7 @@ casper.test.begin("unit tests", 7 + gfxTests.length, function(test) { }); casper - .thenOpen("http://localhost:8000/index.html?main=com/sun/midp/main/MIDletSuiteLoader&midletClassName=tests/alarm/MIDlet1&jad=tests/midlets/alarm/alarm.jad") + .thenOpen("http://localhost:8000/index.html?main=com/sun/midp/main/MIDletSuiteLoader&midletClassName=tests/alarm/MIDlet1&jad=tests/midlets/alarm/alarm.jad&jars=tests/tests.jar") .withFrame(0, function() { casper.waitForText("Hello World from MIDlet2", function() { test.pass(); @@ -83,7 +83,7 @@ casper.test.begin("unit tests", 7 + gfxTests.length, function(test) { }); casper - .thenOpen("http://localhost:8000/index.html?midletClassName=tests.sms.SMSMIDlet&main=com/sun/midp/main/MIDletSuiteLoader") + .thenOpen("http://localhost:8000/index.html?midletClassName=tests.sms.SMSMIDlet&main=com/sun/midp/main/MIDletSuiteLoader&jars=tests/tests.jar") .withFrame(0, function() { this.waitForText("START", function() { this.evaluate(function() { @@ -100,7 +100,7 @@ casper.test.begin("unit tests", 7 + gfxTests.length, function(test) { }); casper - .thenOpen("http://localhost:8000/index.html?midletClassName=tests.fileui.FileUIMIDlet") + .thenOpen("http://localhost:8000/index.html?midletClassName=tests.fileui.FileUIMIDlet&jars=tests/tests.jar") .withFrame(0, function() { this.waitForText("START", function() { this.waitUntilVisible(".nokia-fileui-prompt", function() { @@ -127,7 +127,7 @@ casper.test.begin("unit tests", 7 + gfxTests.length, function(test) { gfxTests.forEach(function(testCase) { casper - .thenOpen("http://localhost:8000/index.html?main=com/sun/midp/main/MIDletSuiteLoader&midletClassName=" + testCase.name) + .thenOpen("http://localhost:8000/index.html?main=com/sun/midp/main/MIDletSuiteLoader&midletClassName=" + testCase.name + "&jars=tests/tests.jar") .withFrame(0, function() { casper.waitForText("PAINTED", function() { this.waitForSelector("#canvas", function() { From c066886cd1ecbd5721d0ae7cf854c390614d577d Mon Sep 17 00:00:00 2001 From: Marcus Cavanaugh Date: Fri, 7 Nov 2014 16:03:23 -0800 Subject: [PATCH 10/12] Listen on all interfaces for the test server, for ease of development. --- tests/httpServer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/httpServer.py b/tests/httpServer.py index 5eef8b34..e59bf318 100755 --- a/tests/httpServer.py +++ b/tests/httpServer.py @@ -14,5 +14,5 @@ port = 8000 if len(sys.argv) > 1: port = int(sys.argv[1]) -httpd = BaseHTTPServer.HTTPServer(('localhost', port), CustomRequestHandler) +httpd = BaseHTTPServer.HTTPServer(('', port), CustomRequestHandler) httpd.serve_forever() From 313fbb3a8b32982d53c1fbc5c6746c07bc394570 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Mon, 10 Nov 2014 08:30:57 -0800 Subject: [PATCH 11/12] ignore output/ directory generated by tools/package.sh script --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ec309bc8..58f3f54f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ certs/j2se_main.ks certs/j2se_test.ks tests/Testlets.java test.log +output/ # These are generated by the Java pre-processor. They're generated from # their *.jpp equivalents. Keep them up-to-date when you add *.jpp files! From 9e258a25080a1afb098d526a085509b6c3122f91 Mon Sep 17 00:00:00 2001 From: Myk Melez Date: Mon, 10 Nov 2014 14:30:32 -0800 Subject: [PATCH 12/12] specify origin for packaged app in manifest --- manifest.webapp | 1 + 1 file changed, 1 insertion(+) diff --git a/manifest.webapp b/manifest.webapp index 560e110b..49b2a420 100644 --- a/manifest.webapp +++ b/manifest.webapp @@ -2,6 +2,7 @@ "name": "j2me.js", "description": "j2me interpreter for firefox os", "launch_path": "/index.html?autosize=1&logConsole=web", + "origin": "app://j2mejs.mozilla.org", "icons": { "128": "/img/icon-128.png" },