blob: 10b7d2782a6baaa325aa664ed83f17387b23f813 [file] [log] [blame]
/*
* 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;
}
}