/* * Copyright 2000-2012 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.sdklib.AndroidVersion; import com.android.sdklib.internal.avd.AvdInfo; import com.android.sdklib.internal.avd.AvdManager; import com.android.tools.idea.model.AndroidModuleInfo; import com.android.tools.idea.run.LaunchCompatibility; import com.google.common.base.Predicate; import com.intellij.execution.ui.ConfigurationModuleSelector; import com.intellij.icons.AllIcons; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.module.Module; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.LabeledComponent; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.ListCellRendererWrapper; import com.intellij.ui.PanelWithAnchor; import com.intellij.ui.RawCommandLineEditor; import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBLabel; import com.intellij.util.ThreeState; import org.jetbrains.android.facet.AndroidFacet; import org.jetbrains.android.sdk.AndroidPlatform; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /** * @author yole */ public class AndroidRunConfigurationEditor<T extends AndroidRunConfigurationBase> extends SettingsEditor<T> implements PanelWithAnchor { private JPanel myPanel; private JComboBox myModulesComboBox; private JPanel myConfigurationSpecificPanel; private JCheckBox myWipeUserDataCheckBox; private JComboBox myNetworkSpeedCombo; private JComboBox myNetworkLatencyCombo; private JCheckBox myDisableBootAnimationCombo; private JCheckBox myClearLogCheckBox; private JBLabel myModuleJBLabel; private JRadioButton myShowChooserRadioButton; private JRadioButton myEmulatorRadioButton; private JRadioButton myUsbDeviceRadioButton; private LabeledComponent<AvdComboBox> myAvdComboComponent; private JBLabel myMinSdkInfoMessageLabel; private JBCheckBox myUseAdditionalCommandLineOptionsCheckBox; private RawCommandLineEditor myCommandLineField; private JCheckBox myShowLogcatCheckBox; private JCheckBox myFilterLogcatCheckBox; private JCheckBox myUseLastSelectedDeviceCheckBox; private AvdComboBox myAvdCombo; private String incorrectPreferredAvd; private JComponent anchor; @NonNls private final static String[] NETWORK_SPEEDS = new String[]{"Full", "GSM", "HSCSD", "GPRS", "EDGE", "UMTS", "HSPDA"}; @NonNls private final static String[] NETWORK_LATENCIES = new String[]{"None", "GPRS", "EDGE", "UMTS"}; private final ConfigurationModuleSelector myModuleSelector; private ConfigurationSpecificEditor<T> myConfigurationSpecificEditor; public void setConfigurationSpecificEditor(ConfigurationSpecificEditor<T> configurationSpecificEditor) { myConfigurationSpecificEditor = configurationSpecificEditor; myConfigurationSpecificPanel.add(configurationSpecificEditor.getComponent()); setAnchor(myConfigurationSpecificEditor.getAnchor()); myShowLogcatCheckBox.setVisible(configurationSpecificEditor instanceof ApplicationRunParameters); myFilterLogcatCheckBox.setVisible(configurationSpecificEditor instanceof ApplicationRunParameters); } public AndroidRunConfigurationEditor(final Project project, final Predicate<AndroidFacet> libraryProjectValidator) { myCommandLineField.setDialogCaption("Emulator Additional Command Line Options"); myModuleSelector = new ConfigurationModuleSelector(project, myModulesComboBox) { @Override public boolean isModuleAccepted(Module module) { if (module == null || !super.isModuleAccepted(module)) { return false; } final AndroidFacet facet = AndroidFacet.getInstance(module); if (facet == null) { return false; } return !facet.isLibraryProject() || libraryProjectValidator.apply(facet); } }; myAvdComboComponent.setComponent(new AvdComboBox(true, false) { @Override public Module getModule() { return getModuleSelector().getModule(); } }); myAvdCombo = myAvdComboComponent.getComponent(); // In Darcula, combo boxes are not resized appropriately when a model is set, so we have to explicitly size it myAvdCombo.setMinimumSize(new Dimension(250, myAvdCombo.getMinimumSize().height)); myAvdCombo.getComboBox().addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { final String warning = myEmulatorRadioButton.isSelected() ? getAvdCompatibilityWarning() : null; resetAvdCompatibilityWarningLabel(warning); } }); myMinSdkInfoMessageLabel.setBorder(IdeBorderFactory.createEmptyBorder(10, 0, 0, 0)); myMinSdkInfoMessageLabel.setIcon(AllIcons.General.BalloonWarning); Disposer.register(this, myAvdCombo); final ActionListener listener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { boolean emulatorSelected = myEmulatorRadioButton.isSelected(); myAvdComboComponent.setEnabled(emulatorSelected); final String warning = emulatorSelected ? getAvdCompatibilityWarning() : null; resetAvdCompatibilityWarningLabel(warning); } }; myModulesComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { myAvdCombo.startUpdatingAvds(ModalityState.current()); } }); myShowChooserRadioButton.addActionListener(listener); myEmulatorRadioButton.addActionListener(listener); myUsbDeviceRadioButton.addActionListener(listener); myUseLastSelectedDeviceCheckBox.addActionListener(listener); myNetworkSpeedCombo.setModel(new DefaultComboBoxModel(NETWORK_SPEEDS)); myNetworkLatencyCombo.setModel(new DefaultComboBoxModel(NETWORK_LATENCIES)); myUseAdditionalCommandLineOptionsCheckBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { myCommandLineField.setEnabled(myUseAdditionalCommandLineOptionsCheckBox.isSelected()); } }); } private void resetAvdCompatibilityWarningLabel(@Nullable String warning) { if (warning != null) { myMinSdkInfoMessageLabel.setVisible(true); myMinSdkInfoMessageLabel.setText(warning); } else { myMinSdkInfoMessageLabel.setVisible(false); } } @Nullable private String getAvdCompatibilityWarning() { final String selectedAvdName = (String)myAvdCombo.getComboBox().getSelectedItem(); if (selectedAvdName != null) { final Module module = getModuleSelector().getModule(); if (module == null) { return null; } final AndroidFacet facet = AndroidFacet.getInstance(module); if (facet == null) { return null; } final AvdManager avdManager = facet.getAvdManagerSilently(); if (avdManager == null) { return null; } final AvdInfo avd = avdManager.getAvd(selectedAvdName, false); if (avd == null || avd.getTarget() == null) { return null; } AndroidPlatform platform = facet.getConfiguration().getAndroidPlatform(); if (platform == null) { return null; } AndroidVersion minSdk = AndroidModuleInfo.get(facet).getRuntimeMinSdkVersion(); LaunchCompatibility compatibility = LaunchCompatibility.canRunOnAvd(minSdk, platform.getTarget(), avd.getTarget()); if (compatibility.isCompatible() == ThreeState.NO) { // todo: provide info about current module configuration return String.format("'%1$s' may be incompatible with your configuration (%2$s)", selectedAvdName, StringUtil.notNullize(compatibility.getReason())); } } return null; } @Override public JComponent getAnchor() { return anchor; } @Override public void setAnchor(JComponent anchor) { this.anchor = anchor; myModuleJBLabel.setAnchor(anchor); } private static boolean containsItem(JComboBox combo, @NotNull Object item) { for (int i = 0, n = combo.getItemCount(); i < n; i++) { if (item.equals(combo.getItemAt(i))) { return true; } } return false; } @Override protected void resetEditorFrom(T configuration) { myModuleSelector.reset(configuration); final String avd = configuration.PREFERRED_AVD; if (avd != null) { JComboBox combo = myAvdCombo.getComboBox(); if (containsItem(combo, avd)) { combo.setSelectedItem(avd); } else { combo.setRenderer(new ListCellRendererWrapper() { @Override public void customize(JList list, Object value, int index, boolean selected, boolean hasFocus) { if (value == null) { setText("<html><font color='red'>" + avd + "</font></html>"); } } }); combo.setSelectedItem(null); incorrectPreferredAvd = avd; } } final TargetSelectionMode targetSelectionMode = configuration.getTargetSelectionMode(); myShowChooserRadioButton.setSelected(targetSelectionMode == TargetSelectionMode.SHOW_DIALOG); myEmulatorRadioButton.setSelected(targetSelectionMode == TargetSelectionMode.EMULATOR); myUsbDeviceRadioButton.setSelected(targetSelectionMode == TargetSelectionMode.USB_DEVICE); myUseLastSelectedDeviceCheckBox.setSelected(configuration.USE_LAST_SELECTED_DEVICE); myAvdComboComponent.setEnabled(targetSelectionMode == TargetSelectionMode.EMULATOR); resetAvdCompatibilityWarningLabel(targetSelectionMode == TargetSelectionMode.EMULATOR ? getAvdCompatibilityWarning() : null); myUseAdditionalCommandLineOptionsCheckBox.setSelected(configuration.USE_COMMAND_LINE); myCommandLineField.setText(configuration.COMMAND_LINE); myConfigurationSpecificEditor.resetFrom(configuration); myWipeUserDataCheckBox.setSelected(configuration.WIPE_USER_DATA); myDisableBootAnimationCombo.setSelected(configuration.DISABLE_BOOT_ANIMATION); selectItemCaseInsensitively(myNetworkSpeedCombo, configuration.NETWORK_SPEED); selectItemCaseInsensitively(myNetworkLatencyCombo, configuration.NETWORK_LATENCY); myClearLogCheckBox.setSelected(configuration.CLEAR_LOGCAT); myShowLogcatCheckBox.setSelected(configuration.SHOW_LOGCAT_AUTOMATICALLY); myFilterLogcatCheckBox.setSelected(configuration.FILTER_LOGCAT_AUTOMATICALLY); } private static void selectItemCaseInsensitively(@NotNull JComboBox comboBox, @Nullable String item) { if (item == null) { comboBox.setSelectedItem(null); return; } final ComboBoxModel model = comboBox.getModel(); for (int i = 0, n = model.getSize(); i < n; i++) { final Object element = model.getElementAt(i); if (element instanceof String && item.equalsIgnoreCase((String)element)) { comboBox.setSelectedItem(element); return; } } } @Override protected void applyEditorTo(T configuration) throws ConfigurationException { myModuleSelector.applyTo(configuration); if (myShowChooserRadioButton.isSelected()) { configuration.setTargetSelectionMode(TargetSelectionMode.SHOW_DIALOG); } else if (myEmulatorRadioButton.isSelected()) { configuration.setTargetSelectionMode(TargetSelectionMode.EMULATOR); } else if (myUsbDeviceRadioButton.isSelected()) { configuration.setTargetSelectionMode(TargetSelectionMode.USB_DEVICE); } configuration.USE_LAST_SELECTED_DEVICE = myUseLastSelectedDeviceCheckBox.isSelected(); configuration.COMMAND_LINE = myCommandLineField.getText(); configuration.USE_COMMAND_LINE = myUseAdditionalCommandLineOptionsCheckBox.isSelected(); configuration.PREFERRED_AVD = ""; configuration.WIPE_USER_DATA = myWipeUserDataCheckBox.isSelected(); configuration.DISABLE_BOOT_ANIMATION = myDisableBootAnimationCombo.isSelected(); configuration.NETWORK_SPEED = ((String)myNetworkSpeedCombo.getSelectedItem()).toLowerCase(); configuration.NETWORK_LATENCY = ((String)myNetworkLatencyCombo.getSelectedItem()).toLowerCase(); configuration.CLEAR_LOGCAT = myClearLogCheckBox.isSelected(); configuration.SHOW_LOGCAT_AUTOMATICALLY = myShowLogcatCheckBox.isSelected(); configuration.FILTER_LOGCAT_AUTOMATICALLY = myFilterLogcatCheckBox.isSelected(); if (myAvdComboComponent.isEnabled()) { JComboBox combo = myAvdCombo.getComboBox(); String preferredAvd = (String)combo.getSelectedItem(); if (preferredAvd == null) { preferredAvd = incorrectPreferredAvd != null ? incorrectPreferredAvd : ""; } configuration.PREFERRED_AVD = preferredAvd; } myConfigurationSpecificEditor.applyTo(configuration); } @Override @NotNull protected JComponent createEditor() { return myPanel; } public ConfigurationModuleSelector getModuleSelector() { return myModuleSelector; } }