aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java424
1 files changed, 424 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java
new file mode 100644
index 000000000..86fc2ffae
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/launch/LaunchConfigDelegate.java
@@ -0,0 +1,424 @@
+/*
+ * 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.common.xml.ManifestData;
+import com.android.ide.common.xml.ManifestData.Activity;
+import com.android.ide.eclipse.adt.AdtConstants;
+import com.android.ide.eclipse.adt.AdtPlugin;
+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 org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspace;
+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;
+
+ /** Flag indicating whether the last used device should be used for future launches. */
+ public static final String ATTR_REUSE_LAST_USED_DEVICE =
+ AdtPlugin.PLUGIN_ID + ".reuse.last.used.device"; //$NON-NLS-1$
+
+ /** Device on which the last launch happened. */
+ public static final String ATTR_LAST_USED_DEVICE =
+ AdtPlugin.PLUGIN_ID + ".last.used.device"; //$NON-NLS-1$
+
+ /**
+ * 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$
+
+ @Override
+ 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 and its dependencies are built
+ // and PostCompilerBuilder runs.
+ // This is a synchronous call which returns when the
+ // build is done.
+ ProjectHelper.doFullIncrementalDebugBuild(project, 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(AdtConstants.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;
+ }
+}