blob: 7a164d7f7c7df5dbebd5435dc4900eaf7d89f1a3 [file] [log] [blame]
/*
* 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 "&nbsp;" part so set the text here.
mySignatureHelpLabel.setText("<html><a href=\"\">Signature&nbsp;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;
}
}