blob: b19cb5860a63a60b1af1895a97cfef0ea1dcdded [file] [log] [blame]
Adam Lesinski282e1812014-01-23 18:17:42 -08001/*
2 * Copyright (C) 2008 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 android.view;
18
Adam Lesinski282e1812014-01-23 18:17:42 -080019import com.android.ide.common.rendering.api.LayoutLog;
Deepanshu Gupta2bc2daa2015-03-30 15:01:03 -070020import com.android.ide.common.rendering.api.LayoutlibCallback;
Adam Lesinski282e1812014-01-23 18:17:42 -080021import com.android.ide.common.rendering.api.MergeCookie;
22import com.android.ide.common.rendering.api.ResourceReference;
23import com.android.ide.common.rendering.api.ResourceValue;
24import com.android.layoutlib.bridge.Bridge;
Jens Ole Lauridsen4dfe4d42015-06-05 08:04:27 -070025import com.android.layoutlib.bridge.BridgeConstants;
Deepanshu Guptad30c1412015-10-20 17:29:44 -070026import com.android.layoutlib.bridge.MockView;
Adam Lesinski282e1812014-01-23 18:17:42 -080027import com.android.layoutlib.bridge.android.BridgeContext;
28import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
Deepanshu Guptaccbc1172015-07-10 17:38:29 -070029import com.android.layoutlib.bridge.android.support.DrawerLayoutUtil;
Deepanshu Gupta61f23e92015-07-06 18:31:20 -070030import com.android.layoutlib.bridge.android.support.RecyclerViewUtil;
Adam Lesinski282e1812014-01-23 18:17:42 -080031import com.android.layoutlib.bridge.impl.ParserFactory;
Deepanshu Gupta61f23e92015-07-06 18:31:20 -070032import com.android.layoutlib.bridge.util.ReflectionUtils;
Adam Lesinski282e1812014-01-23 18:17:42 -080033import com.android.resources.ResourceType;
34import com.android.util.Pair;
35
36import org.xmlpull.v1.XmlPullParser;
37
Deepanshu Guptaccbc1172015-07-10 17:38:29 -070038import android.annotation.NonNull;
Adam Lesinski282e1812014-01-23 18:17:42 -080039import android.content.Context;
Deepanshu Gupta75351772015-08-28 11:53:46 -070040import android.content.res.TypedArray;
Adam Lesinski282e1812014-01-23 18:17:42 -080041import android.util.AttributeSet;
Jerome Gaillard1d8b0bb2016-06-02 15:35:22 +010042import android.widget.NumberPicker;
Adam Lesinski282e1812014-01-23 18:17:42 -080043
44import java.io.File;
Diego Perezfb175662016-05-27 14:33:41 +010045import java.util.Arrays;
46import java.util.Collections;
Deepanshu Guptaccbc1172015-07-10 17:38:29 -070047import java.util.HashMap;
Diego Perezfb175662016-05-27 14:33:41 +010048import java.util.HashSet;
Deepanshu Guptaccbc1172015-07-10 17:38:29 -070049import java.util.Map;
Diego Perezfb175662016-05-27 14:33:41 +010050import java.util.Set;
Adam Lesinski282e1812014-01-23 18:17:42 -080051
Diego Perezfb175662016-05-27 14:33:41 +010052import static com.android.SdkConstants.AUTO_COMPLETE_TEXT_VIEW;
53import static com.android.SdkConstants.BUTTON;
54import static com.android.SdkConstants.CHECKED_TEXT_VIEW;
55import static com.android.SdkConstants.CHECK_BOX;
56import static com.android.SdkConstants.EDIT_TEXT;
57import static com.android.SdkConstants.IMAGE_BUTTON;
Diego Perez55656fe2016-07-22 10:05:32 +010058import static com.android.SdkConstants.IMAGE_VIEW;
Diego Perezfb175662016-05-27 14:33:41 +010059import static com.android.SdkConstants.MULTI_AUTO_COMPLETE_TEXT_VIEW;
60import static com.android.SdkConstants.RADIO_BUTTON;
61import static com.android.SdkConstants.SEEK_BAR;
62import static com.android.SdkConstants.SPINNER;
63import static com.android.SdkConstants.TEXT_VIEW;
Deepanshu Guptaada85902015-03-13 11:06:47 -070064import static com.android.layoutlib.bridge.android.BridgeContext.getBaseContext;
65
Adam Lesinski282e1812014-01-23 18:17:42 -080066/**
67 * Custom implementation of {@link LayoutInflater} to handle custom views.
68 */
69public final class BridgeInflater extends LayoutInflater {
70
Deepanshu Gupta37dbb8b2015-04-14 16:39:41 -070071 private final LayoutlibCallback mLayoutlibCallback;
Diego Perezfb175662016-05-27 14:33:41 +010072 /**
73 * If true, the inflater will try to replace the framework widgets with the AppCompat versions.
74 * Ideally, this should be based on the activity being an AppCompat activity but since that is
75 * not trivial to check from layoutlib, we currently base the decision on the current theme
76 * being an AppCompat theme.
77 */
78 private boolean mLoadAppCompatViews;
Diego Perez3a6cdb12016-07-28 17:04:05 +010079 /**
80 * This set contains the framework views that have an AppCompat version but failed to load.
81 * This might happen because not all widgets are contained in all versions of the support
82 * library.
83 * This will help us to avoid trying to load the AppCompat version multiple times if it
84 * doesn't exist.
85 */
86 private Set<String> mFailedAppCompatViews = new HashSet<>();
Adam Lesinski282e1812014-01-23 18:17:42 -080087 private boolean mIsInMerge = false;
88 private ResourceReference mResourceReference;
Deepanshu Guptaccbc1172015-07-10 17:38:29 -070089 private Map<View, String> mOpenDrawerLayouts;
Adam Lesinski282e1812014-01-23 18:17:42 -080090
Deepanshu Gupta75351772015-08-28 11:53:46 -070091 // Keep in sync with the same value in LayoutInflater.
92 private static final int[] ATTRS_THEME = new int[] {com.android.internal.R.attr.theme };
93
Diego Perezfb175662016-05-27 14:33:41 +010094 private static final String APPCOMPAT_WIDGET_PREFIX = "android.support.v7.widget.AppCompat";
95 /** List of platform widgets that have an AppCompat version */
96 private static final Set<String> APPCOMPAT_VIEWS = Collections.unmodifiableSet(
97 new HashSet<>(
Diego Perez55656fe2016-07-22 10:05:32 +010098 Arrays.asList(TEXT_VIEW, IMAGE_VIEW, BUTTON, EDIT_TEXT, SPINNER,
Diego Perezfb175662016-05-27 14:33:41 +010099 IMAGE_BUTTON, CHECK_BOX, RADIO_BUTTON, CHECKED_TEXT_VIEW,
100 AUTO_COMPLETE_TEXT_VIEW, MULTI_AUTO_COMPLETE_TEXT_VIEW, "RatingBar",
101 SEEK_BAR)));
102
Adam Lesinski282e1812014-01-23 18:17:42 -0800103 /**
104 * List of class prefixes which are tried first by default.
105 * <p/>
106 * This should match the list in com.android.internal.policy.impl.PhoneLayoutInflater.
107 */
108 private static final String[] sClassPrefixList = {
109 "android.widget.",
Deepanshu Guptaf8ea7502015-05-18 18:47:07 -0700110 "android.webkit.",
111 "android.app."
Adam Lesinski282e1812014-01-23 18:17:42 -0800112 };
113
Deepanshu Guptaf8ea7502015-05-18 18:47:07 -0700114 public static String[] getClassPrefixList() {
115 return sClassPrefixList;
116 }
117
Diego Perezfb175662016-05-27 14:33:41 +0100118 private BridgeInflater(LayoutInflater original, Context newContext) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800119 super(original, newContext);
Deepanshu Guptaada85902015-03-13 11:06:47 -0700120 newContext = getBaseContext(newContext);
121 if (newContext instanceof BridgeContext) {
Deepanshu Gupta37dbb8b2015-04-14 16:39:41 -0700122 mLayoutlibCallback = ((BridgeContext) newContext).getLayoutlibCallback();
Diego Perezfb175662016-05-27 14:33:41 +0100123 mLoadAppCompatViews = ((BridgeContext) newContext).isAppCompatTheme();
Deepanshu Guptaada85902015-03-13 11:06:47 -0700124 } else {
Deepanshu Gupta37dbb8b2015-04-14 16:39:41 -0700125 mLayoutlibCallback = null;
Diego Perezfb175662016-05-27 14:33:41 +0100126 mLoadAppCompatViews = false;
Deepanshu Guptaada85902015-03-13 11:06:47 -0700127 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800128 }
129
130 /**
Deepanshu Gupta37dbb8b2015-04-14 16:39:41 -0700131 * Instantiate a new BridgeInflater with an {@link LayoutlibCallback} object.
Adam Lesinski282e1812014-01-23 18:17:42 -0800132 *
133 * @param context The Android application context.
Deepanshu Gupta37dbb8b2015-04-14 16:39:41 -0700134 * @param layoutlibCallback the {@link LayoutlibCallback} object.
Adam Lesinski282e1812014-01-23 18:17:42 -0800135 */
Diego Perezfb175662016-05-27 14:33:41 +0100136 public BridgeInflater(BridgeContext context, LayoutlibCallback layoutlibCallback) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800137 super(context);
Deepanshu Gupta37dbb8b2015-04-14 16:39:41 -0700138 mLayoutlibCallback = layoutlibCallback;
Adam Lesinski282e1812014-01-23 18:17:42 -0800139 mConstructorArgs[0] = context;
Diego Perezfb175662016-05-27 14:33:41 +0100140 mLoadAppCompatViews = context.isAppCompatTheme();
Adam Lesinski282e1812014-01-23 18:17:42 -0800141 }
142
143 @Override
144 public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
145 View view = null;
146
147 try {
Diego Perez3a6cdb12016-07-28 17:04:05 +0100148 if (mLoadAppCompatViews
149 && APPCOMPAT_VIEWS.contains(name)
150 && !mFailedAppCompatViews.contains(name)) {
Diego Perezfb175662016-05-27 14:33:41 +0100151 // We are using an AppCompat theme so try to load the appcompat views
Diego Perez3a6cdb12016-07-28 17:04:05 +0100152 view = loadCustomView(APPCOMPAT_WIDGET_PREFIX + name, attrs, true);
Diego Perezfb175662016-05-27 14:33:41 +0100153
154 if (view == null) {
Diego Perez3a6cdb12016-07-28 17:04:05 +0100155 mFailedAppCompatViews.add(name); // Do not try this one anymore
Diego Perezfb175662016-05-27 14:33:41 +0100156 }
Diego Perez3a6cdb12016-07-28 17:04:05 +0100157 }
Diego Perezfb175662016-05-27 14:33:41 +0100158
Diego Perez3a6cdb12016-07-28 17:04:05 +0100159 if (view == null) {
Diego Perezfb175662016-05-27 14:33:41 +0100160 // First try to find a class using the default Android prefixes
161 for (String prefix : sClassPrefixList) {
162 try {
163 view = createView(name, prefix, attrs);
164 if (view != null) {
165 break;
166 }
167 } catch (ClassNotFoundException e) {
168 // Ignore. We'll try again using the base class below.
169 }
170 }
171
172 // Next try using the parent loader. This will most likely only work for
173 // fully-qualified class names.
Adam Lesinski282e1812014-01-23 18:17:42 -0800174 try {
Diego Perezfb175662016-05-27 14:33:41 +0100175 if (view == null) {
176 view = super.onCreateView(name, attrs);
Adam Lesinski282e1812014-01-23 18:17:42 -0800177 }
178 } catch (ClassNotFoundException e) {
Diego Perezfb175662016-05-27 14:33:41 +0100179 // Ignore. We'll try again using the custom view loader below.
Adam Lesinski282e1812014-01-23 18:17:42 -0800180 }
181 }
182
Adam Lesinski282e1812014-01-23 18:17:42 -0800183 // Finally try again using the custom view loader
Deepanshu Gupta61f23e92015-07-06 18:31:20 -0700184 if (view == null) {
185 view = loadCustomView(name, attrs);
Adam Lesinski282e1812014-01-23 18:17:42 -0800186 }
Deepanshu Guptad30c1412015-10-20 17:29:44 -0700187 } catch (InflateException e) {
188 // Don't catch the InflateException below as that results in hiding the real cause.
189 throw e;
Adam Lesinski282e1812014-01-23 18:17:42 -0800190 } catch (Exception e) {
191 // Wrap the real exception in a ClassNotFoundException, so that the calling method
192 // can deal with it.
Deepanshu Guptad345f442015-03-05 18:38:37 -0800193 throw new ClassNotFoundException("onCreateView", e);
Adam Lesinski282e1812014-01-23 18:17:42 -0800194 }
195
196 setupViewInContext(view, attrs);
197
198 return view;
199 }
200
201 @Override
Alan Viverette6194d722015-03-20 15:49:06 -0700202 public View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
Deepanshu Gupta75351772015-08-28 11:53:46 -0700203 boolean ignoreThemeAttr) {
Diego Pereze852d192016-05-27 14:38:49 +0100204 View view = null;
205 if (name.equals("view")) {
206 // This is usually done by the superclass but this allows us catching the error and
207 // reporting something useful.
208 name = attrs.getAttributeValue(null, "class");
209
210 if (name == null) {
211 Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to inflate view tag without " +
212 "class attribute", null);
213 // We weren't able to resolve the view so we just pass a mock View to be able to
214 // continue rendering.
215 view = new MockView(context, attrs);
216 ((MockView) view).setText("view");
217 }
218 }
219
Adam Lesinski282e1812014-01-23 18:17:42 -0800220 try {
Diego Pereze852d192016-05-27 14:38:49 +0100221 if (view == null) {
222 view = super.createViewFromTag(parent, name, context, attrs, ignoreThemeAttr);
223 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800224 } catch (InflateException e) {
Deepanshu Gupta75351772015-08-28 11:53:46 -0700225 // Creation of ContextThemeWrapper code is same as in the super method.
226 // Apply a theme wrapper, if allowed and one is specified.
227 if (!ignoreThemeAttr) {
228 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
229 final int themeResId = ta.getResourceId(0, 0);
230 if (themeResId != 0) {
231 context = new ContextThemeWrapper(context, themeResId);
232 }
233 ta.recycle();
234 }
Deepanshu Guptad30c1412015-10-20 17:29:44 -0700235 if (!(e.getCause() instanceof ClassNotFoundException)) {
236 // There is some unknown inflation exception in inflating a View that was found.
237 view = new MockView(context, attrs);
238 ((MockView) view).setText(name);
239 Bridge.getLog().error(LayoutLog.TAG_BROKEN, e.getMessage(), e, null);
240 } else {
241 final Object lastContext = mConstructorArgs[0];
242 mConstructorArgs[0] = context;
243 // try to load the class from using the custom view loader
244 try {
245 view = loadCustomView(name, attrs);
246 } catch (Exception e2) {
247 // Wrap the real exception in an InflateException so that the calling
248 // method can deal with it.
249 InflateException exception = new InflateException();
250 if (!e2.getClass().equals(ClassNotFoundException.class)) {
251 exception.initCause(e2);
252 } else {
253 exception.initCause(e);
254 }
255 throw exception;
256 } finally {
257 mConstructorArgs[0] = lastContext;
Adam Lesinski282e1812014-01-23 18:17:42 -0800258 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800259 }
260 }
261
262 setupViewInContext(view, attrs);
263
264 return view;
265 }
266
267 @Override
268 public View inflate(int resource, ViewGroup root) {
269 Context context = getContext();
Deepanshu Guptaada85902015-03-13 11:06:47 -0700270 context = getBaseContext(context);
Adam Lesinski282e1812014-01-23 18:17:42 -0800271 if (context instanceof BridgeContext) {
272 BridgeContext bridgeContext = (BridgeContext)context;
273
274 ResourceValue value = null;
275
Deepanshu Gupta37dbb8b2015-04-14 16:39:41 -0700276 @SuppressWarnings("deprecation")
Adam Lesinski282e1812014-01-23 18:17:42 -0800277 Pair<ResourceType, String> layoutInfo = Bridge.resolveResourceId(resource);
278 if (layoutInfo != null) {
279 value = bridgeContext.getRenderResources().getFrameworkResource(
280 ResourceType.LAYOUT, layoutInfo.getSecond());
281 } else {
Deepanshu Gupta37dbb8b2015-04-14 16:39:41 -0700282 layoutInfo = mLayoutlibCallback.resolveResourceId(resource);
Adam Lesinski282e1812014-01-23 18:17:42 -0800283
284 if (layoutInfo != null) {
285 value = bridgeContext.getRenderResources().getProjectResource(
286 ResourceType.LAYOUT, layoutInfo.getSecond());
287 }
288 }
289
290 if (value != null) {
291 File f = new File(value.getValue());
292 if (f.isFile()) {
293 try {
Deepanshu Guptae91096c2015-09-28 18:03:18 -0700294 XmlPullParser parser = ParserFactory.create(f, true);
Adam Lesinski282e1812014-01-23 18:17:42 -0800295
296 BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
Deepanshu Gupta33c1c072015-02-23 09:18:08 -0800297 parser, bridgeContext, value.isFramework());
Adam Lesinski282e1812014-01-23 18:17:42 -0800298
299 return inflate(bridgeParser, root);
300 } catch (Exception e) {
301 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
Deepanshu Guptad345f442015-03-05 18:38:37 -0800302 "Failed to parse file " + f.getAbsolutePath(), e, null);
Adam Lesinski282e1812014-01-23 18:17:42 -0800303
304 return null;
305 }
306 }
307 }
308 }
309 return null;
310 }
311
Diego Perez3a6cdb12016-07-28 17:04:05 +0100312 /**
313 * Instantiates the given view name and returns the instance. If the view doesn't exist, a
314 * MockView or null might be returned.
315 * @param name the custom view name
316 * @param attrs the {@link AttributeSet} to be passed to the view constructor
317 * @param silent if true, errors while loading the view won't be reported and, if the view
318 * doesn't exist, null will be returned.
319 */
320 private View loadCustomView(String name, AttributeSet attrs, boolean silent) throws Exception {
Deepanshu Gupta37dbb8b2015-04-14 16:39:41 -0700321 if (mLayoutlibCallback != null) {
Adam Lesinski282e1812014-01-23 18:17:42 -0800322 // first get the classname in case it's not the node name
323 if (name.equals("view")) {
324 name = attrs.getAttributeValue(null, "class");
Diego Pereze852d192016-05-27 14:38:49 +0100325 if (name == null) {
326 return null;
327 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800328 }
329
330 mConstructorArgs[1] = attrs;
331
Diego Perez3a6cdb12016-07-28 17:04:05 +0100332 Object customView = silent ?
333 mLayoutlibCallback.loadClass(name, mConstructorSignature, mConstructorArgs)
334 : mLayoutlibCallback.loadView(name, mConstructorSignature, mConstructorArgs);
Adam Lesinski282e1812014-01-23 18:17:42 -0800335
336 if (customView instanceof View) {
337 return (View)customView;
338 }
339 }
340
341 return null;
342 }
343
Diego Perez3a6cdb12016-07-28 17:04:05 +0100344 private View loadCustomView(String name, AttributeSet attrs) throws Exception {
345 return loadCustomView(name, attrs, false);
346 }
347
Adam Lesinski282e1812014-01-23 18:17:42 -0800348 private void setupViewInContext(View view, AttributeSet attrs) {
Deepanshu Gupta10019612014-04-18 12:32:38 -0700349 Context context = getContext();
Deepanshu Guptaada85902015-03-13 11:06:47 -0700350 context = getBaseContext(context);
Deepanshu Gupta10019612014-04-18 12:32:38 -0700351 if (context instanceof BridgeContext) {
352 BridgeContext bc = (BridgeContext) context;
353 // get the view key
354 Object viewKey = getViewKeyFromParser(attrs, bc, mResourceReference, mIsInMerge);
355 if (viewKey != null) {
356 bc.addViewKey(view, viewKey);
Adam Lesinski282e1812014-01-23 18:17:42 -0800357 }
Diego Perezd1545912016-03-04 18:33:26 +0000358 String scrollPosX = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES, "scrollX");
359 if (scrollPosX != null && scrollPosX.endsWith("px")) {
360 int value = Integer.parseInt(scrollPosX.substring(0, scrollPosX.length() - 2));
361 bc.setScrollXPos(view, value);
362 }
363 String scrollPosY = attrs.getAttributeValue(BridgeConstants.NS_RESOURCES, "scrollY");
364 if (scrollPosY != null && scrollPosY.endsWith("px")) {
365 int value = Integer.parseInt(scrollPosY.substring(0, scrollPosY.length() - 2));
366 bc.setScrollYPos(view, value);
Jens Ole Lauridsen4dfe4d42015-06-05 08:04:27 -0700367 }
Deepanshu Gupta61f23e92015-07-06 18:31:20 -0700368 if (ReflectionUtils.isInstanceOf(view, RecyclerViewUtil.CN_RECYCLER_VIEW)) {
369 Integer resourceId = null;
370 String attrVal = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI,
371 BridgeConstants.ATTR_LIST_ITEM);
372 if (attrVal != null && !attrVal.isEmpty()) {
373 ResourceValue resValue = bc.getRenderResources().findResValue(attrVal, false);
374 if (resValue.isFramework()) {
375 resourceId = Bridge.getResourceId(resValue.getResourceType(),
376 resValue.getName());
377 } else {
378 resourceId = mLayoutlibCallback.getResourceId(resValue.getResourceType(),
379 resValue.getName());
380 }
381 }
382 if (resourceId == null) {
383 resourceId = 0;
384 }
385 RecyclerViewUtil.setAdapter(view, bc, mLayoutlibCallback, resourceId);
Deepanshu Guptaccbc1172015-07-10 17:38:29 -0700386 } else if (ReflectionUtils.isInstanceOf(view, DrawerLayoutUtil.CN_DRAWER_LAYOUT)) {
387 String attrVal = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI,
388 BridgeConstants.ATTR_OPEN_DRAWER);
389 if (attrVal != null) {
390 getDrawerLayoutMap().put(view, attrVal);
391 }
Deepanshu Gupta61f23e92015-07-06 18:31:20 -0700392 }
Jerome Gaillard1d8b0bb2016-06-02 15:35:22 +0100393 else if (view instanceof NumberPicker) {
394 NumberPicker numberPicker = (NumberPicker) view;
395 String minValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "minValue");
396 if (minValue != null) {
397 numberPicker.setMinValue(Integer.parseInt(minValue));
398 }
399 String maxValue = attrs.getAttributeValue(BridgeConstants.NS_TOOLS_URI, "maxValue");
400 if (maxValue != null) {
401 numberPicker.setMaxValue(Integer.parseInt(maxValue));
402 }
403 }
Deepanshu Guptaccbc1172015-07-10 17:38:29 -0700404
Adam Lesinski282e1812014-01-23 18:17:42 -0800405 }
406 }
407
408 public void setIsInMerge(boolean isInMerge) {
409 mIsInMerge = isInMerge;
410 }
411
412 public void setResourceReference(ResourceReference reference) {
413 mResourceReference = reference;
414 }
415
416 @Override
417 public LayoutInflater cloneInContext(Context newContext) {
418 return new BridgeInflater(this, newContext);
419 }
Deepanshu Gupta10019612014-04-18 12:32:38 -0700420
421 /*package*/ static Object getViewKeyFromParser(AttributeSet attrs, BridgeContext bc,
422 ResourceReference resourceReference, boolean isInMerge) {
423
424 if (!(attrs instanceof BridgeXmlBlockParser)) {
425 return null;
426 }
427 BridgeXmlBlockParser parser = ((BridgeXmlBlockParser) attrs);
428
429 // get the view key
430 Object viewKey = parser.getViewCookie();
431
432 if (viewKey == null) {
433 int currentDepth = parser.getDepth();
434
435 // test whether we are in an included file or in a adapter binding view.
436 BridgeXmlBlockParser previousParser = bc.getPreviousParser();
437 if (previousParser != null) {
438 // looks like we are inside an embedded layout.
439 // only apply the cookie of the calling node (<include>) if we are at the
440 // top level of the embedded layout. If there is a merge tag, then
441 // skip it and look for the 2nd level
442 int testDepth = isInMerge ? 2 : 1;
443 if (currentDepth == testDepth) {
444 viewKey = previousParser.getViewCookie();
445 // if we are in a merge, wrap the cookie in a MergeCookie.
446 if (viewKey != null && isInMerge) {
447 viewKey = new MergeCookie(viewKey);
448 }
449 }
450 } else if (resourceReference != null && currentDepth == 1) {
451 // else if there's a resource reference, this means we are in an adapter
452 // binding case. Set the resource ref as the view cookie only for the top
453 // level view.
454 viewKey = resourceReference;
455 }
456 }
457
458 return viewKey;
459 }
Deepanshu Guptaccbc1172015-07-10 17:38:29 -0700460
461 public void postInflateProcess(View view) {
462 if (mOpenDrawerLayouts != null) {
463 String gravity = mOpenDrawerLayouts.get(view);
464 if (gravity != null) {
465 DrawerLayoutUtil.openDrawer(view, gravity);
466 }
467 mOpenDrawerLayouts.remove(view);
468 }
469 }
470
471 @NonNull
472 private Map<View, String> getDrawerLayoutMap() {
473 if (mOpenDrawerLayouts == null) {
474 mOpenDrawerLayouts = new HashMap<View, String>(4);
475 }
476 return mOpenDrawerLayouts;
477 }
478
479 public void onDoneInflation() {
480 if (mOpenDrawerLayouts != null) {
481 mOpenDrawerLayouts.clear();
482 }
483 }
Adam Lesinski282e1812014-01-23 18:17:42 -0800484}