package com.intellij.lang.javascript.flex.actions.airpackage; import com.intellij.flex.model.bc.BuildConfigurationNature; import com.intellij.flex.model.bc.TargetPlatform; import com.intellij.lang.javascript.flex.FlexBundle; import com.intellij.lang.javascript.flex.actions.FlexBCTree; import com.intellij.lang.javascript.flex.build.FlashProjectStructureProblem; import com.intellij.lang.javascript.flex.build.ValidateFlashConfigurationsPrecompileTask; import com.intellij.lang.javascript.flex.projectStructure.model.AirPackagingOptions; import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfiguration; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureProblemType; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.ValidationInfo; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.util.Consumer; import com.intellij.util.ui.UIUtil; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Collection; import static com.intellij.lang.javascript.flex.actions.airpackage.AirPackageProjectParameters.*; public class AirPackageDialog extends DialogWrapper { private JPanel myMainPanel; private FlexBCTree myTree; private JComboBox myDesktopTypeCombo; private JComboBox myAndroidTypeCombo; private JCheckBox myApkCaptiveRuntimeCheckBox; private JPanel myApkDebugPortPanel; private JTextField myApkDebugPortTextField; //private JPanel myApkDebugHostPanel; //private JTextField myApkDebugHostTextField; private JComboBox myIOSTypeCombo; private JCheckBox myIosFastPackagingCheckBox; private JLabel myDesktopTypeLabel; private JLabel myAndroidTypeLabel; private JLabel myIosTypeLabel; private final Project myProject; //private final String myOwnIpAddress; private PasswordStore myPasswords; protected AirPackageDialog(final Project project) { super(project); myProject = project; //myOwnIpAddress = FlexUtils.getOwnIpAddress(); setTitle(FlexBundle.message("package.air.application.title")); setOKButtonText("Package"); setupComboBoxes(); init(); loadParameters(); updateControlsVisibility(); updateControlsEnabledState(); } private void setupComboBoxes() { myDesktopTypeCombo.setModel(new DefaultComboBoxModel(DesktopPackageType.values())); myAndroidTypeCombo.setModel(new DefaultComboBoxModel(AndroidPackageType.values())); myIOSTypeCombo.setModel(new DefaultComboBoxModel( new IOSPackageType[]{IOSPackageType.Test, IOSPackageType.DebugOverNetwork, IOSPackageType.AdHoc, IOSPackageType.AppStore})); final ActionListener listener = new ActionListener() { public void actionPerformed(final ActionEvent e) { updateControlsVisibility(); } }; myDesktopTypeCombo.addActionListener(listener); myAndroidTypeCombo.addActionListener(listener); myIOSTypeCombo.addActionListener(listener); } private void updateControlsVisibility() { final AndroidPackageType androidPackaging = (AndroidPackageType)myAndroidTypeCombo.getSelectedItem(); myApkCaptiveRuntimeCheckBox.setVisible(androidPackaging == AndroidPackageType.Release); myApkDebugPortPanel.setVisible(androidPackaging == AndroidPackageType.DebugOverUSB); //myApkDebugHostPanel.setVisible(androidPackaging == AndroidPackageType.DebugOverNetwork); final IOSPackageType iosPackaging = (IOSPackageType)myIOSTypeCombo.getSelectedItem(); myIosFastPackagingCheckBox.setVisible(iosPackaging == IOSPackageType.DebugOverNetwork || iosPackaging == IOSPackageType.Test); } private void updateControlsEnabledState() { boolean desktopPresent = false; boolean androidPresent = false; boolean iosPresent = false; for (Pair<Module, FlexBuildConfiguration> moduleAndBC : getSelectedBCs()) { final FlexBuildConfiguration bc = moduleAndBC.second; final BuildConfigurationNature nature = bc.getNature(); if (nature.isDesktopPlatform()) desktopPresent = true; if (nature.isMobilePlatform() && bc.getAndroidPackagingOptions().isEnabled()) androidPresent = true; if (nature.isMobilePlatform() && bc.getIosPackagingOptions().isEnabled()) iosPresent = true; if (desktopPresent && androidPresent && iosPresent) break; } myDesktopTypeLabel.setEnabled(desktopPresent); myDesktopTypeCombo.setEnabled(desktopPresent); myAndroidTypeLabel.setEnabled(androidPresent); myAndroidTypeCombo.setEnabled(androidPresent); myApkCaptiveRuntimeCheckBox.setEnabled(androidPresent); UIUtil.setEnabled(myApkDebugPortPanel, androidPresent, true); //UIUtil.setEnabled(myApkDebugHostPanel, androidPresent, true); myIosTypeLabel.setEnabled(iosPresent); myIOSTypeCombo.setEnabled(iosPresent); myIosFastPackagingCheckBox.setEnabled(iosPresent); } protected JComponent createCenterPanel() { return myMainPanel; } private void createUIComponents() { myTree = new FlexBCTree(myProject, bc -> { final BuildConfigurationNature nature = bc.getNature(); return nature.isApp() && !nature.isWebPlatform(); }); myTree.addToggleCheckBoxListener(new ChangeListener() { public void stateChanged(final ChangeEvent e) { updateControlsEnabledState(); } }); } protected String getDimensionServiceKey() { return "AirPackageDialog.DimensionServiceKey"; } protected String getHelpId() { return "reference.flex.package.air.application"; } protected ValidationInfo doValidate() { final Collection<Pair<Module, FlexBuildConfiguration>> modulesAndBCs = getSelectedBCs(); if (modulesAndBCs.isEmpty()) return new ValidationInfo("Please select one or more build configurations"); if (myApkDebugPortTextField.isVisible() && myApkDebugPortPanel.isEnabled()) { try { final String portValue = myApkDebugPortTextField.getText().trim(); final int port = portValue.isEmpty() ? AirPackageUtil.DEBUG_PORT_DEFAULT : Integer.parseInt(portValue); if (port <= 0 || port > 65535) return new ValidationInfo("Incorrect port", myApkDebugPortPanel); } catch (NumberFormatException e) { return new ValidationInfo("Incorrect port", myApkDebugPortTextField); } } for (Pair<Module, FlexBuildConfiguration> moduleAndBC : modulesAndBCs) { final FlexBuildConfiguration bc = moduleAndBC.second; if (bc.isSkipCompile() && LocalFileSystem.getInstance().findFileByPath(bc.getActualOutputFilePath()) == null) { return new ValidationInfo( FlexBundle.message("can.not.package.bc", bc.getName(), FlexBundle.message("compilation.is.switched.off"))); } final BuildConfigurationNature nature = bc.getNature(); if (nature.isMobilePlatform()) { if (!bc.getAndroidPackagingOptions().isEnabled() && !bc.getIosPackagingOptions().isEnabled()) { return new ValidationInfo(FlexBundle.message("can.not.package.bc", bc.getName(), "both Android and iOS packaging disabled")); } if (bc.getAndroidPackagingOptions().isEnabled() && bc.getIosPackagingOptions().isEnabled()) { final AndroidPackageType androidPackage = (AndroidPackageType)myAndroidTypeCombo.getSelectedItem(); final IOSPackageType iosPackage = (IOSPackageType)myIOSTypeCombo.getSelectedItem(); final boolean androidDebug = androidPackage != AndroidPackageType.Release; final boolean iosDebug = iosPackage == IOSPackageType.DebugOverNetwork; if (androidDebug != iosDebug) { return new ValidationInfo(FlexBundle.message("can.not.package.bc", bc.getName(), FlexBundle.message("different.debug.settings", androidDebug ? 1 : 2))); } } } final Ref<String> firstErrorRef = new Ref<>(); ValidateFlashConfigurationsPrecompileTask.checkPackagingOptions(moduleAndBC.first, bc, problem -> { if (problem.severity == ProjectStructureProblemType.Severity.ERROR && firstErrorRef.isNull()) { firstErrorRef.set(problem.errorMessage); } }); // todo better error reporting. May be just mention that errors exist in some BC and provide link to Project Structure if (!firstErrorRef.isNull()) { return new ValidationInfo(FlexBundle.message("can.not.package.bc", bc.getName(), firstErrorRef.get())); } } return null; } protected void doOKAction() { final Collection<Pair<Module, FlexBuildConfiguration>> selectedBCs = getSelectedBCs(); if (!checkDisabledCompilation(myProject, selectedBCs)) return; if (!checkPasswords(selectedBCs)) return; saveParameters(); super.doOKAction(); } private static boolean checkDisabledCompilation(final Project project, final Collection<Pair<Module, FlexBuildConfiguration>> selectedBCs) { final Collection<FlexBuildConfiguration> bcsWithDisabledCompilation = new ArrayList<>(); for (Pair<Module, FlexBuildConfiguration> moduleAndBC : selectedBCs) { if (moduleAndBC.second.isSkipCompile()) { bcsWithDisabledCompilation.add(moduleAndBC.second); } } if (!bcsWithDisabledCompilation.isEmpty()) { final StringBuilder bcs = new StringBuilder(); for (FlexBuildConfiguration bc : bcsWithDisabledCompilation) { bcs.append("<b>").append(StringUtil.escapeXml(bc.getName())).append("</b><br>"); } final String message = FlexBundle.message("package.bc.with.disabled.compilation", bcsWithDisabledCompilation.size(), bcs.toString()); final int answer = Messages.showYesNoDialog(project, message, FlexBundle.message("package.air.application.title"), Messages.getWarningIcon()); return answer == Messages.YES; } return true; } private boolean checkPasswords(final Collection<Pair<Module, FlexBuildConfiguration>> selectedBCs) { final Collection<AirPackagingOptions> allPackagingOptions = new ArrayList<>(); for (Pair<Module, FlexBuildConfiguration> moduleAndBC : selectedBCs) { final FlexBuildConfiguration bc = moduleAndBC.second; if (bc.getTargetPlatform() == TargetPlatform.Desktop) { if (myDesktopTypeCombo.getSelectedItem() != DesktopPackageType.Airi) { allPackagingOptions.add(bc.getAirDesktopPackagingOptions()); } } else { if (bc.getAndroidPackagingOptions().isEnabled()) { allPackagingOptions.add(bc.getAndroidPackagingOptions()); } if (bc.getIosPackagingOptions().isEnabled()) { allPackagingOptions.add(bc.getIosPackagingOptions()); } } } myPasswords = AirPackageAction.getPasswords(myProject, allPackagingOptions); return myPasswords != null; } private void loadParameters() { final AirPackageProjectParameters params = AirPackageProjectParameters.getInstance(myProject); myDesktopTypeCombo.setSelectedItem(params.desktopPackageType); myAndroidTypeCombo.setSelectedItem(params.androidPackageType); myApkCaptiveRuntimeCheckBox.setSelected(params.apkCaptiveRuntime); myApkDebugPortTextField.setText(String.valueOf(params.apkDebugListenPort)); //myApkDebugHostTextField.setText(params.apkDebugConnectHost.isEmpty() ? myOwnIpAddress : params.apkDebugConnectHost); myIOSTypeCombo.setSelectedItem(params.iosPackageType); myIosFastPackagingCheckBox.setSelected(params.iosFastPackaging); for (String entry : StringUtil.split(params.deselectedBCs, "\n")) { final int tabIndex = entry.indexOf("\t"); if (tabIndex > 0) { myTree.setChecked(entry.substring(0, tabIndex), entry.substring(tabIndex + 1), false); } } } private void saveParameters() { final AirPackageProjectParameters params = AirPackageProjectParameters.getInstance(myProject); params.desktopPackageType = (DesktopPackageType)myDesktopTypeCombo.getSelectedItem(); params.androidPackageType = (AndroidPackageType)myAndroidTypeCombo.getSelectedItem(); params.apkCaptiveRuntime = myApkCaptiveRuntimeCheckBox.isSelected(); try { final String portValue = myApkDebugPortTextField.getText().trim(); final int port = portValue.isEmpty() ? AirPackageUtil.DEBUG_PORT_DEFAULT : Integer.parseInt(portValue); if (port > 0 && port <= 65535) { params.apkDebugListenPort = port; } } catch (NumberFormatException e) {/*ignore*/} //final String host = myApkDebugHostTextField.getText().trim(); //params.apkDebugConnectHost = host.equals(myOwnIpAddress) ? "" : host; params.iosPackageType = (IOSPackageType)myIOSTypeCombo.getSelectedItem(); params.iosFastPackaging = myIosFastPackagingCheckBox.isSelected(); final Collection<Pair<Module, FlexBuildConfiguration>> deselectedBCs = myTree.getDeselectedBCs(); final StringBuilder buf = new StringBuilder(); for (Pair<Module, FlexBuildConfiguration> moduleAndBC : deselectedBCs) { if (buf.length() > 0) buf.append('\n'); buf.append(moduleAndBC.first.getName()).append('\t').append(moduleAndBC.second.getName()); } params.deselectedBCs = buf.toString(); } public Collection<Pair<Module, FlexBuildConfiguration>> getSelectedBCs() { return myTree.getSelectedBCs(); } public PasswordStore getPasswords() { assert isOK() : "ask for passwords only after OK in dialog"; return myPasswords; } }