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