/* * 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.wizard; 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.Parameter; import com.android.tools.idea.templates.Template; import com.android.tools.idea.templates.TemplateManager; import com.android.tools.idea.templates.TemplateMetadata; 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.wizard.AssetStudioAssetGenerator.*; import static com.android.tools.idea.wizard.ScopedStateStore.Key; import static com.android.tools.idea.wizard.ScopedStateStore.Scope.PATH; import static com.android.tools.idea.wizard.ScopedStateStore.Scope.STEP; import static com.android.tools.idea.wizard.ScopedStateStore.createKey; /** * {@linkplain IconStep} is a wizard page that lets the user create a variety of density-scaled assets. */ public class IconStep extends DynamicWizardStepWithHeaderAndDescription 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_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<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); 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 myImageFileLabel; private JLabel myTextLabel; private JLabel myFontFamilyLabel; private JLabel myChooseClipartLabel; private JLabel myBackgroundColorLabel; private JLabel myForegroundColorLabel; private JLabel myAssetTypeLabel; private JComboBox myChooseThemeComboBox; private JLabel myChooseThemeLabel; private JLabel myShapeLabel; private JTextField myResourceNameField; private JLabel myResourceNameLabel; 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 JComponent[] myScalingButtons; private JComponent[] myShapeButtons; 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("Asset Studio", null, null, disposable); myTemplateKey = templateKey; mySourceProviderKey = sourceProviderKey; mySourceProviders = sourceProviders; myScalingButtons = new JComponent[]{myCropRadioButton, myCenterRadioButton}; myShapeButtons = new JComponent[]{myNoneRadioButton, mySquareRadioButton, myCircleRadioButton}; 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 ComboBoxItem(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 ComboBoxItem(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", "drawable-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) { ComboBoxItem selected = null; for (File directory : folders) { ComboBoxItem item = new ComboBoxItem(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: hide(myChooseClipart, myChooseClipartLabel, myText, myTextLabel, myFontFamily, myFontFamilyLabel, myForegroundColor, myForegroundColorLabel); show(myImageFile, myImageFileLabel, myBackgroundColor, myBackgroundColorLabel); break; case CLIPART: hide(myText, myTextLabel, myFontFamily, myFontFamilyLabel, myImageFile, myImageFileLabel); show(myChooseClipart, myChooseClipartLabel, myBackgroundColor, myBackgroundColorLabel, myForegroundColor, myForegroundColorLabel); break; case TEXT: hide(myChooseClipart, myChooseClipartLabel, myImageFile, myImageFileLabel); show(myText, myTextLabel, myFontFamily, myFontFamilyLabel, myBackgroundColor, myBackgroundColorLabel, myForegroundColor, myForegroundColorLabel); myFontFamily.setSelectedItem(myState.get(ATTR_FONT)); break; } } // Asset Type Combo Box AssetType assetType = myState.get(ATTR_ASSET_TYPE); if (assetType != null) { //mySelectedAssetType = selectedAssetType; switch (assetType) { case LAUNCHER: hide(myChooseThemeComboBox, myChooseThemeLabel); show(myShapeLabel, myResourceNameLabel, myResourceNameField); show(myScalingButtons); show(myShapeButtons); break; case ACTIONBAR: show(myResourceNameField, myResourceNameLabel); show(myChooseThemeComboBox, myChooseThemeLabel); hide(myShapeLabel, myBackgroundColorLabel, myBackgroundColor); hide(myScalingButtons); hide(myShapeButtons); break; case NOTIFICATION: show(myResourceNameField, myResourceNameLabel); hide(myChooseThemeComboBox, myChooseThemeLabel, myForegroundColor, myForegroundColorLabel); hide(myShapeLabel, myBackgroundColorLabel, myBackgroundColor); hide(myScalingButtons); hide(myShapeButtons); 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.getClipartNames(); 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); if (type == null) { show(myLauncherRadioButton, myActionBarAndTabsRadioButton, myNotificationRadioButton, myAssetTypeLabel); } else { hide(myLauncherRadioButton, myActionBarAndTabsRadioButton, myNotificationRadioButton, myAssetTypeLabel); } } @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"; } @Override public void dispose() { myUpdateQueue.cancelAllUpdates(); } }