Home | History | Annotate | Download | only in drawable
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.ide.eclipse.adt.internal.editors.drawable;
     17 
     18 import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
     19 
     20 import com.android.ide.common.api.IAttributeInfo.Format;
     21 import com.android.ide.common.resources.platform.AttributeInfo;
     22 import com.android.ide.common.resources.platform.DeclareStyleableInfo;
     23 import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors;
     24 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
     25 import com.android.ide.eclipse.adt.internal.editors.descriptors.IDescriptorProvider;
     26 import com.android.ide.eclipse.adt.internal.editors.descriptors.ReferenceAttributeDescriptor;
     27 import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
     28 import com.android.resources.ResourceType;
     29 import com.android.sdklib.SdkConstants;
     30 
     31 import java.util.ArrayList;
     32 import java.util.HashMap;
     33 import java.util.List;
     34 import java.util.Map;
     35 
     36 /**
     37  * Descriptors for /res/drawable files
     38  */
     39 public class DrawableDescriptors implements IDescriptorProvider {
     40     private static final String SDK_URL_BASE =
     41         "http://d.android.com/guide/topics/resources/"; //$NON-NLS-1$
     42 
     43     /** The root element descriptor */
     44     private ElementDescriptor mDescriptor;
     45     /** The root element descriptors */
     46     private ElementDescriptor[] mRootDescriptors;
     47     private Map<String, ElementDescriptor> nameToDescriptor;
     48 
     49     /** @return the root descriptor. */
     50     public ElementDescriptor getDescriptor() {
     51         if (mDescriptor == null) {
     52             mDescriptor = new ElementDescriptor("", getRootElementDescriptors()); //$NON-NLS-1$
     53         }
     54 
     55         return mDescriptor;
     56     }
     57 
     58     public ElementDescriptor[] getRootElementDescriptors() {
     59         return mRootDescriptors;
     60     }
     61 
     62     /**
     63      * Returns a descriptor for the given root tag name
     64      *
     65      * @param tag the tag name to look up a descriptor for
     66      * @return a descriptor with the given tag name
     67      */
     68     public ElementDescriptor getElementDescriptor(String tag) {
     69         if (nameToDescriptor == null) {
     70             nameToDescriptor = new HashMap<String, ElementDescriptor>();
     71             for (ElementDescriptor descriptor : getRootElementDescriptors()) {
     72                 nameToDescriptor.put(descriptor.getXmlName(), descriptor);
     73             }
     74         }
     75 
     76         ElementDescriptor descriptor = nameToDescriptor.get(tag);
     77         if (descriptor == null) {
     78             descriptor = getDescriptor();
     79         }
     80         return descriptor;
     81     }
     82 
     83     public synchronized void updateDescriptors(Map<String, DeclareStyleableInfo> styleMap) {
     84         XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_NAME,
     85                 SdkConstants.NS_RESOURCES);
     86         Format[] referenceFormat = new Format[] { Format.REFERENCE };
     87 
     88         List<ElementDescriptor> descriptors = new ArrayList<ElementDescriptor>();
     89 
     90         AnimatorDescriptors.addElement(descriptors, styleMap,
     91             "animation-list", "Animation List", "AnimationDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
     92             "An animation defined in XML that shows a sequence of images in "
     93                 + "order (like a film)",
     94             SDK_URL_BASE + "animation-resource.html#Frame",
     95             xmlns, null, true /*mandatory*/);
     96 
     97         AnimatorDescriptors.addElement(descriptors, styleMap,
     98             "animated-rotate", "Animated Rotate", "AnimatedRotateDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
     99             // Need docs
    100             null /* tooltip */,
    101             null /* sdk_url */,
    102             xmlns, null, true /*mandatory*/);
    103 
    104         AnimatorDescriptors.addElement(descriptors, styleMap,
    105             "bitmap", "BitMap", "BitmapDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    106             "An XML bitmap is a resource defined in XML that points to a bitmap file. "
    107                     + "The effect is an alias for a raw bitmap file. The XML can "
    108                     + "specify additional properties for the bitmap such as "
    109                     + "dithering and tiling.",
    110             SDK_URL_BASE + "drawable-resource.html#Bitmap", //$NON-NLS-1$
    111             xmlns, null, true /* mandatory */);
    112 
    113         AnimatorDescriptors.addElement(descriptors, styleMap,
    114             "clip", "Clip", "ClipDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    115             "An XML file that defines a drawable that clips another Drawable based on "
    116                 + "this Drawable's current level value.",
    117             SDK_URL_BASE + "drawable-resource.html#Clip", //$NON-NLS-1$
    118             xmlns, null, true /*mandatory*/);
    119 
    120 
    121         AnimatorDescriptors.addElement(descriptors, styleMap,
    122             "color", "Color", "ColorDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    123             "XML resource that carries a color value (a hexadecimal color)",
    124             SDK_URL_BASE + "more-resources.html#Color",
    125             xmlns, null, true /*mandatory*/);
    126 
    127         AnimatorDescriptors.addElement(descriptors, styleMap,
    128             "inset", "Inset", "InsetDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    129             "An XML file that defines a drawable that insets another drawable by a "
    130                 + "specified distance. This is useful when a View needs a background "
    131                 + "drawble that is smaller than the View's actual bounds.",
    132             SDK_URL_BASE + "drawable-resource.html#Inset", //$NON-NLS-1$
    133             xmlns, null, true /*mandatory*/);
    134 
    135         // Layer list
    136 
    137         // An <item> in a <selector> or <
    138         ElementDescriptor layerItem = AnimatorDescriptors.addElement(null, styleMap,
    139                 "item", "Item", "LayerDrawableItem", null, //$NON-NLS-1$ //$NON-NLS-3$
    140                 "Defines a drawable to place in the layer drawable, in a position "
    141                     + "defined by its attributes. Must be a child of a <selector> "
    142                     + "element. Accepts child <bitmap> elements.",
    143                 SDK_URL_BASE + "drawable-resource.html#LayerList", //$NON-NLS-1$
    144                 null, /* extra attribute */
    145                 null, /* This is wrong -- we can now embed any above drawable
    146                             (but without xmlns as extra) */
    147                 false /*mandatory*/);
    148         ElementDescriptor[] layerChildren = layerItem != null
    149             ? new ElementDescriptor[] { layerItem } : null;
    150 
    151         AnimatorDescriptors.addElement(descriptors, styleMap,
    152             "layer-list", "Layer List", "LayerDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    153             "A Drawable that manages an array of other Drawables. These are drawn in "
    154                     + "array order, so the element with the largest index is be drawn on top.",
    155             SDK_URL_BASE + "drawable-resource.html#LayerList", //$NON-NLS-1$
    156             xmlns,
    157             layerChildren,
    158             true /*mandatory*/);
    159 
    160         // Level list children
    161         ElementDescriptor levelListItem = AnimatorDescriptors.addElement(null, styleMap,
    162             "item", "Item", "LevelListDrawableItem", null, //$NON-NLS-1$ //$NON-NLS-3$
    163             "Defines a drawable to use at a certain level.",
    164             SDK_URL_BASE + "drawable-resource.html#LevelList", //$NON-NLS-1$
    165             null, /* extra attribute */
    166             null, /* no further children */
    167             // TODO: The inflation code seems to show that all drawables can be nested here!
    168             false /*mandatory*/);
    169         AnimatorDescriptors.addElement(descriptors, styleMap,
    170             "level-list", "Level List", "LevelListDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    171             "An XML file that defines a drawable that manages a number of alternate "
    172                 + "Drawables, each assigned a maximum numerical value",
    173             SDK_URL_BASE + "drawable-resource.html#LevelList", //$NON-NLS-1$
    174             xmlns,
    175             levelListItem != null ? new ElementDescriptor[] { levelListItem } : null,
    176             true /*mandatory*/);
    177 
    178         // Not yet supported
    179         //addElement(descriptors, styleMap, "mipmap", "Mipmap", "MipmapDrawable", null,
    180         //        null /* tooltip */,
    181         //        null /* sdk_url */,
    182         //        xmlns, null, true /*mandatory*/);
    183 
    184         AnimatorDescriptors.addElement(descriptors, styleMap,
    185             "nine-patch", "Nine Patch", "NinePatchDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    186             "A PNG file with stretchable regions to allow image resizing "
    187                     + "based on content (.9.png).",
    188             SDK_URL_BASE + "drawable-resource.html#NinePatch", //$NON-NLS-1$
    189             xmlns, null, true /*mandatory*/);
    190 
    191         AnimatorDescriptors.addElement(descriptors, styleMap,
    192             "rotate", "Rotate", "RotateDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    193             // Need docs
    194             null /* tooltip */,
    195             null /* sdk_url */,
    196             xmlns, null, true /*mandatory*/);
    197 
    198         AnimatorDescriptors.addElement(descriptors, styleMap,
    199             "scale", "Shape", "ScaleDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    200             "An XML file that defines a drawable that changes the size of another Drawable "
    201                 + "based on its current level value.",
    202             SDK_URL_BASE + "drawable-resource.html#Scale", //$NON-NLS-1$
    203             xmlns, null, true /*mandatory*/);
    204 
    205         // Selector children
    206         ElementDescriptor selectorItem = AnimatorDescriptors.addElement(null, styleMap,
    207             "item", "Item", "DrawableStates", null, //$NON-NLS-1$ //$NON-NLS-3$
    208             "Defines a drawable to use during certain states, as described by "
    209                  + "its attributes. Must be a child of a <selector> element.",
    210             SDK_URL_BASE + "drawable-resource.html#StateList", //$NON-NLS-1$
    211             new ReferenceAttributeDescriptor(
    212                     ResourceType.DRAWABLE, "drawable", "drawable", //$NON-NLS-1$ //$NON-NLS-2$
    213                     SdkConstants.NS_RESOURCES,
    214                     "Reference to a drawable resource.",
    215                     new AttributeInfo("drawable", referenceFormat)),
    216             null, /* This is wrong -- we can now embed any above drawable
    217                         (but without xmlns as extra) */
    218             false /*mandatory*/);
    219 
    220         AnimatorDescriptors.addElement(descriptors, styleMap,
    221             "selector", "Selector", "StateListDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    222             "An XML file that references different bitmap graphics for different states "
    223                 + "(for example, to use a different image when a button is pressed).",
    224             SDK_URL_BASE + "drawable-resource.html#StateList", //$NON-NLS-1$
    225             xmlns,
    226             selectorItem != null ? new ElementDescriptor[] { selectorItem } : null,
    227             true /*mandatory*/);
    228 
    229         // Shape
    230         // Shape children
    231         List<ElementDescriptor> shapeChildren = new ArrayList<ElementDescriptor>();
    232         // Selector children
    233         AnimatorDescriptors.addElement(shapeChildren, styleMap,
    234             "size", "Size", "GradientDrawableSize", null, //$NON-NLS-1$ //$NON-NLS-3$
    235             null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
    236             null /* children */, false /* mandatory */);
    237         AnimatorDescriptors.addElement(shapeChildren, styleMap,
    238             "gradient", "Gradient", "GradientDrawableGradient", null, //$NON-NLS-1$ //$NON-NLS-3$
    239             null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
    240             null /* children */, false /* mandatory */);
    241         AnimatorDescriptors.addElement(shapeChildren, styleMap,
    242             "solid", "Solid", "GradientDrawableSolid", null, //$NON-NLS-1$ //$NON-NLS-3$
    243             null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
    244             null /* children */, false /* mandatory */);
    245         AnimatorDescriptors.addElement(shapeChildren, styleMap,
    246             "stroke", "Stroke", "GradientDrawableStroke", null, //$NON-NLS-1$ //$NON-NLS-3$
    247             null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
    248             null /* children */, false /* mandatory */);
    249         AnimatorDescriptors.addElement(shapeChildren, styleMap,
    250             "corners", "Corners", "DrawableCorners", null, //$NON-NLS-1$ //$NON-NLS-3$
    251             null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
    252             null /* children */, false /* mandatory */);
    253         AnimatorDescriptors.addElement(shapeChildren, styleMap,
    254             "padding", "Padding", "GradientDrawablePadding", null, //$NON-NLS-1$ //$NON-NLS-3$
    255             null /* tooltip */, null /* sdk_url */, null /* extra attribute */,
    256             null /* children */, false /* mandatory */);
    257 
    258         AnimatorDescriptors.addElement(descriptors, styleMap,
    259             "shape", "Shape", //$NON-NLS-1$
    260 
    261             // The documentation says that a <shape> element creates a ShapeDrawable,
    262             // but ShapeDrawable isn't finished and the code currently creates
    263             // a GradientDrawable.
    264             //"ShapeDrawable", //$NON-NLS-1$
    265             "GradientDrawable", //$NON-NLS-1$
    266 
    267             null,
    268             "An XML file that defines a geometric shape, including colors and gradients.",
    269             SDK_URL_BASE + "drawable-resource.html#Shape", //$NON-NLS-1$
    270             xmlns,
    271 
    272             // These are the GradientDrawable children, not the ShapeDrawable children
    273             shapeChildren.toArray(new ElementDescriptor[shapeChildren.size()]),
    274             true /*mandatory*/);
    275 
    276         AnimatorDescriptors.addElement(descriptors, styleMap,
    277             "transition", "Transition", "TransitionDrawable", null, //$NON-NLS-1$ //$NON-NLS-3$
    278             "An XML file that defines a drawable that can cross-fade between two "
    279                 + "drawable resources. Each drawable is represented by an <item> "
    280                 + "element inside a single <transition> element. No more than two "
    281                 + "items are supported. To transition forward, call startTransition(). "
    282                 + "To transition backward, call reverseTransition().",
    283             SDK_URL_BASE + "drawable-resource.html#Transition", //$NON-NLS-1$
    284             xmlns,
    285             layerChildren, // children: a TransitionDrawable is a LayerDrawable
    286             true /*mandatory*/);
    287 
    288         mRootDescriptors = descriptors.toArray(new ElementDescriptor[descriptors.size()]);
    289 
    290         // A <selector><item> can contain any of the top level drawables
    291         if (selectorItem != null) {
    292             selectorItem.setChildren(mRootDescriptors);
    293         }
    294         // Docs says it can accept <bitmap> but code comment suggests any is possible;
    295         // test and either use this or just { bitmap }
    296         if (layerItem != null) {
    297             layerItem.setChildren(mRootDescriptors);
    298         }
    299     }
    300 }
    301