Files
openide/plugins/android/src/org/jetbrains/android/run/AndroidRunConfigurationBase.java
T
2011-01-28 20:46:18 +03:00

423 lines
16 KiB
Java

/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* 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 org.jetbrains.android.run;
import com.android.ddmlib.*;
import com.android.sdklib.internal.avd.AvdManager;
import com.intellij.CommonBundle;
import com.intellij.diagnostic.logging.LogConsole;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.*;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.ui.ConsoleView;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.DependencyScope;
import com.intellij.openapi.roots.ModuleOrderEntry;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.roots.OrderEntry;
import com.intellij.openapi.roots.ui.configuration.ModulesConfigurator;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.DefaultJDOMExternalizer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.util.PsiNavigateUtil;
import com.intellij.util.containers.HashMap;
import com.intellij.util.xml.GenericAttributeValue;
import com.intellij.util.xml.converters.values.BooleanValueConverter;
import org.jdom.Element;
import org.jetbrains.android.actions.AndroidEnableDdmsAction;
import org.jetbrains.android.dom.manifest.Application;
import org.jetbrains.android.dom.manifest.Manifest;
import org.jetbrains.android.facet.AndroidFacet;
import org.jetbrains.android.facet.AndroidFacetConfiguration;
import org.jetbrains.android.logcat.AndroidLogFilterModel;
import org.jetbrains.android.logcat.AndroidLogcatFiltersPreferences;
import org.jetbrains.android.sdk.AndroidPlatform;
import org.jetbrains.android.sdk.AndroidSdk;
import org.jetbrains.android.sdk.AndroidSdkImpl;
import org.jetbrains.android.util.AndroidBundle;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Created by IntelliJ IDEA.
* User: Eugene.Kudelevsky
* Date: Aug 27, 2009
* Time: 2:20:54 PM
* To change this template use File | Settings | File Templates.
*/
public abstract class AndroidRunConfigurationBase extends ModuleBasedConfiguration<JavaRunConfigurationModule> {
@NonNls private static final String ANDROID_TARGET_DEVICES_PROPERTY = "AndroidTargetDevices";
public boolean CHOOSE_DEVICE_MANUALLY = false;
public String PREFERRED_AVD = "";
public String COMMAND_LINE = "";
public boolean WIPE_USER_DATA = false;
public boolean DISABLE_BOOT_ANIMATION = false;
public String NETWORK_SPEED = "full";
public String NETWORK_LATENCY = "none";
public boolean CLEAR_LOGCAT = false;
public AndroidRunConfigurationBase(final String name, final Project project, final ConfigurationFactory factory) {
super(name, new JavaRunConfigurationModule(project, false), factory);
}
@Override
public final void checkConfiguration() throws RuntimeConfigurationException {
JavaRunConfigurationModule configurationModule = getConfigurationModule();
configurationModule.checkForWarning();
Module module = configurationModule.getModule();
if (module == null) return;
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet == null) {
throw new RuntimeConfigurationError(AndroidBundle.message("android.no.facet.error"));
}
if (facet.getConfiguration().LIBRARY_PROJECT) {
throw new RuntimeConfigurationError(AndroidBundle.message("android.cannot.run.library.project.error"));
}
if (facet.getConfiguration().getAndroidPlatform() == null) {
throw new RuntimeConfigurationError(AndroidBundle.message("select.platform.error"));
}
if (facet.getManifest() == null) {
throw new RuntimeConfigurationError(AndroidBundle.message("android.manifest.not.found.error"));
}
if (PREFERRED_AVD.length() > 0) {
AvdManager avdManager = facet.getAvdManagerSilently();
if (avdManager == null) {
AndroidSdk sdk = facet.getConfiguration().getAndroidSdk();
if (sdk instanceof AndroidSdkImpl) {
throw new RuntimeConfigurationError(AndroidBundle.message("avd.cannot.be.loaded.error"));
}
}
if (avdManager != null) {
AvdManager.AvdInfo avdInfo = avdManager.getAvd(PREFERRED_AVD, false);
if (avdInfo == null) {
throw new RuntimeConfigurationError(AndroidBundle.message("avd.not.found.error", PREFERRED_AVD));
}
if (!facet.isCompatibleAvd(avdInfo)) {
throw new RuntimeConfigurationError(AndroidBundle.message("avd.not.compatible.error", PREFERRED_AVD));
}
if (avdInfo.getStatus() != AvdManager.AvdInfo.AvdStatus.OK) {
throw new RuntimeConfigurationError(AndroidBundle.message("avd.not.valid.error", PREFERRED_AVD));
}
}
}
checkConfiguration(facet);
}
protected abstract void checkConfiguration(@NotNull AndroidFacet facet) throws RuntimeConfigurationException;
public Collection<Module> getValidModules() {
final List<Module> result = new ArrayList<Module>();
Module[] modules = ModuleManager.getInstance(getProject()).getModules();
for (Module module : modules) {
if (AndroidFacet.getInstance(module) != null) {
result.add(module);
}
}
return result;
}
private static boolean fillRuntimeAndTestDependencies(@NotNull Module module, @NotNull Map<AndroidFacet, String> module2PackageName) {
for (OrderEntry entry : ModuleRootManager.getInstance(module).getOrderEntries()) {
if (entry instanceof ModuleOrderEntry) {
ModuleOrderEntry moduleOrderEntry = (ModuleOrderEntry)entry;
Module depModule = moduleOrderEntry.getModule();
if (depModule != null) {
AndroidFacet depFacet = AndroidFacet.getInstance(depModule);
if (depFacet != null &&
!module2PackageName.containsKey(depFacet) &&
!depFacet.getConfiguration().LIBRARY_PROJECT &&
moduleOrderEntry.getScope() != DependencyScope.COMPILE) {
String packageName = getPackageName(depFacet);
if (packageName == null) {
return false;
}
module2PackageName.put(depFacet, packageName);
if (!fillRuntimeAndTestDependencies(depModule, module2PackageName)) {
return false;
}
}
}
}
}
return true;
}
private static boolean containsRealDevice(@NotNull IDevice[] devices) {
for (IDevice device : devices) {
if (!device.isEmulator()) {
return true;
}
}
return false;
}
public RunProfileState getState(@NotNull final Executor executor, @NotNull ExecutionEnvironment env) throws ExecutionException {
final Module module = getConfigurationModule().getModule();
if (module == null) {
throw new ExecutionException("Module is not found");
}
AndroidFacet facet = AndroidFacet.getInstance(module);
if (facet == null) {
throw new ExecutionException(AndroidBundle.message("no.facet.error", module.getName()));
}
Project project = env.getProject();
boolean debug = DefaultDebugExecutor.EXECUTOR_ID.equals(executor.getId());
if (debug) {
if (!activateDdmsIfNeccessary(facet)) {
return null;
}
if (!CHOOSE_DEVICE_MANUALLY && PREFERRED_AVD.length() == 0) {
if (!checkDebuggableOption(facet)) {
return null;
}
}
}
AndroidFacetConfiguration configuration = facet.getConfiguration();
AndroidPlatform platform = configuration.getAndroidPlatform();
if (platform == null) {
Messages.showErrorDialog(project, AndroidBundle.message("specify.platform.error"), CommonBundle.getErrorTitle());
ModulesConfigurator.showFacetSettingsDialog(facet, null);
return null;
}
else {
String aPackage = getPackageName(facet);
if (aPackage == null) return null;
Map<AndroidFacet, String> depModule2PackageName = new HashMap<AndroidFacet, String>();
if (!fillRuntimeAndTestDependencies(module, depModule2PackageName)) return null;
if (platform.getSdk().getDebugBridge(project) == null) return null;
IDevice[] targetDevices = new IDevice[0];
if (CHOOSE_DEVICE_MANUALLY) {
IDevice[] devices = chooseDevicesManually(facet);
if (devices.length > 0) {
if (debug && containsRealDevice(devices)) {
if (!checkDebuggableOption(facet)) {
return null;
}
}
targetDevices = devices;
PropertiesComponent.getInstance(getProject()).setValue(ANDROID_TARGET_DEVICES_PROPERTY, toString(targetDevices));
}
if (targetDevices.length == 0) return null;
}
AndroidApplicationLauncher applicationLauncher = getApplicationLauncher(facet);
if (applicationLauncher != null) {
return new AndroidRunningState(env, facet, targetDevices, PREFERRED_AVD.length() > 0 ? PREFERRED_AVD : null,
computeCommandLine(), aPackage, applicationLauncher, depModule2PackageName) {
@NotNull
@Override
protected ConsoleView attachConsole() throws ExecutionException {
return AndroidRunConfigurationBase.this.attachConsole(this, executor);
}
};
}
}
return null;
}
private static boolean checkDebuggableOption(@NotNull AndroidFacet facet) {
Manifest manifest = facet.getManifest();
// validated in checkConfiguration()
assert manifest != null;
final Application application = manifest.getApplication();
if (application != null) {
String debuggable = application.getDebuggable().getValue();
BooleanValueConverter booleanValueConverter = BooleanValueConverter.getInstance(true);
if (debuggable == null || !booleanValueConverter.isTrue(debuggable)) {
Project project = facet.getModule().getProject();
int result = Messages.showYesNoCancelDialog(project, AndroidBundle.message("android.manifest.debuggable.attribute.not.true.warning"),
CommonBundle.getWarningTitle(),
Messages.getWarningIcon());
if (result == 0) {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
@Override
public void run() {
application.getDebuggable().setValue("true");
}
});
}
return result != 2;
}
}
return true;
}
private static boolean activateDdmsIfNeccessary(@NotNull AndroidFacet facet) {
Project project = facet.getModule().getProject();
boolean ddmsEnabled = AndroidEnableDdmsAction.isDdmsEnabled();
if (ddmsEnabled && isDdmsCorrupted(facet)) {
ddmsEnabled = false;
AndroidEnableDdmsAction.setDdmsEnabled(project, false);
}
if (!ddmsEnabled) {
int result = Messages.showYesNoDialog(project, AndroidBundle.message("android.ddms.disabled.error"),
AndroidBundle.message("android.ddms.disabled.dialog.title"),
Messages.getQuestionIcon());
if (result != 0) {
return false;
}
AndroidEnableDdmsAction.setDdmsEnabled(project, true);
}
return true;
}
static boolean isDdmsCorrupted(@NotNull AndroidFacet facet) {
AndroidDebugBridge bridge = facet.getDebugBridge();
if (bridge != null) {
IDevice[] devices = bridge.getDevices();
if (devices.length > 0) {
Client[] clients = devices[0].getClients();
if (clients.length > 0) {
ClientData clientData = clients[0].getClientData();
return clientData == null || clientData.getVmIdentifier() == null;
}
}
}
return false;
}
@Nullable
private static String getPackageName(AndroidFacet facet) {
Manifest manifest = facet.getManifest();
if (manifest == null) return null;
GenericAttributeValue<String> packageAttrValue = manifest.getPackage();
String aPackage = packageAttrValue.getValue();
if (aPackage == null || aPackage.length() == 0) {
Project project = facet.getModule().getProject();
Messages.showErrorDialog(project, AndroidBundle.message("specify.main.package.error", facet.getModule().getName()),
CommonBundle.getErrorTitle());
XmlAttributeValue attrValue = packageAttrValue.getXmlAttributeValue();
if (attrValue != null) {
PsiNavigateUtil.navigate(attrValue);
}
else {
PsiNavigateUtil.navigate(manifest.getXmlElement());
}
return null;
}
return aPackage;
}
private String computeCommandLine() {
StringBuilder result = new StringBuilder();
result.append("-netspeed ").append(NETWORK_SPEED).append(' ');
result.append("-netdelay ").append(NETWORK_LATENCY).append(' ');
if (WIPE_USER_DATA) {
result.append("-wipe-data ");
}
if (DISABLE_BOOT_ANIMATION) {
result.append("-no-boot-anim ");
}
result.append(COMMAND_LINE);
int last = result.length() - 1;
if (result.charAt(last) == ' ') {
result.deleteCharAt(last);
}
return result.toString();
}
@NotNull
protected abstract ConsoleView attachConsole(AndroidRunningState state, Executor executor) throws ExecutionException;
@Nullable
protected abstract AndroidApplicationLauncher getApplicationLauncher(AndroidFacet facet);
protected abstract boolean supportMultipleDevices();
private static String toString(IDevice[] devices) {
StringBuilder builder = new StringBuilder();
for (int i = 0, n = devices.length; i < n; i++) {
builder.append(devices[i].getSerialNumber());
if (i < n - 1) {
builder.append(' ');
}
}
return builder.toString();
}
private static String[] fromString(String s) {
return s.split(" ");
}
@NotNull
private IDevice[] chooseDevicesManually(@NotNull AndroidFacet facet) {
String value = PropertiesComponent.getInstance(getProject()).getValue(ANDROID_TARGET_DEVICES_PROPERTY);
String[] selectedSerials = value != null ? fromString(value) : null;
DeviceChooser chooser = new DeviceChooser(facet, supportMultipleDevices(), selectedSerials);
chooser.show();
IDevice[] devices = chooser.getSelectedDevices();
if (chooser.getExitCode() != DeviceChooser.OK_EXIT_CODE || devices.length == 0) {
return DeviceChooser.EMPTY_DEVICE_ARRAY;
}
return devices;
}
@Override
public void customizeLogConsole(LogConsole console) {
final Project project = getProject();
console.setFilterModel(new AndroidLogFilterModel(AndroidLogcatFiltersPreferences.getInstance(project).TAB_LOG_LEVEL) {
@Override
protected void setCustomFilter(String filter) {
AndroidLogcatFiltersPreferences.getInstance(project).TAB_CUSTOM_FILTER = filter;
}
@Override
protected void saveLogLevel(Log.LogLevel logLevel) {
AndroidLogcatFiltersPreferences.getInstance(project).TAB_LOG_LEVEL = logLevel.name();
}
@Override
public String getCustomFilter() {
return AndroidLogcatFiltersPreferences.getInstance(project).TAB_CUSTOM_FILTER;
}
});
}
public void readExternal(Element element) throws InvalidDataException {
super.readExternal(element);
readModule(element);
DefaultJDOMExternalizer.readExternal(this, element);
}
public void writeExternal(Element element) throws WriteExternalException {
super.writeExternal(element);
writeModule(element);
DefaultJDOMExternalizer.writeExternal(this, element);
}
}