Loading tools/layoutlib/bridge/src/android/view/BridgeInflater.java +23 −8 Original line number Diff line number Diff line Loading @@ -25,7 +25,9 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; import com.android.layoutlib.bridge.impl.ParserFactory; import com.android.layoutlib.bridge.util.ReflectionUtils; import com.android.resources.ResourceType; import com.android.util.Pair; Loading Loading @@ -112,15 +114,9 @@ public final class BridgeInflater extends LayoutInflater { } // Finally try again using the custom view loader try { if (view == null) { view = loadCustomView(name, attrs); } } catch (ClassNotFoundException e) { // If the class was not found, we throw the exception directly, because this // method is already expected to throw it. throw e; } } catch (Exception e) { // Wrap the real exception in a ClassNotFoundException, so that the calling method // can deal with it. Loading Loading @@ -242,6 +238,25 @@ public final class BridgeInflater extends LayoutInflater { bc.setScrollYPos(view, value); } } if (ReflectionUtils.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) { Integer resourceId = null; String attrVal = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, BridgeConstants.ATTR_LIST_ITEM); if (attrVal != null && !attrVal.isEmpty()) { ResourceValue resValue = bc.getRenderResources().findResValue(attrVal, false); if (resValue.isFramework()) { resourceId = Bridge.getResourceId(resValue.getResourceType(), resValue.getName()); } else { resourceId = mLayoutlibCallback.getResourceId(resValue.getResourceType(), resValue.getName()); } } if (resourceId == null) { resourceId = 0; } RecyclerViewUtil.setAdapter(view, bc, mLayoutlibCallback, resourceId); } } } Loading tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java +2 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ public class BridgeConstants { /** App auto namespace */ public final static String NS_APP_RES_AUTO = "http://schemas.android.com/apk/res-auto"; public final static String NS_TOOLS_URI = "http://schemas.android.com/tools"; public final static String R = "com.android.internal.R"; Loading @@ -50,5 +51,5 @@ public class BridgeConstants { public final static String WRAP_CONTENT = "wrap_content"; /** Attribute in the tools namespace used to specify layout manager for RecyclerView. */ public static final String ATTR_LAYOUT_MANAGER_TYPE = "layoutManager"; public static final String ATTR_LIST_ITEM = "list_item"; } tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java +37 −23 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.layoutlib.bridge.android.support; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.SessionParams; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.RenderParamsFlags; Loading @@ -37,7 +36,6 @@ import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke; /** * Utility class for working with android.support.v7.widget.RecyclerView */ @SuppressWarnings("SpellCheckingInspection") // for "recycler". public class RecyclerViewUtil { private static final String RV_PKG_PREFIX = "android.support.v7.widget."; Loading @@ -57,15 +55,24 @@ public class RecyclerViewUtil { * Any exceptions thrown during the process are logged in {@link Bridge#getLog()} */ public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context, @NonNull SessionParams params) { @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout) { try { setLayoutManager(recyclerView, context, params.getLayoutlibCallback()); Object adapter = createAdapter(params); setLayoutManager(recyclerView, context, layoutlibCallback); Object adapter = createAdapter(layoutlibCallback); if (adapter != null) { setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter"); setProperty(adapter, int.class, adapterLayout, "setLayoutId"); } } catch (ReflectionException e) { Throwable cause = getCause(e); Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Error occured while trying to setup RecyclerView.", e, null); "Error occurred while trying to setup RecyclerView.", cause, null); } } private static Throwable getCause(Throwable throwable) { Throwable cause = throwable.getCause(); return cause == null ? throwable : cause; } private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context, Loading @@ -73,9 +80,11 @@ public class RecyclerViewUtil { if (getLayoutManager(recyclerView) == null) { // Only set the layout manager if not already set by the recycler view. Object layoutManager = createLayoutManager(context, callback); if (layoutManager != null) { setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager"); } } } /** Creates a LinearLayoutManager using the provided context. */ @Nullable Loading @@ -91,34 +100,39 @@ public class RecyclerViewUtil { } @Nullable private static Object getLayoutManager(View recyclerview) throws ReflectionException { Method getLayoutManager = getMethod(recyclerview.getClass(), "getLayoutManager"); return getLayoutManager != null ? invoke(getLayoutManager, recyclerview) : null; private static Object getLayoutManager(View recyclerView) throws ReflectionException { Method getLayoutManager = getMethod(recyclerView.getClass(), "getLayoutManager"); return getLayoutManager != null ? invoke(getLayoutManager, recyclerView) : null; } @Nullable private static Object createAdapter(@NonNull SessionParams params) throws ReflectionException { Boolean ideSupport = params.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback) throws ReflectionException { Boolean ideSupport = layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); if (ideSupport != Boolean.TRUE) { return null; } try { return params.getLayoutlibCallback().loadView(CN_ADAPTER, new Class[0], new Object[0]); return layoutlibCallback.loadClass(CN_ADAPTER, new Class[0], new Object[0]); } catch (Exception e) { throw new ReflectionException(e); } } private static void setProperty(@NonNull View recyclerView, @NonNull String propertyClassName, @Nullable Object propertyValue, @NonNull String propertySetter) private static void setProperty(@NonNull Object object, @NonNull String propertyClassName, @NonNull Object propertyValue, @NonNull String propertySetter) throws ReflectionException { if (propertyValue != null) { Class<?> layoutManagerClass = getClassInstance(propertyValue, propertyClassName); Method setLayoutManager = getMethod(recyclerView.getClass(), propertySetter, layoutManagerClass); if (setLayoutManager != null) { invoke(setLayoutManager, recyclerView, propertyValue); Class<?> propertyClass = getClassInstance(propertyValue, propertyClassName); setProperty(object, propertyClass, propertyValue, propertySetter); } private static void setProperty(@NonNull Object object, @NonNull Class<?> propertyClass, @Nullable Object propertyValue, @NonNull String propertySetter) throws ReflectionException { Method setter = getMethod(object.getClass(), propertySetter, propertyClass); if (setter != null) { invoke(setter, object, propertyValue); } } Loading tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +1 −19 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.RenderParamsFlags; import com.android.layoutlib.bridge.android.support.DesignLibUtil; import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; import com.android.layoutlib.bridge.bars.AppCompatActionBar; import com.android.layoutlib.bridge.bars.BridgeActionBar; import com.android.layoutlib.bridge.bars.Config; Loading Loading @@ -116,6 +115,7 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLA import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN; import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; import static com.android.layoutlib.bridge.util.ReflectionUtils.isInstanceOf; /** * Class implementing the render session. Loading Loading @@ -1341,8 +1341,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } } } else if (isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) { RecyclerViewUtil.setAdapter(view, getContext(), getParams()); } else if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); Loading Loading @@ -1446,22 +1444,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } /** * Check if the object is an instance of a class named {@code className}. This doesn't work * for interfaces. */ public static boolean isInstanceOf(Object object, String className) { Class superClass = object.getClass(); while (superClass != null) { String name = superClass.getName(); if (name.equals(className)) { return true; } superClass = superClass.getSuperclass(); } return false; } /** * Sets up a {@link TabHost} object. * @param tabHost the TabHost to setup. Loading tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java +16 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,22 @@ public class ReflectionUtils { throw new ReflectionException(ex); } /** * Check if the object is an instance of a class named {@code className}. This doesn't work * for interfaces. */ public static boolean isInstanceOf(Object object, String className) { Class superClass = object.getClass(); while (superClass != null) { String name = superClass.getName(); if (name.equals(className)) { return true; } superClass = superClass.getSuperclass(); } return false; } /** * Wraps all reflection related exceptions. Created since ReflectiveOperationException was * introduced in 1.7 and we are still on 1.6 Loading Loading
tools/layoutlib/bridge/src/android/view/BridgeInflater.java +23 −8 Original line number Diff line number Diff line Loading @@ -25,7 +25,9 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; import com.android.layoutlib.bridge.impl.ParserFactory; import com.android.layoutlib.bridge.util.ReflectionUtils; import com.android.resources.ResourceType; import com.android.util.Pair; Loading Loading @@ -112,15 +114,9 @@ public final class BridgeInflater extends LayoutInflater { } // Finally try again using the custom view loader try { if (view == null) { view = loadCustomView(name, attrs); } } catch (ClassNotFoundException e) { // If the class was not found, we throw the exception directly, because this // method is already expected to throw it. throw e; } } catch (Exception e) { // Wrap the real exception in a ClassNotFoundException, so that the calling method // can deal with it. Loading Loading @@ -242,6 +238,25 @@ public final class BridgeInflater extends LayoutInflater { bc.setScrollYPos(view, value); } } if (ReflectionUtils.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) { Integer resourceId = null; String attrVal = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, BridgeConstants.ATTR_LIST_ITEM); if (attrVal != null && !attrVal.isEmpty()) { ResourceValue resValue = bc.getRenderResources().findResValue(attrVal, false); if (resValue.isFramework()) { resourceId = Bridge.getResourceId(resValue.getResourceType(), resValue.getName()); } else { resourceId = mLayoutlibCallback.getResourceId(resValue.getResourceType(), resValue.getName()); } } if (resourceId == null) { resourceId = 0; } RecyclerViewUtil.setAdapter(view, bc, mLayoutlibCallback, resourceId); } } } Loading
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java +2 −1 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ public class BridgeConstants { /** App auto namespace */ public final static String NS_APP_RES_AUTO = "http://schemas.android.com/apk/res-auto"; public final static String NS_TOOLS_URI = "http://schemas.android.com/tools"; public final static String R = "com.android.internal.R"; Loading @@ -50,5 +51,5 @@ public class BridgeConstants { public final static String WRAP_CONTENT = "wrap_content"; /** Attribute in the tools namespace used to specify layout manager for RecyclerView. */ public static final String ATTR_LAYOUT_MANAGER_TYPE = "layoutManager"; public static final String ATTR_LIST_ITEM = "list_item"; }
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/support/RecyclerViewUtil.java +37 −23 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.layoutlib.bridge.android.support; import com.android.ide.common.rendering.api.LayoutLog; import com.android.ide.common.rendering.api.LayoutlibCallback; import com.android.ide.common.rendering.api.SessionParams; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.RenderParamsFlags; Loading @@ -37,7 +36,6 @@ import static com.android.layoutlib.bridge.util.ReflectionUtils.invoke; /** * Utility class for working with android.support.v7.widget.RecyclerView */ @SuppressWarnings("SpellCheckingInspection") // for "recycler". public class RecyclerViewUtil { private static final String RV_PKG_PREFIX = "android.support.v7.widget."; Loading @@ -57,15 +55,24 @@ public class RecyclerViewUtil { * Any exceptions thrown during the process are logged in {@link Bridge#getLog()} */ public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context, @NonNull SessionParams params) { @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout) { try { setLayoutManager(recyclerView, context, params.getLayoutlibCallback()); Object adapter = createAdapter(params); setLayoutManager(recyclerView, context, layoutlibCallback); Object adapter = createAdapter(layoutlibCallback); if (adapter != null) { setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter"); setProperty(adapter, int.class, adapterLayout, "setLayoutId"); } } catch (ReflectionException e) { Throwable cause = getCause(e); Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Error occured while trying to setup RecyclerView.", e, null); "Error occurred while trying to setup RecyclerView.", cause, null); } } private static Throwable getCause(Throwable throwable) { Throwable cause = throwable.getCause(); return cause == null ? throwable : cause; } private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context, Loading @@ -73,9 +80,11 @@ public class RecyclerViewUtil { if (getLayoutManager(recyclerView) == null) { // Only set the layout manager if not already set by the recycler view. Object layoutManager = createLayoutManager(context, callback); if (layoutManager != null) { setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager"); } } } /** Creates a LinearLayoutManager using the provided context. */ @Nullable Loading @@ -91,34 +100,39 @@ public class RecyclerViewUtil { } @Nullable private static Object getLayoutManager(View recyclerview) throws ReflectionException { Method getLayoutManager = getMethod(recyclerview.getClass(), "getLayoutManager"); return getLayoutManager != null ? invoke(getLayoutManager, recyclerview) : null; private static Object getLayoutManager(View recyclerView) throws ReflectionException { Method getLayoutManager = getMethod(recyclerView.getClass(), "getLayoutManager"); return getLayoutManager != null ? invoke(getLayoutManager, recyclerView) : null; } @Nullable private static Object createAdapter(@NonNull SessionParams params) throws ReflectionException { Boolean ideSupport = params.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback) throws ReflectionException { Boolean ideSupport = layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); if (ideSupport != Boolean.TRUE) { return null; } try { return params.getLayoutlibCallback().loadView(CN_ADAPTER, new Class[0], new Object[0]); return layoutlibCallback.loadClass(CN_ADAPTER, new Class[0], new Object[0]); } catch (Exception e) { throw new ReflectionException(e); } } private static void setProperty(@NonNull View recyclerView, @NonNull String propertyClassName, @Nullable Object propertyValue, @NonNull String propertySetter) private static void setProperty(@NonNull Object object, @NonNull String propertyClassName, @NonNull Object propertyValue, @NonNull String propertySetter) throws ReflectionException { if (propertyValue != null) { Class<?> layoutManagerClass = getClassInstance(propertyValue, propertyClassName); Method setLayoutManager = getMethod(recyclerView.getClass(), propertySetter, layoutManagerClass); if (setLayoutManager != null) { invoke(setLayoutManager, recyclerView, propertyValue); Class<?> propertyClass = getClassInstance(propertyValue, propertyClassName); setProperty(object, propertyClass, propertyValue, propertySetter); } private static void setProperty(@NonNull Object object, @NonNull Class<?> propertyClass, @Nullable Object propertyValue, @NonNull String propertySetter) throws ReflectionException { Method setter = getMethod(object.getClass(), propertySetter, propertyClass); if (setter != null) { invoke(setter, object, propertyValue); } } Loading
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +1 −19 Original line number Diff line number Diff line Loading @@ -45,7 +45,6 @@ import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.android.RenderParamsFlags; import com.android.layoutlib.bridge.android.support.DesignLibUtil; import com.android.layoutlib.bridge.android.support.RecyclerViewUtil; import com.android.layoutlib.bridge.bars.AppCompatActionBar; import com.android.layoutlib.bridge.bars.BridgeActionBar; import com.android.layoutlib.bridge.bars.Config; Loading Loading @@ -116,6 +115,7 @@ import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLA import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; import static com.android.ide.common.rendering.api.Result.Status.ERROR_VIEWGROUP_NO_CHILDREN; import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; import static com.android.layoutlib.bridge.util.ReflectionUtils.isInstanceOf; /** * Class implementing the render session. Loading Loading @@ -1341,8 +1341,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } } } else if (isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) { RecyclerViewUtil.setAdapter(view, getContext(), getParams()); } else if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); Loading Loading @@ -1446,22 +1444,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } /** * Check if the object is an instance of a class named {@code className}. This doesn't work * for interfaces. */ public static boolean isInstanceOf(Object object, String className) { Class superClass = object.getClass(); while (superClass != null) { String name = superClass.getName(); if (name.equals(className)) { return true; } superClass = superClass.getSuperclass(); } return false; } /** * Sets up a {@link TabHost} object. * @param tabHost the TabHost to setup. Loading
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/ReflectionUtils.java +16 −0 Original line number Diff line number Diff line Loading @@ -51,6 +51,22 @@ public class ReflectionUtils { throw new ReflectionException(ex); } /** * Check if the object is an instance of a class named {@code className}. This doesn't work * for interfaces. */ public static boolean isInstanceOf(Object object, String className) { Class superClass = object.getClass(); while (superClass != null) { String name = superClass.getName(); if (name.equals(className)) { return true; } superClass = superClass.getSuperclass(); } return false; } /** * Wraps all reflection related exceptions. Created since ReflectiveOperationException was * introduced in 1.7 and we are still on 1.6 Loading