javauis/eswt_qt/org.eclipse.swt/Eclipse SWT/qt/org/eclipse/ercp/swt/mobile/MobileDevice.java
author hgs
Fri, 15 Oct 2010 12:29:39 +0300
changeset 80 d6dafc5d983f
parent 50 023eef975703
permissions -rw-r--r--
v2.2.19_1

/*******************************************************************************
 * Copyright (c) 2004 IBM Corp.
 * Portion Copyright (c) 2005, 2008 Nokia Corporation and/or its subsidiary(-ies).
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Mark Rogalski (IBM Corp.) - initial API specification
 *    Nokia Corporation - S60 implementation
 *    Nokia Corporation - QT implementation
 *******************************************************************************/

package org.eclipse.ercp.swt.mobile;

import java.util.Vector;

import org.eclipse.swt.SWT;
import org.eclipse.swt.internal.qt.OS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Internal_PackageSupport;

/** 
 * Instances of this class represent the device that is being used.
 * It provides methods that enable applications to learn more about the device 
 * specific characteristics and capabilities.
 * <p>
 * Applications can query what input features and display screens are permanently 
 * part of the device. These are considered <i>local</i> features. Some devices also
 * allow input mechanisms or screens to be attached to the device at runtime. These
 * are considered <i>remote</i> features.  Since local features do not come and go,
 * it is sufficient to query for them once. On the other hand, since remote devices
 * can be added or removed at any time, an application needs to add a <code>
 * MobileDeviceListener</code> to be informed of these events.
 * <p> 
 * Local features may also be <i>internal</i> or <i>external</i>. 
 * External features are only available when a device is closed.
 * Internal features are only available when a device is opened.
 *
 * @see Screen
 * @see Input
 * @see MobileDeviceListener
 */
public class MobileDevice {

    private static int  handleMobileDevice;
    private Vector      mobileDeviceListeners;
    private Screen[]    screens;
    private Input[]     inputs;
    private int         alertLevel;
    private boolean     isOpen;
    private static int  desktopWidgetHandle;
    static MobileDevice currentMobileDevice;
    private int         activeScreenId;
    
    

    
    /** 
     * feature which is permanently attached to the mobile device and always available
     */    
    public static final int LOCAL = 0;

    /**
     * local feature which is only available when mobile device is opened
     */
    public static final int INTERNAL = 1;

    /**
     * local feature which is only available when mobile device is closed
     */
    public static final int EXTERNAL = 2;
    
    /**
     * feature which is not part of the mobile device unless connected via wire or wireless signal
     */
    public static final int REMOTE = 3;
    
    /**
     * alert level indicating simple notification (ex. e-mail arrival)
     */
    public static final int ALERT_LOW_IMPORTANCE = 1;
    
    /**
     * alert level indicating user attention desired (ex. instant message arrival)
     */
    public static final int ALERT_HIGH_IMPORTANCE = 2;
    
    /**
     * alert level indicating immediate attention is required (ex. battery level at 1%)
     */
    public static final int ALERT_CRITICAL = 3;
   /**
    * virtual keyboard status indicating normal show and hide operation
    */
    public static final int VK_NORMAL = 1;

    /**
     * virtual keyboard status indicating keyboard is always visible
     */
    public static final int VK_ALWAYS_ON = 2;

    /**
     * virtual keyboard status indicating keyboard is always hidden
     */
    public static final int VK_ALWAYS_OFF = 3;      
    
    /**
     * Constructs a new instance of this class. Non-public to prevent
     * instantiation outside this package.
     * 
     */
    MobileDevice () {
       
        (getDisplay()).syncExec(new Runnable() {
            public void run() {
                handleMobileDevice = Internal_PackageSupport.initializeMobileDevice(
                        Internal_PackageSupport.getDisplayInstance());
                if (handleMobileDevice == 0) {
                    SWT.error(SWT.ERROR_NO_HANDLES);
                }
                OS.MobileDevice_createFlipWatch(handleMobileDevice);
            }
        }); 
        mobileDeviceListeners = new Vector();
        isOpen = true;
    }
    
    private static Display getDisplay(){
        Display display = Internal_PackageSupport.getDisplayInstance();
        
        // Following if-statement (getting default display) should be removed
        // if agreed that MobileDevice shouldn't create the display.
        if(display==null){
            display = Display.getDefault();
        }
        
        if(display == null || display.isDisposed()){
            SWT.error(SWT.ERROR_DEVICE_DISPOSED);
        }
        return display;
    }
    
    private void sendMobileDeviceChangedEvent(int eventType) {
        // MobileDevice changed event for open and closed
        int count = mobileDeviceListeners.size();
        for (int i = 0; i <count; ++i) {
            MobileDeviceEvent event = new MobileDeviceEvent(this);
            event.type = eventType;
            MobileDeviceListener listener = (MobileDeviceListener) mobileDeviceListeners.elementAt(i);
            listener.deviceChanged(event);
        }
    }
    
