javamanager/javainstaller/installerui/javasrc/com/nokia/mj/impl/installer/ui/eswt/InstallerUiEswt.java
changeset 21 2a9601315dfc
child 23 98ccebc37403
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javamanager/javainstaller/installerui/javasrc/com/nokia/mj/impl/installer/ui/eswt/InstallerUiEswt.java	Mon May 03 12:27:20 2010 +0300
@@ -0,0 +1,1305 @@
+/*
+* Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+
+package com.nokia.mj.impl.installer.ui.eswt;
+
+import com.nokia.mj.impl.fileutils.FileUtility;
+import com.nokia.mj.impl.installer.ui.ApplicationInfo;
+import com.nokia.mj.impl.installer.ui.DownloadInfo;
+import com.nokia.mj.impl.installer.ui.InstallerUi;
+import com.nokia.mj.impl.installer.ui.InstallerUiListener;
+import com.nokia.mj.impl.installer.ui.InstallInfo;
+import com.nokia.mj.impl.installer.ui.LaunchAppInfo;
+import com.nokia.mj.impl.installer.ui.PermissionInfo;
+import com.nokia.mj.impl.installer.ui.UninstallInfo;
+import com.nokia.mj.impl.rt.ui.RuntimeUi;
+import com.nokia.mj.impl.rt.ui.RuntimeUiFactory;
+import com.nokia.mj.impl.utils.ResourceUtil;
+import com.nokia.mj.impl.utils.StartUpTrace;
+import com.nokia.mj.impl.utils.exception.InstallerExceptionBase;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.graphics.Font;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.ImageLoader;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+//import org.eclipse.swt.widgets.MessageBox;
+
+/**
+ * JavaInstaller eSWT UI.
+ *
+ * @author Nokia Corporation
+ * @version $Rev: 0 $
+ */
+public class InstallerUiEswt extends InstallerUi
+{
+    /** Default shell style. */
+    private static final int SHELL_STYLE =
+        SWT.BORDER | SWT.APPLICATION_MODAL | SWT.ON_TOP;
+
+    private Shell iParent = null;
+    private Shell iDialog = null;
+    private ProgressView iProgressView = null;
+    private ProgressView iDlProgressView = null;
+    private ProgressView iOcspProgressView = null;
+    private InstallConfirmationView iInstallConfirmationView = null;
+    private UninstallConfirmationView iUninstallConfirmationView = null;
+    private PermissionConfirmationView iPermissionConfirmationView = null;
+    private UsernamePasswordView iUsernamePasswordView = null;
+    private LaunchAppQueryView iLaunchAppQueryView = null;
+    /** Synchronization object for waiting for the UI initialization. */
+    private Object iInitWaitObject = new Object();
+    /** Synchronization object for waiting for the UI termination. */
+    private Object iExitWaitObject = new Object();
+    /**
+     * Synchronization object for synchronizing progress updates
+     * and confirmation dialogs. Used to guard disabling and
+     * getting iDisplayProgress variable.
+     */
+    private Object iProgressSyncObject = new Object();
+    /** Flag telling if UI main thread exists. */
+    private boolean iUiThreadExists = false;
+    /** Name of the application to be installed. */
+    private String iAppName = null;
+    /** Security icon.  */
+    private Image iSecurityIcon = null;
+    /** Flag telling if progress bar should be displayed. */
+    private boolean iDisplayProgress = false;
+    /** Flag telling if the first progress bar update has been traced. */
+    private boolean iProgressBarUpdateTraced = false;
+    /** Flag telling if MinimalUi should be used if UI creation fails. */
+    private boolean iMinimalUiEnabled = true;
+
+    /** Hashtable for storing the loaded icons. */
+    private static Hashtable iImageTable = null;
+
+    /** Default shell bounds. */
+    private Rectangle iDefaultShellBounds = null;
+    private Rectangle iDefaultShellClientBounds = null;
+
+    /** Bold font for views. */
+    private Font iBoldFont = null;
+
+    private ViewBase iActiveView = null;
+
+    /**
+     * Constructor.
+     */
+    public InstallerUiEswt()
+    {
+        super();
+    }
+
+    /**
+     * Initialise InstallerUi after construction.
+     */
+    public void init(int aMode, InstallerUiListener aListener)
+    {
+        super.init(aMode, aListener);
+        StartUpTrace.doTrace("InstallerUiEswt init");
+        // Create a hashtable for icons.
+        iImageTable = new Hashtable();
+        // Create a new thread to be the UI main thread.
+        iUiThreadExists = true;
+        new Thread(new Runnable()
+        {
+            public void run()
+            {
+                uiMain();
+            }
+        }, "InstallerUiMainThread").start();
+        // To wait InstallerUi to be ready before installer main thread
+        // continues, uncomment the following line.
+        //waitForUi();
+    }
+
+    /**
+     * This method is executed in UI main thread.
+     */
+    private void uiMain()
+    {
+        log("uiMain: thread started");
+        try
+        {
+            // Create the necessary views.
+            Display display = new Display();
+            StartUpTrace.doTrace("InstallerUiEswt display created");
+            display.setAppName(""); // Remove display title.
+            iParent = new Shell(display);
+            iDialog = new Shell(iParent, SHELL_STYLE);
+            iDefaultShellBounds = iDialog.internal_getDefaultBounds();
+            iDefaultShellClientBounds = iDialog.getClientArea();
+            iBoldFont = getBoldFont();
+            StartUpTrace.doTrace("InstallerUiEswt shell created");
+            iProgressView = new ProgressView(this, iDialog, getTitle());
+
+            iParent.addControlListener(new CListener(this));
+            log("InstallerUiEswt CListener added");
+
+            display.addListener(SWT.Dispose, new Listener()
+            {
+                public void handleEvent(Event aEvent)
+                {
+                    log("Dispose event for display");
+                    // Prevent UI from being automatically disposed.
+                    //aEvent.doit = false;
+                }
+            });
+
+            synchronized (iInitWaitObject)
+            {
+                // Notify that UI is now ready.
+                iInitWaitObject.notify();
+            }
+            StartUpTrace.doTrace("InstallerUiEswt ready");
+
+            // If there has been ui update requests, update the ui right
+            // away.
+            if (iMode == MODE_INSTALL && getOcspIndicator())
+            {
+                setOcspIndicator(getOcspIndicator());
+            }
+            else if (iMode == MODE_APP_CONVERSION &&
+                     iAppConversionTotal > 0)
+            {
+                updateAppConversionIndicator(
+                    iAppConversionCurrent, iAppConversionTotal);
+            }
+
+            // UI event loop must be executed in UI main thread,
+            // which is the thread where Display is created.
+            while (isUiReady())
+            {
+                if (!display.readAndDispatch())
+                {
+                    display.sleep();
+                }
+            }
+            if (iBoldFont != null && !iBoldFont.isDisposed())
+            {
+                iBoldFont.dispose();
+            }
+            display.dispose();
+            log("uiMain: display disposed");
+            synchronized (iExitWaitObject)
+            {
+                // Notify that UI main thread has been terminated.
+                iExitWaitObject.notify();
+            }
+        }
+        catch (Throwable t)
+        {
+            logError("Exception in uiMain", t);
+            // Release wait object in case someone is waiting for them.
+            synchronized (iInitWaitObject)
+            {
+                iUiThreadExists = false;
+                iInitWaitObject.notify();
+            }
+            synchronized (iExitWaitObject)
+            {
+                iExitWaitObject.notify();
+            }
+        }
+        iUiThreadExists = false;
+        log("uiMain: thread ended");
+    }
+
+    /**
+     * Cancels all confirmation views that are currently displayed.
+     */
+    public void cancelConfirmations()
+    {
+        super.cancelConfirmations();
+        if (iInstallConfirmationView != null)
+        {
+            iInstallConfirmationView.confirmCancel();
+        }
+        if (iUninstallConfirmationView != null)
+        {
+            iUninstallConfirmationView.confirmCancel();
+        }
+        if (iPermissionConfirmationView != null)
+        {
+            iPermissionConfirmationView.confirmCancel();
+        }
+        if (iUsernamePasswordView != null)
+        {
+            iUsernamePasswordView.confirmCancel();
+        }
+        if (iLaunchAppQueryView != null)
+        {
+            iLaunchAppQueryView.confirmCancel();
+        }
+        // Remove download progress bar if it visible.
+        if (iDlProgressView != null && !iDlProgressView.isDisposed())
+        {
+            iDlProgressView.dispose();
+            iDlProgressView = null;
+        }
+    }
+
+    /**
+     * Confirm installation. UI should display an installation
+     * confirmation dialog to the user. UI must update
+     * aInstallInfo basing on user selections.
+     * This method blocks until user has answered to the dialog.
+     *
+     * @param aInstallInfo installation information
+     * @return true if user has accepted installation, false otherwise
+     */
+    public boolean confirm(InstallInfo aInstallInfo)
+    {
+        super.confirm(aInstallInfo);
+
+        waitForUi();
+        boolean result = true;
+        if (!isUiReady())
+        {
+            result = false;
+            if (iMinimalUiEnabled)
+            {
+                result = MinimalUi.confirmStatic(aInstallInfo);
+                log("MinimalUi installation confirmation returns " + result);
+                return result;
+            }
+            else
+            {
+                // If UI is not ready by the time confirmation is requested,
+                // throw an exception.
+                throw new RuntimeException("JavaInstallerUi not ready");
+            }
+        }
+
+        if (result)
+        {
+            StartUpTrace.doTrace("InstallerUiEswt confirm");
+            if (iInstallConfirmationView == null)
+            {
+                final Display display = iParent.getDisplay();
+                final InstallerUiEswt self = this;
+                display.syncExec
+                (new Runnable()
+                {
+                    public void run()
+                    {
+                        iInstallConfirmationView =
+                            new InstallConfirmationView(self, iDialog);
+                    }
+                });
+            }
+            result = iInstallConfirmationView.confirm(aInstallInfo);
+            iInstallConfirmationView.dispose();
+            iInstallConfirmationView = null;
+        }
+        if (result)
+        {
+            iAppName = aInstallInfo.getName();
+            if (iUsernamePasswordView != null)
+            {
+                // UsernamePasswordView blocks prompting until
+                // app name is set so that username/password
+                // prompt will not be displayed if the user
+                // does not confirm installation.
+                iUsernamePasswordView.setAppName(iAppName);
+            }
+            else
+            {
+                // UsernamePasswordView is not being displayed,
+                // let's allow progress to be displayed.
+                iDisplayProgress = true;
+                // If download progress view is in use,
+                // display it to user only after confirmation.
+                if (iDlProgressView != null && !iDlProgressView.isVisible())
+                {
+                    iDlProgressView.setVisible(true);
+                }
+            }
+        }
+        else
+        {
+            // The install confirmation has been rejected,
+            // nothing to display anymore.
+            iParent.getDisplay().syncExec
+            (new Runnable()
+            {
+                public void run()
+                {
+                    iParent.dispose();
+                }
+            });
+        }
+        log("Installation confirmation returns " + result);
+        return result;
+    }
+
+    /**
+     * Confirm permissions. UI should display a permission
+     * confirmation dialog to the user.
+     * This method blocks until user has answered to the dialog.
+     *
+     * @param aPermissionInfo permission information
+     * @return true if user has accepted permissions, false otherwise
+     */
+    public boolean confirmPermissions(PermissionInfo aPermissionInfo)
+    {
+        super.confirmPermissions(aPermissionInfo);
+
+        waitForUi();
+        if (!isUiReady())
+        {
+            aPermissionInfo.setPermissionAllowed(false);
+            return true;
+        }
+
+        synchronized (iProgressSyncObject)
+        {
+            // Do not display progress bar during dialog.
+            iDisplayProgress = false;
+        }
+        if (iPermissionConfirmationView == null)
+        {
+            final Display display = iParent.getDisplay();
+            final InstallerUiEswt self = this;
+            display.syncExec
+            (new Runnable()
+            {
+                public void run()
+                {
+                    iPermissionConfirmationView =
+                        new PermissionConfirmationView(self, iDialog);
+                }
+            });
+        }
+        boolean result = iPermissionConfirmationView.confirm(
+                             iInstallInfo, aPermissionInfo);
+        iPermissionConfirmationView.dispose();
+        iPermissionConfirmationView = null;
+        iDisplayProgress = true;
+        iProgressView.setVisible(true);
+        log("Permission confirmation returns " + result +
+            ", user selection " + aPermissionInfo.isPermissionAllowed());
+        return result;
+    }
+
+    /**
+     * Confirm uninstallation. UI should display an uninstallation
+     * confirmation dialog to the user.
+     * This method blocks until user has answered to the dialog.
+     *
+     * @param aUninstallInfo uninstallation information
+     * @return true if user has accepted uninstallation, false otherwise
+     */
+    public boolean confirm(UninstallInfo aUninstallInfo)
+    {
+        super.confirm(aUninstallInfo);
+
+        waitForUi();
+        boolean result = true;
+        if (!isUiReady())
+        {
+            result = false;
+            if (iMinimalUiEnabled)
+            {
+                result = MinimalUi.confirmStatic(aUninstallInfo);
+                log("MinimalUi uninstallation confirmation returns " + result);
+                return result;
+            }
+            else
+            {
+                // If UI is not ready by the time confirmation is requested,
+                // throw an exception.
+                throw new RuntimeException("JavaInstallerUi not ready");
+            }
+        }
+        if (result)
+        {
+            StartUpTrace.doTrace("InstallerUiEswt confirm");
+            if (iUninstallConfirmationView == null)
+            {
+                final Display display = iParent.getDisplay();
+                final InstallerUiEswt self = this;
+                display.syncExec
+                (new Runnable()
+                {
+                    public void run()
+                    {
+                        iUninstallConfirmationView =
+                            new UninstallConfirmationView(self, iDialog);
+                    }
+                });
+            }
+            result = iUninstallConfirmationView.confirm(aUninstallInfo);
+            iUninstallConfirmationView.dispose();
+            iUninstallConfirmationView = null;
+        }
+        if (result)
+        {
+            iDisplayProgress = true;
+        }
+        else
+        {
+            // The uninstall confirmation has been rejected,
+            // nothing to display anymore.
+            iParent.getDisplay().syncExec
+            (new Runnable()
+            {
+                public void run()
+                {
+                    iParent.dispose();
+                }
+            });
+        }
+        log("Uninstallation confirmation returns " + result);
+        return result;
+    }
+
+    /**
+     * This method is used to notify UI that installation or
+     * uninstallation has started. Upon this call UI could
+     * for example display progress bar.
+     * This method must return quickly.
+     */
+    public void started()
+    {
+        super.started();
+    }
+
+    /**
+     * This method is used to notify UI that installation or
+     * uninstallation has progressed. Upon this call UI can
+     * update progress bar.
+     * This method must return quickly.
+     *
+     * @param aProgress progress value between 0 and 100
+     */
+    public void updateProgress(int aProgress)
+    {
+        super.updateProgress(aProgress);
+        if (!isUiReady())
+        {
+            return;
+        }
+        // UI is created asynchronously, so it might be that
+        // UI was not yet ready when started() was called.
+        // Ensure that iProgressView has been opened before
+        // updating it.
+        synchronized (iProgressSyncObject)
+        {
+            if (iDisplayProgress && !iProgressView.isVisible())
+            {
+                iProgressView.setVisible(true);
+            }
+            if (iDisplayProgress && !iProgressBarUpdateTraced)
+            {
+                StartUpTrace.doTrace(
+                    "InstallerUiEswt progress " + aProgress + " %");
+                iProgressBarUpdateTraced = true;
+            }
+        }
+        iProgressView.updateProgress(aProgress);
+
+    }
+
+    /**
+     * This method is used to notify UI that installation or
+     * uninstallation has ended. Upon this call UI can
+     * stop displaying progress bar.
+     * This method must return quickly.
+     */
+    public void ended()
+    {
+        super.ended();
+        if (!isUiReady())
+        {
+            return;
+        }
+        Display display = iParent.getDisplay();
+        display.syncExec
+        (new Runnable()
+        {
+            public void run()
+            {
+                iParent.dispose();
+            }
+        });
+        log("ended: parent disposed");
+        // Let's wait for ui to be properly terminated before returning.
+        synchronized (iExitWaitObject)
+        {
+            try
+            {
+                if (!display.isDisposed())
+                {
+                    iExitWaitObject.wait();
+                }
+            }
+            catch (InterruptedException ie)
+            {
+                // Ignore silently.
+            }
+        }
+        log("ended returns");
+    }
+
+    /**
+     * This method is used to notify UI that a download has started.
+     * Upon this call UI can for example prepare to display a download
+     * progress bar.
+     * NOTE: When this method is called, the totalSize in DownlodInfo
+     * is not yet initialized.
+     * This method must return quickly.
+     *
+     * @param aDownloadInfo information about download
+     */
+    public void started(DownloadInfo aDownloadInfo)
+    {
+        super.started(aDownloadInfo);
+        if (!isUiReady())
+        {
+            return;
+        }
+        // Ensure that download progress bar is displayed and
+        // updated to zero progress.
+        long oldCurrentSize = aDownloadInfo.getCurrentSize();
+        long oldTotalSize = aDownloadInfo.getTotalSize();
+        aDownloadInfo.setCurrentSize(0);
+        aDownloadInfo.setTotalSize(100);
+        updateProgress(aDownloadInfo);
+        aDownloadInfo.setCurrentSize(oldCurrentSize);
+        aDownloadInfo.setTotalSize(oldTotalSize);
+    }
+
+    /**
+     * This method is used to notify UI that a download
+     * has progressed. Upon this call UI can
+     * update download progress bar.
+     * This method must return quickly.
+     *
+     * @param aDownloadInfo information about download
+     */
+    public void updateProgress(DownloadInfo aDownloadInfo)
+    {
+        super.updateProgress(aDownloadInfo);
+        if (!isUiReady())
+        {
+            return;
+        }
+
+        if (iDlProgressView == null)
+        {
+            final boolean indeterminate =
+                (aDownloadInfo.getTotalSize() <= 0);
+            final InstallerUiEswt self = this;
+            iParent.getDisplay().syncExec(new Runnable()
+            {
+                public void run()
+                {
+                    iDlProgressView = new ProgressView(
+                        self, iDialog,
+                        InstallerUiTexts.get(InstallerUiTexts.DOWNLOADING),
+                        indeterminate);
+                }
+            });
+            iDlProgressView.addCancelCommand();
+        }
+
+        synchronized (iProgressSyncObject)
+        {
+            if (iDisplayProgress && !iDlProgressView.isVisible())
+            {
+                iDlProgressView.setVisible(true);
+            }
+        }
+        if (aDownloadInfo.getTotalSize() > 0)
+        {
+            int progress = (int)((aDownloadInfo.getCurrentSize()*100)/
+                                 aDownloadInfo.getTotalSize());
+            iDlProgressView.updateProgress(progress);
+        }
+    }
+
+    /**
+     * This method is used to notify UI that a download
+     * has ended. Upon this call UI can stop
+     * displaying download progress bar.
+     * This method must return quickly.
+     *
+     * @param aDownloadInfo information about download
+     */
+    public void ended(DownloadInfo aDownloadInfo)
+    {
+        super.ended(aDownloadInfo);
+        if (!isUiReady())
+        {
+            return;
+        }
+        if (iDlProgressView != null)
+        {
+            iDlProgressView.dispose();
+            iDlProgressView = null;
+        }
+        synchronized (iProgressSyncObject)
+        {
+            if (iDisplayProgress && !iProgressView.isVisible())
+            {
+                iProgressView.setVisible(true);
+            }
+        }
+    }
+
+    /**
+     * Set OCSP indicator on or off.
+     *
+     * @param aOn true when OCSP is started, false when OCSP is stopped
+     */
+    public void setOcspIndicator(boolean aOn)
+    {
+        super.setOcspIndicator(aOn);
+        waitForUi();
+        if (!isUiReady())
+        {
+            log("UI not ready, could not set OCSP indicator to " + aOn);
+            return;
+        }
+        if (aOn)
+        {
+            if (iOcspProgressView == null)
+            {
+                final InstallerUiEswt self = this;
+                iParent.getDisplay().syncExec
+                (new Runnable()
+                {
+                    public void run()
+                    {
+                        iOcspProgressView = new ProgressView(self, iDialog,
+                                                             InstallerUiTexts.get(InstallerUiTexts.OCSP_CHECK_PROGRESS),
+                                                             true);
+                    }
+                });
+                iOcspProgressView.addCancelCommand();
+            }
+            if (iOcspProgressView != null)
+            {
+                if (!iOcspProgressView.isVisible())
+                {
+                    iOcspProgressView.setVisible(true);
+                }
+            }
+        }
+        else
+        {
+            if (iOcspProgressView != null)
+            {
+                if (iOcspProgressView.isVisible())
+                {
+                    iOcspProgressView.setVisible(false);
+                }
+                iOcspProgressView.dispose();
+                iOcspProgressView = null;
+            }
+        }
+    }
+
+    /**
+     * Update application conversion indicator.
+     *
+     * @param aCurrent Index of the current application (1..total).
+     * @param aTotal Total number of applications. Setting aTotal to
+     * zero indicates that application conversion has been completed
+     * and ui can be disposed.
+     */
+    public void updateAppConversionIndicator(int aCurrent, int aTotal)
+    {
+        super.updateAppConversionIndicator(aCurrent, aTotal);
+        if (!isUiReady())
+        {
+            log("UI not ready, could not update app conversion indicator");
+            return;
+        }
+        int progress = 101;
+        if (aTotal > 0)
+        {
+            progress = (aCurrent*100)/aTotal;
+        }
+        if (progress <= 100)
+        {
+            iProgressView.setText(getTitle());
+            iProgressView.updateProgress(progress);
+            if (!iProgressView.isVisible())
+            {
+                iProgressView.removeCancelCommand();
+                iProgressView.setVisible(true);
+            }
+            iParent.getDisplay().syncExec(new Runnable()
+            {
+                public void run()
+                {
+                    // Ensure that conversion indicator
+                    // is drawn to the foreground.
+                    iParent.setMaximized(true);
+                }
+            });
+        }
+        else
+        {
+            iProgressView.updateProgress(100);
+            // Call ended() method which will dispose the ui.
+            ended();
+        }
+    }
+
+    /**
+     * Notify user that an error has occurred.
+     * This method must return quickly.
+     *
+     * @param aInstallerException exception indicating the error reason
+     */
+    public void error(InstallerExceptionBase aInstallerException)
+    {
+        super.error(aInstallerException);
+
+        waitForUi();
+        // InstallerUi does not have to be ready as long as
+        // RuntimeUi is used to display error messages.
+        //if (!isUiReady()) {
+        //    return;
+        //}
+
+        boolean identified = false;
+        //String tmpAppName = null;
+        if (iInstallInfo != null)
+        {
+            //tmpAppName = iInstallInfo.getName();
+            if (iInstallInfo.getCertificates() != null)
+            {
+                identified = true;
+            }
+        }
+        else if (iUninstallInfo != null)
+        {
+            //tmpAppName = iUninstallInfo.getName();
+            if (iUninstallInfo.getCertificates() != null)
+            {
+                identified = true;
+            }
+        }
+        String tmpTitle = "";
+        if (iMode == MODE_INSTALL)
+        {
+            tmpTitle = InstallerUiTexts.get(InstallerUiTexts.INSTALL_FAILED);
+        }
+        else if (iMode == MODE_UNINSTALL)
+        {
+            tmpTitle = InstallerUiTexts.get(InstallerUiTexts.UNINSTALL_FAILED);
+        }
+
+        // Ensure that no confirmations are being displayed.
+        cancelConfirmations();
+        // Hide progress view before displaying error message.
+        if (iProgressView != null)
+        {
+            iProgressView.setVisible(false);
+        }
+        // Use RuntimeUi to display error message.
+        RuntimeUi runtimeUi = RuntimeUiFactory.getRuntimeUi(identified);
+        runtimeUi.error(tmpTitle, aInstallerException);
+        runtimeUi.destroy();
+
+        /*
+        // Display error message using eSWT MessageBox.
+        final String appName = tmpAppName;
+        final String title = tmpTitle;
+        final String shortMsg = aInstallerException.getShortMessage();
+        final String detailedMsg = aInstallerException.getDetailedMessage();
+        // UI updates must be executed in UI thread.
+        iParent.getDisplay().syncExec
+            (new Runnable() {
+                    public void run() {
+                        if (detailedMsg == null || detailedMsg.length() == 0) {
+                            // No detailed msg, display only short msg.
+                            MessageBox messageBox = new MessageBox
+                                (iParent, SWT.ICON_ERROR | SWT.OK);
+                            messageBox.setText(title);
+                            messageBox.setMessage
+                                (getMessage(title, appName, shortMsg, false));
+                            messageBox.open();
+                        } else {
+                            // Display both short and detailed msgs.
+                            MessageBox messageBox = new MessageBox
+                                (iParent, SWT.ICON_ERROR | SWT.YES | SWT.NO);
+                            messageBox.setText(title);
+                            messageBox.setMessage
+                                (getMessage(title, appName, shortMsg, true));
+                            int answer = messageBox.open();
+                            if ((answer & SWT.YES) != 0) {
+                                // User wants to see details, display them.
+                                messageBox = new MessageBox
+                                    (iParent, SWT.ICON_ERROR | SWT.OK);
+                                messageBox.setText(title);
+                                messageBox.setMessage
+                                    (getMessage(title, appName, detailedMsg, false));
+                                messageBox.open();
+                            }
+                        }
+                    }
+                    private String getMessage(String aTitle, String aAppName,
+                                              String aMsg, boolean aDetailsQuery) {
+                        //String result = aTitle + "\n\n";
+                        String result = "";
+                        if (aAppName == null) {
+                            result += aMsg;
+                        } else {
+                            result += aAppName + "\n\n" + aMsg;
+                        }
+                        if (aDetailsQuery) {
+                            result += "\n\n";
+                            result += InstallerUiTexts.get
+                                (InstallerUiTexts.DETAILS_QUERY);
+                        }
+                        return result;
+                    }
+                });
+
+        */
+    }
+
+    /**
+     * Ask username and password for http authentication.
+     * This method blocks until user has answered to the dialog.
+     *
+     * @param aUrl url for which authentication is required
+     * @return Array of two strings, the first being username
+     * and second being password. If username and password
+     * cannot be obtained, this method returns null.
+     */
+    public String[] getUsernamePassword(String aUrl)
+    {
+        waitForUi();
+        if (!isUiReady())
+        {
+            return null;
+        }
+
+        synchronized (iProgressSyncObject)
+        {
+            // Do not display progress bar during dialog.
+            iDisplayProgress = false;
+        }
+        if (iUsernamePasswordView == null)
+        {
+            final Display display = iParent.getDisplay();
+            final InstallerUiEswt self = this;
+            display.syncExec(new Runnable()
+            {
+                public void run()
+                {
+                    iUsernamePasswordView =
+                        new UsernamePasswordView(self, iDialog);
+                }
+            });
+        }
+        iUsernamePasswordView.setAppName(iAppName);
+        final String[] usernamePassword =
+            iUsernamePasswordView.getUsernamePassword(aUrl);
+        iUsernamePasswordView.dispose();
+        iUsernamePasswordView = null;
+        iDisplayProgress = true;
+
+        if (usernamePassword != null)
+        {
+            log("Username: " + usernamePassword[0]);
+            // Do not write confidential information (password) to log.
+            //log("Password: " + usernamePassword[1]);
+        }
+        else
+        {
+            log("No username and password provided");
+        }
+        return usernamePassword;
+    }
+
+    /**
+     * Asks from the user if the installed application should be launched.
+     *
+     * @param aLaunchAppInfo Information about application that can
+     * be launched. Unless the user cancels the query,
+     * this data will be filled in with user's answer upon return.
+     * @return true if the user has chosen to launch the application,
+     * false if the user has canceled the query
+     */
+    public boolean launchAppQuery(LaunchAppInfo aLaunchAppInfo)
+    {
+        waitForUi();
+        if (!isUiReady() || iConfirmationsCanceled)
+        {
+            // Either UI is not yet ready, or user has cancelled
+            // installation, in both cases do nothing.
+            return false;
+        }
+
+        if (iLaunchAppQueryView == null)
+        {
+            final Display display = iParent.getDisplay();
+            final InstallerUiEswt self = this;
+            display.syncExec
+            (new Runnable()
+            {
+                public void run()
+                {
+                    iLaunchAppQueryView =
+                        new LaunchAppQueryView(self, iDialog);
+                }
+            });
+        }
+
+        boolean result = iLaunchAppQueryView.launchAppQuery(aLaunchAppInfo);
+        iParent.getDisplay().syncExec
+        (new Runnable()
+        {
+            public void run()
+            {
+                iParent.dispose();
+            }
+        });
+        iLaunchAppQueryView = null;
+        log("LaunchAppQuery returns " + result + " for " + aLaunchAppInfo);
+        return result;
+    }
+
+    /**
+     * Returns string title basing on mode of this InstallerUi.
+     */
+    protected String getTitle()
+    {
+        String result = null;
+        if (iMode == MODE_INSTALL)
+        {
+            result = InstallerUiTexts.get(InstallerUiTexts.INSTALLING);
+        }
+        else if (iMode == MODE_UNINSTALL)
+        {
+            result = InstallerUiTexts.get(InstallerUiTexts.UNINSTALLING);
+        }
+        else if (iMode == MODE_APP_CONVERSION)
+        {
+            result = InstallerUiTexts.get(
+                         InstallerUiTexts.APP_CONVERSION_PROGRESS,
+                         new Object[] { new Integer(iAppConversionCurrent),
+                                        new Integer(iAppConversionTotal)
+                                      });
+        }
+        return result;
+    }
+
+    /**
+     * Returns icon for indicating identified or unidentified application.
+     *
+     * @param aDisplay display to which the icon will be added
+     * @param aIdentified if true returns icon for identified application,
+     * otherwise returns icon for unidentified application
+     * @return icon for identified or unidentified application, or null
+     * if icon cannot be loaded
+     */
+    protected Image getSecurityIcon(Display aDisplay, boolean aIdentified)
+    {
+        if (iSecurityIcon != null)
+        {
+            return iSecurityIcon;
+        }
+        String iconFilename = ResourceUtil.UNTRUSTED_ICON_NAME;
+        if (aIdentified)
+        {
+            iconFilename = ResourceUtil.TRUSTED_ICON_NAME;
+        }
+        String resourceDir = ResourceUtil.getResourceDir(0);
+        for (int i = 1; iSecurityIcon == null && resourceDir != null; i++)
+        {
+            iSecurityIcon = loadImage(aDisplay, resourceDir + iconFilename, false);
+            resourceDir = ResourceUtil.getResourceDir(i);
+        }
+        return iSecurityIcon;
+    }
+
+    /**
+     * Loads image from specified InputStream. This method scales the image
+     * to optimum size.
+     *
+     * @param aDisplay display to which the icon will be added
+     * @param aInputStream InputStream where the image is loaded
+     * @param aImageName name of the image to be loaded
+     * @return image from InputStream or null if image cannot be loaded
+     */
+    protected static Image loadImage(
+        Display aDisplay, InputStream aInputStream, String aImageName)
+    {
+        return loadImage(aDisplay, aInputStream, aImageName, true);
+    }
+
+    /**
+     * Loads image from specified file.
+     *
+     * @param aDisplay display to which the icon will be added
+     * @param aImageFilename name of the image file to be loaded
+     * @param aScaleImage flag telling if the loaded image should be scaled
+     * @return image from file or null if image cannot be loaded
+     */
+    private static Image loadImage(
+        Display aDisplay, String aImageFilename, boolean aScaleImage)
+    {
+        Image result = null;
+        InputStream imageInputStream = null;
+        try
+        {
+            FileUtility imageFile = new FileUtility(aImageFilename);
+            if (imageFile.exists())
+            {
+                imageInputStream = imageFile.openInputStream();
+                result = loadImage(aDisplay, imageInputStream,
+                                   aImageFilename, aScaleImage);
+            }
+        }
+        catch (IOException ioe)
+        {
+            log("Can not get InputStream for " + aImageFilename + ": " + ioe);
+        }
+        finally
+        {
+            if (imageInputStream != null)
+            {
+                try
+                {
+                    imageInputStream.close();
+                    imageInputStream = null;
+                }
+                catch (IOException ioe)
+                {
+                    logError("Closing image InputStream failed", ioe);
+                }
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Loads image from specified InputStream.
+     *
+     * @param aDisplay display to which the icon will be added
+     * @param aInputStream InputStream where the image is loaded
+     * @param aImageName name of the image to be loaded
+     * @param aScaleImage flag telling if the loaded image should be scaled
+     * @return image from InputStream or null if image cannot be loaded
+     */
+    private static Image loadImage(
+        Display aDisplay, InputStream aInputStream,
+        String aImageName, boolean aScaleImage)
+    {
+        Image result = null;
+        if (aImageName != null)
+        {
+            result = (Image)iImageTable.get(aImageName);
+        }
+        if (result != null)
+        {
+            log("Using already loaded image " + aImageName);
+            return result;
+        }
+        try
+        {
+            long startTime = System.currentTimeMillis();
+            ImageData[] imageDatas = new ImageLoader().load(aInputStream);
+            ImageData imageData = imageDatas[0];
+            if (aScaleImage)
+            {
+                Point bestSize = getBestImageSize(
+                                     imageData.width, imageData.height);
+                if (bestSize.x != imageData.width ||
+                        bestSize.y != imageData.height)
+                {
+                    imageData = imageData.scaledTo(bestSize.x, bestSize.y);
+                    log("Image " + aImageName + " scaled from " +
+                        imageDatas[0].width + "x" + imageDatas[0].height +
+                        " to " + bestSize.x + "x" + bestSize.y);
+                }
+            }
+            result = new Image(aDisplay, imageData);
+            long endTime = System.currentTimeMillis();
+            log("Loaded image " + aImageName + " (load time " +
+                (endTime - startTime) + " ms)");
+            iImageTable.put(aImageName, result);
+        }
+        catch (Throwable t)
+        {
+            log("Can not load image " + aImageName + ": " + t);
+            //logError("Exception while loading image " + aImageName, t);
+        }
+        return result;
+    }
+
+    /**
+     * Determines the best image size for the image of given size.
+     */
+    private static Point getBestImageSize(int aWidth, int aHeight)
+    {
+        // Actually maximum image width and height should be obtained with
+        // org.eclipse.swt.internal.extension.DisplayExtension
+        // getBestImageWidth() and getBestImageHeight().
+        final int MAX_WIDTH = 50; // max width in pixels
+        final int MAX_HEIGHT = 50; // max height in pixels
+        Point result = new Point(aWidth, aHeight);
+        if (result.x > MAX_WIDTH || result.y > MAX_HEIGHT)
+        {
+            if (result.x >= MAX_WIDTH)
+            {
+                result.x = MAX_WIDTH;
+                result.y = MAX_WIDTH * aHeight / aWidth;
+            }
+            if (result.y >= MAX_HEIGHT)
+            {
+                result.x = MAX_HEIGHT * aWidth / aHeight;
+                result.y = MAX_HEIGHT;
+            }
+        }
+        return result;
+    }
+
+    /** Returns true if UI has been created and can be used. */
+    protected boolean isUiReady()
+    {
+        if (iProgressView == null)
+        {
+            log("ui not ready, iProgressView is null");
+            return false;
+        }
+        if (iProgressView.isDisposed())
+        {
+            log("ui not ready, iProgressView is disposed");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Blocks until UI has became ready.
+     */
+    private void waitForUi()
+    {
+        synchronized (iInitWaitObject)
+        {
+            try
+            {
+                if (iUiThreadExists && iProgressView == null)
+                {
+                    iInitWaitObject.wait();
+                }
+            }
+            catch (InterruptedException ie)
+            {
+                // Ignore silently.
+            }
+        }
+    }
+
+    /**
+     * Called when screen orientation changes.
+     */
+    private void screenOrientationChanged()
+    {
+        iDefaultShellBounds = iDialog.internal_getDefaultBounds();
+
+        if (iActiveView != null)
+        {
+            iActiveView.screenOrientationChanged();
+        }
+    }
+
+    private static class CListener implements ControlListener
+    {
+        private InstallerUiEswt iInstallerUi = null;
+
+        CListener(InstallerUiEswt aInstallerUi)
+        {
+            iInstallerUi = aInstallerUi;
+        }
+
+        public void controlMoved(ControlEvent aEvent)
+        {
+        }
+
+        public void controlResized(ControlEvent aEvent)
+        {
+            iInstallerUi.screenOrientationChanged();
+        }
+    }
+
+    Rectangle defaultShellBounds()
+    {
+        return iDefaultShellBounds;
+    }
+
+    Rectangle defaultShellClientBounds()
+    {
+        return iDefaultShellClientBounds;
+    }
+
+    /** Get bold font for the current view. */
+    Font getBoldFont()
+    {
+        if (iBoldFont == null)
+        {
+            FontData[] fontDatas = iParent.getFont().getFontData();
+            FontData[] boldFontDatas = new FontData[fontDatas.length];
+            for (int i = 0; i < boldFontDatas.length; i++)
+            {
+                boldFontDatas[i] = new FontData
+                (fontDatas[i].getName(), fontDatas[i].getHeight()+1, SWT.BOLD);
+            }
+            iBoldFont = new Font(iParent.getDisplay(), boldFontDatas);
+        }
+        return iBoldFont;
+    }
+
+    void setActiveView(ViewBase aView)
+    {
+        if (iActiveView != null && iActiveView != aView &&
+                !iActiveView.isDisposed())
+        {
+            iActiveView.setVisible(false);
+        }
+        iActiveView = aView;
+    }
+
+    ViewBase getActiveView()
+    {
+        return iActiveView;
+    }
+}