LayoutLibTestCallback.java revision 78af25584633462e4ab8cf9bafe10f43e7cb2d83
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.layoutlib.bridge.intensive.setup;
18
19import com.android.SdkConstants;
20import com.android.annotations.NonNull;
21import com.android.annotations.Nullable;
22import com.android.ide.common.rendering.api.ActionBarCallback;
23import com.android.ide.common.rendering.api.AdapterBinding;
24import com.android.ide.common.rendering.api.ILayoutPullParser;
25import com.android.ide.common.rendering.api.LayoutlibCallback;
26import com.android.ide.common.rendering.api.ResourceReference;
27import com.android.ide.common.rendering.api.ResourceValue;
28import com.android.ide.common.resources.IntArrayWrapper;
29import com.android.resources.ResourceType;
30import com.android.util.Pair;
31import com.android.utils.ILogger;
32
33import org.kxml2.io.KXmlParser;
34import org.xmlpull.v1.XmlPullParser;
35
36import java.io.File;
37import java.lang.reflect.Constructor;
38import java.lang.reflect.Field;
39import java.lang.reflect.Modifier;
40import java.util.Map;
41
42import com.google.android.collect.Maps;
43
44import static org.junit.Assert.fail;
45
46@SuppressWarnings("deprecation") // For Pair
47public class LayoutLibTestCallback extends LayoutlibCallback {
48
49    private static final String PROJECT_CLASSES_LOCATION = "/testApp/MyApplication/build/intermediates/classes/debug/";
50    private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication";
51
52    private final Map<Integer, Pair<ResourceType, String>> mProjectResources = Maps.newHashMap();
53    private final Map<IntArrayWrapper, String> mStyleableValueToNameMap = Maps.newHashMap();
54    private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap();
55    private final ILogger mLog;
56    private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
57    private final ClassLoader mModuleClassLoader = new ModuleClassLoader(PROJECT_CLASSES_LOCATION);
58
59    public LayoutLibTestCallback(ILogger logger) {
60        mLog = logger;
61    }
62
63    public void initResources() throws ClassNotFoundException {
64        Class<?> rClass = mModuleClassLoader.loadClass(PACKAGE_NAME + ".R");
65        Class<?>[] nestedClasses = rClass.getDeclaredClasses();
66        for (Class<?> resClass : nestedClasses) {
67            final ResourceType resType = ResourceType.getEnum(resClass.getSimpleName());
68
69            if (resType != null) {
70                final Map<String, Integer> resName2Id = Maps.newHashMap();
71                mResources.put(resType, resName2Id);
72
73                for (Field field : resClass.getDeclaredFields()) {
74                    final int modifiers = field.getModifiers();
75                    if (Modifier.isStatic(modifiers)) { // May not be final in library projects
76                        final Class<?> type = field.getType();
77                        try {
78                            if (type.isArray() && type.getComponentType() == int.class) {
79                                mStyleableValueToNameMap.put(
80                                        new IntArrayWrapper((int[]) field.get(null)),
81                                        field.getName());
82                            } else if (type == int.class) {
83                                final Integer value = (Integer) field.get(null);
84                                mProjectResources.put(value, Pair.of(resType, field.getName()));
85                                resName2Id.put(field.getName(), value);
86                            } else {
87                                mLog.error(null, "Unknown field type in R class: %1$s", type);
88                            }
89                        } catch (IllegalAccessException ignored) {
90                            mLog.error(ignored, "Malformed R class: %1$s", PACKAGE_NAME + ".R");
91                        }
92                    }
93                }
94            }
95        }
96    }
97
98
99    @Override
100    public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
101            throws Exception {
102        Class<?> viewClass = mModuleClassLoader.loadClass(name);
103        Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature);
104        viewConstructor.setAccessible(true);
105        return viewConstructor.newInstance(constructorArgs);
106    }
107
108    @Override
109    public String getNamespace() {
110        return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S,
111                PACKAGE_NAME);
112    }
113
114    @Override
115    public Pair<ResourceType, String> resolveResourceId(int id) {
116        return mProjectResources.get(id);
117    }
118
119    @Override
120    public String resolveResourceId(int[] id) {
121        return mStyleableValueToNameMap.get(new IntArrayWrapper(id));
122    }
123
124    @Override
125    public Integer getResourceId(ResourceType type, String name) {
126        return mResources.get(type).get(name);
127    }
128
129    @Override
130    public ILayoutPullParser getParser(String layoutName) {
131        fail("This method shouldn't be called by this version of LayoutLib.");
132        return null;
133    }
134
135    @Override
136    public ILayoutPullParser getParser(ResourceValue layoutResource) {
137        return new LayoutPullParser(new File(layoutResource.getValue()));
138    }
139
140    @Override
141    public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
142            ResourceReference itemRef, int fullPosition, int positionPerType,
143            int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
144            ViewAttribute viewAttribute, Object defaultValue) {
145        return null;
146    }
147
148    @Override
149    public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
150            Object viewObject) {
151        return null;
152    }
153
154    @Override
155    public ActionBarCallback getActionBarCallback() {
156        return mActionBarCallback;
157    }
158
159    @Override
160    public boolean supports(int ideFeature) {
161        return false;
162    }
163
164    @NonNull
165    @Override
166    public XmlPullParser createParser(@Nullable String name) {
167        return new KXmlParser();
168    }
169}
170