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