aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java354
1 files changed, 354 insertions, 0 deletions
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
new file mode 100644
index 000000000..48667bea0
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/actions/SdkManagerAction.java
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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.android.ide.eclipse.adt.internal.actions;
+
+import com.android.SdkConstants;
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
+import com.android.ide.eclipse.adt.internal.sdk.AdtConsoleSdkLog;
+import com.android.ide.eclipse.adt.internal.sdk.Sdk;
+import com.android.sdklib.io.FileOp;
+import com.android.sdklib.repository.ISdkChangeListener;
+import com.android.utils.GrabProcessOutput;
+import com.android.utils.GrabProcessOutput.IProcessOutput;
+import com.android.utils.GrabProcessOutput.Wait;
+import com.android.sdkuilib.repository.SdkUpdaterWindow;
+import com.android.sdkuilib.repository.SdkUpdaterWindow.SdkInvocationContext;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.action.IAction;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.ProgressMonitorDialog;
+import org.eclipse.jface.operation.IRunnableWithProgress;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IObjectActionDelegate;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchWindow;
+import org.eclipse.ui.IWorkbenchWindowActionDelegate;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Delegate for the toolbar/menu action "Android SDK Manager".
+ * It displays the Android SDK Manager.
+ */
+public class SdkManagerAction implements IWorkbenchWindowActionDelegate, IObjectActionDelegate {
+
+ @Override
+ public void dispose() {
+ // nothing to dispose.
+ }
+
+ @Override
+ public void init(IWorkbenchWindow window) {
+ // no init
+ }
+
+ @Override
+ public void run(IAction action) {
+ // Although orthogonal to the sdk manager action, this is a good time
+ // to check whether the SDK has changed on disk.
+ AdtPlugin.getDefault().refreshSdk();
+
+ if (!openExternalSdkManager()) {
+ // If we failed to execute the sdk manager, check the SDK location.
+ // If it's not properly set, the check will display a dialog to state
+ // so to the user and a link to the prefs.
+ // Here's it's ok to call checkSdkLocationAndId() since it will not try
+ // to run the SdkManagerAction (it might run openExternalSdkManager though.)
+ // If checkSdkLocationAndId tries to open the SDK Manager, it end up using
+ // the internal one.
+ if (AdtPlugin.getDefault().checkSdkLocationAndId()) {
+ // The SDK check was successful, yet the sdk manager fail to launch anyway.
+ AdtPlugin.displayError(
+ "Android SDK",
+ "Failed to run the Android SDK Manager. Check the Android Console View for details.");
+ }
+ }
+ }
+
+ /**
+ * A custom implementation of {@link ProgressMonitorDialog} that allows us
+ * to rename the "Cancel" button to "Close" from the internal task.
+ */
+ private static class CloseableProgressMonitorDialog extends ProgressMonitorDialog {
+
+ public CloseableProgressMonitorDialog(Shell parent) {
+ super(parent);
+ }
+
+ public void changeCancelToClose() {
+ if (cancel != null && !cancel.isDisposed()) {
+ Display display = getShell() == null ? null : getShell().getDisplay();
+ if (display != null) {
+ display.syncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (cancel != null && !cancel.isDisposed()) {
+ cancel.setText(IDialogConstants.CLOSE_LABEL);
+ }
+ }
+ });
+ }
+ }
+ }
+ }
+
+ /**
+ * Opens the SDK Manager as an external application.
+ * This call is asynchronous, it doesn't wait for the manager to be closed.
+ * <p/>
+ * Important: this method must NOT invoke {@link AdtPlugin#checkSdkLocationAndId}
+ * (in any of its variations) since the dialog uses this method to invoke the sdk
+ * manager if needed.
+ *
+ * @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() {
+
+ // On windows this takes a couple seconds and it's not clear the launch action
+ // has been invoked. To prevent the user from randomly clicking the "open sdk manager"
+ // button multiple times, show a progress window that will automatically close
+ // after a couple seconds.
+
+ // By default openExternalSdkManager will return false.
+ final AtomicBoolean returnValue = new AtomicBoolean(false);
+
+ final CloseableProgressMonitorDialog p =
+ new CloseableProgressMonitorDialog(AdtPlugin.getShell());
+ p.setOpenOnRun(true);
+ try {
+ p.run(true /*fork*/, true /*cancelable*/, new IRunnableWithProgress() {
+ @Override
+ public void run(IProgressMonitor monitor)
+ throws InvocationTargetException, InterruptedException {
+
+ // Get the SDK locatiom from the current SDK or as fallback
+ // directly from the ADT preferences.
+ Sdk sdk = Sdk.getCurrent();
+ String osSdkLocation = sdk == null ? null : sdk.getSdkOsLocation();
+ if (osSdkLocation == null || !new File(osSdkLocation).isDirectory()) {
+ osSdkLocation = AdtPrefs.getPrefs().getOsSdkFolder();
+ }
+
+ // If there's no SDK location or it's not a valid directory,
+ // there's nothing we can do. When this is invoked from run()
+ // the checkSdkLocationAndId method call should display a dialog
+ // telling the user to configure the preferences.
+ if (osSdkLocation == null || !new File(osSdkLocation).isDirectory()) {
+ return;
+ }
+
+ final int numIter = 30; //30*100=3s to wait for window
+ final int sleepMs = 100;
+ monitor.beginTask("Starting Android SDK Manager", numIter);
+
+ File androidBat = FileOp.append(
+ osSdkLocation,
+ SdkConstants.FD_TOOLS,
+ SdkConstants.androidCmdName());
+
+ if (!androidBat.exists()) {
+ AdtPlugin.printErrorToConsole("SDK Manager",
+ "Missing %s file in Android SDK.", SdkConstants.androidCmdName());
+ return;
+ }
+
+ if (monitor.isCanceled()) {
+ // Canceled by user; return true as if it succeeded.
+ returnValue.set(true);
+ return;
+ }
+
+ p.changeCancelToClose();
+
+ try {
+ final AdtConsoleSdkLog logger = new AdtConsoleSdkLog();
+
+ 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(@Nullable String line) {
+ // Ignore stdout
+ }
+
+ @Override
+ public void err(@Nullable String line) {
+ if (line != null) {
+ logger.info("[SDK Manager] %s", line);
+ }
+ }
+ });
+
+ // Set openExternalSdkManager to return true.
+ returnValue.set(true);
+ } catch (Exception ignore) {
+ }
+
+ // This small wait prevents the progress dialog from closing too fast.
+ for (int i = 0; i < numIter; i++) {
+ if (monitor.isCanceled()) {
+ // Canceled by user; return true as if it succeeded.
+ returnValue.set(true);
+ return;
+ }
+ if (i == 10) {
+ monitor.subTask("Initializing... SDK Manager will show up shortly.");
+ }
+ try {
+ Thread.sleep(sleepMs);
+ monitor.worked(1);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+
+ monitor.done();
+ }
+ });
+ } catch (Exception e) {
+ AdtPlugin.log(e, "SDK Manager exec failed"); //$NON-NLS-1#
+ return false;
+ }
+
+ return returnValue.get();
+ }
+
+ /**
+ * 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.getShell(),
+ new AdtConsoleSdkLog() {
+ @Override
+ public void info(@NonNull String msgFormat, Object... args) {
+ // Do not show non-error/warning log in Eclipse.
+ };
+ @Override
+ public void verbose(@NonNull String msgFormat, Object... args) {
+ // Do not show non-error/warning log in Eclipse.
+ };
+ },
+ sdk.getSdkOsLocation(),
+ 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.
+ * <p/>
+ * {@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*/);
+ sdk.unloadDexWrappers();
+ }
+ }
+
+ /**
+ * Nothing to do. We'll reparse the SDK later in onSdkReload.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public void postInstallHook() {
+ }
+
+ /**
+ * Reparse the SDK in case anything was add/removed.
+ * <p/>
+ * {@inheritDoc}
+ */
+ @Override
+ public void onSdkReload() {
+ AdtPlugin.getDefault().reparseSdk();
+ }
+ };
+
+ window.addListener(listener);
+ window.open();
+
+ return true;
+ }
+
+ @Override
+ public void selectionChanged(IAction action, ISelection selection) {
+ // nothing related to the current selection.
+ }
+
+ @Override
+ public void setActivePart(IAction action, IWorkbenchPart targetPart) {
+ // nothing to do.
+ }
+}