    void qt_swt_event_mobiledevice_changed(boolean aStatus) {
        // update MobileDevice status to opened/closed
        isOpen = aStatus;
        
        if(isOpen){
            sendMobileDeviceChangedEvent(MobileDeviceEvent.OPENED);
        }else{
            sendMobileDeviceChangedEvent(MobileDeviceEvent.CLOSED);
        }
    }
    
    void qt_swt_event_mobiledevice_screen_activate(boolean aStatus) {
        
        if(screens.length > 0 && activeScreenId<screens.length){
            if(aStatus){
                // update the active screen
                activeScreenId = OS.getScreenDeviceNumber();
                //forward to screen activated event to new active screen
                screens[activeScreenId].internal_sendScreenEventActivated();
            }else {
                //forward to screen deactivated event to old active screen
                screens[activeScreenId].internal_sendScreenEventDeactivated();
            }
        }
    }
    
    void qt_swt_event_mobiledevice_orientationchanged() {
        activeScreenId = OS.getScreenDeviceNumber();
        if(activeScreenId<screens.length){
            // forward to orientation change event to active screen
            screens[activeScreenId].internal_sendOrientationScreenChanged();
        }
    }
    
    private void sendMobileDeviceScreenChangedEvent(int eventType, Screen screen) {
        int count = mobileDeviceListeners.size();
        for (int i = 0; i <count; ++i) {
            MobileDeviceEvent event = new MobileDeviceEvent(this);
            event.type = eventType;
            event.data = screen;
            event.feature = screen;
            MobileDeviceListener listener = (MobileDeviceListener) mobileDeviceListeners.elementAt(i);
            listener.screenChanged(event);
        }
    }
    
    void qt_signal_screen_changed(int aCount) {
        int type = 0;
        Screen screen = null;
        Screen []newScreens = new Screen[aCount];
        
        // if getScreen is not called and if still want 
        // screen changed event in the MobileDevice
        if(screens.length==0){
            getScreens();
        }
        
        if(screens.length > aCount){
            // screen removed
            int screenHandle = 0;
            for(int j=0; j<screens.length; j++) {
                // found removed screen
                boolean found = false;
                for(int i=0; i<aCount; i++) {     
                    screenHandle = OS.QDesktopWidget_screen(desktopWidgetHandle, i);
                    if(screens[j].screenHandle== screenHandle){
                        newScreens[i] = screens[j];
                        newScreens[i].id = i;
                        found = true;
                        break;
                     }
                }
                if(!found){
                    screen = screens[j];
                    screens[j].internal_dispose();
                } 
            }
            screens = new Screen[aCount];
            // remove detached screen from 'screens'
            System.arraycopy(newScreens, 0, screens, 0, aCount);
            type = MobileDeviceEvent.REMOVED;
        } else if( screens.length < aCount){
            // screen attached
            for(int i=0; i<aCount; i++) {   
                // found added screen
                int screenHandle = OS.QDesktopWidget_screen(desktopWidgetHandle, i);
                boolean found=false;
                for(int j=0; j<screens.length; j++) {
                    if(screens[j].screenHandle== screenHandle){
                        newScreens[i] = screens[j];
                        newScreens[i].id = i;
                        found = true;
                        break;
                    }
                }
                if(!found){
                    screen = screens[i];
                    newScreens[i] = new Screen(i, desktopWidgetHandle);
                } 
            }
            screens = new Screen[aCount];
            // add attached screen to 'screens'
            System.arraycopy(newScreens, 0, screens, 0, aCount);
            type = MobileDeviceEvent.ADDED;
        }
        sendMobileDeviceScreenChangedEvent(type, screen);
        activeScreenId = OS.getScreenDeviceNumber();
    }
    
    private static void hookEvents() {
        int screenSignalProxy = OS.SignalHandler_new(desktopWidgetHandle, OS.QSIGNAL_QDESKTOPWIDGET_SCREENCOUNTCHANGED);
        OS.QObject_connectOrThrow(desktopWidgetHandle, "screenCountChanged(int)", screenSignalProxy,
                "widgetSignal(int)", OS.QT_AUTOCONNECTION);
    }

