Add theme information for Application, Activity and View descriptors

Summary: This will query for and display all of the associated theme information for each of the descriptors.

Reviewed By: passy

Differential Revision: D19987003

fbshipit-source-id: c9b51311d28712a1af76c45a434bfb25d28d5c54
This commit is contained in:
Hilal Alsibai
2020-02-20 04:07:21 -08:00
committed by Facebook Github Bot
parent 97f9b2494d
commit 6f4de969fb
4 changed files with 130 additions and 3 deletions

View File

@@ -14,6 +14,7 @@ import com.facebook.flipper.core.FlipperObject;
import com.facebook.flipper.plugins.inspector.Named;
import com.facebook.flipper.plugins.inspector.NodeDescriptor;
import com.facebook.flipper.plugins.inspector.Touch;
import com.facebook.flipper.plugins.inspector.descriptors.utils.ContextDescriptorUtils;
import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentActivityAccessor;
import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentCompat;
import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentManagerAccessor;
@@ -65,7 +66,7 @@ public class ActivityDescriptor extends NodeDescriptor<Activity> {
@Override
public List<Named<FlipperObject>> getData(Activity node) {
return Collections.EMPTY_LIST;
return Collections.singletonList(new Named<>("Theme", ContextDescriptorUtils.themeData(node)));
}
@Override

View File

@@ -19,6 +19,7 @@ import com.facebook.flipper.plugins.inspector.ApplicationWrapper;
import com.facebook.flipper.plugins.inspector.Named;
import com.facebook.flipper.plugins.inspector.NodeDescriptor;
import com.facebook.flipper.plugins.inspector.Touch;
import com.facebook.flipper.plugins.inspector.descriptors.utils.ContextDescriptorUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -204,7 +205,8 @@ public class ApplicationDescriptor extends NodeDescriptor<ApplicationWrapper> {
@Override
public List<Named<FlipperObject>> getData(ApplicationWrapper node) {
return Collections.EMPTY_LIST;
return Collections.singletonList(
new Named<>("Theme", ContextDescriptorUtils.themeData(node.getApplication())));
}
@Override

View File

@@ -38,6 +38,7 @@ import com.facebook.flipper.plugins.inspector.Touch;
import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityEvaluationUtil;
import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityRoleUtil;
import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityUtil;
import com.facebook.flipper.plugins.inspector.descriptors.utils.ContextDescriptorUtils;
import com.facebook.flipper.plugins.inspector.descriptors.utils.EnumMapping;
import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.ResourcesUtil;
import java.lang.reflect.Field;
@@ -190,7 +191,9 @@ public class ViewDescriptor extends NodeDescriptor<View> {
viewProps.put("foreground", fromDrawable(node.getForeground()));
}
return Arrays.asList(new Named<>("View", viewProps.build()));
return Arrays.asList(
new Named<>("View", viewProps.build()),
new Named<>("Theme", ContextDescriptorUtils.themeData(node.getContext())));
}
@Override

View File

@@ -0,0 +1,121 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
package com.facebook.flipper.plugins.inspector.descriptors.utils;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.util.Log;
import android.util.TypedValue;
import com.facebook.flipper.core.FlipperObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public final class ContextDescriptorUtils {
private static String TAG = "ContextDescriptor";
private static Field sThemeImplField;
private static Field sThemeImplThemeKeyField;
private static Field sThemeImplAssetManagerField;
private static Field sThemeKeyResIdField;
private static Method sAssetManagerGetStyleAttributesMethod;
private ContextDescriptorUtils() {}
public static FlipperObject themeData(Context context) {
FlipperObject.Builder themeData = new FlipperObject.Builder();
Map<String, FlipperObject.Builder> builders = collectThemeValues(context);
for (Map.Entry<String, FlipperObject.Builder> entry : builders.entrySet()) {
themeData.put(entry.getKey(), entry.getValue().build());
}
return themeData.build();
}
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 (sThemeImplField == null
|| sThemeImplThemeKeyField == null
|| sThemeKeyResIdField == null
|| sThemeImplAssetManagerField == null) {
sThemeImplField = theme.getClass().getDeclaredField("mThemeImpl");
sThemeImplField.setAccessible(true);
themeImpl = sThemeImplField.get(theme);
sThemeImplThemeKeyField = themeImpl.getClass().getDeclaredField("mKey");
sThemeImplThemeKeyField.setAccessible(true);
sThemeImplAssetManagerField = themeImpl.getClass().getDeclaredField("mAssets");
sThemeImplAssetManagerField.setAccessible(true);
sAssetManagerGetStyleAttributesMethod =
assetManager.getClass().getDeclaredMethod("getStyleAttributes", int.class);
sAssetManagerGetStyleAttributesMethod.setAccessible(true);
themeKey = sThemeImplThemeKeyField.get(themeImpl);
sThemeKeyResIdField = themeKey.getClass().getDeclaredField("mResId");
sThemeKeyResIdField.setAccessible(true);
} else {
themeImpl = sThemeImplField.get(theme);
themeKey = sThemeImplThemeKeyField.get(themeImpl);
}
int[] appliedThemeResIds = (int[]) sThemeKeyResIdField.get(themeKey);
TypedValue typedValue = new TypedValue();
Resources resources = context.getResources();
for (int themeId : appliedThemeResIds) {
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;
}
}