| /* |
| * Copyright (C) 2014 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 org.jetbrains.android.exportSignedPackage; |
| |
| import com.android.ide.common.repository.GradleVersion; |
| import com.android.tools.idea.gradle.project.model.AndroidModuleModel; |
| import com.android.tools.idea.help.StudioHelpManagerImpl; |
| import com.google.common.collect.Sets; |
| import com.intellij.ide.util.PropertiesComponent; |
| import com.intellij.ide.wizard.CommitStepException; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptor; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; |
| import com.intellij.openapi.ui.TextFieldWithBrowseButton; |
| import com.intellij.openapi.vfs.VfsUtilCore; |
| import com.intellij.ui.ListSpeedSearch; |
| import com.intellij.ui.components.JBLabel; |
| import com.intellij.ui.components.JBList; |
| import com.intellij.util.ArrayUtil; |
| import gnu.trove.TIntArrayList; |
| import org.jetbrains.android.util.AndroidBundle; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.awt.*; |
| import java.awt.event.MouseAdapter; |
| import java.awt.event.MouseEvent; |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Set; |
| |
| public class GradleSignStep extends ExportSignedPackageWizardStep { |
| private static final Logger LOG = Logger.getInstance("#org.jetbrains.android.exportSignedPackage.GradleSignStep"); |
| |
| @NonNls private static final String PROPERTY_APK_PATH = "ExportApk.ApkPath"; |
| @NonNls private static final String PROPERTY_BUILD_VARIANTS = "ExportApk.BuildVariants"; |
| @NonNls private static final String PROPERTY_V1_SIGN = "ExportApk.SignV1"; |
| @NonNls private static final String PROPERTY_V2_SIGN = "ExportApk.SignV2"; |
| |
| public static final GradleVersion MIN_SIGNATURE_SELECTION_VERSION = new GradleVersion(2, 3, 0); |
| |
| private JPanel myContentPanel; |
| private TextFieldWithBrowseButton myApkPathField; |
| private JBList<String> myBuildVariantsList; |
| private JCheckBox myV1JarSignatureCheckBox; |
| private JCheckBox myV2FullAPKSignatureCheckBox; |
| private JBLabel mySignatureHelpLabel; |
| private JBLabel mySignatureLabel; |
| private JPanel mySignaturePanel; |
| |
| private final ExportSignedPackageWizard myWizard; |
| private final DefaultListModel<String> myBuildVariantsListModel = new DefaultListModel<>(); |
| |
| private AndroidModuleModel myAndroidModel; |
| |
| public GradleSignStep(@NotNull ExportSignedPackageWizard exportSignedPackageWizard) { |
| myWizard = exportSignedPackageWizard; |
| |
| myBuildVariantsList.setModel(myBuildVariantsListModel); |
| myBuildVariantsList.setEmptyText(AndroidBundle.message("android.apk.sign.gradle.no.variants")); |
| new ListSpeedSearch<>(myBuildVariantsList); |
| } |
| |
| @Override |
| public void _init() { |
| myAndroidModel = AndroidModuleModel.get(myWizard.getFacet()); |
| |
| PropertiesComponent properties = PropertiesComponent.getInstance(myWizard.getProject()); |
| |
| myBuildVariantsListModel.clear(); |
| List<String> buildVariants = new ArrayList<>(); |
| if (myAndroidModel != null) { |
| buildVariants.addAll(myAndroidModel.getVariantNames()); |
| Collections.sort(buildVariants); |
| } |
| |
| TIntArrayList lastSelectedIndices = new TIntArrayList(buildVariants.size()); |
| String[] cachedVariants = properties.getValues(PROPERTY_BUILD_VARIANTS); |
| Set<String> lastSelectedVariants = cachedVariants == null ? Collections.emptySet() : Sets.newHashSet(cachedVariants); |
| |
| for (int i = 0; i < buildVariants.size(); i++) { |
| String variant = buildVariants.get(i); |
| myBuildVariantsListModel.addElement(variant); |
| |
| if (lastSelectedVariants.contains(variant)) { |
| lastSelectedIndices.add(i); |
| } |
| } |
| |
| myBuildVariantsList.setSelectedIndices(lastSelectedIndices.toNativeArray()); |
| |
| String lastApkFolderPath = properties.getValue(PROPERTY_APK_PATH); |
| File lastApkFolder; |
| if (lastApkFolderPath != null) { |
| lastApkFolder = new File(lastApkFolderPath); |
| } |
| else { |
| if (myAndroidModel == null) { |
| lastApkFolder = VfsUtilCore.virtualToIoFile(myWizard.getProject().getBaseDir()); |
| } |
| else { |
| lastApkFolder = myAndroidModel.getRootDirPath(); |
| } |
| } |
| myApkPathField.setText(lastApkFolder.getAbsolutePath()); |
| FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); |
| myApkPathField.addBrowseFolderListener("Select APK Destination Folder", null, myWizard.getProject(), descriptor); |
| |
| boolean isBundle = myWizard.getTargetType().equals(ExportSignedPackageWizard.BUNDLE); |
| mySignaturePanel.setVisible(!isBundle); |
| mySignatureLabel.setVisible(!isBundle); |
| myV1JarSignatureCheckBox.setEnabled(!isBundle); |
| if (!isBundle) { |
| GradleVersion modelVersion = null; |
| if (myAndroidModel != null) { |
| modelVersion = myAndroidModel.getModelVersion(); |
| } |
| initSignaturePanel(properties, modelVersion); |
| } |
| } |
| |
| private void initSignaturePanel(PropertiesComponent properties, GradleVersion modelVersion) { |
| boolean enabled = modelVersion != null && modelVersion.compareIgnoringQualifiers(MIN_SIGNATURE_SELECTION_VERSION) >= 0; |
| myV1JarSignatureCheckBox.setEnabled(enabled); |
| myV1JarSignatureCheckBox.setSelected(properties.getBoolean(PROPERTY_V1_SIGN)); |
| myV2FullAPKSignatureCheckBox.setEnabled(enabled); |
| myV2FullAPKSignatureCheckBox.setSelected(properties.getBoolean(PROPERTY_V2_SIGN)); |
| |
| // Set HTML label here; the visual editor does not like the " " part so set the text here. |
| mySignatureHelpLabel.setText("<html><a href=\"\">Signature Help</a></html>"); |
| mySignatureHelpLabel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); |
| mySignatureHelpLabel.addMouseListener(new MouseAdapter() { |
| @Override |
| public void mousePressed(MouseEvent e) { |
| if (Desktop.isDesktopSupported()) { |
| Desktop desktop = Desktop.getDesktop(); |
| if (desktop.isSupported(Desktop.Action.BROWSE)) { |
| URI uri; |
| try { |
| uri = new URI("http://developer.android.com/about/versions/nougat/android-7.0.html#apk_signature_v2"); |
| } |
| catch (URISyntaxException ex) { |
| throw new AssertionError(ex); |
| } |
| |
| try { |
| desktop.browse(uri); |
| } |
| catch (IOException ex) { |
| LOG.error("Failed to open URI '" + uri + "'", ex); |
| } |
| } |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public String getHelpId() { |
| return StudioHelpManagerImpl.STUDIO_HELP_PREFIX + "r/studio-ui/app-signing"; |
| } |
| |
| @Override |
| protected void commitForNext() throws CommitStepException { |
| if (myAndroidModel == null) { |
| throw new CommitStepException(AndroidBundle.message("android.apk.sign.gradle.no.model")); |
| } |
| |
| final String apkFolder = myApkPathField.getText().trim(); |
| if (apkFolder.isEmpty()) { |
| throw new CommitStepException(AndroidBundle.message("android.apk.sign.gradle.missing.destination", myWizard.getTargetType())); |
| } |
| |
| File f = new File(apkFolder); |
| if (!f.isDirectory() || !f.canWrite()) { |
| throw new CommitStepException(AndroidBundle.message("android.apk.sign.gradle.invalid.destination")); |
| } |
| |
| int[] selectedVariantIndices = myBuildVariantsList.getSelectedIndices(); |
| if (myBuildVariantsList.isEmpty() || selectedVariantIndices.length == 0) { |
| throw new CommitStepException(AndroidBundle.message("android.apk.sign.gradle.missing.variants")); |
| } |
| |
| List buildVariants = myBuildVariantsList.getSelectedValuesList(); |
| |
| boolean isV1 = myV1JarSignatureCheckBox.isSelected(); |
| boolean isV2 = myV2FullAPKSignatureCheckBox.isSelected(); |
| if (myV1JarSignatureCheckBox.isEnabled() && !isV1 && !isV2) { |
| throw new CommitStepException(AndroidBundle.message("android.apk.sign.gradle.missing.signature-version")); |
| } |
| |
| myWizard.setApkPath(apkFolder); |
| //noinspection unchecked |
| myWizard.setGradleOptions(myBuildVariantsList.getSelectedValuesList()); |
| myWizard.setV1Signature(isV1); |
| myWizard.setV2Signature(isV2); |
| |
| PropertiesComponent properties = PropertiesComponent.getInstance(myWizard.getProject()); |
| properties.setValue(PROPERTY_APK_PATH, apkFolder); |
| //noinspection unchecked |
| properties.setValues(PROPERTY_BUILD_VARIANTS, ArrayUtil.toStringArray(buildVariants)); |
| properties.setValue(PROPERTY_V1_SIGN, myV1JarSignatureCheckBox.isSelected()); |
| properties.setValue(PROPERTY_V2_SIGN, myV2FullAPKSignatureCheckBox.isSelected()); |
| } |
| |
| @Override |
| public JComponent getComponent() { |
| return myContentPanel; |
| } |
| } |