198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta/*
298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta * Copyright (C) 2014 The Android Open Source Project
398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta *
498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta * you may not use this file except in compliance with the License.
698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta * You may obtain a copy of the License at
798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta *
898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta *
1098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta * Unless required by applicable law or agreed to in writing, software
1198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
1298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta * See the License for the specific language governing permissions and
1498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta * limitations under the License.
1598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta */
1698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
1798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptapackage com.android.layoutlib.bridge.intensive.setup;
1898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
1998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.android.SdkConstants;
2098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.android.ide.common.rendering.api.ActionBarCallback;
2198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.android.ide.common.rendering.api.AdapterBinding;
2298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.android.ide.common.rendering.api.ILayoutPullParser;
23b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Guptaimport com.android.ide.common.rendering.api.LayoutlibCallback;
244455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Guptaimport com.android.ide.common.rendering.api.ParserFactory;
2598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.android.ide.common.rendering.api.ResourceReference;
2698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.android.ide.common.rendering.api.ResourceValue;
27d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Guptaimport com.android.ide.common.rendering.api.SessionParams.Key;
2898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.android.ide.common.resources.IntArrayWrapper;
29d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Guptaimport com.android.layoutlib.bridge.android.RenderParamsFlags;
30ba6a2dc17b4d86982f7e5dfd4e5b3b51f67e116bDeepanshu Guptaimport com.android.resources.ResourceType;
3198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.android.util.Pair;
3298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.android.utils.ILogger;
3398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
344bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Guptaimport org.kxml2.io.KXmlParser;
354bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Guptaimport org.xmlpull.v1.XmlPullParser;
364455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Guptaimport org.xmlpull.v1.XmlPullParserException;
374bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta
38442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.NonNull;
39442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Guptaimport android.annotation.Nullable;
40442aee6bc1abfb143dcfa1ba60d696e576d066c4Deepanshu Gupta
4198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport java.io.File;
42d4c426d085994b714f394b982451cc7fa27ff1b7Diego Perezimport java.io.FileNotFoundException;
4398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport java.lang.reflect.Constructor;
4498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport java.lang.reflect.Field;
4598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport java.lang.reflect.Modifier;
4698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport java.util.Map;
4798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
4898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Guptaimport com.google.android.collect.Maps;
4998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
50ba6a2dc17b4d86982f7e5dfd4e5b3b51f67e116bDeepanshu Guptaimport static org.junit.Assert.fail;
51ba6a2dc17b4d86982f7e5dfd4e5b3b51f67e116bDeepanshu Gupta
5298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta@SuppressWarnings("deprecation") // For Pair
53b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Guptapublic class LayoutLibTestCallback extends LayoutlibCallback {
5498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
5598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    private static final String PROJECT_CLASSES_LOCATION = "/testApp/MyApplication/build/intermediates/classes/debug/";
5698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication";
5798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
5898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    private final Map<Integer, Pair<ResourceType, String>> mProjectResources = Maps.newHashMap();
5998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    private final Map<IntArrayWrapper, String> mStyleableValueToNameMap = Maps.newHashMap();
6098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap();
6198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    private final ILogger mLog;
6298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
63c702b276058dc81072b9db892d7ac9dd35da1935Diego Perez    private final ClassLoader mModuleClassLoader;
64b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard    private String mAdaptiveIconMaskPath;
6598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
66c702b276058dc81072b9db892d7ac9dd35da1935Diego Perez    public LayoutLibTestCallback(ILogger logger, ClassLoader classLoader) {
6798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        mLog = logger;
68c702b276058dc81072b9db892d7ac9dd35da1935Diego Perez        mModuleClassLoader = classLoader;
6998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
7098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
7198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public void initResources() throws ClassNotFoundException {
72b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta        Class<?> rClass = mModuleClassLoader.loadClass(PACKAGE_NAME + ".R");
7398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        Class<?>[] nestedClasses = rClass.getDeclaredClasses();
7498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        for (Class<?> resClass : nestedClasses) {
7598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta            final ResourceType resType = ResourceType.getEnum(resClass.getSimpleName());
7698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
7798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta            if (resType != null) {
7898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                final Map<String, Integer> resName2Id = Maps.newHashMap();
7998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                mResources.put(resType, resName2Id);
8098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
8198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                for (Field field : resClass.getDeclaredFields()) {
8298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                    final int modifiers = field.getModifiers();
8398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                    if (Modifier.isStatic(modifiers)) { // May not be final in library projects
8498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                        final Class<?> type = field.getType();
8598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                        try {
8698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                            if (type.isArray() && type.getComponentType() == int.class) {
8798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                                mStyleableValueToNameMap.put(
8898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                                        new IntArrayWrapper((int[]) field.get(null)),
8998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                                        field.getName());
9098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                            } else if (type == int.class) {
9198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                                final Integer value = (Integer) field.get(null);
9298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                                mProjectResources.put(value, Pair.of(resType, field.getName()));
9398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                                resName2Id.put(field.getName(), value);
9498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                            } else {
9598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                                mLog.error(null, "Unknown field type in R class: %1$s", type);
9698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                            }
9798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                        } catch (IllegalAccessException ignored) {
9898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                            mLog.error(ignored, "Malformed R class: %1$s", PACKAGE_NAME + ".R");
9998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                        }
10098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                    }
10198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                }
10298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta            }
10398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        }
10498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
10598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
10698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
10798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
10898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
10998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta            throws Exception {
110b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta        Class<?> viewClass = mModuleClassLoader.loadClass(name);
11198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature);
11298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        viewConstructor.setAccessible(true);
11398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        return viewConstructor.newInstance(constructorArgs);
11498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
11598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
11698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
11798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public String getNamespace() {
11898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S,
11998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta                PACKAGE_NAME);
12098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
12198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
12298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
12398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public Pair<ResourceType, String> resolveResourceId(int id) {
12498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        return mProjectResources.get(id);
12598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
12698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
12798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
12898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public String resolveResourceId(int[] id) {
12998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        return mStyleableValueToNameMap.get(new IntArrayWrapper(id));
13098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
13198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
13298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
13398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public Integer getResourceId(ResourceType type, String name) {
1348c9f093a47bb9fe3b483ff47f55d957c51c7a1d9Jerome Gaillard        Map<String, Integer> resName2Id = mResources.get(type);
1358c9f093a47bb9fe3b483ff47f55d957c51c7a1d9Jerome Gaillard        if (resName2Id == null) {
1368c9f093a47bb9fe3b483ff47f55d957c51c7a1d9Jerome Gaillard            return null;
1378c9f093a47bb9fe3b483ff47f55d957c51c7a1d9Jerome Gaillard        }
1388c9f093a47bb9fe3b483ff47f55d957c51c7a1d9Jerome Gaillard        return resName2Id.get(name);
13998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
14098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
14198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
14298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public ILayoutPullParser getParser(String layoutName) {
143ba6a2dc17b4d86982f7e5dfd4e5b3b51f67e116bDeepanshu Gupta        fail("This method shouldn't be called by this version of LayoutLib.");
14498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        return null;
14598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
14698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
14798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
14898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public ILayoutPullParser getParser(ResourceValue layoutResource) {
149d4c426d085994b714f394b982451cc7fa27ff1b7Diego Perez        try {
150d4c426d085994b714f394b982451cc7fa27ff1b7Diego Perez            return LayoutPullParser.createFromFile(new File(layoutResource.getValue()));
151d4c426d085994b714f394b982451cc7fa27ff1b7Diego Perez        } catch (FileNotFoundException e) {
152d4c426d085994b714f394b982451cc7fa27ff1b7Diego Perez            return null;
153d4c426d085994b714f394b982451cc7fa27ff1b7Diego Perez        }
15498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
15598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
15698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
15798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
15898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta            ResourceReference itemRef, int fullPosition, int positionPerType,
15998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta            int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
16098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta            ViewAttribute viewAttribute, Object defaultValue) {
16198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        return null;
16298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
16398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
16498796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
16598796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
16698796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta            Object viewObject) {
16798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        return null;
16898796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
16998796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta
17098796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    @Override
17198796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    public ActionBarCallback getActionBarCallback() {
17298796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta        return mActionBarCallback;
17398796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta    }
174b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta
175b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta    @Override
176b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta    public boolean supports(int ideFeature) {
177b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta        return false;
178b111e84752652ec862efa7e0fcaa224430feb97fDeepanshu Gupta    }
1794bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta
1804bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    @NonNull
1814bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    @Override
1824455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Gupta    public ParserFactory getParserFactory() {
1834455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Gupta        return new ParserFactory() {
1844455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Gupta            @NonNull
1854455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Gupta            @Override
1864455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Gupta            public XmlPullParser createParser(@Nullable String debugName)
1874455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Gupta                    throws XmlPullParserException {
1884455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Gupta                return new KXmlParser();
1894455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Gupta            }
1904455837e72aa018178c5f8dbc080f7077ad1193fDeepanshu Gupta        };
1914bf28b6af45f76a33b466ad8157aa1844716cbcbDeepanshu Gupta    }
192d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Gupta
193d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Gupta    @Override
194d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Gupta    public <T> T getFlag(Key<T> key) {
195d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Gupta        if (key.equals(RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE)) {
196d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Gupta            return (T) PACKAGE_NAME;
197d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Gupta        }
198b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard        if (key.equals(RenderParamsFlags.FLAG_KEY_ADAPTIVE_ICON_MASK_PATH)) {
199b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard            return (T) mAdaptiveIconMaskPath;
200b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard        }
201d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Gupta        return null;
202d6b76a32f54da1ba6031f891ff58ccce5d221a41Deepanshu Gupta    }
203b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard
204b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard    public void setAdaptiveIconMaskPath(String adaptiveIconMaskPath) {
205b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard        mAdaptiveIconMaskPath = adaptiveIconMaskPath;
206b1bd12381b7b0b57eff6d9bb1a5ae23309ab2b5cJerome Gaillard    }
20798796b464984574c1abe362478c9f47bda62fe7eDeepanshu Gupta}
208