OSDN Git Service

original
[gb-231r1-is01/GB_2.3_IS01.git] / sdk / eclipse / plugins / com.android.ide.eclipse.adt / src / com / android / ide / eclipse / adt / internal / launch / LaunchConfigDelegate.java
diff --git a/sdk/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java b/sdk/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java
new file mode 100644 (file)
index 0000000..e747e7e
--- /dev/null
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2007 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.launch;
+
+import com.android.ddmlib.AndroidDebugBridge;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.AndroidConstants;
+import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode;
+import com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper;
+import com.android.ide.eclipse.adt.internal.project.ProjectHelper;
+import com.android.sdklib.xml.ManifestData;
+import com.android.sdklib.xml.ManifestData.Activity;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.IncrementalProjectBuilder;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.core.ILaunch;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.model.LaunchConfigurationDelegate;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
+
+/**
+ * Implementation of an eclipse LauncConfigurationDelegate to launch android
+ * application in debug.
+ */
+public class LaunchConfigDelegate extends LaunchConfigurationDelegate {
+    final static int INVALID_DEBUG_PORT = -1;
+
+    public final static String ANDROID_LAUNCH_TYPE_ID =
+        "com.android.ide.eclipse.adt.debug.LaunchConfigType"; //$NON-NLS-1$
+
+    /** Target mode parameters: true is automatic, false is manual */
+    public static final String ATTR_TARGET_MODE = AdtPlugin.PLUGIN_ID + ".target"; //$NON-NLS-1$
+    public static final TargetMode DEFAULT_TARGET_MODE = TargetMode.AUTO;
+
+    /**
+     * Launch action:
+     * <ul>
+     * <li>0: launch default activity</li>
+     * <li>1: launch specified activity. See {@link #ATTR_ACTIVITY}</li>
+     * <li>2: Do Nothing</li>
+     * </ul>
+     */
+    public final static String ATTR_LAUNCH_ACTION = AdtPlugin.PLUGIN_ID + ".action"; //$NON-NLS-1$
+
+    /** Default launch action. This launches the activity that is setup to be found in the HOME
+     * screen.
+     */
+    public final static int ACTION_DEFAULT = 0;
+    /** Launch action starting a specific activity. */
+    public final static int ACTION_ACTIVITY = 1;
+    /** Launch action that does nothing. */
+    public final static int ACTION_DO_NOTHING = 2;
+    /** Default launch action value. */
+    public final static int DEFAULT_LAUNCH_ACTION = ACTION_DEFAULT;
+
+    /**
+     * Activity to be launched if {@link #ATTR_LAUNCH_ACTION} is 1
+     */
+    public static final String ATTR_ACTIVITY = AdtPlugin.PLUGIN_ID + ".activity"; //$NON-NLS-1$
+
+    public static final String ATTR_AVD_NAME = AdtPlugin.PLUGIN_ID + ".avd"; //$NON-NLS-1$
+
+    public static final String ATTR_SPEED = AdtPlugin.PLUGIN_ID + ".speed"; //$NON-NLS-1$
+
+    /**
+     * Index of the default network speed setting for the emulator.<br>
+     * Get the emulator option with <code>EmulatorConfigTab.getSpeed(index)</code>
+     */
+    public static final int DEFAULT_SPEED = 0;
+
+    public static final String ATTR_DELAY = AdtPlugin.PLUGIN_ID + ".delay"; //$NON-NLS-1$
+
+    /**
+     * Index of the default network latency setting for the emulator.<br>
+     * Get the emulator option with <code>EmulatorConfigTab.getDelay(index)</code>
+     */
+    public static final int DEFAULT_DELAY = 0;
+
+    public static final String ATTR_COMMANDLINE = AdtPlugin.PLUGIN_ID + ".commandline"; //$NON-NLS-1$
+
+    public static final String ATTR_WIPE_DATA = AdtPlugin.PLUGIN_ID + ".wipedata"; //$NON-NLS-1$
+    public static final boolean DEFAULT_WIPE_DATA = false;
+
+    public static final String ATTR_NO_BOOT_ANIM = AdtPlugin.PLUGIN_ID + ".nobootanim"; //$NON-NLS-1$
+    public static final boolean DEFAULT_NO_BOOT_ANIM = false;
+
+    public static final String ATTR_DEBUG_PORT =
+        AdtPlugin.PLUGIN_ID + ".debugPort"; //$NON-NLS-1$
+
+    public void launch(ILaunchConfiguration configuration, String mode,
+            ILaunch launch, IProgressMonitor monitor) throws CoreException {
+        // We need to check if it's a standard launch or if it's a launch
+        // to debug an application already running.
+        int debugPort = AndroidLaunchController.getPortForConfig(configuration);
+
+        // get the project
+        IProject project = getProject(configuration);
+
+        // first we make sure the launch is of the proper type
+        AndroidLaunch androidLaunch = null;
+        if (launch instanceof AndroidLaunch) {
+            androidLaunch = (AndroidLaunch)launch;
+        } else {
+            // wrong type, not sure how we got there, but we don't do
+            // anything else
+            AdtPlugin.printErrorToConsole(project, "Wrong Launch Type!");
+            return;
+        }
+
+        // if we have a valid debug port, this means we're debugging an app
+        // that's already launched.
+        if (debugPort != INVALID_DEBUG_PORT) {
+            AndroidLaunchController.launchRemoteDebugger(debugPort, androidLaunch, monitor);
+            return;
+        }
+
+        if (project == null) {
+            AdtPlugin.printErrorToConsole("Couldn't get project object!");
+            androidLaunch.stopLaunch();
+            return;
+        }
+
+        // make sure the project is built. This is a synchronous call which returns when the
+        // build is done.
+        project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, monitor);
+
+        // check if the project has errors, and abort in this case.
+        if (ProjectHelper.hasError(project, true)) {
+            AdtPlugin.displayError("Android Launch",
+                    "Your project contains error(s), please fix them before running your application.");
+            return;
+        }
+
+        AdtPlugin.printToConsole(project, "------------------------------"); //$NON-NLS-1$
+        AdtPlugin.printToConsole(project, "Android Launch!");
+
+        // check if the project is using the proper sdk.
+        // if that throws an exception, we simply let it propagate to the caller.
+        if (checkAndroidProject(project) == false) {
+            AdtPlugin.printErrorToConsole(project, "Project is not an Android Project. Aborting!");
+            androidLaunch.stopLaunch();
+            return;
+        }
+
+        // Check adb status and abort if needed.
+        AndroidDebugBridge bridge = AndroidDebugBridge.getBridge();
+        if (bridge == null || bridge.isConnected() == false) {
+            try {
+                int connections = -1;
+                int restarts = -1;
+                if (bridge != null) {
+                    connections = bridge.getConnectionAttemptCount();
+                    restarts = bridge.getRestartAttemptCount();
+                }
+
+                // if we get -1, the device monitor is not even setup (anymore?).
+                // We need to ask the user to restart eclipse.
+                // This shouldn't happen, but it's better to let the user know in case it does.
+                if (connections == -1 || restarts == -1) {
+                    AdtPlugin.printErrorToConsole(project,
+                            "The connection to adb is down, and a severe error has occured.",
+                            "You must restart adb and Eclipse.",
+                            String.format(
+                                    "Please ensure that adb is correctly located at '%1$s' and can be executed.",
+                                    AdtPlugin.getOsAbsoluteAdb()));
+                    return;
+                }
+
+                if (restarts == 0) {
+                    AdtPlugin.printErrorToConsole(project,
+                            "Connection with adb was interrupted.",
+                            String.format("%1$s attempts have been made to reconnect.", connections),
+                            "You may want to manually restart adb from the Devices view.");
+                } else {
+                    AdtPlugin.printErrorToConsole(project,
+                            "Connection with adb was interrupted, and attempts to reconnect have failed.",
+                            String.format("%1$s attempts have been made to restart adb.", restarts),
+                            "You may want to manually restart adb from the Devices view.");
+
+                }
+                return;
+            } finally {
+                androidLaunch.stopLaunch();
+            }
+        }
+
+        // since adb is working, we let the user know
+        // TODO have a verbose mode for launch with more info (or some of the less useful info we now have).
+        AdtPlugin.printToConsole(project, "adb is running normally.");
+
+        // make a config class
+        AndroidLaunchConfiguration config = new AndroidLaunchConfiguration();
+
+        // fill it with the config coming from the ILaunchConfiguration object
+        config.set(configuration);
+
+        // get the launch controller singleton
+        AndroidLaunchController controller = AndroidLaunchController.getInstance();
+
+        // get the application package
+        IFile applicationPackage = ProjectHelper.getApplicationPackage(project);
+        if (applicationPackage == null) {
+            androidLaunch.stopLaunch();
+            return;
+        }
+
+        // we need some information from the manifest
+        ManifestData manifestData = AndroidManifestHelper.parseForData(project);
+
+        if (manifestData == null) {
+            AdtPlugin.printErrorToConsole(project, "Failed to parse AndroidManifest: aborting!");
+            androidLaunch.stopLaunch();
+            return;
+        }
+
+        doLaunch(configuration, mode, monitor, project, androidLaunch, config, controller,
+                applicationPackage, manifestData);
+    }
+
+    protected void doLaunch(ILaunchConfiguration configuration, String mode,
+            IProgressMonitor monitor, IProject project, AndroidLaunch androidLaunch,
+            AndroidLaunchConfiguration config, AndroidLaunchController controller,
+            IFile applicationPackage, ManifestData manifestData) {
+
+       String activityName = null;
+
+        if (config.mLaunchAction == ACTION_ACTIVITY) {
+            // Get the activity name defined in the config
+            activityName = getActivityName(configuration);
+
+            // Get the full activity list and make sure the one we got matches.
+            Activity[] activities = manifestData.getActivities();
+
+            // first we check that there are, in fact, activities.
+            if (activities.length == 0) {
+                // if the activities list is null, then the manifest is empty
+                // and we can't launch the app. We'll revert to a sync-only launch
+                AdtPlugin.printErrorToConsole(project,
+                        "The Manifest defines no activity!",
+                        "The launch will only sync the application package on the device!");
+                config.mLaunchAction = ACTION_DO_NOTHING;
+            } else if (activityName == null) {
+                // if the activity we got is null, we look for the default one.
+                AdtPlugin.printErrorToConsole(project,
+                        "No activity specified! Getting the launcher activity.");
+                Activity launcherActivity = manifestData.getLauncherActivity();
+                if (launcherActivity != null) {
+                    activityName = launcherActivity.getName();
+                }
+
+                // if there's no default activity. We revert to a sync-only launch.
+                if (activityName == null) {
+                    revertToNoActionLaunch(project, config);
+                }
+            } else {
+
+                // check the one we got from the config matches any from the list
+                boolean match = false;
+                for (Activity a : activities) {
+                    if (a != null && a.getName().equals(activityName)) {
+                        match = true;
+                        break;
+                    }
+                }
+
+                // if we didn't find a match, we revert to the default activity if any.
+                if (match == false) {
+                    AdtPlugin.printErrorToConsole(project,
+                            "The specified activity does not exist! Getting the launcher activity.");
+                    Activity launcherActivity = manifestData.getLauncherActivity();
+                    if (launcherActivity != null) {
+                        activityName = launcherActivity.getName();
+                    } else {
+                        // if there's no default activity. We revert to a sync-only launch.
+                        revertToNoActionLaunch(project, config);
+                    }
+                }
+            }
+        } else if (config.mLaunchAction == ACTION_DEFAULT) {
+            Activity launcherActivity = manifestData.getLauncherActivity();
+            if (launcherActivity != null) {
+                activityName = launcherActivity.getName();
+            }
+
+            // if there's no default activity. We revert to a sync-only launch.
+            if (activityName == null) {
+                revertToNoActionLaunch(project, config);
+            }
+        }
+
+        IAndroidLaunchAction launchAction = null;
+        if (config.mLaunchAction == ACTION_DO_NOTHING || activityName == null) {
+            launchAction = new EmptyLaunchAction();
+        } else {
+            launchAction = new ActivityLaunchAction(activityName, controller);
+        }
+
+        // everything seems fine, we ask the launch controller to handle
+        // the rest
+        controller.launch(project, mode, applicationPackage,manifestData.getPackage(),
+                manifestData.getPackage(), manifestData.getDebuggable(),
+                manifestData.getMinSdkVersionString(), launchAction, config, androidLaunch,
+                monitor);
+    }
+
+    @Override
+    public boolean buildForLaunch(ILaunchConfiguration configuration,
+            String mode, IProgressMonitor monitor) throws CoreException {
+        // if this returns true, this forces a full workspace rebuild which is not
+        // what we want.
+        // Instead in the #launch method, we'll rebuild only the launching project.
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @throws CoreException
+     */
+    @Override
+    public ILaunch getLaunch(ILaunchConfiguration configuration, String mode)
+            throws CoreException {
+        return new AndroidLaunch(configuration, mode, null);
+    }
+
+    /**
+     * Returns the IProject object matching the name found in the configuration
+     * object under the name
+     * <code>IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME</code>
+     * @param configuration
+     * @return The IProject object or null
+     */
+    private IProject getProject(ILaunchConfiguration configuration){
+        // get the project name from the config
+        String projectName;
+        try {
+            projectName = configuration.getAttribute(
+                    IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, "");
+        } catch (CoreException e) {
+            return null;
+        }
+
+        // get the current workspace
+        IWorkspace workspace = ResourcesPlugin.getWorkspace();
+
+        // and return the project with the name from the config
+        return workspace.getRoot().getProject(projectName);
+    }
+
+    /**
+     * Checks the project is an android project.
+     * @param project The project to check
+     * @return true if the project is an android SDK.
+     * @throws CoreException
+     */
+    private boolean checkAndroidProject(IProject project) throws CoreException {
+        // check if the project is a java and an android project.
+        if (project.hasNature(JavaCore.NATURE_ID) == false) {
+            String msg = String.format("%1$s is not a Java project!", project.getName());
+            AdtPlugin.displayError("Android Launch", msg);
+            return false;
+        }
+
+        if (project.hasNature(AndroidConstants.NATURE_DEFAULT) == false) {
+            String msg = String.format("%1$s is not an Android project!", project.getName());
+            AdtPlugin.displayError("Android Launch", msg);
+            return false;
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Returns the name of the activity.
+     */
+    private String getActivityName(ILaunchConfiguration configuration) {
+        String empty = "";
+        String activityName;
+        try {
+            activityName = configuration.getAttribute(ATTR_ACTIVITY, empty);
+        } catch (CoreException e) {
+            return null;
+        }
+
+        return (activityName != empty) ? activityName : null;
+    }
+
+    private final void revertToNoActionLaunch(IProject project, AndroidLaunchConfiguration config) {
+        AdtPlugin.printErrorToConsole(project,
+                "No Launcher activity found!",
+                "The launch will only sync the application package on the device!");
+        config.mLaunchAction = ACTION_DO_NOTHING;
+    }
+}