/* * 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.editors.drawable; import static com.android.SdkConstants.ANDROID_NS_NAME; import static com.android.SdkConstants.ANDROID_URI; import com.android.ide.common.api.IAttributeInfo.Format; import com.android.ide.common.resources.platform.AttributeInfo; import com.android.ide.common.resources.platform.DeclareStyleableInfo; import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors; import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider; import com.android.ide.eclipse.adt.internal.editors.descriptors.ReferenceAttributeDescriptor; import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor; import com.android.resources.ResourceType; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Descriptors for /res/drawable files */ public class DrawableDescriptors implements IDescriptorProvider { private static final String SDK_URL_BASE = "http://d.android.com/guide/topics/resources/"; //$NON-NLS-1$ /** The root element descriptor */ private ElementDescriptor mDescriptor; /** The root element descriptors */ private ElementDescriptor[] mRootDescriptors; private Map<String, ElementDescriptor> nameToDescriptor; /** @return the root descriptor. */ @Override public ElementDescriptor getDescriptor() { if (mDescriptor == null) { mDescriptor = new ElementDescriptor("", getRootElementDescriptors()); //$NON-NLS-1$ } return mDescriptor; } @Override public ElementDescriptor[] getRootElementDescriptors() { return mRootDescriptors; } /** * Returns a descriptor for the given root tag name * * @param tag the tag name to look up a descriptor for * @return a descriptor with the given tag name */ public ElementDescriptor getElementDescriptor(String tag) { if (nameToDescriptor == null) { nameToDescriptor = new HashMap<String, ElementDescriptor>(); for (ElementDescriptor descriptor : getRootElementDescriptors()) { nameToDescriptor.put(descriptor.getXmlName(), descriptor); } } ElementDescriptor descriptor = nameToDescriptor.get(tag); if (descriptor == null) { descriptor = getDescriptor(); } return descriptor; } public synchronized void updateDescriptors(Map<String, DeclareStyleableInfo> styleMap) { XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_NAME, ANDROID_URI); List<ElementDescriptor> descriptors = new ArrayList<ElementDescriptor>(); AnimatorDescriptors.addElement(descriptors, styleMap, "animation-list", "Animation List", "AnimationDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "An animation defined in XML that shows a sequence of images in " + "order (like a film)", SDK_URL_BASE + "animation-resource.html#Frame", xmlns, null, true /*mandatory*/); /* For some reason, android.graphics.drawable.AnimatedRotateDrawable is marked with @hide * so we should not register it and its attributes here. See issue #19248 for details. AnimatorDescriptors.addElement(descriptors, styleMap, "animated-rotate", "Animated Rotate", "AnimatedRotateDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ // Need docs null, // tooltip null, // sdk_url xmlns, null, true); */ AnimatorDescriptors.addElement(descriptors, styleMap, "bitmap", "BitMap", "BitmapDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "An XML bitmap is a resource defined in XML that points to a bitmap file. " + "The effect is an alias for a raw bitmap file. The XML can " + "specify additional properties for the bitmap such as " + "dithering and tiling.", SDK_URL_BASE + "drawable-resource.html#Bitmap", //$NON-NLS-1$ xmlns, null, true /* mandatory */); AnimatorDescriptors.addElement(descriptors, styleMap, "clip", "Clip", "ClipDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "An XML file that defines a drawable that clips another Drawable based on " + "this Drawable's current level value.", SDK_URL_BASE + "drawable-resource.html#Clip", //$NON-NLS-1$ xmlns, null, true /*mandatory*/); AnimatorDescriptors.addElement(descriptors, styleMap, "color", "Color", "ColorDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "XML resource that carries a color value (a hexadecimal color)", SDK_URL_BASE + "more-resources.html#Color", xmlns, null, true /*mandatory*/); AnimatorDescriptors.addElement(descriptors, styleMap, "inset", "Inset", "InsetDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "An XML file that defines a drawable that insets another drawable by a " + "specified distance. This is useful when a View needs a background " + "drawble that is smaller than the View's actual bounds.", SDK_URL_BASE + "drawable-resource.html#Inset", //$NON-NLS-1$ xmlns, null, true /*mandatory*/); // Layer list // An <item> in a <selector> or < ElementDescriptor layerItem = AnimatorDescriptors.addElement(null, styleMap, "item", "Item", "LayerDrawableItem", null, //$NON-NLS-1$ //$NON-NLS-3$ "Defines a drawable to place in the layer drawable, in a position " + "defined by its attributes. Must be a child of a <selector> " + "element. Accepts child <bitmap> elements.", SDK_URL_BASE + "drawable-resource.html#LayerList", //$NON-NLS-1$ null, /* extra attribute */ null, /* This is wrong -- we can now embed any above drawable (but without xmlns as extra) */ false /*mandatory*/); ElementDescriptor[] layerChildren = layerItem != null ? new ElementDescriptor[] { layerItem } : null; AnimatorDescriptors.addElement(descriptors, styleMap, "layer-list", "Layer List", "LayerDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "A Drawable that manages an array of other Drawables. These are drawn in " + "array order, so the element with the largest index is be drawn on top.", SDK_URL_BASE + "drawable-resource.html#LayerList", //$NON-NLS-1$ xmlns, layerChildren, true /*mandatory*/); // Level list children ElementDescriptor levelListItem = AnimatorDescriptors.addElement(null, styleMap, "item", "Item", "LevelListDrawableItem", null, //$NON-NLS-1$ //$NON-NLS-3$ "Defines a drawable to use at a certain level.", SDK_URL_BASE + "drawable-resource.html#LevelList", //$NON-NLS-1$ null, /* extra attribute */ null, /* no further children */ // TODO: The inflation code seems to show that all drawables can be nested here! false /*mandatory*/); AnimatorDescriptors.addElement(descriptors, styleMap, "level-list", "Level List", "LevelListDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "An XML file that defines a drawable that manages a number of alternate " + "Drawables, each assigned a maximum numerical value", SDK_URL_BASE + "drawable-resource.html#LevelList", //$NON-NLS-1$ xmlns, levelListItem != null ? new ElementDescriptor[] { levelListItem } : null, true /*mandatory*/); // Not yet supported //addElement(descriptors, styleMap, "mipmap", "Mipmap", "MipmapDrawable", null, // null /* tooltip */, // null /* sdk_url */, // xmlns, null, true /*mandatory*/); AnimatorDescriptors.addElement(descriptors, styleMap, "nine-patch", "Nine Patch", "NinePatchDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "A PNG file with stretchable regions to allow image resizing " + "based on content (.9.png).", SDK_URL_BASE + "drawable-resource.html#NinePatch", //$NON-NLS-1$ xmlns, null, true /*mandatory*/); AnimatorDescriptors.addElement(descriptors, styleMap, "rotate", "Rotate", "RotateDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ // Need docs null /* tooltip */, null /* sdk_url */, xmlns, null, true /*mandatory*/); AnimatorDescriptors.addElement(descriptors, styleMap, "scale", "Shape", "ScaleDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "An XML file that defines a drawable that changes the size of another Drawable " + "based on its current level value.", SDK_URL_BASE + "drawable-resource.html#Scale", //$NON-NLS-1$ xmlns, null, true /*mandatory*/); // Selector children ElementDescriptor selectorItem = AnimatorDescriptors.addElement(null, styleMap, "item", "Item", "DrawableStates", null, //$NON-NLS-1$ //$NON-NLS-3$ "Defines a drawable to use during certain states, as described by " + "its attributes. Must be a child of a <selector> element.", SDK_URL_BASE + "drawable-resource.html#StateList", //$NON-NLS-1$ new ReferenceAttributeDescriptor( ResourceType.DRAWABLE, "drawable", ANDROID_URI, //$NON-NLS-1$ new AttributeInfo("drawable", Format.REFERENCE_SET)) .setTooltip("Reference to a drawable resource."), null, /* This is wrong -- we can now embed any above drawable (but without xmlns as extra) */ false /*mandatory*/); AnimatorDescriptors.addElement(descriptors, styleMap, "selector", "Selector", "StateListDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "An XML file that references different bitmap graphics for different states " + "(for example, to use a different image when a button is pressed).", SDK_URL_BASE + "drawable-resource.html#StateList", //$NON-NLS-1$ xmlns, selectorItem != null ? new ElementDescriptor[] { selectorItem } : null, true /*mandatory*/); // Shape // Shape children List<ElementDescriptor> shapeChildren = new ArrayList<ElementDescriptor>(); // Selector children AnimatorDescriptors.addElement(shapeChildren, styleMap, "size", "Size", "GradientDrawableSize", null, //$NON-NLS-1$ //$NON-NLS-3$ null /* tooltip */, null /* sdk_url */, null /* extra attribute */, null /* children */, false /* mandatory */); AnimatorDescriptors.addElement(shapeChildren, styleMap, "gradient", "Gradient", "GradientDrawableGradient", null, //$NON-NLS-1$ //$NON-NLS-3$ null /* tooltip */, null /* sdk_url */, null /* extra attribute */, null /* children */, false /* mandatory */); AnimatorDescriptors.addElement(shapeChildren, styleMap, "solid", "Solid", "GradientDrawableSolid", null, //$NON-NLS-1$ //$NON-NLS-3$ null /* tooltip */, null /* sdk_url */, null /* extra attribute */, null /* children */, false /* mandatory */); AnimatorDescriptors.addElement(shapeChildren, styleMap, "stroke", "Stroke", "GradientDrawableStroke", null, //$NON-NLS-1$ //$NON-NLS-3$ null /* tooltip */, null /* sdk_url */, null /* extra attribute */, null /* children */, false /* mandatory */); AnimatorDescriptors.addElement(shapeChildren, styleMap, "corners", "Corners", "DrawableCorners", null, //$NON-NLS-1$ //$NON-NLS-3$ null /* tooltip */, null /* sdk_url */, null /* extra attribute */, null /* children */, false /* mandatory */); AnimatorDescriptors.addElement(shapeChildren, styleMap, "padding", "Padding", "GradientDrawablePadding", null, //$NON-NLS-1$ //$NON-NLS-3$ null /* tooltip */, null /* sdk_url */, null /* extra attribute */, null /* children */, false /* mandatory */); AnimatorDescriptors.addElement(descriptors, styleMap, "shape", "Shape", //$NON-NLS-1$ // The documentation says that a <shape> element creates a ShapeDrawable, // but ShapeDrawable isn't finished and the code currently creates // a GradientDrawable. //"ShapeDrawable", //$NON-NLS-1$ "GradientDrawable", //$NON-NLS-1$ null, "An XML file that defines a geometric shape, including colors and gradients.", SDK_URL_BASE + "drawable-resource.html#Shape", //$NON-NLS-1$ xmlns, // These are the GradientDrawable children, not the ShapeDrawable children shapeChildren.toArray(new ElementDescriptor[shapeChildren.size()]), true /*mandatory*/); AnimatorDescriptors.addElement(descriptors, styleMap, "transition", "Transition", "TransitionDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$ "An XML file that defines a drawable that can cross-fade between two " + "drawable resources. Each drawable is represented by an <item> " + "element inside a single <transition> element. No more than two " + "items are supported. To transition forward, call startTransition(). " + "To transition backward, call reverseTransition().", SDK_URL_BASE + "drawable-resource.html#Transition", //$NON-NLS-1$ xmlns, layerChildren, // children: a TransitionDrawable is a LayerDrawable true /*mandatory*/); mRootDescriptors = descriptors.toArray(new ElementDescriptor[descriptors.size()]); // A <selector><item> can contain any of the top level drawables if (selectorItem != null) { selectorItem.setChildren(mRootDescriptors); } // Docs says it can accept <bitmap> but code comment suggests any is possible; // test and either use this or just { bitmap } if (layerItem != null) { layerItem.setChildren(mRootDescriptors); } } }