/* * 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.avdmanager; import com.android.sdklib.*; import com.android.sdklib.repository.descriptors.PkgType; import com.android.sdklib.repository.local.LocalSdk; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.intellij.icons.AllIcons; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Comparing; import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.ScrollPaneFactory; import com.intellij.ui.components.JBLabel; import com.intellij.ui.table.TableView; import com.intellij.util.ui.ColumnInfo; import com.intellij.util.ui.ListTableModel; import org.jetbrains.android.sdk.AndroidSdkData; import org.jetbrains.android.sdk.AndroidSdkUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableCellRenderer; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Comparator; import java.util.Set; import java.util.List; /** * Displays a list of system images currently installed and allows selection of one */ public class SystemImageList extends JPanel implements ListSelectionListener { private static final Logger LOG = Logger.getInstance(SystemImageList.class); private final JButton myRefreshButton = new JButton(AllIcons.Actions.Refresh); private final LocalSdk mySdk; private TableView<AvdWizardConstants.SystemImageDescription> myTable = new TableView<AvdWizardConstants.SystemImageDescription>(); private ListTableModel<AvdWizardConstants.SystemImageDescription> myModel = new ListTableModel<AvdWizardConstants.SystemImageDescription>(); private Set<SystemImageSelectionListener> myListeners = Sets.newHashSet(); private Predicate<ISystemImage> myFilter; /** * Components which wish to receive a notification when the user has selected an AVD from this * table must implement this interface and register themselves through {@link #addSelectionListener(SystemImageSelectionListener)} */ public interface SystemImageSelectionListener { void onSystemImageSelected(@Nullable AvdWizardConstants.SystemImageDescription systemImage); } public SystemImageList() { AndroidSdkData androidSdkData = AndroidSdkUtils.tryToChooseAndroidSdk(); if (androidSdkData == null) { throw new RuntimeException("No SDK Found"); } mySdk = androidSdkData.getLocalSdk(); myModel.setColumnInfos(ourColumnInfos); myModel.setSortable(true); refreshImages(true); myTable.setModelAndUpdateColumns(myModel); myTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); myTable.setRowSelectionAllowed(true); setLayout(new BorderLayout()); add(ScrollPaneFactory.createScrollPane(myTable), BorderLayout.CENTER); JPanel southPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); southPanel.add(myRefreshButton); myRefreshButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { refreshImages(true); } }); add(southPanel, BorderLayout.SOUTH); myTable.getSelectionModel().addListSelectionListener(this); } public void refreshImages(boolean forceRefresh) { if (forceRefresh) { mySdk.clearLocalPkg(PkgType.PKG_ALL); } List<IAndroidTarget> targets = Lists.newArrayList(mySdk.getTargets()); List<AvdWizardConstants.SystemImageDescription> items = Lists.newArrayList(); for (IAndroidTarget target : targets) { ISystemImage[] systemImages = target.getSystemImages(); if (systemImages != null) { for (ISystemImage image : systemImages) { // If we don't have a filter or this image passes the filter if (myFilter == null || myFilter.apply(image)) { items.add(new AvdWizardConstants.SystemImageDescription(target, image)); } } } } myModel.setItems(items); } public void setFilter(Predicate<ISystemImage> filter) { myFilter = filter; } public void addSelectionListener(SystemImageSelectionListener listener) { myListeners.add(listener); } public void removeSelectionListener(SystemImageSelectionListener listener) { myListeners.remove(listener); } public void setSelectedImage(@Nullable AvdWizardConstants.SystemImageDescription selectedImage) { if (selectedImage != null) { for (AvdWizardConstants.SystemImageDescription listItem : myModel.getItems()) { if (selectedImage.target.getVersion().equals(listItem.target.getVersion()) && selectedImage.systemImage.getAbiType().equals(listItem.systemImage.getAbiType())) { myTable.setSelection(ImmutableSet.of(listItem)); return; } } } else { myTable.clearSelection(); } } /** * This class implements the table selection interface and passes the selection events on to its listeners. * @param e */ @Override public void valueChanged(ListSelectionEvent e) { AvdWizardConstants.SystemImageDescription selected = myTable.getSelectedObject(); for (SystemImageSelectionListener listener : myListeners) { listener.onSystemImageSelected(selected); } } /** * List of columns present in our table. Each column is represented by a ColumnInfo which tells the table how to get * the cell value in that column for a given row item. */ private static final ColumnInfo[] ourColumnInfos = new ColumnInfo[] { new SystemImageColumnInfo("Release Name") { @Nullable @Override public String valueOf(AvdWizardConstants.SystemImageDescription systemImage) { String codeName = SdkVersionInfo.getCodeName(systemImage.target.getVersion().getApiLevel()); String maybeDeprecated = systemImage.target.getVersion().getApiLevel() < SdkVersionInfo.LOWEST_ACTIVE_API ? " (Deprecated)" : ""; return codeName == null ? "Unknown" : codeName + maybeDeprecated; } }, new SystemImageColumnInfo("API Level", 100) { @Nullable @Override public String valueOf(AvdWizardConstants.SystemImageDescription systemImage) { return systemImage.target.getVersion().getApiString(); } @Nullable @Override public Comparator<AvdWizardConstants.SystemImageDescription> getComparator() { return new Comparator<AvdWizardConstants.SystemImageDescription>() { @Override public int compare(AvdWizardConstants.SystemImageDescription o1, AvdWizardConstants.SystemImageDescription o2) { return o1.target.getVersion().getApiLevel() - o2.target.getVersion().getApiLevel(); } }; } }, new SystemImageColumnInfo("ABI", 100) { @Nullable @Override public String valueOf(AvdWizardConstants.SystemImageDescription systemImage) { return systemImage.systemImage.getAbiType(); } }, new SystemImageColumnInfo("Target") { @Nullable @Override public String valueOf(AvdWizardConstants.SystemImageDescription systemImage) { return systemImage.target.getFullName(); } }, }; /** * This class extends {@link com.intellij.util.ui.ColumnInfo} in order to pull a string value from a given {@link com.android.sdklib.internal.avd.AvdInfo}. * This is the column info used for most of our table, including the Name, Resolution, and API level columns. * It uses the text field renderer ({@link #myRenderer}) and allows for sorting by the lexicographical value * of the string displayed by the {@link com.intellij.ui.components.JBLabel} rendered as the cell component. An explicit width may be used * by calling the overloaded constructor, otherwise the column will auto-scale to fill available space. */ public abstract static class SystemImageColumnInfo extends ColumnInfo<AvdWizardConstants.SystemImageDescription, String> { private final Border myBorder = IdeBorderFactory.createEmptyBorder(10, 10, 10, 10); private final int myWidth; public SystemImageColumnInfo(@NotNull String name, int width) { super(name); myWidth = width; } public SystemImageColumnInfo(@NotNull String name) { this(name, -1); } @Nullable @Override public TableCellRenderer getRenderer(AvdWizardConstants.SystemImageDescription o) { return new TableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { JBLabel label = new JBLabel((String)value); label.setBorder(myBorder); if (table.getSelectedRow() == row) { label.setBackground(table.getSelectionBackground()); label.setForeground(table.getSelectionForeground()); label.setOpaque(true); } return label; } }; } @Nullable @Override public Comparator<AvdWizardConstants.SystemImageDescription> getComparator() { return new Comparator<AvdWizardConstants.SystemImageDescription>() { @Override public int compare(AvdWizardConstants.SystemImageDescription o1, AvdWizardConstants.SystemImageDescription o2) { String s1 = valueOf(o1); String s2 = valueOf(o2); return Comparing.compare(s1, s2); } }; } @Override public int getWidth(JTable table) { return myWidth; } } }