/* * 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.sun.lwuit; import com.sun.lwuit.animations.Animation; import com.sun.lwuit.animations.CommonTransitions; import com.sun.lwuit.animations.Transition; import com.sun.lwuit.events.ActionEvent; import com.sun.lwuit.events.ActionListener; import com.sun.lwuit.geom.Dimension; import com.sun.lwuit.impl.ImplementationFactory; import com.sun.lwuit.impl.LWUITImplementation; import com.sun.lwuit.impl.VirtualKeyboardInterface; import com.sun.lwuit.plaf.UIManager; import com.sun.lwuit.util.EventDispatcher; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; /** * Central class for the API that manages rendering/events and is used to place top * level components ({@link Form}) on the "display". Before any Form is shown the Developer must * invoke Display.init(Object m) in order to register the current MIDlet. * <p>This class handles the main thread for the toolkit referenced here on as the EDT * (Event Dispatch Thread) similar to the Swing EDT. This thread encapsulates the platform * specific event delivery and painting semantics and enables threading features such as * animations etc... * <p>The EDT should not be blocked since paint operations and events would also be blocked * in much the same way as they would be in other platforms. In order to serialize calls back * into the EDT use the methods {@link Display#callSerially} & {@link Display#callSeriallyAndWait}. * <p>Notice that all LWUIT calls occur on the EDT (events, painting, animations etc...), LWUIT * should normally be manipulated on the EDT as well (hence the {@link Display#callSerially} & * {@link Display#callSeriallyAndWait} methods). Theoretically it should be possible to manipulate * some LWUIT features from other threads but this can't be guaranteed to work for all use cases. * * @author Chen Fishbein, Shai Almog */ public final class Display { private EventDispatcher errorHandler; boolean lwuitExited; /** * A common sound type that can be used with playBuiltinSound */ public static final String SOUND_TYPE_ALARM = "alarm"; /** * A common sound type that can be used with playBuiltinSound */ public static final String SOUND_TYPE_CONFIRMATION = "confirmation"; /** * A common sound type that can be used with playBuiltinSound */ public static final String SOUND_TYPE_ERROR = "error"; /** * A common sound type that can be used with playBuiltinSound */ public static final String SOUND_TYPE_INFO = "info"; /** * A common sound type that can be used with playBuiltinSound */ public static final String SOUND_TYPE_WARNING = "warning"; /** * A common sound type that can be used with playBuiltinSound */ public static final String SOUND_TYPE_BUTTON_PRESS = "press"; /** * Unknown keyboard type is the default indicating the software should try * to detect the keyboard type if necessary */ public static final int KEYBOARD_TYPE_UNKNOWN = 0; /** * Numeric keypad keyboard type */ public static final int KEYBOARD_TYPE_NUMERIC = 1; /** * Full QWERTY keypad keyboard type, even if a numeric keyboard also exists */ public static final int KEYBOARD_TYPE_QWERTY = 2; /** * Touch device without a physical keyboard that should popup a keyboad */ public static final int KEYBOARD_TYPE_VIRTUAL = 3; /** * Half QWERTY which needs software assistance for completion */ public static final int KEYBOARD_TYPE_HALF_QWERTY = 4; private static final int POINTER_PRESSED = 1; private static final int POINTER_RELEASED = 2; private static final int POINTER_DRAGGED = 3; private static final int POINTER_HOVER = 8; private static final int POINTER_HOVER_RELEASED = 11; private static final int POINTER_HOVER_PRESSED = 12; private static final int KEY_PRESSED = 4; private static final int KEY_RELEASED = 5; private static final int KEY_LONG_PRESSED = 6; private static final int SIZE_CHANGED = 7; private static final int HIDE_NOTIFY = 9; private static final int SHOW_NOTIFY = 10; /** * Very Low Density 176x220 And Smaller */ public static final int DENSITY_VERY_LOW = 10; /** * Low Density Up To 240x320 */ public static final int DENSITY_LOW = 20; /** * Medium Density Up To 360x480 */ public static final int DENSITY_MEDIUM = 30; /** * Hi Density Up To 480x854 */ public static final int DENSITY_HIGH = 40; /** * Very Hi Density Up To 1440x720 */ public static final int DENSITY_VERY_HIGH = 50; /** * HD Up To 1920x1080 */ public static final int DENSITY_HD = 60; /** * A pure touch device has no focus showing when the user is using the touch * interface. Selection only shows when the user actually touches the screen * or suddenly switches to using a keypad/trackball. This sort of interface * is common in Android devices */ private boolean pureTouch; private Graphics lwuitGraphics; /** * Indicates whether this is a touch device */ private boolean touchScreen; private Hashtable localProperties; /** * Indicates whether the edt should sleep between each loop */ private boolean noSleep = false; /** * Normally LWUIT folds the VKB when switching forms this field allows us * to block that behavior. */ private boolean autoFoldVKBOnFormSwitch = true; /** * Indicates the maximum drawing speed of no more than 10 frames per second * by default (this can be increased or decreased) the advantage of limiting * framerate is to allow the CPU to perform other tasks besides drawing. * Notice that when no change is occurring on the screen no frame is drawn and * so a high/low FPS will have no effect then. */ private int framerateLock = 30; /** * Game action for fire */ public static final int GAME_FIRE = 8; /** * Game action for left key */ public static final int GAME_LEFT = 2; /** * Game action for right key */ public static final int GAME_RIGHT = 5; /** * Game action for UP key */ public static final int GAME_UP = 1; /** * Game action for down key */ public static final int GAME_DOWN = 6; /** * An attribute that encapsulates '#' int value. */ public static final int KEY_POUND = '#'; private static final Display INSTANCE = new Display(); static int transitionDelay = -1; private LWUITImplementation impl; private boolean lwuitRunning = false; /** * Contains the call serially pending elements */ private Vector pendingSerialCalls = new Vector(); /** * This is the instance of the EDT used internally to indicate whether * we are executing on the EDT or some arbitrary thread */ private Thread edt; /** * Contains animations that must be played in full by the EDT before anything further * may be processed. This is useful for transitions/intro's etc... that animate without * user interaction. */ private Vector animationQueue; /** * Indicates whether the 3rd softbutton should be supported on this device */ private boolean thirdSoftButton = false; private boolean editingText; /** * Ignore all calls to show occurring during edit, they are discarded immediately */ public static final int SHOW_DURING_EDIT_IGNORE = 1; /** * If show is called while editing text in the native text box an exception is thrown */ public static final int SHOW_DURING_EDIT_EXCEPTION = 2; /** * Allow show to occur during edit and discard all user input at this moment */ public static final int SHOW_DURING_EDIT_ALLOW_DISCARD = 3; /** * Allow show to occur during edit and save all user input at this moment */ public static final int SHOW_DURING_EDIT_ALLOW_SAVE = 4; /** * Show will update the current form to which the OK button of the text box * will return */ public static final int SHOW_DURING_EDIT_SET_AS_NEXT = 5; private int showDuringEdit; static final Object lock = new Object(); /** * Events to broadcast on the EDT */ private Vector inputEvents = new Vector(); private boolean longPointerCharged; private boolean pointerPressedAndNotReleasedOrDragged; private int pointerX, pointerY; private boolean keyRepeatCharged; private boolean longPressCharged; private long longKeyPressTime; private int longPressInterval = 800; private long nextKeyRepeatEvent; private int keyRepeatValue; private int keyRepeatInitialIntervalTime = 800; private int keyRepeatNextIntervalTime = 10; private boolean lastInteractionWasKeypad; private boolean dragOccured; private boolean processingSerialCalls; private int PATHLENGTH; private float[] dragPathX; private float[] dragPathY; private long[] dragPathTime; private int dragPathOffset = 0; private int dragPathLength = 0; /** * Internally track display initialization time as a fixed point to allow tagging of pointer * events with an integer timestamp (System.currentTimeMillis() - displayInitTime) * and not a long value. */ private long displayInitTime = 0; /** * Allows a LWUIT application to minimize without forcing it to the front whenever * a new dialog is poped up */ private boolean allowMinimizing; /** * Indicates that the LWUIT implementation should decide internally the command * behavior most appropriate for this platform. */ public static final int COMMAND_BEHAVIOR_DEFAULT = 1; /** * Indicates the classic LWUIT command behavior where the commands are placed in * a list within a dialog. This is the most customizable approach for none touch devices. */ public static final int COMMAND_BEHAVIOR_SOFTKEY = 2; /** * Indicates the touch menu dialog rendered by LWUIT where commands are placed * into a scrollable dialog */ public static final int COMMAND_BEHAVIOR_TOUCH_MENU = 3; /** * Indicates that commands should be added to an always visible bar at the * bottom of the form. */ public static final int COMMAND_BEHAVIOR_BUTTON_BAR = 4; /** * Identical to the bar behavior, places the back command within the title bar * of the form/dialg */ public static final int COMMAND_BEHAVIOR_BUTTON_BAR_TITLE_BACK = 5; /** * Places all commands on the right side of the title bar with a uniform size * grid layout */ public static final int COMMAND_BEHAVIOR_BUTTON_BAR_TITLE_RIGHT = 6; /** * Indicates that commands should try to add themselves to the native menus */ public static final int COMMAND_BEHAVIOR_NATIVE = 10; private int commandBehavior = COMMAND_BEHAVIOR_DEFAULT; private static String selectedVirtualKeyboard = VirtualKeyboard.NAME; private static Hashtable virtualKeyboards = new Hashtable(); private boolean dropEvents; /** * Private constructor to prevent instanciation */ private Display() { } Vector getAnimationQueue() { return animationQueue; } /** * This is the Display initialization method. * This method must be called before any Form is shown * * @param m the main running MIDlet */ public static void init(Object m) { if(!INSTANCE.lwuitRunning) { INSTANCE.lwuitRunning = true; INSTANCE.displayInitTime = System.currentTimeMillis(); INSTANCE.impl = ImplementationFactory.getInstance().createImplementation(); INSTANCE.impl.setDisplayLock(lock); INSTANCE.impl.init(m); INSTANCE.lwuitGraphics = new Graphics(INSTANCE.impl.getNativeGraphics()); INSTANCE.impl.setLWUITGraphics(INSTANCE.lwuitGraphics); // only enable but never disable the third softbutton if(INSTANCE.impl.isThirdSoftButton()) { INSTANCE.thirdSoftButton = true; } if(INSTANCE.impl.getSoftkeyCount() > 0) { MenuBar.leftSK = INSTANCE.impl.getSoftkeyCode(0)[0]; if(INSTANCE.impl.getSoftkeyCount() > 1) { MenuBar.rightSK = INSTANCE.impl.getSoftkeyCode(1)[0]; if(INSTANCE.impl.getSoftkeyCode(1).length > 1){ MenuBar.rightSK2 = INSTANCE.impl.getSoftkeyCode(1)[1]; } } MenuBar.backSK = INSTANCE.impl.getBackKeyCode(); MenuBar.backspaceSK = INSTANCE.impl.getBackspaceKeyCode(); MenuBar.clearSK = INSTANCE.impl.getClearKeyCode(); } INSTANCE.PATHLENGTH = INSTANCE.impl.getDragPathLength(); INSTANCE.dragPathX = new float[INSTANCE.PATHLENGTH]; INSTANCE.dragPathY = new float[INSTANCE.PATHLENGTH]; INSTANCE.dragPathTime = new long[INSTANCE.PATHLENGTH]; // this can happen on some cases where an application was restarted etc... // generally its probably a bug but we can let it slide... if(INSTANCE.edt == null) { INSTANCE.touchScreen = INSTANCE.impl.isTouchDevice(); // initialize the LWUIT EDT which from now on will take all responsibility // for the event delivery. INSTANCE.edt = new Thread(new RunnableWrapper(null, 3), "EDT"); INSTANCE.edt.setPriority(Thread.NORM_PRIORITY + 1); INSTANCE.edt.start(); } UIManager.getInstance(); com.sun.lwuit.VirtualKeyboard vkb = new com.sun.lwuit.VirtualKeyboard(); INSTANCE.registerVirtualKeyboard(vkb); }else{ INSTANCE.impl.confirmControlView(); } } /** * Closes down the EDT and LWUIT, under normal conditions this method is completely unnecessary * since exiting the application will shut down LWUIT. However, if the application is minimized * and the user wishes to free all resources without exiting the application then this method can be used. * Once this method is used LWUIT will no longer work and Display.init(Object) should be invoked * again for any further LWUIT call! * Notice that minimize (being a LWUIT method) MUST be invoked before invoking this method! */ public static void deinitialize() { INSTANCE.lwuitRunning = false; synchronized(lock) { lock.notifyAll(); } } /** * This method returns true if the Display is initialized. * * @return true if the EDT is running */ public static boolean isInitialized(){ return INSTANCE.lwuitRunning; } /** * Return the Display instance * * @return the Display instance */ public static Display getInstance(){ return INSTANCE; } /** * This method allows us to manipulate the drag started detection logic. * If the pointer was dragged for more than this percentage of the display size it * is safe to assume that a drag is in progress. * * @return motion percentage */ public int getDragStartPercentage() { return getImplementation().getDragStartPercentage(); } /** * This method allows us to manipulate the drag started detection logic. * If the pointer was dragged for more than this percentage of the display size it * is safe to assume that a drag is in progress. * * @param dragStartPercentage percentage of the screen required to initiate drag */ public void setDragStartPercentage(int dragStartPercentage) { getImplementation().setDragStartPercentage(dragStartPercentage); } LWUITImplementation getImplementation() { return impl; } /** * Indicates the maximum frames the API will try to draw every second * by default this is set to 10. The advantage of limiting * framerate is to allow the CPU to perform other tasks besides drawing. * Notice that when no change is occurring on the screen no frame is drawn and * so a high/low FPS will have no effect then. * 10FPS would be very reasonable for a business application. * * @param rate the frame rate */ public void setFramerate(int rate) { framerateLock = 1000 / rate; } /** * Vibrates the device for the given length of time * * @param duration length of time to vibrate */ public void vibrate(int duration) { impl.vibrate(duration); } /** * Flash the backlight of the device for the given length of time * * @param duration length of time to flash the backlight */ public void flashBacklight(int duration) { impl.flashBacklight(duration); } /** * Invoking the show() method of a form/dialog while the user is editing * text in the native text box can have several behaviors: SHOW_DURING_EDIT_IGNORE, * SHOW_DURING_EDIT_EXCEPTION, SHOW_DURING_EDIT_ALLOW_DISCARD, * SHOW_DURING_EDIT_ALLOW_SAVE, SHOW_DURING_EDIT_SET_AS_NEXT * * @param showDuringEdit one of the following: SHOW_DURING_EDIT_IGNORE, * SHOW_DURING_EDIT_EXCEPTION, SHOW_DURING_EDIT_ALLOW_DISCARD, * SHOW_DURING_EDIT_ALLOW_SAVE, SHOW_DURING_EDIT_SET_AS_NEXT */ public void setShowDuringEditBehavior(int showDuringEdit) { this.showDuringEdit = showDuringEdit; } /** * Returns the status of the show during edit flag * * @return one of the following: SHOW_DURING_EDIT_IGNORE, * SHOW_DURING_EDIT_EXCEPTION, SHOW_DURING_EDIT_ALLOW_DISCARD, * SHOW_DURING_EDIT_ALLOW_SAVE, SHOW_DURING_EDIT_SET_AS_NEXT */ public int getShowDuringEditBehavior() { return showDuringEdit; } /** * Indicates the maximum frames the API will try to draw every second * * @return the frame rate */ public int getFrameRate() { return 1000 / framerateLock; } /** * Returns true if we are currently in the event dispatch thread. * This is useful for generic code that can be used both with the * EDT and outside of it. * * @return true if we are currently in the event dispatch thread; * otherwise false */ public boolean isEdt() { return edt == Thread.currentThread(); } /** * Plays sound for the dialog */ void playDialogSound(final int type) { impl.playDialogSound(type); } /** * Causes the runnable to be invoked on the event dispatch thread. This method * returns immediately and will not wait for the serial call to occur * * @param r runnable (NOT A THREAD!) that will be invoked on the EDT serial to * the paint and key handling events */ public void callSerially(Runnable r){ synchronized(lock) { pendingSerialCalls.addElement(r); lock.notify(); } } /** * Identical to callSerially with the added benefit of waiting for the Runnable method to complete. * * @param r runnable (NOT A THREAD!) that will be invoked on the EDT serial to * the paint and key handling events * @throws IllegalStateException if this method is invoked on the event dispatch thread (e.g. during * paint or event handling). */ public void callSeriallyAndWait(Runnable r){ RunnableWrapper c = new RunnableWrapper(r, 0); callSerially(c); flushEdt(); synchronized(lock) { while(!c.isDone()) { try { // poll doneness to prevent potential race conditions lock.wait(50); } catch(InterruptedException err) {} } } } /** * Identical to callSerially with the added benefit of waiting for the Runnable method to complete. * * @param r runnable (NOT A THREAD!) that will be invoked on the EDT serial to * the paint and key handling events * @param timeout timeout duration, on timeout the method just returns * @throws IllegalStateException if this method is invoked on the event dispatch thread (e.g. during * paint or event handling). */ public void callSeriallyAndWait(Runnable r, int timeout){ RunnableWrapper c = new RunnableWrapper(r, 0); callSerially(c); synchronized(lock) { long t = System.currentTimeMillis(); while(!c.isDone()) { try { // poll doneness to prevent potential race conditions lock.wait(20); } catch(InterruptedException err) {} if(System.currentTimeMillis() - t >= timeout) { return; } } } } /** * Allows us to "flush" the edt to allow any pending transitions and input to go * by before continuing with our other tasks. */ void flushEdt() { if(!isEdt()){ return; } while(!shouldEDTSleepNoFormAnimation()) { edtLoopImpl(); } while(animationQueue != null && animationQueue.size() > 0){ edtLoopImpl(); } } /** * Restores the menu in the given form */ private void restoreMenu(Form f) { if(f != null) { f.restoreMenu(); } } private void paintTransitionAnimation() { Animation ani = (Animation) animationQueue.elementAt(0); if (!ani.animate()) { animationQueue.removeElementAt(0); if (ani instanceof Transition) { Form source = (Form) ((Transition)ani).getSource(); restoreMenu(source); if (animationQueue.size() > 0) { ani = (Animation) animationQueue.elementAt(0); if (ani instanceof Transition) { ((Transition) ani).initTransition(); } }else{ Form f = (Form) ((Transition)ani).getDestination(); restoreMenu(f); if (source == null || source == impl.getCurrentForm() || source == getCurrent()) { setCurrentForm(f); } ((Transition) ani).cleanup(); } return; } } ani.paint(lwuitGraphics); impl.flushGraphics(); if(transitionDelay > 0) { // yield for a fraction, some devices don't "properly" implement // flush and so require the painting thread to get CPU too. try { synchronized(lock){ lock.wait(transitionDelay); } } catch (InterruptedException ex) { ex.printStackTrace(); } } } /** * This method represents the event thread for the UI library on which * all events are carried out. It differs from the MIDP event thread to * prevent blocking of actual input and drawing operations. This also * enables functionality such as "true" modal dialogs etc... */ void mainEDTLoop() { impl.initEDT(); try { synchronized(lock){ // when there is no current form the EDT is useful only // for features such as call serially while(impl.getCurrentForm() == null) { if(shouldEDTSleep()) { lock.wait(); } // paint transition or intro animations and don't do anything else if such // animations are in progress... if(animationQueue != null && animationQueue.size() > 0) { paintTransitionAnimation(); continue; } processSerialCalls(); } } } catch(Throwable err) { err.printStackTrace(); if(!impl.handleEDTException(err)) { if(errorHandler != null) { errorHandler.fireActionEvent(new ActionEvent(err)); } else { Dialog.show("Error", "An internal application error occurred: " + err.toString(), "OK", null); } } } while(lwuitRunning) { try { // wait indefinetly but no more than the framerate if // there are no animations... If animations exist then // only wait for the framerate // Lock surrounds the should method to prevent serial calls from // getting "lost" synchronized(lock){ if(shouldEDTSleep()) { impl.edtIdle(true); lock.wait(); impl.edtIdle(false); } } edtLoopImpl(); } catch(Throwable err) { err.printStackTrace(); if(!impl.handleEDTException(err)) { if(errorHandler != null) { errorHandler.fireActionEvent(new ActionEvent(err)); } else { Dialog.show("Error", "An internal application error occurred: " + err.toString(), "OK", null); } } } } INSTANCE.impl.deinitialize(); //INSTANCE.impl = null; //INSTANCE.lwuitGraphics = null; INSTANCE.edt = null; } long time; /** * Implementation of the event dispatch loop content */ void edtLoopImpl() { try { // transitions shouldn't be bound by framerate if(animationQueue == null || animationQueue.size() == 0) { // prevents us from waking up the EDT too much and // thus exhausting the systems resources. The + 1 // prevents us from ever waiting 0 milliseconds which // is the same as waiting with no time limit if(!noSleep){ synchronized(lock){ lock.wait(Math.max(1, framerateLock - (time))); } } } else { // paint transition or intro animations and don't do anything else if such // animations are in progress... paintTransitionAnimation(); return; } } catch(Exception ignor) { ignor.printStackTrace(); } long currentTime = System.currentTimeMillis(); while(inputEvents.size() > 0) { int[] i = (int[])inputEvents.elementAt(0); inputEvents.removeElementAt(0); handleEvent(i); } lwuitGraphics.setGraphics(impl.getNativeGraphics()); impl.paintDirty(); // draw the animations Form current = impl.getCurrentForm(); if(current != null){ current.repaintAnimations(); // check key repeat events long t = System.currentTimeMillis(); if(keyRepeatCharged && nextKeyRepeatEvent <= t) { current.keyRepeated(keyRepeatValue); nextKeyRepeatEvent = t + keyRepeatNextIntervalTime; } if(longPressCharged && longPressInterval <= t - longKeyPressTime) { longPressCharged = false; current.longKeyPress(keyRepeatValue); } if(longPointerCharged && longPressInterval <= t - longKeyPressTime) { longPointerCharged = false; current.longPointerPress(pointerX, pointerY); } processSerialCalls(); } time = System.currentTimeMillis() - currentTime; } boolean hasNoSerialCallsPending() { return pendingSerialCalls.size() == 0; } /** * Called by the underlying implementation to indicate that editing in the native * system has completed and changes should propogate into LWUIT * * @param c edited component * @param text new text for the component */ public void onEditingComplete(Component c, String text) { c.onEditComplete(text); c.fireActionEvent(); } /** * Used by the EDT to process all the calls submitted via call serially */ void processSerialCalls() { processingSerialCalls = true; int size = pendingSerialCalls.size(); if(size > 0) { Runnable[] array = null; synchronized(lock) { size = pendingSerialCalls.size(); array = new Runnable[size]; // copy all elements to an array and remove them otherwise invokeAndBlock from // within a callSerially() can cause an infinite loop... pendingSerialCalls.copyInto(array); if(size == pendingSerialCalls.size()) { // this is faster pendingSerialCalls.removeAllElements(); } else { // this can occur if an element was added during the loop for(int iter = 0 ; iter < size ; iter++) { pendingSerialCalls.removeElementAt(0); } } } for(int iter = 0 ; iter < size ; iter++) { array[iter].run(); } // after finishing an event cycle there might be serial calls waiting // to return. synchronized(lock){ lock.notify(); } } processingSerialCalls = false; } boolean isProcessingSerialCalls() { return processingSerialCalls; } void notifyDisplay(){ synchronized (lock) { lock.notify(); } } /** * Invokes runnable and blocks the current thread, if the current thread is the * edt it will still be blocked however a separate thread would be launched * to perform the duties of the EDT while it is blocked. Once blocking is finished * the EDT would be restored to its original position. This is very similar to the * "foxtrot" Swing toolkit and allows coding "simpler" logic that requires blocking * code in the middle of event sensitive areas. * * @param r runnable (NOT A THREAD!) that will be invoked synchroniously by this method * @param dropEvents indicates if the display should drop all events * while this runnable is running */ public void invokeAndBlock(Runnable r, boolean dropEvents){ this.dropEvents = dropEvents; if(isEdt()) { // this class allows a runtime exception to propogate correctly out of the // internal thread RunnableWrapper w = new RunnableWrapper(r, 1); RunnableWrapper.pushToThreadPool(w); synchronized(lock) { try { // yeald the CPU for a very short time to let the invoke thread // get started lock.wait(2); } catch (InterruptedException ex) { ex.printStackTrace(); } } // loop over the EDT until the thread completes then return while(!w.isDone() && lwuitRunning) { edtLoopImpl(); } // if the thread thew an exception we need to throw it onwards if(w.getErr() != null) { throw w.getErr(); } } else { r.run(); } this.dropEvents = false; } /** * Invokes runnable and blocks the current thread, if the current thread is the * edt it will still be blocked however a separate thread would be launched * to perform the duties of the EDT while it is blocked. Once blocking is finished * the EDT would be restored to its original position. This is very similar to the * "foxtrot" Swing toolkit and allows coding "simpler" logic that requires blocking * code in the middle of event sensitive areas. * * @param r runnable (NOT A THREAD!) that will be invoked synchroniously by this method */ public void invokeAndBlock(Runnable r){ invokeAndBlock(r, false); } /** * Indicates if this is a touch screen device that will return pen events, * defaults to true if the device has pen events but can be overriden by * the developer. * * @return true if this device supports touch events */ public boolean isTouchScreenDevice() { return touchScreen; } /** * Indicates if this is a touch screen device that will return pen events, * defaults to true if the device has pen events but can be overriden by * the developer. * * @param touchScreen false if this is not a touch screen device */ public void setTouchScreenDevice(boolean touchScreen) { this.touchScreen = touchScreen; } /** * Calling this method with noSleep=true will cause the edt to run without sleeping. * * @param noSleep causes the edt to stop the sleeping periods between 2 cycles */ public void setNoSleep(boolean noSleep){ this.noSleep = noSleep; } /** * Displays the given Form on the screen. * * @param newForm the Form to Display */ void setCurrent(final Form newForm, boolean reverse){ if(edt == null) { throw new IllegalStateException("Initialize must be invoked before setCurrent!"); } if(autoFoldVKBOnFormSwitch && !(newForm instanceof Dialog)) { setShowVirtualKeyboard(false); } if(editingText) { switch(showDuringEdit) { case SHOW_DURING_EDIT_ALLOW_DISCARD: break; case SHOW_DURING_EDIT_ALLOW_SAVE: impl.saveTextEditingState(); break; case SHOW_DURING_EDIT_EXCEPTION: throw new IllegalStateException("Show during edit"); case SHOW_DURING_EDIT_IGNORE: return; case SHOW_DURING_EDIT_SET_AS_NEXT: impl.setCurrentForm(newForm); return; } } if(!isEdt()) { callSerially(new RunnableWrapper(newForm, null, reverse)); return; } Form current = impl.getCurrentForm(); if(current != null){ if(current.isInitialized()) { current.deinitializeImpl(); } } if(!newForm.isInitialized()) { newForm.initComponentImpl(); } if(newForm.getWidth() != getDisplayWidth() || newForm.getHeight() != getDisplayHeight()) { newForm.setSize(new Dimension(getDisplayWidth(), getDisplayHeight())); newForm.setShouldCalcPreferredSize(true); newForm.layoutContainer(); } synchronized(lock) { boolean transitionExists = false; if(animationQueue != null && animationQueue.size() > 0) { Object o = animationQueue.lastElement(); if(o instanceof Transition) { current = (Form)((Transition)o).getDestination(); impl.setCurrentForm(current); } } if(current != null) { // make sure the fold menu occurs as expected then set the current // to the correct parent! if(current instanceof Dialog && ((Dialog)current).isMenu()) { Transition t = current.getTransitionOutAnimator(); if(t != null) { // go back to the parent form first if(((Dialog)current).getPreviousForm() != null) { initTransition(t.copy(false), current, ((Dialog)current).getPreviousForm()); } } current = ((Dialog)current).getPreviousForm(); impl.setCurrentForm(current); } // prevent the transition from occurring from a form into itself if(newForm != current) { if((current != null && current.getTransitionOutAnimator() != null) || newForm.getTransitionInAnimator() != null) { if(animationQueue == null) { animationQueue = new Vector(); } // prevent form transitions from breaking our dialog based // transitions which are a bit sensitive if(current != null && (!(newForm instanceof Dialog))) { Transition t = current.getTransitionOutAnimator(); if(current != null && t != null) { transitionExists = initTransition(t.copy(reverse), current, newForm); } } if(current != null && !(current instanceof Dialog)) { Transition t = newForm.getTransitionInAnimator(); if(t != null) { transitionExists = initTransition(t.copy(reverse), current, newForm); } } } } } lock.notify(); if(!transitionExists) { if(animationQueue == null || animationQueue.size() == 0) { setCurrentForm(newForm); } else { // we need to add an empty transition to "serialize" this // screen change... Transition t = CommonTransitions.createEmpty(); initTransition(t, current, newForm); } } } } /** * Initialize the transition and add it to the queue */ private boolean initTransition(Transition transition, Form source, Form dest) { try { dest.setVisible(true); transition.init(source, dest); animationQueue.addElement(transition); if (animationQueue.size() == 1) { transition.initTransition(); } } catch (Throwable e) { e.printStackTrace(); transition.cleanup(); animationQueue.removeElement(transition); return false; } return true; } void setCurrentForm(Form newForm){ boolean forceShow = false; Form current = impl.getCurrentForm(); if(current != null){ current.setVisible(false); } else { forceShow = true; } keyRepeatCharged = false; longPressCharged = false; longPointerCharged = false; current = newForm; impl.setCurrentForm(current); current.setVisible(true); if(forceShow || !allowMinimizing) { impl.confirmControlView(); } int w = current.getWidth(); int h = current.getHeight(); if(isEdt() && ( w != impl.getDisplayWidth() || h != impl.getDisplayHeight())){ current.sizeChangedInternal(impl.getDisplayWidth(), impl.getDisplayHeight()); }else{ repaint(current); } lastKeyPressed = 0; previousKeyPressed = 0; newForm.onShowCompletedImpl(); } /** * Indicates whether a delay should exist between calls to flush graphics during * transition. In some devices flushGraphics is asynchronious causing it to be * very slow with our background thread. The solution is to add a short wait allowing * the implementation time to paint the screen. This value is set automatically by default * but can be overriden for some devices. * * @param transitionD -1 for no delay otherwise delay in milliseconds */ public void setTransitionYield(int transitionD) { transitionDelay = transitionD; } /** * Encapsulates the editing code which is specific to the platform, some platforms * would allow "in place editing" MIDP does not. * * @param cmp the {@link TextArea} component * @param maxSize the maximum size from the text area * @param constraint the constraints of the text area * @param text the string to edit */ public void editString(Component cmp, int maxSize, int constraint, String text) { editString(cmp, maxSize, constraint, text, 0); } /** * Encapsulates the editing code which is specific to the platform, some platforms * would allow "in place editing" MIDP does not. * * @param cmp the {@link TextArea} component * @param maxSize the maximum size from the text area * @param constraint the constraints of the text area * @param text the string to edit * @param initiatingKeycode the keycode used to initiate the edit. */ public void editString(Component cmp, int maxSize, int constraint, String text, int initiatingKeycode) { editingText = true; keyRepeatCharged = false; longPressCharged = false; lastKeyPressed = 0; previousKeyPressed = 0; impl.editString(cmp, maxSize, constraint, text, initiatingKeycode); editingText = false; } /** * Minimizes the current application if minimization is supported by the platform (may fail). * Returns false if minimization failed. * * @return false if minimization failed true if it succeeded or seems to be successful */ public boolean minimizeApplication() { return getImplementation().minimizeApplication(); } /** * Indicates whether an application is minimized * * @return true if the application is minimized */ public boolean isMinimized() { return getImplementation().isMinimized(); } /** * Restore the minimized application if minimization is supported by the platform */ public void restoreMinimizedApplication() { getImplementation().restoreMinimizedApplication(); } private void addInputEvent(int[] ev) { synchronized(lock) { if (this.dropEvents && (ev[0] == KEY_PRESSED || ev[0] == KEY_RELEASED || ev[0] == POINTER_PRESSED || ev[0] == POINTER_RELEASED || ev[0] == POINTER_DRAGGED || ev[0] == POINTER_HOVER)) { return; } inputEvents.addElement(ev); lock.notify(); } } /** * Creates a pointer event with the following properties */ private int[] createPointerEvent(int[] x, int[] y, int eventType) { // apply timestamp early to ensure the timing happens on the native UI // thread and not later on the EDT. final int stamp = (int) (System.currentTimeMillis() - displayInitTime); if (x.length == 1) { return new int[]{eventType, x[0], y[0], stamp}; } int[] arr = new int[2 + x.length * 2]; arr[0] = eventType; int arrayOffset = 1; for(int iter = 0 ; iter < x.length ; iter++) { arr[arrayOffset] = x[iter]; arrayOffset++; arr[arrayOffset] = y[iter]; arrayOffset++; } arr[arrayOffset] = stamp; return arr; } private int[] createKeyEvent(int keyCode, boolean pressed) { if(pressed) { return new int[] {KEY_PRESSED, keyCode}; } else { return new int[] {KEY_RELEASED, keyCode}; } } private int previousKeyPressed; private int lastKeyPressed; /** * Pushes a key press event with the given keycode into LWUIT * * @param keyCode keycode of the key event */ public void keyPressed(final int keyCode){ if(impl.getCurrentForm() == null){ return; } addInputEvent(createKeyEvent(keyCode, true)); lastInteractionWasKeypad = lastInteractionWasKeypad || (keyCode != MenuBar.leftSK && keyCode != MenuBar.clearSK && keyCode != MenuBar.backSK); // this solves a Sony Ericsson bug where on slider open/close someone "brilliant" chose // to send a keyPress with a -43/-44 keycode... Without ever sending a key release! keyRepeatCharged = (keyCode >= 0 || getGameAction(keyCode) > 0) || keyCode == impl.getClearKeyCode(); longPressCharged = keyRepeatCharged; longKeyPressTime = System.currentTimeMillis(); keyRepeatValue = keyCode; nextKeyRepeatEvent = System.currentTimeMillis() + keyRepeatInitialIntervalTime; previousKeyPressed = lastKeyPressed; lastKeyPressed = keyCode; } /** * Pushes a key release event with the given keycode into LWUIT * * @param keyCode keycode of the key event */ public void keyReleased(final int keyCode){ keyRepeatCharged = false; longPressCharged = false; if(impl.getCurrentForm() == null){ return; } // this can happen when traversing from the native form to the current form // caused by a keypress // We need the previous key press for lwuit issue 108 which can occur when typing into // text field rapidly and pressing two buttons at once. Originally I had a patch // here specifically to the native edit but that patch doesn't work properly for // all native phone bugs (e.g. incoming phone call rejected and the key release is // sent to the java application). if(keyCode != lastKeyPressed) { if(keyCode != previousKeyPressed) { return; } else { previousKeyPressed = 0; } } else { lastKeyPressed = 0; } addInputEvent(createKeyEvent(keyCode, false)); } void keyRepeatedInternal(final int keyCode){ } /** * Pushes a pointer drag event with the given coordinates into LWUIT * * @param x the x position of the pointer * @param y the y position of the pointer */ public void pointerDragged(final int[] x, final int[] y){ if(impl.getCurrentForm() == null){ return; } longPointerCharged = false; addInputEvent(createPointerEvent(x, y, POINTER_DRAGGED)); } /** * Pushes a pointer hover event with the given coordinates into LWUIT * * @param x the x position of the pointer * @param y the y position of the pointer */ public void pointerHover(final int[] x, final int[] y){ if(impl.getCurrentForm() == null){ return; } addInputEvent(createPointerEvent(x, y, POINTER_HOVER)); } /** * Pushes a pointer hover release event with the given coordinates into LWUIT * * @param x the x position of the pointer * @param y the y position of the pointer */ public void pointerHoverPressed(final int[] x, final int[] y){ if(impl.getCurrentForm() == null){ return; } addInputEvent(createPointerEvent(x, y, POINTER_HOVER_PRESSED)); } /** * Pushes a pointer hover release event with the given coordinates into LWUIT * * @param x the x position of the pointer * @param y the y position of the pointer */ public void pointerHoverReleased(final int[] x, final int[] y){ if(impl.getCurrentForm() == null){ return; } addInputEvent(createPointerEvent(x, y, POINTER_HOVER_RELEASED)); } /** * Pushes a pointer press event with the given coordinates into LWUIT * * @param x the x position of the pointer * @param y the y position of the pointer */ public void pointerPressed(final int[] x,final int[] y){ if(impl.getCurrentForm() == null){ return; } lastInteractionWasKeypad = false; longPointerCharged = true; longKeyPressTime = System.currentTimeMillis(); pointerX = x[0]; pointerY = y[0]; addInputEvent(createPointerEvent(x, y, POINTER_PRESSED)); } /** * Pushes a pointer release event with the given coordinates into LWUIT * * @param x the x position of the pointer * @param y the y position of the pointer */ public void pointerReleased(final int[] x, final int[] y){ longPointerCharged = false; if(impl.getCurrentForm() == null){ return; } addInputEvent(createPointerEvent(x, y, POINTER_RELEASED)); } /** * Notifies LWUIT of display size changes, this method is invoked by the implementation * class and is for internal use * * @param w the width of the drawing surface * @param h the height of the drawing surface */ public void sizeChanged(int w, int h){ Form current = impl.getCurrentForm(); if(current == null) { return; } if(w == current.getWidth() && h == current.getHeight()) { return; } addInputEvent(createSizeChangedEvent(w, h)); } private int[] createSizeChangedEvent(int w, int h) { return new int[] {SIZE_CHANGED, w, h}; } /** * Broadcasts hide notify into LWUIT, this method is invoked by the LWUIT implementation * to notify LWUIT of hideNotify events */ public void hideNotify(){ keyRepeatCharged = false; longPressCharged = false; longPointerCharged = false; pointerPressedAndNotReleasedOrDragged = false; addInputEvent(new int[]{HIDE_NOTIFY}); } /** * Broadcasts show notify into LWUIT, this method is invoked by the LWUIT implementation * to notify LWUIT of showNotify events */ public void showNotify(){ addInputEvent(new int[]{SHOW_NOTIFY}); } /** * Used by the flush functionality which doesn't care much about component * animations */ boolean shouldEDTSleepNoFormAnimation() { boolean b; synchronized(lock){ b = inputEvents.size() == 0 && hasNoSerialCallsPending() && (!keyRepeatCharged || !longPressCharged); } return b; } private void updateDragSpeedStatus(int[] ev) { //save dragging input to calculate the dragging speed later dragPathX[dragPathOffset] = pointerEvent(1, ev)[0]; dragPathY[dragPathOffset] = pointerEvent(2, ev)[0]; dragPathTime[dragPathOffset] = displayInitTime + (long) ev[ev.length - 1]; if (dragPathLength < PATHLENGTH) { dragPathLength++; } dragPathOffset++; if (dragPathOffset >= PATHLENGTH) { dragPathOffset = 0; } } /** * Invoked on the EDT to propagate the event */ private void handleEvent(int[] ev) { Form f = getCurrentUpcomingForm(true); // might happen when returning from a deinitialized version of LWUIT if(f == null) { return; } switch(ev[0]) { case KEY_PRESSED: f.keyPressed(ev[1]); break; case KEY_RELEASED: f.keyReleased(ev[1]); break; case POINTER_PRESSED: dragOccured = false; dragPathLength = 0; pointerPressedAndNotReleasedOrDragged = true; f.pointerPressed(pointerEvent(1, ev), pointerEvent(2, ev)); break; case POINTER_RELEASED: pointerPressedAndNotReleasedOrDragged = false; f.pointerReleased(pointerEvent(1, ev), pointerEvent(2, ev)); break; case POINTER_DRAGGED: dragOccured = true; updateDragSpeedStatus(ev); pointerPressedAndNotReleasedOrDragged = false; f.pointerDragged(pointerEvent(1, ev), pointerEvent(2, ev)); break; case POINTER_HOVER: updateDragSpeedStatus(ev); f.pointerHover(pointerEvent(1, ev), pointerEvent(2, ev)); break; case POINTER_HOVER_RELEASED: f.pointerHoverReleased(pointerEvent(1, ev), pointerEvent(2, ev)); break; case POINTER_HOVER_PRESSED: f.pointerHoverPressed(pointerEvent(1, ev), pointerEvent(2, ev)); break; case SIZE_CHANGED: f.sizeChangedInternal(ev[1], ev[2]); break; case HIDE_NOTIFY: f.hideNotify(); break; case SHOW_NOTIFY: f.showNotify(); break; } } /** * This method should be invoked by components that broadcast events on the pointerReleased callback. * This method will indicate if a drag occured since the pointer press event, notice that this method will not * behave as expected for multi-touch events. * * @return true if a drag has occured since the last pointer pressed */ public boolean hasDragOccured() { return dragOccured; } private int[] pointerEvent(int off, int[] event) { int[] peX = new int[(event.length - 1) / 2]; int offset = 0; for (int iter = off; iter < (event.length - 1); iter += 2) { peX[offset] = event[iter]; offset++; } return peX; } /** * Returns true for a case where the EDT has nothing at all to do */ boolean shouldEDTSleep() { Form current = impl.getCurrentForm(); return (current == null || (!current.hasAnimations())) && (animationQueue == null || animationQueue.size() == 0) && inputEvents.size() == 0 && (!impl.hasPendingPaints()) && hasNoSerialCallsPending() && !keyRepeatCharged && !longPointerCharged; } /** * Returns the video control for the media player * * @param player the media player * @return the video control for the media player */ Object getVideoControl(Object player) { return impl.getVideoControl(player); } Form getCurrentInternal() { return impl.getCurrentForm(); } /** * Same as getCurrent with the added exception of looking into the future * transitions and returning the last current in the transition (the upcoming * value for current) * * @return the form currently displayed on the screen or null if no form is * currently displayed */ Form getCurrentUpcoming() { return getCurrentUpcomingForm(false); } private Form getCurrentUpcomingForm(boolean includeMenus) { Form upcoming = null; // we are in the middle of a transition so we should extract the next form if(animationQueue != null) { int size = animationQueue.size(); for(int iter = 0 ; iter < size ; iter++) { Object o = animationQueue.elementAt(iter); if(o instanceof Transition) { upcoming = (Form)((Transition)o).getDestination(); } } } if(upcoming == null) { if(includeMenus){ Form f = impl.getCurrentForm(); if(f instanceof Dialog) { if(((Dialog)f).isDisposed()) { return getCurrent(); } } return f; }else{ return getCurrent(); } } return upcoming; } /** * Return the form currently displayed on the screen or null if no form is * currently displayed. * * @return the form currently displayed on the screen or null if no form is * currently displayed */ public Form getCurrent(){ Form current = impl.getCurrentForm(); if(current != null && current instanceof Dialog) { if(((Dialog)current).isMenu() || ((Dialog)current).isDisposed()) { Form p = current.getPreviousForm(); if(p != null) { return p; } // we are in the middle of a transition so we should extract the next form if(animationQueue != null) { int size = animationQueue.size(); for(int iter = 0 ; iter < size ; iter++) { Object o = animationQueue.elementAt(iter); if(o instanceof Transition) { return (Form)((Transition)o).getDestination(); } } } } } return current; } /** * Return the number of alpha levels supported by the implementation. * * @return the number of alpha levels supported by the implementation */ public int numAlphaLevels(){ return impl.numAlphaLevels(); } /** * Returns the number of colors applicable on the device, note that the API * does not support gray scale devices. * * @return the number of colors applicable on the device */ public int numColors() { return impl.numColors(); } /** * Return the width of the display * * @return the width of the display */ public int getDisplayWidth(){ return impl.getDisplayWidth(); } /** * Return the height of the display * * @return the height of the display */ public int getDisplayHeight(){ return impl.getDisplayHeight(); } /** * Causes the given component to repaint, used internally by Form * * @param cmp the given component to repaint */ void repaint(final Animation cmp){ impl.repaint(cmp); } /** * Converts the dips count to pixels, dips are roughly 1mm in length. This is a very rough estimate and not * to be relied upon * * @param dipCount the dips that we will convert to pixels * @param horizontal indicates pixels in the horizontal plane * @return value in pixels */ public int convertToPixels(int dipCount, boolean horizontal) { return impl.convertToPixels(dipCount, horizontal); } /** * Returns the game action code matching the given key combination * * @param keyCode key code received from the event * @return game action matching this keycode */ public int getGameAction(int keyCode){ return impl.getGameAction(keyCode); } /** * Returns the keycode matching the given game action constant (the opposite of getGameAction). * On some devices getKeyCode returns numeric keypad values for game actions, * this breaks the code since we filter these values (to prevent navigation on '2'). * We pick unused negative values for game keys and assign them to game keys for * getKeyCode so they will work with getGameAction. * * @param gameAction game action constant from this class * @return keycode matching this constant * @deprecated this method doesn't work properly across device and is mocked up here * mostly for the case of unit testing. Do not use it for anything other than that! Do * not rely on getKeyCode(GAME_*) == keyCodeFromKeyEvent, this will never actually happen! */ public int getKeyCode(int gameAction){ return impl.getKeyCode(gameAction); } /** * Indicates whether the 3rd softbutton should be supported on this device * * @return true if a third softbutton should be used */ public boolean isThirdSoftButton() { return thirdSoftButton; } /** * Indicates whether the 3rd softbutton should be supported on this device * * @param thirdSoftButton true if a third softbutton should be used */ public void setThirdSoftButton(boolean thirdSoftButton) { this.thirdSoftButton = thirdSoftButton; } /** * Displays the virtual keyboard on devices that support manually poping up * the vitual keyboard * * @param show toggles the virtual keyboards visibility */ public void setShowVirtualKeyboard(boolean show) { if(isTouchScreenDevice()){ VirtualKeyboardInterface vkb = getDefaultVirtualKeyboard(); if(vkb != null){ vkb.showKeyboard(show); } } } /** * Indicates if the virtual keyboard is currently showing or not * * @return true if the virtual keyboard is showing */ public boolean isVirtualKeyboardShowing() { if(!isTouchScreenDevice()){ return false; } return getDefaultVirtualKeyboard() != null && getDefaultVirtualKeyboard().isVirtualKeyboardShowing(); } /** * Returns all platform supported virtual keyboards names * @return all platform supported virtual keyboards names */ public String [] getSupportedVirtualKeyboard(){ String [] retVal = new String[virtualKeyboards.size()]; int index = 0; Enumeration keys = virtualKeyboards.keys(); while (keys.hasMoreElements()) { retVal[index++] = (String) keys.nextElement(); } return retVal; } /** * Register a virtual keyboard * @param vkb */ public void registerVirtualKeyboard(VirtualKeyboardInterface vkb){ virtualKeyboards.put(vkb.getVirtualKeyboardName(), vkb); } /** * Sets the default virtual keyboard to be used by the platform * * @param vkb a VirtualKeyboard to be used or null to disable the * VirtualKeyboard */ public void setDefaultVirtualKeyboard(VirtualKeyboardInterface vkb){ if(vkb != null){ selectedVirtualKeyboard = vkb.getVirtualKeyboardName(); if(!virtualKeyboards.containsKey(selectedVirtualKeyboard)){ registerVirtualKeyboard(vkb); } }else{ selectedVirtualKeyboard = null; } } /** * Get the default virtual keyboard or null if the VirtualKeyboard is disabled * @return the default vkb */ public VirtualKeyboardInterface getDefaultVirtualKeyboard(){ if(selectedVirtualKeyboard == null){ return null; } return (VirtualKeyboardInterface)virtualKeyboards.get(selectedVirtualKeyboard); } /** * Returns the type of the input device one of: * KEYBOARD_TYPE_UNKNOWN, KEYBOARD_TYPE_NUMERIC, KEYBOARD_TYPE_QWERTY, * KEYBOARD_TYPE_VIRTUAL, KEYBOARD_TYPE_HALF_QWERTY * * @return KEYBOARD_TYPE_UNKNOWN */ public int getKeyboardType() { return impl.getKeyboardType(); } /** * Indicates whether the device supports native in place editing in which case * lightweight input logic shouldn't be used for input. * * @return false by default */ public boolean isNativeInputSupported() { return impl.isNativeInputSupported(); } /** * Indicates whether the device supports multi-touch events, this is only * relevant when touch events are supported * * @return false by default */ public boolean isMultiTouch() { return impl.isMultiTouch(); } /** * Indicates whether the device has a double layer screen thus allowing two * stages to touch events: click and hover. This is true for devices such * as the storm but can also be true for a PC with a mouse pointer floating * on top. * <p>A click touch screen will also send pointer hover events to the underlying * software and will only send the standard pointer events on click. * * @return false by default */ public boolean isClickTouchScreen() { return impl.isClickTouchScreen(); } /** * This method returns the dragging speed based on the latest dragged * events * @param yAxis indicates what axis speed is required * @return the dragging speed */ public float getDragSpeed(boolean yAxis){ float speed; if(yAxis){ speed = impl.getDragSpeed(dragPathY, dragPathTime, dragPathOffset, dragPathLength); }else{ speed = impl.getDragSpeed(dragPathX, dragPathTime, dragPathOffset, dragPathLength); } return speed; } /** * Indicates whether LWUIT should consider the bidi RTL algorithm * when drawing text or navigating with the text field cursor. * * @return true if the bidi algorithm should be considered */ public boolean isBidiAlgorithm() { return impl.isBidiAlgorithm(); } /** * Indicates whether LWUIT should consider the bidi RTL algorithm * when drawing text or navigating with the text field cursor. * * @param activate set to true to activate the bidi algorithm, false to * disable it */ public void setBidiAlgorithm(boolean activate) { impl.setBidiAlgorithm(activate); } /** * Converts the given string from logical bidi layout to visual bidi layout so * it can be rendered properly on the screen. This method is only necessary * for devices/platforms that don't have "built in" bidi support such as * Sony Ericsson devices. * See <a href="http://www.w3.org/International/articles/inline-bidi-markup/#visual">this</a> * for more on visual vs. logical ordering. * * * @param s a "logical" string with RTL characters * @return a "visual" renderable string */ public String convertBidiLogicalToVisual(String s) { return impl.convertBidiLogicalToVisual(s); } /** * Returns the index of the given char within the source string, the actual * index isn't necessarily the same when bidi is involved * See <a href="http://www.w3.org/International/articles/inline-bidi-markup/#visual">this</a> * for more on visual vs. logical ordering. * * @param source the string in which we are looking for the position * @param index the "logical" location of the cursor * @return the "visual" location of the cursor */ public int getCharLocation(String source, int index) { return impl.getCharLocation(source, index); } /** * Returns true if the given character is an RTL character * * @param c character to test * @return true if the charcter is an RTL character */ public boolean isRTL(char c) { return impl.isRTL(c); } /** * This method is essentially equivalent to cls.getResourceAsStream(String) * however some platforms might define unique ways in which to load resources * within the implementation. * * @param cls class to load the resource from * @param resource relative/absolute URL based on the Java convention * @return input stream for the resource or null if not found */ public InputStream getResourceAsStream(Class cls, String resource) { return impl.getResourceAsStream(cls, resource); } /** * An error handler will receive an action event with the source exception from the EDT * once an error handler is installed the default LWUIT error dialog will no longer appear * * @param e listener receiving the errors */ public void addEdtErrorHandler(ActionListener e) { if(errorHandler == null) { errorHandler = new EventDispatcher(); } errorHandler.addListener(e); } /** * An error handler will receive an action event with the source exception from the EDT * once an error handler is installed the default LWUIT error dialog will no longer appear * * @param e listener receiving the errors */ public void removeEdtErrorHandler(ActionListener e) { if(errorHandler != null) { errorHandler.removeListener(e); Vector v = errorHandler.getListenerVector(); if(v == null || v.size() == 0) { errorHandler = null; } } } /** * Allows a LWUIT application to minimize without forcing it to the front whenever * a new dialog is poped up * * @param allowMinimizing value */ public void setAllowMinimizing(boolean allowMinimizing) { this.allowMinimizing = allowMinimizing; } /** * Allows a LWUIT application to minimize without forcing it to the front whenever * a new dialog is poped up * * @return allowMinimizing value */ public boolean isAllowMinimizing() { return allowMinimizing; } /** * This is an internal state flag relevant only for pureTouch mode (otherwise it * will always be true). A pureTouch mode is stopped if a user switches to using * the trackball/navigation pad and this flag essentially toggles between those two modes. * * @return the shouldRenderSelection */ public boolean shouldRenderSelection() { return !pureTouch || pointerPressedAndNotReleasedOrDragged || lastInteractionWasKeypad; } /** * This is an internal state flag relevant only for pureTouch mode (otherwise it * will always be true). A pureTouch mode is stopped if a user switches to using * the trackball/navigation pad and this flag essentially toggles between those two modes. * * @param c the component to test against, this prevents a touch outside of the component that triggers a repaint from painting the component selection * @return the shouldRenderSelection */ public boolean shouldRenderSelection(Component c) { if(c.isCellRenderer()) { return shouldRenderSelection(); } return !pureTouch || lastInteractionWasKeypad || (pointerPressedAndNotReleasedOrDragged && c.contains(pointerX, pointerY)); } /** * A pure touch device has no focus showing when the user is using the touch * interface. Selection only shows when the user actually touches the screen * or suddenly switches to using a keypad/trackball. This sort of interface * is common in Android devices * * @return the pureTouch flag */ public boolean isPureTouch() { return pureTouch; } /** * A pure touch device has no focus showing when the user is using the touch * interface. Selection only shows when the user actually touches the screen * or suddenly switches to using a keypad/trackball. This sort of interface * is common in Android devices * * @param pureTouch the value for pureTouch */ public void setPureTouch(boolean pureTouch) { this.pureTouch = pureTouch; } /** * Indicates whether LWUIT commands should be mapped to the native menus * * @return the nativeCommands status * @deprecated use getCommandBehavior() == Display.COMMAND_BEHAVIOR_NATIVE */ public boolean isNativeCommands() { return getCommandBehavior() == COMMAND_BEHAVIOR_NATIVE; } /** * Indicates whether LWUIT commands should be mapped to the native menus * * @param nativeCommands the flag to set * @deprecated use setCommandBehavior(Display.COMMAND_BEHAVIOR_NATIVE) */ public void setNativeCommands(boolean nativeCommands) { setCommandBehavior(COMMAND_BEHAVIOR_NATIVE); } /** * Exits the application... */ public void exitApplication() { lwuitExited = true; impl.exitApplication(); } /** * Shows a native Form/Canvas or some other heavyweight native screen * * @param nativeFullScreenPeer the native screen peer */ public void showNativeScreen(Object nativeFullScreenPeer) { impl.showNativeScreen(nativeFullScreenPeer); } /** * Normally LWUIT folds the VKB when switching forms this field allows us * to block that behavior. * @return the autoFoldVKBOnFormSwitch */ public boolean isAutoFoldVKBOnFormSwitch() { return autoFoldVKBOnFormSwitch; } /** * Normally LWUIT folds the VKB when switching forms this field allows us * to block that behavior. * @param autoFoldVKBOnFormSwitch the autoFoldVKBOnFormSwitch to set */ public void setAutoFoldVKBOnFormSwitch(boolean autoFoldVKBOnFormSwitch) { this.autoFoldVKBOnFormSwitch = autoFoldVKBOnFormSwitch; } /** * Indicates the way commands should be added to a form as one of the ocmmand constants defined * in this class * * @return the commandBehavior */ public int getCommandBehavior() { return commandBehavior; } /** * Indicates the way commands should be added to a form as one of the ocmmand constants defined * in this class * * @param commandBehavior the commandBehavior to set */ public void setCommandBehavior(int commandBehavior) { this.commandBehavior = commandBehavior; impl.notifyCommandBehavior(commandBehavior); } /** * Returns the property from the underlying platform deployment or the default * value if no deployment values are supported. This is equivalent to the * getAppProperty from the jad file. * <p>The implementation should be responsible for the following keys to return * reasonable valid values for the application: * <ol> * <li>AppName * <li>UserAgent - ideally although not required * <li>AppVersion * <li>Platform - Similar to microedition.platform * <li>OS - returns what is the underlying platform e.g. - J2ME, RIM, SE... * * </ol> * @param key the key of the property * @param defaultValue a default return value * @return the value of the property */ public String getProperty(String key, String defaultValue) { if(localProperties != null) { String v = (String)localProperties.get(key); if(v != null) { return v; } } return impl.getProperty(key, defaultValue); } /** * Sets a local property to the application, this method has no effect on the * implementation code and only allows the user to override the logic of getProperty * for internal application purposes. * * @param key key the key of the property * @param value the value of the property */ public void setProperty(String key, String value) { if(localProperties == null) { localProperties = new Hashtable(); } localProperties.put(key, value); } /** * Executes the given URL on the native platform * * @param url the url to execute */ public void execute(String url) { impl.execute(url); } /** * Returns one of the density variables appropriate for this device, notice that * density doesn't alwyas correspond to resolution and an implementation might * decide to change the density based on DPI constraints. * * @return one of the DENSITY constants of Display */ public int getDeviceDensity() { return impl.getDeviceDensity(); } /** * Plays a builtin device sound matching the given identifier, implementations * and themes can offer additional identifiers to the ones that are already built * in. * * @param soundIdentifier the sound identifier which can match one of the * common constants in this class or be a user/implementation defined sound */ public void playBuiltinSound(String soundIdentifier) { impl.playBuiltinSound(soundIdentifier); } /** * Installs a replacement sound as the builtin sound responsible for the given * sound identifier (this will override the system sound if such a sound exists). * * @param soundIdentifier the sound string passed to playBuiltinSound * @param data an input stream containing platform specific audio file, its usually safe * to assume that wav/mp3 would be supported. * @throws IOException if the stream throws an exception */ public void installBuiltinSound(String soundIdentifier, InputStream data) throws IOException { impl.installBuiltinSound(soundIdentifier, data); } /** * Indicates whether a user installed or system sound is available * * @param soundIdentifier the sound string passed to playBuiltinSound * @return true if a sound of this given type is avilable */ public boolean isBuiltinSoundAvailable(String soundIdentifier) { return impl.isBuiltinSoundAvailable(soundIdentifier); } /** * Allows muting/unmuting the builtin sounds easily * * @param enabled indicates whether the sound is muted */ public void setBuiltinSoundsEnabled(boolean enabled) { impl.setBuiltinSoundsEnabled(enabled); } /** * Allows muting/unmuting the builtin sounds easily * * @return true if the sound is *not* muted */ public boolean isBuiltinSoundsEnabled() { return impl.isBuiltinSoundsEnabled(); } /** * Creates a sound in the given URI which is partially platform specific. * Notice that an audio is "auto destroyed" on completion and cannot be played * twice! * * @param uri the platform specific location for the sound * @return a handle that can be used to control the playback of the audio * @throws java.io.IOException if the URI access fails */ public Object createAudio(String uri) throws IOException { return createAudio(uri, null); } /** * Create the sound in the given stream * Notice that an audio is "auto destroyed" on completion and cannot be played * twice! * * @param stream the stream containing the media data * @param mimeType the type of the data in the stream * @return a handle that can be used to control the playback of the audio * @throws java.io.IOException if the URI access fails */ public Object createAudio(InputStream stream, String mimeType) throws IOException { return createAudio(stream, mimeType); } /** * Creates a sound in the given URI which is partially platform specific. * Notice that an audio is "auto destroyed" on completion and cannot be played * twice! * * @param uri the platform specific location for the sound * @param onCompletion invoked when the audio file finishes playing, may be null * @return a handle that can be used to control the playback of the audio * @throws java.io.IOException if the URI access fails */ public Object createAudio(String uri, Runnable onCompletion) throws IOException { return impl.createAudio(uri, onCompletion); } /** * Create the sound in the given stream * Notice that an audio is "auto destroyed" on completion and cannot be played * twice! * * @param stream the stream containing the media data * @param mimeType the type of the data in the stream * @param onCompletion invoked when the audio file finishes playing, may be null * @return a handle that can be used to control the playback of the audio * @throws java.io.IOException if the URI access fails */ public Object createAudio(InputStream stream, String mimeType, Runnable onCompletion) throws IOException { return impl.createAudio(stream, mimeType, onCompletion); } /** * Starts playing the audio file * * @param handle the handle object returned by create audio */ public void playAudio(Object handle) { impl.playAudio(handle); } /** * Pauses the playback of the audio file * * @param handle the handle object returned by create audio */ public void pauseAudio(Object handle) { impl.pauseAudio(handle); } /** * Stops the audio playback and cleans up the resources related to it immediately. * * @param handle the playback handle */ public void cleanupAudio(Object handle) { impl.cleanupAudio(handle); } /** * Returns the time in seconds in the audio file * * @param handle the handle object returned by create audio * @return time in milli seconds */ public int getAudioTime(Object handle) { return impl.getAudioTime(handle); } /** * Sets the position in the audio file * * @param handle the handle object returned by create audio * @param time in milli seconds */ public void setAudioTime(Object handle, int time) { impl.setAudioTime(handle, time); } /** * Returns the length in seconds of the audio file * * @param handle the handle object returned by create audio * @return time in milli seconds */ public int getAudioDuration(Object handle) { return impl.getAudioDuration(handle); } /** * Sets the media playback volume in percentage * * @param vol the volume for media playback */ public void setVolume(int vol) { impl.setVolume(vol); } /** * Returns the media playback volume in percentage * * @return the volume percentage */ public int getVolume() { return impl.getVolume(); } /** * Creates a soft/weak reference to an object that allows it to be collected * yet caches it. This method is in the porting layer since CLDC only includes * weak references while some platforms include nothing at all and some include * the superior soft references. * * @param o object to cache * @return a caching object or null if caching isn't supported */ public Object createSoftWeakRef(Object o) { return impl.createSoftWeakRef(o); } /** * Extracts the hard reference from the soft/weak reference given * * @param o the reference returned by createSoftWeakRef * @return the original object submitted or null */ public Object extractHardRef(Object o) { return impl.extractHardRef(o); } /** * Indicates if the implemenetation has a native underlying theme * * @return true if the implementation has a native theme available */ public boolean hasNativeTheme() { return impl.hasNativeTheme(); } /** * Installs the native theme, this is only applicable if hasNativeTheme() returned true. Notice that this method * might replace the DefaultLookAndFeel instance and the default transitions. */ public void installNativeTheme() { impl.installNativeTheme(); } /** * Performs a clipboard copy operation, if the native clipboard is supported by the implementation it would be used * * @param obj object to copy, while this can be any arbitrary object it is recommended that only Strings or LWUIT * image objects be used to copy */ public void copyToClipboard(Object obj) { impl.copyToClipboard(obj); } /** * Returns the current content of the clipboard * * @return can be any object or null see copyToClipboard */ public Object getPasteDataFromClipboard() { return impl.getPasteDataFromClipboard(); } /** * Returns true if the device is currently in portrait mode * * @return true if the device is in portrait mode */ public boolean isPortrait() { return impl.isPortrait(); } /** * Returns true if the device allows forcing the orientation via code, feature phones do not allow this * although some include a jad property allowing for this feature * * @return true if lockOrientation would work */ public boolean canForceOrientation() { return impl.canForceOrientation(); } /** * On devices that return true for canForceOrientation() this method can lock the device orientation * either to portrait or landscape mode * * @param portrait true to lock to portrait mode, false to lock to landscape mode */ public void lockOrientation(boolean portrait) { impl.lockOrientation(portrait); } /** * Indicates whether the device is a tablet, notice that this is often a guess * * @return true if the device is assumed to be a tablet */ public boolean isTablet() { return impl.isTablet(); } }