/*
* 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 com.android.tools.idea.sdk.wizard;
import com.android.sdklib.internal.repository.packages.License;
import com.android.sdklib.repository.descriptors.IPkgDesc;
import com.android.tools.idea.wizard.DynamicWizardStepWithHeaderAndDescription;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.Disposable;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.treeStructure.Tree;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import java.util.Map;
import static com.android.tools.idea.wizard.WizardConstants.INSTALL_REQUESTS_KEY;
/**
* A review step for reviewing the changes about to be made and accepting the required licences.
*/
public class LicenseAgreementStep extends DynamicWizardStepWithHeaderAndDescription {
private JTextPane myLicenseTextField;
private Tree myChangeTree;
private JPanel myComponent;
private JRadioButton myDeclineRadioButton;
private JRadioButton myAcceptRadioButton;
private DefaultTreeModel myTreeModel = new DefaultTreeModel(null);
private Map<String, Boolean> myAcceptances = Maps.newHashMap();
private String myCurrentLicense;
public LicenseAgreementStep(@NotNull Disposable disposable) {
super("License Agreement", "Read and agree to the licenses for the components which will be installed", null, disposable);
setBodyComponent(myComponent);
}
@Override
public void init() {
super.init();
myChangeTree.setModel(myTreeModel);
myChangeTree.setShowsRootHandles(false);
myLicenseTextField.setEditable(false);
// Initialize radio buttons
ButtonGroup group = new ButtonGroup();
group.add(myDeclineRadioButton);
group.add(myAcceptRadioButton);
myDeclineRadioButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myAcceptances.put(myCurrentLicense, Boolean.FALSE);
invokeUpdate(null);
}
});
myAcceptRadioButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
myAcceptances.put(myCurrentLicense, Boolean.TRUE);
invokeUpdate(null);
}
});
myChangeTree.addTreeSelectionListener(new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
DefaultMutableTreeNode selected = (DefaultMutableTreeNode)myChangeTree.getLastSelectedPathComponent();
if (selected != null && selected.isRoot()) {
return;
}
if (selected != null && !selected.isLeaf()) {
License license = (License)selected.getUserObject();
myLicenseTextField.setText(license.getLicense());
myCurrentLicense = license.getLicenseRef();
}
else if (selected != null && !selected.isRoot()) {
Change change = (Change)selected.getUserObject();
myLicenseTextField.setText(change.license.getLicense());
myCurrentLicense = change.license.getLicenseRef();
}
if (myAcceptances.get(myCurrentLicense)) {
myAcceptRadioButton.setSelected(true);
} else {
myDeclineRadioButton.setSelected(true);
}
myLicenseTextField.setCaretPosition(0);
}
});
myChangeTree.setCellRenderer(new ColoredTreeCellRenderer() {
@Override
public void customizeCellRenderer(@NotNull JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
if (row == 0) {
append("Licenses", SimpleTextAttributes.REGULAR_ATTRIBUTES);
return;
}
DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
if (!leaf) {
License license = (License)node.getUserObject();
append(license.getLicenseRef(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
} else {
Change change = (Change)node.getUserObject();
if (change == null) {
return;
}
append(change.toString(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
setIcon(change.getIcon());
}
}
});
setChanges(createChangesList());
}
@Override
public boolean isStepVisible() {
return myState.listSize(INSTALL_REQUESTS_KEY) > 0;
}
@Override
public boolean validate() {
for (String s : myAcceptances.keySet()) {
if (!myAcceptances.get(s)) {
return false;
}
}
return true;
}
@NotNull
@Override
public String getStepName() {
return "License Agreement";
}
private void expandTree() {
for (int i = 0; i < myChangeTree.getRowCount(); ++i) {
myChangeTree.expandRow(i);
}
}
private List<Change> createChangesList() {
List<Change> toReturn = Lists.newArrayList();
List requestedPackageNames = myState.get(INSTALL_REQUESTS_KEY);
if (requestedPackageNames != null) {
for (Object o : requestedPackageNames) {
IPkgDesc desc = (IPkgDesc)o;
if (desc.getAndroidVersion() != null && desc.getAndroidVersion().isPreview()) {
toReturn.add(new Change(ChangeType.INSTALL, (IPkgDesc)o, AndroidSdkLicenseTemporaryData.HARDCODED_ANDROID_PREVIEW_SDK_LICENSE));
} else {
toReturn.add(new Change(ChangeType.INSTALL, (IPkgDesc)o, AndroidSdkLicenseTemporaryData.HARDCODED_ANDROID_SDK_LICENSE));
}
}
}
return toReturn;
}
public void setChanges(List<Change> changes) {
Map<String, DefaultMutableTreeNode> licenseNodeMap = Maps.newHashMap();
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
DefaultMutableTreeNode firstChild = null;
for (Change change : changes) {
if (!licenseNodeMap.containsKey(change.license.getLicenseRef())) {
DefaultMutableTreeNode n = new DefaultMutableTreeNode(change.license);
if (firstChild == null) {
firstChild = n;
}
String licenceRef = change.license.getLicenseRef();
licenseNodeMap.put(licenceRef, n);
myAcceptances.put(licenceRef, Boolean.FALSE);
root.add(n);
}
licenseNodeMap.get(change.license.getLicenseRef()).add(new DefaultMutableTreeNode(change));
}
myTreeModel = new DefaultTreeModel(root);
myChangeTree.setModel(myTreeModel);
expandTree();
if (firstChild != null) {
myChangeTree.setSelectionPath(new TreePath(firstChild.getPath()));
}
}
@Override
public void onEnterStep() {
super.onEnterStep();
setChanges(createChangesList());
}
@Override
public JComponent getPreferredFocusedComponent() {
return myChangeTree;
}
protected enum ChangeType {
INSTALL,
UPDATE,
REMOVE
}
protected static class Change {
public ChangeType type;
public IPkgDesc packageDescription;
public License license;
public Change(@NotNull ChangeType type, @NotNull IPkgDesc packageDescription, @NotNull License license) {
this.type = type;
this.packageDescription = packageDescription;
this.license = license;
}
@Override
public String toString() {
if (packageDescription.getListDescription() != null) {
return packageDescription.getListDescription();
} else {
return "INCORRECT PACKAGE DESCRIPTION";
}
}
public Icon getIcon() {
switch (type) {
case INSTALL:
return AllIcons.Actions.Download;
case UPDATE:
return AllIcons.Actions.Refresh;
case REMOVE:
return AllIcons.Actions.Cancel;
default:
return null;
}
}
}
}