in android/src/main/java/com/facebook/flipper/plugins/inspector/descriptors/utils/ContextDescriptorUtils.java [42:155]
private static Map<String, FlipperObject.Builder> collectThemeValues(Context context) {
Map<String, FlipperObject.Builder> builderMap = new HashMap<>(3);
try {
Resources.Theme theme = context.getTheme();
AssetManager assetManager = context.getAssets();
final Object themeImpl;
final Object themeKey;
// Nasty reflection to get a list of theme attributes that apply to this context.
if (!doneFieldDiscovery) {
doneFieldDiscovery = true; // even if it fails, we don't want to retry
try {
sThemeImplField = theme.getClass().getDeclaredField("mThemeImpl");
sThemeImplField.setAccessible(true);
} catch (NoSuchFieldException e) {
// hardening against #1736
return builderMap;
}
themeImpl = sThemeImplField.get(theme);
try {
sThemeImplThemeKeyField = themeImpl.getClass().getDeclaredField("mKey");
sThemeImplThemeKeyField.setAccessible(true);
} catch (NoSuchFieldException e) {
// hardening against #1736
return builderMap;
}
try {
sThemeImplAssetManagerField = themeImpl.getClass().getDeclaredField("mAssets");
sThemeImplAssetManagerField.setAccessible(true);
} catch (NoSuchFieldException e) {
// hardening against #1736
return builderMap;
}
try {
sAssetManagerGetStyleAttributesMethod =
assetManager.getClass().getDeclaredMethod("getStyleAttributes", int.class);
sAssetManagerGetStyleAttributesMethod.setAccessible(true);
} catch (NoSuchMethodException e) {
// hardening against #1736
return builderMap;
}
themeKey = sThemeImplThemeKeyField.get(themeImpl);
try {
sThemeKeyResIdField = themeKey.getClass().getDeclaredField("mResId");
sThemeKeyResIdField.setAccessible(true);
} catch (NoSuchFieldException e) {
// hardening against #1736
return builderMap;
}
} else if (sThemeImplField != null && sThemeImplThemeKeyField != null) {
themeImpl = sThemeImplField.get(theme);
themeKey = sThemeImplThemeKeyField.get(themeImpl);
} else {
themeKey = null;
}
if (sThemeKeyResIdField == null
|| sAssetManagerGetStyleAttributesMethod == null
|| themeKey == null) {
return builderMap;
}
int[] appliedThemeResIds = (int[]) sThemeKeyResIdField.get(themeKey);
TypedValue typedValue = new TypedValue();
Resources resources = context.getResources();
for (int themeId : appliedThemeResIds) {
if (themeId == 0) {
continue;
}
String name = resources.getResourceName(themeId);
// The res id array can have duplicates
if (builderMap.containsKey(name)) {
continue;
}
FlipperObject.Builder builder = new FlipperObject.Builder();
builderMap.put(name, builder);
int[] attributes =
(int[]) sAssetManagerGetStyleAttributesMethod.invoke(assetManager, themeId);
for (int attribute : attributes) {
if (!theme.resolveAttribute(attribute, typedValue, true)) {
continue;
}
String attributeName = context.getResources().getResourceName(attribute);
String[] nameParts = attributeName.split(":");
if (nameParts.length < 2) {
Log.d(TAG, "Unknown attribute name format " + attributeName);
} else {
attributeName = nameParts[1].split("/")[1];
}
String strValue = TypedValue.coerceToString(typedValue.type, typedValue.data);
if (strValue == null) {
strValue = "null";
} else if (strValue.startsWith("@")) {
int resId = Integer.parseInt(strValue.substring(1));
if (resId == 0) {
strValue = "null";
} else {
strValue = context.getResources().getResourceName(resId);
}
}
builder.put(attributeName, strValue);
}
}
} catch (Throwable ignored) {
Log.d(TAG, "Failed to generate theme attribute data!", ignored);
}
return builderMap;
}