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 BridgeXmlPullAttributes(@onNull XmlPullParser parser, @NonNull BridgeContext context, boolean platformFile, @NonNull Function<String, Map<String, Integer>> frameworkEnumValueSupplier, @NonNull Function<String, Map<String, Integer>> projectEnumValueSupplier)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 BridgeXmlPullAttributes(@onNull XmlPullParser parser, @NonNull BridgeContext context, boolean platformFile)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 getAttributeNameResource(int index)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 getAttributeListValue(String namespace, String attribute, String[] options, int defaultValue)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 getAttributeBooleanValue(String namespace, String attribute, boolean defaultValue)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 getAttributeResourceValue(String namespace, String attribute, int defaultValue)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 getAttributeIntValue(String namespace, String attribute, int defaultValue)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 getAttributeUnsignedIntValue(String namespace, String attribute, int defaultValue)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 getAttributeFloatValue(String namespace, String attribute, float defaultValue)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 getAttributeListValue(int index, String[] options, int defaultValue)213 public int getAttributeListValue(int index, 214 String[] options, int defaultValue) { 215 return XmlUtils.convertValueToList( 216 getAttributeValue(index), options, defaultValue); 217 } 218 219 @Override getAttributeBooleanValue(int index, boolean defaultValue)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 getAttributeResourceValue(int index, int defaultValue)236 public int getAttributeResourceValue(int index, int defaultValue) { 237 String value = getAttributeValue(index); 238 239 return resolveResourceValue(value, defaultValue); 240 } 241 242 @Override getAttributeIntValue(int index, int defaultValue)243 public int getAttributeIntValue(int index, int defaultValue) { 244 return getAttributeIntValue(mParser.getAttributeNamespace(index), 245 getAttributeName(index) 246 , defaultValue); 247 } 248 249 @Override getAttributeUnsignedIntValue(int index, int defaultValue)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 getAttributeFloatValue(int index, float defaultValue)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 */ getResourceValue(String value)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 */ resolveResourceValue(String value, int defaultValue)295 private int resolveResourceValue(String value, int defaultValue) { 296 ResourceValue resource = getResourceValue(value); 297 if (resource != null) { 298 Integer id = null; 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