diff options
97 files changed, 2132 insertions, 476 deletions
diff --git a/assetstudio/.classpath b/assetstudio/.classpath index ee79d96..53a77a1 100644 --- a/assetstudio/.classpath +++ b/assetstudio/.classpath @@ -1,7 +1,9 @@ <?xml version="1.0" encoding="UTF-8"?> <classpath> <classpathentry kind="src" path="src"/> + <classpathentry kind="src" path="tests/src"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> - <classpathentry kind="output" path="bin"/> <classpathentry combineaccessrules="false" kind="src" path="/common"/> + <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> + <classpathentry kind="output" path="bin"/> </classpath> diff --git a/assetstudio/src/com/android/assetstudiolib/ActionBarIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/ActionBarIconGenerator.java new file mode 100644 index 0000000..b54aad5 --- /dev/null +++ b/assetstudio/src/com/android/assetstudiolib/ActionBarIconGenerator.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.assetstudiolib; + +import com.android.assetstudiolib.Util.Effect; +import com.android.assetstudiolib.Util.FillEffect; +import com.android.assetstudiolib.Util.ShadowEffect; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; + +/** + * Generate icons for the action bar + */ +public class ActionBarIconGenerator extends GraphicGenerator { + + /** Creates a new {@link ActionBarIconGenerator} */ + public ActionBarIconGenerator() { + } + + @Override + public BufferedImage generate(GraphicGeneratorContext context, Options options) { + Rectangle iconSizeHdpi = new Rectangle(0, 0, 48, 48); + Rectangle targetRectHdpi = new Rectangle(6, 6, 36, 36); + final float scaleFactor = GraphicGenerator.getHdpiScaleFactor(options.density); + Rectangle imageRect = Util.scaleRectangle(iconSizeHdpi, scaleFactor); + Rectangle targetRect = Util.scaleRectangle(targetRectHdpi, scaleFactor); + BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); + Graphics2D g = (Graphics2D) outImage.getGraphics(); + + BufferedImage tempImage = Util.newArgbBufferedImage( + imageRect.width, imageRect.height); + Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); + Util.drawCenterInside(g2, options.sourceImage, targetRect); + + ActionBarOptions actionBarOptions = (ActionBarOptions) options; + if (actionBarOptions.theme == Theme.HOLO_LIGHT) { + Util.drawEffects(g, tempImage, 0, 0, new Effect[] { + new FillEffect(new Color(0x898989)), + }); + } else { + assert actionBarOptions.theme == Theme.HOLO_DARK; + Util.drawEffects(g, tempImage, 0, 0, new Effect[] { + // TODO: should be white @ 60% opacity, but + // the fill then blends with the drop shadow + new FillEffect(new Color(0x909090)), + new ShadowEffect( + 0, + 0, + 3 * scaleFactor, + Color.BLACK, + 0.85, + false), + }); + } + + g.dispose(); + g2.dispose(); + + return outImage; + } + + /** Options specific to generating action bar icons */ + public static class ActionBarOptions extends GraphicGenerator.Options { + /** The theme to generate icons for */ + public Theme theme = Theme.HOLO_LIGHT; + } + + /** The themes to generate action bar icons for */ + public enum Theme { + /** Theme.Holo - a dark (and default) version of the Honeycomb theme */ + HOLO_DARK, + + /** Theme.HoloLight - a light version of the Honeycomb theme */ + HOLO_LIGHT; + } +} diff --git a/assetstudio/src/com/android/assetstudiolib/GraphicGenerator.java b/assetstudio/src/com/android/assetstudiolib/GraphicGenerator.java index 902b21d..dea6eeb 100644 --- a/assetstudio/src/com/android/assetstudiolib/GraphicGenerator.java +++ b/assetstudio/src/com/android/assetstudiolib/GraphicGenerator.java @@ -26,9 +26,13 @@ import java.net.URISyntaxException; import java.net.URL; import java.security.ProtectionDomain; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.Enumeration; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.jar.JarFile; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -38,15 +42,165 @@ import javax.imageio.ImageIO; /** * The base Generator class. */ -public class GraphicGenerator { +public abstract class GraphicGenerator { /** * Options used for all generators. */ public static class Options { + /** Source image to use as a basis for the icon */ + public BufferedImage sourceImage; + /** The density to generate the icon with */ + public Density density = Density.XHIGH; } - public static float getScaleFactor(Density density) { - return density.getDpiValue() / (float) Density.DEFAULT_DENSITY; + /** Shapes that can be used for icon backgrounds */ + public static enum Shape { + /** Circular background */ + CIRCLE("circle"), + /** Square background */ + SQUARE("square"); + + /** Id, used in filenames to identify associated stencils */ + public final String id; + + Shape(String id) { + this.id = id; + } + } + + /** Foreground effects styles */ + public static enum Style { + /** No effects */ + SIMPLE("fore1"), + /** "Fancy" effects */ + FANCY("fore2"), + /** A glossy look */ + GLOSSY("fore3"); + + /** Id, used in filenames to identify associated stencils */ + public final String id; + + Style(String id) { + this.id = id; + } + } + + /** + * Generate a single icon using the given options + * + * @param context render context to use for looking up resources etc + * @param options options controlling the appearance of the icon + * @return a {@link BufferedImage} with the generated icon + */ + public abstract BufferedImage generate(GraphicGeneratorContext context, Options options); + + /** + * Computes the target filename (relative to the Android project folder) + * where an icon rendered with the given options should be stored. This is + * also used as the map keys in the result map used by + * {@link #generate(String, Map, GraphicGeneratorContext, Options, String)}. + * + * @param options the options object used by the generator for the current + * image + * @param name the base name to use when creating the path + * @return a path relative to the res/ folder where the image should be + * stored (will always use / as a path separator, not \ on Windows) + */ + protected String getIconPath(Options options, String name) { + return getIconFolder(options) + '/' + getIconName(options, name); + } + + /** + * Gets name of the file itself. It is sometimes modified by options, for + * example in unselected tabs we change foo.png to foo-unselected.png + */ + protected String getIconName(Options options, String name) { + return name + ".png"; //$NON-NLS-1$ + } + + /** + * Gets name of the folder to contain the resource. It usually includes the + * density, but is also sometimes modified by options. For example, in some + * notification icons we add in -v9 or -v11. + */ + protected String getIconFolder(Options options) { + return "res/drawable-" + options.density.getResourceValue(); //$NON-NLS-1$ + } + + /** + * Generates a full set of icons into the given map. The values in the map + * will be the generated images, and each value is keyed by the + * corresponding relative path of the image, which is determined by the + * {@link #getIconPath(Options, String)} method. + * + * @param category the current category to place images into (if null the + * density name will be used) + * @param categoryMap the map to put images into, should not be null. The + * map is a map from a category name, to a map from file path to + * image. + * @param context a generator context which for example can load resources + * @param options options to apply to this generator + * @param name the base name of the icons to generate + */ + public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap, + GraphicGeneratorContext context, Options options, String name) { + Density[] densityValues = Density.values(); + // Sort density values into ascending order + Arrays.sort(densityValues, new Comparator<Density>() { + public int compare(Density d1, Density d2) { + return d1.getDpiValue() - d2.getDpiValue(); + } + }); + + for (Density density : densityValues) { + if (!density.isValidValueForDevice()) { + continue; + } + if (density == Density.TV) { + // Not yet supported -- missing stencil image + continue; + } + options.density = density; + BufferedImage image = generate(context, options); + if (image != null) { + String mapCategory = category; + if (mapCategory == null) { + mapCategory = options.density.getResourceValue(); + } + Map<String, BufferedImage> imageMap = categoryMap.get(mapCategory); + if (imageMap == null) { + imageMap = new LinkedHashMap<String, BufferedImage>(); + categoryMap.put(mapCategory, imageMap); + } + imageMap.put(getIconPath(options, name), image); + } + } + } + + /** + * Returns the scale factor to apply for a given HDPI density to compute the + * absolute pixel count to use to draw an icon of the given target density + * + * @param density the density + * @return a factor to multiple hdpi distances with to compute the target density + */ + public static float getHdpiScaleFactor(Density density) { + // We used to do this: + //return density.getDpiValue() / (float) Density.DEFAULT_DENSITY; + // However, the HTML5 version of the AssetStudio would end up with different + // sizes for the assets, because it uses this table: + // studio.util.getMultBaseHdpi = function(density) { + // switch (density) { + // case 'xhdpi': return 1.333333; + // case 'hdpi': return 1.0; + // case 'mdpi': return 0.666667; + // case 'ldpi': return 0.5; + // } + // return 1.0; + // }; + // This corresponds to dividing the dpi value not by Density.MEDIUM but + // Density.HIGH: + return density.getDpiValue() / (float) Density.HIGH.getDpiValue(); } /** diff --git a/assetstudio/src/com/android/assetstudiolib/LauncherIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/LauncherIconGenerator.java index b6715a4..66cb6ae 100644 --- a/assetstudio/src/com/android/assetstudiolib/LauncherIconGenerator.java +++ b/assetstudio/src/com/android/assetstudiolib/LauncherIconGenerator.java @@ -16,93 +16,136 @@ package com.android.assetstudiolib; -import com.android.resources.Density; - import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; /** * A {@link GraphicGenerator} that generates Android "launcher" icons. */ public class LauncherIconGenerator extends GraphicGenerator { - private static final Rectangle BASE_IMAGE_RECT = new Rectangle(0, 0, 48, 48); - private static final Rectangle BASE_TARGET_RECT = new Rectangle(4, 4, 40, 40); - - private Options mOptions; - private BufferedImage mBackImage; - private BufferedImage mMaskImage; - private BufferedImage mForeImage; - - public LauncherIconGenerator(GraphicGeneratorContext context, Options options) { - mOptions = options; - mBackImage = context.loadImageResource("/images/launcher_stencil/" - + options.shape.id + "/" + options.density.getResourceValue() + "/back.png"); - mForeImage = context.loadImageResource("/images/launcher_stencil/" - + options.shape.id + "/" + options.density.getResourceValue() + "/" - + options.style.id + ".png"); - mMaskImage = context.loadImageResource("/images/launcher_stencil/" - + options.shape.id + "/" + options.density.getResourceValue() + "/mask.png"); - } - - public BufferedImage generate() { - final float scaleFactor = GraphicGenerator.getScaleFactor(mOptions.density); - Rectangle imageRect = Util.scaleRectangle(BASE_IMAGE_RECT, scaleFactor); - Rectangle targetRect = Util.scaleRectangle(BASE_TARGET_RECT, scaleFactor); + private static final Rectangle IMAGE_SIZE_HDPI = new Rectangle(0, 0, 72, 72); + /* TODO: Adapt from html version: + 'square-web-targetRect': { x: 57, y: 57, w: 398, h: 398 }, + 'circle-web-targetRect': { x: 42, y: 42, w: 428, h: 428 }, + 'square-xhdpi-targetRect': { x: 11, y: 11, w: 74, h: 74 }, + 'circle-xhdpi-targetRect': { x: 8, y: 8, w: 80, h: 80 }, + 'square-hdpi-targetRect': { x: 8, y: 8, w: 56, h: 56 }, + 'circle-hdpi-targetRect': { x: 6, y: 6, w: 60, h: 60 }, <==== + 'square-mdpi-targetRect': { x: 5, y: 5, w: 38, h: 38 }, + 'circle-mdpi-targetRect': { x: 4, y: 4, w: 40, h: 40 }, + 'square-ldpi-targetRect': { x: 4, y: 4, w: 28, h: 28 }, + 'circle-ldpi-targetRect': { x: 3, y: 3, w: 30, h: 30 } + */ + private static final Rectangle TARGET_RECT_HDPI = new Rectangle(6, 6, 60, 60); + + @Override + public BufferedImage generate(GraphicGeneratorContext context, Options options) { + LauncherOptions launcherOptions = (LauncherOptions) options; + + String density; + if (launcherOptions.isWebGraphic) { + density = "web"; + } else { + density = launcherOptions.density.getResourceValue(); + } + String shape = launcherOptions.shape.id; + BufferedImage mBackImage = context.loadImageResource("/images/launcher_stencil/" + + shape + "/" + density + "/back.png"); + BufferedImage mForeImage = context.loadImageResource("/images/launcher_stencil/" + + shape + "/" + density + "/" + launcherOptions.style.id + ".png"); + BufferedImage mMaskImage = context.loadImageResource("/images/launcher_stencil/" + + shape + "/" + density + "/mask.png"); + + float scaleFactor = GraphicGenerator.getHdpiScaleFactor(launcherOptions.density); + if (launcherOptions.isWebGraphic) { + // Target size for the web graphic is 512 + scaleFactor = 512 / (float) IMAGE_SIZE_HDPI.height; + } + Rectangle imageRect = Util.scaleRectangle(IMAGE_SIZE_HDPI, scaleFactor); + Rectangle targetRect = Util.scaleRectangle(TARGET_RECT_HDPI, scaleFactor); BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); Graphics2D g = (Graphics2D) outImage.getGraphics(); g.drawImage(mBackImage, 0, 0, null); - { - BufferedImage tempImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); - Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); - g2.drawImage(mMaskImage, 0, 0, null); - g2.setComposite(AlphaComposite.SrcAtop); - g2.setPaint(new Color(mOptions.backgroundColor)); - g2.fillRect(0, 0, imageRect.width, imageRect.height); - - if (mOptions.crop) { - Util.drawCenterCrop(g2, mOptions.sourceImage, targetRect); - } else { - Util.drawCenterInside(g2, mOptions.sourceImage, targetRect); - } - - g.drawImage(tempImage, 0, 0, null); + BufferedImage tempImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); + Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); + g2.drawImage(mMaskImage, 0, 0, null); + g2.setComposite(AlphaComposite.SrcAtop); + g2.setPaint(new Color(launcherOptions.backgroundColor)); + g2.fillRect(0, 0, imageRect.width, imageRect.height); + + if (launcherOptions.crop) { + Util.drawCenterCrop(g2, launcherOptions.sourceImage, targetRect); + } else { + Util.drawCenterInside(g2, launcherOptions.sourceImage, targetRect); } + g.drawImage(tempImage, 0, 0, null); g.drawImage(mForeImage, 0, 0, null); + + g.dispose(); + g2.dispose(); + return outImage; } - public static class Options { - public BufferedImage sourceImage; - public int backgroundColor = 0; - public boolean crop = true; - public Shape shape = Shape.SQUARE; - public Style style = Style.SIMPLE; - public Density density = Density.XHIGH; + @Override + public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap, + GraphicGeneratorContext context, Options options, String name) { + LauncherOptions launcherOptions = (LauncherOptions) options; + boolean generateWebImage = launcherOptions.isWebGraphic; + launcherOptions.isWebGraphic = false; + super.generate(category, categoryMap, context, options, name); + + if (generateWebImage) { + launcherOptions.isWebGraphic = true; + BufferedImage image = generate(context, options); + if (image != null) { + Map<String, BufferedImage> imageMap = new HashMap<String, BufferedImage>(); + categoryMap.put("Web", imageMap); + imageMap.put(getIconPath(options, name), image); + } + } + } - public static enum Shape { - CIRCLE("circle"), SQUARE("square"); + @Override + protected String getIconPath(Options options, String name) { + if (((LauncherOptions) options).isWebGraphic) { + return name + "-web.png"; // Store at the root of the project + } - public String id; + return super.getIconPath(options, name); + } - Shape(String id) { - this.id = id; - } - } + /** Options specific to generating launcher icons */ + public static class LauncherOptions extends GraphicGenerator.Options { + /** Background color, as an RRGGBB packed integer */ + public int backgroundColor = 0; - public static enum Style { - SIMPLE("fore1"), FANCY("fore2"), GLOSSY("fore3"); + /** Whether the image should be cropped or not */ + public boolean crop = true; - public String id; + /** The shape to use for the background */ + public Shape shape = Shape.SQUARE; - Style(String id) { - this.id = id; - } - } + /** The effects to apply to the foreground */ + public Style style = Style.SIMPLE; + + /** + * Whether a web graphic should be generated (will ignore normal density + * setting). The {@link #generate(GraphicGeneratorContext, Options)} + * method will use this to decide whether to generate a normal density + * icon or a high res web image. The + * {@link GraphicGenerator#generate(String, Map, GraphicGeneratorContext, Options, String)} + * method will use this flag to determine whether it should include a + * web graphic in its iteration. + */ + public boolean isWebGraphic; } } diff --git a/assetstudio/src/com/android/assetstudiolib/MenuIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/MenuIconGenerator.java index 07b7a6b..8fdbddf 100644 --- a/assetstudio/src/com/android/assetstudiolib/MenuIconGenerator.java +++ b/assetstudio/src/com/android/assetstudiolib/MenuIconGenerator.java @@ -16,7 +16,9 @@ package com.android.assetstudiolib; -import com.android.resources.Density; +import com.android.assetstudiolib.Util.Effect; +import com.android.assetstudiolib.Util.FillEffect; +import com.android.assetstudiolib.Util.ShadowEffect; import java.awt.Color; import java.awt.GradientPaint; @@ -28,65 +30,59 @@ import java.awt.image.BufferedImage; * A {@link GraphicGenerator} that generates Android "menu" icons. */ public class MenuIconGenerator extends GraphicGenerator { - private static final Rectangle BASE_IMAGE_RECT = new Rectangle(0, 0, 48, 48); - private static final Rectangle BASE_TARGET_RECT = new Rectangle(8, 8, 32, 32); - - private Options mOptions; - - public MenuIconGenerator(GraphicGeneratorContext context, Options options) { - mOptions = options; + /** Creates a menu icon generator */ + public MenuIconGenerator() { } - public BufferedImage generate() { - final float scaleFactor = GraphicGenerator.getScaleFactor(mOptions.density); - Rectangle imageRect = Util.scaleRectangle(BASE_IMAGE_RECT, scaleFactor); - Rectangle targetRect = Util.scaleRectangle(BASE_TARGET_RECT, scaleFactor); + @Override + public BufferedImage generate(GraphicGeneratorContext context, Options options) { + Rectangle imageSizeHdpi = new Rectangle(0, 0, 72, 72); + Rectangle targetRectHdpi = new Rectangle(12, 12, 48, 48); + float scaleFactor = GraphicGenerator.getHdpiScaleFactor(options.density); + Rectangle imageRect = Util.scaleRectangle(imageSizeHdpi, scaleFactor); + Rectangle targetRect = Util.scaleRectangle(targetRectHdpi, scaleFactor); BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); Graphics2D g = (Graphics2D) outImage.getGraphics(); - { - BufferedImage tempImage = Util.newArgbBufferedImage( - imageRect.width, imageRect.height); - Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); - Util.drawCenterInside(g2, mOptions.sourceImage, targetRect); + BufferedImage tempImage = Util.newArgbBufferedImage( + imageRect.width, imageRect.height); + Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); + Util.drawCenterInside(g2, options.sourceImage, targetRect); - Util.drawEffects(g, tempImage, 0, 0, new Util.Effect[]{ - new Util.FillEffect( - new GradientPaint( - 0, 0, - new Color(0xa3a3a3), - 0, imageRect.height, - new Color(0x787878))), - new Util.ShadowEffect( - 0, - 3 * scaleFactor, - 3 * scaleFactor, - Color.black, - 0.2, - true), - new Util.ShadowEffect( - 0, - 1, - 0, - Color.black, - 0.35, - true), - new Util.ShadowEffect( - 0, - -1, - 0, - Color.white, - 0.35, - true), - }); - } + Util.drawEffects(g, tempImage, 0, 0, new Effect[] { + new FillEffect( + new GradientPaint( + 0, 0, + new Color(0xa3a3a3), + 0, imageRect.height, + new Color(0x787878))), + new ShadowEffect( + 0, + 3 * scaleFactor, + 3 * scaleFactor, + Color.BLACK, + 0.2, + true), + new ShadowEffect( + 0, + 1, + 0, + Color.BLACK, + 0.35, + true), + new ShadowEffect( + 0, + -1, + 0, + Color.WHITE, + 0.35, + true), + }); - return outImage; - } + g.dispose(); + g2.dispose(); - public static class Options { - public BufferedImage sourceImage; - public Density density = Density.XHIGH; + return outImage; } } diff --git a/assetstudio/src/com/android/assetstudiolib/NotificationIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/NotificationIconGenerator.java new file mode 100644 index 0000000..1237a89 --- /dev/null +++ b/assetstudio/src/com/android/assetstudiolib/NotificationIconGenerator.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.assetstudiolib; + +import com.android.assetstudiolib.Util.Effect; +import com.android.assetstudiolib.Util.FillEffect; +import com.android.assetstudiolib.Util.ShadowEffect; + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.util.Map; + +/** + * Generate icons for the notifications bar + */ +public class NotificationIconGenerator extends GraphicGenerator { + /** Creates a new {@link NotificationIconGenerator} */ + public NotificationIconGenerator() { + } + + @Override + public BufferedImage generate(GraphicGeneratorContext context, Options options) { + Rectangle iconSizeHdpi; + Rectangle targetRectHdpi; + NotificationOptions notificationOptions = (NotificationOptions) options; + if (notificationOptions.version == Version.OLDER) { + iconSizeHdpi = new Rectangle(0, 0, 38, 38); + targetRectHdpi = new Rectangle(6, 6, 26, 26); + } else if (notificationOptions.version == Version.V11) { + iconSizeHdpi = new Rectangle(0, 0, 48, 48); + targetRectHdpi = new Rectangle(6, 6, 36, 36); + } else { + assert notificationOptions.version == Version.V9; + iconSizeHdpi = new Rectangle(0, 0, 24, 38); + targetRectHdpi = new Rectangle(0, 7, 24, 24); + } + + final float scaleFactor = GraphicGenerator.getHdpiScaleFactor(options.density); + Rectangle imageRect = Util.scaleRectangle(iconSizeHdpi, scaleFactor); + Rectangle targetRect = Util.scaleRectangle(targetRectHdpi, scaleFactor); + + BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); + Graphics2D g = (Graphics2D) outImage.getGraphics(); + + BufferedImage tempImage = Util.newArgbBufferedImage( + imageRect.width, imageRect.height); + Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); + + if (notificationOptions.version == Version.OLDER) { + BufferedImage mBackImage = context.loadImageResource( + "/images/notification_stencil/" + + notificationOptions.shape.id + '/' + + notificationOptions.density.getResourceValue() + + ".png"); + g.drawImage(mBackImage, 0, 0, null); + BufferedImage top = options.sourceImage; + BufferedImage filled = Util.filledImage(top, Color.WHITE); + Util.drawCenterInside(g, filled, targetRect); + } else if (notificationOptions.version == Version.V11) { + Util.drawCenterInside(g2, options.sourceImage, targetRect); + Util.drawEffects(g, tempImage, 0, 0, new Effect[] { + new FillEffect( + Color.WHITE), + }); + } else { + assert notificationOptions.version == Version.V9; + Util.drawCenterInside(g2, options.sourceImage, targetRect); + Util.drawEffects(g, tempImage, 0, 0, new Effect[] { + new FillEffect( + new GradientPaint( + 0, 0, + new Color(0x919191), + 0, imageRect.height, + new Color(0x828282))), + new ShadowEffect( + 0, + 1, + 0, + Color.WHITE, + 0.10, + true), + }); + } + + g.dispose(); + g2.dispose(); + + return outImage; + } + + @Override + public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap, + GraphicGeneratorContext context, Options options, String name) { + for (Version version : Version.values()) { + ((NotificationOptions) options).version = version; + super.generate(version.getDisplayName(), categoryMap, context, options, name); + } + } + + @Override + protected String getIconFolder(Options options) { + String folder = super.getIconFolder(options); + Version version = ((NotificationOptions) options).version; + if (version == Version.V11) { + return folder + "-v11"; //$NON-NLS-1$ + } else if (version == Version.V9) { + return folder + "-v9"; //$NON-NLS-1$ + } else { + return folder; + } + } + + /** + * Options specific to generating notification icons + */ + public static class NotificationOptions extends GraphicGenerator.Options { + /** + * The shape to use for graphics behind the icon (for {@link Version#OLDER} only) + */ + public Shape shape = Shape.SQUARE; + + /** + * The version of the icon to generate - different styles are used for different + * versions of Android + */ + public Version version = Version.V9; + } + + /** + * The version of the icon to generate - different styles are used for different + * versions of Android + */ + public enum Version { + /** Icon style used for -v9 and -v10 */ + V9("V9"), + + /** Icon style used for -v11 (Honeycomb) and later */ + V11("V11"), + + /** Icon style used for versions older than v9 */ + OLDER("Other"); + + private final String mDisplayName; + + Version(String displayName) { + mDisplayName = displayName; + } + + /** + * Returns the display name for this version, typically shown as a + * category + * + * @return the display name, never null + */ + public String getDisplayName() { + return mDisplayName; + } + } +} diff --git a/assetstudio/src/com/android/assetstudiolib/TabIconGenerator.java b/assetstudio/src/com/android/assetstudiolib/TabIconGenerator.java new file mode 100644 index 0000000..506aebd --- /dev/null +++ b/assetstudio/src/com/android/assetstudiolib/TabIconGenerator.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.assetstudiolib; + +import com.android.assetstudiolib.Util.Effect; +import com.android.assetstudiolib.Util.FillEffect; +import com.android.assetstudiolib.Util.ShadowEffect; + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.util.Map; + + +/** + * Generate icons for tabs + */ +public class TabIconGenerator extends GraphicGenerator { + /** Creates a new {@link TabIconGenerator} */ + public TabIconGenerator() { + } + + @Override + public BufferedImage generate(GraphicGeneratorContext context, Options options) { + Rectangle iconSizeHdpi = new Rectangle(0, 0, 48, 48); + Rectangle targetRectHdpi = new Rectangle(3, 3, 42, 42); + final float scaleFactor = GraphicGenerator.getHdpiScaleFactor(options.density); + Rectangle imageRect = Util.scaleRectangle(iconSizeHdpi, scaleFactor); + Rectangle targetRect = Util.scaleRectangle(targetRectHdpi, scaleFactor); + BufferedImage outImage = Util.newArgbBufferedImage(imageRect.width, imageRect.height); + Graphics2D g = (Graphics2D) outImage.getGraphics(); + + BufferedImage tempImage = Util.newArgbBufferedImage( + imageRect.width, imageRect.height); + Graphics2D g2 = (Graphics2D) tempImage.getGraphics(); + Util.drawCenterInside(g2, options.sourceImage, targetRect); + + TabOptions tabOptions = (TabOptions) options; + if (tabOptions.selected) { + if (tabOptions.oldStyle) { + Util.drawEffects(g, tempImage, 0, 0, new Effect[] { + new FillEffect( + new GradientPaint( + 0, 0, + new Color(0xa3a3a3), + 0, imageRect.height, + new Color(0x787878))), + new ShadowEffect( + 0, + 3 * scaleFactor, + 3 * scaleFactor, + Color.BLACK, + 0.2, + true), + new ShadowEffect( + 0, + 1, + 0, + Color.BLACK, + 0.35, + true), + new ShadowEffect( + 0, + -1, + 0, + Color.WHITE, + 0.35, + true), + }); + } else { + Util.drawEffects(g, tempImage, 0, 0, new Effect[] { + new FillEffect(Color.WHITE), + new ShadowEffect( + 0, + 0, + 5 * scaleFactor, + Color.BLACK, + 0.25, + false), + }); + } + } else { + // Unselected + if (tabOptions.oldStyle) { + Util.drawEffects(g, tempImage, 0, 0, new Effect[] { + new FillEffect( + new GradientPaint( + 0, 0.25f * imageRect.height, + new Color(0xf9f9f9), + 0, imageRect.height, + new Color(0xdfdfdf))), + new ShadowEffect( + 0, + 3 * scaleFactor, + 3 * scaleFactor, + Color.BLACK, + 0.1, + true), + new ShadowEffect( + 0, + 1, + 0, + Color.BLACK, + 0.35, + true), + new ShadowEffect( + 0, + -1, + 0, + Color.WHITE, + 0.35, + true), + }); + } else { + Util.drawEffects(g, tempImage, 0, 0, new Effect[] { + new FillEffect(new Color(0x808080)), + }); + } + } + + g.dispose(); + g2.dispose(); + + return outImage; + } + + @Override + public void generate(String category, Map<String, Map<String, BufferedImage>> categoryMap, + GraphicGeneratorContext context, Options options, String name) { + TabOptions tabOptions = (TabOptions) options; + // Generate all permutations of tabOptions.selected and tabOptions.oldStyle + tabOptions.selected = true; + tabOptions.oldStyle = false; + super.generate("Selected (v5+)", categoryMap, context, options, name); + tabOptions.oldStyle = true; + super.generate("Selected", categoryMap, context, options, name); + tabOptions.selected = false; + tabOptions.oldStyle = false; + super.generate("Unselected (v5+)", categoryMap, context, options, name); + tabOptions.oldStyle = true; + super.generate("Unselected", categoryMap, context, options, name); + } + + @Override + protected String getIconFolder(Options options) { + String folder = super.getIconFolder(options); + + TabOptions tabOptions = (TabOptions) options; + if (tabOptions.oldStyle) { + return folder; + } else { + return folder + "-v5"; //$NON-NLS-1$ + } + } + + @Override + protected String getIconName(Options options, String name) { + TabOptions tabOptions = (TabOptions) options; + if (tabOptions.selected) { + return name + "_selected.png"; //$NON-NLS-1$ + } else { + return name + "_unselected.png"; //$NON-NLS-1$ + } + } + + /** Options specific to generating tab icons */ + public static class TabOptions extends GraphicGenerator.Options { + /** Generate icon in the style used prior to v5 */ + public boolean oldStyle; + /** Generate "selected" icon if true, and "unselected" icon if false */ + public boolean selected = true; + } +} diff --git a/assetstudio/src/com/android/assetstudiolib/TextRenderUtil.java b/assetstudio/src/com/android/assetstudiolib/TextRenderUtil.java index a103701..e08a234 100644 --- a/assetstudio/src/com/android/assetstudiolib/TextRenderUtil.java +++ b/assetstudio/src/com/android/assetstudiolib/TextRenderUtil.java @@ -16,14 +16,14 @@ package com.android.assetstudiolib; +import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; +import java.awt.RenderingHints; import java.awt.font.FontRenderContext; -import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; -import java.util.Hashtable; -import java.util.Map; /** * A set of utility classes for rendering text to a {@link BufferedImage}, suitable for use as a @@ -31,13 +31,18 @@ import java.util.Map; */ public class TextRenderUtil { /** - * Renders the given string with the provided {@link Options} to a {@link BufferedImage}. + * Renders the given string with the provided {@link Options} to a + * {@link BufferedImage}. * - * @param text The text to render. + * @param text The text to render. + * @param paddingPercentage If nonzero, a percentage of the width or height + * (whichever is smaller) to add as padding around the text * @param options The optional parameters for rendering the text. - * @return An image, suitable for use as an input to a {@link GraphicGenerator}. + * @return An image, suitable for use as an input to a + * {@link GraphicGenerator}. */ - public static BufferedImage renderTextImage(String text, Options options) { + public static BufferedImage renderTextImage(String text, int paddingPercentage, + Options options) { if (options == null) { options = new Options(); } @@ -59,13 +64,32 @@ public class TextRenderUtil { FontRenderContext frc = tempG.getFontRenderContext(); - Rectangle2D bounds = font.getStringBounds(text, frc); + TextLayout layout = new TextLayout(text, font, frc); + Rectangle2D bounds = layout.getBounds(); + + // The padding is a percentage relative to the overall minimum of the width or height + if (paddingPercentage != 0) { + double minDimension = Math.min(bounds.getWidth(), bounds.getHeight()); + double delta = minDimension * paddingPercentage / 100; + bounds.setRect(bounds.getMinX() - delta, bounds.getMinY() - delta, + bounds.getWidth() + 2 * delta, bounds.getHeight() + 2 * delta); + } BufferedImage image = Util.newArgbBufferedImage( Math.max(1, (int) bounds.getWidth()), Math.max(1, (int) bounds.getHeight())); Graphics2D g = (Graphics2D) image.getGraphics(); + g.setColor(new Color(options.foregroundColor, true)); g.setFont(font); - g.drawString(text, 0, (float) -bounds.getY()); + + g.setRenderingHint( + RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint( + RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + + g.drawString(text, (float) -bounds.getX(), (float) -bounds.getY()); + + g.dispose(); + tempG.dispose(); return image; } @@ -79,6 +103,9 @@ public class TextRenderUtil { // TODO: Instead, a graphic generator should use a different source image for each density. private static final int DEFAULT_FONT_SIZE = 512; + /** Foreground color to render text with, as an AARRGGBB packed integer */ + public int foregroundColor = 0xFFFFFFFF; + /** * The optional {@link Font} to use. If null, a {@link Font} object will be generated using * the other options. diff --git a/assetstudio/src/com/android/assetstudiolib/Util.java b/assetstudio/src/com/android/assetstudiolib/Util.java index 1a6b00e..927ca30 100644 --- a/assetstudio/src/com/android/assetstudiolib/Util.java +++ b/assetstudio/src/com/android/assetstudiolib/Util.java @@ -84,7 +84,7 @@ public class Util { } /** - * Applies a gaussion blur of the given radius to the given {@link BufferedImage} using a kernel + * Applies a gaussian blur of the given radius to the given {@link BufferedImage} using a kernel * convolution. * * @param source The source image. diff --git a/assetstudio/src/images/notification_stencil/circle/hdpi.png b/assetstudio/src/images/notification_stencil/circle/hdpi.png Binary files differnew file mode 100644 index 0000000..4d28710 --- /dev/null +++ b/assetstudio/src/images/notification_stencil/circle/hdpi.png diff --git a/assetstudio/src/images/notification_stencil/circle/ldpi.png b/assetstudio/src/images/notification_stencil/circle/ldpi.png Binary files differnew file mode 100644 index 0000000..5c9a9f7 --- /dev/null +++ b/assetstudio/src/images/notification_stencil/circle/ldpi.png diff --git a/assetstudio/src/images/notification_stencil/circle/mdpi.png b/assetstudio/src/images/notification_stencil/circle/mdpi.png Binary files differnew file mode 100644 index 0000000..1f064a2 --- /dev/null +++ b/assetstudio/src/images/notification_stencil/circle/mdpi.png diff --git a/assetstudio/src/images/notification_stencil/circle/xhdpi.png b/assetstudio/src/images/notification_stencil/circle/xhdpi.png Binary files differnew file mode 100644 index 0000000..f6a57c1 --- /dev/null +++ b/assetstudio/src/images/notification_stencil/circle/xhdpi.png diff --git a/assetstudio/src/images/notification_stencil/square/hdpi.png b/assetstudio/src/images/notification_stencil/square/hdpi.png Binary files differnew file mode 100644 index 0000000..f755f4f --- /dev/null +++ b/assetstudio/src/images/notification_stencil/square/hdpi.png diff --git a/assetstudio/src/images/notification_stencil/square/ldpi.png b/assetstudio/src/images/notification_stencil/square/ldpi.png Binary files differnew file mode 100644 index 0000000..23846bd --- /dev/null +++ b/assetstudio/src/images/notification_stencil/square/ldpi.png diff --git a/assetstudio/src/images/notification_stencil/square/mdpi.png b/assetstudio/src/images/notification_stencil/square/mdpi.png Binary files differnew file mode 100644 index 0000000..5f80247 --- /dev/null +++ b/assetstudio/src/images/notification_stencil/square/mdpi.png diff --git a/assetstudio/src/images/notification_stencil/square/xhdpi.png b/assetstudio/src/images/notification_stencil/square/xhdpi.png Binary files differnew file mode 100644 index 0000000..7c27d82 --- /dev/null +++ b/assetstudio/src/images/notification_stencil/square/xhdpi.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/ActionBarIconGeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/ActionBarIconGeneratorTest.java new file mode 100644 index 0000000..ed5ea04 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/ActionBarIconGeneratorTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.assetstudiolib; + +import com.android.assetstudiolib.ActionBarIconGenerator.ActionBarOptions; +import com.android.assetstudiolib.ActionBarIconGenerator.Theme; + +import java.io.IOException; + +@SuppressWarnings("javadoc") +public class ActionBarIconGeneratorTest extends GeneratorTest { + private void checkGraphic(String baseName, Theme theme) throws IOException { + ActionBarOptions options = new ActionBarOptions(); + options.theme = theme; + + ActionBarIconGenerator generator = new ActionBarIconGenerator(); + checkGraphic(4, "actions", baseName, generator, options); + } + + public void testDark() throws Exception { + checkGraphic("ic_action_dark", Theme.HOLO_DARK); + } + + public void testLight() throws Exception { + checkGraphic("ic_action_light", Theme.HOLO_LIGHT); + } +} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/GeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/GeneratorTest.java new file mode 100644 index 0000000..59812bf --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/GeneratorTest.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.assetstudiolib; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.imageio.ImageIO; + +import junit.framework.TestCase; + +/** + * Shared test infrastructure for code generator + */ +public abstract class GeneratorTest extends TestCase implements GraphicGeneratorContext { + private static final String TEST_DATA_REL_PATH = + "assetstudio/tests/src/com/android/assetstudiolib/testdata"; + + protected void checkGraphic(int expectedFileCount, String folderName, String baseName, + GraphicGenerator generator, GraphicGenerator.Options options) + throws IOException { + Map<String, Map<String, BufferedImage>> categoryMap = + new HashMap<String, Map<String, BufferedImage>>(); + options.sourceImage = GraphicGenerator.getClipartImage("android.png"); + generator.generate(null, categoryMap, this, options, baseName); + + File targetDir = getTargetDir(); + + List<String> errors = new ArrayList<String>(); + int fileCount = 0; + for (Map<String, BufferedImage> previews : categoryMap.values()) { + for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) { + String relativePath = entry.getKey(); + BufferedImage image = entry.getValue(); + + String path = "testdata" + File.separator + folderName + File.separator + + relativePath; + InputStream is = GeneratorTest.class.getResourceAsStream(path); + if (is == null) { + if (targetDir == null) { + fail("Did not find " + path + + ". Set ADT_SDK_SOURCE_PATH to have it created automatically"); + } + File fileName = new File(targetDir, folderName + File.separator + + relativePath); + assertFalse(fileName.exists()); + if (!fileName.getParentFile().exists()) { + boolean mkdir = fileName.getParentFile().mkdirs(); + assertTrue(fileName.getParent(), mkdir); + } + + ImageIO.write(image, "PNG", fileName); + errors.add("File did not exist, created " + fileName.getPath()); + } else { + BufferedImage goldenImage = ImageIO.read(is); + assertImageSimilar(relativePath, goldenImage, image, 5.0f); + } + } + + fileCount += previews.values().size(); + } + if (errors.size() > 0) { + fail(errors.toString()); + } + + assertEquals("Wrong number of generated files", expectedFileCount, fileCount); + } + + private void assertImageSimilar(String imageName, BufferedImage goldenImage, + BufferedImage image, float maxPercentDifferent) throws IOException { + assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getWidth() + - image.getWidth()) < 2); + assertTrue("Widths differ too much for " + imageName, Math.abs(goldenImage.getHeight() + - image.getHeight()) < 2); + + assertEquals(BufferedImage.TYPE_INT_ARGB, image.getType()); + + if (goldenImage.getType() != BufferedImage.TYPE_INT_ARGB) { + BufferedImage temp = new BufferedImage(goldenImage.getWidth(), goldenImage.getHeight(), + BufferedImage.TYPE_INT_ARGB); + temp.getGraphics().drawImage(goldenImage, 0, 0, null); + goldenImage = temp; + } + assertEquals(BufferedImage.TYPE_INT_ARGB, goldenImage.getType()); + + int imageWidth = Math.min(goldenImage.getWidth(), image.getWidth()); + int imageHeight = Math.min(goldenImage.getHeight(), image.getHeight()); + + // Blur the images to account for the scenarios where there are pixel + // differences + // in where a sharp edge occurs + // goldenImage = blur(goldenImage, 6); + // image = blur(image, 6); + + int width = 3 * imageWidth; + int height = imageHeight; + BufferedImage deltaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); + Graphics g = deltaImage.getGraphics(); + + // Compute delta map + long delta = 0; + for (int y = 0; y < imageHeight; y++) { + for (int x = 0; x < imageWidth; x++) { + int goldenRgb = goldenImage.getRGB(x, y); + int rgb = image.getRGB(x, y); + if (goldenRgb == rgb) { + deltaImage.setRGB(imageWidth + x, y, 0x00808080); + continue; + } + + // If the pixels have no opacity, don't delta colors at all + if (((goldenRgb & 0xFF000000) == 0) && (rgb & 0xFF000000) == 0) { + deltaImage.setRGB(imageWidth + x, y, 0x00808080); + continue; + } + + int deltaR = ((rgb & 0xFF0000) >>> 16) - ((goldenRgb & 0xFF0000) >>> 16); + int newR = 128 + deltaR & 0xFF; + int deltaG = ((rgb & 0x00FF00) >>> 8) - ((goldenRgb & 0x00FF00) >>> 8); + int newG = 128 + deltaG & 0xFF; + int deltaB = (rgb & 0x0000FF) - (goldenRgb & 0x0000FF); + int newB = 128 + deltaB & 0xFF; + + int avgAlpha = ((((goldenRgb & 0xFF000000) >>> 24) + + ((rgb & 0xFF000000) >>> 24)) / 2) << 24; + + int newRGB = avgAlpha | newR << 16 | newG << 8 | newB; + deltaImage.setRGB(imageWidth + x, y, newRGB); + + delta += Math.abs(deltaR); + delta += Math.abs(deltaG); + delta += Math.abs(deltaB); + } + } + + // 3 different colors, 256 color levels + long total = imageHeight * imageWidth * 3L * 256L; + float percentDifference = (float) (delta * 100 / (double) total); + + if (percentDifference > maxPercentDifferent) { + // Expected on the left + // Golden on the right + g.drawImage(goldenImage, 0, 0, null); + g.drawImage(image, 2 * imageWidth, 0, null); + + // Labels + if (imageWidth > 80) { + g.setColor(Color.RED); + g.drawString("Expected", 10, 20); + g.drawString("Actual", 2 * imageWidth + 10, 20); + } + + File output = new File(getTempDir(), "delta-" + + imageName.replace(File.separatorChar, '_')); + if (output.exists()) { + output.delete(); + } + ImageIO.write(deltaImage, "PNG", output); + String message = String.format("Images differ (by %.1f%%) - see details in %s", + percentDifference, output); + System.out.println(message); + fail(message); + } + + g.dispose(); + } + + protected File getTempDir() { + if (System.getProperty("os.name").equals("Mac OS X")) { + return new File("/tmp"); //$NON-NLS-1$ + } + + return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$ + } + + public BufferedImage loadImageResource(String path) { + try { + return GraphicGenerator.getStencilImage(path); + } catch (IOException e) { + fail(e.toString()); + } + + return null; + } + + /** Get the location to write missing golden files to */ + protected File getTargetDir() { + // Set $ADT_SDK_SOURCE_PATH to point to your git "sdk" directory + String sdk = System.getenv("ADT_SDK_SOURCE_PATH"); + if (sdk != null) { + File sdkPath = new File(sdk); + if (sdkPath.exists()) { + File testData = new File(sdkPath, TEST_DATA_REL_PATH.replace('/', + File.separatorChar)); + if (testData.exists()) { + return testData; + } + } + } + + return null; + } +} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/LauncherIconGeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/LauncherIconGeneratorTest.java new file mode 100644 index 0000000..4d6680c --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/LauncherIconGeneratorTest.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.assetstudiolib; + +import com.android.assetstudiolib.LauncherIconGenerator.LauncherOptions; + +import java.io.IOException; + +@SuppressWarnings("javadoc") +public class LauncherIconGeneratorTest extends GeneratorTest { + private void checkGraphic(String baseName, + GraphicGenerator.Shape shape, GraphicGenerator.Style style, + boolean crop, int background, boolean isWebGraphic) throws IOException { + LauncherOptions options = new LauncherOptions(); + options.shape = shape; + options.crop = crop; + options.style = style; + options.backgroundColor = background; + options.isWebGraphic = isWebGraphic; + + LauncherIconGenerator generator = new LauncherIconGenerator(); + checkGraphic(4 + (isWebGraphic ? 1 : 0), "launcher", baseName, generator, options); + } + + public void testLauncher_fancyCircle() throws Exception { + checkGraphic("red_fancy_circle", GraphicGenerator.Shape.CIRCLE, + GraphicGenerator.Style.FANCY, true, 0xFF0000, true); + } + + public void testLauncher_glossySquare() throws Exception { + checkGraphic("blue_glossy_square", GraphicGenerator.Shape.SQUARE, + GraphicGenerator.Style.GLOSSY, true, 0x0040FF, true); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureLauncherAssetsPanel.java b/assetstudio/tests/src/com/android/assetstudiolib/MenuIconGeneratorTest.java index 930e93c..700be4b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureLauncherAssetsPanel.java +++ b/assetstudio/tests/src/com/android/assetstudiolib/MenuIconGeneratorTest.java @@ -13,25 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.ide.eclipse.adt.internal.assetstudio; -import org.eclipse.swt.widgets.Composite; +package com.android.assetstudiolib; -public class ConfigureLauncherAssetsPanel extends Composite { - - /** - * Create the composite. - * @param parent - * @param style - */ - public ConfigureLauncherAssetsPanel(Composite parent, int style) { - super(parent, style); +import java.io.IOException; +@SuppressWarnings("javadoc") +public class MenuIconGeneratorTest extends GeneratorTest { + private void checkGraphic(String baseName) throws IOException { + MenuIconGenerator generator = new MenuIconGenerator(); + checkGraphic(4, "menus", baseName, generator, new GraphicGenerator.Options()); } - @Override - protected void checkSubclass() { - // Disable the check that prevents subclassing of SWT components + public void testMenu() throws Exception { + checkGraphic("ic_menu_1"); } - } diff --git a/assetstudio/tests/src/com/android/assetstudiolib/NotificationIconGeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/NotificationIconGeneratorTest.java new file mode 100644 index 0000000..7ac5634 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/NotificationIconGeneratorTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.assetstudiolib; + +import com.android.assetstudiolib.NotificationIconGenerator.NotificationOptions; + +import java.io.IOException; + +@SuppressWarnings("javadoc") +public class NotificationIconGeneratorTest extends GeneratorTest { + private void checkGraphic(String baseName, + GraphicGenerator.Shape shape) throws IOException { + NotificationOptions options = new NotificationOptions(); + options.shape = shape; + + NotificationIconGenerator generator = new NotificationIconGenerator(); + checkGraphic(12, "notification", baseName, generator, options); + } + + public void testNotification1() throws Exception { + checkGraphic("ic_stat_circle", GraphicGenerator.Shape.CIRCLE); + } + + public void testNotification2() throws Exception { + checkGraphic("ic_stat_square", GraphicGenerator.Shape.SQUARE); + } +} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/TabIconGeneratorTest.java b/assetstudio/tests/src/com/android/assetstudiolib/TabIconGeneratorTest.java new file mode 100644 index 0000000..4fb4801 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/TabIconGeneratorTest.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.assetstudiolib; + +import java.io.IOException; + +@SuppressWarnings("javadoc") +public class TabIconGeneratorTest extends GeneratorTest { + private void checkGraphic(String baseName) throws IOException { + TabIconGenerator generator = new TabIconGenerator(); + checkGraphic(16, "tabs", baseName, generator, new TabIconGenerator.TabOptions()); + } + + public void testTabs1() throws Exception { + checkGraphic("ic_tab_1"); + } +} diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_dark.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_dark.png Binary files differnew file mode 100644 index 0000000..ac9fcf9 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_dark.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_light.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_light.png Binary files differnew file mode 100644 index 0000000..15da7b9 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-hdpi/ic_action_light.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-ldpi/ic_action_dark.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-ldpi/ic_action_dark.png Binary files differnew file mode 100644 index 0000000..e9ea176 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-ldpi/ic_action_dark.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-ldpi/ic_action_light.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-ldpi/ic_action_light.png Binary files differnew file mode 100644 index 0000000..1dca5d2 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-ldpi/ic_action_light.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_dark.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_dark.png Binary files differnew file mode 100644 index 0000000..82ec17f --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_dark.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_light.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_light.png Binary files differnew file mode 100644 index 0000000..a9ecda2 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-mdpi/ic_action_light.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_dark.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_dark.png Binary files differnew file mode 100644 index 0000000..cd6b555 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_dark.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_light.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_light.png Binary files differnew file mode 100644 index 0000000..cdc2cfd --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/actions/res/drawable-xhdpi/ic_action_light.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/blue_glossy_square-web.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/blue_glossy_square-web.png Binary files differnew file mode 100644 index 0000000..d8b8294 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/blue_glossy_square-web.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/red_fancy_circle-web.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/red_fancy_circle-web.png Binary files differnew file mode 100644 index 0000000..e9bd6fe --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/red_fancy_circle-web.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-hdpi/blue_glossy_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-hdpi/blue_glossy_square.png Binary files differnew file mode 100644 index 0000000..f549138 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-hdpi/blue_glossy_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-hdpi/red_fancy_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-hdpi/red_fancy_circle.png Binary files differnew file mode 100644 index 0000000..b068fb5 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-hdpi/red_fancy_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-ldpi/blue_glossy_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-ldpi/blue_glossy_square.png Binary files differnew file mode 100644 index 0000000..fcc7c1c --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-ldpi/blue_glossy_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-ldpi/red_fancy_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-ldpi/red_fancy_circle.png Binary files differnew file mode 100644 index 0000000..b85b2b5 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-ldpi/red_fancy_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-mdpi/blue_glossy_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-mdpi/blue_glossy_square.png Binary files differnew file mode 100644 index 0000000..29da2c3 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-mdpi/blue_glossy_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-mdpi/red_fancy_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-mdpi/red_fancy_circle.png Binary files differnew file mode 100644 index 0000000..54ea5ed --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-mdpi/red_fancy_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xhdpi/blue_glossy_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xhdpi/blue_glossy_square.png Binary files differnew file mode 100644 index 0000000..3377ddc --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xhdpi/blue_glossy_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xhdpi/red_fancy_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xhdpi/red_fancy_circle.png Binary files differnew file mode 100644 index 0000000..896da29 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/launcher/res/drawable-xhdpi/red_fancy_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-hdpi/ic_menu_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-hdpi/ic_menu_1.png Binary files differnew file mode 100644 index 0000000..0d6d67f --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-hdpi/ic_menu_1.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-ldpi/ic_menu_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-ldpi/ic_menu_1.png Binary files differnew file mode 100644 index 0000000..85655ed --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-ldpi/ic_menu_1.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-mdpi/ic_menu_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-mdpi/ic_menu_1.png Binary files differnew file mode 100644 index 0000000..de68199 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-mdpi/ic_menu_1.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-xhdpi/ic_menu_1.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-xhdpi/ic_menu_1.png Binary files differnew file mode 100644 index 0000000..dca9a75 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/menus/res/drawable-xhdpi/ic_menu_1.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v11/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v11/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..daad503 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v11/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v11/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v11/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..daad503 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v11/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v9/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v9/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..bdd8598 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v9/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v9/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v9/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..bdd8598 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi-v9/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..d2ea9f5 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..a1c9285 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-hdpi/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v11/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v11/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..228ab60 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v11/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v11/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v11/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..228ab60 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v11/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v9/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v9/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..ea4b105 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v9/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v9/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v9/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..ea4b105 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi-v9/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..c315dff --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..8592c1f --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-ldpi/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v11/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v11/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..7749b11 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v11/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v11/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v11/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..7749b11 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v11/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v9/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v9/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..1956c8e --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v9/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v9/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v9/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..1956c8e --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi-v9/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..c63fc8f --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..40b27af --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-mdpi/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v11/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v11/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..3dbfa96 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v11/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v11/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v11/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..3dbfa96 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v11/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v9/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v9/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..8d52ab7 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v9/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v9/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v9/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..8d52ab7 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi-v9/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi/ic_stat_circle.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi/ic_stat_circle.png Binary files differnew file mode 100644 index 0000000..d05fbff --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi/ic_stat_circle.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi/ic_stat_square.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi/ic_stat_square.png Binary files differnew file mode 100644 index 0000000..c7159ec --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/notification/res/drawable-xhdpi/ic_stat_square.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_selected.png Binary files differnew file mode 100644 index 0000000..e8e7e71 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_selected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_unselected.png Binary files differnew file mode 100644 index 0000000..8d1ea96 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi-v5/ic_tab_1_unselected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_selected.png Binary files differnew file mode 100644 index 0000000..5a49be9 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_selected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_unselected.png Binary files differnew file mode 100644 index 0000000..d957240 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-hdpi/ic_tab_1_unselected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi-v5/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi-v5/ic_tab_1_selected.png Binary files differnew file mode 100644 index 0000000..0550dd2 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi-v5/ic_tab_1_selected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi-v5/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi-v5/ic_tab_1_unselected.png Binary files differnew file mode 100644 index 0000000..82f4412 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi-v5/ic_tab_1_unselected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi/ic_tab_1_selected.png Binary files differnew file mode 100644 index 0000000..81302e9 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi/ic_tab_1_selected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi/ic_tab_1_unselected.png Binary files differnew file mode 100644 index 0000000..00568c7 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-ldpi/ic_tab_1_unselected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_selected.png Binary files differnew file mode 100644 index 0000000..646490f --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_selected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_unselected.png Binary files differnew file mode 100644 index 0000000..c11de02 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi-v5/ic_tab_1_unselected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_selected.png Binary files differnew file mode 100644 index 0000000..7c603b7 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_selected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_unselected.png Binary files differnew file mode 100644 index 0000000..521bf60 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-mdpi/ic_tab_1_unselected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_selected.png Binary files differnew file mode 100644 index 0000000..448454b --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_selected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_unselected.png Binary files differnew file mode 100644 index 0000000..52852ee --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi-v5/ic_tab_1_unselected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_selected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_selected.png Binary files differnew file mode 100644 index 0000000..317fc5a --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_selected.png diff --git a/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_unselected.png b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_unselected.png Binary files differnew file mode 100644 index 0000000..7a0ffc6 --- /dev/null +++ b/assetstudio/tests/src/com/android/assetstudiolib/testdata/tabs/res/drawable-xhdpi/ic_tab_1_unselected.png diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt index 7843eb1..b6042cb 100644 --- a/eclipse/dictionary.txt +++ b/eclipse/dictionary.txt @@ -102,6 +102,8 @@ guava hardcode hardcoded hardcodes +hdpi +helvetica highlighter holo hotfix diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java index 1db8fa0..38ecbe0 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AdtUtils.java @@ -60,6 +60,42 @@ public class AdtUtils { } /** + * Strips off the last file extension from the given filename, e.g. + * "foo.backup.diff" will be turned into "foo.backup". + * <p> + * Note that dot files (e.g. ".profile") will be left alone. + * + * @param filename the filename to be stripped + * @return the filename without the last file extension. + */ + public static String stripLastExtension(String filename) { + int dotIndex = filename.lastIndexOf('.'); + if (dotIndex > 0) { // > 0 instead of != -1: Treat dot files (e.g. .profile) differently + return filename.substring(0, dotIndex); + } else { + return filename; + } + } + + /** + * Strips off all extensions from the given filename, e.g. "foo.9.png" will + * be turned into "foo". + * <p> + * Note that dot files (e.g. ".profile") will be left alone. + * + * @param filename the filename to be stripped + * @return the filename without any file extensions + */ + public static String stripAllExtensions(String filename) { + int dotIndex = filename.indexOf('.'); + if (dotIndex > 0) { // > 0 instead of != -1: Treat dot files (e.g. .profile) differently + return filename.substring(0, dotIndex); + } else { + return filename; + } + } + + /** * Capitalizes the string, i.e. transforms the initial [a-z] into [A-Z]. * Returns the string unmodified if the first character is not [a-z]. * diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/AssetType.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/AssetType.java new file mode 100644 index 0000000..29cb910 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/AssetType.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.internal.assetstudio; + +/** + * The type of asset to create: launcher icon, menu icon, etc. + */ +enum AssetType { + /** Launcher icon to be shown in the application list */ + LAUNCHER("Launcher Icons", "ic_launcher"), //$NON-NLS-2$ + + /** Icons shown in menus */ + MENU("Menu Icons", "ic_menu_%s"), //$NON-NLS-2$ + + /** Icons shown in the action bar */ + ACTIONBAR("Action Bar Icons (Android 3.0+)", "ic_action_%s"), //$NON-NLS-2$ + + /** Icons shown as part of tabs */ + TAB("Tab Icons", "ic_tab_%s"), //$NON-NLS-2$ + + /** Icons shown in a notification message */ + NOTIFICATION("Notification Icons", "ic_stat_%s"); //$NON-NLS-2$ + + /** Display name to show to the user in the asset type selection list */ + private final String mDisplayName; + + /** Default asset name format */ + private String mDefaultNameFormat; + + AssetType(String displayName, String defaultNameFormat) { + mDisplayName = displayName; + mDefaultNameFormat = defaultNameFormat; + } + + /** + * Returns the display name of this asset type to show to the user in the + * asset wizard selection page etc + */ + String getDisplayName() { + return mDisplayName; + } + + /** + * Returns the default format to use to suggest a name for the asset + */ + String getDefaultNameFormat() { + return mDefaultNameFormat; + } + + /** Whether this asset type configures foreground scaling */ + boolean needsForegroundScaling() { + return this == LAUNCHER; + } + + /** Whether this asset type needs a shape parameter */ + boolean needsShape() { + return this == LAUNCHER || this == NOTIFICATION; + } + + /** Whether this asset type needs foreground and background color parameters */ + boolean needsColors() { + return this == LAUNCHER; + } + + /** Whether this asset type needs an effects parameter */ + boolean needsEffects() { + return this == LAUNCHER; + } + + /** Whether this asset type needs a theme parameter */ + boolean needsTheme() { + return this == ACTIONBAR; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ChooseAssetTypePage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ChooseAssetTypePage.java index a5b0480..7b406eb 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ChooseAssetTypePage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ChooseAssetTypePage.java @@ -16,12 +16,11 @@ package com.android.ide.eclipse.adt.internal.assetstudio; -import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper; +import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.ProjectButton; import com.android.ide.eclipse.adt.internal.resources.ResourceNameValidator; import com.android.resources.ResourceFolderType; import org.eclipse.core.resources.IProject; -import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.wizard.WizardPage; import org.eclipse.swt.SWT; @@ -36,20 +35,23 @@ import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Text; +/** Page for choosing the type of asset to create, as well as the target project */ public class ChooseAssetTypePage extends WizardPage implements SelectionListener, ModifyListener { - private Text mProjectText; - private Button mBrowseButton; - private ProjectChooserHelper mProjectChooserHelper; + private ProjectButton mProjectButton; + private Button mClipboardButton; private IProject mProject; private Text mNameText; + private AssetType mType = AssetType.LAUNCHER; + private boolean mNameModified; + private Label mResourceName; /** * Create the wizard. */ public ChooseAssetTypePage() { super("chooseAssetTypePage"); - setTitle("Choose Asset Set Type"); - setDescription("Select the type of asset set to create:"); + setTitle("Choose Icon Set Type"); + setDescription("Select the type of icon set to create:"); } /** @@ -57,39 +59,20 @@ public class ChooseAssetTypePage extends WizardPage implements SelectionListener * * @param parent the parent composite */ - @SuppressWarnings("unused") public void createControl(Composite parent) { Composite container = new Composite(parent, SWT.NULL); setControl(container); container.setLayout(new GridLayout(3, false)); - Button launcherIcons = new Button(container, SWT.RADIO); - launcherIcons.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); - launcherIcons.setSelection(true); - launcherIcons.setText("Launcher Icons"); - - Button menuIcons = new Button(container, SWT.RADIO); - menuIcons.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); - menuIcons.setEnabled(false); - menuIcons.setText("Menu Icons"); - - Button actionBarIcons = new Button(container, SWT.RADIO); - actionBarIcons.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); - actionBarIcons.setEnabled(false); - actionBarIcons.setText("Action Bar Icons (Android 3.0+)"); - - Button tabIcons = new Button(container, SWT.RADIO); - tabIcons.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); - tabIcons.setEnabled(false); - tabIcons.setText("Tab Icons"); - - Button notificationIcons = new Button(container, SWT.RADIO); - notificationIcons.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); - notificationIcons.setEnabled(false); - notificationIcons.setText("Notification Icons"); - - mProjectChooserHelper = new ProjectChooserHelper(parent.getShell(), null /*filter*/); + for (AssetType type : AssetType.values()) { + Button button = new Button(container, SWT.RADIO); + button.setData(type); + button.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); + button.setSelection(type == mType); + button.setText(type.getDisplayName()); + button.addSelectionListener(this); + } Label separator = new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL); GridData gdSeparator = new GridData(SWT.FILL, SWT.CENTER, false, false, 3, 1); @@ -100,28 +83,33 @@ public class ChooseAssetTypePage extends WizardPage implements SelectionListener projectLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); projectLabel.setText("Project:"); - mProjectText = new Text(container, SWT.BORDER); - mProjectText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - if (mProject != null) { - mProjectText.setText(mProject.getName()); - } - mProjectText.addModifyListener(this); - - mBrowseButton = new Button(container, SWT.FLAT); - mBrowseButton.setText("Browse..."); - mBrowseButton.setToolTipText("Allows you to select the Android project to modify."); + mProjectButton = new ProjectButton(container, mProject); + mProjectButton.setAlignment(SWT.LEFT); + mProjectButton.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + mProjectButton.addSelectionListener(this); Label assetLabel = new Label(container, SWT.NONE); assetLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - assetLabel.setText("Asset Name:"); + assetLabel.setText("Icon Name:"); mNameText = new Text(container, SWT.BORDER); - mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mNameText.setText("ic_launcher"); + mNameText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); mNameText.addModifyListener(this); - new Label(container, SWT.NONE); - mBrowseButton.addSelectionListener(this); + Label resourceLabel = new Label(container, SWT.NONE); + resourceLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + resourceLabel.setText("Resource:"); + + mResourceName = new Label(container, SWT.NONE); + mResourceName.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1)); + + mClipboardButton = new Button(container, SWT.FLAT); + mClipboardButton.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mClipboardButton.setText("Copy Name to Clipboard"); + + mClipboardButton.addSelectionListener(this); + + updateAssetType(); validatePage(); parent.getDisplay().asyncExec(new Runnable() { public void run() { @@ -130,6 +118,29 @@ public class ChooseAssetTypePage extends WizardPage implements SelectionListener }); } + private void updateAssetType() { + if (!mNameModified) { + // Default name suggestion, possibly as a suffix, e.g. "ic_menu_<name>" + String replace = "name"; + String suggestedName = String.format(getAssetType().getDefaultNameFormat(), replace); + mNameText.setText(suggestedName); + updateResourceLabel(); + mNameModified = false; + int start = suggestedName.indexOf(replace); + if (start != -1) { + mNameText.setSelection(start, start + replace.length()); + } else { + mNameText.selectAll(); + } + } else { + mNameText.selectAll(); + } + } + + private void updateResourceLabel() { + mResourceName.setText("@drawable/" + getOutputName()); //$NON-NLS-1$ + } + void setProject(IProject project) { mProject = project; } @@ -144,14 +155,17 @@ public class ChooseAssetTypePage extends WizardPage implements SelectionListener } public void widgetSelected(SelectionEvent e) { - if (e.getSource() == mBrowseButton) { - IJavaProject p = mProjectChooserHelper.chooseJavaProject(mProjectText.getText(), - "Please select the target project"); - if (p != null) { - changeProject(p.getProject()); - mProjectText.setText(mProject.getName()); + Object source = e.getSource(); + if (source == mProjectButton) { + mProject = mProjectButton.getSelectedProject(); + validatePage(); + } else if (source instanceof Button) { + // User selected a different asset type to be created + Object data = ((Button) source).getData(); + if (data instanceof AssetType) { + mType = (AssetType) data; + updateAssetType(); } - } } @@ -159,56 +173,42 @@ public class ChooseAssetTypePage extends WizardPage implements SelectionListener } public void modifyText(ModifyEvent e) { - String project = mProjectText.getText(); - - // Is this a valid project? - IJavaProject[] projects = mProjectChooserHelper.getAndroidProjects(null /*javaModel*/); - IProject found = null; - for (IJavaProject p : projects) { - if (p.getProject().getName().equals(project)) { - found = p.getProject(); - break; - } - } - - if (found != mProject) { - changeProject(found); + Object source = e.getSource(); + if (source == mNameText) { + mNameModified = true; + updateResourceLabel(); } validatePage(); } - private void changeProject(IProject newProject) { - mProject = newProject; - validatePage(); - } - String getOutputName() { return mNameText.getText().trim(); } + AssetType getAssetType() { + return mType; + } + private void validatePage() { String error = null; - //String warning = null; if (getProject() == null) { error = "Please select an Android project."; - } - - String outputName = getOutputName(); - if (outputName == null || outputName.length() == 0) { - error = "Please enter a name"; } else { - ResourceNameValidator validator = - ResourceNameValidator.create(true, ResourceFolderType.DRAWABLE); - error = validator.isValid(outputName); + String outputName = getOutputName(); + if (outputName == null || outputName.length() == 0) { + error = "Please enter a name"; + } else { + ResourceNameValidator validator = + ResourceNameValidator.create(true, ResourceFolderType.DRAWABLE); + error = validator.isValid(outputName); + } } setPageComplete(error == null); if (error != null) { setMessage(error, IMessageProvider.ERROR); - //} else if (warning != null) { - // setMessage(warning, IMessageProvider.WARNING); } else { setErrorMessage(null); setMessage(null); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureAssetSetPage.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureAssetSetPage.java index 8804a47..e60d9f9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureAssetSetPage.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/ConfigureAssetSetPage.java @@ -16,25 +16,25 @@ package com.android.ide.eclipse.adt.internal.assetstudio; -import static com.android.assetstudiolib.LauncherIconGenerator.Options.Shape.CIRCLE; -import static com.android.assetstudiolib.LauncherIconGenerator.Options.Shape.SQUARE; - +import com.android.assetstudiolib.ActionBarIconGenerator; import com.android.assetstudiolib.GraphicGenerator; import com.android.assetstudiolib.GraphicGeneratorContext; import com.android.assetstudiolib.LauncherIconGenerator; -import com.android.assetstudiolib.LauncherIconGenerator.Options.Style; +import com.android.assetstudiolib.MenuIconGenerator; +import com.android.assetstudiolib.NotificationIconGenerator; +import com.android.assetstudiolib.TabIconGenerator; import com.android.assetstudiolib.TextRenderUtil; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageControl; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.ImageUtils; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils; -import com.android.resources.Density; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.wizard.WizardPage; @@ -54,6 +54,7 @@ import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.layout.RowData; import org.eclipse.swt.layout.RowLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.ColorDialog; @@ -69,13 +70,12 @@ import org.eclipse.swt.widgets.Text; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; -import java.util.Arrays; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; import javax.imageio.ImageIO; @@ -87,7 +87,8 @@ import javax.imageio.ImageIO; public class ConfigureAssetSetPage extends WizardPage implements SelectionListener, GraphicGeneratorContext, ModifyListener { - private Composite configurationArea; + private static final int PREVIEW_AREA_WIDTH = 120; + private Composite mConfigurationArea; private Button mImageRadio; private Button mClipartRadio; private Button mTextRadio; @@ -123,16 +124,26 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen private static String sImagePath; private Button mChooseClipart; private Composite mClipartPreviewPanel; + private Label mThemeLabel; + private Composite mThemeComposite; + private Button mHoloLightRadio; + private Button mHoloDarkRadio; + private Label mScalingLabel; + private Composite mScalingComposite; + private Label mShapeLabel; + private Composite mShapeComposite; + private Label mBgColorLabel; + private Label mFgColorLabel; + private Label mEffectsLabel; + private Composite mEffectsComposite; /** * Create the wizard. - * - * @param wizard the surrounding wizard */ public ConfigureAssetSetPage() { super("configureAssetPage"); - setTitle("Configure Asset Set"); - setDescription("Configure the attributes of the asset set"); + setTitle("Configure Icon Set"); + setDescription("Configure the attributes of the icon set"); } /** @@ -157,18 +168,19 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen configurationScrollArea.setExpandHorizontal(true); configurationScrollArea.setExpandVertical(true); - configurationArea = new Composite(configurationScrollArea, SWT.NONE); + mConfigurationArea = new Composite(configurationScrollArea, SWT.NONE); GridLayout glConfigurationArea = new GridLayout(3, false); + glConfigurationArea.horizontalSpacing = 0; glConfigurationArea.marginRight = 15; glConfigurationArea.marginWidth = 0; glConfigurationArea.marginHeight = 0; - configurationArea.setLayout(glConfigurationArea); + mConfigurationArea.setLayout(glConfigurationArea); - Label foregroundLabel = new Label(configurationArea, SWT.NONE); + Label foregroundLabel = new Label(mConfigurationArea, SWT.NONE); foregroundLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); foregroundLabel.setText("Foreground:"); - Composite foregroundComposite = new Composite(configurationArea, SWT.NONE); + Composite foregroundComposite = new Composite(mConfigurationArea, SWT.NONE); foregroundComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); GridLayout glForegroundComposite = new GridLayout(5, false); glForegroundComposite.horizontalSpacing = 0; @@ -188,9 +200,9 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen mTextRadio.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); mTextRadio.setText("Text"); mTextRadio.addSelectionListener(this); - new Label(configurationArea, SWT.NONE); + new Label(mConfigurationArea, SWT.NONE); - mForegroundArea = new Composite(configurationArea, SWT.NONE); + mForegroundArea = new Composite(mConfigurationArea, SWT.NONE); mForegroundArea.setLayout(new StackLayout()); mForegroundArea.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1)); @@ -247,120 +259,133 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen mFontButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1)); mFontButton.addSelectionListener(this); mFontButton.setText("Choose Font..."); - new Label(configurationArea, SWT.NONE); + new Label(mConfigurationArea, SWT.NONE); - mTrimCheckBox = new Button(configurationArea, SWT.CHECK); + mTrimCheckBox = new Button(mConfigurationArea, SWT.CHECK); mTrimCheckBox.setEnabled(false); mTrimCheckBox.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); - mTrimCheckBox.setSelection(true); + mTrimCheckBox.setSelection(false); mTrimCheckBox.setText("Trim Surrounding Blank Space"); - new Label(configurationArea, SWT.NONE); + new Label(mConfigurationArea, SWT.NONE); - Label paddingLabel = new Label(configurationArea, SWT.NONE); + Label paddingLabel = new Label(mConfigurationArea, SWT.NONE); paddingLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); paddingLabel.setText("Additional Padding:"); - new Label(configurationArea, SWT.NONE); + new Label(mConfigurationArea, SWT.NONE); - mPaddingSlider = new Slider(configurationArea, SWT.NONE); + mPaddingSlider = new Slider(mConfigurationArea, SWT.NONE); mPaddingSlider.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); - mPaddingSlider.setEnabled(false); // This doesn't work right -- not sure why. For now just use a plain slider - // and subtract from it to get the real range. + // and subtract 10 from it to get the real range. //mPaddingSlider.setValues(0, -10, 50, 0, 1, 10); - mPaddingSlider.setSelection(10); + mPaddingSlider.setSelection(10 + 15); mPaddingSlider.addSelectionListener(this); - mPercentLabel = new Label(configurationArea, SWT.NONE); - mPercentLabel.setText(" 0%"); // Enough available space for -10% + mPercentLabel = new Label(mConfigurationArea, SWT.NONE); + mPercentLabel.setText(" 15%"); // Enough available space for -10% - Label scalingLabel = new Label(configurationArea, SWT.NONE); - scalingLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - scalingLabel.setText("Foreground Scaling:"); + mScalingLabel = new Label(mConfigurationArea, SWT.NONE); + mScalingLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mScalingLabel.setText("Foreground Scaling:"); - Composite scalingComposite = new Composite(configurationArea, SWT.NONE); - scalingComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); - GridLayout glScalingComposite = new GridLayout(5, false); - glScalingComposite.horizontalSpacing = 0; - scalingComposite.setLayout(glScalingComposite); + mScalingComposite = new Composite(mConfigurationArea, SWT.NONE); + mScalingComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); + GridLayout gl_mScalingComposite = new GridLayout(5, false); + gl_mScalingComposite.horizontalSpacing = 0; + mScalingComposite.setLayout(gl_mScalingComposite); - mCropRadio = new Button(scalingComposite, SWT.FLAT | SWT.TOGGLE); + mCropRadio = new Button(mScalingComposite, SWT.FLAT | SWT.TOGGLE); mCropRadio.setSelection(true); mCropRadio.setText("Crop"); mCropRadio.addSelectionListener(this); - mCenterRadio = new Button(scalingComposite, SWT.FLAT | SWT.TOGGLE); + mCenterRadio = new Button(mScalingComposite, SWT.FLAT | SWT.TOGGLE); mCenterRadio.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1)); mCenterRadio.setText("Center"); mCenterRadio.addSelectionListener(this); - Label shapeLabel = new Label(configurationArea, SWT.NONE); - shapeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - shapeLabel.setText("Shape"); + mShapeLabel = new Label(mConfigurationArea, SWT.NONE); + mShapeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mShapeLabel.setText("Shape"); - Composite shapeComposite = new Composite(configurationArea, SWT.NONE); - shapeComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); - GridLayout glShapeComposite = new GridLayout(5, false); - glShapeComposite.horizontalSpacing = 0; - shapeComposite.setLayout(glShapeComposite); + mShapeComposite = new Composite(mConfigurationArea, SWT.NONE); + mShapeComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); + GridLayout gl_mShapeComposite = new GridLayout(5, false); + gl_mShapeComposite.horizontalSpacing = 0; + mShapeComposite.setLayout(gl_mShapeComposite); - mSquareRadio = new Button(shapeComposite, SWT.FLAT | SWT.TOGGLE); + mSquareRadio = new Button(mShapeComposite, SWT.FLAT | SWT.TOGGLE); mSquareRadio.setSelection(true); mSquareRadio.setText("Square"); mSquareRadio.addSelectionListener(this); - mCircleButton = new Button(shapeComposite, SWT.FLAT | SWT.TOGGLE); + mCircleButton = new Button(mShapeComposite, SWT.FLAT | SWT.TOGGLE); mCircleButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 4, 1)); mCircleButton.setText("Circle"); mCircleButton.addSelectionListener(this); - Label bgColorLabel = new Label(configurationArea, SWT.NONE); - bgColorLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - bgColorLabel.setText("Background Color:"); + mThemeLabel = new Label(mConfigurationArea, SWT.NONE); + mThemeLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mThemeLabel.setText("Theme"); + + mThemeComposite = new Composite(mConfigurationArea, SWT.NONE); + mThemeComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1)); + GridLayout gl_mThemeComposite = new GridLayout(2, false); + gl_mThemeComposite.horizontalSpacing = 0; + mThemeComposite.setLayout(gl_mThemeComposite); + + mHoloLightRadio = new Button(mThemeComposite, SWT.FLAT | SWT.TOGGLE); + mHoloLightRadio.setText("Holo Light"); + mHoloLightRadio.setSelection(true); + mHoloLightRadio.addSelectionListener(this); - mBgButton = new Button(configurationArea, SWT.FLAT); + mHoloDarkRadio = new Button(mThemeComposite, SWT.FLAT | SWT.TOGGLE); + mHoloDarkRadio.setText("Holo Dark"); + mHoloDarkRadio.addSelectionListener(this); + + mBgColorLabel = new Label(mConfigurationArea, SWT.NONE); + mBgColorLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mBgColorLabel.setText("Background Color:"); + + mBgButton = new Button(mConfigurationArea, SWT.FLAT); + mBgButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); mBgButton.addSelectionListener(this); mBgButton.setAlignment(SWT.CENTER); - new Label(configurationArea, SWT.NONE); - Label fgColorLabel = new Label(configurationArea, SWT.NONE); - fgColorLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - fgColorLabel.setText("Foreground Color:"); + mFgColorLabel = new Label(mConfigurationArea, SWT.NONE); + mFgColorLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mFgColorLabel.setText("Foreground Color:"); - mFgButton = new Button(configurationArea, SWT.FLAT); + mFgButton = new Button(mConfigurationArea, SWT.FLAT); + mFgButton.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1)); mFgButton.setAlignment(SWT.CENTER); mFgButton.addSelectionListener(this); - new Label(configurationArea, SWT.NONE); - Label effectsLabel = new Label(configurationArea, SWT.NONE); - effectsLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); - effectsLabel.setText("Foreground Effects:"); + mEffectsLabel = new Label(mConfigurationArea, SWT.NONE); + mEffectsLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1)); + mEffectsLabel.setText("Foreground Effects:"); - Composite effectsComposite = new Composite(configurationArea, SWT.NONE); - effectsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1)); - GridLayout glEffectsComposite = new GridLayout(5, false); - glEffectsComposite.horizontalSpacing = 0; - effectsComposite.setLayout(glEffectsComposite); + mEffectsComposite = new Composite(mConfigurationArea, SWT.NONE); + mEffectsComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1)); + GridLayout gl_mEffectsComposite = new GridLayout(5, false); + gl_mEffectsComposite.horizontalSpacing = 0; + mEffectsComposite.setLayout(gl_mEffectsComposite); - mSimpleRadio = new Button(effectsComposite, SWT.FLAT | SWT.TOGGLE); - mSimpleRadio.setEnabled(false); + mSimpleRadio = new Button(mEffectsComposite, SWT.FLAT | SWT.TOGGLE); mSimpleRadio.setSelection(true); mSimpleRadio.setText("Simple"); mSimpleRadio.addSelectionListener(this); - mFancyRadio = new Button(effectsComposite, SWT.FLAT | SWT.TOGGLE); - mFancyRadio.setEnabled(false); + mFancyRadio = new Button(mEffectsComposite, SWT.FLAT | SWT.TOGGLE); mFancyRadio.setText("Fancy"); mFancyRadio.addSelectionListener(this); - mGlossyRadio = new Button(effectsComposite, SWT.FLAT | SWT.TOGGLE); - mGlossyRadio.setEnabled(false); + mGlossyRadio = new Button(mEffectsComposite, SWT.FLAT | SWT.TOGGLE); mGlossyRadio.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 3, 1)); mGlossyRadio.setText("Glossy"); mGlossyRadio.addSelectionListener(this); - - new Label(configurationArea, SWT.NONE); - configurationScrollArea.setContent(configurationArea); - configurationScrollArea.setMinSize(configurationArea.computeSize(SWT.DEFAULT, + configurationScrollArea.setContent(mConfigurationArea); + configurationScrollArea.setMinSize(mConfigurationArea.computeSize(SWT.DEFAULT, SWT.DEFAULT)); Label previewLabel = new Label(container, SWT.NONE); @@ -369,8 +394,8 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen mPreviewArea = new Composite(container, SWT.BORDER); - RowLayout rlPreviewAreaPreviewArea = new RowLayout(SWT.VERTICAL); - rlPreviewAreaPreviewArea.wrap = false; + RowLayout rlPreviewAreaPreviewArea = new RowLayout(SWT.HORIZONTAL); + rlPreviewAreaPreviewArea.wrap = true; rlPreviewAreaPreviewArea.pack = true; rlPreviewAreaPreviewArea.center = true; rlPreviewAreaPreviewArea.spacing = 0; @@ -380,12 +405,13 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen rlPreviewAreaPreviewArea.marginLeft = 0; mPreviewArea.setLayout(rlPreviewAreaPreviewArea); GridData gdMPreviewArea = new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1); - gdMPreviewArea.widthHint = 120; + gdMPreviewArea.widthHint = PREVIEW_AREA_WIDTH; mPreviewArea.setLayoutData(gdMPreviewArea); // Initial color Display display = parent.getDisplay(); - updateColor(display, new RGB(0xa4, 0xc6, 0x39), true /*background*/); + //updateColor(display, new RGB(0xa4, 0xc6, 0x39), true /*background*/); + updateColor(display, new RGB(0xff, 0x00, 0x00), true /*background*/); updateColor(display, new RGB(0x00, 0x00, 0x00), false /*background*/); // Start out showing the image form @@ -395,10 +421,40 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen // initially and we still get images mTextRadio.setSelection(true); chooseForegroundTab(mTextRadio, mTextForm); + new Label(mConfigurationArea, SWT.NONE); + new Label(mConfigurationArea, SWT.NONE); + new Label(mConfigurationArea, SWT.NONE); validatePage(); } + void configureAssetType(AssetType type) { + showGroup(type.needsForegroundScaling(), mScalingLabel, mScalingComposite); + showGroup(type.needsShape(), mShapeLabel, mShapeComposite); + showGroup(type.needsTheme(), mThemeLabel, mThemeComposite); + showGroup(type.needsColors(), mBgColorLabel, mBgButton); + showGroup(type.needsColors(), mFgColorLabel, mFgButton); + showGroup(type.needsEffects(), mEffectsLabel, mEffectsComposite); + + Composite parent = mScalingLabel.getParent(); + parent.pack(); + parent.layout(); + } + + private static void showGroup(boolean show, Control control1, Control control2) { + showControl(show, control1); + showControl(show, control2); + } + + private static void showControl(boolean show, Control control) { + Object data = control.getLayoutData(); + if (data instanceof GridData) { + GridData gridData = (GridData) data; + gridData.exclude = !show; + } + control.setVisible(show); + } + @Override public void setVisible(boolean visible) { super.setVisible(visible); @@ -407,10 +463,25 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen // that method is called when the wizard is created, and we want to wait until the // user has chosen a project before attempting to look up the right default image to use if (visible) { + // Clear out old previews - important if the user goes back to page one, changes + // asset type and steps into page 2 - at that point we arrive here and we might + // display the old previews for a brief period until the preview delay timer expires. + for (Control c : mPreviewArea.getChildren()) { + c.dispose(); + } + mPreviewArea.layout(true); + + // Update asset type configuration: will show/hide parameter controls depending + // on which asset type is chosen + CreateAssetSetWizard wizard = (CreateAssetSetWizard) getWizard(); + AssetType type = wizard.getAssetType(); + assert type != null; + configureAssetType(type); + // Initial image - use the most recently used image, or the default launcher // icon created in our default projects, if there if (sImagePath == null) { - IProject project = ((CreateAssetSetWizard) getWizard()).getProject(); + IProject project = wizard.getProject(); if (project != null) { IResource icon = project.findMember("res/drawable-hdpi/icon.png"); //$NON-NLS-1$ if (icon != null) { @@ -453,7 +524,6 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen error = "Enter text"; } } else { - //error = "Clipart not yet implemented"; assert mClipartRadio.getSelection(); if (mSelectedClipart == null) { error = "Select clip art"; @@ -473,6 +543,12 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen return error == null; } + @Override + public boolean isPageComplete() { + // Force user to reach second page before hitting Finish + return isCurrentPage(); + } + // ---- Implements ModifyListener ---- public void modifyText(ModifyEvent e) { @@ -517,25 +593,39 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen // Enforce Radio Groups if (source == mCropRadio) { + mCropRadio.setSelection(true); // Ensure that you can't toggle it off mCenterRadio.setSelection(false); } else if (source == mCenterRadio) { + mCenterRadio.setSelection(true); mCropRadio.setSelection(false); } if (source == mSquareRadio) { + mSquareRadio.setSelection(true); mCircleButton.setSelection(false); } else if (source == mCircleButton) { + mCircleButton.setSelection(true); mSquareRadio.setSelection(false); } if (source == mSimpleRadio) { + mSimpleRadio.setSelection(true); mGlossyRadio.setSelection(false); mFancyRadio.setSelection(false); } else if (source == mFancyRadio) { + mFancyRadio.setSelection(true); mSimpleRadio.setSelection(false); mGlossyRadio.setSelection(false); } else if (source == mGlossyRadio) { + mGlossyRadio.setSelection(true); mSimpleRadio.setSelection(false); mFancyRadio.setSelection(false); } + if (source == mHoloDarkRadio) { + mHoloDarkRadio.setSelection(true); + mHoloLightRadio.setSelection(false); + } else if (source == mHoloLightRadio) { + mHoloLightRadio.setSelection(true); + mHoloDarkRadio.setSelection(false); + } if (source == mChooseClipart) { MessageDialog dialog = new MessageDialog(mChooseClipart.getShell(), @@ -646,12 +736,22 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen if (source == mFontButton) { FontDialog dialog = new FontDialog(mFontButton.getShell()); + FontData[] fontList; + if (mFontButton.getData() == null) { + fontList = mFontButton.getDisplay().getFontList("Helvetica", true /*scalable*/); + } else { + fontList = mFontButton.getFont().getFontData(); + } + dialog.setFontList(fontList); FontData data = dialog.open(); if (data != null) { Font font = new Font(mFontButton.getDisplay(), dialog.getFontList()); mFontButton.setFont(font); updateFontLabel(font); mFontButton.getParent().pack(); + // Mark the font on the button as custom (since the renderer needs to + // distinguish between this font and the default font it starts out with) + mFontButton.setData(Boolean.TRUE); } } @@ -673,6 +773,19 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen } private java.awt.Font getSelectedFont() { + // Always use a large font for the rendering, even though user is typically + // picking small font sizes in the font chooser + //int dpi = mFontButton.getDisplay().getDPI().y; + //int height = (int) Math.round(fontData.getHeight() * dpi / 72.0); + int fontHeight = new TextRenderUtil.Options().fontSize; + + if (mFontButton.getData() == null) { + // The user has not yet picked a font; look up the default font to use + // (Helvetica Bold, not whatever font happened to be used for button widgets + // in SWT on this platform) + return new java.awt.Font("Helvetica", java.awt.Font.BOLD, fontHeight); //$NON-NLS-1$ + } + Font font = mFontButton.getFont(); FontData fontData = font.getFontData()[0]; @@ -686,13 +799,7 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen awtStyle = java.awt.Font.BOLD; } - // Always use a large font for the rendering, even though user is typically - // picking small font sizes in the font chooser - //int dpi = mFontButton.getDisplay().getDPI().y; - //int height = (int) Math.round(fontData.getHeight() * dpi / 72.0); - int height = new TextRenderUtil.Options().fontSize; - - return new java.awt.Font(fontData.getName(), awtStyle, height); + return new java.awt.Font(fontData.getName(), awtStyle, fontHeight); } private int getPadding() { @@ -700,7 +807,7 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen return mPaddingSlider.getSelection() - 10; } - public void chooseForegroundTab(Button newButton, Composite newArea) { + private void chooseForegroundTab(Button newButton, Composite newArea) { if (newButton.getSelection()) { mImageRadio.setSelection(false); mClipartRadio.setSelection(false); @@ -709,6 +816,10 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen StackLayout stackLayout = (StackLayout) mForegroundArea.getLayout(); stackLayout.topControl = newArea; mForegroundArea.layout(); + } else { + // Treat it as a radio button: you can't click to turn it off, you have to + // click on one of the other buttons + newButton.setSelection(true); } } @@ -745,26 +856,39 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen return; } - Map<String, BufferedImage> map = generatePreviews(); - for (Map.Entry<String, BufferedImage> entry : map.entrySet()) { - String id = entry.getKey(); - BufferedImage image = entry.getValue(); - Label nameLabel = new Label(mPreviewArea, SWT.NONE); - nameLabel.setText(id); - - Image swtImage = SwtUtils.convertToSwt(display, image, true, -1); - if (swtImage != null) { - @SuppressWarnings("unused") // Has side effect - ImageControl imageControl = new ImageControl(mPreviewArea, SWT.NONE, swtImage); + Map<String, Map<String, BufferedImage>> map = generateImages(true /*previewOnly*/); + for (Entry<String, Map<String, BufferedImage>> categoryEntry : map.entrySet()) { + String category = categoryEntry.getKey(); + if (category.length() > 0) { + Label nameLabel = new Label(mPreviewArea, SWT.NONE); + nameLabel.setText(String.format("%1$s:", category)); + RowData rowData = new RowData(); + nameLabel.setLayoutData(rowData); + // Ensure these get their own rows + rowData.width = PREVIEW_AREA_WIDTH; + } + + Map<String, BufferedImage> images = categoryEntry.getValue(); + for (Entry<String, BufferedImage> entry : images.entrySet()) { + BufferedImage image = entry.getValue(); + Image swtImage = SwtUtils.convertToSwt(display, image, true, -1); + if (swtImage != null) { + @SuppressWarnings("unused") // Has side effect + ImageControl imageControl = new ImageControl(mPreviewArea, SWT.NONE, swtImage); + } } } mPreviewArea.layout(true); } - Map<String, BufferedImage> generatePreviews() { + Map<String, Map<String, BufferedImage>> generateImages(boolean previewOnly) { // Map of ids to images: Preserve insertion order (the densities) - Map<String, BufferedImage> imageMap = new LinkedHashMap<String, BufferedImage>(); + Map<String, Map<String, BufferedImage>> categoryMap = + new LinkedHashMap<String, Map<String, BufferedImage>>(); + + CreateAssetSetWizard wizard = (CreateAssetSetWizard) getWizard(); + AssetType type = wizard.getAssetType(); BufferedImage sourceImage = null; if (mImageRadio.getSelection()) { @@ -787,7 +911,14 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen String text = mText.getText(); TextRenderUtil.Options options = new TextRenderUtil.Options(); options.font = getSelectedFont(); - sourceImage = TextRenderUtil.renderTextImage(text, options); + int color; + if (type.needsColors()) { + color = 0xFF000000 | (mFgColor.red << 16) | (mFgColor.green << 8) | mFgColor.blue; + } else { + color = 0xFFFFFFFF; + } + options.foregroundColor = color; + sourceImage = TextRenderUtil.renderTextImage(text, getPadding(), options); } else { assert mClipartRadio.getSelection(); assert mSelectedClipart != null; @@ -795,48 +926,69 @@ public class ConfigureAssetSetPage extends WizardPage implements SelectionListen sourceImage = GraphicGenerator.getClipartImage(mSelectedClipart); } catch (IOException e) { AdtPlugin.log(e, null); - return imageMap; + return categoryMap; } } - LauncherIconGenerator.Options options = new LauncherIconGenerator.Options(); - options.shape = mCircleButton.getSelection() ? CIRCLE : SQUARE; - options.crop = mCropRadio.getSelection(); - - int color = (mBgColor.red << 16) | (mBgColor.green << 8) | mBgColor.blue; - options.backgroundColor = color; - options.sourceImage = sourceImage; - - Density[] densityValues = Density.values(); - // Sort density values into ascending order - Arrays.sort(densityValues, new Comparator<Density>() { - public int compare(Density d1, Density d2) { - return d1.getDpiValue() - d2.getDpiValue(); + GraphicGenerator generator = null; + GraphicGenerator.Options options = null; + switch (type) { + case LAUNCHER: { + generator = new LauncherIconGenerator(); + LauncherIconGenerator.LauncherOptions launcherOptions = + new LauncherIconGenerator.LauncherOptions(); + launcherOptions.shape = mCircleButton.getSelection() + ? GraphicGenerator.Shape.CIRCLE : GraphicGenerator.Shape.SQUARE; + launcherOptions.crop = mCropRadio.getSelection(); + launcherOptions.style = mFancyRadio.getSelection() ? + GraphicGenerator.Style.FANCY : mGlossyRadio.getSelection() + ? GraphicGenerator.Style.GLOSSY : GraphicGenerator.Style.SIMPLE; + int color = (mBgColor.red << 16) | (mBgColor.green << 8) | mBgColor.blue; + launcherOptions.backgroundColor = color; + // Flag which tells the generator iterator to include a web graphic + launcherOptions.isWebGraphic = !previewOnly; + options = launcherOptions; + + break; } - - }); - Style[] styleValues = LauncherIconGenerator.Options.Style.values(); - - for (Density density : densityValues) { - if (!density.isValidValueForDevice()) { - continue; - } - if (density == Density.TV) { - // Not yet supported -- missing stencil image - continue; + case MENU: + generator = new MenuIconGenerator(); + options = new GraphicGenerator.Options(); + break; + case ACTIONBAR: { + generator = new ActionBarIconGenerator(); + ActionBarIconGenerator.ActionBarOptions actionBarOptions = + new ActionBarIconGenerator.ActionBarOptions(); + actionBarOptions.theme = mHoloDarkRadio.getSelection() + ? ActionBarIconGenerator.Theme.HOLO_DARK + : ActionBarIconGenerator.Theme.HOLO_LIGHT; + + options = actionBarOptions; + break; } - options.density = density; - for (LauncherIconGenerator.Options.Style style : styleValues) { - options.style = style; - LauncherIconGenerator generator = new LauncherIconGenerator(this, options); - BufferedImage image = generator.generate(); - if (image != null) { - imageMap.put(density.getResourceValue(), image); - } + case NOTIFICATION: { + generator = new NotificationIconGenerator(); + NotificationIconGenerator.NotificationOptions notificationOptions = + new NotificationIconGenerator.NotificationOptions(); + notificationOptions.shape = mCircleButton.getSelection() + ? GraphicGenerator.Shape.CIRCLE : GraphicGenerator.Shape.SQUARE; + options = notificationOptions; + break; } + case TAB: + generator = new TabIconGenerator(); + options = new TabIconGenerator.TabOptions(); + break; + default: + AdtPlugin.log(IStatus.ERROR, "Unsupported asset type: %1$s", type); + return categoryMap; } - return imageMap; + options.sourceImage = sourceImage; + String baseName = wizard.getBaseName(); + generator.generate(null, categoryMap, this, options, baseName); + + return categoryMap; } private void updateColor(Display display, RGB color, boolean isBackground) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/CreateAssetSetWizard.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/CreateAssetSetWizard.java index 4057824..b3d7348 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/CreateAssetSetWizard.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/CreateAssetSetWizard.java @@ -15,10 +15,6 @@ */ package com.android.ide.eclipse.adt.internal.assetstudio; -import static com.android.ide.eclipse.adt.AdtConstants.DOT_PNG; -import static com.android.ide.eclipse.adt.AdtConstants.WS_RESOURCES; -import static com.android.ide.eclipse.adt.AdtConstants.WS_SEP; - import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; @@ -67,99 +63,118 @@ import java.util.Map; import javax.imageio.ImageIO; +/** + * Wizard for creating a new icon set + */ public class CreateAssetSetWizard extends Wizard implements INewWizard { - private ChooseAssetTypePage chooseAssetPage; - private ConfigureAssetSetPage configureAssetPage; + private ChooseAssetTypePage mChooseAssetPage; + private ConfigureAssetSetPage mConfigureAssetPage; private IProject mInitialProject; + private List<IResource> mCreatedFiles; + /** Creates a new asset set wizard */ public CreateAssetSetWizard() { setWindowTitle("Create Asset Set"); } @Override public void addPages() { - chooseAssetPage = new ChooseAssetTypePage(); - chooseAssetPage.setProject(mInitialProject); - configureAssetPage = new ConfigureAssetSetPage(); + mChooseAssetPage = new ChooseAssetTypePage(); + mChooseAssetPage.setProject(mInitialProject); + mConfigureAssetPage = new ConfigureAssetSetPage(); + + addPage(mChooseAssetPage); + addPage(mConfigureAssetPage); + } - addPage(chooseAssetPage); - addPage(configureAssetPage); + String getBaseName() { + return mChooseAssetPage.getOutputName(); } @Override public boolean performFinish() { - String name = chooseAssetPage.getOutputName(); - Map<String, BufferedImage> previews = configureAssetPage.generatePreviews(); + Map<String, Map<String, BufferedImage>> categories = + mConfigureAssetPage.generateImages(false); + IProject project = getProject(); // Write out the images into the project boolean yesToAll = false; - List<IFile> createdFiles = new ArrayList<IFile>(); - for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) { - String id = entry.getKey(); - - IPath dest = new Path(WS_RESOURCES + WS_SEP + "drawable-" //$NON-NLS-1$ - + id + WS_SEP + name + DOT_PNG); - IFile file = project.getFile(dest); - if (file.exists()) { - // Warn that the file already exists and ask the user what to do - if (!yesToAll) { - MessageDialog dialog = new MessageDialog(null, "File Already Exists", null, - String.format("%1$s already exists.\nWould you like to replace it?", - file.getProjectRelativePath().toOSString()), - MessageDialog.QUESTION, new String[] { - // Yes will be moved to the end because it's the default - "Yes", "No", "Cancel", "Yes to All" - }, 0); - int result = dialog.open(); - switch (result) { - case 0: - // Yes - break; - case 3: - // Yes to all - yesToAll = true; - break; - case 1: - // No - continue; - case SWT.DEFAULT: - case 2: - // Cancel - return false; + mCreatedFiles = new ArrayList<IResource>(); + + for (Map<String, BufferedImage> previews : categories.values()) { + for (Map.Entry<String, BufferedImage> entry : previews.entrySet()) { + String relativePath = entry.getKey(); + IPath dest = new Path(relativePath); + IFile file = project.getFile(dest); + if (file.exists()) { + // Warn that the file already exists and ask the user what to do + if (!yesToAll) { + MessageDialog dialog = new MessageDialog(null, "File Already Exists", null, + String.format( + "%1$s already exists.\nWould you like to replace it?", + file.getProjectRelativePath().toOSString()), + MessageDialog.QUESTION, new String[] { + // Yes will be moved to the end because it's the default + "Yes", "No", "Cancel", "Yes to All" + }, 0); + int result = dialog.open(); + switch (result) { + case 0: + // Yes + break; + case 3: + // Yes to all + yesToAll = true; + break; + case 1: + // No + continue; + case SWT.DEFAULT: + case 2: + // Cancel + return false; + } + } + + try { + file.delete(true, new NullProgressMonitor()); + } catch (CoreException e) { + AdtPlugin.log(e, null); } } + NewXmlFileWizard.createWsParentDirectory(file.getParent()); + BufferedImage image = entry.getValue(); + + ByteArrayOutputStream stream = new ByteArrayOutputStream(); try { - file.delete(true, new NullProgressMonitor()); + ImageIO.write(image, "PNG", stream); //$NON-NLS-1$ + byte[] bytes = stream.toByteArray(); + InputStream is = new ByteArrayInputStream(bytes); + file.create(is, true /*force*/, null /*progress*/); + mCreatedFiles.add(file); + } catch (IOException e) { + AdtPlugin.log(e, null); } catch (CoreException e) { AdtPlugin.log(e, null); } - } - NewXmlFileWizard.createWsParentDirectory(file.getParent()); - BufferedImage image = entry.getValue(); - - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - try { - ImageIO.write(image, "PNG", stream); //$NON-NLS-1$ - byte[] bytes = stream.toByteArray(); - InputStream is = new ByteArrayInputStream(bytes); - file.create(is, true /*force*/, null /*progress*/); - createdFiles.add(file); - } catch (IOException e) { - AdtPlugin.log(e, null); - } catch (CoreException e) { - AdtPlugin.log(e, null); - } - - try { - file.getParent().refreshLocal(1, new NullProgressMonitor()); - } catch (CoreException e) { - AdtPlugin.log(e, null); + try { + file.getParent().refreshLocal(1, new NullProgressMonitor()); + } catch (CoreException e) { + AdtPlugin.log(e, null); + } } } + // Finally select the files themselves + selectFiles(project, mCreatedFiles); + + return true; + } + + private void selectFiles(IProject project, List<? extends IResource> createdFiles) { // Attempt to select the newly created files in the Package Explorer IWorkbench workbench = AdtPlugin.getDefault().getWorkbench(); IWorkbenchPage page = workbench.getActiveWorkbenchWindow().getActivePage(); @@ -172,50 +187,85 @@ public class CreateAssetSetWizard extends Wizard implements INewWizard { } catch (CoreException e) { AdtPlugin.log(e, null); } - ISelectionProvider provider = site.getSelectionProvider(); + final ISelectionProvider provider = site.getSelectionProvider(); if (provider != null) { List<TreePath> pathList = new ArrayList<TreePath>(); - for (IFile file : createdFiles) { + for (IResource file : createdFiles) { // Create a TreePath for the given file, // which should be the JavaProject, followed by the folders down to // the final file. List<Object> segments = new ArrayList<Object>(); segments.add(file); IContainer folder = file.getParent(); - segments.add(folder); - // res folder - folder = folder.getParent(); - segments.add(folder); + if (folder != null && !(folder instanceof IProject)) { + segments.add(folder); + // res folder + folder = folder.getParent(); + if (folder != null && !(folder instanceof IProject)) { + segments.add(folder); + } + } // project segments.add(javaProject); Collections.reverse(segments); TreePath path = new TreePath(segments.toArray()); pathList.add(path); + + // IDEA: Maybe normalize the files backwards (IFile objects aren't unique) + // by maybe using the package explorer icons instead } TreePath[] paths = pathList.toArray(new TreePath[pathList.size()]); - provider.setSelection(new TreeSelection(paths)); + final TreeSelection selection = new TreeSelection(paths); + + provider.setSelection(selection); + + // Workaround: The above doesn't always work; it will frequently select + // some siblings of the real files. I've tried a number of workarounds: + // normalizing the IFile objects by looking up the canonical ones via + // their relative paths from the project; deferring execution with + // Display.asyncRun; first calling select on the parents, etc. + // However, it turns out a simple workaround works best: Calling this + // method TWICE. The first call seems to expand all the necessary parents, + // and the second call ensures that the correct children are selected! + provider.setSelection(selection); + + viewPart.setFocus(); } } - - return true; } + /** + * Returns the project to be used by the wizard (which may differ from the + * project initialized by {@link #init(IWorkbench, IStructuredSelection)} or + * set by {@link #setProject(IProject)} if the user changes the project + * in the first page of the wizard. + */ IProject getProject() { - if (chooseAssetPage != null) { - return chooseAssetPage.getProject(); + if (mChooseAssetPage != null) { + return mChooseAssetPage.getProject(); } else { return mInitialProject; } } + /** Sets the initial project to be used by the wizard */ + void setProject(IProject project) { + mInitialProject = project; + } + + /** Returns the {@link AssetType} to create */ + AssetType getAssetType() { + return mChooseAssetPage.getAssetType(); + } + public void init(IWorkbench workbench, IStructuredSelection selection) { setHelpAvailable(false); mInitialProject = guessProject(selection); - if (chooseAssetPage != null) { - chooseAssetPage.setProject(mInitialProject); + if (mChooseAssetPage != null) { + mChooseAssetPage.setProject(mInitialProject); } } @@ -276,4 +326,14 @@ public class CreateAssetSetWizard extends Wizard implements INewWizard { return null; } + + /** + * Returns the list of files created by the wizard. This method will return + * null if {@link #performFinish()} has not yet been called. + * + * @return a list of files created by the wizard, or null + */ + List<IResource> getCreatedFiles() { + return mCreatedFiles; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/OpenCreateAssetSetWizardAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/OpenCreateAssetSetWizardAction.java new file mode 100644 index 0000000..09cc325 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/assetstudio/OpenCreateAssetSetWizardAction.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.internal.assetstudio; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.ui.actions.AbstractOpenWizardAction; +import org.eclipse.ui.INewWizard; + +import java.util.List; + +/** An action for opening the Create Icon Set wizard */ +public class OpenCreateAssetSetWizardAction extends AbstractOpenWizardAction { + private IProject mProject; + private CreateAssetSetWizard mWizard; + + /** + * Creates a new {@link #OpenCreateAssetSetWizardAction} instance + * + * @param project the initial project to associate with the wizard + */ + public OpenCreateAssetSetWizardAction(IProject project) { + mProject = project; + } + + + @Override + protected INewWizard createWizard() throws CoreException { + mWizard = new CreateAssetSetWizard(); + mWizard.setProject(mProject); + return mWizard; + } + + /** + * Returns the list of files created by the wizard. Must only be called + * after this action's {@link #run()} method has been called. May return + * null if the user cancels out of the wizard. + * + * @return a list of files created by the wizard, or null + */ + public List<IResource> getCreatedFiles() { + return mWizard.getCreatedFiles(); + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectChooserHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectChooserHelper.java index 27b26bf..ce996f9 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectChooserHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/project/ProjectChooserHelper.java @@ -29,7 +29,15 @@ import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.ui.JavaElementLabelProvider; import org.eclipse.jface.viewers.ILabelProvider; import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.dialogs.ElementListSelectionDialog; /** @@ -194,4 +202,78 @@ public class ProjectChooserHelper { } return iproject; } + + /** + * A selector button for showing the currently selected project and for + * changing the selection + */ + public static class ProjectButton extends Button implements SelectionListener { + /** Currently chosen project, or null when no project has been initialized or selected */ + private IProject mProject; + + /** + * Creates a new project selector button + * + * @param parent parent composite to add the button to + * @param initialProject the initial project to select, or null (which + * will show a "Please Choose Project..." label instead.) + */ + public ProjectButton(Composite parent, IProject initialProject) { + super(parent, SWT.BORDER | SWT.FLAT); + mProject = initialProject; + setProjectLabel(initialProject); + addSelectionListener(this); + } + + /** + * Returns the project selected by this chooser (or the initial project + * passed to the constructor if the user did not change it) + * + * @return the selected project + */ + public IProject getSelectedProject() { + return mProject; + } + + /** Updates the selection with the given project */ + private void setProjectLabel(IProject project) { + ILabelProvider labelProvider = new JavaElementLabelProvider( + JavaElementLabelProvider.SHOW_DEFAULT); + if (project == null) { + setText("Choose Project..."); + ISharedImages sharedImages = PlatformUI.getWorkbench().getSharedImages(); + Image errorImage = sharedImages.getImage(ISharedImages.IMG_OBJS_ERROR_TSK); + setImage(errorImage); + } else { + setText(labelProvider.getText(project)); + Image projectImage = labelProvider.getImage(project); + if (projectImage != null) { + setImage(projectImage); + } + } + } + + /** + * Click handler for the button: Open the {@link ProjectChooserHelper} + * dialog for selecting a new project. + */ + public void widgetSelected(SelectionEvent e) { + ProjectChooserHelper helper = + new ProjectChooserHelper(getShell(), null /* filter */); + IJavaProject p = helper.chooseJavaProject(getText(), + "Please select the target project"); + if (p != null) { + mProject = p.getProject(); + setProjectLabel(mProject); + } + } + + public void widgetDefaultSelected(SelectionEvent e) { + } + + @Override + protected void checkSubclass() { + // Disable the check that prevents subclassing of SWT components + } + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java index 5228ff9..f9d915b 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/ResourceHelper.java @@ -287,16 +287,30 @@ public class ResourceHelper { return false; } - // We can create all value types - if (isValueBasedResourceType(type)) { - return true; - } + return canCreateResourceType(type); + } - // We can create -some- file-based types - those supported by the New XML wizard: - for (ResourceFolderType folderType : FolderTypeRelationship.getRelatedFolders(type)) { - if (NewXmlFileWizard.canCreateXmlFile(folderType)) { - return true; - } + return false; + } + + /** + * Returns true if this class can create resources of the given resource + * type + * + * @param type the type of resource to be created + * @return true if the {@link #createResource} method can create resources + * of this type (provided the name parameter is also valid) + */ + public static boolean canCreateResourceType(ResourceType type) { + // We can create all value types + if (isValueBasedResourceType(type)) { + return true; + } + + // We can create -some- file-based types - those supported by the New XML wizard: + for (ResourceFolderType folderType : FolderTypeRelationship.getRelatedFolders(type)) { + if (NewXmlFileWizard.canCreateXmlFile(folderType)) { + return true; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java index 856928d..75b78f5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/ui/ResourceChooser.java @@ -20,6 +20,8 @@ package com.android.ide.eclipse.adt.internal.ui; import com.android.ide.common.resources.ResourceItem; import com.android.ide.common.resources.ResourceRepository; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AdtUtils; +import com.android.ide.eclipse.adt.internal.assetstudio.OpenCreateAssetSetWizardAction; import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringRefactoring; import com.android.ide.eclipse.adt.internal.refactorings.extractstring.ExtractStringWizard; import com.android.ide.eclipse.adt.internal.resources.ResourceHelper; @@ -29,10 +31,12 @@ import com.android.util.Pair; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.IDialogConstants; import org.eclipse.jface.dialogs.IInputValidator; +import org.eclipse.jface.dialogs.InputDialog; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.window.Window; import org.eclipse.ltk.ui.refactoring.RefactoringWizard; @@ -57,6 +61,7 @@ import org.eclipse.ui.dialogs.SelectionStatusDialog; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -77,6 +82,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog { private Button mProjectButton; private Button mSystemButton; private Button mNewButton; + private Button mNewIconButton; private String mCurrentResource; private final IProject mProject; private IInputValidator mInputValidator; @@ -191,7 +197,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog { super.widgetSelected(e); if (mProjectButton.getSelection()) { setupResourceList(); - mNewButton.setEnabled(true); + updateNewButton(false /*isSystem*/); updatePreview(); } } @@ -204,7 +210,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog { super.widgetSelected(e); if (mSystemButton.getSelection()) { setupResourceList(); - mNewButton.setEnabled(false); + updateNewButton(true /*isSystem*/); updatePreview(); } } @@ -219,28 +225,68 @@ public class ResourceChooser extends AbstractElementListSelectionDialog { mNewButton = new Button(top, SWT.NONE); String title = String.format("New %1$s...", mResourceType.getDisplayName()); + if (mResourceType == ResourceType.DRAWABLE) { + title = "Create New Icon..."; + } mNewButton.setText(title); - // We only support adding new values right now - mNewButton.setEnabled(ResourceHelper.isValueBasedResourceType(mResourceType)); - mNewButton.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { super.widgetSelected(e); if (mResourceType == ResourceType.STRING) { - createNewString(); - } else { - assert ResourceHelper.isValueBasedResourceType(mResourceType); - String newName = createNewValue(mResourceType); - if (newName != null) { + // Special case: Use Extract String refactoring wizard UI + String newName = createNewString(); + selectAddedItem(newName); + } else if (mResourceType == ResourceType.DRAWABLE) { + // Special case: Use the "Create Icon Set" wizard + OpenCreateAssetSetWizardAction action = + new OpenCreateAssetSetWizardAction(mProject); + action.run(); + List<IResource> files = action.getCreatedFiles(); + if (files != null && files.size() > 0) { + String newName = AdtUtils.stripAllExtensions(files.get(0).getName()); // Recompute the "current resource" to select the new id ResourceItem[] items = setupResourceList(); selectItemName(newName, items); } + } else { + if (ResourceHelper.isValueBasedResourceType(mResourceType)) { + String newName = createNewValue(mResourceType); + selectAddedItem(newName); + } else { + String newName = createNewFile(mResourceType); + selectAddedItem(newName); + } } } + + private void selectAddedItem(String newName) { + // Recompute the "current resource" to select the new id + ResourceItem[] items = setupResourceList(); + + // Ensure that the name is in the list. There's a delay after + // an item is added (until the builder runs and processes the delta) + // so if it's not in the list, add it + boolean found = false; + for (ResourceItem item : items) { + if (newName.equals(item.getName())) { + found = true; + break; + } + } + if (!found) { + ResourceItem[] newItems = new ResourceItem[items.length + 1]; + System.arraycopy(items, 0, newItems, 0, items.length); + newItems[items.length] = new ResourceItem(newName); + items = newItems; + Arrays.sort(items); + setListElements(items); + } + + selectItemName(newName, items); + } }); } @@ -273,6 +319,38 @@ public class ResourceChooser extends AbstractElementListSelectionDialog { } } + private String createNewFile(ResourceType type) { + // Show a name/value dialog entering the key name and the value + Shell shell = AdtPlugin.getDisplay().getActiveShell(); + if (shell == null) { + return null; + } + + ResourceNameValidator validator = ResourceNameValidator.create(true /*allowXmlExtension*/, + mProject, mResourceType); + InputDialog d = new InputDialog( + AdtPlugin.getDisplay().getActiveShell(), + "Enter name", // title + "Enter name", + "", //$NON-NLS-1$ + validator); + if (d.open() == Window.OK) { + String name = d.getValue().trim(); + if (name.length() == 0) { + return null; + } + + Pair<IFile, IRegion> resource = ResourceHelper.createResource(mProject, type, name, + null); + if (resource != null) { + return name; + } + } + + return null; + } + + private String createNewValue(ResourceType type) { // Show a name/value dialog entering the key name and the value Shell shell = AdtPlugin.getDisplay().getActiveShell(); @@ -298,7 +376,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog { return null; } - private void createNewString() { + private String createNewString() { ExtractStringRefactoring ref = new ExtractStringRefactoring( mProject, true /*enforceNew*/); RefactoringWizard wizard = new ExtractStringWizard(ref, mProject); @@ -307,16 +385,13 @@ public class ResourceChooser extends AbstractElementListSelectionDialog { IWorkbench w = PlatformUI.getWorkbench(); if (op.run(w.getDisplay().getActiveShell(), wizard.getDefaultPageTitle()) == IDialogConstants.OK_ID) { - - // Recompute the "current resource" to select the new id - ResourceItem[] items = setupResourceList(); - - // select it if possible - selectItemName(ref.getXmlStringId(), items); + return ref.getXmlStringId(); } } catch (InterruptedException ex) { // Interrupted. Pass. } + + return null; } /** @@ -389,7 +464,7 @@ public class ResourceChooser extends AbstractElementListSelectionDialog { // Update the repository selection mProjectButton.setSelection(!isSystem); mSystemButton.setSelection(isSystem); - mNewButton.setEnabled(!isSystem); + updateNewButton(isSystem); // Update the list ResourceItem[] items = setupResourceList(); @@ -400,6 +475,10 @@ public class ResourceChooser extends AbstractElementListSelectionDialog { } } + private void updateNewButton(boolean isSystem) { + mNewButton.setEnabled(!isSystem && ResourceHelper.canCreateResourceType(mResourceType)); + } + /** Dialog asking for a Name/Value pair */ private class NameValueDialog extends SelectionStatusDialog implements Listener { private org.eclipse.swt.widgets.Text mNameText; diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java index 6f56f73..f4daf59 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/AdtUtilsTest.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt; import junit.framework.TestCase; +@SuppressWarnings("javadoc") public class AdtUtilsTest extends TestCase { public void testEndsWithIgnoreCase() { assertTrue(AdtUtils.endsWithIgnoreCase("foo", "foo")); @@ -36,6 +37,22 @@ public class AdtUtilsTest extends TestCase { assertEquals("foobar", AdtUtils.stripWhitespace(" foo bar \n\t")); } + public void testStripAllExtensions() { + assertEquals("", AdtUtils.stripAllExtensions("")); + assertEquals("foobar", AdtUtils.stripAllExtensions("foobar")); + assertEquals("foobar", AdtUtils.stripAllExtensions("foobar.png")); + assertEquals("foobar", AdtUtils.stripAllExtensions("foobar.9.png")); + assertEquals(".profile", AdtUtils.stripAllExtensions(".profile")); + } + + public void testStripLastExtension() { + assertEquals("", AdtUtils.stripLastExtension("")); + assertEquals("foobar", AdtUtils.stripLastExtension("foobar")); + assertEquals("foobar", AdtUtils.stripLastExtension("foobar.png")); + assertEquals("foobar.9", AdtUtils.stripLastExtension("foobar.9.png")); + assertEquals(".profile", AdtUtils.stripLastExtension(".profile")); + } + public void testCapitalize() { assertEquals("UPPER", AdtUtils.capitalize("UPPER")); assertEquals("Lower", AdtUtils.capitalize("lower")); diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java index d05e54f..6340528 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/resources/ResourceHelperTest.java @@ -32,6 +32,7 @@ import junit.framework.TestCase; /** * Test ResourceHelper */ +@SuppressWarnings("javadoc") public class ResourceHelperTest extends TestCase { /** @@ -170,6 +171,16 @@ public class ResourceHelperTest extends TestCase { assertFalse(ResourceHelper.canCreateResource("@android:color/foo")); } + public void testCanCreateResourceType() throws Exception { + assertTrue(ResourceHelper.canCreateResourceType(ResourceType.LAYOUT)); + assertTrue(ResourceHelper.canCreateResourceType(ResourceType.STRING)); + assertTrue(ResourceHelper.canCreateResourceType(ResourceType.DIMEN)); + assertTrue(ResourceHelper.canCreateResourceType(ResourceType.COLOR)); + + assertFalse(ResourceHelper.canCreateResourceType(ResourceType.RAW)); + assertFalse(ResourceHelper.canCreateResourceType(ResourceType.XML)); + } + public void testStyleToTheme() throws Exception { assertEquals("Foo", ResourceHelper.styleToTheme("Foo")); assertEquals("Theme", ResourceHelper.styleToTheme("@android:style/Theme")); |