diff options
Diffstat (limited to 'src/plugins/launch/src/com/motorola/studio')
12 files changed, 3763 insertions, 0 deletions
diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ILaunchConfigurationConstants.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ILaunchConfigurationConstants.java new file mode 100644 index 0000000..58bbdbf --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ILaunchConfigurationConstants.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch; + +import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; + +import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchConfiguration.TargetMode; +import com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate; + +/** + * This interface holds the constants for Launch Configuration + */ +@SuppressWarnings("restriction") +public interface ILaunchConfigurationConstants +{ + + /** + * Launch configuration id + */ + public final static String LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID = + "androidLaunchConfigurationType"; + + public final static String MOTODEV_APP_ICO = "icons/motodevapp.gif"; + + public final static String DEFAULT_VALUE = ""; + + public final static boolean DEFAULT_BOOL_VALUE = false; + + /** + * Launch Configuration attribute ID: Project Name + */ + public final static String ATTR_PROJECT_NAME = + IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME; + + /** + * Launch Configuration attribute ID : Terminate Boolean If true, the VM + * supports terminate action. + */ + public final static String ATTR_ALLOW_TERMINATE = + IJavaLaunchConfigurationConstants.ATTR_ALLOW_TERMINATE; + + public final static boolean ATTR_ALLOW_TERMINATE_DEFAULT = true; + + /** + * Launch Configuration attribute ID : Launch Action. The type of launch to + * be performed. + * 0: launch default activity. + * 1: launch specified activity. + * 2: Do Nothing + * + * Should always be 'activity' for now. + */ + public final static String ATTR_LAUNCH_ACTION = LaunchConfigDelegate.ATTR_LAUNCH_ACTION; + + public final static int ATTR_LAUNCH_ACTION_DEFAULT = LaunchConfigDelegate.ACTION_DEFAULT; + + public final static int ATTR_LAUNCH_ACTION_DO_NOTHING = LaunchConfigDelegate.ACTION_DO_NOTHING; + + public final static int ATTR_LAUNCH_ACTION_ACTIVITY = LaunchConfigDelegate.ACTION_ACTIVITY; + + /** + * Launch Configuration attribute ID: Activity Name + */ + public final static String ATTR_ACTIVITY = LaunchConfigDelegate.ATTR_ACTIVITY; + + /** + * Launch Configuration attribute ID: Target Mode + * True: Automatic + * False: Manual + */ + public final static String ATTR_TARGET_MODE = LaunchConfigDelegate.ATTR_TARGET_MODE; + + public final static TargetMode ATTR_TARGET_MODE_DEFAULT = + LaunchConfigDelegate.DEFAULT_TARGET_MODE; + + /** + * This is the attribute we use to store the name of the device. We could use ADT's directly if we were + * not forced to remove ADT's entry for it to work with handsets. If we don't store in our own key, the + * device name not to be restored the next time the user opens the Run As window, which is against + * Eclipse standards. + */ + public final static String ATTR_DEVICE_INSTANCE_NAME = + "com.motorola.studio.android.launch.instanceName"; + + /** + * Launch Configuration attribute ID: Instance Name (VM Name for ADT) + */ + public final static String ATTR_ADT_DEVICE_INSTANCE_NAME = LaunchConfigDelegate.ATTR_AVD_NAME; + + /** + * Launch Configuration attribute ID: Emulator Network Speed + * + * Default value is 0. + */ + public final static String ATTR_SPEED = LaunchConfigDelegate.ATTR_SPEED; + + public final static int ATTR_SPEED_DEFAULT = LaunchConfigDelegate.DEFAULT_SPEED; + + /** + * Launch Configuration attribute ID: Emulator Network Latency + */ + public final static String ATTR_DELAY = LaunchConfigDelegate.ATTR_DELAY; + + public final static int ATTR_DELAY_DEFAULT = LaunchConfigDelegate.DEFAULT_DELAY; + + /** + * Launch Configuration attribute ID: Wipe Data + * + * Default value is FALSE. + * + */ + public final static String ATTR_WIPE_DATA = LaunchConfigDelegate.ATTR_WIPE_DATA; + + public final static boolean ATTR_WIPE_DATA_DEFAULT = LaunchConfigDelegate.DEFAULT_WIPE_DATA; + + /** + * Launch Configuration attribute ID: Boot Animation + * + * Default value is FALSE. + */ + public final static String ATTR_NO_BOOT_ANIM = LaunchConfigDelegate.ATTR_NO_BOOT_ANIM; + + public final static boolean ATTR_NO_BOOT_ANIM_DEFAULT = + LaunchConfigDelegate.DEFAULT_NO_BOOT_ANIM; + + /** + * Launch Configuration attribute ID: Command Line + * + * Additional command line options. Default value is empty. + */ + public final static String ATTR_COMMANDLINE = LaunchConfigDelegate.ATTR_COMMANDLINE; + + /* + * Console View ID + */ + public final static String ANDROID_CONSOLE_ID = "Android"; +} diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchConfigurationShortcut.java b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchConfigurationShortcut.java new file mode 100644 index 0000000..b849a0f --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchConfigurationShortcut.java @@ -0,0 +1,408 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.ui.ILaunchShortcut; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IEditorPart; + +import com.motorola.studio.android.adt.ISerialNumbered; +import com.motorola.studio.android.adt.SdkUtils; +import com.motorola.studio.android.common.log.StudioLogger; +import com.motorola.studio.android.devices.DevicesManager; +import com.motorola.studio.android.launch.i18n.LaunchNLS; + +public class LaunchConfigurationShortcut implements ILaunchShortcut +{ + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchShortcut#launch(org.eclipse.jface.viewers.ISelection, java.lang.String) + */ + public void launch(ISelection selection, String mode) + { + ILaunchConfiguration launchConfiguration = + getLaunchConfigurationForSelection(selection, true); + handleLaunch(mode, launchConfiguration); + + } + + /* (non-Javadoc) + * @see org.eclipse.debug.ui.ILaunchShortcut#launch(org.eclipse.ui.IEditorPart, java.lang.String) + */ + public void launch(IEditorPart editor, String mode) + { + IResource resource = (IResource) editor.getEditorInput().getAdapter(IResource.class); + if (resource != null) + { + ILaunchConfiguration launchConfiguration = + getLaunchConfigurationForResource(resource, true); + handleLaunch(mode, launchConfiguration); + } + } + + private void handleLaunch(String mode, ILaunchConfiguration launchConfiguration) + { + if (launchConfiguration != null) + { + final ILaunchConfiguration config = launchConfiguration; + final String launchMode = mode; + + Job job = new Job("Launch Job") + { + + @Override + protected IStatus run(IProgressMonitor monitor) + { + IStatus status = Status.OK_STATUS; + try + { + config.launch(launchMode, monitor); + } + catch (CoreException e) + { + status = + new Status( + IStatus.ERROR, + LaunchPlugin.PLUGIN_ID, + LaunchNLS.ERR_LaunchConfigurationShortcut_CannotLaunchSelectedResourceMsg, + e); + } + return status; + } + }; + + job.schedule(); + } + else + { + LaunchUtils.showErrorDialog(LaunchNLS.ERR_LaunchConfigurationShortcut_MsgTitle, + LaunchNLS.ERR_LaunchConfigurationShortcut_CannotLaunchSelectedResourceMsg); + } + } + + /** + * Gets a launch configuration for a desired selection + * + * @param selection The selection + * @param create If the launch configuration does not exist, does it must be created? + * + * @return The launch configuration for the selection + */ + + private ILaunchConfiguration getLaunchConfigurationForSelection(ISelection selection, + boolean create) + { + ILaunchConfiguration config = null; + IStructuredSelection newSelection; + Object selectedObject; + IResource selectedResource = null; + + if (selection instanceof IStructuredSelection) + { + newSelection = (IStructuredSelection) selection; + selectedObject = newSelection.getFirstElement(); + + if (selectedObject instanceof IResource) + { + selectedResource = (IResource) selectedObject; + } + else if (selectedObject instanceof IJavaElement) + { + selectedResource = ((IJavaElement) selectedObject).getResource(); + } + + if (selectedResource != null) + { + config = getLaunchConfigurationForResource(selectedResource, create); + } + } + + return config; + } + + /** + * Gets a launch configuration for a resource + * + * @param resource The resource + * @param create If the launch configuration does not exist, does it must be created? + * + * @return The launch configuration for the resource + */ + private ILaunchConfiguration getLaunchConfigurationForResource(IResource resource, + boolean create) + { + IResource app; + IResource project; + ILaunchConfiguration config = null; + + if (resource != null) + { + if (resource.getType() == IResource.PROJECT) + { + project = resource; + } + else + { + project = resource.getProject(); + } + // Try to retrieve an existent launch configuration + config = findLaunchConfiguration(project); + + if ((config == null) && create) + { + // No launch configuration could be found. Try to create a + // launch configuration with the first runnable activity + app = getFirstActivity((IProject) project); + + // If no application could be found, use the project + // to create the launch configuration + app = app == null ? resource : app; + config = createLaunchConfiguration(app); + } + + } + + return config; + } + + /** + * Finds a launch configuration for a descriptor, a mpkg file or a project + * + * @param resource A descriptor, a mpkg file or a project + * + * @return A launch configuration or null if it could not be found + */ + private ILaunchConfiguration findLaunchConfiguration(IResource resource) + { + ILaunchConfiguration launchConfig = null; + + if (resource != null) + { + try + { + List<ILaunchConfiguration> projectLC = + getProjectLaunchConfigurations(resource.getProject()); + + if ((resource.getType() == IResource.PROJECT) + || (resource.getType() == IResource.FILE)) + { + // If the resource is a project, return the first launch configuration found + // for the project + if (!projectLC.isEmpty()) + { + launchConfig = projectLC.iterator().next(); + } + } + } + catch (CoreException e) + { + StudioLogger.error( + LaunchConfigurationShortcut.class, + "Error searching for launch configuration for resource: " + + resource.getName(), e); + } + } + + return launchConfig; + } + + /** + * Scan for all LaunchConfigurations associated with a project. + * @param selectedResource, the project itself or any file within the project to be scanned + * @return List with all LaunchConfiguration associated with a project or an empty List if none is found. + * @throws CoreException + */ + protected List<ILaunchConfiguration> getProjectLaunchConfigurations(IProject project) + throws CoreException + { + List<ILaunchConfiguration> matches; + + ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); + ILaunchConfigurationType motodevLaunchType = + launchManager + .getLaunchConfigurationType(ILaunchConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID); + + ILaunchConfiguration[] motodevLaunchConfigurations = + launchManager.getLaunchConfigurations(motodevLaunchType); + matches = new ArrayList<ILaunchConfiguration>(motodevLaunchConfigurations.length); + for (ILaunchConfiguration launchConfiguration : motodevLaunchConfigurations) + { + if (launchConfiguration.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, + "").equals(project.getName())) //$NON-NLS-1$ + { + matches.add(launchConfiguration); + } + } + + return matches; + } + + /** + * Gets the first runnable application/widget for a project. It can be a + * application/widget root folder or a mpkg file + * + * @param project The project + * + * @return The first runnable application/widget or null if it does not exist + */ + private IResource getFirstActivity(IProject project) + { + IResource app = null; + + String[] allActivities = LaunchUtils.getProjectActivities(project); + + if ((allActivities != null) && (allActivities.length >= 1)) + { + app = project.getFile(allActivities[0]); + } + + return app; + } + + /** + * Creates a launch configuration based on a resource + * + * @param resource The resource + * + * @return A launch configuration + */ + private ILaunchConfiguration createLaunchConfiguration(IResource resource) + { + ILaunchConfiguration config = null; + ILaunchManager launchManager = DebugPlugin.getDefault().getLaunchManager(); + ILaunchConfigurationType motodevLaunchType = + launchManager + .getLaunchConfigurationType(ILaunchConfigurationConstants.LAUNCH_CONFIGURATION_TYPE_EXTENSION_ID); + String projectName; + + String configBaseName = resource.getName(); + + String launchConfigurationName = + launchManager.generateLaunchConfigurationName(configBaseName); + try + { + ILaunchConfigurationWorkingCopy workingCopy = + motodevLaunchType.newInstance(null, launchConfigurationName); + + //Set Defaults + workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, + ILaunchConfigurationConstants.DEFAULT_VALUE); + workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY, + ILaunchConfigurationConstants.DEFAULT_VALUE); + // It is default not to exist Preferred AVD attribute, so we just set the Studio's + // device instance name attribute here + workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, + (String) null); + LaunchUtils.setADTLaunchConfigurationDefaults(workingCopy); + + //Launch Settings + IProject project = resource.getProject(); + projectName = project.getName(); + workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, projectName); + + if (resource.getType() != IResource.PROJECT) + { + workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY, + resource.getName()); + } + + String deviceName = getSelectedInstanceName(project); + workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, + deviceName); + // Preferred AVD name shall only exist in the launch configuration if an AVD is selected + Collection<String> validAvds = SdkUtils.getAllValidVmNames(); + if (validAvds.contains(deviceName)) + { + workingCopy.setAttribute( + ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME, deviceName); + } + + if (workingCopy.getAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY, + ILaunchConfigurationConstants.DEFAULT_VALUE).equals("")) + { + workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION, + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT); + } + + config = workingCopy.doSave(); + } + catch (CoreException e) + { + StudioLogger.error(LaunchConfigurationShortcut.class, + "Error creating launch configuration for resource: " + resource.getName(), e); + } + + return config; + } + + /** + * Get a available and compatible instance name. + * This method seeks within all registered instances, following the criteria: + * Phone device with "full" compatibility (API version = project min. API) + * Phone device with "partial" compatibility (API version > project min. API) + * Emulator device with "full" compatibility (API version = project min. API) + * Emulator device with "partial" compatibility (API version = project min. API) + * @param project + */ + protected String getSelectedInstanceName(IProject project) + { + String selectedDevice = ""; + + //get all instances according ddms + Collection<ISerialNumbered> instances = DevicesManager.getInstance().getAllDevicesSorted(); + String candidate = ""; + for (ISerialNumbered instance : instances) + { + + IStatus compatible = LaunchUtils.isCompatible(project, instance); + if (compatible.isOK()) + { + selectedDevice = instance.getDeviceName(); + break; + } + else if (compatible.getSeverity() == IStatus.WARNING) + { + candidate = instance.getDeviceName(); + } + + } + if ((selectedDevice.equals("")) && !candidate.equals("")) + { + selectedDevice = candidate; + } + + return selectedDevice; + } + +} diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchPlugin.java b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchPlugin.java new file mode 100644 index 0000000..106a460 --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchPlugin.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +import com.motorola.studio.android.common.log.StudioLogger; + +/** + * The activator class controls the plug-in life cycle + */ +public class LaunchPlugin extends AbstractUIPlugin +{ + + // The plug-in ID + public static final String PLUGIN_ID = "com.motorola.studio.android.launch"; + + // The shared instance + private static LaunchPlugin plugin; + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext + * ) + */ + @Override + public void start(BundleContext context) throws Exception + { + StudioLogger.debug(LaunchPlugin.class, "Starting MOTODEV Android Launch Plugin..."); + + super.start(context); + plugin = this; + + StudioLogger.debug(LaunchPlugin.class, "MOTODEV Android Launch Plugin started."); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext + * ) + */ + @Override + public void stop(BundleContext context) throws Exception + { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static LaunchPlugin getDefault() + { + return plugin; + } + +} diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchUtils.java b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchUtils.java new file mode 100644 index 0000000..10b7d27 --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/LaunchUtils.java @@ -0,0 +1,515 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.osgi.util.NLS; +import org.eclipse.sequoyah.device.framework.factory.InstanceRegistry; +import org.eclipse.sequoyah.device.framework.model.IInstance; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +import com.android.ide.eclipse.adt.io.IFolderWrapper; +import com.android.sdklib.xml.AndroidManifestParser; +import com.android.sdklib.xml.ManifestData; +import com.android.sdklib.xml.ManifestData.Activity; +import com.motorola.studio.android.AndroidPlugin; +import com.motorola.studio.android.adt.DDMSFacade; +import com.motorola.studio.android.adt.ISerialNumbered; +import com.motorola.studio.android.adt.SdkUtils; +import com.motorola.studio.android.common.log.StudioLogger; +import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager; +import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance; +import com.motorola.studio.android.emulator.logic.IAndroidLogicInstance; +import com.motorola.studio.android.launch.i18n.LaunchNLS; + +/** + * DESCRIPTION: Utilities for Studio for Android Launch use + * + * RESPONSIBILITY: Provide common utility methods that can be used by any Studio + * for Android Launch plugin. + * + * COLABORATORS: None + * + * USAGE: This class should not be instantiated and its methods should be called + * statically. + */ +@SuppressWarnings("restriction") +public class LaunchUtils +{ + /** + * Retrieve a instance by name + * + * @param instanceName + * @return IInstance with the given name or null if none is found, or it's not available. + */ + public static String getSerialNumberForInstance(String instanceName) + { + String serial = null; + List<IInstance> list = InstanceRegistry.getInstance().getInstances(); + for (IInstance inst : list) + { + if ((inst.getName().equals(instanceName)) && (inst instanceof ISerialNumbered)) + { + serial = ((ISerialNumbered) inst).getSerialNumber(); + } + } + return serial; + } + + /** + * Get a project in the current workspace based on its projectName + * + * @param projectName + * @return the IProject representing the project, or null if none is found + */ + public static IProject getProject(String projectName) + { + IProject project = null; + + Path projectPath = new Path(projectName); + if (projectPath.isValidSegment(projectName)) + { + project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + } + + return project; + } + + /** + * Verify if a given project is supported by the Studio for Android + * Launcher, checking if the project is a Android project + * + * @param project + * to be verified + * @return true if project is a an Android project, false otherwise. + */ + public static boolean isProjectSupported(IProject project) + { + boolean hasNature = false; + boolean isLibrary = true; + + if ((project != null) && project.isOpen()) + { + try + { + hasNature = project.hasNature(AndroidPlugin.Android_Nature); + isLibrary = SdkUtils.isLibraryProject(project); + } + catch (CoreException e) + { + // Do nothing + } + } + + return hasNature && !isLibrary; + } + + /** + * Get all Android Projects within the current workspace. + * + * @return IProject array with all Android projects in the current + * workspace, or an empty array if none is found + */ + public static IProject[] getSupportedProjects() + { + Collection<IProject> projectCollection = new ArrayList<IProject>(); + IProject[] projectsName = ResourcesPlugin.getWorkspace().getRoot().getProjects(); + + /* select only Android projects */ + for (IProject project : projectsName) + { + if (project.isAccessible()) + { + if (LaunchUtils.isProjectSupported(project)) + { + projectCollection.add(project); + } + } + } + + return projectCollection.toArray(new IProject[projectCollection.size()]); + } + + /** + * Retrieve the project activities from the MANIFEST.xml file + * + * @param project + * @return An array of activities. + */ + public static String[] getProjectActivities(IProject project) + { + + String[] activities = null; + Activity[] adtActivities = null; + + // parse the manifest for the list of activities. + try + { + ManifestData manifestParser = AndroidManifestParser.parse(new IFolderWrapper(project)); + + if (manifestParser != null) + { + adtActivities = manifestParser.getActivities(); + } + + if ((adtActivities != null) && (adtActivities.length > 0)) + { + activities = new String[adtActivities.length]; + for (int i = 0; i < adtActivities.length; i++) + { + activities[i] = adtActivities[i].getName(); + } + } + + } + catch (Exception e) + { + StudioLogger.error(LaunchUtils.class, + "An error occurred trying to parse AndroidManifest", e); + } + + return activities; + + } + + /** + * Set the default launch configuration values + */ + public static void setADTLaunchConfigurationDefaults( + ILaunchConfigurationWorkingCopy configuration) + { + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_ALLOW_TERMINATE, + ILaunchConfigurationConstants.ATTR_ALLOW_TERMINATE_DEFAULT); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION, + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_TARGET_MODE, + ILaunchConfigurationConstants.ATTR_TARGET_MODE_DEFAULT.toString()); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_SPEED, + ILaunchConfigurationConstants.ATTR_SPEED_DEFAULT); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_DELAY, + ILaunchConfigurationConstants.ATTR_DELAY_DEFAULT); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_WIPE_DATA, + ILaunchConfigurationConstants.ATTR_WIPE_DATA_DEFAULT); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_NO_BOOT_ANIM, + ILaunchConfigurationConstants.ATTR_NO_BOOT_ANIM_DEFAULT); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_COMMANDLINE, + ILaunchConfigurationConstants.DEFAULT_VALUE); + + } + + /** + * Update the launch configuration values + */ + public static void updateLaunchConfigurationDefaults( + ILaunchConfigurationWorkingCopy configuration) + { + try + { + String deviceName = + configuration.getAttribute( + ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, ""); + + if ((deviceName != null) && !deviceName.equals("")) + { + IAndroidEmulatorInstance deviceInstance = + DeviceFrameworkManager.getInstance().getInstanceByName(deviceName); + + if (deviceInstance instanceof IAndroidLogicInstance) + { + String commandLine = + ((IAndroidLogicInstance) deviceInstance).getCommandLineArguments(); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_COMMANDLINE, + commandLine); + } + } + } + catch (CoreException e) + { + StudioLogger.error(LaunchUtils.class, + "Error updating launch configuration values for : " + configuration.getName(), + e); + } + } + + /** + * Get the shell of the active workbench or null if there is no active + * workbench. + * + * @return the active workbench shell + */ + public static Shell getActiveWorkbenchShell() + { + class ActiveShellRunnable implements Runnable + { + private Shell shell = null; + + public Shell getShell() + { + return shell; + } + + public void run() + { + IWorkbenchWindow activeWorkbench = + PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + + if (activeWorkbench != null) + { + shell = activeWorkbench.getShell(); + } + } + } + ; + + ActiveShellRunnable runnable = new ActiveShellRunnable(); + PlatformUI.getWorkbench().getDisplay().syncExec(runnable); + + return runnable.getShell(); + } + + /** + * Show the error message using the given title and message + * + * @param title + * of the error dialog + * @param message + * to be displayed in the error dialog. + */ + public static void showErrorDialog(final String title, final String message) + { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() + { + public void run() + { + IWorkbenchWindow ww = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + MessageDialog.openError(ww.getShell(), title, message); + } + }); + } + + public static ISerialNumbered resolveInstance(List<IInstance> instances) + { + ISerialNumbered theInstance = null; + Iterator<IInstance> it = instances.iterator(); + while (it.hasNext() && (theInstance == null)) + { + IInstance anInstance = it.next(); + if (anInstance instanceof ISerialNumbered) + { + theInstance = (ISerialNumbered) anInstance; + } + } + return theInstance; + } + + /** + * Check if an instanceName is compatible with some project + * @param project + * @param instanceName + * @return {@link IStatus#OK} if fully compatible, {@link IStatus#WARNING} if can be compatible and {@link IStatus#ERROR} if not compatible. Return <code>null</code> if the instance does not exists + */ + + public static IStatus isCompatible(IProject project, String instanceName) + { + IStatus status = null; + List<IInstance> instances = InstanceRegistry.getInstance().getInstances(); + for (IInstance instance : instances) + { + if (instanceName.equals(instance.getName())) + { + if (instance instanceof ISerialNumbered) + { + status = isCompatible(project, (ISerialNumbered) instance); + break; + } + } + } + return status; + } + + /** + * Check if the given instance name is compatible with the given project + * @param project + * @param instance + * @return {@link IStatus#OK} if fully compatible, {@link IStatus#WARNING} if can be compatible and {@link IStatus#ERROR} if not compatible. Return <code>null</code> if the instance does not exists + */ + public static IStatus isCompatible(IProject project, ISerialNumbered instance) + { + IStatus compatible = null; + int projectAPILevel = SdkUtils.getApiVersionNumberForProject(project); + String minSdkVersionStr = SdkUtils.getMinSdkVersion(project); + int minSdkVersion; + boolean isProjectTargetAPlatform = + SdkUtils.getTarget(project) != null ? SdkUtils.getTarget(project).isPlatform() + : true; + + try + { + minSdkVersion = Integer.parseInt(minSdkVersionStr); + } + catch (Exception e) + { + // the projectAPILevel will be used and minSdkVersion will be ignored + minSdkVersion = projectAPILevel; + } + String projectTarget = SdkUtils.getTargetNameForProject(project); + + // if the instance is an emulator add the instance only if they have the same target and at least the same APILevel + if (instance instanceof IAndroidEmulatorInstance) + { + IAndroidEmulatorInstance emulatorInstance = (IAndroidEmulatorInstance) instance; + int emulatorApi = emulatorInstance.getAPILevel(); + String emulatorTarget = emulatorInstance.getTarget(); + + if (emulatorApi >= minSdkVersion) + { + String emulatorInstanceName = emulatorInstance.getName(); + String emulatorInstanceBaseTarget = SdkUtils.getBaseTarget(emulatorInstanceName); + boolean isEmulatorTargetAPlatform = SdkUtils.isPlatformTarget(emulatorInstanceName); + + // if they have same target its ok + if (emulatorTarget.equals(projectTarget)) + { + compatible = Status.OK_STATUS; + } + //if the emulator isn't a platform, but the base target is the same as the project, everything is ok + else if (!isEmulatorTargetAPlatform + && emulatorInstanceBaseTarget.equals(projectTarget)) + { + compatible = Status.OK_STATUS; + } + else + { + compatible = + new Status(IStatus.WARNING, LaunchPlugin.PLUGIN_ID, NLS.bind( + LaunchNLS.UI_LaunchConfigurationTab_WARN_DEVICE_INCOMPATIBLE, + emulatorApi, projectAPILevel)); + } + } + else + { + compatible = + new Status(IStatus.ERROR, LaunchPlugin.PLUGIN_ID, NLS.bind( + LaunchNLS.UI_LaunchConfigurationTab_ERR_EMULATOR_INCOMPATIBLE, + emulatorTarget, projectTarget)); + } + } + else + { + if (instance != null) + { + int deviceSdkVersion = -1; + int tries = 0; + + //wait the device to be online + while ((tries < 5) && (deviceSdkVersion <= 0)) + { + try + { + deviceSdkVersion = + Integer.parseInt(DDMSFacade.getDeviceProperty( + instance.getSerialNumber(), "ro.build.version.sdk")); + } + catch (NumberFormatException e) + { + deviceSdkVersion = 0; + try + { + Thread.sleep(100); + } + catch (InterruptedException e1) + { + //do nothing + } + } + tries++; + } + + if (deviceSdkVersion < minSdkVersion) + { + compatible = + new Status(IStatus.ERROR, LaunchPlugin.PLUGIN_ID, NLS.bind( + LaunchNLS.UI_LaunchConfigurationTab_ERR_DEVICE_INCOMPATIBLE, + deviceSdkVersion, projectAPILevel)); + } + else if (deviceSdkVersion == projectAPILevel) + { + if (!isProjectTargetAPlatform) + { + compatible = + new Status( + IStatus.WARNING, + LaunchPlugin.PLUGIN_ID, + LaunchNLS.UI_LaunchConfigurationTab_WARN_DEVICE_TARGET_MISSING); + } + else + { + compatible = Status.OK_STATUS; + } + } + else + { + compatible = + new Status(IStatus.WARNING, LaunchPlugin.PLUGIN_ID, NLS.bind( + LaunchNLS.UI_LaunchConfigurationTab_WARN_DEVICE_INCOMPATIBLE, + deviceSdkVersion, projectAPILevel)); + } + } + + } + return compatible; + } + + /** + * Filter instances the compatible with the given project + * @param project whose compatible instances need to be retrieved + * @return a new collection containing only the instances that are compatible with the given project + **/ + public static Collection<ISerialNumbered> filterInstancesByProject( + Collection<ISerialNumbered> allInstances, IProject project) + { + Collection<ISerialNumbered> filteredInstances = new LinkedList<ISerialNumbered>(); + + for (ISerialNumbered instance : allInstances) + { + IStatus compatible = LaunchUtils.isCompatible(project, instance); + + if (compatible.getSeverity() != IStatus.ERROR) + { + filteredInstances.add(instance); + } + } + + return filteredInstances; + } + +}
\ No newline at end of file diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/StudioAndroidConfigurationDelegate.java b/src/plugins/launch/src/com/motorola/studio/android/launch/StudioAndroidConfigurationDelegate.java new file mode 100644 index 0000000..aec89e5 --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/StudioAndroidConfigurationDelegate.java @@ -0,0 +1,762 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.debug.ui.ILaunchGroup; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.osgi.util.NLS; +import org.eclipse.sequoyah.device.framework.model.IInstance; +import org.eclipse.sequoyah.device.framework.model.handler.ServiceHandler; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.console.ConsolePlugin; +import org.eclipse.ui.console.IConsole; + +import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener; +import com.android.ddmlib.Client; +import com.android.ddmlib.ClientData; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.launch.AndroidLaunch; +import com.android.ide.eclipse.adt.internal.launch.AndroidLaunchController; +import com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate; +import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.ide.eclipse.adt.io.IFolderWrapper; +import com.android.sdklib.xml.AndroidManifestParser; +import com.android.sdklib.xml.ManifestData; +import com.android.sdklib.xml.ManifestData.Activity; +import com.motorola.studio.android.adt.DDMSFacade; +import com.motorola.studio.android.adt.SdkUtils; +import com.motorola.studio.android.adt.StudioAndroidEventManager; +import com.motorola.studio.android.common.log.StudioLogger; +import com.motorola.studio.android.common.preferences.DialogWithToggleUtils; +import com.motorola.studio.android.emulator.EmulatorPlugin; +import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager; +import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance; +import com.motorola.studio.android.launch.i18n.LaunchNLS; +import com.motorola.studio.android.launch.ui.StartedInstancesDialog; + +/** + * DESCRIPTION: This class is responsible to execute the launch process <br> + * RESPONSIBILITY: Perform application launch on a device. <br> + * COLABORATORS: none <br> + */ +@SuppressWarnings("restriction") +public class StudioAndroidConfigurationDelegate extends LaunchConfigDelegate +{ + + private static final String ERRONEOUS_LAUNCH_CONFIGURATION = "erroneous.launch.config.dialog"; + + private static final String NO_COMPATIBLE_DEVICE = "no.compatible.device.dialog"; + + IAndroidEmulatorInstance compatibleInstance = null; + + IAndroidEmulatorInstance initialEmulatorInstance = null; + + public List<Client> waitingDebugger = new ArrayList<Client>(); + + private class RunAsClientListener implements IClientChangeListener + { + /** + * + */ + private final IAndroidEmulatorInstance instance; + + /** + * + */ + private final String appToLaunch; + + /** + * + * @param instance + */ + RunAsClientListener(IAndroidEmulatorInstance instance, String appToLaunch) + { + this.instance = instance; + this.appToLaunch = appToLaunch; + } + + /* + * (non-Javadoc) + * @see com.android.ddmlib.AndroidDebugBridge.IClientChangeListener#clientChanged(com.android.ddmlib.Client, int) + */ + public void clientChanged(Client client, int changeMask) + { + if ((changeMask & Client.CHANGE_NAME) == Client.CHANGE_NAME) + { + String applicationName = client.getClientData().getClientDescription(); + if (applicationName != null) + { + IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore(); + String home = store.getString(AdtPrefs.PREFS_HOME_PACKAGE); + if (home.equals(applicationName)) + { + String serialNumber = client.getDevice().getSerialNumber(); + String avdName = DDMSFacade.getNameBySerialNumber(serialNumber); + if ((instance != null) && instance.getName().equals(avdName)) + { + StudioLogger.info(StudioAndroidConfigurationDelegate.class, + "Delegating launch session to ADT... "); + + synchronized (StudioAndroidConfigurationDelegate.this) + { + StudioAndroidConfigurationDelegate.this.notify(); + } + } + } + + Client removeClient = null; + for (Client waiting : waitingDebugger) + { + int pid = waiting.getClientData().getPid(); + if (pid == client.getClientData().getPid()) + { + client.getDebuggerListenPort(); + synchronized (StudioAndroidConfigurationDelegate.this) + { + StudioAndroidConfigurationDelegate.this.notify(); + } + removeClient = waiting; + break; + } + } + + if (removeClient != null) + { + waitingDebugger.remove(removeClient); + } + } + } + + if ((changeMask & Client.CHANGE_DEBUGGER_STATUS) == Client.CHANGE_DEBUGGER_STATUS) + { + ClientData clientData = client.getClientData(); + String applicationName = clientData.getClientDescription(); + if (clientData.getDebuggerConnectionStatus() == ClientData.DebuggerStatus.DEFAULT) + { + if (((appToLaunch != null) && (applicationName != null)) + && applicationName.equals(appToLaunch.substring(0, + appToLaunch.lastIndexOf(".")))) + { + client.getDebuggerListenPort(); + synchronized (StudioAndroidConfigurationDelegate.this) + { + StudioAndroidConfigurationDelegate.this.notify(); + } + } + else if (appToLaunch != null) + { + waitingDebugger.add(client); + } + } + } + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.debug.core.model.LaunchConfigurationDelegate#preLaunchCheck(org.eclipse.debug.core.ILaunchConfiguration, java.lang.String, org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + public boolean preLaunchCheck(ILaunchConfiguration configuration, String mode, + IProgressMonitor monitor) throws CoreException + { + initialEmulatorInstance = null; + boolean isOk = super.preLaunchCheck(configuration, mode, monitor); + + if (isOk) + { + final String instanceName = + configuration.getAttribute( + ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, (String) null); + + // we found an instance + if ((instanceName != null) && (instanceName.length() > 0)) + { + IAndroidEmulatorInstance instance = + DeviceFrameworkManager.getInstance().getInstanceByName(instanceName); + if (instance == null) + { + String serialNumber = LaunchUtils.getSerialNumberForInstance(instanceName); + if (!DDMSFacade.isDeviceOnline(serialNumber)) + { + isOk = false; + handleErrorDuringLaunch(configuration, mode, instanceName); + } + } + else + { + if (!instance.isAvailable()) + { + isOk = false; + handleErrorDuringLaunch(configuration, mode, instanceName); + } + + if (!instance.isStarted()) + { + initialEmulatorInstance = instance; + //updates the compatible instance with user response + isOk = checkForCompatibleRunningInstances(configuration); + } + } + } + else + { + isOk = false; + handleErrorDuringLaunch(configuration, mode, null); + } + } + // validate if the project isn't a library project + if (isOk) + { + String projectName = + configuration.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, + (String) null); + if (projectName != null) + { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + if ((project != null) && SdkUtils.isLibraryProject(project)) + { + handleProjectError(configuration, project, mode); + isOk = false; + } + } + } + + return isOk; + } + + private void handleProjectError(final ILaunchConfiguration config, final IProject project, + final String mode) + { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() + { + + public void run() + { + Shell shell = LaunchUtils.getActiveWorkbenchShell(); + + String message = LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_IS_LIBRARY; + + String prefKey = ERRONEOUS_LAUNCH_CONFIGURATION; + + DialogWithToggleUtils.showInformation(prefKey, + LaunchNLS.ERR_LaunchConfigurationShortcut_MsgTitle, message); + + StructuredSelection struturedSelection; + + String groupId = IDebugUIConstants.ID_RUN_LAUNCH_GROUP; + + ILaunchGroup group = DebugUITools.getLaunchGroup(config, mode); + groupId = group.getIdentifier(); + struturedSelection = new StructuredSelection(config); + + DebugUITools.openLaunchConfigurationDialogOnGroup(shell, struturedSelection, + groupId); + } + }); + } + + private void handleErrorDuringLaunch(final ILaunchConfiguration config, final String mode, + final String instanceName) + { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() + { + + public void run() + { + Shell shell = LaunchUtils.getActiveWorkbenchShell(); + + String message = + instanceName != null ? NLS.bind( + LaunchNLS.ERR_LaunchDelegate_InvalidDeviceInstance, instanceName) + : NLS.bind(LaunchNLS.ERR_LaunchDelegate_No_Compatible_Device, + config.getName()); + + String prefKey = + instanceName != null ? ERRONEOUS_LAUNCH_CONFIGURATION + : NO_COMPATIBLE_DEVICE; + + DialogWithToggleUtils.showInformation(prefKey, + LaunchNLS.ERR_LaunchConfigurationShortcut_MsgTitle, message); + + StructuredSelection struturedSelection; + + String groupId = IDebugUIConstants.ID_RUN_LAUNCH_GROUP; + + ILaunchGroup group = DebugUITools.getLaunchGroup(config, mode); + groupId = group.getIdentifier(); + struturedSelection = new StructuredSelection(config); + + DebugUITools.openLaunchConfigurationDialogOnGroup(shell, struturedSelection, + groupId); + } + }); + } + + /** + * Launches an Android application based on the given launch configuration. + */ + @Override + public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, + IProgressMonitor monitor) throws CoreException + + { + //use a working copy because it can be changed and these changes should not be propagated to the original copy + ILaunchConfigurationWorkingCopy configurationWorkingCopy = configuration.getWorkingCopy(); + + StudioLogger.info(StudioAndroidConfigurationDelegate.class, + "Launch Android Application using Studio for Android wizard. Configuration: " + + configurationWorkingCopy + " mode:" + mode + " launch: " + launch); + try + { + + String projectName = + configurationWorkingCopy.getAttribute( + ILaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null); + int launchAction = + configurationWorkingCopy.getAttribute( + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION, + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT); + + String instanceName = + configurationWorkingCopy.getAttribute( + ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, (String) null); + + if ((projectName != null) && (instanceName != null)) + { + IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + if (project == null) + { + IStatus status = + new Status(Status.ERROR, LaunchPlugin.PLUGIN_ID, + "Could not retrieve project: " + projectName); + throw new CoreException(status); + } + + String appToLaunch = null; + if (launchAction == ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT) + { + ManifestData manifestParser = + AndroidManifestParser.parse(new IFolderWrapper(project)); + Activity launcherActivity = manifestParser.getLauncherActivity(); + String activityName = null; + if (launcherActivity != null) + { + activityName = launcherActivity.getName(); + } + + // if there's no default activity. Then there's nothing to be launched. + if (activityName != null) + { + appToLaunch = activityName; + } + } + // case for a specific activity + else if (launchAction == ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY) + { + appToLaunch = + configurationWorkingCopy.getAttribute( + ILaunchConfigurationConstants.ATTR_ACTIVITY, (String) null); + + if ((appToLaunch == null) || "".equals(appToLaunch)) + { + IStatus status = + new Status( + Status.ERROR, + LaunchPlugin.PLUGIN_ID, + "Activity field cannot be empty. Specify an activity or use the default activity on launch configuration."); + throw new CoreException(status); + } + } + // for the do nothing case there is nothing to do + + IAndroidEmulatorInstance emuInstance = + DeviceFrameworkManager.getInstance().getInstanceByName(instanceName); + + RunAsClientListener list = null; + + //if initialEmulatorInstance is not null it means that it was offline and user has interacted with StartedInstancesDialog. + //The emuInstance variable should be overrided by the initialEmulatorInstance because the emuInstance can be the new + //user choice (in case he has selected the check box in dialog asking to update the run configuration) + if (initialEmulatorInstance != null) + { + emuInstance = initialEmulatorInstance; + } + + try + { + if (appToLaunch != null) + { + list = new RunAsClientListener(emuInstance, appToLaunch); + StudioAndroidEventManager.asyncAddClientChangeListener(list); + } + + // The instance from the launch configuration is an emulator (because the query returned + // something different from null) and is not started. + if ((emuInstance != null) && (!emuInstance.isStarted())) + { + if (compatibleInstance != null) + { + emuInstance = compatibleInstance; + instanceName = emuInstance.getName(); + configurationWorkingCopy.setAttribute( + ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, + emuInstance.getName()); + configurationWorkingCopy.setAttribute( + ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME, + emuInstance.getName()); + } + else + { + startEmuInstance(emuInstance); + } + } + + StudioLogger.info(StudioAndroidConfigurationDelegate.class, + "AVD where the application will be executed: " + instanceName); + + + String serialNumber = LaunchUtils.getSerialNumberForInstance(instanceName); + if (serialNumber == null) + { + IStatus status = + new Status(Status.ERROR, LaunchPlugin.PLUGIN_ID, + "Could not retrieve AVD instance: " + instanceName); + throw new CoreException(status); + } + + bringConsoleView(); + + // Determining if it is an emulator or handset and creating the description + //to be used for usage data collection + String descriptionToLog = ""; + if (emuInstance != null) + { + descriptionToLog = StudioLogger.VALUE_EMULATOR; + } + else + { + if ((serialNumber != null) && (!serialNumber.equals(""))) + { + descriptionToLog = StudioLogger.VALUE_HANDSET; + } + } + + if (!descriptionToLog.equals("")) + { + descriptionToLog = + StudioLogger.KEY_DEVICE_TYPE + descriptionToLog + + StudioLogger.SEPARATOR; + } + + descriptionToLog = descriptionToLog + StudioLogger.KEY_USE_VDL; + + descriptionToLog = descriptionToLog + StudioLogger.VALUE_NO; + super.launch(configurationWorkingCopy, mode, launch, monitor); + + // Collecting usage data for statistical purposes + try + { + String prjTarget = ""; + if (project != null) + { + prjTarget = Sdk.getCurrent().getTarget(project).getName(); + } + + if (!descriptionToLog.equals("")) + { + descriptionToLog = descriptionToLog + StudioLogger.SEPARATOR; + } + + descriptionToLog = + descriptionToLog + StudioLogger.KEY_PRJ_TARGET + prjTarget; + + if (emuInstance != null) + { + String emuTarget = emuInstance.getTarget(); + descriptionToLog = descriptionToLog + StudioLogger.SEPARATOR; + descriptionToLog = + descriptionToLog + StudioLogger.KEY_TARGET + emuTarget; + } + + StudioLogger.collectUsageData(mode, StudioLogger.KIND_APP_MANAGEMENT, + descriptionToLog, LaunchPlugin.PLUGIN_ID, LaunchPlugin.getDefault() + .getBundle().getVersion().toString()); + } + catch (Throwable e) + { + //Do nothing, but error on the log should never prevent app from working + } + + } + finally + { + if (list != null) + { + StudioAndroidEventManager.asyncRemoveClientChangeListener(list); + } + StudioAndroidEventManager.asyncAddClientChangeListener(AndroidLaunchController + .getInstance()); + } + } + else + { + throw new CoreException(new Status(IStatus.ERROR, LaunchPlugin.PLUGIN_ID, + "Missing parameters for launch")); + } + } + catch (CoreException e) + { + AndroidLaunch androidLaunch = (AndroidLaunch) launch; + androidLaunch.stopLaunch(); + StudioLogger.error(StudioAndroidConfigurationDelegate.class, "Error while lauching " + + configurationWorkingCopy.getName(), e); + throw e; + } + catch (Exception e) + { + StudioLogger.error(LaunchUtils.class, + "An error occurred trying to parse AndroidManifest", e); + } + finally + { + if (mode.equals(ILaunchManager.RUN_MODE)) + { + AndroidLaunch androidLaunch = (AndroidLaunch) launch; + androidLaunch.stopLaunch(); + } + } + } + + /** + * @param project + * @param emuInstance + * @throws CoreException + */ + private boolean checkForCompatibleRunningInstances(ILaunchConfiguration configuration) + throws CoreException + { + IProject project = null; + compatibleInstance = null; + + final String projectName = + configuration.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, + (String) null); + + project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + if (project == null) + { + IStatus status = + new Status(Status.ERROR, LaunchPlugin.PLUGIN_ID, "Could not retrieve project: " + + projectName); + throw new CoreException(status); + } + + //Check if there is a compatible instance running to launch the app + Collection<IAndroidEmulatorInstance> startedInstances = + DeviceFrameworkManager.getInstance().getAllStartedInstances(); + + final Collection<IAndroidEmulatorInstance> compatibleStartedInstances = + new HashSet<IAndroidEmulatorInstance>(); + + boolean continueLaunch = true; + + for (IAndroidEmulatorInstance i : startedInstances) + { + IStatus resultStatus = LaunchUtils.isCompatible(project, i.getName()); + if ((resultStatus.getSeverity() == Status.OK) + || (resultStatus.getSeverity() == Status.WARNING)) + { + compatibleStartedInstances.add(i); + } + } + if (compatibleStartedInstances.size() > 0) + { + //show a dialog with compatible running instances so the user can + //choose one to run the app, or he can choose to run the preferred AVD + + StartedInstancesDialogProxy proxy = + new StartedInstancesDialogProxy(compatibleStartedInstances, configuration, + project); + PlatformUI.getWorkbench().getDisplay().syncExec(proxy); + + compatibleInstance = proxy.getSelectedInstance(); + continueLaunch = proxy.continueLaunch(); + } + return continueLaunch; + } + + private class StartedInstancesDialogProxy implements Runnable + { + private IAndroidEmulatorInstance selectedInstance = null; + + private boolean continueLaunch = true; + + private final ILaunchConfiguration configuration; + + Collection<IAndroidEmulatorInstance> compatibleStartedInstances = null; + + IProject project = null; + + /** + * + */ + public StartedInstancesDialogProxy( + Collection<IAndroidEmulatorInstance> compatibleStartedInstances, + ILaunchConfiguration configuration, IProject project) + { + this.compatibleStartedInstances = compatibleStartedInstances; + this.configuration = configuration; + this.project = project; + } + + public void run() + { + Shell aShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + Shell shell = new Shell(aShell); + StartedInstancesDialog dialog; + try + { + dialog = + new StartedInstancesDialog(shell, compatibleStartedInstances, + configuration, project); + dialog.setBlockOnOpen(true); + dialog.open(); + + selectedInstance = null; + if (dialog.getReturnCode() == IDialogConstants.OK_ID) + { + selectedInstance = dialog.getSelectedInstance(); + } + else if (dialog.getReturnCode() == IDialogConstants.ABORT_ID) + { + continueLaunch = false; + } + } + catch (CoreException e) + { + StudioLogger.error(StudioAndroidConfigurationDelegate.class, + "It was not possible to open Started Instance Dialog", e); + } + } + + public IAndroidEmulatorInstance getSelectedInstance() + { + return selectedInstance; + } + + public boolean continueLaunch() + { + return continueLaunch; + } + } + + /** + * Bring Console View to the front and activate the appropriate stream + * + */ + private void bringConsoleView() + { + IConsole activeConsole = null; + + IConsole[] consoles = ConsolePlugin.getDefault().getConsoleManager().getConsoles(); + for (IConsole console : consoles) + { + if (console.getName().equals(ILaunchConfigurationConstants.ANDROID_CONSOLE_ID)) + { + activeConsole = console; + } + } + + // Bring Console View to the front + if (activeConsole != null) + { + ConsolePlugin.getDefault().getConsoleManager().showConsoleView(activeConsole); + } + + } + + /** + * + * @param instance + * @throws CoreException + */ + private void startEmuInstance(IAndroidEmulatorInstance instance) throws CoreException + { + StudioLogger.info(StudioAndroidConfigurationDelegate.class, + "Needs to Start the AVD instance before launching... "); + + ServiceHandler startHandler = EmulatorPlugin.getStartServiceHandler(); + IStatus status = startHandler.run((IInstance) instance, null, new NullProgressMonitor()); + + StudioLogger.info(StudioAndroidConfigurationDelegate.class, + "Status of the launch service: " + status); + + if (status.getSeverity() == Status.ERROR) + { + throw new CoreException(status); + } + else if (status.getSeverity() == Status.CANCEL) + { + StudioLogger.info(StudioAndroidConfigurationDelegate.class, + "Abort launch session because the AVD start was canceled. "); + return; + } + + if (!instance.isStarted()) + { + status = + new Status(Status.ERROR, LaunchPlugin.PLUGIN_ID, + "The Android Virtual Device is not started: " + instance.getName()); + throw new CoreException(status); + } + + synchronized (this) + { + try + { + wait(); + } + catch (InterruptedException e) + { + StudioLogger.info("Could not wait: ", e.getMessage()); + } + } + } +} diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/LaunchNLS.java b/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/LaunchNLS.java new file mode 100644 index 0000000..9894057 --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/LaunchNLS.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch.i18n; + +import org.eclipse.osgi.util.NLS; + +/** + * DESCRIPTION: This class is the NLS component for Launch plugin. It is the + * main class for internationalization + * + * RESPONSIBILITY: Provide local strings for using throughout the tool + * + * COLABORATORS: messages.properties: file that contains the strings that will + * be provided to the plugin + * + * USAGE: Use any of the public static variables for accessing the local strings + */ +public class LaunchNLS extends NLS +{ + /** + * The bundle location. It refers to messages.properties file inside this + * package + */ + + static + { + LaunchNLS.initializeMessages("com.motorola.studio.android.launch.i18n.launchNLS", + LaunchNLS.class); + } + + /* + * UI string area + */ + + public static String LaunchComposite_UI_LaunchComposite_DestinationGroupText; + + public static String LaunchConfigurationTab_CreateNewAVDLink; + + public static String LaunchConfigurationTab_DoNothingButton; + + public static String LaunchConfigurationTab_LaunchButton; + + public static String UI_LaunchComposite_ProjectNameLabel; + + public static String UI_LaunchComposite_ActivityDefaultButton; + + public static String UI_LaunchComposite_ActivityGroupLabel; + + public static String UI_LaunchComposite_DeviceNameLabel; + + public static String UI_LaunchComposite_BrowseButton; + + public static String UI_LaunchComposite_ProjectRequiredMessage; + + public static String UI_LaunchComposite_ProjectRequiredTitle; + + public static String UI_LaunchComposite_SelectProjectScreenTitle; + + public static String UI_LaunchComposite_SelectProjectScreenMessage; + + public static String UI_LaunchComposite_SelectActivityScreenTitle; + + public static String UI_LaunchComposite_SelectActivityScreenMessage; + + public static String UI_LaunchComposite_SelectDeviceScreenTitle; + + public static String UI_LaunchComposite_SelectDeviceScreenMessage; + + public static String UI_LaunchConfigurationTab_ERR_DEVICE_INEXISTENT; + + public static String UI_LaunchConfigurationTab_ERR_DEVICE_INCOMPATIBLE; + + public static String UI_LaunchConfigurationTab_ERR_INVALID_ACTIVITY; + + public static String UI_LaunchConfigurationTab_ERR_ACTIVITY_NOT_EXIST; + + public static String UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST; + + public static String UI_LaunchConfigurationTab_WARN_DEVICE_INCOMPATIBLE; + + public static String UI_LaunchConfigurationTab_Tab_Name; + + public static String UI_LaunchConfigurationTab_InfoSelectInstance; + + public static String UI_LaunchConfigurationTab_InfoSelectActivity; + + public static String UI_LaunchConfigurationTab_InfoSelectProject; + + public static String ERR_LaunchConfigurationShortcut_MsgTitle; + + public static String ERR_LaunchConfigurationShortcut_CannotLaunchSelectedResourceMsg; + + public static String ERR_LaunchDelegate_InvalidDeviceInstance; + + public static String ERR_LaunchDelegate_No_Compatible_Device; + + public static String UI_LaunchConfigurationTab_ERR_EMULATOR_INCOMPATIBLE; + + public static String UI_LaunchConfigurationTab_WARN_DEVICE_TARGET_MISSING; + + public static String UI_LaunchConfigurationTab_ERR_PROJECT_IS_LIBRARY; + + public static String UI_StartedInstancesDialog_CompatibleAvdsColumnName; + + public static String UI_StartedInstancesDialog_Message; + + public static String UI_StartedInstancesDialog_Title; + + public static String UI_StartedInstancesDialog_WindowTitle; + + public static String UI_StartedInstancesDialog_ApiLevel; + + public static String UI_StartedInstancesDialog_Tooltip; + + public static String UI_StartedInstancesDialog_UpdateRunConfigurarion; +} diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/launchNLS.properties b/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/launchNLS.properties new file mode 100644 index 0000000..6a7113f --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/i18n/launchNLS.properties @@ -0,0 +1,57 @@ +# +# Copyright (C) 2012 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +# +# 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. +# + +LaunchComposite_UI_LaunchComposite_DestinationGroupText=Basic settings +LaunchConfigurationTab_CreateNewAVDLink=<a>Create new AVD...</a> +LaunchConfigurationTab_DoNothingButton=Do Nothing +LaunchConfigurationTab_LaunchButton=Launch: +UI_LaunchComposite_ProjectNameLabel=Project: +UI_LaunchComposite_ActivityGroupLabel=Activity +UI_LaunchComposite_DeviceNameLabel=Device: +UI_LaunchComposite_BrowseButton=Browse +UI_LaunchComposite_ActivityDefaultButton=Launch Default Activity +UI_LaunchComposite_ProjectRequiredMessage=A project must be selected before browsing for an activity +UI_LaunchComposite_ProjectRequiredTitle=Project required +UI_LaunchComposite_SelectProjectScreenTitle=Project Selection +UI_LaunchComposite_SelectProjectScreenMessage=Select a project +UI_LaunchComposite_SelectActivityScreenTitle=Activity Selection +UI_LaunchComposite_SelectActivityScreenMessage=Select an activity +UI_LaunchComposite_SelectDeviceScreenTitle=Device Selection +UI_LaunchComposite_SelectDeviceScreenMessage=Select a device instance +UI_LaunchConfigurationTab_ERR_DEVICE_INEXISTENT=The selected device instance does not exist or is invalid. +UI_LaunchConfigurationTab_ERR_DEVICE_INCOMPATIBLE=The selected device instance is not compatible. Its API level is {0} while the project API level is {1} +UI_LaunchConfigurationTab_ERR_EMULATOR_INCOMPATIBLE=The selected device instance is not compatible. Its Device target is {0} while the Project target is {1} +UI_LaunchConfigurationTab_ERR_INVALID_ACTIVITY=The selected file is not a valid activity. +UI_LaunchConfigurationTab_ERR_ACTIVITY_NOT_EXIST=The selected activity does not exist. +UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST=The selected project does not exist. +UI_LaunchConfigurationTab_ERR_PROJECT_IS_LIBRARY=Cannot launch library projects. +UI_LaunchConfigurationTab_WARN_DEVICE_INCOMPATIBLE=The selected device instance has an API level ({0}) higher than the project API level ({1}) +UI_LaunchConfigurationTab_WARN_DEVICE_TARGET_MISSING=The selected instance isn't an emulator and its target cannot be checked +UI_LaunchConfigurationTab_Tab_Name=Main +UI_LaunchConfigurationTab_InfoSelectInstance=Select a device instance +UI_LaunchConfigurationTab_InfoSelectActivity=Select an activity +UI_LaunchConfigurationTab_InfoSelectProject=Select a project +UI_StartedInstancesDialog_CompatibleAvdsColumnName=Compatible online AVDs +UI_StartedInstancesDialog_Message=Select an online AVD instance and click OK to launch the project in that AVD. \nClick Ignore to start the preferred AVD, or click Abort to cancel the launch. +UI_StartedInstancesDialog_Title=AVD "{0}" is offline. +UI_StartedInstancesDialog_WindowTitle=Preferred AVD offline +UI_StartedInstancesDialog_ApiLevel=API Level +UI_StartedInstancesDialog_Tooltip=The project and device API level are not an exact match +UI_StartedInstancesDialog_UpdateRunConfigurarion=Update configuration to use selected AVD +ERR_LaunchConfigurationShortcut_MsgTitle=MOTODEV Studio For Android +ERR_LaunchConfigurationShortcut_CannotLaunchSelectedResourceMsg=The selected resource cannot be launched. +ERR_LaunchDelegate_InvalidDeviceInstance=Device instance "{0}" does not exist or is not available.\nPlease verify and try again. +ERR_LaunchDelegate_No_Compatible_Device=No compatible device instance was found to run "{0}". Review your configuration and try again.
\ No newline at end of file diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/AndroidProjectsSelectionDialog.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/AndroidProjectsSelectionDialog.java new file mode 100644 index 0000000..c775271 --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/AndroidProjectsSelectionDialog.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch.ui; + +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.ElementListSelectionDialog; +import org.eclipse.ui.model.WorkbenchLabelProvider; + +import com.motorola.studio.android.launch.LaunchPlugin; +import com.motorola.studio.android.launch.LaunchUtils; +import com.motorola.studio.android.launch.i18n.LaunchNLS; + +/** + * DESCRIPTION: + * Selection Dialog with only opened MOTOMAGX projects + * <br> + * RESPONSIBILITY: + * Provides a Element Selection Dialog to select a MOTOMAGX project + * <br> + * COLABORATORS: + * none + * <br> + * USAGE: + * This should be instanced when the user must choose one of a MOTOMAGX project list + */ +public class AndroidProjectsSelectionDialog extends ElementListSelectionDialog +{ + + private static final String PRJ_SELECTION_CONTEXT_HELP_ID = + LaunchPlugin.PLUGIN_ID + ".projectSelectionDialog"; + + /** + * Create a new Project Selection Dialog + * @param parent the parent shell + * @param renderer the label provider + */ + public AndroidProjectsSelectionDialog(Shell parent, ILabelProvider renderer) + { + super(parent, renderer); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.dialogs.ElementListSelectionDialog#createDialogArea(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createDialogArea(Composite parent) + { + Control control = super.createDialogArea(parent); + + setHelpAvailable(true); + PlatformUI.getWorkbench().getHelpSystem().setHelp(control, PRJ_SELECTION_CONTEXT_HELP_ID); + return control; + } + + /** + * Creates a selection dialog with the workbench label provider + * @param parent The parent composite + */ + public AndroidProjectsSelectionDialog(Shell parent) + { + super(parent, new WorkbenchLabelProvider()); + } + + /** + * Sets the default elements: the list of all opened Studio for Android projects + */ + public void setDefaultElements() + { + this.setElements(LaunchUtils.getSupportedProjects()); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.dialogs.AbstractElementListSelectionDialog#open() + */ + @Override + public int open() + { + this.setTitle(LaunchNLS.UI_LaunchComposite_SelectProjectScreenTitle); + this.setMessage(LaunchNLS.UI_LaunchComposite_SelectProjectScreenMessage); + + setDefaultElements(); + return super.open(); + } +} diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/DeviceSelectionDialog.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/DeviceSelectionDialog.java new file mode 100644 index 0000000..1659966 --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/DeviceSelectionDialog.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch.ui; + +import java.util.Collection; +import java.util.Properties; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.sequoyah.device.framework.model.IInstance; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.ElementListSelectionDialog; + +import com.motorola.studio.android.adt.ISerialNumbered; +import com.motorola.studio.android.devices.DevicesManager; +import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance; +import com.motorola.studio.android.launch.LaunchPlugin; +import com.motorola.studio.android.launch.LaunchUtils; +import com.motorola.studio.android.launch.i18n.LaunchNLS; + +/** + * DESCRIPTION: + * This class implements the device selection dialog + * + * RESPONSIBILITY: + * Provides a dialog populated with the available device instances + * + * COLABORATORS: + * None. + * + * USAGE: + * This class is intended to be used by Eclipse only + */ +public class DeviceSelectionDialog extends ElementListSelectionDialog +{ + private static final String DEV_SELECTION_CONTEXT_HELP_ID = LaunchPlugin.PLUGIN_ID + + ".deviceSelectionDialog"; + + /** + * Default constructor + * + * @param parent Parent shell + * @param description Dialog description + */ + public DeviceSelectionDialog(Shell parent, String description, final IProject project) + { + super(parent, new LabelProvider() + { + @Override + public String getText(Object element) + { + String result = ""; + if (element instanceof ISerialNumbered) + { + ISerialNumbered serialNumbered = (ISerialNumbered) element; + result = serialNumbered.getDeviceName(); + if (serialNumbered instanceof IAndroidEmulatorInstance) + { + IAndroidEmulatorInstance emulatorInstance = + (IAndroidEmulatorInstance) serialNumbered; + int emulatorApi = emulatorInstance.getAPILevel(); + String emulatorTarget = emulatorInstance.getTarget(); + result += " (" + emulatorTarget + ", Api version " + emulatorApi + ")"; + } + else if (serialNumbered instanceof IInstance) + { + IInstance instance = (IInstance) serialNumbered; + Properties properties = instance.getProperties(); + if (properties != null) + { + String target = properties.getProperty("ro.build.version.release"); //$NON-NLS-1$ + if (target != null) + { + result += " (Android " + target + ")"; + } + } + } + } + return result; + } + + @Override + public Image getImage(Object element) + { + + Image img = null; + + ISerialNumbered serialNumbered = (ISerialNumbered) element; + IStatus compatible = LaunchUtils.isCompatible(project, serialNumbered); + + // notify the warning state + if (compatible.getSeverity() == IStatus.WARNING) + { + img = + PlatformUI.getWorkbench().getSharedImages() + .getImage(ISharedImages.IMG_OBJS_WARN_TSK); + } + + return img; + } + }); + + this.setTitle(LaunchNLS.UI_LaunchComposite_SelectDeviceScreenTitle); + this.setMessage(description); + + Collection<ISerialNumbered> instances = DevicesManager.getInstance().getAllDevicesSorted(); + if ((project != null) && (instances != null) && (instances.size() > 0)) + { + Collection<ISerialNumbered> filteredInstances = + LaunchUtils.filterInstancesByProject(instances, project); + Object[] filteredInstancesArray = filteredInstances.toArray(); + this.setElements(filteredInstancesArray); + } + + this.setHelpAvailable(true); + PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, DEV_SELECTION_CONTEXT_HELP_ID); + } +} diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTab.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTab.java new file mode 100644 index 0000000..1d68897 --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTab.java @@ -0,0 +1,981 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch.ui; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.ui.AbstractLaunchConfigurationTab; +import org.eclipse.debug.ui.ILaunchConfigurationTab; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.sequoyah.device.framework.events.IInstanceListener; +import org.eclipse.sequoyah.device.framework.events.InstanceEvent; +import org.eclipse.sequoyah.device.framework.events.InstanceEventManager; +import org.eclipse.sequoyah.device.framework.model.IInstance; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.ElementListSelectionDialog; +import org.eclipse.ui.dialogs.ISelectionStatusValidator; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +import com.android.ide.eclipse.adt.io.IFolderWrapper; +import com.android.sdklib.xml.AndroidManifestParser; +import com.android.sdklib.xml.ManifestData; +import com.android.sdklib.xml.ManifestData.Activity; +import com.motorola.studio.android.adt.ISerialNumbered; +import com.motorola.studio.android.adt.SdkUtils; +import com.motorola.studio.android.common.log.StudioLogger; +import com.motorola.studio.android.emulator.device.handlers.OpenNewDeviceWizardHandler; +import com.motorola.studio.android.emulator.device.refresh.InstancesListRefresh; +import com.motorola.studio.android.launch.ILaunchConfigurationConstants; +import com.motorola.studio.android.launch.LaunchPlugin; +import com.motorola.studio.android.launch.LaunchUtils; +import com.motorola.studio.android.launch.i18n.LaunchNLS; + +/** + * DESCRIPTION: This class implements the tab that is shown when the user is + * editing the configuration to run a MOTODEV Studio for Android application + * + * RESPONSIBILITY: User interface to allow the user to enter information to + * launch the application. + * + * COLABORATORS: This class is one of the tabs of the + * LaunchConfigurationTabGroup + * + * USAGE: This class should be created/used by the LaunchConfigurationTabGroup + * only. + */ +@SuppressWarnings("restriction") +public class LaunchConfigurationTab extends AbstractLaunchConfigurationTab +{ + private static final String NAME = LaunchNLS.UI_LaunchConfigurationTab_Tab_Name; + + private static final Object UPDATE_WIDGETS_EVENT = new Object(); + + private Composite mainComposite; + + private String projectName = ""; //$NON-NLS-1$ + + private String activityName = ""; //$NON-NLS-1$ + + private String deviceName = ""; //$NON-NLS-1$ + + private boolean activitySpecified = false; + + private boolean runDefaultActivity = true; + + private final String LAUNCH_DIALOG_HELP = LaunchPlugin.PLUGIN_ID + ".mainLaunchTab"; //$NON-NLS-1$ + + private Button defaultLauncherButton = null; + + private Button vdlLauncherButton = null; + + private Button deviceNameBrowseButton = null; + + private final IInstanceListener instanceListener = new IInstanceListener() + { + + private void fireUpdate() + { + Display currentDisplay = PlatformUI.getWorkbench().getDisplay(); + if (!currentDisplay.isDisposed()) + { + currentDisplay.syncExec(new Runnable() + { + + public void run() + { + updateDeviceChooserButton(); + updateLaunchConfigurationDialog(); + } + }); + } + } + + public void instanceUpdated(InstanceEvent instanceevent) + { + fireUpdate(); + } + + public void instanceUnloaded(InstanceEvent instanceevent) + { + fireUpdate(); + } + + public void instanceTransitioned(InstanceEvent instanceevent) + { + fireUpdate(); + } + + public void instanceLoaded(InstanceEvent instanceevent) + { + fireUpdate(); + } + + public void instanceDeleted(InstanceEvent instanceevent) + { + fireUpdate(); + } + + public void instanceCreated(InstanceEvent instanceevent) + { + fireUpdate(); + } + + public void instanceAboutToTransition(InstanceEvent instanceevent) + { + fireUpdate(); + } + }; + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse.swt.widgets.Composite) + */ + public void createControl(Composite parent) + { + Composite main = new Composite(parent, SWT.NONE); + + GridLayout layout = new GridLayout(1, false); + GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); + gd.widthHint = 430; + gd.heightHint = 130; + main.setLayout(layout); + main.setLayoutData(gd); + + createMainInfoGroup(main); + setControl(main); + } + + private void updateDeviceChooserButton() + { + // button is always enabled + if (!deviceNameBrowseButton.isDisposed()) + { + deviceNameBrowseButton.setEnabled(true); + } + } + + /** + * Create the main information selection group + * @param mainComposite: the parent composite + */ + private void createMainInfoGroup(Composite mainComposite) + { + this.mainComposite = mainComposite; + + // create destination group + Group destinationGroup = new Group(mainComposite, SWT.NONE); + GridLayout layout = new GridLayout(3, false); + destinationGroup.setLayout(layout); + GridData defaultDestGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1); + destinationGroup.setLayoutData(defaultDestGridData); + destinationGroup.setText(LaunchNLS.LaunchComposite_UI_LaunchComposite_DestinationGroupText); + + // Project Name Label + Label projectNameLabel = new Label(destinationGroup, SWT.NONE); + projectNameLabel.setText(LaunchNLS.UI_LaunchComposite_ProjectNameLabel); + GridData folderGridData = new GridData(SWT.LEFT, SWT.CENTER, false, false); + projectNameLabel.setLayoutData(folderGridData); + + // Project Name Text + final Text projectNameText = new Text(destinationGroup, SWT.SINGLE | SWT.BORDER); + projectNameText.setText(projectName); + folderGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1); + projectNameText.setLayoutData(folderGridData); + projectNameText.addModifyListener(new ModifyListener() + { + /* + * (non-Javadoc) + * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent) + */ + public void modifyText(ModifyEvent e) + { + if (e.data == UPDATE_WIDGETS_EVENT) + { + projectNameText.setText(projectName); + } + else + { + projectName = projectNameText.getText(); + updateLaunchConfigurationDialog(); + } + } + }); + + // Project Name Browse Button + Button projectNameBrowseButton = new Button(destinationGroup, SWT.PUSH); + folderGridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1); + projectNameBrowseButton.setLayoutData(folderGridData); + projectNameBrowseButton.setText(LaunchNLS.UI_LaunchComposite_BrowseButton); + projectNameBrowseButton.addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetSelected(SelectionEvent e) + { + AndroidProjectsSelectionDialog dialog = + new AndroidProjectsSelectionDialog(getShell()); + int result = dialog.open(); + if (result == Dialog.OK) + { + Object resultProject = dialog.getFirstResult(); + if (resultProject instanceof IProject) + { + IProject project = (IProject) resultProject; + projectNameText.setText(project.getName()); + } + } + } + + }); + + Group activityGroup = new Group(mainComposite, SWT.NONE); + GridLayout activityLayout = new GridLayout(3, false); + activityGroup.setLayout(activityLayout); + GridData activityGrid = new GridData(SWT.FILL, SWT.CENTER, true, false, 3, 1); + activityGroup.setLayoutData(activityGrid); + activityGroup.setText(LaunchNLS.UI_LaunchComposite_ActivityGroupLabel); + + final Button defaultActivityButton = new Button(activityGroup, SWT.RADIO); + defaultActivityButton.setText(LaunchNLS.UI_LaunchComposite_ActivityDefaultButton); + GridData gridData = new GridData(GridData.FILL_HORIZONTAL); + gridData.horizontalSpan = 3; + defaultActivityButton.setLayoutData(gridData); + + // Activity Name Button + final Button specificActivityButton = new Button(activityGroup, SWT.RADIO); + specificActivityButton.setText(LaunchNLS.LaunchConfigurationTab_LaunchButton); + GridData activityData = new GridData(SWT.LEFT, SWT.CENTER, false, false); + specificActivityButton.setLayoutData(activityData); + + // Activity Name Text + final Text activityNameText = new Text(activityGroup, SWT.SINGLE | SWT.BORDER); + activityNameText.setText(activityName); + activityData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1); + activityNameText.setLayoutData(activityData); + activityNameText.addModifyListener(new ModifyListener() + { + /* + * (non-Javadoc) + * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent) + */ + public void modifyText(ModifyEvent e) + { + if (e.data == UPDATE_WIDGETS_EVENT) + { + activityNameText.setText(activityName); + } + else + { + activityName = activityNameText.getText(); + updateLaunchConfigurationDialog(); + } + } + }); + + // Activity Name Browse Button + final Button activityNameBrowseButton = new Button(activityGroup, SWT.PUSH); + activityData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1); + activityNameBrowseButton.setLayoutData(activityData); + activityNameBrowseButton.setText(LaunchNLS.UI_LaunchComposite_BrowseButton); + activityNameBrowseButton.addSelectionListener(new SelectionAdapter() + { + /** + * Retrieve all activities of a given project + * @return All the activities of a given project + */ + private Set<String> getAllActivities(String projectName) + { + String[] tempActivities = null; + Set<String> activities = new HashSet<String>(); + + if (projectName.length() != 0) + { + IProject selectedProject = LaunchUtils.getProject(projectName); + + tempActivities = LaunchUtils.getProjectActivities(selectedProject); + for (String s : tempActivities) + { + activities.add(s); + } + } + return activities; + } + + /* + * (non-Javadoc) + * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) + */ + @Override + public void widgetSelected(SelectionEvent e) + { + if (projectName.length() == 0) + { + IWorkbenchWindow ww = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + MessageDialog.openInformation(ww.getShell(), + LaunchNLS.UI_LaunchComposite_ProjectRequiredTitle, + LaunchNLS.UI_LaunchComposite_ProjectRequiredMessage); + } + else + { + + ElementListSelectionDialog dialog = + new ElementListSelectionDialog(getShell(), new LabelProvider() + { + @Override + public String getText(Object element) + { + String activity = (String) element; + return activity; + } + }) + { + /* + * (non-Javadoc) + * @see org.eclipse.ui.dialogs.ElementListSelectionDialog#createDialogArea(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createDialogArea(Composite parent) + { + PlatformUI.getWorkbench().getHelpSystem() + .setHelp(parent, ACTIVITY_SELECTION_DIALOG_HELPID); + return super.createDialogArea(parent); + } + + }; + + dialog.setTitle(LaunchNLS.UI_LaunchComposite_SelectActivityScreenTitle); + dialog.setMessage(LaunchNLS.UI_LaunchComposite_SelectActivityScreenMessage); + + Object[] allActivities = getAllActivities(projectNameText.getText()).toArray(); + if (allActivities.length == 0) + { + activityNameText.setText(""); //$NON-NLS-1$ + } + else + { + dialog.setElements(getAllActivities(projectNameText.getText()).toArray()); + + int buttonId = dialog.open(); + if (buttonId == IDialogConstants.OK_ID) + { + String activity = (String) dialog.getFirstResult(); + activityNameText.setText(activity); + + } + } + } + } + + protected static final String ACTIVITY_SELECTION_DIALOG_HELPID = + "com.motorola.studio.android.launch.activitySelectionDialog"; //$NON-NLS-1$ + }); + + final Button noActivityButton = new Button(activityGroup, SWT.RADIO); + noActivityButton.setText(LaunchNLS.LaunchConfigurationTab_DoNothingButton); + gridData = new GridData(GridData.FILL_HORIZONTAL); + gridData.horizontalSpan = 3; + noActivityButton.setLayoutData(gridData); + + defaultActivityButton.addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetSelected(SelectionEvent e) + { + if (e.data == UPDATE_WIDGETS_EVENT) + { + defaultActivityButton.setSelection(!activitySpecified && runDefaultActivity); + activityNameText.setEnabled(activitySpecified); + activityNameBrowseButton.setEnabled(activitySpecified); + } + else + { + // handle variables + handleActivityLauncherTypeVariables(defaultActivityButton, + specificActivityButton, activityNameText, activityNameBrowseButton); + } + } + }); + + specificActivityButton.addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetSelected(SelectionEvent e) + { + if (e.data == UPDATE_WIDGETS_EVENT) + { + specificActivityButton.setSelection(activitySpecified && !runDefaultActivity); + activityNameText.setEnabled(activitySpecified); + activityNameBrowseButton.setEnabled(activitySpecified); + } + else + { + // handle variables + handleActivityLauncherTypeVariables(defaultActivityButton, + specificActivityButton, activityNameText, activityNameBrowseButton); + } + } + }); + + noActivityButton.addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetSelected(SelectionEvent e) + { + if (e.data == UPDATE_WIDGETS_EVENT) + { + noActivityButton.setSelection(!activitySpecified && !runDefaultActivity); + activityNameText.setEnabled(activitySpecified); + activityNameBrowseButton.setEnabled(activitySpecified); + } + else + { + // handle variables + handleActivityLauncherTypeVariables(defaultActivityButton, + specificActivityButton, activityNameText, activityNameBrowseButton); + } + } + }); + + // Device Name Label + Label deviceNameLabel = new Label(destinationGroup, SWT.NONE); + deviceNameLabel.setText(LaunchNLS.UI_LaunchComposite_DeviceNameLabel); + GridData deviceGridData = new GridData(SWT.LEFT, SWT.CENTER, false, false); + deviceNameLabel.setLayoutData(deviceGridData); + + // Device Name Text + final Text deviceNameText = new Text(destinationGroup, SWT.SINGLE | SWT.BORDER); + deviceNameText.setText(deviceName); + deviceGridData = new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1); + deviceNameText.setLayoutData(deviceGridData); + deviceNameText.addModifyListener(new ModifyListener() + { + /* + * (non-Javadoc) + * @see org.eclipse.swt.events.ModifyListener#modifyText(org.eclipse.swt.events.ModifyEvent) + */ + public void modifyText(ModifyEvent e) + { + if (e.data == UPDATE_WIDGETS_EVENT) + { + deviceNameText.setText(deviceName); + } + else + { + deviceName = deviceNameText.getText(); + updateLaunchConfigurationDialog(); + } + } + }); + + // Device Name Browse Button + deviceNameBrowseButton = new Button(destinationGroup, SWT.PUSH); + deviceGridData = new GridData(SWT.FILL, SWT.CENTER, false, false, 1, 1); + deviceNameBrowseButton.setLayoutData(deviceGridData); + deviceNameBrowseButton.setText(LaunchNLS.UI_LaunchComposite_BrowseButton); + deviceNameBrowseButton.addSelectionListener(new SelectionAdapter() + { + + /* + * (non-Javadoc) + * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent) + */ + @Override + public void widgetSelected(SelectionEvent e) + { + IProject selectedProject = LaunchUtils.getProject(projectNameText.getText()); + DeviceSelectionDialog dialog = + new DeviceSelectionDialog(getShell(), + LaunchNLS.UI_LaunchComposite_SelectDeviceScreenMessage, + selectedProject); + dialog.setTitle(LaunchNLS.UI_LaunchComposite_SelectDeviceScreenTitle); + dialog.setMultipleSelection(false); + dialog.setValidator(new ISelectionStatusValidator() + { + + public IStatus validate(Object[] selection) + { + IStatus status = new Status(IStatus.OK, LaunchPlugin.PLUGIN_ID, ""); //$NON-NLS-1$ + if (selection.length == 0) + { + status = + new Status(IStatus.ERROR, LaunchPlugin.PLUGIN_ID, + "No selected instance"); //$NON-NLS-1$ + } + return status; + } + }); + int res = dialog.open(); + if (res == IDialogConstants.OK_ID) + { + ISerialNumbered serialNumbered = (ISerialNumbered) dialog.getFirstResult(); + String selectedDevice = ((IInstance) serialNumbered).getName(); + deviceNameText.setText(selectedDevice); + } + } + + }); + + InstanceEventManager.getInstance().addInstanceListener(instanceListener); + + Link createNewAvdLink = new Link(destinationGroup, SWT.NONE); + deviceGridData = new GridData(SWT.RIGHT, SWT.CENTER, true, false, 3, 1); + createNewAvdLink.setLayoutData(deviceGridData); + createNewAvdLink.setText(LaunchNLS.LaunchConfigurationTab_CreateNewAVDLink); + createNewAvdLink.addSelectionListener(new SelectionAdapter() + { + @Override + public void widgetSelected(SelectionEvent e) + { + OpenNewDeviceWizardHandler handler = new OpenNewDeviceWizardHandler(); + try + { + handler.execute(new ExecutionEvent()); + } + catch (ExecutionException exception) + { + //do nothing + } + } + }); + + mainComposite.addListener(SWT.Modify, new Listener() + { + /* + * (non-Javadoc) + * @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event) + */ + public void handleEvent(Event e) + { + projectNameText.notifyListeners(SWT.Modify, e); + activityNameText.notifyListeners(SWT.Modify, e); + deviceNameText.notifyListeners(SWT.Modify, e); + defaultActivityButton.notifyListeners(SWT.Selection, e); + specificActivityButton.notifyListeners(SWT.Selection, e); + noActivityButton.notifyListeners(SWT.Selection, e); + + if (defaultLauncherButton != null) + { + defaultLauncherButton.notifyListeners(SWT.Selection, e); + } + if (vdlLauncherButton != null) + { + vdlLauncherButton.notifyListeners(SWT.Selection, e); + } + } + }); + + PlatformUI.getWorkbench().getHelpSystem().setHelp(mainComposite, LAUNCH_DIALOG_HELP); //$NON-NLS-1$ + } + + /** + * Handle the variables regarding Activity Launcher options. + * + * @param defaultActivityButton {@link Button} for Default Activity. + * @param specificActivityButton {@link Button} for Specific Activity. + * @param activityNameText {@link Text} holding the Activity to be launched name. + * @param activityNameBrowseButton Activity browser {@link Button}. + */ + private void handleActivityLauncherTypeVariables(final Button defaultActivityButton, + final Button specificActivityButton, final Text activityNameText, + final Button activityNameBrowseButton) + { + activitySpecified = specificActivityButton.getSelection(); + runDefaultActivity = defaultActivityButton.getSelection(); + activityNameText.setEnabled(activitySpecified); + activityNameBrowseButton.setEnabled(activitySpecified); + updateLaunchConfigurationDialog(); + } + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() + */ + public String getName() + { + return LaunchConfigurationTab.NAME; + } + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage() + */ + @Override + public Image getImage() + { + return AbstractUIPlugin.imageDescriptorFromPlugin(LaunchPlugin.PLUGIN_ID, + ILaunchConfigurationConstants.MOTODEV_APP_ICO).createImage(); + } + + @Override + public void dispose() + { + InstanceEventManager.getInstance().removeInstanceListener(instanceListener); + super.dispose(); + } + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse.debug.core.ILaunchConfiguration) + */ + public void initializeFrom(ILaunchConfiguration configuration) + { + // Assure that when loading the configuration, the TmL devices are in sync with the + // AVD available at the SDK + InstancesListRefresh.refresh(); + + try + { + projectName = + configuration.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, + ILaunchConfigurationConstants.DEFAULT_VALUE); + + activityName = + configuration.getAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY, + ILaunchConfigurationConstants.DEFAULT_VALUE); + + activitySpecified = + (configuration.getAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION, + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY)) == ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY; + + runDefaultActivity = + (configuration.getAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION, + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY)) == ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT; + + deviceName = + configuration.getAttribute( + ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, + ILaunchConfigurationConstants.DEFAULT_VALUE); + + Event e = new Event(); + e.type = SWT.Modify; + e.data = UPDATE_WIDGETS_EVENT; + mainComposite.notifyListeners(SWT.Modify, e); + } + catch (CoreException e) + { + // Do nothing for now + } + } + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void performApply(ILaunchConfigurationWorkingCopy configuration) + { + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, projectName); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY, activityName); + + // For now we are not preventing the device chooser dialog to appear if the user choose a + // handset in the device field. However, if the user chooses an AVD, we set the preferred + // AVD field so that we force the launch to happen in the selected AVD without asking the + // user. + Collection<String> validAvds = SdkUtils.getAllValidVmNames(); + if (validAvds.contains(deviceName)) + { + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, + deviceName); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME, + deviceName); + } + else + { + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, + deviceName); + configuration + .removeAttribute(ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME); + } + + if (activitySpecified) + { + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION, + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_ACTIVITY); + } + else if (runDefaultActivity) + { + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION, + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT); + } + else + { + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION, + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DO_NOTHING); + } + + LaunchUtils.updateLaunchConfigurationDefaults(configuration); + + IProject project = LaunchUtils.getProject(projectName); + IResource[] mappedResources = null; + if (project != null) + { + mappedResources = new IResource[] + { + project + }; + } + + configuration.setMappedResources(mappedResources); + } + + /** + * @see org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse.debug.core.ILaunchConfigurationWorkingCopy) + */ + public void setDefaults(ILaunchConfigurationWorkingCopy configuration) + { + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, + ILaunchConfigurationConstants.DEFAULT_VALUE); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY, + ILaunchConfigurationConstants.DEFAULT_VALUE); + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION, + ILaunchConfigurationConstants.ATTR_LAUNCH_ACTION_DEFAULT); + // It is default not to exist Preferred AVD attribute, so we just set the Studio's + // device instance name attribute here + configuration.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, + ILaunchConfigurationConstants.DEFAULT_VALUE); + + LaunchUtils.setADTLaunchConfigurationDefaults(configuration); + + projectName = ILaunchConfigurationConstants.DEFAULT_VALUE; //$NON-NLS-1$ + activityName = ILaunchConfigurationConstants.DEFAULT_VALUE; //$NON-NLS-1$ + deviceName = ILaunchConfigurationConstants.DEFAULT_VALUE; //$NON-NLS-1$ + activitySpecified = ILaunchConfigurationConstants.DEFAULT_BOOL_VALUE; + runDefaultActivity = !ILaunchConfigurationConstants.DEFAULT_BOOL_VALUE; + + if (mainComposite != null) + { + Event e = new Event(); + e.type = SWT.Modify; + e.data = UPDATE_WIDGETS_EVENT; + mainComposite.notifyListeners(SWT.Modify, e); + } + } + + /** + * @see ILaunchConfigurationTab#isValid(ILaunchConfiguration) + */ + @Override + public boolean isValid(ILaunchConfiguration launchConfig) + { + boolean isValid = true; + boolean hasWarning = false; + + String projectName = ""; //$NON-NLS-1$ + String instanceName = ""; //$NON-NLS-1$ + String activityName = ""; //$NON-NLS-1$ + + try + { + projectName = + launchConfig.getAttribute(ILaunchConfigurationConstants.ATTR_PROJECT_NAME, + ILaunchConfigurationConstants.DEFAULT_VALUE); + instanceName = + launchConfig.getAttribute( + ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME, (String) null); + activityName = + launchConfig.getAttribute(ILaunchConfigurationConstants.ATTR_ACTIVITY, + ILaunchConfigurationConstants.DEFAULT_VALUE); + } + catch (CoreException e) + { + StudioLogger.error(LaunchConfigurationTab.class, + "Error validating launch configuration " + launchConfig.getName(), e); //$NON-NLS-1$ + } + + /* Validate current project */ + + IProject project = null; + + if (isValid && (projectName.length() > 0)) + { + Path projectPath = new Path(projectName); + if (!projectPath.isValidSegment(projectName)) + { + isValid = false; + setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST); + } + project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); + if ((project != null) && !project.exists()) + { + isValid = false; + setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST); + } + else if ((project != null) && SdkUtils.isLibraryProject(project)) + { + isValid = false; + setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_IS_LIBRARY); + } + else if (project == null) + { + isValid = false; + setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_PROJECT_NOT_EXIST); + } + } + else if (isValid && (projectName.length() == 0)) + { + setErrorMessage(null); + } + + // if we have a chosen project, enable/disable the device selection + if (project != null) + { + updateDeviceChooserButton(); + } + + /* Validate current device instance */ + if (isValid && (instanceName != null) && (instanceName.length() > 0)) + { + IStatus compatible = LaunchUtils.isCompatible(project, instanceName); + if (compatible == null) + { + setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_DEVICE_INEXISTENT); + isValid = false; + } + else if (compatible.getSeverity() == IStatus.ERROR) + { + setErrorMessage(compatible.getMessage()); + isValid = false; + } + else if (compatible.getSeverity() == IStatus.WARNING) + { + setMessage(compatible.getMessage()); + hasWarning = true; + } + } + else if (isValid && (instanceName != null) && (instanceName.length() == 0)) + { + setErrorMessage(null); + } + + /* Validate current activity */ + if (isValid && (activityName.length() > 0) && activitySpecified) + { + /* + * Check if the activity is valid in the current METAINF project + * file + */ + + Activity[] currentActivities = null; + boolean activityValid = false; + + ManifestData manifestParser = null; + try + { + manifestParser = AndroidManifestParser.parse(new IFolderWrapper(project)); + } + catch (Exception e) + { + StudioLogger.error(LaunchUtils.class, + "An error occurred trying to parse AndroidManifest", e); //$NON-NLS-1$ + } + if (manifestParser != null) + { + currentActivities = manifestParser.getActivities(); + } + else + { + // There's a problem with the manifest file / parser. Invalidate + // current settings. + isValid = false; + setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_INVALID_ACTIVITY); + } + + /* See if the chosen activity is there */ + + for (Activity s : currentActivities) + { + if (s.getName().equals(activityName)) + { + activityValid = true; + } + } + + if (!activityValid) + { + isValid = false; + setErrorMessage(LaunchNLS.UI_LaunchConfigurationTab_ERR_ACTIVITY_NOT_EXIST); + } + + } + else if (isValid && ((activityName.length() == 0) && activitySpecified)) + { + setErrorMessage(null); + } + + /* Wrap up validation */ + if (isValid + && ((projectName.length() == 0) + || ((activitySpecified) && (activityName.length() == 0)) || (instanceName + .length() == 0))) + { + isValid = false; + + if (projectName.length() == 0) + { + setMessage(LaunchNLS.UI_LaunchConfigurationTab_InfoSelectProject); + } + else if (instanceName.length() == 0) + { + setMessage(LaunchNLS.UI_LaunchConfigurationTab_InfoSelectInstance); + } + else if (activityName.length() == 0) + { + setMessage(LaunchNLS.UI_LaunchConfigurationTab_InfoSelectActivity); + } + + } + + if (isValid) + { + setErrorMessage(null); + if (!hasWarning) + { + setMessage(null); + } + } + + return isValid; + } +} diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTabGroup.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTabGroup.java new file mode 100644 index 0000000..4799e0e --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/LaunchConfigurationTabGroup.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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.motorola.studio.android.launch.ui; + +import org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup; +import org.eclipse.debug.ui.CommonTab; +import org.eclipse.debug.ui.ILaunchConfigurationDialog; +import org.eclipse.debug.ui.ILaunchConfigurationTab; + +/** + * DESCRIPTION: + * This class builds the configuration tabs that are displayed when the user is + * editing the configuration to run MOTODEV Studio for Android applications. + * + * RESPONSIBILITY: + * Build the configuration tab of the "Run As" features. + * + * COLABORATORS: + * << class relationship> + * + * USAGE: + * Used only by the extension definition. + */ +public class LaunchConfigurationTabGroup extends AbstractLaunchConfigurationTabGroup +{ + /** + * Creates the tabs + * + * @param dialog dialog + * @param mode the launch mode + */ + public void createTabs(ILaunchConfigurationDialog dialog, String mode) + { + ILaunchConfigurationTab mainLaunchTab = new LaunchConfigurationTab(); + setTabs(new ILaunchConfigurationTab[] + { + mainLaunchTab, new CommonTab() + }); + } + +} diff --git a/src/plugins/launch/src/com/motorola/studio/android/launch/ui/StartedInstancesDialog.java b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/StartedInstancesDialog.java new file mode 100644 index 0000000..f7c03dd --- /dev/null +++ b/src/plugins/launch/src/com/motorola/studio/android/launch/ui/StartedInstancesDialog.java @@ -0,0 +1,386 @@ +/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
+ *
+ * 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.motorola.studio.android.launch.ui;
+
+import java.util.Collection;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.TitleAreaDialog;
+import org.eclipse.jface.viewers.ArrayContentProvider;
+import org.eclipse.jface.viewers.ColumnLabelProvider;
+import org.eclipse.jface.viewers.ColumnViewerToolTipSupport;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.TableViewer;
+import org.eclipse.jface.viewers.TableViewerColumn;
+import org.eclipse.jface.window.ToolTip;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.ISharedImages;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.plugin.AbstractUIPlugin;
+
+import com.motorola.studio.android.AndroidPlugin;
+import com.motorola.studio.android.common.log.StudioLogger;
+import com.motorola.studio.android.emulator.core.devfrm.DeviceFrameworkManager;
+import com.motorola.studio.android.emulator.core.model.IAndroidEmulatorInstance;
+import com.motorola.studio.android.launch.ILaunchConfigurationConstants;
+import com.motorola.studio.android.launch.LaunchPlugin;
+import com.motorola.studio.android.launch.LaunchUtils;
+import com.motorola.studio.android.launch.i18n.LaunchNLS;
+
+/**
+ * This class shows a dialog to the user with online AVDs compatibles with prefferedAvd.
+ * The user can choose an online AVD and click Ok, ignore the dialog or abort the ongoing action.
+ */
+public class StartedInstancesDialog extends TitleAreaDialog
+{
+
+ Collection<IAndroidEmulatorInstance> compatibleStartedInstances = null;
+
+ private TableViewer viewer;
+
+ private IAndroidEmulatorInstance selectedInstance;
+
+ private Button okButton;
+
+ private Button ignoreButton;
+
+ private Button abortButton;
+
+ private final IAndroidEmulatorInstance preferredAvd;
+
+ private IProject project = null;
+
+ private ILaunchConfiguration configuration = null;
+
+ private boolean isUpdateConfigurationSelected = false;
+
+ private static String DIALOG_IMAGE = "icons/choose_compatible_avd_instance.png";
+
+ private static final String STARTED_INSTANCES_HELP_ID = AndroidPlugin.PLUGIN_ID
+ + ".started_instances_selection_dialog";
+
+ /**
+ * @param parentShell
+ * @throws CoreException
+ */
+ public StartedInstancesDialog(Shell parentShell,
+ Collection<IAndroidEmulatorInstance> compatibleStartedInstances,
+ ILaunchConfiguration configuration, IProject project) throws CoreException
+ {
+ super(parentShell);
+
+ this.configuration = configuration;
+ this.compatibleStartedInstances = compatibleStartedInstances;
+ this.project = project;
+
+ final String instanceName =
+ configuration.getAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ (String) null);
+
+ this.preferredAvd = DeviceFrameworkManager.getInstance().getInstanceByName(instanceName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.TitleAreaDialog#createContents(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createContents(Composite parent)
+ {
+ Control control = super.createContents(parent);
+
+ getShell().setText(LaunchNLS.UI_StartedInstancesDialog_WindowTitle);
+
+ setTitle(NLS.bind(LaunchNLS.UI_StartedInstancesDialog_Title, preferredAvd.getName()));
+ setMessage(LaunchNLS.UI_StartedInstancesDialog_Message);
+ setTitleImage(AbstractUIPlugin.imageDescriptorFromPlugin(LaunchPlugin.PLUGIN_ID,
+ DIALOG_IMAGE).createImage());
+
+ enableOkButton();
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(parent, STARTED_INSTANCES_HELP_ID);
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(control, STARTED_INSTANCES_HELP_ID);
+
+ return control;
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected void createButtonsForButtonBar(Composite parent)
+ {
+ // create OK, Ignore and Abort buttons
+ okButton = createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
+ ignoreButton =
+ createButton(parent, IDialogConstants.IGNORE_ID, IDialogConstants.IGNORE_LABEL,
+ false);
+ abortButton =
+ createButton(parent, IDialogConstants.ABORT_ID, IDialogConstants.ABORT_LABEL, false);
+
+ ignoreButton.addMouseListener(new MouseListener()
+ {
+
+ public void mouseUp(MouseEvent e)
+ {
+ setReturnCode(IDialogConstants.IGNORE_ID);
+ close();
+ }
+
+ public void mouseDown(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ //do nothing
+ }
+ });
+
+ abortButton.addMouseListener(new MouseListener()
+ {
+
+ public void mouseUp(MouseEvent e)
+ {
+ setReturnCode(IDialogConstants.ABORT_ID);
+ close();
+ }
+
+ public void mouseDown(MouseEvent e)
+ {
+ //do nothing
+ }
+
+ public void mouseDoubleClick(MouseEvent e)
+ {
+ //do nothing
+ }
+ });
+ }
+
+ /**
+ * Handles the enablement of the OK button.
+ */
+ private void enableOkButton()
+ {
+ if (viewer.getTable().getSelectionCount() > 0)
+ {
+ okButton.setEnabled(true);
+ }
+ else
+ {
+ okButton.setEnabled(false);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
+ */
+ @Override
+ protected Control createDialogArea(Composite parent)
+ {
+ GridLayout layout = new GridLayout(1, false);
+ parent.setLayout(layout);
+
+ createTableArea(parent);
+
+ return parent;
+ }
+
+ /**
+ * @param parent
+ */
+ private void createTableArea(Composite parent)
+ {
+ viewer =
+ new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL
+ | SWT.FULL_SELECTION | SWT.BORDER);
+ viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));
+ viewer.getTable().setHeaderVisible(true);
+ viewer.getTable().setLinesVisible(false);
+
+ TableViewerColumn avds = new TableViewerColumn(viewer, SWT.NONE);
+ avds.getColumn().setText(LaunchNLS.UI_StartedInstancesDialog_CompatibleAvdsColumnName);
+ avds.getColumn().setResizable(true);
+ avds.getColumn().setWidth(480);
+
+ viewer.addSelectionChangedListener(new ISelectionChangedListener()
+ {
+
+ public void selectionChanged(SelectionChangedEvent event)
+ {
+ enableOkButton();
+ }
+ });
+
+ avds.setLabelProvider(new ColumnLabelProvider()
+ {
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object)
+ */
+ @Override
+ public String getText(Object element)
+ {
+ IAndroidEmulatorInstance instance = (IAndroidEmulatorInstance) element;
+ return instance.getName() + " (" + instance.getTarget() + ", "
+ + LaunchNLS.UI_StartedInstancesDialog_ApiLevel + " "
+ + instance.getAPILevel() + ")";
+ }
+
+ @Override
+ public Image getImage(Object element)
+ {
+
+ Image img = null;
+
+ IAndroidEmulatorInstance instance = (IAndroidEmulatorInstance) element;
+ IStatus compatible = LaunchUtils.isCompatible(project, instance.getName());
+
+ // notify the warning state
+ if (compatible.getSeverity() == IStatus.WARNING)
+ {
+ img =
+ PlatformUI.getWorkbench().getSharedImages()
+ .getImage(ISharedImages.IMG_OBJS_WARN_TSK);
+ }
+
+ return img;
+ }
+
+ @Override
+ public String getToolTipText(Object element)
+ {
+ String toolTip = null;
+
+ IAndroidEmulatorInstance instance = (IAndroidEmulatorInstance) element;
+ IStatus compatible = LaunchUtils.isCompatible(project, instance.getName());
+
+ if (compatible.getSeverity() == IStatus.WARNING)
+ {
+ toolTip = LaunchNLS.UI_StartedInstancesDialog_Tooltip;
+
+ }
+
+ return toolTip;
+ }
+
+ @Override
+ public int getToolTipDisplayDelayTime(Object object)
+ {
+ return 500;
+ }
+
+ @Override
+ public int getToolTipTimeDisplayed(Object object)
+ {
+ return 5000;
+ }
+
+ });
+
+ ColumnViewerToolTipSupport.enableFor(viewer, ToolTip.NO_RECREATE);
+
+ ArrayContentProvider provider = new ArrayContentProvider();
+ viewer.setContentProvider(provider);
+ viewer.setInput(compatibleStartedInstances);
+
+ Button checkBox = new Button(parent, SWT.CHECK);
+ GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
+ checkBox.setLayoutData(gridData);
+ checkBox.setText(LaunchNLS.UI_StartedInstancesDialog_UpdateRunConfigurarion);
+ checkBox.addSelectionListener(new SelectionAdapter()
+ {
+
+ @Override
+ public void widgetSelected(SelectionEvent e)
+ {
+ if (isUpdateConfigurationSelected)
+ {
+ isUpdateConfigurationSelected = false;
+ }
+ else
+ {
+ isUpdateConfigurationSelected = true;
+ }
+ }
+ });
+ }
+
+ public IAndroidEmulatorInstance getSelectedInstance()
+ {
+ return selectedInstance;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.dialogs.Dialog#okPressed()
+ */
+ @Override
+ protected void okPressed()
+ {
+ selectedInstance = null;
+
+ if (viewer.getTable().getSelectionCount() > 0)
+ {
+ selectedInstance =
+ (IAndroidEmulatorInstance) viewer.getTable().getSelection()[0].getData();
+ }
+
+ if (isUpdateConfigurationSelected)
+ {
+ try
+ {
+ updateRunConfiguration();
+ }
+ catch (CoreException e)
+ {
+ StudioLogger.error(StartedInstancesDialog.class,
+ "It was not possible to update the current run configuration");
+ }
+ }
+
+ super.okPressed();
+ }
+
+ private void updateRunConfiguration() throws CoreException
+ {
+ ILaunchConfigurationWorkingCopy workingCopy = configuration.getWorkingCopy();
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_DEVICE_INSTANCE_NAME,
+ selectedInstance.getName());
+ workingCopy.setAttribute(ILaunchConfigurationConstants.ATTR_ADT_DEVICE_INSTANCE_NAME,
+ selectedInstance.getName());
+ workingCopy.doSave();
+ }
+}
|