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: D20008056

fbshipit-source-id: 720d224144b0822528ca8e23cdbec9fe9dd25e6b
This commit is contained in:
Hilal Alsibai
2020-02-24 11:31:04 -08:00
committed by Facebook Github Bot
parent 16a24d82c8
commit 33bbeadfe9
5 changed files with 131 additions and 4 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.Named;
import com.facebook.flipper.plugins.inspector.NodeDescriptor; import com.facebook.flipper.plugins.inspector.NodeDescriptor;
import com.facebook.flipper.plugins.inspector.Touch; 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.FragmentActivityAccessor;
import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentCompat; import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentCompat;
import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentManagerAccessor; import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.FragmentManagerAccessor;
@@ -65,7 +66,7 @@ public class ActivityDescriptor extends NodeDescriptor<Activity> {
@Override @Override
public List<Named<FlipperObject>> getData(Activity node) { public List<Named<FlipperObject>> getData(Activity node) {
return Collections.EMPTY_LIST; return Collections.singletonList(new Named<>("Theme", ContextDescriptorUtils.themeData(node)));
} }
@Override @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.Named;
import com.facebook.flipper.plugins.inspector.NodeDescriptor; import com.facebook.flipper.plugins.inspector.NodeDescriptor;
import com.facebook.flipper.plugins.inspector.Touch; import com.facebook.flipper.plugins.inspector.Touch;
import com.facebook.flipper.plugins.inspector.descriptors.utils.ContextDescriptorUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -204,7 +205,8 @@ public class ApplicationDescriptor extends NodeDescriptor<ApplicationWrapper> {
@Override @Override
public List<Named<FlipperObject>> getData(ApplicationWrapper node) { public List<Named<FlipperObject>> getData(ApplicationWrapper node) {
return Collections.EMPTY_LIST; return Collections.singletonList(
new Named<>("Theme", ContextDescriptorUtils.themeData(node.getApplication())));
} }
@Override @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.AccessibilityEvaluationUtil;
import com.facebook.flipper.plugins.inspector.descriptors.utils.AccessibilityRoleUtil; 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.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.EnumMapping;
import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.ResourcesUtil; import com.facebook.flipper.plugins.inspector.descriptors.utils.stethocopies.ResourcesUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
@@ -186,7 +187,9 @@ public class ViewDescriptor extends NodeDescriptor<View> {
viewProps.put("foreground", fromDrawable(node.getForeground())); 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 @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;
}
}

View File

@@ -98,7 +98,7 @@ public class InspectorFlipperPluginTest {
new FlipperObject.Builder() new FlipperObject.Builder()
.put("id", "com.facebook.flipper") .put("id", "com.facebook.flipper")
.put("name", "com.facebook.flipper") .put("name", "com.facebook.flipper")
.put("data", new FlipperObject.Builder()) .put("data", new FlipperObject.Builder().put("Theme", new FlipperObject.Builder()))
.put("children", new FlipperArray.Builder().put("test")) .put("children", new FlipperArray.Builder().put("test"))
.put("attributes", new FlipperArray.Builder()) .put("attributes", new FlipperArray.Builder())
.put("decoration", (String) null) .put("decoration", (String) null)