Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
      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 
     17 package android.util;
     18 
     19 import com.android.ide.common.rendering.api.AttrResourceValue;
     20 import com.android.ide.common.rendering.api.RenderResources;
     21 import com.android.ide.common.rendering.api.ResourceValue;
     22 import com.android.internal.util.XmlUtils;
     23 import com.android.layoutlib.bridge.Bridge;
     24 import com.android.layoutlib.bridge.BridgeConstants;
     25 import com.android.layoutlib.bridge.android.BridgeContext;
     26 import com.android.layoutlib.bridge.impl.ResourceHelper;
     27 import com.android.resources.ResourceType;
     28 
     29 import org.xmlpull.v1.XmlPullParser;
     30 
     31 import android.annotation.NonNull;
     32 
     33 import java.util.Map;
     34 import java.util.function.Function;
     35 
     36 /**
     37  * A correct implementation of the {@link AttributeSet} interface on top of a XmlPullParser
     38  */
     39 public class BridgeXmlPullAttributes extends XmlPullAttributes {
     40 
     41     private final BridgeContext mContext;
     42     private final boolean mPlatformFile;
     43     private final Function<String, Map<String, Integer>> mFrameworkEnumValueSupplier;
     44     private final Function<String, Map<String, Integer>> mProjectEnumValueSupplier;
     45 
     46     // VisibleForTesting
     47     BridgeXmlPullAttributes(@NonNull XmlPullParser parser, @NonNull BridgeContext context,
     48             boolean platformFile,
     49             @NonNull Function<String, Map<String, Integer>> frameworkEnumValueSupplier,
     50             @NonNull Function<String, Map<String, Integer>> projectEnumValueSupplier) {
     51         super(parser);
     52         mContext = context;
     53         mPlatformFile = platformFile;
     54         mFrameworkEnumValueSupplier = frameworkEnumValueSupplier;
     55         mProjectEnumValueSupplier = projectEnumValueSupplier;
     56     }
     57 
     58     public BridgeXmlPullAttributes(@NonNull XmlPullParser parser, @NonNull BridgeContext context,
     59             boolean platformFile) {
     60         this(parser, context, platformFile, Bridge::getEnumValues, attrName -> {
     61             // get the styleable matching the resolved name
     62             RenderResources res = context.getRenderResources();
     63             ResourceValue attr = res.getProjectResource(ResourceType.ATTR, attrName);
     64             return attr instanceof AttrResourceValue ?
     65                     ((AttrResourceValue) attr).getAttributeValues() : null;
     66         });
     67     }
     68 
     69     /*
     70      * (non-Javadoc)
     71      * @see android.util.XmlPullAttributes#getAttributeNameResource(int)
     72      *
     73      * This methods must return com.android.internal.R.attr.<name> matching
     74      * the name of the attribute.
     75      * It returns 0 if it doesn't find anything.
     76      */
     77     @Override
     78     public int getAttributeNameResource(int index) {
     79         // get the attribute name.
     80         String name = getAttributeName(index);
     81 
     82         // get the attribute namespace
     83         String ns = mParser.getAttributeNamespace(index);
     84 
     85         if (BridgeConstants.NS_RESOURCES.equals(ns)) {
     86             return Bridge.getResourceId(ResourceType.ATTR, name);
     87 
     88         }
     89 
     90         // this is not an attribute in the android namespace, we query the customviewloader, if
     91         // the namespaces match.
     92         if (mContext.getLayoutlibCallback().getNamespace().equals(ns)) {
     93             Integer v = mContext.getLayoutlibCallback().getResourceId(ResourceType.ATTR, name);
     94             if (v != null) {
     95                 return v;
     96             }
     97         }
     98 
     99         return 0;
    100     }
    101 
    102     @Override
    103     public int getAttributeListValue(String namespace, String attribute,
    104             String[] options, int defaultValue) {
    105         String value = getAttributeValue(namespace, attribute);
    106         if (value != null) {
    107             ResourceValue r = getResourceValue(value);
    108 
    109             if (r != null) {
    110                 value = r.getValue();
    111             }
    112 
    113             return XmlUtils.convertValueToList(value, options, defaultValue);
    114         }
    115 
    116         return defaultValue;
    117     }
    118 
    119     @Override
    120     public boolean getAttributeBooleanValue(String namespace, String attribute,
    121             boolean defaultValue) {
    122         String value = getAttributeValue(namespace, attribute);
    123         if (value != null) {
    124             ResourceValue r = getResourceValue(value);
    125 
    126             if (r != null) {
    127                 value = r.getValue();
    128             }
    129 
    130             return XmlUtils.convertValueToBoolean(value, defaultValue);
    131         }
    132 
    133         return defaultValue;
    134     }
    135 
    136     @Override
    137     public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
    138         String value = getAttributeValue(namespace, attribute);
    139 
    140         return resolveResourceValue(value, defaultValue);
    141     }
    142 
    143     @Override
    144     public int getAttributeIntValue(String namespace, String attribute, int defaultValue) {
    145         String value = getAttributeValue(namespace, attribute);
    146         if (value == null) {
    147             return defaultValue;
    148         }
    149 
    150         ResourceValue r = getResourceValue(value);
    151 
    152         if (r != null) {
    153             value = r.getValue();
    154         }
    155 
    156         if (value.charAt(0) == '#') {
    157             return ResourceHelper.getColor(value);
    158         }
    159 
    160         try {
    161             return XmlUtils.convertValueToInt(value, defaultValue);
    162         } catch (NumberFormatException e) {
    163             // This is probably an enum
    164             Map<String, Integer> enumValues = BridgeConstants.NS_RESOURCES.equals(namespace) ?
    165                     mFrameworkEnumValueSupplier.apply(attribute) :
    166                     mProjectEnumValueSupplier.apply(attribute);
    167 
    168             Integer enumValue = enumValues != null ? enumValues.get(value) : null;
    169             if (enumValue != null) {
    170                 return enumValue;
    171             }
    172 
    173             // We weren't able to find the enum int value
    174             throw e;
    175         }
    176     }
    177 
    178     @Override
    179     public int getAttributeUnsignedIntValue(String namespace, String attribute,
    180             int defaultValue) {
    181         String value = getAttributeValue(namespace, attribute);
    182         if (value != null) {
    183             ResourceValue r = getResourceValue(value);
    184 
    185             if (r != null) {
    186                 value = r.getValue();
    187             }
    188 
    189             return XmlUtils.convertValueToUnsignedInt(value, defaultValue);
    190         }
    191 
    192         return defaultValue;
    193     }
    194 
    195     @Override
    196     public float getAttributeFloatValue(String namespace, String attribute,
    197             float defaultValue) {
    198         String s = getAttributeValue(namespace, attribute);
    199         if (s != null) {
    200             ResourceValue r = getResourceValue(s);
    201 
    202             if (r != null) {
    203                 s = r.getValue();
    204             }
    205 
    206             return Float.parseFloat(s);
    207         }
    208 
    209         return defaultValue;
    210     }
    211 
    212     @Override
    213     public int getAttributeListValue(int index,
    214             String[] options, int defaultValue) {
    215         return XmlUtils.convertValueToList(
    216             getAttributeValue(index), options, defaultValue);
    217     }
    218 
    219     @Override
    220     public boolean getAttributeBooleanValue(int index, boolean defaultValue) {
    221         String value = getAttributeValue(index);
    222         if (value != null) {
    223             ResourceValue r = getResourceValue(value);
    224 
    225             if (r != null) {
    226                 value = r.getValue();
    227             }
    228 
    229             return XmlUtils.convertValueToBoolean(value, defaultValue);
    230         }
    231 
    232         return defaultValue;
    233     }
    234 
    235     @Override
    236     public int getAttributeResourceValue(int index, int defaultValue) {
    237         String value = getAttributeValue(index);
    238 
    239         return resolveResourceValue(value, defaultValue);
    240     }
    241 
    242     @Override
    243     public int getAttributeIntValue(int index, int defaultValue) {
    244         return getAttributeIntValue(mParser.getAttributeNamespace(index),
    245                 getAttributeName(index)
    246                 , defaultValue);
    247     }
    248 
    249     @Override
    250     public int getAttributeUnsignedIntValue(int index, int defaultValue) {
    251         String value = getAttributeValue(index);
    252         if (value != null) {
    253             ResourceValue r = getResourceValue(value);
    254 
    255             if (r != null) {
    256                 value = r.getValue();
    257             }
    258 
    259             return XmlUtils.convertValueToUnsignedInt(value, defaultValue);
    260         }
    261 
    262         return defaultValue;
    263     }
    264 
    265     @Override
    266     public float getAttributeFloatValue(int index, float defaultValue) {
    267         String s = getAttributeValue(index);
    268         if (s != null) {
    269             ResourceValue r = getResourceValue(s);
    270 
    271             if (r != null) {
    272                 s = r.getValue();
    273             }
    274 
    275             return Float.parseFloat(s);
    276         }
    277 
    278         return defaultValue;
    279     }
    280 
    281     // -- private helper methods
    282 
    283     /**
    284      * Returns a resolved {@link ResourceValue} from a given value.
    285      */
    286     private ResourceValue getResourceValue(String value) {
    287         // now look for this particular value
    288         RenderResources resources = mContext.getRenderResources();
    289         return resources.resolveResValue(resources.findResValue(value, mPlatformFile));
    290     }
    291 
    292     /**
    293      * Resolves and return a value to its associated integer.
    294      */
    295     private int resolveResourceValue(String value, int defaultValue) {
    296         ResourceValue resource = getResourceValue(value);
    297         if (resource != null) {
    298             Integer id;
    299             if (mPlatformFile || resource.isFramework()) {
    300                 id = Bridge.getResourceId(resource.getResourceType(), resource.getName());
    301             } else {
    302                 id = mContext.getLayoutlibCallback().getResourceId(
    303                         resource.getResourceType(), resource.getName());
    304             }
    305 
    306             if (id != null) {
    307                 return id;
    308             }
    309         }
    310 
    311         return defaultValue;
    312     }
    313 }
    314