com.motorola.studio.android.packaging.ui.export.PackageExportWizardArea.java Source code

Java tutorial

Introduction

Here is the source code for com.motorola.studio.android.packaging.ui.export.PackageExportWizardArea.java

Source

/*
 * Copyright (C) 2012 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.motorola.studio.android.packaging.ui.export;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.security.UnrecoverableKeyException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarFile;

import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.DecorationOverlayIcon;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.IWizardPage;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.plugin.AbstractUIPlugin;

import com.motorola.studio.android.AndroidPlugin;
import com.motorola.studio.android.adt.AdtUtils;
import com.motorola.studio.android.adt.SdkUtils;
import com.motorola.studio.android.common.log.StudioLogger;
import com.motorola.studio.android.common.utilities.EclipseUtils;
import com.motorola.studio.android.packaging.ui.PackagingUIPlugin;
import com.motorola.studio.android.packaging.ui.i18n.Messages;
import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
import com.motorolamobility.studio.android.certmanager.core.KeyStoreManager;
import com.motorolamobility.studio.android.certmanager.exception.InvalidPasswordException;
import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
import com.motorolamobility.studio.android.certmanager.job.CreateKeyJob;
import com.motorolamobility.studio.android.certmanager.packaging.PackageFile;
import com.motorolamobility.studio.android.certmanager.packaging.sign.PackageFileSigner;
import com.motorolamobility.studio.android.certmanager.packaging.sign.SignException;
import com.motorolamobility.studio.android.certmanager.ui.model.IKeyStore;
import com.motorolamobility.studio.android.certmanager.ui.model.ITreeNode;
import com.motorolamobility.studio.android.certmanager.ui.wizards.CreateKeyWizard;
import com.motorolamobility.studio.android.certmanager.ui.wizards.CreateKeystoreWizard;
import com.motorolamobility.studio.android.certmanager.ui.wizards.SelectExistentKeystoreWizard;

/**
 * 
 * This Class is an area implementation for Package Export Wizards It contains
 * all UI and finish logics
 * 
 */
@SuppressWarnings("restriction")
public class PackageExportWizardArea {
    /**
     * It holds the selection
     */
    private final IStructuredSelection selection;

    /**
     * The destination Folder selected by the user
     */
    private Text destinationText;

    private Button selectAllButton;

    private Button deselectAllButton;

    private Button packageDestinationBrowseButton;

    private Button defaultDestination;

    private Button signCheckBox;

    private final Composite parentComposite;

    private final boolean signingEnabled;

    private final HashMap<IProject, Integer> projectSeverity;

    /**
     * The tree which contains all descriptor files
     */
    private Tree tree;

    private String message;

    private int severity;

    private boolean treeSelectionChanged;

    private final Image icon_ok, icon_nok;

    private Combo keystores;

    private Combo keysCombo;

    private Group signingGroup;

    private Button buttonAddKey;

    // Used in parallel with keystore combo. Use it with the selection index.
    private static ArrayList<IKeyStore> keystoreList = new ArrayList<IKeyStore>();

    //maps keystore->password
    private final Map<IKeyStore, String> keystorePasswords = new HashMap<IKeyStore, String>();

    private IKeyStore previousSelectedKeystore;

    private String previousSelectedKey;

    private Button buttonExisting;

    private Button buttonAddNew;

    /**
     * Creates a new Export Area.
     * 
     * @param selection
     *            The current Selection
     */
    public PackageExportWizardArea(IStructuredSelection selection, Composite parent, boolean signingEnabled) {
        this.selection = normalizeSelection(selection);
        this.parentComposite = parent;
        this.signingEnabled = signingEnabled;
        this.projectSeverity = new HashMap<IProject, Integer>();
        validateProjects();
        ImageDescriptor adtProjectImageDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin(
                "com.android.ide.eclipse.adt", //$NON-NLS-1$
                "icons/android_project.png"); //$NON-NLS-1$
        ImageDescriptor errorImageDescriptor = JavaPluginImages.DESC_OVR_ERROR;
        ImageDescriptor projectImage = PlatformUI.getWorkbench().getSharedImages()
                .getImageDescriptor(org.eclipse.ui.ide.IDE.SharedImages.IMG_OBJ_PROJECT);
        // (IDecoration.TOP_LEFT, IDecoration.TOP_RIGHT,
        // IDecoration.BOTTOM_LEFT, IDecoration.BOTTOM_RIGHT and
        // IDecoration.UNDERLAY).
        ImageDescriptor[] overlays = new ImageDescriptor[5];
        overlays[1] = adtProjectImageDescriptor;
        icon_ok = new DecorationOverlayIcon(projectImage.createImage(), overlays).createImage();
        overlays[2] = errorImageDescriptor;
        icon_nok = new DecorationOverlayIcon(projectImage.createImage(), overlays).createImage();
        createControl(parent);
        this.treeSelectionChanged = false;
    }

    /**
     * It opens the Directory Selection Dialog that allows the user enter the
     * destination folder
     * 
     * @param originalPath
     *            The Folder to show first
     * 
     * @return The entire path of the user choice
     */
    private String directoryDialog(String originalPath) {
        DirectoryDialog directoryDialog;

        directoryDialog = new DirectoryDialog(parentComposite.getShell());

        File directory = new File(originalPath);

        if (directory.exists()) {
            directoryDialog.setFilterPath(directory.getPath());
        }

        String returnedPath = directoryDialog.open();

        return returnedPath;
    }

    /**
     * This method normalize the selection only to contain projects and
     * descriptors
     * 
     * @return
     * @throws CoreException
     */
    @SuppressWarnings("unchecked")
    private IStructuredSelection normalizeSelection(IStructuredSelection selection) {
        ArrayList<Object> normalized = new ArrayList<Object>();
        Iterator<Object> iterator = selection.iterator();
        while (iterator.hasNext()) {
            Object item = iterator.next();
            IResource resource = null;
            if (item instanceof IResource) {
                resource = (IResource) item;
            } else if (item instanceof IAdaptable) {
                try {
                    resource = (IResource) ((IAdaptable) item).getAdapter(IResource.class);
                } catch (Exception e) {
                    StudioLogger.warn("Error retrieving projects.");
                }
            }
            if (resource != null) {
                IProject project = resource.getProject();
                if (!normalized.contains(project)) {
                    normalized.add(project);
                }
            }

        }
        return new StructuredSelection(normalized);
    }

