From e27bbc4d1cf9c9d6b23344b977331bed0a3357a5 Mon Sep 17 00:00:00 2001 From: Raphael Date: Tue, 21 Feb 2012 23:19:33 -0800 Subject: ADT: Suggest solution to ADT version check error. This amends the ADT version check to help the user either: - open the SDK Manager - open the P2 Updater - open the Android Preference On Windows the launch the *external* SDK Manager since eventually we know that ADT will lock something that would prevent the update from working in the first place. Change-Id: Ib20e4e1411b36e3cd794cccbc02518db0a40ced9 --- .../src/com/android/ide/eclipse/adt/AdtPlugin.java | 158 +++++++++++++- .../ide/eclipse/adt/internal/VersionCheck.java | 7 +- .../adt/internal/actions/DexDumpAction.java | 3 +- .../adt/internal/actions/SdkManagerAction.java | 233 ++++++++++++++------- .../eclipse/adt/internal/build/BuildHelper.java | 3 +- .../preferences/AndroidPreferencePage.java | 9 +- .../eclipse/adt/internal/welcome/AdtStartup.java | 9 +- .../adt/internal/welcome/WelcomeWizardPage.java | 9 +- .../adt/internal/wizards/export/ExportWizard.java | 3 +- .../android/sdklib/internal/avd/AvdManager.java | 3 +- .../sdklib/internal/build/KeystoreHelper.java | 3 +- .../internal/repository/ArchiveInstaller.java | 3 +- .../sdklib/internal/repository/ToolPackage.java | 3 +- .../com/android/sdklib/util/GrabProcessOutput.java | 39 +++- 14 files changed, 380 insertions(+), 105 deletions(-) diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java index 2584933..f6fe09f 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtPlugin.java @@ -20,7 +20,9 @@ import com.android.AndroidConstants; import com.android.ide.common.log.ILogger; import com.android.ide.common.resources.ResourceFile; import com.android.ide.common.sdk.LoadStatus; +import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler.Solution; import com.android.ide.eclipse.adt.internal.VersionCheck; +import com.android.ide.eclipse.adt.internal.actions.SdkManagerAction; import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; import com.android.ide.eclipse.adt.internal.editors.IconFactory; import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; @@ -42,6 +44,7 @@ import com.android.resources.ResourceFolderType; import com.android.sdklib.IAndroidTarget; import com.android.sdklib.SdkConstants; +import org.eclipse.core.commands.Command; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarkerDelta; import org.eclipse.core.resources.IProject; @@ -59,8 +62,10 @@ import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.ui.JavaUI; +import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceDialog; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.util.IPropertyChangeListener; @@ -78,11 +83,14 @@ import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.browser.IWebBrowser; import org.eclipse.ui.browser.IWorkbenchBrowserSupport; +import org.eclipse.ui.commands.ICommandService; import org.eclipse.ui.console.ConsolePlugin; import org.eclipse.ui.console.IConsole; import org.eclipse.ui.console.IConsoleConstants; import org.eclipse.ui.console.MessageConsole; import org.eclipse.ui.console.MessageConsoleStream; +import org.eclipse.ui.dialogs.PreferencesUtil; +import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.plugin.AbstractUIPlugin; import org.eclipse.ui.texteditor.AbstractTextEditor; @@ -162,15 +170,23 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { * checkSdkLocationAndId. */ public static abstract class CheckSdkErrorHandler { + + public enum Solution { + NONE, + OPEN_SDK_MANAGER, + OPEN_ANDROID_PREFS, + OPEN_P2_UPDATE + } + /** Handle an error message during sdk location check. Returns whatever * checkSdkLocationAndId() should returns. */ - public abstract boolean handleError(String message); + public abstract boolean handleError(Solution solution, String message); /** Handle a warning message during sdk location check. Returns whatever * checkSdkLocationAndId() should returns. */ - public abstract boolean handleWarning(String message); + public abstract boolean handleWarning(Solution solution, String message); } /** @@ -1064,17 +1080,133 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { } return checkSdkLocationAndId(sdkLocation, new CheckSdkErrorHandler() { + private String mTitle = "Android SDK Verification"; @Override - public boolean handleError(String message) { - AdtPlugin.displayError("Android SDK Verification", message); + public boolean handleError(Solution solution, String message) { + displayMessage(solution, message, MessageDialog.ERROR); return false; } @Override - public boolean handleWarning(String message) { - AdtPlugin.displayWarning("Android SDK Verification", message); + public boolean handleWarning(Solution solution, String message) { + displayMessage(solution, message, MessageDialog.WARNING); return true; } + + private void displayMessage( + final Solution solution, + final String message, + final int dialogImageType) { + final Display disp = getDisplay(); + disp.asyncExec(new Runnable() { + @Override + public void run() { + Shell shell = disp.getActiveShell(); + if (shell == null) { + return; + } + + String customLabel = null; + switch(solution) { + case OPEN_ANDROID_PREFS: + customLabel = "Open Preferences"; + break; + case OPEN_P2_UPDATE: + customLabel = "Check for Updates"; + break; + case OPEN_SDK_MANAGER: + customLabel = "Open SDK Manager"; + break; + } + + String btnLabels[] = new String[customLabel == null ? 1 : 2]; + btnLabels[0] = customLabel; + btnLabels[btnLabels.length - 1] = IDialogConstants.CLOSE_LABEL; + + MessageDialog dialog = new MessageDialog( + shell, // parent + mTitle, + null, // dialogTitleImage + message, + dialogImageType, + btnLabels, + btnLabels.length - 1); + int index = dialog.open(); + + if (customLabel != null && index == 0) { + switch(solution) { + case OPEN_ANDROID_PREFS: + openAndroidPrefs(); + break; + case OPEN_P2_UPDATE: + openP2Update(); + break; + case OPEN_SDK_MANAGER: + openSdkManager(); + break; + } + } + } + }); + } + + private void openSdkManager() { + // Windows only: open the standalone external SDK Manager since we know + // that ADT on Windows is bound to be locking some SDK folders. + if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_WINDOWS) { + if (SdkManagerAction.openExternalSdkManager()) { + return; + } + } + + // Otherwise open the regular SDK Manager bundled within ADT + if (!SdkManagerAction.openAdtSdkManager()) { + // We failed because the SDK location is undefined. In this case + // let's open the preferences instead. + openAndroidPrefs(); + } + } + + private void openP2Update() { + Display disp = getDisplay(); + if (disp == null) { + return; + } + disp.asyncExec(new Runnable() { + @Override + public void run() { + String cmdId = "org.eclipse.equinox.p2.ui.sdk.update"; //$NON-NLS-1$ + IWorkbench wb = PlatformUI.getWorkbench(); + if (wb == null) { + return; + } + + ICommandService cs = (ICommandService) wb.getService(ICommandService.class); + IHandlerService is = (IHandlerService) wb.getService(IHandlerService.class); + if (cs == null || is == null) { + return; + } + + Command cmd = cs.getCommand(cmdId); + if (cmd != null && cmd.isDefined()) { + try { + is.executeCommand(cmdId, null/*event*/); + } catch (Exception ignore) { + AdtPlugin.log(ignore, "Failed to execute command %s", cmdId); + } + } + } + }); + } + + private void openAndroidPrefs() { + PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn( + getDisplay().getActiveShell(), + "com.android.ide.eclipse.preferences.main", //$NON-NLS-1$ preferencePageId + null, // displayedIds + null); // data + dialog.open(); + } }); } @@ -1093,6 +1225,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { File osSdkFolder = new File(osSdkLocation); if (osSdkFolder.isDirectory() == false) { return errorHandler.handleError( + Solution.OPEN_ANDROID_PREFS, String.format(Messages.Could_Not_Find_Folder, osSdkLocation)); } @@ -1100,6 +1233,7 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { File toolsFolder = new File(osTools); if (toolsFolder.isDirectory() == false) { return errorHandler.handleError( + Solution.OPEN_ANDROID_PREFS, String.format(Messages.Could_Not_Find_Folder_In_SDK, SdkConstants.FD_TOOLS, osSdkLocation)); } @@ -1113,13 +1247,17 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { // check that we have both the tools component and the platform-tools component. String platformTools = osSdkLocation + SdkConstants.OS_SDK_PLATFORM_TOOLS_FOLDER; if (checkFolder(platformTools) == false) { - return errorHandler.handleWarning("SDK Platform Tools component is missing!\n" + + return errorHandler.handleWarning( + Solution.OPEN_SDK_MANAGER, + "SDK Platform Tools component is missing!\n" + "Please use the SDK Manager to install it."); } String tools = osSdkLocation + SdkConstants.OS_SDK_TOOLS_FOLDER; if (checkFolder(tools) == false) { - return errorHandler.handleError("SDK Tools component is missing!\n" + + return errorHandler.handleError( + Solution.OPEN_SDK_MANAGER, + "SDK Tools component is missing!\n" + "Please use the SDK Manager to install it."); } @@ -1131,7 +1269,9 @@ public class AdtPlugin extends AbstractUIPlugin implements ILogger { }; for (String file : filesToCheck) { if (checkFile(file) == false) { - return errorHandler.handleError(String.format(Messages.Could_Not_Find, file)); + return errorHandler.handleError( + Solution.OPEN_ANDROID_PREFS, + String.format(Messages.Could_Not_Find, file)); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java index 9055b29..b468c5e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/VersionCheck.java @@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.internal; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler; +import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler.Solution; import com.android.ide.eclipse.adt.Messages; import com.android.sdklib.SdkConstants; import com.android.sdklib.repository.PkgProps; @@ -103,7 +104,9 @@ public final class VersionCheck { // Failed to get the min plugin version number? if (minMajorVersion == -1 || minMinorVersion == -1 || minMicroVersion ==-1) { - return errorHandler.handleWarning(Messages.VersionCheck_Plugin_Version_Failed); + return errorHandler.handleWarning( + Solution.OPEN_SDK_MANAGER, + Messages.VersionCheck_Plugin_Version_Failed); } // test the plugin number @@ -126,6 +129,7 @@ public final class VersionCheck { if (valid == false) { return errorHandler.handleError( + Solution.OPEN_P2_UPDATE, String.format(Messages.VersionCheck_Plugin_Too_Old, minMajorVersion, minMinorVersion, minMicroVersion, versionString)); } @@ -163,6 +167,7 @@ public final class VersionCheck { // this is a warning only as we need to parse the SDK to allow updating // of the tools! return errorHandler.handleWarning( + Solution.OPEN_SDK_MANAGER, String.format(Messages.VersionCheck_Tools_Too_Old, MIN_TOOLS_REV, toolsRevision)); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java index 970bc47..f8a080a 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/DexDumpAction.java @@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.sdklib.SdkConstants; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; +import com.android.sdklib.util.GrabProcessOutput.Wait; import org.eclipse.core.filesystem.EFS; import org.eclipse.core.filesystem.IFileStore; @@ -175,7 +176,7 @@ public class DexDumpAction implements IObjectActionDelegate { int err = GrabProcessOutput.grabProcessOutput( process, - true /*waitForReaders*/, + Wait.WAIT_FOR_READERS, new IProcessOutput() { @Override public void out(String line) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java index ddf180f..0ff50b5 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java @@ -20,6 +20,11 @@ import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.build.DexWrapper; import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog; import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.sdklib.SdkConstants; +import com.android.sdklib.io.FileOp; +import com.android.sdklib.util.GrabProcessOutput; +import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; +import com.android.sdklib.util.GrabProcessOutput.Wait; import com.android.sdkuilib.repository.ISdkChangeListener; import com.android.sdkuilib.repository.SdkUpdaterWindow; import com.android.sdkuilib.repository.SdkUpdaterWindow.SdkInvocationContext; @@ -31,6 +36,8 @@ import org.eclipse.ui.IWorkbenchPart; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.IWorkbenchWindowActionDelegate; +import java.io.File; + /** * Delegate for the toolbar/menu action "Android SDK Manager". * It displays the Android SDK Manager. @@ -49,93 +56,161 @@ public class SdkManagerAction implements IWorkbenchWindowActionDelegate, IObject @Override public void run(IAction action) { - final Sdk sdk = Sdk.getCurrent(); - if (sdk != null) { + if (!openAdtSdkManager()) { + AdtPlugin.displayError( + "Android SDK", + "Location of the Android SDK has not been setup in the preferences."); + } + } - // Runs the updater window, directing only warning/errors logs to the ADT console - // (normal log is just dropped, which is fine since the SDK Manager has its own - // log window now.) + /** + * Opens the SDK Manager as an external application. + * This call is asynchronous, it doesn't wait for the manager to be closed. + * + * @return True if the application was found and executed. False if it could not + * be located or could not be launched. + */ + public static boolean openExternalSdkManager() { + final Sdk sdk = Sdk.getCurrent(); + if (sdk == null) { + return false; + } - SdkUpdaterWindow window = new SdkUpdaterWindow( - AdtPlugin.getDisplay().getActiveShell(), - new AdtConsoleSdkLog() { - @Override - public void printf(String msgFormat, Object... args) { - // Do not show non-error/warning log in Eclipse. - }; - }, - sdk.getSdkLocation(), - SdkInvocationContext.IDE); - - ISdkChangeListener listener = new ISdkChangeListener() { - @Override - public void onSdkLoaded() { - // Ignore initial load of the SDK. - } + File androidBat = FileOp.append( + sdk.getSdkLocation(), + SdkConstants.FD_TOOLS, + SdkConstants.androidCmdName()); - /** - * Unload all we can from the SDK before new packages are installed. - * Typically we need to get rid of references to dx from platform-tools - * and to any platform resource data. - *

- * {@inheritDoc} - */ - @Override - public void preInstallHook() { - - // TODO we need to unload as much of as SDK as possible. Otherwise - // on Windows we end up with Eclipse locking some files and we can't - // replace them. - // - // At this point, we know what the user wants to install so it would be - // possible to pass in flags to know what needs to be unloaded. Typically - // we need to: - // - unload dex if platform-tools is going to be updated. There's a vague - // attempt below at removing any references to dex and GCing. Seems - // to do the trick. - // - unload any target that is going to be updated since it may have - // resource data used by a current layout editor (e.g. data/*.ttf - // and various data/res/*.xml). - // - // Most important we need to make sure there isn't a build going on - // and if there is one, either abort it or wait for it to complete and - // then we want to make sure we don't get any attempt to use the SDK - // before the postInstallHook is called. - - if (sdk != null) { - sdk.unloadTargetData(true /*preventReload*/); - - DexWrapper dx = sdk.getDexWrapper(); - dx.unload(); - } - } + if (!androidBat.exists()) { + return false; + } - /** - * Nothing to do. We'll reparse the SDK later in onSdkReload. - *

- * {@inheritDoc} - */ - @Override - public void postInstallHook() { - } + try { + final AdtConsoleSdkLog logger = new AdtConsoleSdkLog(); - /** - * Reparse the SDK in case anything was add/removed. - *

- * {@inheritDoc} - */ - @Override - public void onSdkReload() { - AdtPlugin.getDefault().reparseSdk(); - } + String command[] = new String[] { + androidBat.getAbsolutePath(), + "sdk" //$NON-NLS-1$ }; + Process process = Runtime.getRuntime().exec(command); + GrabProcessOutput.grabProcessOutput( + process, + Wait.ASYNC, + new IProcessOutput() { + @Override + public void out(String line) { + // Ignore stdout + } - window.addListener(listener); - window.open(); - } else { - AdtPlugin.displayError("Android SDK", - "Location of the Android SDK has not been setup in the preferences."); + @Override + public void err(String line) { + if (line != null) { + logger.printf("[SDK Manager] %s", line); + } + } + }); + } catch (Exception ignore) { } + + return true; + } + + /** + * Opens the SDK Manager bundled within ADT. + * The call is blocking and does not return till the SD Manager window is closed. + * + * @return True if the SDK location is known and the SDK Manager was started. + * False if the SDK location is not set and we can't open a SDK Manager to + * manage files in an unknown location. + */ + public static boolean openAdtSdkManager() { + final Sdk sdk = Sdk.getCurrent(); + if (sdk == null) { + return false; + } + + // Runs the updater window, directing only warning/errors logs to the ADT console + // (normal log is just dropped, which is fine since the SDK Manager has its own + // log window now.) + + SdkUpdaterWindow window = new SdkUpdaterWindow( + AdtPlugin.getDisplay().getActiveShell(), + new AdtConsoleSdkLog() { + @Override + public void printf(String msgFormat, Object... args) { + // Do not show non-error/warning log in Eclipse. + }; + }, + sdk.getSdkLocation(), + SdkInvocationContext.IDE); + + ISdkChangeListener listener = new ISdkChangeListener() { + @Override + public void onSdkLoaded() { + // Ignore initial load of the SDK. + } + + /** + * Unload all we can from the SDK before new packages are installed. + * Typically we need to get rid of references to dx from platform-tools + * and to any platform resource data. + *

+ * {@inheritDoc} + */ + @Override + public void preInstallHook() { + + // TODO we need to unload as much of as SDK as possible. Otherwise + // on Windows we end up with Eclipse locking some files and we can't + // replace them. + // + // At this point, we know what the user wants to install so it would be + // possible to pass in flags to know what needs to be unloaded. Typically + // we need to: + // - unload dex if platform-tools is going to be updated. There's a vague + // attempt below at removing any references to dex and GCing. Seems + // to do the trick. + // - unload any target that is going to be updated since it may have + // resource data used by a current layout editor (e.g. data/*.ttf + // and various data/res/*.xml). + // + // Most important we need to make sure there isn't a build going on + // and if there is one, either abort it or wait for it to complete and + // then we want to make sure we don't get any attempt to use the SDK + // before the postInstallHook is called. + + if (sdk != null) { + sdk.unloadTargetData(true /*preventReload*/); + + DexWrapper dx = sdk.getDexWrapper(); + dx.unload(); + } + } + + /** + * Nothing to do. We'll reparse the SDK later in onSdkReload. + *

+ * {@inheritDoc} + */ + @Override + public void postInstallHook() { + } + + /** + * Reparse the SDK in case anything was add/removed. + *

+ * {@inheritDoc} + */ + @Override + public void onSdkReload() { + AdtPlugin.getDefault().reparseSdk(); + } + }; + + window.addListener(listener); + window.open(); + + return true; } @Override diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java index 3ae9f64..5e3c11e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/build/BuildHelper.java @@ -41,6 +41,7 @@ import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException; import com.android.sdklib.internal.build.SignedJarBuilder; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; +import com.android.sdklib.util.GrabProcessOutput.Wait; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; @@ -1196,7 +1197,7 @@ public class BuildHelper { return GrabProcessOutput.grabProcessOutput( process, - false /*waitForReaders*/, + Wait.WAIT_FOR_PROCESS, new IProcessOutput() { @SuppressWarnings("unused") diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java index 426149e..eff883b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/preferences/AndroidPreferencePage.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.preferences; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler; import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.ide.eclipse.adt.internal.sdk.Sdk.ITargetChangeListener; import com.android.sdklib.IAndroidTarget; @@ -140,13 +141,17 @@ public class AndroidPreferencePage extends FieldEditorPreferencePage implements boolean ok = AdtPlugin.getDefault().checkSdkLocationAndId(fileName, new AdtPlugin.CheckSdkErrorHandler() { @Override - public boolean handleError(String message) { + public boolean handleError( + CheckSdkErrorHandler.Solution solution, + String message) { setErrorMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$ return false; // Apply/OK must be disabled } @Override - public boolean handleWarning(String message) { + public boolean handleWarning( + CheckSdkErrorHandler.Solution solution, + String message) { showMessage(message.replaceAll("\n", " ")); //$NON-NLS-1$ //$NON-NLS-2$ return true; // Apply/OK must be enabled } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java index 2b3e2c2..96add71 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/AdtStartup.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.welcome; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler; import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; import com.android.sdkstats.DdmsPreferenceStore; import com.android.sdkstats.SdkStatsService; @@ -83,12 +84,16 @@ public class AdtStartup implements IStartup { boolean ok = AdtPlugin.getDefault().checkSdkLocationAndId(osSdkPath, new AdtPlugin.CheckSdkErrorHandler() { @Override - public boolean handleError(String message) { + public boolean handleError( + CheckSdkErrorHandler.Solution solution, + String message) { return false; } @Override - public boolean handleWarning(String message) { + public boolean handleWarning( + CheckSdkErrorHandler.Solution solution, + String message) { return true; } }); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java index d418432..4e580fe 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/welcome/WelcomeWizardPage.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.adt.internal.welcome; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AdtPlugin.CheckSdkErrorHandler; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.wizard.WizardPage; @@ -262,14 +263,18 @@ public class WelcomeWizardPage extends WizardPage implements ModifyListener, Sel AdtPlugin.getDefault().checkSdkLocationAndId(path, new AdtPlugin.CheckSdkErrorHandler() { @Override - public boolean handleError(String message) { + public boolean handleError( + CheckSdkErrorHandler.Solution solution, + String message) { message = message.replaceAll("\n", " "); //$NON-NLS-1$ //$NON-NLS-2$ errorReference.set(message); return false; // Apply/OK must be disabled } @Override - public boolean handleWarning(String message) { + public boolean handleWarning( + CheckSdkErrorHandler.Solution solution, + String message) { message = message.replaceAll("\n", " "); //$NON-NLS-1$ //$NON-NLS-2$ warningReference.set(message); return true; // Apply/OK must be enabled diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java index 4ce8230..8fcf902 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/export/ExportWizard.java @@ -24,6 +24,7 @@ import com.android.sdklib.internal.build.DebugKeyProvider.IKeyGenOutput; import com.android.sdklib.internal.build.KeystoreHelper; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; +import com.android.sdklib.util.GrabProcessOutput.Wait; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; @@ -543,7 +544,7 @@ public final class ExportWizard extends Wizard implements IExportWizard { int status = GrabProcessOutput.grabProcessOutput( process, - true /*waitForReaders*/, + Wait.WAIT_FOR_READERS, new IProcessOutput() { @Override public void out(String line) { diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java index 436f2e8..db3cc33 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/avd/AvdManager.java @@ -28,6 +28,7 @@ import com.android.sdklib.internal.avd.AvdInfo.AvdStatus; import com.android.sdklib.internal.project.ProjectProperties; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; +import com.android.sdklib.util.GrabProcessOutput.Wait; import com.android.util.Pair; import java.io.File; @@ -1398,7 +1399,7 @@ public class AvdManager { int status = GrabProcessOutput.grabProcessOutput( process, - true /*waitForReaders*/, + Wait.WAIT_FOR_READERS, new IProcessOutput() { @Override public void out(String line) { diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java index 06f5351..af5b401 100644 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/build/KeystoreHelper.java @@ -20,6 +20,7 @@ import com.android.sdklib.internal.build.DebugKeyProvider.IKeyGenOutput; import com.android.sdklib.internal.build.DebugKeyProvider.KeytoolException; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; +import com.android.sdklib.util.GrabProcessOutput.Wait; import java.io.File; import java.io.IOException; @@ -107,7 +108,7 @@ public final class KeystoreHelper { Process process = Runtime.getRuntime().exec(commandArray); result = GrabProcessOutput.grabProcessOutput( process, - true /*waitForReaders*/, + Wait.WAIT_FOR_READERS, new IProcessOutput() { @Override public void out(String line) { diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java index 9e50430..2e2396f 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ArchiveInstaller.java @@ -25,6 +25,7 @@ import com.android.sdklib.io.IFileOp; import com.android.sdklib.repository.RepoConstants; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; +import com.android.sdklib.util.GrabProcessOutput.Wait; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipFile; @@ -598,7 +599,7 @@ public class ArchiveInstaller { Process process = Runtime.getRuntime().exec(command); int retCode = GrabProcessOutput.grabProcessOutput( process, - true /*waitForReaders*/, + Wait.WAIT_FOR_READERS, new IProcessOutput() { @Override public void out(String line) { diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java index ffd561f..3ddacb4 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/internal/repository/ToolPackage.java @@ -25,6 +25,7 @@ import com.android.sdklib.internal.repository.Archive.Os; import com.android.sdklib.repository.SdkRepoConstants; import com.android.sdklib.util.GrabProcessOutput; import com.android.sdklib.util.GrabProcessOutput.IProcessOutput; +import com.android.sdklib.util.GrabProcessOutput.Wait; import org.w3c.dom.Node; @@ -276,7 +277,7 @@ public class ToolPackage extends Package implements IMinPlatformToolsDependency final String tag = scriptName; status = GrabProcessOutput.grabProcessOutput( proc, - false /*waitForReaders*/, + Wait.WAIT_FOR_PROCESS, new IProcessOutput() { @Override public void out(String line) { diff --git a/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java b/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java index b470153..2935493 100755 --- a/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java +++ b/sdkmanager/libs/sdklib/src/com/android/sdklib/util/GrabProcessOutput.java @@ -25,6 +25,35 @@ import java.io.InputStreamReader; public class GrabProcessOutput { + public enum Wait { + /** + * Doesn't wait for the exec to complete. + * This still monitors the output but does not wait for the process to finish. + * In this mode the process return code is unknown and always 0. + */ + ASYNC, + /** + * This waits for the process to finish. + * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the + * error code from the process. + * In some rare cases and depending on the OS, the process might not have + * finished dumping data into stdout/stderr. + *

+ * Use this when you don't particularly care for the output but instead + * care for the return code of the executed process. + */ + WAIT_FOR_PROCESS, + /** + * This waits for the process to finish and for the stdout/stderr + * threads to complete. + * In this mode, {@link GrabProcessOutput#grabProcessOutput} returns the + * error code from the process. + *

+ * Use this one when capturing all the output from the process is important. + */ + WAIT_FOR_READERS, + } + public interface IProcessOutput { /** * Processes an stdout message line. @@ -46,13 +75,13 @@ public class GrabProcessOutput { * @param output Optional object to capture stdout/stderr. * Note that on Windows capturing the output is not optional. If output is null * the stdout/stderr will be captured and discarded. - * @param waitForReaders True to wait for the reader threads to finish. + * @param waitMode Whether to wait for the process and/or the readers to finish. * @return the process return code. * @throws InterruptedException if {@link Process#waitFor()} was interrupted. */ public static int grabProcessOutput( @NonNull final Process process, - boolean waitForReaders, + Wait waitMode, @Nullable final IProcessOutput output) throws InterruptedException { // read the lines as they come. if null is returned, it's // because the process finished @@ -104,10 +133,14 @@ public class GrabProcessOutput { threadErr.start(); threadOut.start(); + if (waitMode == Wait.ASYNC) { + return 0; + } + // it looks like on windows process#waitFor() can return // before the thread have filled the arrays, so we wait for both threads and the // process itself. - if (waitForReaders) { + if (waitMode == Wait.WAIT_FOR_READERS) { try { threadErr.join(); } catch (InterruptedException e) { -- cgit v1.1