| /* |
| * 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.npw; |
| |
| import com.android.SdkConstants; |
| import com.android.assetstudiolib.GraphicGenerator; |
| import com.android.builder.model.SourceProvider; |
| import com.android.resources.Density; |
| import com.android.resources.ResourceFolderType; |
| import com.android.resources.ResourceType; |
| import com.android.tools.idea.templates.*; |
| import com.android.tools.idea.ui.ComboBoxItemWithApiTag; |
| import com.android.tools.idea.ui.ImageComponent; |
| import com.android.tools.idea.wizard.dynamic.DynamicWizardStepWithDescription; |
| import com.android.tools.idea.wizard.dynamic.RadioButtonGroupBinding; |
| import com.google.common.base.Objects; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory; |
| import com.intellij.openapi.module.Module; |
| import com.intellij.openapi.ui.TextFieldWithBrowseButton; |
| import com.intellij.openapi.util.io.FileUtil; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.ui.ColorPanel; |
| import com.intellij.util.ui.update.MergingUpdateQueue; |
| import com.intellij.util.ui.update.Update; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import javax.swing.*; |
| import javax.swing.text.Document; |
| import java.awt.*; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| import java.awt.image.BufferedImage; |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.*; |
| import java.util.List; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| import static com.android.assetstudiolib.ActionBarIconGenerator.Theme; |
| import static com.android.tools.idea.npw.AssetStudioAssetGenerator.*; |
| import static com.android.tools.idea.wizard.dynamic.ScopedStateStore.Key; |
| import static com.android.tools.idea.wizard.dynamic.ScopedStateStore.Scope.PATH; |
| import static com.android.tools.idea.wizard.dynamic.ScopedStateStore.Scope.STEP; |
| import static com.android.tools.idea.wizard.dynamic.ScopedStateStore.createKey; |
| |
| /** |
| * {@linkplain IconStep} is a wizard page that lets the user create a variety of density-scaled assets. |
| * TODO: It seems like the "Launcher" and "Action Bar and Tabs" modes might not be used anymore. |
| */ |
| public class IconStep extends DynamicWizardStepWithDescription implements Disposable { |
| public static final Key<String> ATTR_ASSET_NAME = createKey(AssetStudioAssetGenerator.ATTR_ASSET_NAME, PATH, String.class); |
| public static final Key<String> ATTR_CLIPART_NAME = createKey(AssetStudioAssetGenerator.ATTR_CLIPART_NAME, PATH, String.class); |
| public static final Key<String> ATTR_VECTOR_LIB_ICON_PATH = createKey(AssetStudioAssetGenerator.ATTR_VECTOR_LIB_ICON_PATH, PATH, String.class); |
| public static final Key<String> ATTR_TEXT = createKey(AssetStudioAssetGenerator.ATTR_TEXT, PATH, String.class); |
| public static final Key<String> ATTR_FONT = createKey(AssetStudioAssetGenerator.ATTR_FONT, PATH, String.class); |
| public static final Key<AssetType> ATTR_ASSET_TYPE = createKey(AssetStudioAssetGenerator.ATTR_ASSET_TYPE, PATH, AssetType.class); |
| public static final Key<String> ATTR_ASSET_THEME = createKey(AssetStudioAssetGenerator.ATTR_ASSET_THEME, PATH, String.class); |
| public static final Key<Scaling> ATTR_SCALING = createKey(AssetStudioAssetGenerator.ATTR_SCALING, PATH, Scaling.class); |
| public static final Key<GraphicGenerator.Shape> ATTR_SHAPE = |
| createKey(AssetStudioAssetGenerator.ATTR_SHAPE, PATH, GraphicGenerator.Shape.class); |
| public static final Key<SourceType> ATTR_SOURCE_TYPE = createKey(AssetStudioAssetGenerator.ATTR_SOURCE_TYPE, PATH, SourceType.class); |
| public static final Key<Boolean> ATTR_TRIM = createKey(AssetStudioAssetGenerator.ATTR_TRIM, PATH, Boolean.class); |
| public static final Key<Boolean> ATTR_DOGEAR = createKey(AssetStudioAssetGenerator.ATTR_DOGEAR, PATH, Boolean.class); |
| public static final Key<Integer> ATTR_PADDING = createKey(AssetStudioAssetGenerator.ATTR_PADDING, PATH, Integer.class); |
| public static final Key<Color> ATTR_FOREGROUND_COLOR = createKey(AssetStudioAssetGenerator.ATTR_FOREGROUND_COLOR, PATH, Color.class); |
| public static final Key<Color> ATTR_BACKGROUND_COLOR = createKey(AssetStudioAssetGenerator.ATTR_BACKGROUND_COLOR, PATH, Color.class); |
| public static final Key<String> ATTR_IMAGE_PATH = createKey(AssetStudioAssetGenerator.ATTR_IMAGE_PATH, PATH, String.class); |
| public static final Key<String> ATTR_ICON_RESOURCE = createKey("icon_resource", PATH, String.class); |
| public static final Key<Integer> ATTR_FONT_SIZE = createKey(AssetStudioAssetGenerator.ATTR_FONT_SIZE, PATH, Integer.class); |
| public static final Key<File> ATTR_OUTPUT_FOLDER = createKey(ChooseOutputResDirStep.ATTR_OUTPUT_FOLDER, STEP, File.class); |
| public static final Key<String> ATTR_ERROR_LOG = createKey(AssetStudioAssetGenerator.ATTR_ERROR_LOG, PATH, String.class); |
| public static final Key<String> ATTR_VECTOR_DRAWBLE_WIDTH = createKey(AssetStudioAssetGenerator.ATTR_VECTOR_DRAWBLE_WIDTH, STEP, String.class); |
| public static final Key<String> ATTR_VECTOR_DRAWBLE_HEIGHT = createKey(AssetStudioAssetGenerator.ATTR_VECTOR_DRAWBLE_HEIGHT, STEP, String.class); |
| public static final Key<Integer> ATTR_ORIGINAL_WIDTH = createKey(AssetStudioAssetGenerator.ATTR_ORIGINAL_WIDTH, STEP, Integer.class); |
| public static final Key<Integer> ATTR_ORIGINAL_HEIGHT = createKey(AssetStudioAssetGenerator.ATTR_ORIGINAL_HEIGHT, STEP, Integer.class); |
| public static final Key<Integer> ATTR_VECTOR_DRAWBLE_OPACTITY = createKey(AssetStudioAssetGenerator.ATTR_VECTOR_DRAWBLE_OPACTITY, STEP, Integer.class); |
| public static final Key<Boolean> ATTR_VECTOR_DRAWBLE_AUTO_MIRRORED = createKey(AssetStudioAssetGenerator.ATTR_VECTOR_DRAWBLE_AUTO_MIRRORED, STEP, Boolean.class); |
| |
| |
| private static final Logger LOG = Logger.getInstance(IconStep.class); |
| private static final int CLIPART_ICON_SIZE = 32; |
| private static final int CLIPART_DIALOG_BORDER = 10; |
| private static final int DIALOG_HEADER = 20; |
| private static final String V11 = "V11"; |
| private static final String V9 = "V9"; |
| |
| private final StringEvaluator myStringEvaluator = new StringEvaluator(); |
| private final MergingUpdateQueue myUpdateQueue; |
| private final Map<String, Map<String, BufferedImage>> myImageMap = new ConcurrentHashMap<String, Map<String, BufferedImage>>(); |
| private final Key<TemplateEntry> myTemplateKey; |
| private final Key<SourceProvider> mySourceProviderKey; |
| private final SourceProvider[] mySourceProviders; |
| private AssetStudioAssetGenerator myAssetGenerator; |
| private JPanel myPanel; |
| private JRadioButton myImageRadioButton; |
| private JRadioButton myClipartRadioButton; |
| private JRadioButton myTextRadioButton; |
| private JRadioButton myCropRadioButton; |
| private JRadioButton myCenterRadioButton; |
| private JRadioButton myCircleRadioButton; |
| private JRadioButton mySquareRadioButton; |
| private JRadioButton myNoneRadioButton; |
| private JButton myChooseClipart; |
| private JCheckBox myTrimBlankSpace; |
| private JTextField myText; |
| private JComboBox myFontFamily; |
| private TextFieldWithBrowseButton myImageFile; |
| private ColorPanel myBackgroundColor; |
| private ColorPanel myForegroundColor; |
| private ImageComponent myMdpiPreview; |
| private ImageComponent myHdpiPreview; |
| private ImageComponent myXHdpiPreview; |
| private JSlider myPaddingSlider; |
| private ImageComponent myXXHdpiPreview; |
| private JLabel myForegroundColorLabel; |
| private JLabel myAssetTypeLabel; |
| private JComboBox myChooseThemeComboBox; |
| private JTextField myResourceNameField; |
| private ImageComponent myV9XHdpiPreview; |
| private ImageComponent myV9XXHdpiPreview; |
| private ImageComponent myV9MdpiPreview; |
| private ImageComponent myV9HdpiPreview; |
| private ImageComponent myV11MdpiPreview; |
| private ImageComponent myV11HdpiPreview; |
| private ImageComponent myV11XHdpiPreview; |
| private ImageComponent myV11XXHdpiPreview; |
| private JTextField myPaddingTextField; |
| private JRadioButton myLauncherRadioButton; |
| private JRadioButton myActionBarAndTabsRadioButton; |
| private JRadioButton myNotificationRadioButton; |
| private JPanel myPageBook; |
| private JLabel mySourceSetLabel; |
| private JComboBox mySourceSetComboBox; |
| private JPanel myTypePanel; |
| private JPanel myAssetSourceCardPanel; |
| private JPanel myTypeCardPanel; |
| private String myDefaultName; |
| |
| @SuppressWarnings("UseJBColor") // Colors are used for the graphics generator, not the plugin UI |
| public IconStep(Key<TemplateEntry> templateKey, Key<SourceProvider> sourceProviderKey, |
| @Nullable SourceProvider[] sourceProviders, Disposable disposable) { |
| super(disposable); |
| myTemplateKey = templateKey; |
| mySourceProviderKey = sourceProviderKey; |
| mySourceProviders = sourceProviders; |
| |
| myUpdateQueue = new MergingUpdateQueue("asset.studio", 200, true, null, this, null, false); |
| |
| myImageFile.addBrowseFolderListener(null, null, null, FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()); |
| myForegroundColor.setSelectedColor(Color.BLUE); |
| myBackgroundColor.setSelectedColor(Color.WHITE); |
| |
| for (String font : GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()) { |
| myFontFamily.addItem(new ComboBoxItemWithApiTag(font, font, 1, 1)); |
| if (font.equals(myState.get(ATTR_FONT))) { |
| myFontFamily.setSelectedIndex(myFontFamily.getItemCount() - 1); |
| } |
| } |
| |
| myChooseClipart.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| displayClipartDialog(); |
| } |
| }); |
| setBodyComponent(myPanel); |
| } |
| |
| @Nullable |
| private static Icon getClipartIcon(@Nullable String clipartName) { |
| if (StringUtil.isEmpty(clipartName)) { |
| return null; |
| } |
| BufferedImage icon = null; |
| try { |
| icon = GraphicGenerator.getClipartIcon(clipartName); |
| } |
| catch (IOException e) { |
| Logger.getInstance(IconStep.class).error(e); |
| } |
| return new ImageIcon(icon, clipartName); |
| } |
| |
| private static void populateThemeComboBox(JComboBox comboBox) { |
| for (Theme theme : Theme.values()) { |
| String themeName = theme.name(); |
| comboBox.addItem(new ComboBoxItemWithApiTag(themeName, themeName, 0, 0)); |
| } |
| } |
| |
| private static void show(JComponent... components) { |
| for (JComponent component : components) { |
| component.setVisible(true); |
| component.getParent().invalidate(); |
| } |
| } |
| |
| private static void hide(JComponent... components) { |
| for (JComponent component : components) { |
| component.setVisible(false); |
| component.getParent().invalidate(); |
| } |
| } |
| |
| private static void setIconOrClear(@NotNull ImageComponent component, @Nullable BufferedImage image) { |
| if (image == null) { |
| component.setIcon(null); |
| } |
| else { |
| component.setIcon(new ImageIcon(image)); |
| } |
| } |
| |
| @Nullable |
| private static BufferedImage getImage(@NotNull Map<String, Map<String, BufferedImage>> map, @NotNull String name) { |
| final Map<String, BufferedImage> images = map.get(name); |
| if (images == null) { |
| return null; |
| } |
| |
| final Collection<BufferedImage> values = images.values(); |
| return values.isEmpty() ? null : values.iterator().next(); |
| } |
| |
| @Nullable |
| private static BufferedImage getImage(@NotNull Map<String, Map<String, BufferedImage>> map, |
| @NotNull String category, @NotNull Density density) { |
| String densityString = density.getResourceValue(); |
| final Map<String, BufferedImage> images = map.get(category); |
| if (images == null) { |
| return null; |
| } |
| |
| for (String key : images.keySet()) { |
| if (key.contains(densityString)) { |
| return images.get(key); |
| } |
| } |
| return null; |
| } |
| |
| private static String getResourceDirLabel(@Nullable Module module, File directory) { |
| if (module == null) { |
| return directory.getName(); |
| } |
| String filePath = module.getModuleFilePath(); |
| String parent = new File(filePath).getParent(); |
| String path = directory.getPath(); |
| return path.startsWith(parent) ? path.substring(parent.length() + 1) : directory.getName(); |
| } |
| |
| @Override |
| public void init() { |
| super.init(); |
| |
| myAssetGenerator = new AssetStudioAssetGenerator(new ScopedStateStoreAdapter(myState)); |
| |
| myState.put(ATTR_ASSET_TYPE, AssetType.LAUNCHER); |
| //noinspection deprecation |
| String relativeTemplatePath = FileUtil |
| .join(Template.CATEGORY_PROJECTS, NewProjectWizardState.MODULE_TEMPLATE_NAME, |
| "root", "res", "mipmap-xhdpi", "ic_launcher.png"); |
| myState.put(ATTR_IMAGE_PATH, new File(TemplateManager.getTemplateRootFolder(), relativeTemplatePath).getAbsolutePath()); |
| |
| register(ATTR_OUTPUT_FOLDER, mySourceSetComboBox); |
| register(ATTR_IMAGE_PATH, myImageFile); |
| register(ATTR_TEXT, myText); |
| |
| register(ATTR_SCALING, ImmutableMap.of(myCropRadioButton, Scaling.CROP, myCenterRadioButton, Scaling.CENTER)); |
| |
| register(ATTR_SHAPE, ImmutableMap |
| .of(myCircleRadioButton, GraphicGenerator.Shape.CIRCLE, mySquareRadioButton, GraphicGenerator.Shape.SQUARE, myNoneRadioButton, |
| GraphicGenerator.Shape.NONE)); |
| register(ATTR_PADDING, myPaddingSlider); |
| register(ATTR_PADDING, myPaddingTextField, new ComponentBinding<Integer, JTextField>() { |
| @Override |
| public void setValue(@Nullable Integer newValue, @NotNull JTextField component) { |
| component.setText(newValue == null ? "" : String.valueOf(newValue)); |
| } |
| |
| @Nullable |
| @Override |
| public Integer getValue(@NotNull JTextField component) { |
| try { |
| // Shoehorn user input into acceptable bounds. There's slider and preview |
| // so the user already receives enough feedback. |
| return Math.max(0, Math.min(Integer.parseInt(component.getText()), 100)); |
| } |
| catch (NumberFormatException e) { |
| return 0; |
| } |
| } |
| |
| @Override |
| public void addActionListener(@NotNull ActionListener listener, @NotNull JTextField component) { |
| component.addActionListener(listener); |
| } |
| |
| @Nullable |
| @Override |
| public Document getDocument(@NotNull JTextField component) { |
| return component.getDocument(); |
| } |
| }); |
| register(ATTR_TRIM, myTrimBlankSpace); |
| register(ATTR_FONT, myFontFamily); |
| register(ATTR_SOURCE_TYPE, ImmutableMap |
| .of(myImageRadioButton, SourceType.IMAGE, myClipartRadioButton, SourceType.CLIPART, myTextRadioButton, SourceType.TEXT)); |
| register(ATTR_FOREGROUND_COLOR, myForegroundColor); |
| register(ATTR_BACKGROUND_COLOR, myBackgroundColor); |
| register(ATTR_ASSET_TYPE, ImmutableMap |
| .of(myLauncherRadioButton, AssetType.LAUNCHER, myActionBarAndTabsRadioButton, AssetType.ACTIONBAR, myNotificationRadioButton, |
| AssetType.NOTIFICATION)); |
| register(ATTR_ASSET_THEME, myChooseThemeComboBox); |
| register(ATTR_ASSET_NAME, myResourceNameField); |
| register(ATTR_CLIPART_NAME, myChooseClipart, new ComponentBinding<String, JButton>() { |
| @Override |
| public void setValue(@Nullable String newValue, @NotNull JButton component) { |
| component.setIcon(getClipartIcon(newValue)); |
| component.setText(newValue); |
| } |
| }); |
| |
| populateThemeComboBox(myChooseThemeComboBox); |
| String theme = myState.get(ATTR_ASSET_NAME); |
| // Theme chooser |
| if (myChooseThemeComboBox.isVisible() && !StringUtil.isEmpty(theme)) { |
| if (Theme.valueOf(theme).equals(Theme.CUSTOM)) { |
| show(myForegroundColor, myForegroundColorLabel); |
| } |
| else { |
| hide(myForegroundColor, myForegroundColorLabel); |
| } |
| } |
| } |
| |
| private void updateDirectoryCombo() { |
| final boolean showLabelAndCombo; |
| |
| List<File> folders = getResourceFolders(); |
| File res = myState.get(ATTR_OUTPUT_FOLDER); |
| if (!folders.isEmpty()) { |
| if (res == null || !folders.contains(res)) { |
| res = folders.get(0); |
| myState.put(ATTR_OUTPUT_FOLDER, res); |
| } |
| showLabelAndCombo = folders.size() > 1; |
| mySourceSetComboBox.removeAllItems(); |
| if (showLabelAndCombo) { |
| ComboBoxItemWithApiTag selected = null; |
| for (File directory : folders) { |
| ComboBoxItemWithApiTag item = new ComboBoxItemWithApiTag(directory, getResourceDirLabel(getModule(), directory), 0, 0); |
| if (Objects.equal(directory, res)) { |
| selected = item; |
| } |
| mySourceSetComboBox.addItem(item); |
| } |
| mySourceSetComboBox.setSelectedItem(selected); |
| } |
| } |
| else { |
| showLabelAndCombo = false; |
| } |
| mySourceSetComboBox.setVisible(showLabelAndCombo); |
| mySourceSetLabel.setVisible(showLabelAndCombo); |
| } |
| |
| private List<File> getResourceFolders() { |
| SourceProvider[] providers = mySourceProviders; |
| if (providers == null) { |
| SourceProvider provider = myState.get(mySourceProviderKey); |
| if (provider == null) { |
| return Collections.emptyList(); |
| } |
| providers = new SourceProvider[]{provider}; |
| } |
| List<File> dirs = Lists.newLinkedList(); |
| for (final SourceProvider provider : providers) { |
| dirs.addAll(provider.getResDirectories()); |
| } |
| return dirs; |
| } |
| |
| private <E> void register(Key<E> key, Map<JRadioButton, E> buttonsToValues) { |
| RadioButtonGroupBinding<E> binding = new RadioButtonGroupBinding<E>(buttonsToValues); |
| for (JRadioButton button : buttonsToValues.keySet()) { |
| register(key, button, binding); |
| } |
| } |
| |
| @Override |
| public boolean isStepVisible() { |
| TemplateEntry templateEntry = myState.get(myTemplateKey); |
| boolean isVisible = false; |
| if (templateEntry != null) { |
| TemplateMetadata templateMetadata = templateEntry.getMetadata(); |
| if (templateMetadata.getIconType() != null) { |
| isVisible = true; |
| } |
| } |
| return isVisible; |
| } |
| |
| @Override |
| public void deriveValues(Set<Key> modified) { |
| super.deriveValues(modified); |
| |
| AssetType iconType = null; |
| TemplateEntry templateEntry = myState.get(myTemplateKey); |
| if (templateEntry != null) { |
| iconType = templateEntry.getMetadata().getIconType(); |
| } |
| finalizeAssetType(iconType); |
| |
| // Note that this combo may need to reflect source set from another wizard page |
| updateDirectoryCombo(); |
| |
| myState.put(ATTR_ICON_RESOURCE, myState.get(ATTR_ASSET_NAME)); |
| |
| SourceType sourceType = myState.get(ATTR_SOURCE_TYPE); |
| if (sourceType != null) { |
| switch (sourceType) { |
| case IMAGE: |
| ((CardLayout)myAssetSourceCardPanel.getLayout()).show(myAssetSourceCardPanel, "ImageCard"); |
| hide(myForegroundColor, myForegroundColorLabel); |
| break; |
| case CLIPART: |
| ((CardLayout)myAssetSourceCardPanel.getLayout()).show(myAssetSourceCardPanel, "ClipartCard"); |
| show(myForegroundColor, myForegroundColorLabel); |
| break; |
| case TEXT: |
| ((CardLayout)myAssetSourceCardPanel.getLayout()).show(myAssetSourceCardPanel, "TextCard"); |
| show(myForegroundColor, myForegroundColorLabel); |
| myFontFamily.setSelectedItem(myState.get(ATTR_FONT)); |
| break; |
| } |
| } |
| |
| // Asset Type Combo Box |
| AssetType assetType = myState.get(ATTR_ASSET_TYPE); |
| if (assetType != null) { |
| switch (assetType) { |
| case LAUNCHER: |
| ((CardLayout)myTypeCardPanel.getLayout()).show(myTypeCardPanel, "LauncherCard"); |
| break; |
| case ACTIONBAR: |
| ((CardLayout)myTypeCardPanel.getLayout()).show(myTypeCardPanel, "ActionbarCard"); |
| break; |
| case NOTIFICATION: |
| ((CardLayout)myTypeCardPanel.getLayout()).show(myTypeCardPanel, "NotificationCard"); |
| break; |
| } |
| |
| String name = myState.get(ATTR_ASSET_NAME); |
| if (name == null || Objects.equal(myDefaultName, name)) { |
| myDefaultName = computeResourceName(assetType); |
| myState.put(ATTR_ASSET_NAME, myDefaultName); |
| } |
| } |
| |
| // Theme chooser |
| String assetTheme = myState.get(ATTR_ASSET_THEME); |
| if (myChooseThemeComboBox.isVisible() && !StringUtil.isEmpty(assetTheme)) { |
| if (Theme.valueOf(assetTheme).equals(Theme.CUSTOM)) { |
| show(myForegroundColor, myForegroundColorLabel); |
| } |
| else { |
| hide(myForegroundColor, myForegroundColorLabel); |
| } |
| } |
| requestPreviewUpdate(); |
| } |
| |
| @Override |
| public boolean validate() { |
| if (!super.validate()) { |
| return false; |
| } |
| String assetName = myState.get(ATTR_ASSET_NAME); |
| boolean canProceed = true; |
| String error = null; |
| if (StringUtil.isEmpty(assetName)) { |
| canProceed = false; |
| error = "Missing resource name"; |
| } |
| else if (drawableExists(assetName)) { |
| error = String.format("A drawable resource named %s already exists and will be overwritten.", assetName); |
| } |
| setErrorHtml(error); |
| |
| return canProceed; |
| } |
| |
| /** |
| * (Re)schedule the background task which updates the preview images. |
| */ |
| private void requestPreviewUpdate() { |
| myUpdateQueue.cancelAllUpdates(); |
| myUpdateQueue.queue(new Update("update") { |
| @Override |
| public void run() { |
| try { |
| if (myAssetGenerator == null) { // Init not done yet |
| return; |
| } |
| myAssetGenerator.generateImages(myImageMap, true, true); |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| updatePreviewImages(); |
| } |
| }); |
| } |
| catch (final ImageGeneratorException e) { |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| setErrorHtml(e.getMessage()); |
| } |
| }); |
| } |
| } |
| }); |
| } |
| |
| private void updatePreviewImages() { |
| AssetType assetType = myState.get(ATTR_ASSET_TYPE); |
| if (assetType == null || myImageMap.isEmpty()) { |
| return; |
| } |
| |
| if (assetType.equals(AssetType.NOTIFICATION)) { |
| final BufferedImage v9_mdpi = getImage(myImageMap, V9, Density.MEDIUM); |
| final BufferedImage v9_hdpi = getImage(myImageMap, V9, Density.HIGH); |
| final BufferedImage v9_xhdpi = getImage(myImageMap, V9, Density.XHIGH); |
| final BufferedImage v9_xxhdpi = getImage(myImageMap, V9, Density.XXHIGH); |
| setIconOrClear(myV9MdpiPreview, v9_mdpi); |
| setIconOrClear(myV9HdpiPreview, v9_hdpi); |
| setIconOrClear(myV9XHdpiPreview, v9_xhdpi); |
| setIconOrClear(myV9XXHdpiPreview, v9_xxhdpi); |
| |
| final BufferedImage v11_mdpi = getImage(myImageMap, V11, Density.MEDIUM); |
| final BufferedImage v11_hdpi = getImage(myImageMap, V11, Density.HIGH); |
| final BufferedImage v11_xhdpi = getImage(myImageMap, V11, Density.XHIGH); |
| final BufferedImage v11_xxhdpi = getImage(myImageMap, V11, Density.XXHIGH); |
| setIconOrClear(myV11MdpiPreview, v11_mdpi); |
| setIconOrClear(myV11HdpiPreview, v11_hdpi); |
| setIconOrClear(myV11XHdpiPreview, v11_xhdpi); |
| setIconOrClear(myV11XXHdpiPreview, v11_xxhdpi); |
| |
| } |
| else { |
| final BufferedImage mdpi = getImage(myImageMap, Density.MEDIUM.getResourceValue()); |
| final BufferedImage hdpi = getImage(myImageMap, Density.HIGH.getResourceValue()); |
| final BufferedImage xhdpi = getImage(myImageMap, Density.XHIGH.getResourceValue()); |
| final BufferedImage xxhdpi = getImage(myImageMap, Density.XXHIGH.getResourceValue()); |
| |
| setIconOrClear(myMdpiPreview, mdpi); |
| setIconOrClear(myHdpiPreview, hdpi); |
| setIconOrClear(myXHdpiPreview, xhdpi); |
| setIconOrClear(myXXHdpiPreview, xxhdpi); |
| } |
| ((CardLayout)myPageBook.getLayout()).show(myPageBook, assetType == AssetType.NOTIFICATION ? "versions" : "DPI"); |
| } |
| |
| /** |
| * Displays a modal dialog with one button for each entry in the {@link com.android.assetstudiolib.GraphicGenerator} |
| * clipart library. Clicking on a button sets that entry into the {@link #ATTR_CLIPART_NAME} key. |
| */ |
| private void displayClipartDialog() { |
| Window window = SwingUtilities.getWindowAncestor(myPanel); |
| final JDialog dialog = new JDialog(window, Dialog.ModalityType.DOCUMENT_MODAL); |
| FlowLayout layout = new FlowLayout(); |
| dialog.getRootPane().setLayout(layout); |
| int count = 0; |
| for (Iterator<String> iter = GraphicGenerator.getResourcesNames(RasterAssetSetStep.IMAGES_CLIPART_BIG, |
| SdkConstants.DOT_PNG); |
| iter.hasNext(); ) { |
| final String name = iter.next(); |
| try { |
| JButton btn = new JButton(); |
| |
| btn.setIcon(new ImageIcon(GraphicGenerator.getClipartIcon(name))); |
| Dimension d = new Dimension(CLIPART_ICON_SIZE, CLIPART_ICON_SIZE); |
| btn.setMaximumSize(d); |
| btn.setPreferredSize(d); |
| btn.addActionListener(new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| myState.put(ATTR_CLIPART_NAME, name); |
| dialog.setVisible(false); |
| } |
| }); |
| dialog.getRootPane().add(btn); |
| count++; |
| } |
| catch (IOException e) { |
| LOG.error(e); |
| } |
| } |
| int size = (int)(Math.sqrt(count) + 1) * (CLIPART_ICON_SIZE + layout.getHgap()) + CLIPART_DIALOG_BORDER * 2; |
| dialog.setSize(size, size + DIALOG_HEADER); |
| dialog.setLocationRelativeTo(window); |
| dialog.setVisible(true); |
| } |
| |
| public void finalizeAssetType(@Nullable AssetType type) { |
| myState.put(ATTR_ASSET_TYPE, type); |
| myTypePanel.setVisible(type == null); |
| myAssetTypeLabel.setVisible(type == null); |
| } |
| |
| @NotNull |
| private String computeResourceName(AssetType assetType) { |
| String resourceName = null; |
| TemplateEntry templateEntry = myState.get(myTemplateKey); |
| String nameExpression; |
| if (templateEntry != null) { |
| nameExpression = templateEntry.getMetadata().getIconName(); |
| |
| if (!StringUtil.isEmpty(nameExpression)) { |
| Set<Key> allKeys = myState.getAllKeys(); |
| Map<String, Object> parameters = Maps.newHashMapWithExpectedSize(allKeys.size()); |
| for (Key key : allKeys) { |
| parameters.put(key.name, myState.get(key)); |
| } |
| resourceName = myStringEvaluator.evaluate(nameExpression, parameters); |
| } |
| } |
| |
| if (resourceName == null) { |
| resourceName = String.format(assetType.getDefaultNameFormat(), "name"); |
| } |
| |
| // It's unusual to have > 1 launcher icon, don't fix the name for launcher icons. |
| if (drawableExists(resourceName) && assetType != AssetType.LAUNCHER) { |
| // While uniqueness isn't satisfied, increment number and add to end |
| int i = 2; |
| while (drawableExists(resourceName + Integer.toString(i))) { |
| i++; |
| } |
| resourceName += Integer.toString(i); |
| } |
| |
| return resourceName; |
| } |
| |
| /** |
| * Must be run inside a write action. Creates the asset files on disk. |
| */ |
| public void createAssets() { |
| if (isStepVisible()) { |
| File destination = myState.get(ATTR_OUTPUT_FOLDER); |
| assert destination != null; |
| // Asset generator will append "res" by itself |
| myAssetGenerator.outputImagesIntoVariantRoot(destination.getParentFile()); |
| } |
| } |
| |
| private boolean drawableExists(String resourceName) { |
| File resDir = myState.get(ATTR_OUTPUT_FOLDER); |
| if (resDir != null) { |
| return Parameter.existsResourceFile(resDir, ResourceFolderType.DRAWABLE, resourceName); |
| } |
| else { |
| return Parameter.existsResourceFile(getModule(), ResourceType.DRAWABLE, resourceName); |
| } |
| } |
| |
| @Override |
| public JComponent getPreferredFocusedComponent() { |
| return myImageRadioButton; |
| } |
| |
| @NotNull |
| @Override |
| public String getStepName() { |
| return "Asset Studio"; |
| } |
| |
| @NotNull |
| @Override |
| protected String getStepTitle() { |
| return "Asset Studio"; |
| } |
| |
| @Nullable |
| @Override |
| protected String getStepDescription() { |
| return null; |
| } |
| |
| @Override |
| public void dispose() { |
| myUpdateQueue.cancelAllUpdates(); |
| } |
| |
| } |