186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta/*
286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta * Copyright (C) 2014 The Android Open Source Project
386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta *
486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta * you may not use this file except in compliance with the License.
686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta * You may obtain a copy of the License at
786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta *
886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta *
1086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta * Unless required by applicable law or agreed to in writing, software
1186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
1286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta * See the License for the specific language governing permissions and
1486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta * limitations under the License.
1586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta */
1686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
1786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptapackage com.android.layoutlib.bridge.intensive.setup;
1886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
1986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.android.SdkConstants;
2086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.android.ide.common.rendering.api.ActionBarCallback;
2186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.android.ide.common.rendering.api.AdapterBinding;
2286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.android.ide.common.rendering.api.ILayoutPullParser;
2337dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Guptaimport com.android.ide.common.rendering.api.LayoutlibCallback;
243dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Guptaimport com.android.ide.common.rendering.api.ParserFactory;
2586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.android.ide.common.rendering.api.ResourceReference;
2686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.android.ide.common.rendering.api.ResourceValue;
27ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Guptaimport com.android.ide.common.rendering.api.SessionParams.Key;
2886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.android.ide.common.resources.IntArrayWrapper;
29ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Guptaimport com.android.layoutlib.bridge.android.RenderParamsFlags;
30a035dfbf8ddfc8d301dd96088eb50e8c2e63e487Deepanshu Guptaimport com.android.resources.ResourceType;
3186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.android.util.Pair;
3286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.android.utils.ILogger;
3386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
3478af25584633462e4ab8cf9bafe10f43e7cb2d83Deepanshu Guptaimport org.kxml2.io.KXmlParser;
3578af25584633462e4ab8cf9bafe10f43e7cb2d83Deepanshu Guptaimport org.xmlpull.v1.XmlPullParser;
363dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Guptaimport org.xmlpull.v1.XmlPullParserException;
3778af25584633462e4ab8cf9bafe10f43e7cb2d83Deepanshu Gupta
38476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.NonNull;
39476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.Nullable;
40476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Gupta
4186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport java.io.File;
4286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport java.lang.reflect.Constructor;
4386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport java.lang.reflect.Field;
4486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport java.lang.reflect.Modifier;
4586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport java.util.Map;
4686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
4786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Guptaimport com.google.android.collect.Maps;
4886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
49a035dfbf8ddfc8d301dd96088eb50e8c2e63e487Deepanshu Guptaimport static org.junit.Assert.fail;
50a035dfbf8ddfc8d301dd96088eb50e8c2e63e487Deepanshu Gupta
5186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta@SuppressWarnings("deprecation") // For Pair
5237dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Guptapublic class LayoutLibTestCallback extends LayoutlibCallback {
5386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
5486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    private static final String PROJECT_CLASSES_LOCATION = "/testApp/MyApplication/build/intermediates/classes/debug/";
5586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication";
5686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
5786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    private final Map<Integer, Pair<ResourceType, String>> mProjectResources = Maps.newHashMap();
5886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    private final Map<IntArrayWrapper, String> mStyleableValueToNameMap = Maps.newHashMap();
5986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap();
6086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    private final ILogger mLog;
6186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
6237dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta    private final ClassLoader mModuleClassLoader = new ModuleClassLoader(PROJECT_CLASSES_LOCATION);
6386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
6486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public LayoutLibTestCallback(ILogger logger) {
6586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        mLog = logger;
6686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
6786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
6886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public void initResources() throws ClassNotFoundException {
6937dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta        Class<?> rClass = mModuleClassLoader.loadClass(PACKAGE_NAME + ".R");
7086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        Class<?>[] nestedClasses = rClass.getDeclaredClasses();
7186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        for (Class<?> resClass : nestedClasses) {
7286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta            final ResourceType resType = ResourceType.getEnum(resClass.getSimpleName());
7386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
7486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta            if (resType != null) {
7586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                final Map<String, Integer> resName2Id = Maps.newHashMap();
7686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                mResources.put(resType, resName2Id);
7786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
7886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                for (Field field : resClass.getDeclaredFields()) {
7986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                    final int modifiers = field.getModifiers();
8086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                    if (Modifier.isStatic(modifiers)) { // May not be final in library projects
8186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                        final Class<?> type = field.getType();
8286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                        try {
8386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                            if (type.isArray() && type.getComponentType() == int.class) {
8486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                                mStyleableValueToNameMap.put(
8586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                                        new IntArrayWrapper((int[]) field.get(null)),
8686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                                        field.getName());
8786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                            } else if (type == int.class) {
8886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                                final Integer value = (Integer) field.get(null);
8986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                                mProjectResources.put(value, Pair.of(resType, field.getName()));
9086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                                resName2Id.put(field.getName(), value);
9186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                            } else {
9286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                                mLog.error(null, "Unknown field type in R class: %1$s", type);
9386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                            }
9486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                        } catch (IllegalAccessException ignored) {
9586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                            mLog.error(ignored, "Malformed R class: %1$s", PACKAGE_NAME + ".R");
9686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                        }
9786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                    }
9886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                }
9986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta            }
10086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        }
10186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
10286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
10386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
10486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
10586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
10686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta            throws Exception {
10737dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta        Class<?> viewClass = mModuleClassLoader.loadClass(name);
10886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature);
10986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        viewConstructor.setAccessible(true);
11086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return viewConstructor.newInstance(constructorArgs);
11186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
11286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
11386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
11486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public String getNamespace() {
11586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S,
11686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta                PACKAGE_NAME);
11786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
11886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
11986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
12086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public Pair<ResourceType, String> resolveResourceId(int id) {
12186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return mProjectResources.get(id);
12286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
12386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
12486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
12586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public String resolveResourceId(int[] id) {
12686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return mStyleableValueToNameMap.get(new IntArrayWrapper(id));
12786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
12886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
12986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
13086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public Integer getResourceId(ResourceType type, String name) {
13186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return mResources.get(type).get(name);
13286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
13386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
13486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
13586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public ILayoutPullParser getParser(String layoutName) {
136a035dfbf8ddfc8d301dd96088eb50e8c2e63e487Deepanshu Gupta        fail("This method shouldn't be called by this version of LayoutLib.");
13786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return null;
13886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
13986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
14086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
14186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public ILayoutPullParser getParser(ResourceValue layoutResource) {
14286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return new LayoutPullParser(new File(layoutResource.getValue()));
14386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
14486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
14586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
14686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
14786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta            ResourceReference itemRef, int fullPosition, int positionPerType,
14886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta            int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
14986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta            ViewAttribute viewAttribute, Object defaultValue) {
15086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return null;
15186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
15286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
15386eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
15486eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
15586eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta            Object viewObject) {
15686eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return null;
15786eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
15886eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta
15986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    @Override
16086eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    public ActionBarCallback getActionBarCallback() {
16186eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta        return mActionBarCallback;
16286eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta    }
16337dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta
16437dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta    @Override
16537dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta    public boolean supports(int ideFeature) {
16637dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta        return false;
16737dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta    }
16878af25584633462e4ab8cf9bafe10f43e7cb2d83Deepanshu Gupta
16978af25584633462e4ab8cf9bafe10f43e7cb2d83Deepanshu Gupta    @NonNull
17078af25584633462e4ab8cf9bafe10f43e7cb2d83Deepanshu Gupta    @Override
1713dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Gupta    public ParserFactory getParserFactory() {
1723dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Gupta        return new ParserFactory() {
1733dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Gupta            @NonNull
1743dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Gupta            @Override
1753dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Gupta            public XmlPullParser createParser(@Nullable String debugName)
1763dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Gupta                    throws XmlPullParserException {
1773dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Gupta                return new KXmlParser();
1783dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Gupta            }
1793dc19883c571a829c36aa603c04a55c91b00dd57Deepanshu Gupta        };
18078af25584633462e4ab8cf9bafe10f43e7cb2d83Deepanshu Gupta    }
181ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Gupta
182ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Gupta    @Override
183ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Gupta    public <T> T getFlag(Key<T> key) {
184ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Gupta        if (key.equals(RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE)) {
185ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Gupta            return (T) PACKAGE_NAME;
186ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Gupta        }
187ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Gupta        return null;
188ceb1ab1d2a71c6d4e0cccf110a198ff83cb97e5aDeepanshu Gupta    }
18986eb8b4e29b1ad889fc814bae723d4d6e491f53fDeepanshu Gupta}
190