    /**
     * Adds the listener to the collection of listeners who will be notified when a device 
     * configuration change occurs, by calling one of the methods defined in the 
     * <code>MobileDeviceListener</code> interface. 
     * <p>
     * <code>screenChanged</code> is called when a monitor configuration changes.<br>
     * <code>inputChanged</code> is called when an input configuration changes.<br>
     * <code>deviceChanged</code> is called when the device is opened or closed.<br>
     *
     * @param listener instance called when device events occur
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception SWTError <ul>
     *    <li>ERROR_ITEM_NOT_ADDED - if the operation fails because of an operating system failure</li>
     * </ul>
     *
     * @see #removeMobileDeviceListener
     * @see MobileDeviceListener
     */
    public void addMobileDeviceListener(MobileDeviceListener listener) {
        if (listener == null) {
            SWT.error (SWT.ERROR_NULL_ARGUMENT);
        }
        mobileDeviceListeners.addElement(listener);
    }
    
    
    /**
     * Alerts the user using device specific mechanisms such as sounds, flashing, or
     * vibration. <code>Level</code> must be one of ALERT_LOW_IMPORTANCE,
     * ALERT_HIGH_IMPORTANCE, or ALERT_CRITICAL. The method maps these levels to
     * device specific features that may also be dependent upon user configuration
     * or current profile.
     * 
     * @param level
     *            constant describing the importance of the alert
     * 
     * @exception IllegalArgumentException
     *                <ul>
     *                <li>ERROR_INVALID_ARGUMENT - if level is not one of the
     *                allowed class constants</li>
     *                </ul>
     * 
     * @see #ALERT_LOW_IMPORTANCE
     * @see #ALERT_HIGH_IMPORTANCE
     * @see #ALERT_CRITICAL
     */
    public void alert(int level) {
        if (    level != ALERT_LOW_IMPORTANCE &&
                level != ALERT_HIGH_IMPORTANCE &&
                level != ALERT_CRITICAL) {
            SWT.error(SWT.ERROR_INVALID_ARGUMENT);
        }
        alertLevel = level;
        (getDisplay()).syncExec(new Runnable() {
            public void run() {
                if(alertLevel == ALERT_LOW_IMPORTANCE){
                    OS.QApplication_beep();
                }else if(alertLevel == ALERT_HIGH_IMPORTANCE){
                    OS.MobileDevice_flashLights(handleMobileDevice, 3000);
                    OS.MobileDevice_vibration(handleMobileDevice, 3000);
                }else if(alertLevel == ALERT_CRITICAL){
                    OS.QApplication_beep();
                    OS.MobileDevice_flashLights(handleMobileDevice, 3000);
                    OS.MobileDevice_vibration(handleMobileDevice, 3000);
                }
               
            }
        }); 
    }
    
    
    /**
     * Returns an array of Input objects describing the input features available to the device. 
     * The return value may be <code>null</code> if there are no input features available.
     * 
     * @exception SWTError <ul>
     *    <li>ERROR_CANNOT_GET_SELECTION - if the operation fails because 
     *         of an operating system failure</li>
     * </ul> 
     *
     * @see Input
     */
    public Input[] getInputs() {
        if(inputs == null) {
            try {
                (getDisplay()).syncExec(new Runnable() {
                    public void run() {
                        int mask = OS.getHwInputs();
                        int nbInputs = 0;
                        
                        // finding out number of keyboards available
                        if((mask & OS.SYMBIAN_KEYBOARD_FULL)!=0){
                            nbInputs+=2;
                        }
                        
                        if((mask & OS.SYMBIAN_KEYBOARD_KEYPAD)!=0){
                            nbInputs+=2;
                        }
                        // create java counter part of Inputs and
                        // add location and type
                        inputs = new Input[nbInputs];
                        int i=0;
                        int location;
                        if((mask & OS.SYMBIAN_KEYBOARD_FULL)!=0){
                            if((mask & OS.SYMBIAN_KEYBOARD_KEYPAD)!=0){
                                location = MobileDevice.INTERNAL;
                            } else {
                                location = MobileDevice.LOCAL;
                            }
                            inputs[i] = new Input(i);
                            inputs[i].setLocation(location);
                            inputs[i].setType(Input.SOFTKEYS);
                            i+=1;
                            inputs[i] = new Input(i);
                            inputs[i].setLocation(location);
                            inputs[i].setType(Input.FULL_KEYBOARD);
                            i+=1;
                        }
                        
                        if((mask & OS.SYMBIAN_KEYBOARD_KEYPAD)!=0){
                            if((mask & OS.SYMBIAN_KEYBOARD_FULL)!=0){
                                location = MobileDevice.EXTERNAL;
                            } else {
                                location = MobileDevice.LOCAL;
                            }
                            inputs[i] = new Input(i);
                            inputs[i].setLocation(location);
                            inputs[i].setType(Input.SOFTKEYS);
                            i+=1;
                            inputs[i] = new Input(i);
                            inputs[i].setLocation(location);
                            inputs[i].setType(Input.KEYPAD);
                        }
                    }
                });
            } catch(Throwable e) {
                SWT.error(SWT.ERROR_CANNOT_GET_SELECTION);           
            }
        }
        return inputs;
    }
    
    
    /**
     * Returns an array of Screen objects describing the display features available to the device. 
     * The return value may be <code>null</code> if there are no display screens available.
     * 
     * @exception SWTError <ul>
     *    <li>ERROR_CANNOT_GET_SELECTION - if the operation fails because 
     *         of an operating system failure</li>
     * </ul> 
     *
     * @see Screen
     */
    public Screen[] getScreens() {
        if(screens == null) {
            try {
                (getDisplay()).syncExec(new Runnable() {
                    public void run() {
                        int nbScreens = OS.QDesktopWidget_screenCount(desktopWidgetHandle);
                        activeScreenId = OS.getScreenDeviceNumber();
                        screens = new Screen[nbScreens];
                        for(int i=0; i<nbScreens; i++) {
                            screens[i] = new Screen(i, desktopWidgetHandle);
                        }
                    }
                });
            } catch(Throwable e) {
                SWT.error(SWT.ERROR_CANNOT_GET_SELECTION);           
            }
        }
        return screens;
    }
    
    
    /**
     * Returns singleton instance of MobileDevice class.
     * 
     * @return singleton of MobileDevice class. Must not be Null.
     * 
     * @exception SWTError <ul>
     *    <li>ERROR_CANNOT_GET_SELECTION - if the operation fails because of 
     *         an operating system failure</li>
     * </ul> 
     */ 
    public static MobileDevice getMobileDevice() {
        if(currentMobileDevice == null) {
            try {
                currentMobileDevice = new MobileDevice(); 
                Internal_PackageSupport.setMobileDevice(getDisplay(),currentMobileDevice);
                desktopWidgetHandle = OS.QApplication_desktop();
                hookEvents();
            } catch (Throwable e) {
                SWT.error(SWT.ERROR_CANNOT_GET_SELECTION);
            }
        }
        return currentMobileDevice;
    }
     
     
    /**
     * Returns whether device is opened. For devices that have no internal screens 
     * or input features, the method always returns <code>true</code>.
     */
    public boolean isOpen() { 
        getDisplay();

        return isOpen; 
    }

    
    /**
     * Call to dispose the Screen object.
     *
     */
    void internal_dispose() {
        if (inputs != null) {
            for (int i = 0; i < inputs.length; i++) {
                if (inputs[i] != null) {
                    inputs[i].internal_dispose();
                }
            }
        }

        if (screens != null) {
            for (int i = 0; i < screens.length; i++) {
                if (screens[i] != null) {
                    screens[i].internal_dispose();
                }
            }
        }
        // actual native counter part is deleted in display
        currentMobileDevice = null;
        handleMobileDevice = 0;
        inputs = null;
        screens = null;
        desktopWidgetHandle = 0;
    }
    
    
    /**
     * Removes the listener from the collection of listeners who will be notified when a device 
     * configuration change occurs. 
     *
     * @param listener instance called when device events occur
     *
     * @exception IllegalArgumentException <ul>
     *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
     * </ul>
     * @exception SWTError <ul>
     *    <li>ERROR_ITEM_NOT_REMOVED - if the operation fails because 
     *         of an operating system failure</li>
     * </ul>
     *
     * @see #addMobileDeviceListener
     * @see MobileDeviceListener
     */
    public void removeMobileDeviceListener(MobileDeviceListener listener) {
        if (listener == null) {
            SWT.error (SWT.ERROR_NULL_ARGUMENT);
        }

        //remove listener
        //Remove method of Vector class never fails,
        //so ERROR_ITEM_NOT_REMOVED could never be thrown.
        mobileDeviceListeners.removeElement(listener);
    }
    
     /**
      * Sets the status of the system virtual keyboard (if one is available).
      * By default, or when status is set to VK_NORMAL, a system virtual keyboard
      * is displayed when a widget capable of text input gains focus and is
      * hidden when that widget looses focus. However, there are cases where 
      * an application may wish to continuously display the virtual keyboard
      * or always keep it hidden. Setting the status to VK_ALWAYS_ON or VK_ALWAYS_OFF
      * will accomplish this and the effect is immediate. Changing focus will
      * then have no affect on the virtual keyboard. Note: By default, widgets which 
      * normally accept input but are set to <i>read only</i> do not cause the 
      * the virtual keyboard to display.
      *
      * @param status virtual keyboard mode. One of VK_NORMAL, VK_ALWAYS_ON, VK_ALWAYS_OFF
      * 
      * @exception IllegalArgumentException <ul>
      *    <li>ERROR_INVALID_ARGUMENT - if the status parameter is not valid</li>
      * </ul>
      *
      * @see #VK_NORMAL
      * @see #VK_ALWAYS_ON
      * @see #VK_ALWAYS_OFF
      */
    public void setVKStatus(int status){
        
    }
}