diff options
Diffstat (limited to 'src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardArea.java')
-rw-r--r-- | src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardArea.java | 1897 |
1 files changed, 1897 insertions, 0 deletions
diff --git a/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardArea.java b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardArea.java new file mode 100644 index 0000000..e9e17ae --- /dev/null +++ b/src/plugins/packaging.ui/src/com/motorola/studio/android/packaging/ui/export/PackageExportWizardArea.java @@ -0,0 +1,1897 @@ +/* + * 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 + + " " + eclipseProject.getName())); //$NON-NLS-1$ + } + 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; + } + + } +}
\ No newline at end of file |