    /**
     * This method is responsible to add all the existent descriptor files of
     * the current workspace in the tree shown in the wizard.
     */
    private void populateTree() {
        tree.removeAll();
        IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
        try {
            for (IProject project : projects) {
                if (project.isOpen() && (project.getNature(AndroidPlugin.Android_Nature) != null)
                        && !SdkUtils.isLibraryProject(project)) {
                    TreeItem item = new TreeItem(tree, SWT.NONE);
                    item.setData(project);
                    item.setText(project.getName());
                    item.setImage(projectSeverity.get(project) == IMessageProvider.ERROR ? icon_nok : icon_ok);
                    if (selection.toList().contains(project)) {
                        item.setChecked(true);
                    }
                }
            }
        } catch (CoreException e) {
            StudioLogger.error(PackageExportWizardArea.class, "Error populating tree: " + e.getMessage()); //$NON-NLS-1$
        }

    }

    /**
     * Create the destination selection group
     * 
     * @param mainComposite
     *            : the parent composite
     */
    private void createDestinationGroup(Composite mainComposite) {

        // create destination group
        Group destinationGroup = new Group(mainComposite, SWT.SHADOW_ETCHED_OUT);
        GridLayout layout = new GridLayout(3, false);
        destinationGroup.setLayout(layout);
        GridData defaultDestGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1);
        destinationGroup.setLayoutData(defaultDestGridData);
        destinationGroup.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_DESTINATION_LABEL);

        // Default Destination
        this.defaultDestination = new Button(destinationGroup, SWT.CHECK);
        defaultDestGridData = new GridData(SWT.FILL, SWT.FILL, true, false, 3, 1);
        this.defaultDestination.setLayoutData(defaultDestGridData);
        this.defaultDestination.addListener(SWT.Selection, new DefaultDestinationListener());
        this.defaultDestination.setText(Messages.PACKAGE_EXPORT_WIZARD_USE_DEFAULT_DESTINATION);
        this.defaultDestination.setSelection(true);

        // Package Destination Label
        Label packageDestinationLabel = new Label(destinationGroup, SWT.NONE);
        packageDestinationLabel.setText(Messages.PACKAGE_EXPORT_WIZARD_PACKAGE_DESTINATION_LABEL);
        GridData folderGridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
        packageDestinationLabel.setLayoutData(folderGridData);

        // Package Destination
        this.destinationText = new Text(destinationGroup, SWT.SINGLE | SWT.BORDER);
        folderGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
        this.destinationText.setLayoutData(folderGridData);
        this.destinationText.setEnabled(!this.defaultDestination.getSelection());
        this.destinationText.addListener(SWT.Modify, new DestinationTextListener());

        // Browse Button
        this.packageDestinationBrowseButton = new Button(destinationGroup, SWT.PUSH);
        folderGridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
        this.packageDestinationBrowseButton.setLayoutData(folderGridData);
        this.packageDestinationBrowseButton.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_BROWSE_BUTTON_LABEL);
        this.packageDestinationBrowseButton.setEnabled(!this.defaultDestination.getSelection());
        this.packageDestinationBrowseButton.addListener(SWT.Selection, new PackageDestinationButtonListener());

    }

    public final String[] getKeys(IKeyStore iKeyStore) throws KeyStoreManagerException, InvalidPasswordException {
        List<String> aliases = new ArrayList<String>();

        aliases = iKeyStore.getAliases(getKeyStorePassword(iKeyStore));

        return aliases.toArray(new String[0]);
    }

    public String openNewKeyWizard(IKeyStore keyStore, IJobChangeListener createKeyJobListener) {

        CreateKeyWizard wizard = new CreateKeyWizard(keyStore, getKeyStorePassword(getSelectedKeyStore()),
                createKeyJobListener);

        WizardDialog dialog = new WizardDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
                wizard);
        dialog.open();

        return wizard.getAlias();
    }

    private void selectKeystore(IKeyStore newKeystore) {
        if (newKeystore != null) {
            int index = keystoreList.indexOf(newKeystore);

            if (keystores.getItemCount() > index) {
                keystores.select(index);

                loadKeys(newKeystore);

                setEnablement(true);
            }
        } else {
            keystores.deselectAll();
        }

        previousSelectedKeystore = getSelectedKeyStore();
    }

    private void selectKeystoreWithoutLoadingKeys(IKeyStore newKeystore) {
        if (newKeystore != null) {
            int index = keystoreList.indexOf(newKeystore);
            keysCombo.removeAll();

            if (keystores.getItemCount() > index) {
                keystores.select(index);

                setEnablement(true);
            }
        } else {
            keystores.deselectAll();
        }

        previousSelectedKeystore = getSelectedKeyStore();
    }

    private boolean loadKeys(IKeyStore newKeystore) {
        boolean successfullyLoaded = true;
        keysCombo.removeAll();

        try {
            String[] keys = getKeys(newKeystore);
            if (keys != null) {
                keysCombo.setItems(keys);
            }
        } catch (Exception e) {
            successfullyLoaded = false;
            StudioLogger.info(PackageExportWizardArea.class,
                    NLS.bind("Could not load keys for keystore: {0}", newKeystore.getFile() //$NON-NLS-1$
                            .getAbsolutePath()));
        }

        selectKey(0);

        return successfullyLoaded;
    }

    private void selectKey(String key) {

        String[] keys = keysCombo.getItems();

        int index = -1;
        int i = 0;

        for (String k : keys) {

            if (k.equals(key)) {
                index = i;
                break;
            }
            i++;
        }

        selectKey(index);
    }

    private void selectKey(int index) {
        if ((index >= 0) && (index < keysCombo.getItemCount())) {
            keysCombo.select(index);
            setEnablement(true);
        } else {
            keysCombo.deselectAll();
        }

    }

    private void restorePreviousSelections() {
        selectKeystore(previousSelectedKeystore);
        selectKey(previousSelectedKey);
    }

    private String getSelectedKey() {
        String result = null;
        if (keysCombo.getSelectionIndex() >= 0) {
            result = keysCombo.getText();
        }
        return result;
    }

    protected IKeyStore getSelectedKeyStore() {
        IKeyStore result = null;
        if (keystores.getSelectionIndex() >= 0) {
            result = keystoreList.get(keystores.getSelectionIndex());
        }
        return result;
    }

    /**
     * Create the sign selection group
     * 
     * @param mainComposite: the parent composite
     */
    private void createSignGroup(Composite mainComposite) {
        // Create signing group
        signingGroup = new Group(mainComposite, SWT.SHADOW_ETCHED_OUT);
        GridLayout layout = new GridLayout(4, false);
        GridData layoutData = new GridData(SWT.FILL, SWT.CENTER, true, false, 4, 1);
        signingGroup.setLayout(layout);
        signingGroup.setLayoutData(layoutData);
        signingGroup.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGNING_TAB_TEXT);

        // Sign button/Check box
        this.signCheckBox = new Button(signingGroup, SWT.CHECK);
        layoutData = new GridData(SWT.LEFT, SWT.CENTER, true, false, 4, 1);
        this.signCheckBox.setLayoutData(layoutData);
        this.signCheckBox.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_CHECK_LABEL);
        this.signCheckBox.addListener(SWT.Selection, new SignButtonListener());
        this.signCheckBox.setSelection(true);

        //--------------

        // Keystore label
        Label keystoreLabel = new Label(signingGroup, SWT.NONE);
        keystoreLabel.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_KEYSTORE_LABEL);
        GridData gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 1, 1);
        keystoreLabel.setLayoutData(gridData);

        // Keystore combo
        this.keystores = new Combo(signingGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
        gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1);
        gridData.widthHint = 250;
        this.keystores.setLayoutData(gridData);

        //populate mapped keystores from view
        populateKeystoresFromView();

        keystores.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                IKeyStore selectedKeystore = keystoreList.get(keystores.getSelectionIndex());
                boolean keysLoaded = loadKeys(selectedKeystore);

                if (!keysLoaded) {
                    ITreeNode keystoreNode = (ITreeNode) selectedKeystore;

                    if (keystoreNode.getNodeStatus().getCode() == IKeyStore.WRONG_KEYSTORE_TYPE_ERROR_CODE) {
                        EclipseUtils.showInformationDialog(
                                Messages.PackageExportWizardArea_WrongKeystoreTypeDialogTitle,
                                NLS.bind(Messages.PackageExportWizardArea_WrongKeystoreTypeDialogMessage,
                                        keystoreNode.getName()));
                    }

                    restorePreviousSelections();
                } else {
                    previousSelectedKeystore = getSelectedKeyStore();
                    previousSelectedKey = getSelectedKey();
                }
                setEnablement(true);
            }
        });

        if (keystores.getItemCount() <= 0) {
            signCheckBox.setSelection(false);
        }

        // Add keystore buttons       
        buttonExisting = new Button(signingGroup, SWT.NONE);
        buttonAddNew = new Button(signingGroup, SWT.NONE);

        gridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
        buttonExisting.setLayoutData(gridData);
        gridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
        buttonAddNew.setLayoutData(gridData);

        buttonExisting.setText(Messages.PackageExportWizardArea_MenuItem_UseExistent);
        buttonExisting.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                openSelectKeystoreWizard();
            }
        });

        buttonAddNew.setText(Messages.PackageExportWizardArea_MenuItem_AddNew);
        buttonAddNew.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                CreateKeystoreWizard createKeystoreWizard = new CreateKeystoreWizard(
                        new CreateKeystoreJobListener());

                WizardDialog dialog = new WizardDialog(parentComposite.getShell(), createKeystoreWizard);

                //open the wizard to create keystores
                dialog.create();
                if (dialog.open() == Window.OK) {
                    //user really created keystore
                    String keystorePassword = createKeystoreWizard.getCreatedKeystorePassword();
                    addKeystore(createKeystoreWizard.getCreatedKeystoreNode(), true, keystorePassword);
                    //required for case when just keystore is created, but no key is created                    
                    //DO NOT call selectKeystore here because it has loadKeys, that may conflict with createKey (if a key is created), for this case the CreateKeystoreJobListener will solve the problem. 
                    selectKeystoreWithoutLoadingKeys(createKeystoreWizard.getCreatedKeystoreNode());
                }
            }
        });

        // Key label
        Label keyLabel = new Label(signingGroup, SWT.NONE);
        keyLabel.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_KEY_LABEL);
        gridData = new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 1, 1);
        keyLabel.setLayoutData(gridData);

        // Key Combo
        this.keysCombo = new Combo(signingGroup, SWT.DROP_DOWN | SWT.READ_ONLY);
        gridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1);
        this.keysCombo.setLayoutData(gridData);

        if (keysCombo.getItemCount() <= 0) {
            signCheckBox.setSelection(false);
        }

        keysCombo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                super.widgetSelected(e);

                previousSelectedKey = getSelectedKey();

                parentComposite.notifyListeners(SWT.Modify, new Event());
            }
        });

        // Add keystore button
        buttonAddKey = new Button(signingGroup, SWT.PUSH);
        buttonAddKey.setText(Messages.PackageExportWizardArea_AddKeyButton_Text);
        gridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1);
        buttonAddKey.setLayoutData(gridData);

        buttonAddKey.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                IKeyStore keyStore = null;
                if (keystores.getSelectionIndex() >= 0) {
                    keyStore = PackageExportWizardArea.keystoreList.get(keystores.getSelectionIndex());
                }

                if (keyStore != null) {
                    openNewKeyWizard(keyStore, new CreateKeyJobListener());
                }

                setEnablement(true);

            }
        });

        setEnablement(false);

    }

    /**
     * This class is required because when creating a new key during export, the threads are not synchronized (createKey and loadKeys).
     * Otherwise, wizard page could not finish accordingly.
     */
    private class CreateKeyJobListener extends JobChangeAdapter {

        /* (non-Javadoc)
         * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
         */
        @Override
        public void done(final IJobChangeEvent event) {
            PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
                @Override
                public void run() {

                    IKeyStore keyStore = ((CreateKeyJob) event.getJob()).getKeyStore();
                    String key = ((CreateKeyJob) event.getJob()).getCreatedKeyAlias();

                    loadKeys(keyStore);
                    selectKey(key);
                }
            });
        }
    }

    /**
     * This class is required because when creating a new key and new keystore during export, the threads are not synchronized (createKey and loadKeys).
     * Otherwise, wizard page could not finish accordingly.   
     */
    private class CreateKeystoreJobListener extends JobChangeAdapter {

        /* (non-Javadoc)
         * @see org.eclipse.core.runtime.jobs.JobChangeAdapter#done(org.eclipse.core.runtime.jobs.IJobChangeEvent)
         */
        @Override
        public void done(final IJobChangeEvent event) {
            PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable() {
                @Override
                public void run() {
                    IKeyStore keyStore = ((CreateKeyJob) event.getJob()).getKeyStore();
                    String key = ((CreateKeyJob) event.getJob()).getCreatedKeyAlias();

                    loadKeys(keyStore);
                    selectKeystore(keyStore);
                    selectKey(key);
                }
            });
        }
    }

    /**
     * Inserts keystores mapped into keystore combo
     */
    protected void populateKeystoresFromView() {
        try {
            //when reopening wizard, we need to clear the list  
            if (!keystoreList.isEmpty()) {
                keystoreList.clear();
                keystores.removeAll();
            }
            if ((KeyStoreManager.getInstance() != null) && (KeyStoreManager.getInstance().getKeyStores() != null)) {
                List<IKeyStore> keyStores = KeyStoreManager.getInstance().getKeyStores();
                if (keyStores != null) {
                    for (IKeyStore keyStore : keyStores) {
                        insertKeystoreIntoCombo(keyStore);
                    }
                }
            }
        } catch (KeyStoreManagerException e) {
            StudioLogger.error(PackageExportWizardArea.class, "Error retrieving keystore list", //$NON-NLS-1$
                    e);
        }
    }

    protected void insertKeystoreIntoCombo(IKeyStore iKeyStore) {
        File ksFile = iKeyStore.getFile();
        keystores.add(ksFile.getName() + " - ( " + ksFile.getPath() + " )"); //$NON-NLS-1$ //$NON-NLS-2$
        keystoreList.add(iKeyStore);
    }

    /**
     * Adds keystore to keystores combo box and model from GUI 
     * @param iKeyStore
     * @param canSavePassword true if create/select and need to import into view, false if selecting and does NOT need to import into the view
     * @param password to retrieve keys  
     */
    protected void addKeystore(IKeyStore iKeyStore, boolean canSavePassword, String password) {
        keystorePasswords.put(iKeyStore, password);
        insertKeystoreIntoCombo(iKeyStore);
    }

    /**
     * Update the Default Destination The behavior is: if only one project is
     * selected, then default destination text is set to the folder of this
     * project if have more than one project selected or none than default
     * destination text is set to workspace root
     */
    private void updateDefaultDestination() {
        ArrayList<IProject> selectedProjects = getSelectedProjects();
        if (this.defaultDestination.getSelection()) {
            if ((selectedProjects != null) && (selectedProjects.size() == 1)) {
                this.destinationText.setText(selectedProjects.get(0).getLocation().toOSString());
            } else {
                this.destinationText.setText(ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString());
            }
        }
    }

    /**
     * Get selected projects from the tree
     * 
     * @return
     */
    private ArrayList<IProject> getSelectedProjects() {
        ArrayList<IProject> projects = new ArrayList<IProject>();
        for (TreeItem item : tree.getItems()) {
            if (item.getChecked()) {
                projects.add((IProject) item.getData());
            }
        }
        return projects;
    }

    /**
     * Get selected items from the tree
     * 
     * @return
     */
    private ArrayList<TreeItem> getSelectedItems() {
        ArrayList<TreeItem> items = new ArrayList<TreeItem>();
        for (TreeItem item : tree.getItems()) {
            if (item.getChecked()) {
                items.add(item);
            }
        }
        return items;
    }

    /**
     * This method creates the entire structure of the wizard page. Also, it
     * stores the user input.
     * 
     * @param parent
     *            Composite for all the elements.
     */
    public void createControl(Composite parent) {
        Composite mainComposite = new Composite(parent, SWT.NONE);
        GridLayout gridLayout = new GridLayout(3, false);
        GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 3, 1);
        mainComposite.setLayout(gridLayout);
        mainComposite.setLayoutData(gridData);

        // Tree structure
        this.tree = new Tree(mainComposite, SWT.CHECK | SWT.MULTI | SWT.V_SCROLL | SWT.BORDER);
        gridData = new GridData(SWT.FILL, SWT.FILL, true, true, 2, 2);
        gridData.heightHint = 150;
        this.tree.setLayoutData(gridData);
        this.tree.addListener(SWT.Selection, new TreeListener());

        // Add all the descriptor files to the tree
        populateTree();

        // Select All Button
        this.selectAllButton = new Button(mainComposite, SWT.PUSH);
        gridData = new GridData(SWT.FILL, SWT.UP, false, false, 1, 1);
        this.selectAllButton.setLayoutData(gridData);
        this.selectAllButton.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_SELECT_ALL_BUTTON);
        this.selectAllButton.addListener(SWT.Selection, new TreeSelectionButtonListener(true));

        // Deselect All button
        this.deselectAllButton = new Button(mainComposite, SWT.PUSH);
        gridData = new GridData(SWT.FILL, SWT.UP, false, false, 1, 1);
        this.deselectAllButton.setLayoutData(gridData);
        this.deselectAllButton.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_DESELECT_ALL_BUTTON);
        this.deselectAllButton.addListener(SWT.Selection, new TreeSelectionButtonListener(false));

        createDestinationGroup(mainComposite);

        if (this.signingEnabled) {
            createSignGroup(mainComposite);
        }

        String path = ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString();
        Object prj = this.selection.getFirstElement();
        if ((prj != null) && (this.selection.size() == 1)) {
            if (prj instanceof IProject) {
                String realPath = ((IProject) prj).getLocation().toOSString();
                this.destinationText.setText(realPath);
            } else if (prj instanceof IResource) {
                String realPath = path + ((IResource) prj).getProject().getFullPath().toOSString();
                this.destinationText.setText(realPath);
            }
        } else {
            this.destinationText.setText(path);
        }
        /**
         * Force the focus to parent. This action make help ok
         */
        if (!parent.isFocusControl()) {
            parent.forceFocus();
        }
    }

    /**
     * Get all projects severities to avoid user selects erroneous projects
     */
    private void validateProjects() {
        try {
            for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects()) {
                if (project.isOpen()) {
                    int sev = project.findMaxProblemSeverity(null, true, IResource.DEPTH_INFINITE);
                    int projectSev;
                    switch (sev) {
                    case IMarker.SEVERITY_ERROR:
                        projectSev = IMessageProvider.ERROR;
                        break;
                    case IMarker.SEVERITY_INFO:
                        projectSev = IMessageProvider.INFORMATION;
                        break;
                    case IMarker.SEVERITY_WARNING:
                        projectSev = IMessageProvider.WARNING;
                        break;
                    default:
                        projectSev = IMessageProvider.NONE;
                        break;
                    }
                    projectSeverity.put(project, new Integer(projectSev));
                }
            }
        } catch (CoreException e) {
            StudioLogger.error(PackageExportWizardArea.class, "Impossible to get project severity"); //$NON-NLS-1$
        }
    }

    /**
     * Can finish used in {@link IWizardPage} This method validate the page and
     * change the severity/message.
     * 
     * @return true if can finish this wizard, false otherwise
     */
    public boolean canFinish() {
        String messageAux = null;
        int severity_aux = IMessageProvider.NONE;

        /*
         * Check is has selected items
         */
        if (!hasItemChecked()) {
            messageAux = Messages.SELECTOR_MESSAGE_NO_SELECTION;
            if (treeSelectionChanged) {
                severity_aux = IMessageProvider.ERROR;
            } else {
                severity_aux = IMessageProvider.INFORMATION;
            }
        }

        // validate if some selected project has errors
        if (messageAux == null) {
            Iterator<IProject> iterator = getSelectedProjects().iterator();
            while (iterator.hasNext() && (severity_aux != IMessageProvider.ERROR)) {
                severity_aux = projectSeverity.get(iterator.next());
            }
            if (severity_aux == IMessageProvider.ERROR) {
                messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_PROJECTS_WITH_ERRORS_SELECTED;

            }
        }

        /*
         * Check if the selected location is valid, even if non existent.
         */
        IPath path = new Path(this.destinationText.getText());

        if (!this.defaultDestination.getSelection() && (messageAux == null)) {
            // Test if path is blank, to warn user instead of show an error
            // message
            if (this.destinationText.getText().equals("")) //$NON-NLS-1$
            {
                messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_INVALID;
                severity_aux = IMessageProvider.INFORMATION;
            }

            /*
             * Do Win32 Validation
             */
            if ((messageAux == null) && Platform.getOS().equalsIgnoreCase(Platform.OS_WIN32)) {
                // test path size
                if (path.toString().length() > 255) {
                    messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_PATH_TOO_LONG;
                    severity_aux = IMessageProvider.ERROR;
                }
                String device = path.getDevice();
                File deviceFile = null;
                if (device != null) {
                    deviceFile = new File(path.getDevice());
                }

                if ((device != null) && !deviceFile.exists()) {
                    messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_INVALID_DEVICE + " [" + device //$NON-NLS-1$
                            + "]"; //$NON-NLS-1$
                    severity_aux = IMessageProvider.ERROR;
                }

            }
            // test if path is absolute
            if (messageAux == null) {
                if (!path.isAbsolute()) {
                    messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_INVALID;
                    severity_aux = IMessageProvider.ERROR;
                }
            }

            if (messageAux == null) {
                for (String folderName : path.segments()) {
                    if (!ResourcesPlugin.getWorkspace().validateName(folderName, IResource.FOLDER).isOK()) {
                        messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_INVALID;
                        severity_aux = IMessageProvider.ERROR;

                    }
                }
            }

            if ((messageAux == null) && path.toFile().exists() && !path.toFile().isDirectory()) {
                messageAux = Messages.SELECTOR_MESSAGE_LOCATION_ERROR_NOT_DIRECTORY;
                severity_aux = IMessageProvider.ERROR;
            }
        }

        /*
         * Check if there are available certificates and if selection isn't null
         */
        if (messageAux == null) {

            if (this.signingEnabled && (this.signCheckBox != null) && this.signCheckBox.getSelection()
                    && !((this.keystores != null) && (this.keystores.getItemCount() > 0))) {
                messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_NO_KEYSTORE_AVAILABLE;
                severity_aux = IMessageProvider.ERROR;
            }

            else if (this.signCheckBox.getSelection() && !((this.keysCombo != null)
                    && (this.keysCombo.getItemCount() > 0) && (this.keysCombo.getSelectionIndex() >= 0)
                    && (this.keysCombo.getItem(this.keysCombo.getSelectionIndex()) != null)
                    && !this.keysCombo.getItem(this.keysCombo.getSelectionIndex()).equals(""))) //$NON-NLS-1$ 
            {
                messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_SIGN_NO_KEYSTORE_OR_KEY_SELECTED;
                severity_aux = IMessageProvider.ERROR;
            }

        }

        if (messageAux == null) {
            if (!this.signCheckBox.getSelection()) {
                messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_UNSIGNEDPACKAGE_WARNING;
                severity_aux = IMessageProvider.WARNING;
            }
        }

        /*
         * Setting message
         */
        if (messageAux == null) {
            messageAux = Messages.PACKAGE_EXPORT_WIZARD_AREA_DESCRIPTION;
            severity_aux = IMessageProvider.NONE;
        }
        this.message = messageAux;
        this.severity = severity_aux;

        boolean result;
        switch (severity_aux) {
        case IMessageProvider.ERROR:
            // ERROR. can't finish wizard
            result = false;
            break;

        case IMessageProvider.WARNING:
            // WARNING. ok to finish the wizard
            result = true;
            break;

        case IMessageProvider.INFORMATION:
            // INFORMATION. Path is empty, so it's NOT OK to finish the wizard
            result = false;
            break;

        default:
            // by default, canFinish returns true
            result = true;
            break;

        }

        return result;
    }

    /**
     * Check if we have at least one item checked
     * 
     * @param items
     * @return true if some of tree item is checked, false otherwise
     */
    private boolean hasItemChecked() {
        boolean checked = false;
        TreeItem[] items = tree.getItems();
        int i = 0;
        while (!checked && (i < items.length)) {
            if (items[i].getChecked()) {
                checked = true;
            }
            i++;
        }
        return checked;
    }

    /**
     * Check if the destination folder is valid during finish action If the
     * package don't exist, ask to create
     * 
     * @return true if the destination is valid
     * @throws CoreException
     *             when errors with directory creation occurs
     */
    private boolean checkDestination() throws CoreException {
        boolean destinationOK = true;
        if (!defaultDestination.getSelection()) {
            File destination = new File(destinationText.getText());
            if (!destination.exists()) {
                destinationOK = createDestinationFolderDialog();
                if (destinationOK) {
                    if (!destination.mkdirs()) {
                        throw new CoreException(new Status(IStatus.ERROR, PackagingUIPlugin.PLUGIN_ID,
                                Messages.PACKAGE_EXPORT_WIZARD_AREA_ERROR_DESTINATION_CHECK));
                    }
                }
            }
        } else {
            for (TreeItem item : getSelectedItems()) {
                IProject project = (IProject) item.getData();
                IPath dist = project.getLocation().append(CertificateManagerActivator.PACKAGE_PROJECT_DESTINATION);
                File file = dist.toFile();
                if (file.exists() && !file.isDirectory()) {
                    if (!file.delete()) {
                        throw new CoreException(new Status(IStatus.ERROR, PackagingUIPlugin.PLUGIN_ID,
                                Messages.PACKAGE_EXPORT_WIZARD_AREA_ERROR_DESTINATION_CHECK));
                    }
                }
                if (!file.exists()) {
                    if (!file.mkdir()) {
                        throw new CoreException(new Status(IStatus.ERROR, PackagingUIPlugin.PLUGIN_ID,
                                Messages.PACKAGE_EXPORT_WIZARD_AREA_ERROR_DESTINATION_CHECK));
                    }
                }
                project.refreshLocal(IResource.DEPTH_ONE, new NullProgressMonitor());
            }
        }
        return destinationOK;
    }

    /**
     * Create a destination folder, asking user's permission
     * 
     * @return
     */
    private boolean createDestinationFolderDialog() {
        MessageBox box = new MessageBox(parentComposite.getShell(), SWT.ICON_QUESTION | SWT.YES | SWT.NO);
        box.setMessage(Messages.PACKAGE_EXPORT_WIZARD_AREA_CREATE_DIRECTORIES_BOX_MESSAGE);
        box.setText(Messages.PACKAGE_EXPORT_WIZARD_AREA_CREATE_DIRECTORIES_BOX_TITLE);
        return box.open() == SWT.YES;
    }

    /**
     * do the finish: Create the package for each selected descriptor
     */
    public boolean performFinish() {
        final boolean[] finished = { false };
        boolean destOK = false;
        final MultiStatus status = new MultiStatus(PackagingUIPlugin.PLUGIN_ID, IStatus.OK, "", null); //$NON-NLS-1$
        ProgressMonitorDialog monitorDialog = null;
        String DESCRIPTION_TO_LOG = StudioLogger.DESCRIPTION_DEFAULT;

        try {
            destOK = checkDestination();
        } catch (CoreException e) {
            status.add(e.getStatus());
        }

        if (destOK) {
            monitorDialog = new ProgressMonitorDialog(parentComposite.getShell());
            try {
                monitorDialog.run(false, false, new IRunnableWithProgress() {

                    @Override
                    public void run(IProgressMonitor aMonitor)
                            throws InvocationTargetException, InterruptedException {
                        int finishSize = getSelectedItems().size() * PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER;
                        SubMonitor monitor = SubMonitor.convert(aMonitor);
                        monitor.beginTask(Messages.PACKAGE_EXPORT_WIZARD_AREA_FINISH_ACTION_LABEL, finishSize);
                        IPath exportDestinationFolder = new Path(destinationText.getText());
                        IPath exportDestinationFile = null;

                        for (TreeItem item : getSelectedItems()) {
                            // get the eclipse project as a java project to get
                            // the project
                            // destination
                            IProject eclipseProject = (IProject) item.getData();
                            try {
                                monitor.worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER / 4);

                                JavaProject javaProject = new JavaProject();
                                javaProject.setProject(eclipseProject);

                                // find all packages built by Android builder
                                Map<String, String> apkConfigurations = SdkUtils
                                        .getAPKConfigurationsForProject(eclipseProject);

                                Set<String> apkConfNames = new HashSet<String>();
                                if (apkConfigurations != null) {
                                    apkConfNames.addAll(apkConfigurations.keySet());
                                }
                                apkConfNames.add(""); // the default package //$NON-NLS-1$

                                SubMonitor submonitor = monitor
                                        .newChild(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER / 4);

                                submonitor.beginTask(Messages.PACKAGE_EXPORT_WIZARD_AREA_EXPORTING_ACTION_LABEL,
                                        3 * PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER * apkConfNames.size());

                                for (String apkConfName : apkConfNames) {

                                    String apkName = eclipseProject.getName() + (apkConfName.isEmpty() ? apkConfName
                                            : "-" //$NON-NLS-1$ //$NON-NLS-2$
                                                    + apkConfName);
                                    if (defaultDestination.getSelection()) {
                                        exportDestinationFolder = eclipseProject.getLocation()
                                                .append(CertificateManagerActivator.PACKAGE_PROJECT_DESTINATION);
                                    }
                                    exportDestinationFile = exportDestinationFolder.append(apkName)
                                            .addFileExtension(CertificateManagerActivator.PACKAGE_EXTENSION);
                                    File file = exportDestinationFile.toFile();
                                    submonitor.worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER);

                                    //always export unsigned package
                                    AdtUtils.exportUnsignedReleaseApk(javaProject.getProject(), file, submonitor);

                                    submonitor.worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER);

                                    if (signCheckBox.getSelection()) {
                                        //sign the package if required
                                        IStatus signStatus = signPackage(eclipseProject, file);
                                        status.add(signStatus);
                                    }

                                    //zipalign the file and we are done exporting the package
                                    PackageFile.zipAlign(file);

                                    submonitor.worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER);
                                }
                                submonitor.done();
                            } catch (CoreException e) {
                                StudioLogger.error(PackageExportWizardArea.class,
                                        "Error while building project or getting project output folder" //$NON-NLS-1$
                                                + eclipseProject.getName(),
                                        e);
                                status.add(new Status(IStatus.ERROR, PackagingUIPlugin.PLUGIN_ID,
                                        Messages.PACKAGE_EXPORT_WIZARD_AREA_ERROR_PROJECT_BUILD + " " //$NON-NLS-1$
                                                + eclipseProject.getName()));
                            } finally {
                                try {
                                    eclipseProject.refreshLocal(IResource.DEPTH_INFINITE,
                                            monitor.newChild(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER / 4));
                                } catch (CoreException e) {
                                    // do nothing
                                }
                            }
                            monitor.worked(PackagingUIPlugin.PROGRESS_MONITOR_MULTIPLIER / 4);
                        }
                        finished[0] = true;

                    }
                });
            } catch (Exception e) {
                StudioLogger.warn("Error finishing package export.");
            }
        }

        if (!status.isOK()) {
            status.getMessage();
            DESCRIPTION_TO_LOG = Messages.PACKAGE_EXPORT_WIZARD_AREA_READONLY_TITLE;
            ErrorDialog.openError(parentComposite.getShell(), Messages.PACKAGE_EXPORT_WIZARD_AREA_READONLY_TITLE,
                    Messages.PACKAGE_EXPORT_WIZARD_AREA_READONLY_MESSAGE, status);
        }

        // Saving usage data
        try {
            StudioLogger.collectUsageData(StudioLogger.WHAT_APP_MANAGEMENT_PACKAGE,
                    StudioLogger.KIND_APP_MANAGEMENT, DESCRIPTION_TO_LOG, PackagingUIPlugin.PLUGIN_ID,
                    PackagingUIPlugin.getDefault().getBundle().getVersion().toString());
        } catch (Throwable e) {
            // Do nothing, but error on the log should never prevent app from
            // working
        }

        return finished[0];
    }

    /**
     * 
     * @return key entry password
     */
    public String getKeyEntryPassword() {
        String keyEntryPassword = new String();
        try {
            keyEntryPassword = getSelectedKeyStore().getPasswordProvider()
                    .getPassword(this.keysCombo.getItem(this.keysCombo.getSelectionIndex()), true);
        } catch (KeyStoreManagerException e) {
            StudioLogger.error(this.getClass(), "Error retrieving keys entry password", e); //$NON-NLS-1$
        }
        return keyEntryPassword;
    }

    /**
     * @param eclipseProject The project being exported.
     * @param exportedPackage The package to be signed
     * @throws InvalidPasswordException 
     * @throws KeyStoreManagerException 
     */
    private IStatus signPackage(IProject eclipseProject, File exportedPackage) {
        IStatus status = Status.OK_STATUS;

        String keyAlias = keysCombo.getItem(keysCombo.getSelectionIndex());
        String keystorePassword = getKeyStorePassword(getSelectedKeyStore());
        String keyPassword = getKeyEntryPassword();

        JarFile jar = null;
        try {
            PackageFile pack = null;
            boolean keepTrying;
            if (keyPassword != null) {
                keepTrying = true;
            } else {
                keepTrying = false;

            }
            while (keepTrying) {
                try {
                    // Open package and remove signature
                    jar = new JarFile(exportedPackage);
                    pack = new PackageFile(jar);
                    pack.removeMetaEntryFiles();

                    // Sign the new package
                    PackageFileSigner.signPackage(pack, getSelectedKeyStore().getEntry(keyAlias, keystorePassword),
                            keyPassword, PackageFileSigner.MOTODEV_STUDIO);
                    keepTrying = false;
                } catch (UnrecoverableKeyException sE) {
                    try {
                        keyPassword = getSelectedKeyStore().getPasswordProvider().getPassword(keyAlias, true,
                                false);
                    } catch (KeyStoreManagerException e) {
                        status = new Status(Status.ERROR, CertificateManagerActivator.PLUGIN_ID, e.getMessage());
                        StudioLogger.error(PackageExportWizardArea.this.getClass(),
                                "Could not retrieve key password on export: " + e.getMessage()); //$NON-NLS-1$
                    }
                    if (keyPassword == null) {
                        keepTrying = false;
                        status = Status.CANCEL_STATUS;
                    } else {
                        keepTrying = true;
                    }
                } catch (InvalidPasswordException e) {
                    // Should never happen as the entry alias is only available if the keystore password
                    // was typed correctly.
                    // Unless the user changed the keystore password outside the tool while exporting the package.
                    status = new Status(Status.ERROR, CertificateManagerActivator.PLUGIN_ID, e.getMessage());
                } catch (KeyStoreManagerException e) {
                    // Should never happen as the entry alias is only available if the keystore password
                    // was typed correctly.
                    // Unless the user changed the keystore password outside the tool while exporting the package.
                    status = new Status(Status.ERROR, CertificateManagerActivator.PLUGIN_ID, e.getMessage());
                }
            }

            if (status.isOK()) {
                FileOutputStream fileToWrite = null;
                try {
                    // Write the new package file
                    fileToWrite = new FileOutputStream(exportedPackage);
                    pack.write(fileToWrite);
                } finally {

                    fileToWrite.close();
                }
            } else {
                EclipseUtils.showErrorDialog("Package Signing", "Could not sign the package.");
            }
        } catch (IOException e) {
            StudioLogger.error(PackageExportWizardArea.this.getClass(),
                    "Could not sign the package: " + e.getMessage()); //$NON-NLS-1$

            status = new Status(IStatus.ERROR, PackagingUIPlugin.PLUGIN_ID,
                    Messages.PackageExportWizardArea_ErrorWritingSignedPackageFile + " " //$NON-NLS-1$
                            + eclipseProject.getName());
        } catch (SignException e) {
            StudioLogger.error(PackageExportWizardArea.this.getClass(),
                    "Could not sign the package: " + e.getMessage()); //$NON-NLS-1$

            status = new Status(IStatus.ERROR, PackagingUIPlugin.PLUGIN_ID,
                    Messages.PackageExportWizardArea_ErrorSigningPackage + " " + eclipseProject.getName()); //$NON-NLS-1$
        } finally {
            try {
                jar.close();
            } catch (IOException e) {
                StudioLogger.error(PackageExportWizardArea.this.getClass(),
                        "Could not sign the package: " + e.getMessage()); //$NON-NLS-1$
            }
        }
        return status;
    }

    /**
     * Get the area message
     * 
     * @return the message for the wizard
     */
    public String getMessage() {
        return this.message;
    }

    /**
     * Get the area error severity
     * 
     * @return
     */
    public int getSeverity() {
        return this.severity;
    }

    /**
     * "Browser..." button for package destination.
     * 
     */
    private class PackageDestinationButtonListener implements Listener {

        /*
         * (non-Javadoc)
         * 
         * @see
         * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
         * .Event)
         */
        @Override
        public void handleEvent(Event event) {
            String path = destinationText.getText();

            if (path.equals("")) //$NON-NLS-1$
            {
                path = ResourcesPlugin.getWorkspace().getRoot().getLocation().toOSString().subSequence(0, 2)
                        .toString();
            }

            String executablePath = directoryDialog(path);

            if ((executablePath != null) && !executablePath.equals("")) //$NON-NLS-1$
            {
                destinationText.setText(executablePath);
            }
            parentComposite.notifyListeners(SWT.Modify, new Event());
        }
    }

    /**
     * Update the default destination status
     * 
     */
    private class DestinationTextListener implements Listener {

        @Override
        public void handleEvent(Event event) {
            if (!defaultDestination.getSelection()) {
                parentComposite.notifyListeners(SWT.Modify, new Event());
            }
        }
    }

    /**
     * A Listener for the Tree.
     * 
     */
    private class TreeListener implements Listener {
        @Override
        public void handleEvent(Event event) {
            updateDefaultDestination();
            parentComposite.notifyListeners(SWT.Modify, new Event());
            treeSelectionChanged = true;
        }
    }

    /**
     * Enable Destination Text.
     * 
     */
    private class DefaultDestinationListener implements Listener {

        /*
         * (non-Javadoc)
         * 
         * @see
         * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
         * .Event)
         */
        @Override
        public void handleEvent(Event event) {
            Button defaultDestination = (Button) event.widget;
            destinationText.setEnabled(!defaultDestination.getSelection());
            packageDestinationBrowseButton.setEnabled(!defaultDestination.getSelection());
            if (defaultDestination.getSelection()) {
                updateDefaultDestination();
            } else {
                destinationText.setText(""); //$NON-NLS-1$
            }
            destinationText.notifyListeners(SWT.Modify, new Event());
            parentComposite.notifyListeners(SWT.Modify, new Event());

        }

    }

    /**
     * 
     * @param notify Notifies listener that validates if Finish can be enabled
     */
    private void setEnablement(boolean notify) {

        boolean signEnabled = signCheckBox.getSelection();

        keysCombo.setEnabled(signEnabled);
        keystores.setEnabled(signEnabled);
        buttonAddKey.setEnabled(signEnabled);
        //toolbar.setEnabled(signEnabled);
        buttonExisting.setEnabled(signEnabled);
        buttonAddNew.setEnabled(signEnabled);

        if (signEnabled) {

            if (keystores.getSelectionIndex() < 0) {
                //no keystore selected, clear keys combo
                buttonAddKey.setEnabled(false);
                keysCombo.setEnabled(false);
                keysCombo.removeAll();
            }

            if (keystores.getItemCount() <= 0) {
                keysCombo.setEnabled(false);
                keystores.setEnabled(false);
                buttonAddKey.setEnabled(false);
                //toolbar.setEnabled(true);
                buttonExisting.setEnabled(signEnabled);
                buttonAddNew.setEnabled(signEnabled);
            }
        }

        if (notify) {
            parentComposite.notifyListeners(SWT.Modify, new Event());
        }

    }

    protected void openSelectKeystoreWizard() {
        SelectExistentKeystoreWizard selectExistentKeystoreWizard = new SelectExistentKeystoreWizard();

        WizardDialog dialog = new WizardDialog(parentComposite.getShell(), selectExistentKeystoreWizard);

        //open the wizard to select keystores                            
        dialog.create();
        if (dialog.open() == Window.OK) {
            //keystore was really selected : adding keystore to the list
            IKeyStore iKeyStore = selectExistentKeystoreWizard.getSelectedKeystore();
            boolean canSavePassword = selectExistentKeystoreWizard.canSavePassword();
            String password = selectExistentKeystoreWizard.getPassword();
            addKeystore(iKeyStore, canSavePassword, password);
            selectKeystore(iKeyStore);
        }
    }

    /**
     * This method must be used to retrieve the passwod of any keystore in the context of this wizard.
     * It is purpose is to cache the passwords of the keystores, mainly of the ones that do not have its password saved,
     * so the password will be asked only once for each keystore, during the lifetime of this wizard.
     * */
    protected String getKeyStorePassword(IKeyStore keystore) {
        String password = keystorePasswords.get(keystore);

        //check if password is already cached
        if (password == null) {
            password = keystore.getKeyStorePassword(true);
        }
        if (password != null) {
            keystorePasswords.put(keystore, password);
        }

        return password;
    }

    /**
     * Sign button listener to enable/disable combos, buttons and finish/next
     * 
     */
    private class SignButtonListener implements Listener {
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
         * .Event)
         */
        @Override
        public void handleEvent(Event event) {
            if (event.widget == signCheckBox) {
                setEnablement(true);
            }

        }

    }

    /**
     * This class will handle the (Des)Select All buttons
     * 
     */
    private class TreeSelectionButtonListener implements Listener {
        private final boolean checked;

        /**
         * Create a new instance of the listener with the desired check state
         * 
         * @param selectItems
         */
        public TreeSelectionButtonListener(boolean selectItems) {
            this.checked = selectItems;
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets
         * .Event)
         */
        @Override
        public void handleEvent(Event event) {
            for (TreeItem item : tree.getItems()) {
                item.setChecked(checked);
            }
            updateDefaultDestination();
            parentComposite.notifyListeners(SWT.Modify, new Event());
            treeSelectionChanged = true;
        }

    }
}