1432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta/* 2432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * Copyright (C) 2015 The Android Open Source Project 3432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * 4432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License"); 5432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * you may not use this file except in compliance with the License. 6432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * You may obtain a copy of the License at 7432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * 8432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * http://www.apache.org/licenses/LICENSE-2.0 9432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * 10432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software 11432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS, 12432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * See the License for the specific language governing permissions and 14432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * limitations under the License. 15432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta */ 16432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 17432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Guptapackage com.android.layoutlib.bridge.android.support; 18432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 19432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Guptaimport com.android.ide.common.rendering.api.LayoutLog; 20b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Guptaimport com.android.ide.common.rendering.api.LayoutlibCallback; 21432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Guptaimport com.android.layoutlib.bridge.Bridge; 22432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Guptaimport com.android.layoutlib.bridge.android.BridgeContext; 23cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Guptaimport com.android.layoutlib.bridge.android.RenderParamsFlags; 24b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perezimport com.android.layoutlib.bridge.util.ReflectionUtils; 25c102fc0ddf298edd8c1dd17454b564acb1b2bb80Deepanshu Guptaimport com.android.layoutlib.bridge.util.ReflectionUtils.ReflectionException; 26432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 27442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.NonNull; 28442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.Nullable; 29432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Guptaimport android.content.Context; 30432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Guptaimport android.view.View; 31432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 329028fa93da0f9c7dad2176de347cd6e705084c9fDeepanshu Guptaimport static com.android.layoutlib.bridge.util.ReflectionUtils.getCause; 33cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Guptaimport static com.android.layoutlib.bridge.util.ReflectionUtils.getMethod; 34cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Guptaimport static com.android.layoutlib.bridge.util.ReflectionUtils.invoke; 35432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 36432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta/** 37432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * Utility class for working with android.support.v7.widget.RecyclerView 38432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta */ 39432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Guptapublic class RecyclerViewUtil { 40432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 41cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta private static final String RV_PKG_PREFIX = "android.support.v7.widget."; 42cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta public static final String CN_RECYCLER_VIEW = RV_PKG_PREFIX + "RecyclerView"; 43432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta private static final String CN_LAYOUT_MANAGER = CN_RECYCLER_VIEW + "$LayoutManager"; 44432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta private static final String CN_ADAPTER = CN_RECYCLER_VIEW + "$Adapter"; 45432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 46cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta // LinearLayoutManager related constants. 47cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta private static final String CN_LINEAR_LAYOUT_MANAGER = RV_PKG_PREFIX + "LinearLayoutManager"; 48cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta private static final Class<?>[] LLM_CONSTRUCTOR_SIGNATURE = new Class<?>[]{Context.class}; 49cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta 50432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta /** 51432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * Tries to create an Adapter ({@code android.support.v7.widget.RecyclerView.Adapter} and a 52432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * LayoutManager {@code RecyclerView.LayoutManager} and assign these to the {@code RecyclerView} 53432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * that is passed. 54432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * <p/> 55432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta * Any exceptions thrown during the process are logged in {@link Bridge#getLog()} 56432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta */ 57432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta public static void setAdapter(@NonNull View recyclerView, @NonNull BridgeContext context, 58b1ae00e68fac4cb755d3d1b5ed66f0df4adf3fbaDiego Perez @NonNull LayoutlibCallback layoutlibCallback, int adapterLayout, int itemCount) { 59432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta try { 60baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta setLayoutManager(recyclerView, context, layoutlibCallback); 61baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta Object adapter = createAdapter(layoutlibCallback); 62baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta if (adapter != null) { 63baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta setProperty(recyclerView, CN_ADAPTER, adapter, "setAdapter"); 64baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta setProperty(adapter, int.class, adapterLayout, "setLayoutId"); 65b1ae00e68fac4cb755d3d1b5ed66f0df4adf3fbaDiego Perez 66b1ae00e68fac4cb755d3d1b5ed66f0df4adf3fbaDiego Perez if (itemCount != -1) { 67b1ae00e68fac4cb755d3d1b5ed66f0df4adf3fbaDiego Perez setProperty(adapter, int.class, itemCount, "setItemCount"); 68b1ae00e68fac4cb755d3d1b5ed66f0df4adf3fbaDiego Perez } 69baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta } 70432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } catch (ReflectionException e) { 71baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta Throwable cause = getCause(e); 72432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta Bridge.getLog().error(LayoutLog.TAG_BROKEN, 73baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta "Error occurred while trying to setup RecyclerView.", cause, null); 74432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 75432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 76432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 77432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta private static void setLayoutManager(@NonNull View recyclerView, @NonNull BridgeContext context, 78b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta @NonNull LayoutlibCallback callback) throws ReflectionException { 79cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta if (getLayoutManager(recyclerView) == null) { 80cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta // Only set the layout manager if not already set by the recycler view. 81cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta Object layoutManager = createLayoutManager(context, callback); 82baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta if (layoutManager != null) { 83baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta setProperty(recyclerView, CN_LAYOUT_MANAGER, layoutManager, "setLayoutManager"); 84baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta } 85432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 86432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 87432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 88cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta /** Creates a LinearLayoutManager using the provided context. */ 89432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta @Nullable 90cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta private static Object createLayoutManager(@NonNull Context context, 91cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta @NonNull LayoutlibCallback callback) 92432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta throws ReflectionException { 93432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta try { 94cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta return callback.loadView(CN_LINEAR_LAYOUT_MANAGER, LLM_CONSTRUCTOR_SIGNATURE, 95baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta new Object[]{context}); 96432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } catch (Exception e) { 97432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta throw new ReflectionException(e); 98432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 99432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 100432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 101432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta @Nullable 102baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta private static Object getLayoutManager(View recyclerView) throws ReflectionException { 103c102fc0ddf298edd8c1dd17454b564acb1b2bb80Deepanshu Gupta return invoke(getMethod(recyclerView.getClass(), "getLayoutManager"), recyclerView); 104cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta } 105cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta 106cb40f2cb35d559d9ea60382b6dee80022c851124Deepanshu Gupta @Nullable 107baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta private static Object createAdapter(@NonNull LayoutlibCallback layoutlibCallback) 108baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta throws ReflectionException { 109baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta Boolean ideSupport = 110baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta layoutlibCallback.getFlag(RenderParamsFlags.FLAG_KEY_RECYCLER_VIEW_SUPPORT); 111432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta if (ideSupport != Boolean.TRUE) { 112432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta return null; 113432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 114432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta try { 115baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta return layoutlibCallback.loadClass(CN_ADAPTER, new Class[0], new Object[0]); 116432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } catch (Exception e) { 117432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta throw new ReflectionException(e); 118432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 119432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 120432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 121baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta private static void setProperty(@NonNull Object object, @NonNull String propertyClassName, 122baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta @NonNull Object propertyValue, @NonNull String propertySetter) 123baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta throws ReflectionException { 124b321cbfeb00723a6d18746f0d5bbcfd6b6602f6eDiego Perez Class<?> propertyClass = ReflectionUtils.getClassInstance(propertyValue, propertyClassName); 125baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta setProperty(object, propertyClass, propertyValue, propertySetter); 126baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta } 127baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta 128baf88de1f5c435a788f6c38720354b2dbaa19e60Deepanshu Gupta private static void setProperty(@NonNull Object object, @NonNull Class<?> propertyClass, 129432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta @Nullable Object propertyValue, @NonNull String propertySetter) 130432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta throws ReflectionException { 131c102fc0ddf298edd8c1dd17454b564acb1b2bb80Deepanshu Gupta invoke(getMethod(object.getClass(), propertySetter, propertyClass), object, propertyValue); 132432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta } 133432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta 134432578acb80cf2fa827ddb9595cf46edf0b340b0Deepanshu Gupta} 135