1d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta/* 2d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * Copyright (C) 2015 The Android Open Source Project 3d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * 4d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License"); 5d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * you may not use this file except in compliance with the License. 6d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * You may obtain a copy of the License at 7d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * 8d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * http://www.apache.org/licenses/LICENSE-2.0 9d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * 10d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * Unless required by applicable law or agreed to in writing, software 11d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS, 12d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * See the License for the specific language governing permissions and 14d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * limitations under the License. 15d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta */ 16d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 17d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Guptapackage com.android.layoutlib.bridge.android.support; 18d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 19d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Guptaimport com.android.ide.common.rendering.api.LayoutLog; 2037dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Guptaimport com.android.ide.common.rendering.api.LayoutlibCallback; 21d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Guptaimport com.android.layoutlib.bridge.Bridge; 22d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Guptaimport com.android.layoutlib.bridge.android.BridgeContext; 232bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Guptaimport com.android.layoutlib.bridge.android.RenderParamsFlags; 24671b7f9b99316f7224c1213d6923d449a2de9b62Deepanshu Guptaimport com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException; 25d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 26476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.NonNull; 27476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.Nullable; 28d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Guptaimport android.content.Context; 29d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Guptaimport android.view.View; 30d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 31ccbc11770397888cf7780925bb4c7cf1d2f2f80eDeepanshu Guptaimport static com.android.layoutlib.bridge.util.ReflectionUtils.getCause; 322bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Guptaimport static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod; 332bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Guptaimport static com.android.layoutlib.bridge.util.ReflectionUtils.invoke; 34d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 35d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta/** 36d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * Utility class for working with android.support.v7.widget.RecyclerView 37d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta */ 38d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Guptapublic class RecyclerViewUtil { 39d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 402bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta private static final String RV_PKG_PREFIX = "android.support.v7.widget."; 412bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta public static final String CN_RECYCLER_VIEW = RV_PKG_PREFIX + "RecyclerView"; 42d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager"; 43d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter"; 44d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 452bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta // LinearLayoutManager related constants. 462bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta private static final String CN_LINEAR_LAYOUT_MANAGER = RV_PKG_PREFIX + "LinearLayoutManager"; 472bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class}; 482bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta 49d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta /** 50d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a 51d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView} 52d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * that is passed. 53d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * <p/> 54d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * Any exceptions thrown during the process are logged in {@link Bridge#getLog()} 55d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta */ 56d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context, 5761f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout) { 58d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta try { 5961f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta setLayoutManager(recyclerView, context, layoutlibCallback); 6061f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta Object adapter = createAdapter(layoutlibCallback); 6161f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta if (adapter != null) { 6261f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter"); 6361f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta setProperty(adapter, int.class, adapterLayout, "setLayoutId"); 6461f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta } 65d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } catch (ReflectionException e) { 6661f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta Throwable cause = getCause(e); 67d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta Bridge.getLog().error(LayoutLog.TAG_BROKEN, 6861f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta "Error occurred while trying to setup RecyclerView.", cause, null); 69d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 70d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 71d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 72d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context, 7337dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta @NonNull LayoutlibCallback callback) throws ReflectionException { 742bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta if (getLayoutManager(recyclerView) == null) { 752bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta // Only set the layout manager if not already set by the recycler view. 762bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta Object layoutManager = createLayoutManager(context, callback); 7761f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta if (layoutManager != null) { 7861f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager"); 7961f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta } 80d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 81d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 82d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 832bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta /** Creates a LinearLayoutManager using the provided context. */ 84d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta @Nullable 852bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta private static Object createLayoutManager(@NonNull Context context, 862bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta @NonNull LayoutlibCallback callback) 87d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta throws ReflectionException { 88d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta try { 892bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta return callback.loadView(CN_LINEAR_LAYOUT_MANAGER, LLM_CONSTRUCTOR_SIGNATURE, 9061f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta new Object[]{context}); 91d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } catch (Exception e) { 92d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta throw new ReflectionException(e); 93d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 94d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 95d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 96d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta @Nullable 9761f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta private static Object getLayoutManager(View recyclerView) throws ReflectionException { 98671b7f9b99316f7224c1213d6923d449a2de9b62Deepanshu Gupta return invoke(getMethod(recyclerView.getClass(), "getLayoutManager"), recyclerView); 992bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta } 1002bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta 1012bc2daa74eef01135f717eadfab87538a9bef29fDeepanshu Gupta @Nullable 10261f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback) 10361f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta throws ReflectionException { 10461f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta Boolean ideSupport = 10561f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); 106d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta if (ideSupport != Boolean.TRUE) { 107d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta return null; 108d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 109d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta try { 11061f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta return layoutlibCallback.loadClass(CN_ADAPTER, new Class[0], new Object[0]); 111d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } catch (Exception e) { 112d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta throw new ReflectionException(e); 113d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 114d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 115d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 11661f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta private static void setProperty(@NonNull Object object, @NonNull String propertyClassName, 11761f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta @NonNull Object propertyValue, @NonNull String propertySetter) 11861f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta throws ReflectionException { 11961f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta Class<?> propertyClass = getClassInstance(propertyValue, propertyClassName); 12061f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta setProperty(object, propertyClass, propertyValue, propertySetter); 12161f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta } 12261f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta 12361f23e9bf7d784e7a52168196758c4f6c6853e77Deepanshu Gupta private static void setProperty(@NonNull Object object, @NonNull Class<?> propertyClass, 124d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta @Nullable Object propertyValue, @NonNull String propertySetter) 125d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta throws ReflectionException { 126671b7f9b99316f7224c1213d6923d449a2de9b62Deepanshu Gupta invoke(getMethod(object.getClass(), propertySetter, propertyClass), object, propertyValue); 127d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 128d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta 129d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta /** 130d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * Looks through the class hierarchy of {@code object} at runtime and returns the class matching 131d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * the name {@code className}. 132d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * <p/> 133d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * This is used when we cannot use Class.forName() since the class we want was loaded from a 134d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta * different ClassLoader. 135d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta */ 136d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta @NonNull 137d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta private static Class<?> getClassInstance(@NonNull Object object, @NonNull String className) { 138d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta Class<?> superClass = object.getClass(); 139d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta while (superClass != null) { 140d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta if (className.equals(superClass.getName())) { 141d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta return superClass; 142d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 143d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta superClass = superClass.getSuperclass(); 144d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 145d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta throw new RuntimeException("invalid object/classname combination."); 146d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta } 147d345f44a87de1088fcd19e021238852bbffbbaecDeepanshu Gupta} 148