1 /* 2 * Copyright (C) 2008 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 17 package com.android.ide.eclipse.adt.internal.sdk; 18 19 import com.android.ide.common.rendering.LayoutLibrary; 20 import com.android.ide.common.resources.ResourceRepository; 21 import com.android.ide.common.resources.platform.AttrsXmlParser; 22 import com.android.ide.common.resources.platform.DeclareStyleableInfo; 23 import com.android.ide.common.resources.platform.ViewClassInfo; 24 import com.android.ide.eclipse.adt.AdtPlugin; 25 import com.android.ide.eclipse.adt.internal.editors.animator.AnimDescriptors; 26 import com.android.ide.eclipse.adt.internal.editors.animator.AnimatorDescriptors; 27 import com.android.ide.eclipse.adt.internal.editors.color.ColorDescriptors; 28 import com.android.ide.eclipse.adt.internal.editors.drawable.DrawableDescriptors; 29 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; 30 import com.android.ide.eclipse.adt.internal.editors.manifest.descriptors.AndroidManifestDescriptors; 31 import com.android.ide.eclipse.adt.internal.editors.menu.descriptors.MenuDescriptors; 32 import com.android.ide.eclipse.adt.internal.editors.otherxml.descriptors.OtherXmlDescriptors; 33 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; 34 import com.android.sdklib.IAndroidTarget; 35 import com.android.sdklib.SdkConstants; 36 37 import org.eclipse.core.runtime.IProgressMonitor; 38 import org.eclipse.core.runtime.IStatus; 39 import org.eclipse.core.runtime.Status; 40 import org.eclipse.core.runtime.SubMonitor; 41 42 import java.io.BufferedReader; 43 import java.io.FileNotFoundException; 44 import java.io.FileReader; 45 import java.io.IOException; 46 import java.lang.reflect.Field; 47 import java.lang.reflect.Modifier; 48 import java.util.ArrayList; 49 import java.util.Collection; 50 import java.util.Collections; 51 import java.util.HashMap; 52 import java.util.List; 53 import java.util.Map; 54 55 import javax.management.InvalidAttributeValueException; 56 57 /** 58 * Parser for the platform data in an SDK. 59 * <p/> 60 * This gather the following information: 61 * <ul> 62 * <li>Resource ID from <code>android.R</code></li> 63 * <li>The list of permissions values from <code>android.Manifest$permission</code></li> 64 * <li></li> 65 * </ul> 66 */ 67 public final class AndroidTargetParser { 68 69 private static final String TAG = "Framework Resource Parser"; 70 private final IAndroidTarget mAndroidTarget; 71 72 /** 73 * Creates a platform data parser. 74 */ 75 public AndroidTargetParser(IAndroidTarget platformTarget) { 76 mAndroidTarget = platformTarget; 77 } 78 79 /** 80 * Parses the framework, collects all interesting information and stores them in the 81 * {@link IAndroidTarget} given to the constructor. 82 * 83 * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. 84 * @return True if the SDK path was valid and parsing has been attempted. 85 */ 86 public IStatus run(IProgressMonitor monitor) { 87 try { 88 SubMonitor progress = SubMonitor.convert(monitor, 89 String.format("Parsing SDK %1$s", mAndroidTarget.getName()), 90 16); 91 92 AndroidTargetData targetData = new AndroidTargetData(mAndroidTarget); 93 94 // parse the rest of the data. 95 96 AndroidJarLoader classLoader = 97 new AndroidJarLoader(mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); 98 99 preload(classLoader, progress.newChild(40, SubMonitor.SUPPRESS_NONE)); 100 101 if (progress.isCanceled()) { 102 return Status.CANCEL_STATUS; 103 } 104 105 // get the permissions 106 progress.subTask("Permissions"); 107 String[] permissionValues = collectPermissions(classLoader); 108 progress.worked(1); 109 110 if (progress.isCanceled()) { 111 return Status.CANCEL_STATUS; 112 } 113 114 // get the action and category values for the Intents. 115 progress.subTask("Intents"); 116 ArrayList<String> activity_actions = new ArrayList<String>(); 117 ArrayList<String> broadcast_actions = new ArrayList<String>(); 118 ArrayList<String> service_actions = new ArrayList<String>(); 119 ArrayList<String> categories = new ArrayList<String>(); 120 collectIntentFilterActionsAndCategories(activity_actions, broadcast_actions, 121 service_actions, categories); 122 progress.worked(1); 123 124 if (progress.isCanceled()) { 125 return Status.CANCEL_STATUS; 126 } 127 128 // gather the attribute definition 129 progress.subTask("Attributes definitions"); 130 AttrsXmlParser attrsXmlParser = new AttrsXmlParser( 131 mAndroidTarget.getPath(IAndroidTarget.ATTRIBUTES), 132 AdtPlugin.getDefault()); 133 attrsXmlParser.preload(); 134 progress.worked(1); 135 136 progress.subTask("Manifest definitions"); 137 AttrsXmlParser attrsManifestXmlParser = new AttrsXmlParser( 138 mAndroidTarget.getPath(IAndroidTarget.MANIFEST_ATTRIBUTES), 139 attrsXmlParser, 140 AdtPlugin.getDefault()); 141 attrsManifestXmlParser.preload(); 142 progress.worked(1); 143 144 Collection<ViewClassInfo> mainList = new ArrayList<ViewClassInfo>(); 145 Collection<ViewClassInfo> groupList = new ArrayList<ViewClassInfo>(); 146 147 // collect the layout/widgets classes 148 progress.subTask("Widgets and layouts"); 149 collectLayoutClasses(classLoader, attrsXmlParser, mainList, groupList, 150 progress.newChild(1)); 151 152 if (progress.isCanceled()) { 153 return Status.CANCEL_STATUS; 154 } 155 156 ViewClassInfo[] layoutViewsInfo = mainList.toArray( 157 new ViewClassInfo[mainList.size()]); 158 ViewClassInfo[] layoutGroupsInfo = groupList.toArray( 159 new ViewClassInfo[groupList.size()]); 160 mainList.clear(); 161 groupList.clear(); 162 163 // collect the preferences classes. 164 collectPreferenceClasses(classLoader, attrsXmlParser, mainList, groupList, 165 progress.newChild(1)); 166 167 if (progress.isCanceled()) { 168 return Status.CANCEL_STATUS; 169 } 170 171 ViewClassInfo[] preferencesInfo = mainList.toArray(new ViewClassInfo[mainList.size()]); 172 ViewClassInfo[] preferenceGroupsInfo = groupList.toArray( 173 new ViewClassInfo[groupList.size()]); 174 175 Map<String, DeclareStyleableInfo> xmlMenuMap = collectMenuDefinitions(attrsXmlParser); 176 Map<String, DeclareStyleableInfo> xmlSearchableMap = collectSearchableDefinitions( 177 attrsXmlParser); 178 Map<String, DeclareStyleableInfo> manifestMap = collectManifestDefinitions( 179 attrsManifestXmlParser); 180 Map<String, Map<String, Integer>> enumValueMap = attrsXmlParser.getEnumFlagValues(); 181 182 Map<String, DeclareStyleableInfo> xmlAppWidgetMap = null; 183 if (mAndroidTarget.getVersion().getApiLevel() >= 3) { 184 xmlAppWidgetMap = collectAppWidgetDefinitions(attrsXmlParser); 185 } 186 187 if (progress.isCanceled()) { 188 return Status.CANCEL_STATUS; 189 } 190 191 // From the information that was collected, create the pieces that will be put in 192 // the PlatformData object. 193 AndroidManifestDescriptors manifestDescriptors = new AndroidManifestDescriptors(); 194 manifestDescriptors.updateDescriptors(manifestMap); 195 progress.worked(1); 196 197 if (progress.isCanceled()) { 198 return Status.CANCEL_STATUS; 199 } 200 201 LayoutDescriptors layoutDescriptors = new LayoutDescriptors(); 202 layoutDescriptors.updateDescriptors(layoutViewsInfo, layoutGroupsInfo, 203 attrsXmlParser.getDeclareStyleableList(), mAndroidTarget); 204 progress.worked(1); 205 206 if (progress.isCanceled()) { 207 return Status.CANCEL_STATUS; 208 } 209 210 MenuDescriptors menuDescriptors = new MenuDescriptors(); 211 menuDescriptors.updateDescriptors(xmlMenuMap); 212 progress.worked(1); 213 214 if (progress.isCanceled()) { 215 return Status.CANCEL_STATUS; 216 } 217 218 OtherXmlDescriptors otherXmlDescriptors = new OtherXmlDescriptors(); 219 otherXmlDescriptors.updateDescriptors( 220 xmlSearchableMap, 221 xmlAppWidgetMap, 222 preferencesInfo, 223 preferenceGroupsInfo); 224 progress.worked(1); 225 226 if (progress.isCanceled()) { 227 return Status.CANCEL_STATUS; 228 } 229 230 DrawableDescriptors drawableDescriptors = new DrawableDescriptors(); 231 Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); 232 drawableDescriptors.updateDescriptors(map); 233 progress.worked(1); 234 235 if (progress.isCanceled()) { 236 return Status.CANCEL_STATUS; 237 } 238 239 AnimatorDescriptors animatorDescriptors = new AnimatorDescriptors(); 240 animatorDescriptors.updateDescriptors(map); 241 progress.worked(1); 242 243 if (progress.isCanceled()) { 244 return Status.CANCEL_STATUS; 245 } 246 247 AnimDescriptors animDescriptors = new AnimDescriptors(); 248 animDescriptors.updateDescriptors(map); 249 progress.worked(1); 250 251 if (progress.isCanceled()) { 252 return Status.CANCEL_STATUS; 253 } 254 255 ColorDescriptors colorDescriptors = new ColorDescriptors(); 256 colorDescriptors.updateDescriptors(map); 257 progress.worked(1); 258 259 // load the framework resources. 260 ResourceRepository frameworkResources = 261 ResourceManager.getInstance().loadFrameworkResources(mAndroidTarget); 262 progress.worked(1); 263 264 // now load the layout lib bridge 265 LayoutLibrary layoutBridge = LayoutLibrary.load( 266 mAndroidTarget.getPath(IAndroidTarget.LAYOUT_LIB), 267 AdtPlugin.getDefault(), 268 "ADT plug-in"); 269 270 progress.worked(1); 271 272 // and finally create the PlatformData with all that we loaded. 273 targetData.setExtraData( 274 manifestDescriptors, 275 layoutDescriptors, 276 menuDescriptors, 277 otherXmlDescriptors, 278 drawableDescriptors, 279 animatorDescriptors, 280 animDescriptors, 281 colorDescriptors, 282 enumValueMap, 283 permissionValues, 284 activity_actions.toArray(new String[activity_actions.size()]), 285 broadcast_actions.toArray(new String[broadcast_actions.size()]), 286 service_actions.toArray(new String[service_actions.size()]), 287 categories.toArray(new String[categories.size()]), 288 mAndroidTarget.getPlatformLibraries(), 289 mAndroidTarget.getOptionalLibraries(), 290 frameworkResources, 291 layoutBridge); 292 293 Sdk.getCurrent().setTargetData(mAndroidTarget, targetData); 294 295 return Status.OK_STATUS; 296 } catch (Exception e) { 297 AdtPlugin.logAndPrintError(e, TAG, "SDK parser failed"); //$NON-NLS-1$ 298 AdtPlugin.printToConsole("SDK parser failed", e.getMessage()); 299 return new Status(IStatus.ERROR, AdtPlugin.PLUGIN_ID, "SDK parser failed", e); 300 } 301 } 302 303 /** 304 * Preloads all "interesting" classes from the framework SDK jar. 305 * <p/> 306 * Currently this preloads all classes from the framework jar 307 * 308 * @param classLoader The framework SDK jar classloader 309 * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. 310 */ 311 private void preload(AndroidJarLoader classLoader, IProgressMonitor monitor) { 312 try { 313 classLoader.preLoadClasses("" /* all classes */, //$NON-NLS-1$ 314 mAndroidTarget.getName(), // monitor task label 315 monitor); 316 } catch (InvalidAttributeValueException e) { 317 AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$ 318 } catch (IOException e) { 319 AdtPlugin.log(e, "Problem preloading classes"); //$NON-NLS-1$ 320 } 321 } 322 323 /** 324 * Loads, collects and returns the list of default permissions from the framework. 325 * 326 * @param classLoader The framework SDK jar classloader 327 * @return a non null (but possibly empty) array containing the permission values. 328 */ 329 private String[] collectPermissions(AndroidJarLoader classLoader) { 330 try { 331 Class<?> permissionClass = 332 classLoader.loadClass(SdkConstants.CLASS_MANIFEST_PERMISSION); 333 334 if (permissionClass != null) { 335 ArrayList<String> list = new ArrayList<String>(); 336 337 Field[] fields = permissionClass.getFields(); 338 339 for (Field f : fields) { 340 int modifiers = f.getModifiers(); 341 if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers) && 342 Modifier.isPublic(modifiers)) { 343 try { 344 Object value = f.get(null); 345 if (value instanceof String) { 346 list.add((String)value); 347 } 348 } catch (IllegalArgumentException e) { 349 // since we provide null this should not happen 350 } catch (IllegalAccessException e) { 351 // if the field is inaccessible we ignore it. 352 } catch (NullPointerException npe) { 353 // looks like this is not a static field. we can ignore. 354 } catch (ExceptionInInitializerError eiie) { 355 // lets just ignore the field again 356 } 357 } 358 } 359 360 return list.toArray(new String[list.size()]); 361 } 362 } catch (ClassNotFoundException e) { 363 AdtPlugin.logAndPrintError(e, TAG, 364 "Collect permissions failed, class %1$s not found in %2$s", //$NON-NLS-1$ 365 SdkConstants.CLASS_MANIFEST_PERMISSION, 366 mAndroidTarget.getPath(IAndroidTarget.ANDROID_JAR)); 367 } 368 369 return new String[0]; 370 } 371 372 /** 373 * Loads and collects the action and category default values from the framework. 374 * The values are added to the <code>actions</code> and <code>categories</code> lists. 375 * 376 * @param activityActions the list which will receive the activity action values. 377 * @param broadcastActions the list which will receive the broadcast action values. 378 * @param serviceActions the list which will receive the service action values. 379 * @param categories the list which will receive the category values. 380 */ 381 private void collectIntentFilterActionsAndCategories(ArrayList<String> activityActions, 382 ArrayList<String> broadcastActions, 383 ArrayList<String> serviceActions, ArrayList<String> categories) { 384 collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_ACTIVITY), 385 activityActions); 386 collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_BROADCAST), 387 broadcastActions); 388 collectValues(mAndroidTarget.getPath(IAndroidTarget.ACTIONS_SERVICE), 389 serviceActions); 390 collectValues(mAndroidTarget.getPath(IAndroidTarget.CATEGORIES), 391 categories); 392 } 393 394 /** 395 * Collects values from a text file located in the SDK 396 * @param osFilePath The path to the text file. 397 * @param values the {@link ArrayList} to fill with the values. 398 */ 399 private void collectValues(String osFilePath, ArrayList<String> values) { 400 FileReader fr = null; 401 BufferedReader reader = null; 402 try { 403 fr = new FileReader(osFilePath); 404 reader = new BufferedReader(fr); 405 406 String line; 407 while ((line = reader.readLine()) != null) { 408 line = line.trim(); 409 if (line.length() > 0 && line.startsWith("#") == false) { //$NON-NLS-1$ 410 values.add(line); 411 } 412 } 413 } catch (IOException e) { 414 AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$ 415 } finally { 416 try { 417 if (reader != null) { 418 reader.close(); 419 } 420 } catch (IOException e) { 421 AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$ 422 } 423 424 try { 425 if (fr != null) { 426 fr.close(); 427 } 428 } catch (IOException e) { 429 AdtPlugin.log(e, "Failed to read SDK values"); //$NON-NLS-1$ 430 } 431 } 432 } 433 434 /** 435 * Collects all layout classes information from the class loader and the 436 * attrs.xml and sets the corresponding structures in the resource manager. 437 * 438 * @param classLoader The framework SDK jar classloader in case we cannot get the widget from 439 * the platform directly 440 * @param attrsXmlParser The parser of the attrs.xml file 441 * @param mainList the Collection to receive the main list of {@link ViewClassInfo}. 442 * @param groupList the Collection to receive the group list of {@link ViewClassInfo}. 443 * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. 444 */ 445 private void collectLayoutClasses(AndroidJarLoader classLoader, 446 AttrsXmlParser attrsXmlParser, 447 Collection<ViewClassInfo> mainList, 448 Collection<ViewClassInfo> groupList, 449 IProgressMonitor monitor) { 450 LayoutParamsParser ldp = null; 451 try { 452 WidgetClassLoader loader = new WidgetClassLoader( 453 mAndroidTarget.getPath(IAndroidTarget.WIDGETS)); 454 if (loader.parseWidgetList(monitor)) { 455 ldp = new LayoutParamsParser(loader, attrsXmlParser); 456 } 457 // if the parsing failed, we'll use the old loader below. 458 } catch (FileNotFoundException e) { 459 AdtPlugin.log(e, "Android Framework Parser"); //$NON-NLS-1$ 460 // the file does not exist, we'll use the old loader below. 461 } 462 463 if (ldp == null) { 464 ldp = new LayoutParamsParser(classLoader, attrsXmlParser); 465 } 466 ldp.parseLayoutClasses(monitor); 467 468 List<ViewClassInfo> views = ldp.getViews(); 469 List<ViewClassInfo> groups = ldp.getGroups(); 470 471 if (views != null && groups != null) { 472 mainList.addAll(views); 473 groupList.addAll(groups); 474 } 475 } 476 477 /** 478 * Collects all preferences definition information from the attrs.xml and 479 * sets the corresponding structures in the resource manager. 480 * 481 * @param classLoader The framework SDK jar classloader 482 * @param attrsXmlParser The parser of the attrs.xml file 483 * @param mainList the Collection to receive the main list of {@link ViewClassInfo}. 484 * @param groupList the Collection to receive the group list of {@link ViewClassInfo}. 485 * @param monitor A progress monitor. Can be null. Caller is responsible for calling done. 486 */ 487 private void collectPreferenceClasses(AndroidJarLoader classLoader, 488 AttrsXmlParser attrsXmlParser, Collection<ViewClassInfo> mainList, 489 Collection<ViewClassInfo> groupList, IProgressMonitor monitor) { 490 LayoutParamsParser ldp = new LayoutParamsParser(classLoader, attrsXmlParser); 491 492 try { 493 ldp.parsePreferencesClasses(monitor); 494 495 List<ViewClassInfo> prefs = ldp.getViews(); 496 List<ViewClassInfo> groups = ldp.getGroups(); 497 498 if (prefs != null && groups != null) { 499 mainList.addAll(prefs); 500 groupList.addAll(groups); 501 } 502 } catch (NoClassDefFoundError e) { 503 AdtPlugin.logAndPrintError(e, TAG, 504 "Collect preferences failed, class %1$s not found in %2$s", 505 e.getMessage(), 506 classLoader.getSource()); 507 } catch (Throwable e) { 508 AdtPlugin.log(e, "Android Framework Parser: failed to collect preference classes"); //$NON-NLS-1$ 509 AdtPlugin.printErrorToConsole("Android Framework Parser", 510 "failed to collect preference classes"); 511 } 512 } 513 514 /** 515 * Collects all menu definition information from the attrs.xml and returns it. 516 * 517 * @param attrsXmlParser The parser of the attrs.xml file 518 */ 519 private Map<String, DeclareStyleableInfo> collectMenuDefinitions( 520 AttrsXmlParser attrsXmlParser) { 521 Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); 522 Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>(); 523 for (String key : new String[] { "Menu", //$NON-NLS-1$ 524 "MenuItem", //$NON-NLS-1$ 525 "MenuGroup" }) { //$NON-NLS-1$ 526 if (map.containsKey(key)) { 527 map2.put(key, map.get(key)); 528 } else { 529 AdtPlugin.log(IStatus.WARNING, 530 "Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 531 key, attrsXmlParser.getOsAttrsXmlPath()); 532 AdtPlugin.printErrorToConsole("Android Framework Parser", 533 String.format("Menu declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 534 key, attrsXmlParser.getOsAttrsXmlPath())); 535 } 536 } 537 538 return Collections.unmodifiableMap(map2); 539 } 540 541 /** 542 * Collects all searchable definition information from the attrs.xml and returns it. 543 * 544 * @param attrsXmlParser The parser of the attrs.xml file 545 */ 546 private Map<String, DeclareStyleableInfo> collectSearchableDefinitions( 547 AttrsXmlParser attrsXmlParser) { 548 Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); 549 Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>(); 550 for (String key : new String[] { "Searchable", //$NON-NLS-1$ 551 "SearchableActionKey" }) { //$NON-NLS-1$ 552 if (map.containsKey(key)) { 553 map2.put(key, map.get(key)); 554 } else { 555 AdtPlugin.log(IStatus.WARNING, 556 "Searchable declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 557 key, attrsXmlParser.getOsAttrsXmlPath()); 558 AdtPlugin.printErrorToConsole("Android Framework Parser", 559 String.format("Searchable declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 560 key, attrsXmlParser.getOsAttrsXmlPath())); 561 } 562 } 563 564 return Collections.unmodifiableMap(map2); 565 } 566 567 /** 568 * Collects all appWidgetProviderInfo definition information from the attrs.xml and returns it. 569 * 570 * @param attrsXmlParser The parser of the attrs.xml file 571 */ 572 private Map<String, DeclareStyleableInfo> collectAppWidgetDefinitions( 573 AttrsXmlParser attrsXmlParser) { 574 Map<String, DeclareStyleableInfo> map = attrsXmlParser.getDeclareStyleableList(); 575 Map<String, DeclareStyleableInfo> map2 = new HashMap<String, DeclareStyleableInfo>(); 576 for (String key : new String[] { "AppWidgetProviderInfo" }) { //$NON-NLS-1$ 577 if (map.containsKey(key)) { 578 map2.put(key, map.get(key)); 579 } else { 580 AdtPlugin.log(IStatus.WARNING, 581 "AppWidget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 582 key, attrsXmlParser.getOsAttrsXmlPath()); 583 AdtPlugin.printErrorToConsole("Android Framework Parser", 584 String.format("AppWidget declare-styleable %1$s not found in file %2$s", //$NON-NLS-1$ 585 key, attrsXmlParser.getOsAttrsXmlPath())); 586 } 587 } 588 589 return Collections.unmodifiableMap(map2); 590 } 591 592 /** 593 * Collects all manifest definition information from the attrs_manifest.xml and returns it. 594 */ 595 private Map<String, DeclareStyleableInfo> collectManifestDefinitions( 596 AttrsXmlParser attrsXmlParser) { 597 598 return attrsXmlParser.getDeclareStyleableList(); 599 } 600 601 } 602