All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.res.ResourceRemapper Maven / Gradle / Ivy

package org.robolectric.res;

import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

/**
 * This class rewrites application R class resource values from multiple input R classes to all have
 * unique values existing within the same ID space, i.e: no resource collisions. This replicates the
 * behaviour of AAPT when building the final APK.
 *
 * 

IDs are in the format:- * *

0x PPTTEEEE * *

where: * *

P is unique for the package T is unique for the type E is the entry within that type. */ class ResourceRemapper { private BiMap resIds = HashBiMap.create(); private ResourceIdGenerator resourceIdGenerator = new ResourceIdGenerator(0x7F); /** * @param primaryRClass - An R class (usually the applications) that can be assumed to have a * complete set of IDs. If this is provided then use the values from this class for * re-writting all values in follow up calls to {@link #remapRClass(Class)}. If it is not * provided the ResourceRemapper will generate its own unique non-conflicting IDs. */ ResourceRemapper(Class primaryRClass) { if (primaryRClass != null) { remapRClass(true, primaryRClass); } } void remapRClass(Class rClass) { remapRClass(false, rClass); } /** * @param isPrimary - Only one R class can allow final values and that is the final R class for * the application that has had its resource id values generated to include all libraries in * its dependency graph and therefore will be the only R file with the complete set of IDs in * a unique ID space so we can assume to use the values from this class only. All other R * files are partial R files for each library and on non-Android aware build systems like * Maven where library R files are not re-written with the final R values we need to rewrite * them ourselves. */ private void remapRClass(boolean isPrimary, Class rClass) { // Collect all the local attribute id -> name mappings. These are used when processing the // stylables to look up // the reassigned values. Map localAttributeIds = new HashMap<>(); for (Class aClass : rClass.getClasses()) { if (aClass.getSimpleName().equals("attr")) { for (Field field : aClass.getFields()) { try { localAttributeIds.put(field.getInt(null), field.getName()); } catch (IllegalAccessException e) { throw new RuntimeException("Could not read attr value for " + field.getName(), e); } } } } for (Class innerClass : rClass.getClasses()) { String resourceType = innerClass.getSimpleName(); if (!resourceType.startsWith("styleable")) { for (Field field : innerClass.getFields()) { try { if (!isPrimary && Modifier.isFinal(field.getModifiers())) { throw new IllegalArgumentException( rClass + " contains final fields, these will be inlined by the compiler and cannot" + " be remapped."); } String resourceName = resourceType + "/" + field.getName(); Integer value = resIds.get(resourceName); if (value != null) { field.setAccessible(true); field.setInt(null, value); resourceIdGenerator.record(field.getInt(null), resourceType, field.getName()); } else if (resIds.containsValue(field.getInt(null))) { int remappedValue = resourceIdGenerator.generate(resourceType, field.getName()); field.setInt(null, remappedValue); resIds.put(resourceName, remappedValue); } else { if (isPrimary) { resourceIdGenerator.record(field.getInt(null), resourceType, field.getName()); resIds.put(resourceName, field.getInt(null)); } else { int remappedValue = resourceIdGenerator.generate(resourceType, field.getName()); field.setInt(null, remappedValue); resIds.put(resourceName, remappedValue); } } } catch (IllegalAccessException e) { throw new IllegalStateException(e); } } } } // Reassign the ids in the style arrays accordingly. for (Class innerClass : rClass.getClasses()) { String resourceType = innerClass.getSimpleName(); if (resourceType.startsWith("styleable")) { for (Field field : innerClass.getFields()) { if (field.getType().equals(int[].class)) { try { int[] styleableArray = (int[]) field.get(null); for (int k = 0; k < styleableArray.length; k++) { Integer value = resIds.get("attr/" + localAttributeIds.get(styleableArray[k])); if (value != null) { styleableArray[k] = value; } } } catch (IllegalAccessException e) { throw new IllegalStateException(e); } } } } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy