Merge change 21617 into donut

* changes:
  ADT Export wizard now calls ZipAlign
This commit is contained in:
Android (Google) Code Review
2009-08-17 17:13:01 -07:00
5 changed files with 262 additions and 116 deletions

View File

@@ -454,12 +454,17 @@ public class AdtPlugin extends AbstractUIPlugin {
/** Returns the adb path relative to the sdk folder */ /** Returns the adb path relative to the sdk folder */
public static String getOsRelativeAdb() { public static String getOsRelativeAdb() {
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_ADB; return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ADB;
}
/** Returns the zipalign path relative to the sdk folder */
public static String getOsRelativeZipAlign() {
return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_ZIPALIGN;
} }
/** Returns the emulator path relative to the sdk folder */ /** Returns the emulator path relative to the sdk folder */
public static String getOsRelativeEmulator() { public static String getOsRelativeEmulator() {
return SdkConstants.OS_SDK_TOOLS_FOLDER + AndroidConstants.FN_EMULATOR; return SdkConstants.OS_SDK_TOOLS_FOLDER + SdkConstants.FN_EMULATOR;
} }
/** Returns the absolute adb path */ /** Returns the absolute adb path */
@@ -467,6 +472,11 @@ public class AdtPlugin extends AbstractUIPlugin {
return getOsSdkFolder() + getOsRelativeAdb(); return getOsSdkFolder() + getOsRelativeAdb();
} }
/** Returns the absolute zipalign path */
public static String getOsAbsoluteZipAlign() {
return getOsSdkFolder() + getOsRelativeZipAlign();
}
/** Returns the absolute traceview path */ /** Returns the absolute traceview path */
public static String getOsAbsoluteTraceview() { public static String getOsAbsoluteTraceview() {
return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER + return getOsSdkFolder() + SdkConstants.OS_SDK_TOOLS_FOLDER +

View File

@@ -106,10 +106,6 @@ public class AndroidConstants {
public final static Pattern PATTERN_RESOURCES_S_AP_ = public final static Pattern PATTERN_RESOURCES_S_AP_ =
Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$ Pattern.compile("resources-.*\\.ap_", Pattern.CASE_INSENSITIVE); //$NON-NLS-1$
public final static String FN_ADB = SdkConstants.FN_ADB;
public final static String FN_EMULATOR = SdkConstants.FN_EMULATOR;
public final static String FN_TRACEVIEW = public final static String FN_TRACEVIEW =
(SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ? (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) ?
"traceview.exe" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$ "traceview.exe" : "traceview"; //$NON-NLS-1$ //$NON-NLS-2$

View File

@@ -41,17 +41,17 @@ import java.util.zip.ZipOutputStream;
* Export helper for project. * Export helper for project.
*/ */
public final class ExportHelper { public final class ExportHelper {
private static IExportCallback sCallback; private static IExportCallback sCallback;
public interface IExportCallback { public interface IExportCallback {
void startExportWizard(IProject project); void startExportWizard(IProject project);
} }
public static void setCallback(IExportCallback callback) { public static void setCallback(IExportCallback callback) {
sCallback = callback; sCallback = callback;
} }
public static void startExportWizard(IProject project) { public static void startExportWizard(IProject project) {
if (sCallback != null) { if (sCallback != null) {
sCallback.startExportWizard(project); sCallback.startExportWizard(project);
@@ -69,30 +69,30 @@ public final class ExportHelper {
IFolder outputFolder = BaseProjectHelper.getOutputFolder(project); IFolder outputFolder = BaseProjectHelper.getOutputFolder(project);
if (outputFolder != null) { if (outputFolder != null) {
IPath binLocation = outputFolder.getLocation(); IPath binLocation = outputFolder.getLocation();
// make the full path to the package // make the full path to the package
String fileName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE; String fileName = project.getName() + AndroidConstants.DOT_ANDROID_PACKAGE;
File file = new File(binLocation.toOSString() + File.separator + fileName); File file = new File(binLocation.toOSString() + File.separator + fileName);
if (file.exists() == false || file.isFile() == false) { if (file.exists() == false || file.isFile() == false) {
MessageDialog.openInformation(Display.getCurrent().getActiveShell(), MessageDialog.openError(Display.getCurrent().getActiveShell(),
"Android IDE Plug-in", "Android IDE Plug-in",
String.format("Failed to export %1$s: %2$s doesn't exist!", String.format("Failed to export %1$s: %2$s doesn't exist!",
project.getName(), file.getPath())); project.getName(), file.getPath()));
return; return;
} }
// ok now pop up the file save window // ok now pop up the file save window
FileDialog fileDialog = new FileDialog(shell, SWT.SAVE); FileDialog fileDialog = new FileDialog(shell, SWT.SAVE);
fileDialog.setText("Export Project"); fileDialog.setText("Export Project");
fileDialog.setFileName(fileName); fileDialog.setFileName(fileName);
String saveLocation = fileDialog.open(); String saveLocation = fileDialog.open();
if (saveLocation != null) { if (saveLocation != null) {
// get the stream from the original file // get the stream from the original file
ZipInputStream zis = null; ZipInputStream zis = null;
ZipOutputStream zos = null; ZipOutputStream zos = null;
FileInputStream input = null; FileInputStream input = null;
@@ -116,8 +116,8 @@ public final class ExportHelper {
// pass // pass
} }
} }
MessageDialog.openInformation(shell, "Android IDE Plug-in", MessageDialog.openError(shell, "Android IDE Plug-in",
String.format("Failed to export %1$s: %2$s doesn't exist!", String.format("Failed to export %1$s: %2$s doesn't exist!",
project.getName(), file.getPath())); project.getName(), file.getPath()));
return; return;
@@ -125,20 +125,20 @@ public final class ExportHelper {
try { try {
ZipEntry entry; ZipEntry entry;
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
while ((entry = zis.getNextEntry()) != null) { while ((entry = zis.getNextEntry()) != null) {
String name = entry.getName(); String name = entry.getName();
// do not take directories or anything inside the META-INF folder since // do not take directories or anything inside the META-INF folder since
// we want to strip the signature. // we want to strip the signature.
if (entry.isDirectory() || name.startsWith("META-INF/")) { //$NON-NL1$ if (entry.isDirectory() || name.startsWith("META-INF/")) { //$NON-NL1$
continue; continue;
} }
ZipEntry newEntry; ZipEntry newEntry;
// Preserve the STORED method of the input entry. // Preserve the STORED method of the input entry.
if (entry.getMethod() == JarEntry.STORED) { if (entry.getMethod() == JarEntry.STORED) {
newEntry = new JarEntry(entry); newEntry = new JarEntry(entry);
@@ -146,12 +146,12 @@ public final class ExportHelper {
// Create a new entry so that the compressed len is recomputed. // Create a new entry so that the compressed len is recomputed.
newEntry = new JarEntry(name); newEntry = new JarEntry(name);
} }
// add the entry to the jar archive // add the entry to the jar archive
zos.putNextEntry(newEntry); zos.putNextEntry(newEntry);
// read the content of the entry from the input stream, and write it into the archive. // read the content of the entry from the input stream, and write it into the archive.
int count; int count;
while ((count = zis.read(buffer)) != -1) { while ((count = zis.read(buffer)) != -1) {
zos.write(buffer, 0, count); zos.write(buffer, 0, count);
} }
@@ -159,11 +159,10 @@ public final class ExportHelper {
// close the entry for this file // close the entry for this file
zos.closeEntry(); zos.closeEntry();
zis.closeEntry(); zis.closeEntry();
} }
} catch (IOException e) { } catch (IOException e) {
MessageDialog.openInformation(shell, "Android IDE Plug-in", MessageDialog.openError(shell, "Android IDE Plug-in",
String.format("Failed to export %1$s: %2$s", String.format("Failed to export %1$s: %2$s",
project.getName(), e.getMessage())); project.getName(), e.getMessage()));
} finally { } finally {
@@ -178,9 +177,19 @@ public final class ExportHelper {
// pass // pass
} }
} }
// this is unsigned export. Let's tell the developers to run zip align
MessageDialog.openWarning(shell, "Android IDE Plug-in", String.format(
"An unsigned package of the application was saved at\n%1$s\n\n" +
"Before publishing the application you will need to:\n" +
"- Sign the application with your release key,\n" +
"- run zipalign on the signed package. ZipAlign is located in <SDK>/tools/\n\n" +
"Aligning applications allows Android to use application resources\n" +
"more efficiently.", saveLocation));
} }
} else { } else {
MessageDialog.openInformation(shell, "Android IDE Plug-in", MessageDialog.openError(shell, "Android IDE Plug-in",
String.format("Failed to export %1$s: Could not get project output location", String.format("Failed to export %1$s: Could not get project output location",
project.getName())); project.getName()));
} }

View File

@@ -16,19 +16,18 @@
package com.android.ide.eclipse.adt.internal.wizards.export; package com.android.ide.eclipse.adt.internal.wizards.export;
import com.android.ide.eclipse.adt.AdtConstants;
import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper;
import com.android.ide.eclipse.adt.internal.project.ProjectHelper; import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
import com.android.jarutils.KeystoreHelper; import com.android.jarutils.KeystoreHelper;
import com.android.jarutils.SignedJarBuilder; import com.android.jarutils.SignedJarBuilder;
import com.android.jarutils.DebugKeyProvider.IKeyGenOutput; import com.android.jarutils.DebugKeyProvider.IKeyGenOutput;
import com.android.jarutils.DebugKeyProvider.KeytoolException;
import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.operation.IRunnableWithProgress;
@@ -43,15 +42,16 @@ import org.eclipse.ui.IExportWizard;
import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.PlatformUI; import org.eclipse.ui.PlatformUI;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.security.GeneralSecurityException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.KeyStore.PrivateKeyEntry; import java.security.KeyStore.PrivateKeyEntry;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
@@ -62,23 +62,23 @@ import java.util.Set;
import java.util.Map.Entry; import java.util.Map.Entry;
/** /**
* Export wizard to export an apk signed with a release key/certificate. * Export wizard to export an apk signed with a release key/certificate.
*/ */
public final class ExportWizard extends Wizard implements IExportWizard { public final class ExportWizard extends Wizard implements IExportWizard {
private static final String PROJECT_LOGO_LARGE = "icons/android_large.png"; //$NON-NLS-1$ private static final String PROJECT_LOGO_LARGE = "icons/android_large.png"; //$NON-NLS-1$
private static final String PAGE_PROJECT_CHECK = "Page_ProjectCheck"; //$NON-NLS-1$ private static final String PAGE_PROJECT_CHECK = "Page_ProjectCheck"; //$NON-NLS-1$
private static final String PAGE_KEYSTORE_SELECTION = "Page_KeystoreSelection"; //$NON-NLS-1$ private static final String PAGE_KEYSTORE_SELECTION = "Page_KeystoreSelection"; //$NON-NLS-1$
private static final String PAGE_KEY_CREATION = "Page_KeyCreation"; //$NON-NLS-1$ private static final String PAGE_KEY_CREATION = "Page_KeyCreation"; //$NON-NLS-1$
private static final String PAGE_KEY_SELECTION = "Page_KeySelection"; //$NON-NLS-1$ private static final String PAGE_KEY_SELECTION = "Page_KeySelection"; //$NON-NLS-1$
private static final String PAGE_KEY_CHECK = "Page_KeyCheck"; //$NON-NLS-1$ private static final String PAGE_KEY_CHECK = "Page_KeyCheck"; //$NON-NLS-1$
static final String PROPERTY_KEYSTORE = "keystore"; //$NON-NLS-1$ static final String PROPERTY_KEYSTORE = "keystore"; //$NON-NLS-1$
static final String PROPERTY_ALIAS = "alias"; //$NON-NLS-1$ static final String PROPERTY_ALIAS = "alias"; //$NON-NLS-1$
static final String PROPERTY_DESTINATION = "destination"; //$NON-NLS-1$ static final String PROPERTY_DESTINATION = "destination"; //$NON-NLS-1$
static final String PROPERTY_FILENAME = "baseFilename"; //$NON-NLS-1$ static final String PROPERTY_FILENAME = "baseFilename"; //$NON-NLS-1$
static final int APK_FILE_SOURCE = 0; static final int APK_FILE_SOURCE = 0;
static final int APK_FILE_DEST = 1; static final int APK_FILE_DEST = 1;
static final int APK_COUNT = 2; static final int APK_COUNT = 2;
@@ -87,7 +87,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
* Base page class for the ExportWizard page. This class add the {@link #onShow()} callback. * Base page class for the ExportWizard page. This class add the {@link #onShow()} callback.
*/ */
static abstract class ExportWizardPage extends WizardPage { static abstract class ExportWizardPage extends WizardPage {
/** bit mask constant for project data change event */ /** bit mask constant for project data change event */
protected static final int DATA_PROJECT = 0x001; protected static final int DATA_PROJECT = 0x001;
/** bit mask constant for keystore data change event */ /** bit mask constant for keystore data change event */
@@ -99,13 +99,13 @@ public final class ExportWizard extends Wizard implements IExportWizard {
public void verifyText(VerifyEvent e) { public void verifyText(VerifyEvent e) {
// verify the characters are valid for password. // verify the characters are valid for password.
int len = e.text.length(); int len = e.text.length();
// first limit to 127 characters max // first limit to 127 characters max
if (len + ((Text)e.getSource()).getText().length() > 127) { if (len + ((Text)e.getSource()).getText().length() > 127) {
e.doit = false; e.doit = false;
return; return;
} }
// now only take non control characters // now only take non control characters
for (int i = 0 ; i < len ; i++) { for (int i = 0 ; i < len ; i++) {
if (e.text.charAt(i) < 32) { if (e.text.charAt(i) < 32) {
@@ -115,7 +115,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
} }
} }
}; };
/** /**
* Bit mask indicating what changed while the page was hidden. * Bit mask indicating what changed while the page was hidden.
* @see #DATA_PROJECT * @see #DATA_PROJECT
@@ -123,13 +123,13 @@ public final class ExportWizard extends Wizard implements IExportWizard {
* @see #DATA_KEY * @see #DATA_KEY
*/ */
protected int mProjectDataChanged = 0; protected int mProjectDataChanged = 0;
ExportWizardPage(String name) { ExportWizardPage(String name) {
super(name); super(name);
} }
abstract void onShow(); abstract void onShow();
@Override @Override
public void setVisible(boolean visible) { public void setVisible(boolean visible) {
super.setVisible(visible); super.setVisible(visible);
@@ -138,23 +138,23 @@ public final class ExportWizard extends Wizard implements IExportWizard {
mProjectDataChanged = 0; mProjectDataChanged = 0;
} }
} }
final void projectDataChanged(int changeMask) { final void projectDataChanged(int changeMask) {
mProjectDataChanged |= changeMask; mProjectDataChanged |= changeMask;
} }
/** /**
* Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a * Calls {@link #setErrorMessage(String)} and {@link #setPageComplete(boolean)} based on a
* {@link Throwable} object. * {@link Throwable} object.
*/ */
protected void onException(Throwable t) { protected void onException(Throwable t) {
String message = getExceptionMessage(t); String message = getExceptionMessage(t);
setErrorMessage(message); setErrorMessage(message);
setPageComplete(false); setPageComplete(false);
} }
} }
private ExportWizardPage mPages[] = new ExportWizardPage[5]; private ExportWizardPage mPages[] = new ExportWizardPage[5];
private IProject mProject; private IProject mProject;
@@ -189,7 +189,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
setWindowTitle("Export Android Application"); setWindowTitle("Export Android Application");
setImageDescriptor(); setImageDescriptor();
} }
@Override @Override
public void addPages() { public void addPages() {
addPage(mPages[0] = new ProjectCheckPage(this, PAGE_PROJECT_CHECK)); addPage(mPages[0] = new ProjectCheckPage(this, PAGE_PROJECT_CHECK));
@@ -209,7 +209,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
mDestinationParentFolder.getAbsolutePath()); mDestinationParentFolder.getAbsolutePath());
ProjectHelper.saveStringProperty(mProject, PROPERTY_FILENAME, ProjectHelper.saveStringProperty(mProject, PROPERTY_FILENAME,
mApkMap.get(null)[APK_FILE_DEST]); mApkMap.get(null)[APK_FILE_DEST]);
// run the export in an UI runnable. // run the export in an UI runnable.
IWorkbench workbench = PlatformUI.getWorkbench(); IWorkbench workbench = PlatformUI.getWorkbench();
final boolean[] result = new boolean[1]; final boolean[] result = new boolean[1];
@@ -234,10 +234,10 @@ public final class ExportWizard extends Wizard implements IExportWizard {
} catch (InterruptedException e) { } catch (InterruptedException e) {
return false; return false;
} }
return result[0]; return result[0];
} }
private boolean doExport(IProgressMonitor monitor) { private boolean doExport(IProgressMonitor monitor) {
try { try {
// first we make sure the project is built // first we make sure the project is built
@@ -262,13 +262,13 @@ public final class ExportWizard extends Wizard implements IExportWizard {
output.add(message); output.add(message);
} }
}); });
if (createdStore == false) { if (createdStore == false) {
// keystore creation error! // keystore creation error!
displayError(output.toArray(new String[output.size()])); displayError(output.toArray(new String[output.size()]));
return false; return false;
} }
// keystore is created, now load the private key and certificate. // keystore is created, now load the private key and certificate.
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
FileInputStream fis = new FileInputStream(mKeystore); FileInputStream fis = new FileInputStream(mKeystore);
@@ -276,7 +276,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
fis.close(); fis.close();
PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry( PrivateKeyEntry entry = (KeyStore.PrivateKeyEntry)keyStore.getEntry(
mKeyAlias, new KeyStore.PasswordProtection(mKeyPassword.toCharArray())); mKeyAlias, new KeyStore.PasswordProtection(mKeyPassword.toCharArray()));
if (entry != null) { if (entry != null) {
mPrivateKey = entry.getPrivateKey(); mPrivateKey = entry.getPrivateKey();
mCertificate = (X509Certificate)entry.getCertificate(); mCertificate = (X509Certificate)entry.getCertificate();
@@ -287,7 +287,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
return false; return false;
} }
} }
// check the private key/certificate again since it may have been created just above. // check the private key/certificate again since it may have been created just above.
if (mPrivateKey != null && mCertificate != null) { if (mPrivateKey != null && mCertificate != null) {
// get the output folder of the project to export. // get the output folder of the project to export.
@@ -297,27 +297,50 @@ public final class ExportWizard extends Wizard implements IExportWizard {
return false; return false;
} }
String outputOsPath = outputIFolder.getLocation().toOSString(); String outputOsPath = outputIFolder.getLocation().toOSString();
// now generate the packages. // now generate the packages.
Set<Entry<String, String[]>> set = mApkMap.entrySet(); Set<Entry<String, String[]>> set = mApkMap.entrySet();
boolean runZipAlign = false;
String path = AdtPlugin.getOsAbsoluteZipAlign();
File zipalign = new File(path);
runZipAlign = zipalign.isFile();
for (Entry<String, String[]> entry : set) { for (Entry<String, String[]> entry : set) {
String[] defaultApk = entry.getValue(); String[] defaultApk = entry.getValue();
String srcFilename = defaultApk[APK_FILE_SOURCE]; String srcFilename = defaultApk[APK_FILE_SOURCE];
String destFilename = defaultApk[APK_FILE_DEST]; String destFilename = defaultApk[APK_FILE_DEST];
File destFile;
if (runZipAlign) {
destFile = File.createTempFile("android", ".apk");
} else {
destFile = new File(mDestinationParentFolder, destFilename);
}
FileOutputStream fos = new FileOutputStream(
new File(mDestinationParentFolder, destFilename)); FileOutputStream fos = new FileOutputStream(destFile);
SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate); SignedJarBuilder builder = new SignedJarBuilder(fos, mPrivateKey, mCertificate);
// get the input file. // get the input file.
FileInputStream fis = new FileInputStream(new File(outputOsPath, srcFilename)); FileInputStream fis = new FileInputStream(new File(outputOsPath, srcFilename));
// add the content of the source file to the output file, and sign it at // add the content of the source file to the output file, and sign it at
// the same time. // the same time.
try { try {
builder.writeZip(fis, null /* filter */); builder.writeZip(fis, null /* filter */);
// close the builder: write the final signature files, and close the archive. // close the builder: write the final signature files,
// and close the archive.
builder.close(); builder.close();
// now zipalign the result
if (runZipAlign) {
File realDestFile = new File(mDestinationParentFolder, destFilename);
String message = zipAlign(path, destFile, realDestFile);
if (message != null) {
displayError(message);
return false;
}
}
} finally { } finally {
try { try {
fis.close(); fis.close();
@@ -326,25 +349,26 @@ public final class ExportWizard extends Wizard implements IExportWizard {
} }
} }
} }
// export success. In case we didn't run ZipAlign we display a warning
if (runZipAlign == false) {
AdtPlugin.displayWarning("Export Wizard",
"The zipalign tool was not found in the SDK.\n\n" +
"Please update to the latest SDK and re-export your application\n" +
"or run zipalign manually.\n\n" +
"Aligning applications allows Android to use application resources\n" +
"more efficiently.");
}
return true; return true;
} }
} catch (FileNotFoundException e) { } catch (Throwable t) {
displayError(e); displayError(t);
} catch (NoSuchAlgorithmException e) {
displayError(e);
} catch (IOException e) {
displayError(e);
} catch (GeneralSecurityException e) {
displayError(e);
} catch (KeytoolException e) {
displayError(e);
} catch (CoreException e) {
displayError(e);
} }
return false; return false;
} }
@Override @Override
public boolean canFinish() { public boolean canFinish() {
// check if we have the apk to resign, the destination location, and either // check if we have the apk to resign, the destination location, and either
@@ -356,7 +380,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
|| mKeystoreCreationMode || mKeyCreationMode) && || mKeystoreCreationMode || mKeyCreationMode) &&
mDestinationParentFolder != null; mDestinationParentFolder != null;
} }
/* /*
* (non-Javadoc) * (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection) * @see org.eclipse.ui.IWorkbenchWizard#init(org.eclipse.ui.IWorkbench, org.eclipse.jface.viewers.IStructuredSelection)
@@ -364,7 +388,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
public void init(IWorkbench workbench, IStructuredSelection selection) { public void init(IWorkbench workbench, IStructuredSelection selection) {
// get the project from the selection // get the project from the selection
Object selected = selection.getFirstElement(); Object selected = selection.getFirstElement();
if (selected instanceof IProject) { if (selected instanceof IProject) {
mProject = (IProject)selected; mProject = (IProject)selected;
} else if (selected instanceof IAdaptable) { } else if (selected instanceof IAdaptable) {
@@ -374,19 +398,19 @@ public final class ExportWizard extends Wizard implements IExportWizard {
} }
} }
} }
ExportWizardPage getKeystoreSelectionPage() { ExportWizardPage getKeystoreSelectionPage() {
return mKeystoreSelectionPage; return mKeystoreSelectionPage;
} }
ExportWizardPage getKeyCreationPage() { ExportWizardPage getKeyCreationPage() {
return mKeyCreationPage; return mKeyCreationPage;
} }
ExportWizardPage getKeySelectionPage() { ExportWizardPage getKeySelectionPage() {
return mKeySelectionPage; return mKeySelectionPage;
} }
ExportWizardPage getKeyCheckPage() { ExportWizardPage getKeyCheckPage() {
return mKeyCheckPage; return mKeyCheckPage;
} }
@@ -398,39 +422,39 @@ public final class ExportWizard extends Wizard implements IExportWizard {
ImageDescriptor desc = AdtPlugin.getImageDescriptor(PROJECT_LOGO_LARGE); ImageDescriptor desc = AdtPlugin.getImageDescriptor(PROJECT_LOGO_LARGE);
setDefaultPageImageDescriptor(desc); setDefaultPageImageDescriptor(desc);
} }
IProject getProject() { IProject getProject() {
return mProject; return mProject;
} }
void setProject(IProject project) { void setProject(IProject project) {
mProject = project; mProject = project;
updatePageOnChange(ExportWizardPage.DATA_PROJECT); updatePageOnChange(ExportWizardPage.DATA_PROJECT);
} }
void setKeystore(String path) { void setKeystore(String path) {
mKeystore = path; mKeystore = path;
mPrivateKey = null; mPrivateKey = null;
mCertificate = null; mCertificate = null;
updatePageOnChange(ExportWizardPage.DATA_KEYSTORE); updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
} }
String getKeystore() { String getKeystore() {
return mKeystore; return mKeystore;
} }
void setKeystoreCreationMode(boolean createStore) { void setKeystoreCreationMode(boolean createStore) {
mKeystoreCreationMode = createStore; mKeystoreCreationMode = createStore;
updatePageOnChange(ExportWizardPage.DATA_KEYSTORE); updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
} }
boolean getKeystoreCreationMode() { boolean getKeystoreCreationMode() {
return mKeystoreCreationMode; return mKeystoreCreationMode;
} }
void setKeystorePassword(String password) { void setKeystorePassword(String password) {
mKeystorePassword = password; mKeystorePassword = password;
mPrivateKey = null; mPrivateKey = null;
@@ -438,7 +462,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
updatePageOnChange(ExportWizardPage.DATA_KEYSTORE); updatePageOnChange(ExportWizardPage.DATA_KEYSTORE);
} }
String getKeystorePassword() { String getKeystorePassword() {
return mKeystorePassword; return mKeystorePassword;
} }
@@ -447,15 +471,15 @@ public final class ExportWizard extends Wizard implements IExportWizard {
mKeyCreationMode = createKey; mKeyCreationMode = createKey;
updatePageOnChange(ExportWizardPage.DATA_KEY); updatePageOnChange(ExportWizardPage.DATA_KEY);
} }
boolean getKeyCreationMode() { boolean getKeyCreationMode() {
return mKeyCreationMode; return mKeyCreationMode;
} }
void setExistingAliases(List<String> aliases) { void setExistingAliases(List<String> aliases) {
mExistingAliases = aliases; mExistingAliases = aliases;
} }
List<String> getExistingAliases() { List<String> getExistingAliases() {
return mExistingAliases; return mExistingAliases;
} }
@@ -467,7 +491,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
updatePageOnChange(ExportWizardPage.DATA_KEY); updatePageOnChange(ExportWizardPage.DATA_KEY);
} }
String getKeyAlias() { String getKeyAlias() {
return mKeyAlias; return mKeyAlias;
} }
@@ -479,7 +503,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
updatePageOnChange(ExportWizardPage.DATA_KEY); updatePageOnChange(ExportWizardPage.DATA_KEY);
} }
String getKeyPassword() { String getKeyPassword() {
return mKeyPassword; return mKeyPassword;
} }
@@ -488,7 +512,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
mValidity = validity; mValidity = validity;
updatePageOnChange(ExportWizardPage.DATA_KEY); updatePageOnChange(ExportWizardPage.DATA_KEY);
} }
int getValidity() { int getValidity() {
return mValidity; return mValidity;
} }
@@ -497,7 +521,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
mDName = dName; mDName = dName;
updatePageOnChange(ExportWizardPage.DATA_KEY); updatePageOnChange(ExportWizardPage.DATA_KEY);
} }
String getDName() { String getDName() {
return mDName; return mDName;
} }
@@ -522,7 +546,7 @@ public final class ExportWizard extends Wizard implements IExportWizard {
page.projectDataChanged(changeMask); page.projectDataChanged(changeMask);
} }
} }
private void displayError(String... messages) { private void displayError(String... messages) {
String message = null; String message = null;
if (messages.length == 1) { if (messages.length == 1) {
@@ -533,20 +557,125 @@ public final class ExportWizard extends Wizard implements IExportWizard {
sb.append('\n'); sb.append('\n');
sb.append(messages[i]); sb.append(messages[i]);
} }
message = sb.toString(); message = sb.toString();
} }
AdtPlugin.displayError("Export Wizard", message); AdtPlugin.displayError("Export Wizard", message);
} }
private void displayError(Exception e) { private void displayError(Throwable t) {
String message = getExceptionMessage(e); String message = getExceptionMessage(t);
displayError(message); displayError(message);
AdtPlugin.log(e, "Export Wizard Error"); AdtPlugin.log(t, "Export Wizard Error");
} }
/**
* Executes zipalign
* @param zipAlignPath location of the zipalign too
* @param source file to zipalign
* @param destination where to write the resulting file
* @return null if success, the error otherwise
* @throws IOException
*/
private String zipAlign(String zipAlignPath, File source, File destination) throws IOException {
// command line: zipaling -f 4 tmp destination
String[] command = new String[5];
command[0] = zipAlignPath;
command[1] = "-f"; //$NON-NLS-1$
command[2] = "4"; //$NON-NLS-1$
command[3] = source.getAbsolutePath();
command[4] = destination.getAbsolutePath();
Process process = Runtime.getRuntime().exec(command);
ArrayList<String> output = new ArrayList<String>();
try {
if (grabProcessOutput(process, output) != 0) {
// build a single message from the array list
StringBuilder sb = new StringBuilder("Error while running zipalign:");
for (String msg : output) {
sb.append('\n');
sb.append(msg);
}
return sb.toString();
}
} catch (InterruptedException e) {
// ?
}
return null;
}
/**
* Get the stderr output of a process and return when the process is done.
* @param process The process to get the ouput from
* @param results The array to store the stderr output
* @return the process return code.
* @throws InterruptedException
*/
private final int grabProcessOutput(final Process process,
final ArrayList<String> results)
throws InterruptedException {
// Due to the limited buffer size on windows for the standard io (stderr, stdout), we
// *need* to read both stdout and stderr all the time. If we don't and a process output
// a large amount, this could deadlock the process.
// read the lines as they come. if null is returned, it's
// because the process finished
new Thread("") { //$NON-NLS-1$
@Override
public void run() {
// create a buffer to read the stderr output
InputStreamReader is = new InputStreamReader(process.getErrorStream());
BufferedReader errReader = new BufferedReader(is);
try {
while (true) {
String line = errReader.readLine();
if (line != null) {
results.add(line);
} else {
break;
}
}
} catch (IOException e) {
// do nothing.
}
}
}.start();
new Thread("") { //$NON-NLS-1$
@Override
public void run() {
InputStreamReader is = new InputStreamReader(process.getInputStream());
BufferedReader outReader = new BufferedReader(is);
IProject project = getProject();
try {
while (true) {
String line = outReader.readLine();
if (line != null) {
AdtPlugin.printBuildToConsole(AdtConstants.BUILD_VERBOSE,
project, line);
} else {
break;
}
}
} catch (IOException e) {
// do nothing.
}
}
}.start();
// get the return code from the process
return process.waitFor();
}
/** /**
* Returns the {@link Throwable#getMessage()}. If the {@link Throwable#getMessage()} returns * Returns the {@link Throwable#getMessage()}. If the {@link Throwable#getMessage()} returns
* <code>null</code>, the method is called again on the cause of the Throwable object. * <code>null</code>, the method is called again on the cause of the Throwable object.
@@ -556,15 +685,13 @@ public final class ExportWizard extends Wizard implements IExportWizard {
static String getExceptionMessage(Throwable t) { static String getExceptionMessage(Throwable t) {
String message = t.getMessage(); String message = t.getMessage();
if (message == null) { if (message == null) {
Throwable cause = t.getCause(); // no error info? get the stack call to display it
if (cause != null) { // At least that'll give us a better bug report.
return getExceptionMessage(cause); ByteArrayOutputStream baos = new ByteArrayOutputStream();
} t.printStackTrace(new PrintStream(baos));
message = baos.toString();
// no more cause and still no message. display the first exception.
return t.getClass().getCanonicalName();
} }
return message; return message;
} }
} }

View File

@@ -107,6 +107,10 @@ public final class SdkConstants {
public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ? public final static String FN_EMULATOR = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
"emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$ "emulator.exe" : "emulator"; //$NON-NLS-1$ //$NON-NLS-2$
/** zipalign executable (with extension for the current OS) */
public final static String FN_ZIPALIGN = (CURRENT_PLATFORM == PLATFORM_WINDOWS) ?
"zipalign.exe" : "zipalign"; //$NON-NLS-1$ //$NON-NLS-2$
/* Folder Names for Android Projects . */ /* Folder Names for Android Projects . */
/** Resources folder name, i.e. "res". */ /** Resources folder name, i.e. "res". */