| /* |
| * 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; |
| } |
| |
| } |