package com.intellij.lang.javascript.flex.actions.airpackage; import com.intellij.flex.FlexCommonUtils; import com.intellij.ide.util.PropertiesComponent; import com.intellij.lang.javascript.flex.FlexBundle; import com.intellij.lang.javascript.flex.FlexUtils; import com.intellij.lang.javascript.flex.actions.ExternalTask; import com.intellij.lang.javascript.flex.projectStructure.model.*; import com.intellij.lang.javascript.flex.run.BCBasedRunnerParameters; import com.intellij.lang.javascript.flex.run.FlashRunnerParameters; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.PathUtil; import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import static com.intellij.lang.javascript.flex.actions.airpackage.AirPackageProjectParameters.*; import static com.intellij.lang.javascript.flex.run.FlashRunnerParameters.AirMobileDebugTransport; public class AirPackageUtil { public static final int DEBUG_PORT_DEFAULT = 7936; public static final String TEMP_KEYSTORE_PASSWORD = "keystore_password"; static final String ADB_RELATIVE_PATH = "/lib/android/bin/adb" + (SystemInfo.isWindows ? ".exe" : ""); private static final String IDB_RELATIVE_PATH = "/lib/aot/bin/iOSBin/idb" + (SystemInfo.isWindows ? ".exe" : ""); private static final String PREFERRED_IOS_DEVICE_ID_PROPERTY = "flex.preferred.ios.device.id"; private static final String PREFERRED_ANDROID_DEVICE_ID_PROPERTY = "flex.preferred.android.device.id"; private AirPackageUtil() { } public static String getTempKeystorePath() { return FlexCommonUtils.getPathToBundledJar("temp_keystore.p12"); } @Nullable public static String getAdtVersion(final Project project, final Sdk sdk) { final Ref<String> versionRef = new Ref<>(); ExternalTask.runWithProgress(new AdtTask(project, sdk) { protected void appendAdtOptions(final List<String> command) { command.add("-version"); } protected boolean checkMessages() { if (myMessages.size() == 1) { FlexCommonUtils.parseAirVersionFromAdtOutput(myMessages.get(0), versionRef); if (!versionRef.isNull()) { return true; } } return false; } }, FlexBundle.message("checking.air.version"), FlexBundle.message("check.air.version.title")); return versionRef.get(); } /** * Detects AIR runtime version installed on Android device connected to the computer * * @return AIR runtime version or empty string if AIR is not installed on the device * @throws AdtException if failed to detect AIR runtime version, for example when device is not connected */ public static String getAirRuntimeVersionOnDevice(final Project project, final Sdk sdk, final FlashRunnerParameters runnerParameters) throws AdtException { final Ref<String> versionRef = new Ref<>(); final boolean ok = ExternalTask.runWithProgress( new AdtTask(project, sdk) { protected void appendAdtOptions(final List<String> command) { command.add("-runtimeVersion"); command.add("-platform"); command.add("android"); final DeviceInfo device = runnerParameters.getDeviceInfo(); if (device != null) { command.add("-device"); command.add(device.DEVICE_ID); } } protected boolean checkMessages() { if (myMessages.size() == 1) { final String output = myMessages.get(0); // No Devices Detected // Failed to find package com.adobe.air // 2.6.0.1912 if (output.startsWith("Failed to find package com.adobe.air")) { versionRef.set(""); return true; } else if (FlexCommonUtils.AIR_VERSION_PATTERN.matcher(output).matches()) { versionRef.set(output); return true; } } return false; } }, FlexBundle.message("checking.air.version"), FlexBundle.message("check.air.version.title")); if (!ok) { throw new AdtException(); } return versionRef.get(); } public static boolean checkAdtVersionForPackaging(final Project project, final String actualAdtVersion, final String requiredAdtVersion, final String sdkName, final String errorMessageStart) { if (StringUtil.compareVersionNumbers(actualAdtVersion, requiredAdtVersion) < 0) { Messages.showErrorDialog(project, FlexBundle.message("air.mobile.packaging.version.problem", errorMessageStart, sdkName, actualAdtVersion, requiredAdtVersion), FlexBundle.message("air.mobile.version.problem.title")); return false; } return true; } public static boolean startAdbServer(final Project project, final Sdk sdk) { return ExternalTask.runWithProgress(new ExternalTask(project, sdk) { protected List<String> createCommandLine() { final ArrayList<String> command = new ArrayList<>(); command.add(sdk.getHomePath() + ADB_RELATIVE_PATH); command.add("start-server"); return command; } protected void scheduleInputStreamReading() { // Reading input stream causes hang on Windows because adb starts child process that never exits (IDEA-87648) ApplicationManager.getApplication().executeOnPooledThread(() -> { try { getProcess().waitFor(); } catch (InterruptedException ignore) {/**/} finally { cancel(); } }); } }, "adb start-server", "ADB Server Start"); } private static void stopAdbServer(final Project project, final Sdk sdk, final String progressTitle, final String frameTitle) { ExternalTask.runWithProgress(new ExternalTask(project, sdk) { protected List<String> createCommandLine() { final ArrayList<String> command = new ArrayList<>(); command.add(sdk.getHomePath() + ADB_RELATIVE_PATH); command.add("kill-server"); return command; } protected boolean checkMessages() { return true; // do not care } }, progressTitle, frameTitle); } public static boolean checkAirRuntimeOnDevice(final Project project, final Sdk sdk, final FlashRunnerParameters runnerParameters, final String adtVersion) { if (StringUtil.compareVersionNumbers(adtVersion, "3.7") >= 0) { // since AIR SDK 3.7 all Android packages include captive runtime return true; } try { final String airRuntimeVersion = getAirRuntimeVersionOnDevice(project, sdk, runnerParameters); final String adtVersionTruncated = truncateVersionString(adtVersion); final String airRuntimeVersionTruncated = truncateVersionString(airRuntimeVersion); if (airRuntimeVersion.isEmpty() || StringUtil.compareVersionNumbers(adtVersionTruncated, airRuntimeVersionTruncated) > 0) { final String message = airRuntimeVersion.isEmpty() ? FlexBundle.message("air.runtime.not.installed", sdk.getName(), adtVersionTruncated) : FlexBundle .message("update.air.runtime.question", airRuntimeVersionTruncated, sdk.getName(), adtVersionTruncated); final int answer = Messages.showYesNoDialog(project, message, FlexBundle.message("air.runtime.version.title"), Messages.getQuestionIcon()); if (answer == -1) { return false; } if (answer == Messages.YES) { installAirRuntimeOnDevice(project, sdk, runnerParameters.getDeviceInfo(), adtVersionTruncated, !airRuntimeVersion.isEmpty()); } } return true; } catch (AdtException ignore) { return false; } } private static String truncateVersionString(final String version) { final int firstDotIndex = version.indexOf('.'); final int secondDotIndex = version.indexOf('.', firstDotIndex + 1); final int thirdDotIndex = version.indexOf('.', secondDotIndex + 1); return thirdDotIndex > 0 ? version.substring(0, thirdDotIndex) : version; } private static void installAirRuntimeOnDevice(final Project project, final Sdk sdk, final @Nullable DeviceInfo device, final String version, final boolean uninstallExistingBeforeInstalling) { if (uninstallExistingBeforeInstalling) { ExternalTask.runWithProgress(new AdtTask(project, sdk) { protected void appendAdtOptions(final List<String> command) { command.add("-uninstallRuntime"); command.add("-platform"); command.add("android"); if (device != null) { command.add("-device"); command.add(device.DEVICE_ID); } } }, FlexBundle.message("uninstalling.air.runtime"), FlexBundle.message("uninstall.air.runtime.title")); } ExternalTask.runWithProgress(new AdtTask(project, sdk) { protected void appendAdtOptions(final List<String> command) { command.add("-installRuntime"); command.add("-platform"); command.add("android"); if (device != null) { command.add("-device"); command.add(device.DEVICE_ID); } } }, FlexBundle.message("installing.air.runtime", version), FlexBundle.message("install.air.runtime.title")); } public static boolean packageApk(final Module module, final FlexBuildConfiguration bc, final FlashRunnerParameters runnerParameters, final boolean isDebug) { final AndroidPackagingOptions packagingOptions = bc.getAndroidPackagingOptions(); final boolean tempCertificate = packagingOptions.getSigningOptions().isUseTempCertificate(); final PasswordStore passwords = tempCertificate ? null : AirPackageAction.getPasswords(module.getProject(), Collections.singletonList(packagingOptions)); if (!tempCertificate && passwords == null) return false; // user canceled FileDocumentManager.getInstance().saveAllDocuments(); final AndroidPackageType packageType = isDebug ? runnerParameters.getDebugTransport() == AirMobileDebugTransport.Network ? AndroidPackageType.DebugOverNetwork : AndroidPackageType.DebugOverUSB : AndroidPackageType.Release; final ExternalTask task = createAndroidPackageTask(module, bc, packageType, false, runnerParameters.getUsbDebugPort(), passwords); return ExternalTask.runWithProgress(task, FlexBundle.message("creating.android.package"), FlexBundle.message("create.android.package.title")); } public static boolean packageIpaForSimulator(final Module module, final FlexBuildConfiguration bc, final FlashRunnerParameters runnerParameters, final boolean isDebug) { FileDocumentManager.getInstance().saveAllDocuments(); final IOSPackageType packageType = isDebug ? IOSPackageType.DebugOnSimulator : IOSPackageType.TestOnSimulator; final ExternalTask task = createIOSPackageTask(module, bc, packageType, true, runnerParameters.getIOSSimulatorSdkPath(), 0, null); return ExternalTask.runWithProgress(task, FlexBundle.message("creating.ios.package"), FlexBundle.message("create.ios.package.title")); } public static boolean packageIpaForDevice(final Module module, final FlexBuildConfiguration bc, final FlashRunnerParameters runnerParameters, final String adtVersion, final boolean isDebug) { final IosPackagingOptions packagingOptions = bc.getIosPackagingOptions(); final PasswordStore passwords = AirPackageAction.getPasswords(module.getProject(), Collections.singletonList(packagingOptions)); if (passwords == null) return false; // user canceled FileDocumentManager.getInstance().saveAllDocuments(); final IOSPackageType packageType = isDebug ? runnerParameters.getDebugTransport() == AirMobileDebugTransport.Network ? IOSPackageType.DebugOverNetwork : IOSPackageType.DebugOverUSB : IOSPackageType.Test; final Sdk sdk = bc.getSdk(); assert sdk != null; if (packageType == IOSPackageType.DebugOverUSB && !checkAdtVersionForPackaging(module.getProject(), adtVersion, "3.4", sdk.getName(), FlexBundle.message("air.ios.debug.via.usb.requires.3.4"))) { return false; } if (runnerParameters.isFastPackaging() && !checkAdtVersionForPackaging(module.getProject(), adtVersion, "2.7", sdk.getName(), FlexBundle.message("air.mobile.ios.fast.packaging.requires.2.7"))) { return false; } if (!checkAdtVersionForPackaging(module.getProject(), adtVersion, "2.6", sdk.getName(), FlexBundle.message("air.mobile.packaging.requires.2.6"))) { return false; } final ExternalTask task = createIOSPackageTask(module, bc, packageType, runnerParameters.isFastPackaging(), bc.getIosPackagingOptions().getSigningOptions().getIOSSdkPath(), runnerParameters.getUsbDebugPort(), passwords); return ExternalTask.runWithProgress(task, FlexBundle.message("creating.ios.package"), FlexBundle.message("create.ios.package.title")); } public static ExternalTask createAirDesktopTask(final Module module, final FlexBuildConfiguration bc, final DesktopPackageType packageType, final PasswordStore passwords) { final AirDesktopPackagingOptions packagingOptions = bc.getAirDesktopPackagingOptions(); final AirSigningOptions signingOptions = packagingOptions.getSigningOptions(); final boolean tempCertificate = signingOptions.isUseTempCertificate(); final String keystorePassword = tempCertificate ? TEMP_KEYSTORE_PASSWORD : passwords.getKeystorePassword(signingOptions.getKeystorePath()); final String keyPassword = tempCertificate || signingOptions.getKeyAlias().isEmpty() ? "" : passwords.getKeyPassword(signingOptions.getKeystorePath(), signingOptions.getKeyAlias()); final String outputFolder = PathUtil.getParentPath(bc.getActualOutputFilePath()); final String packageFilePath = outputFolder + "/" + packagingOptions.getPackageFileName() + packageType.getFileExtension(); return new AdtPackageTask(module.getProject(), bc.getSdk(), packageFilePath) { protected void appendAdtOptions(List<String> command) { switch (packageType) { case AirInstaller: command.add("-package"); appendSigningOptions(command, packagingOptions, keystorePassword, keyPassword); break; case NativeInstaller: command.add("-package"); appendSigningOptions(command, packagingOptions, keystorePassword, keyPassword); command.add("-target"); command.add("native"); break; case CaptiveRuntimeBundle: command.add("-package"); appendSigningOptions(command, packagingOptions, keystorePassword, keyPassword); command.add("-target"); command.add("bundle"); break; case Airi: command.add("-prepare"); break; } appendPaths(command, module, bc, packagingOptions, null, packageType.getFileExtension()); } }; } public static ExternalTask createAndroidPackageTask(final Module module, final FlexBuildConfiguration bc, final AndroidPackageType packageType, final boolean captiveRuntime, final int debugPort, final PasswordStore passwords) { final AndroidPackagingOptions packagingOptions = bc.getAndroidPackagingOptions(); final AirSigningOptions signingOptions = packagingOptions.getSigningOptions(); final boolean tempCertificate = signingOptions.isUseTempCertificate(); final String keystorePassword = tempCertificate ? TEMP_KEYSTORE_PASSWORD : passwords.getKeystorePassword(signingOptions.getKeystorePath()); final String keyPassword = tempCertificate || signingOptions.getKeyAlias().isEmpty() ? "" : passwords.getKeyPassword(signingOptions.getKeystorePath(), signingOptions.getKeyAlias()); final String outputFolder = PathUtil.getParentPath(bc.getActualOutputFilePath()); final String packageFilePath = outputFolder + "/" + packagingOptions.getPackageFileName() + ".apk"; return new AdtPackageTask(module.getProject(), bc.getSdk(), packageFilePath) { protected void appendAdtOptions(List<String> command) { command.add("-package"); command.add("-target"); switch (packageType) { case Release: command.add(captiveRuntime ? "apk-captive-runtime" : "apk"); break; case DebugOverNetwork: command.add("apk-debug"); command.add("-connect"); break; case DebugOverUSB: command.add("apk-debug"); command.add("-listen"); command.add(String.valueOf(debugPort)); break; } /* if (parameters.AIR_DOWNLOAD_URL.length() > 0) { command.add("-airDownloadURL"); command.add(parameters.AIR_DOWNLOAD_URL); } */ if (!AirSigningOptions.ARCH_DEFAULT.equals(packagingOptions.getSigningOptions().getArch())) { command.add("-arch"); command.add(packagingOptions.getSigningOptions().getArch()); } appendSigningOptions(command, packagingOptions, keystorePassword, keyPassword); appendPaths(command, module, bc, packagingOptions, null, ".apk"); } }; } public static ExternalTask createIOSPackageTask(final Module module, final FlexBuildConfiguration bc, final IOSPackageType packageType, final boolean fastPackaging, final String iosSDKPath, final int usbDebugPort, final PasswordStore passwords) { final IosPackagingOptions packagingOptions = bc.getIosPackagingOptions(); final AirSigningOptions signingOptions = packagingOptions.getSigningOptions(); final boolean simulator = packageType == IOSPackageType.TestOnSimulator || packageType == IOSPackageType.DebugOnSimulator; // temp certificate not applicable for iOS final String keystorePassword = simulator ? TEMP_KEYSTORE_PASSWORD : passwords.getKeystorePassword(signingOptions.getKeystorePath()); final String keyPassword = simulator ? null : passwords.getKeyPassword(signingOptions.getKeystorePath(), signingOptions.getKeyAlias()); final String outputFolder = PathUtil.getParentPath(bc.getActualOutputFilePath()); final String packageFilePath = outputFolder + "/" + packagingOptions.getPackageFileName() + ".ipa"; return new AdtPackageTask(module.getProject(), bc.getSdk(), packageFilePath) { protected void appendAdtOptions(List<String> command) { command.add("-package"); final String adtOptions = packagingOptions.getSigningOptions().getADTOptions(); if (!adtOptions.isEmpty()) { final String undocumentedOptions = FlexCommonUtils.removeOptions(adtOptions, "sampler", "hideAneLibSymbols", "useLegacyAOT"); command.addAll(StringUtil.split(undocumentedOptions, " ")); } command.add("-target"); switch (packageType) { case Test: command.add(fastPackaging ? "ipa-test-interpreter" : "ipa-test"); break; case DebugOverUSB: command.add(fastPackaging ? "ipa-debug-interpreter" : "ipa-debug"); command.add("-listen"); command.add(String.valueOf(usbDebugPort)); break; case DebugOverNetwork: command.add(fastPackaging ? "ipa-debug-interpreter" : "ipa-debug"); command.add("-connect"); break; case TestOnSimulator: command.add("ipa-test-interpreter-simulator"); break; case DebugOnSimulator: command.add("ipa-debug-interpreter-simulator"); break; case AdHoc: command.add("ipa-ad-hoc"); break; case AppStore: command.add("ipa-app-store"); break; } if (!adtOptions.isEmpty() && (adtOptions.equals("-sampler") || adtOptions.startsWith("-sampler ") || adtOptions.endsWith(" -sampler") || adtOptions.contains(" -sampler "))) { command.add("-sampler"); } final List<String> hideAneLibSymbols = FlexCommonUtils.getOptionValues(adtOptions, "hideAneLibSymbols"); if (!hideAneLibSymbols.isEmpty()) { command.add("-hideAneLibSymbols"); command.add(hideAneLibSymbols.get(0)); // has one parameter: "yes" or "no" } final List<String> useLegacyAOT = FlexCommonUtils.getOptionValues(adtOptions, "useLegacyAOT"); if (!useLegacyAOT.isEmpty()) { command.add("-useLegacyAOT"); command.add(useLegacyAOT.get(0)); // has one parameter: "yes" or "no" } if (simulator) { command.add("-storetype"); command.add("PKCS12"); command.add("-keystore"); command.add(getTempKeystorePath()); command.add("-storepass"); command.add(TEMP_KEYSTORE_PASSWORD); } else { appendSigningOptions(command, packagingOptions, keystorePassword, keyPassword); } if (!simulator) { command.add("-provisioning-profile"); command.add(FileUtil.toSystemDependentName(signingOptions.getProvisioningProfilePath())); } appendPaths(command, module, bc, packagingOptions, iosSDKPath, ".ipa"); } }; } public static boolean installApk(final Project project, final Sdk flexSdk, final @Nullable DeviceInfo device, final String apkPath, final String applicationId) { return uninstallAndroidApplication(project, flexSdk, device, applicationId) && ExternalTask.runWithProgress(new AdtTask(project, flexSdk) { protected void appendAdtOptions(final List<String> command) { command.add("-installApp"); command.add("-platform"); command.add("android"); if (device != null) { command.add("-device"); command.add(device.DEVICE_ID); } command.add("-package"); command.add(apkPath); } }, FlexBundle.message("installing.0", apkPath.substring(apkPath.lastIndexOf('/') + 1)), FlexBundle.message("install.android.application.title")); } public static boolean installOnIosSimulator(final Project project, final Sdk flexSdk, final String ipaPath, final String applicationId, final String iOSSdkPath) { return uninstallFromIosSimulator(project, flexSdk, applicationId, iOSSdkPath) && ExternalTask.runWithProgress(new AdtTask(project, flexSdk) { protected void appendAdtOptions(final List<String> command) { command.add("-installApp"); command.add("-platform"); command.add("ios"); command.add("-platformsdk"); command.add(iOSSdkPath); command.add("-device"); command.add("ios-simulator"); command.add("-package"); command.add(ipaPath); } }, FlexBundle.message("installing.0", ipaPath.substring(ipaPath.lastIndexOf('/') + 1)), FlexBundle.message("install.ipa.on.simulator.title")); } public static boolean installOnIosDevice(final Project project, final Sdk flexSdk, final FlashRunnerParameters runnerParameters, final String ipaPath) { return ExternalTask.runWithProgress(new AdtTask(project, flexSdk) { protected void appendAdtOptions(final List<String> command) { command.add("-installApp"); command.add("-platform"); command.add("ios"); //command.add("-platformsdk"); //command.add(iOSSdkPath); final DeviceInfo device = runnerParameters.getDeviceInfo(); if (device != null) { command.add("-device"); command.add(String.valueOf(device.IOS_HANDLE)); } command.add("-package"); command.add(ipaPath); } }, FlexBundle.message("installing.0", ipaPath.substring(ipaPath.lastIndexOf('/') + 1)), FlexBundle.message("install.ios.app.title")); } public static boolean scanIosDevices(final Project project, final Sdk flexSdk, final BCBasedRunnerParameters runnerParameters) { final List<DeviceInfo> devices = DeviceInfo.getIosDevices(project, flexSdk); if (devices.isEmpty()) { final int choice = Messages .showYesNoDialog(project, "No iOS devices connected to the computer", "Error", "Scan Again", "Cancel", Messages.getErrorIcon()); return choice == Messages.YES && scanIosDevices(project, flexSdk, runnerParameters); } else if (devices.size() == 1) { runnerParameters.setDeviceInfo(devices.get(0)); return true; } else { final String preferredId = PropertiesComponent.getInstance(project).getValue(PREFERRED_IOS_DEVICE_ID_PROPERTY); String preferredPresentableName = null; final Collection<String> presentableNames = new ArrayList<>(); for (DeviceInfo device : devices) { final StringBuilder presentableName = new StringBuilder(); if (device.DEVICE_NAME != null && device.IOS_DEVICE_CLASS != null && !device.DEVICE_NAME.contains(device.IOS_DEVICE_CLASS)) { presentableName.append(device.IOS_DEVICE_CLASS).append(' '); } if (device.DEVICE_NAME != null) { presentableName.append('\'').append(device.DEVICE_NAME).append("' "); } presentableName.append('(').append(device.DEVICE_ID.substring(0, 6)).append("...").append(device.DEVICE_ID.substring(36)) .append(')'); if (device.DEVICE_ID.equals(preferredId)) { preferredPresentableName = presentableName.toString(); } presentableNames.add(presentableName.toString()); } if (preferredPresentableName == null) { preferredPresentableName = presentableNames.iterator().next(); } final int choice = Messages.showChooseDialog(project, "Select iOS device", "iOS Device", null, presentableNames.toArray(new String[presentableNames.size()]), preferredPresentableName); if (choice == -1) return false; final DeviceInfo selectedDevice = devices.get(choice); runnerParameters.setDeviceInfo(selectedDevice); PropertiesComponent.getInstance(project).setValue(PREFERRED_IOS_DEVICE_ID_PROPERTY, selectedDevice.DEVICE_ID); return true; } } public static boolean scanAndroidDevices(final Project project, final Sdk flexSdk, final BCBasedRunnerParameters runnerParameters) { final List<DeviceInfo> devices = DeviceInfo.getAndroidDevices(project, flexSdk); if (devices.isEmpty()) { final int choice = Messages .showYesNoDialog(project, "No Android devices connected to the computer", "Error", "Scan Again", "Cancel", Messages.getErrorIcon()); return choice == Messages.YES && scanAndroidDevices(project, flexSdk, runnerParameters); } else if (devices.size() == 1) { runnerParameters.setDeviceInfo(devices.get(0)); return true; } else { final String preferredId = PropertiesComponent.getInstance(project).getValue(PREFERRED_ANDROID_DEVICE_ID_PROPERTY); String preferredPresentableName = null; final Collection<String> presentableNames = new ArrayList<>(); for (DeviceInfo device : devices) { final StringBuilder presentableName = new StringBuilder(); if (!"device".equals(device.DEVICE_NAME)) { presentableName.append(device.DEVICE_NAME).append(' '); presentableName.append('(').append(device.DEVICE_ID).append(')'); } else { presentableName.append(device.DEVICE_ID); } if (device.DEVICE_ID.equals(preferredId)) { preferredPresentableName = presentableName.toString(); } presentableNames.add(presentableName.toString()); } if (preferredPresentableName == null) { preferredPresentableName = presentableNames.iterator().next(); } final int choice = Messages.showChooseDialog(project, "Select Android device", "Android Device", null, presentableNames.toArray(new String[presentableNames.size()]), preferredPresentableName); if (choice == -1) return false; final DeviceInfo selectedDevice = devices.get(choice); runnerParameters.setDeviceInfo(selectedDevice); PropertiesComponent.getInstance(project).setValue(PREFERRED_ANDROID_DEVICE_ID_PROPERTY, selectedDevice.DEVICE_ID); return true; } } private static boolean uninstallFromIosSimulator(final Project project, final Sdk sdk, final String applicationId, final String iOSSdkPath) { return ExternalTask.runWithProgress(new AdtTask(project, sdk) { protected void appendAdtOptions(final List<String> command) { command.add("-uninstallApp"); command.add("-platform"); command.add("ios"); command.add("-platformsdk"); command.add(iOSSdkPath); command.add("-device"); command.add("ios-simulator"); command.add("-appid"); command.add(applicationId); } protected boolean checkMessages() { return myMessages.isEmpty() || (myMessages.size() == 1 && (myMessages.get(0).startsWith("Application is not installed") || myMessages.get(0).equals("Failed to find package " + applicationId))); } }, FlexBundle.message("uninstalling.0", applicationId), FlexBundle.message("uninstall.ios.simulator.application.title")); } private static boolean uninstallAndroidApplication(final Project project, final Sdk flexSdk, final @Nullable DeviceInfo device, final String applicationId) { return ExternalTask.runWithProgress(new AdtTask(project, flexSdk) { protected void appendAdtOptions(final List<String> command) { command.add("-uninstallApp"); command.add("-platform"); command.add("android"); if (device != null) { command.add("-device"); command.add(device.DEVICE_ID); } command.add("-appid"); command.add(applicationId); } protected boolean checkMessages() { return myMessages.isEmpty() || (myMessages.size() == 1 && myMessages.get(0).equals("Failed to find package " + applicationId)); } }, FlexBundle.message("uninstalling.0", applicationId), FlexBundle.message("uninstall.android.application.title")); } public static boolean launchAndroidApplication(final Project project, final Sdk flexSdk, final @Nullable DeviceInfo device, final String applicationId) { return ExternalTask.runWithProgress(new AdtTask(project, flexSdk) { protected void appendAdtOptions(final List<String> command) { command.add("-launchApp"); command.add("-platform"); command.add("android"); if (device != null) { command.add("-device"); command.add(device.DEVICE_ID); } command.add("-appid"); command.add(applicationId); } }, FlexBundle.message("launching.android.application", applicationId), FlexBundle.message("launch.android.application.title")); } public static boolean launchOnIosSimulator(final Project project, final Sdk flexSdk, final String applicationId, final String iOSSdkPath) { return ExternalTask.runWithProgress(new AdtTask(project, flexSdk) { protected void appendAdtOptions(final List<String> command) { command.add("-launchApp"); command.add("-platform"); command.add("ios"); command.add("-platformsdk"); command.add(iOSSdkPath); command.add("-device"); command.add("ios-simulator"); command.add("-appid"); command.add(applicationId); } }, FlexBundle.message("launching.ios.application", applicationId), FlexBundle.message("launch.ios.application.title")); } public static boolean androidForwardTcpPort(final Project project, final Sdk sdk, final @Nullable DeviceInfo device, final int usbDebugPort) { return ExternalTask.runWithProgress(new ExternalTask(project, sdk) { protected List<String> createCommandLine() { final List<String> command = new ArrayList<>(); command.add(sdk.getHomePath() + ADB_RELATIVE_PATH); if (device != null) { command.add("-s"); command.add(device.DEVICE_ID); } command.add("forward"); command.add("tcp:" + usbDebugPort); command.add("tcp:" + usbDebugPort); return command; } }, "adb forward tcp:" + usbDebugPort + " tcp:" + usbDebugPort, FlexBundle.message("adb.forward.title")); } /** * Stops adb server and launches idb process that remains alive; * caller is responsible to invoke {@link #iosStopForwardTcpPort} when debugging is finished. */ public static boolean iosForwardTcpPort(final Project project, final Sdk sdk, final int usbDebugPort, final int deviceHandle) { // running adb server may be forwarding the port that needed for iOS debugging stopAdbServer(project, sdk, FlexBundle.message("idb.forward"), FlexBundle.message("idb.forward.title")); try { // this process remains alive until "idb -stopforward" called Runtime.getRuntime().exec(new String[]{ sdk.getHomePath() + IDB_RELATIVE_PATH, "-forward", String.valueOf(usbDebugPort), String.valueOf(usbDebugPort), String.valueOf(deviceHandle)}); } catch (IOException e) { Messages.showErrorDialog(project, e.getMessage(), "idb -forward " + usbDebugPort + " " + usbDebugPort + " " + deviceHandle); return false; } return true; } public static void iosStopForwardTcpPort(final Sdk sdk, final int usbDebugPort) { try { // this process remains alive until "idb -stopforward" called Runtime.getRuntime().exec(new String[]{ sdk.getHomePath() + IDB_RELATIVE_PATH, "-stopforward", String.valueOf(usbDebugPort)}); } catch (IOException e) { Logger.getInstance(AirPackageUtil.class.getName()).warn(e); } } @Nullable public static String getAppIdFromPackage(final String packagePath) { ZipFile zipFile = null; try { zipFile = new ZipFile(packagePath); final ZipEntry entry = zipFile.getEntry("assets/META-INF/AIR/application.xml"); if (entry != null) { return FlexUtils.findXMLElement(zipFile.getInputStream(entry), "<application><id>"); } } catch (IOException ignore) {/**/} finally { if (zipFile != null) { try { zipFile.close(); } catch (IOException ignored) {/**/} } } return null; } }