• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
2   * Copyright (C) 2022 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  
17  package com.android.layoutlib.bridge.intensive.setup;
18  
19  import com.android.ide.common.rendering.api.ActionBarCallback;
20  import com.android.ide.common.rendering.api.AdapterBinding;
21  import com.android.ide.common.rendering.api.ILayoutPullParser;
22  import com.android.ide.common.rendering.api.LayoutlibCallback;
23  import com.android.ide.common.rendering.api.ResourceReference;
24  import com.android.ide.common.rendering.api.ResourceValue;
25  import com.android.resources.ResourceType;
26  import com.android.utils.ILogger;
27  
28  import org.kxml2.io.KXmlParser;
29  import org.xmlpull.v1.XmlPullParser;
30  import org.xmlpull.v1.XmlPullParserException;
31  
32  import android.annotation.NonNull;
33  import android.annotation.Nullable;
34  
35  import java.io.ByteArrayInputStream;
36  import java.io.ByteArrayOutputStream;
37  import java.io.File;
38  import java.io.FileInputStream;
39  import java.io.FileNotFoundException;
40  import java.io.IOException;
41  import java.lang.reflect.Constructor;
42  import java.lang.reflect.Field;
43  import java.lang.reflect.Modifier;
44  import java.util.HashMap;
45  import java.util.Map;
46  
47  import com.google.common.io.ByteStreams;
48  
49  import static com.android.ide.common.rendering.api.ResourceNamespace.RES_AUTO;
50  
51  public class LayoutLibTestCallback extends LayoutlibCallback {
52      private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication";
53  
54      private final Map<Integer, ResourceReference> mProjectResources = new HashMap<>();
55      private final Map<ResourceReference, Integer> mResources = new HashMap<>();
56      private final ILogger mLog;
57      private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
58      private final ClassLoader mModuleClassLoader;
59      private String mAdaptiveIconMaskPath;
60  
LayoutLibTestCallback(ILogger logger, ClassLoader classLoader)61      public LayoutLibTestCallback(ILogger logger, ClassLoader classLoader) {
62          mLog = logger;
63          mModuleClassLoader = classLoader;
64      }
65  
initResources()66      public void initResources() throws ClassNotFoundException {
67          Class<?> rClass = mModuleClassLoader.loadClass(PACKAGE_NAME + ".R");
68          Class<?>[] nestedClasses = rClass.getDeclaredClasses();
69          for (Class<?> resClass : nestedClasses) {
70              final ResourceType resType = ResourceType.fromClassName(resClass.getSimpleName());
71  
72              if (resType != null) {
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 == int.class) {
79                                  final Integer value = (Integer) field.get(null);
80                                  ResourceReference reference =
81                                          new ResourceReference(RES_AUTO, resType, field.getName());
82                                  mProjectResources.put(value, reference);
83                                  mResources.put(reference, value);
84                              } else if (!(type.isArray() && type.getComponentType() == int.class)) {
85                                  mLog.error(null, "Unknown field type in R class: %1$s", type);
86                              }
87                          } catch (IllegalAccessException e) {
88                              mLog.error(e, "Malformed R class: %1$s", PACKAGE_NAME + ".R");
89                          }
90                      }
91                  }
92              }
93          }
94      }
95  
96  
97      @Override
loadView(@onNull String name, @NonNull Class[] constructorSignature, Object[] constructorArgs)98      public Object loadView(@NonNull String name, @NonNull Class[] constructorSignature, Object[] constructorArgs)
99              throws Exception {
100          Class<?> viewClass = mModuleClassLoader.loadClass(name);
101          Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature);
102          viewConstructor.setAccessible(true);
103          return viewConstructor.newInstance(constructorArgs);
104      }
105  
106      @Override
resolveResourceId(int id)107      public ResourceReference resolveResourceId(int id) {
108          return mProjectResources.get(id);
109      }
110  
111      @Override
getOrGenerateResourceId(@onNull ResourceReference resource)112      public int getOrGenerateResourceId(@NonNull ResourceReference resource) {
113          Integer id = mResources.get(resource);
114          return id != null ? id : 0;
115      }
116  
117      @Override
getParser(@onNull ResourceValue layoutResource)118      public ILayoutPullParser getParser(@NonNull ResourceValue layoutResource) {
119          try {
120              return LayoutPullParser.createFromFile(new File(layoutResource.getValue()));
121          } catch (FileNotFoundException e) {
122              return null;
123          }
124      }
125  
126      @Override
getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, ResourceReference itemRef, int fullPosition, int positionPerType, int fullParentPosition, int parentPositionPerType, ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue)127      public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
128              ResourceReference itemRef, int fullPosition, int positionPerType,
129              int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
130              ViewAttribute viewAttribute, Object defaultValue) {
131          return null;
132      }
133  
134      @Override
getAdapterBinding(Object viewObject, Map<String, String> attributes)135      public AdapterBinding getAdapterBinding(Object viewObject, Map<String, String> attributes) {
136          return null;
137      }
138  
139      @Override
getActionBarCallback()140      public ActionBarCallback getActionBarCallback() {
141          return mActionBarCallback;
142      }
143  
144      @Override
145      @Nullable
createXmlParserForPsiFile(@onNull String fileName)146      public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) {
147          return createXmlParserForFile(fileName);
148      }
149  
150      @Override
151      @Nullable
createXmlParserForFile(@onNull String fileName)152      public XmlPullParser createXmlParserForFile(@NonNull String fileName) {
153          try (FileInputStream fileStream = new FileInputStream(fileName)) {
154              // Read data fully to memory to be able to close the file stream.
155              ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
156              ByteStreams.copy(fileStream, byteOutputStream);
157              KXmlParser parser = new KXmlParser();
158              parser.setInput(new ByteArrayInputStream(byteOutputStream.toByteArray()), null);
159              parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
160              return parser;
161          } catch (IOException | XmlPullParserException e) {
162              return null;
163          }
164      }
165  
166      @Override
167      @NonNull
createXmlParser()168      public XmlPullParser createXmlParser() {
169          return new KXmlParser();
170      }
171  
172      @Override
getApplicationId()173      public String getApplicationId() {
174          return PACKAGE_NAME;
175      }
176  
177      @Override
getResourcePackage()178      public String getResourcePackage() {
179          return PACKAGE_NAME;
180      }
181  
182      @Override
findClass(String name)183      public Class<?> findClass(String name) throws ClassNotFoundException {
184          return mModuleClassLoader.loadClass(name);
185      }
186  }
187