blob: 47b8d921da2e4ae728480228ef9da525a9e530cb [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 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
Tor Norbye7b9c9122013-05-30 16:48:33 -070019import android.annotation.LayoutRes;
Alan Viverettee8489cd2015-02-03 14:40:45 -080020import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060021import android.annotation.SystemService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
Alan Viverette0810b632014-05-01 14:42:56 -070023import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.res.TypedArray;
25import android.content.res.XmlResourceParser;
Alan Viverettee8489cd2015-02-03 14:40:45 -080026import android.graphics.Canvas;
27import android.os.Handler;
28import android.os.Message;
29import android.os.Trace;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.util.AttributeSet;
Alan Viverette0810b632014-05-01 14:42:56 -070031import android.util.Log;
Alan Viverettee8489cd2015-02-03 14:40:45 -080032import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.util.Xml;
Alan Viverettee8489cd2015-02-03 14:40:45 -080034import android.widget.FrameLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070036import com.android.internal.R;
37
38import org.xmlpull.v1.XmlPullParser;
39import org.xmlpull.v1.XmlPullParserException;
40
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import java.io.IOException;
42import java.lang.reflect.Constructor;
43import java.util.HashMap;
44
45/**
Scott Main93dc6422012-02-24 12:04:06 -080046 * Instantiates a layout XML file into its corresponding {@link android.view.View}
47 * objects. It is never used directly. Instead, use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 * {@link android.app.Activity#getLayoutInflater()} or
49 * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance
50 * that is already hooked up to the current context and correctly configured
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060051 * for the device you are running on.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070052 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 * <p>
54 * To create a new LayoutInflater with an additional {@link Factory} for your
55 * own views, you can use {@link #cloneInContext} to clone an existing
56 * ViewFactory, and then call {@link #setFactory} on it to include your
57 * Factory.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070058 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 * <p>
60 * For performance reasons, view inflation relies heavily on pre-processing of
61 * XML files that is done at build time. Therefore, it is not currently possible
62 * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;
63 * it only works with an XmlPullParser returned from a compiled resource
64 * (R.<em>something</em> file.)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060066@SystemService(Context.LAYOUT_INFLATER_SERVICE)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067public abstract class LayoutInflater {
Alan Viverette33e3cda2014-12-17 15:43:29 -080068
Alan Viverette0810b632014-05-01 14:42:56 -070069 private static final String TAG = LayoutInflater.class.getSimpleName();
70 private static final boolean DEBUG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
Alan Viverette8f124812015-09-25 15:17:05 -040072 /** Empty stack trace used to avoid log spam in re-throw exceptions. */
73 private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 /**
76 * This field should be made private, so it is hidden from the SDK.
77 * {@hide}
78 */
79 protected final Context mContext;
80
81 // these are optional, set by the caller
82 private boolean mFactorySet;
83 private Factory mFactory;
Dianne Hackborn625ac272010-09-17 18:29:22 -070084 private Factory2 mFactory2;
Dianne Hackborn420829e2011-01-28 11:30:35 -080085 private Factory2 mPrivateFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 private Filter mFilter;
87
Xavier Ducrohet7f9f99ea2011-08-11 10:16:17 -070088 final Object[] mConstructorArgs = new Object[2];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
Xavier Ducrohet7f9f99ea2011-08-11 10:16:17 -070090 static final Class<?>[] mConstructorSignature = new Class[] {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 Context.class, AttributeSet.class};
92
Gilles Debunne30301932010-06-16 18:32:00 -070093 private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
94 new HashMap<String, Constructor<? extends View>>();
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070095
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 private HashMap<String, Boolean> mFilterMap;
97
Alan Viverette33e3cda2014-12-17 15:43:29 -080098 private TypedValue mTempValue;
99
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 private static final String TAG_MERGE = "merge";
101 private static final String TAG_INCLUDE = "include";
Romain Guy9c1223a2011-05-17 14:25:49 -0700102 private static final String TAG_1995 = "blink";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 private static final String TAG_REQUEST_FOCUS = "requestFocus";
Alan Viverette451a3412014-02-11 18:08:46 -0800104 private static final String TAG_TAG = "tag";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105
Alan Viverette33e3cda2014-12-17 15:43:29 -0800106 private static final String ATTR_LAYOUT = "layout";
107
Alan Viveretteef259e42014-01-24 17:20:12 -0800108 private static final int[] ATTRS_THEME = new int[] {
109 com.android.internal.R.attr.theme };
Alan Viverette24927f22014-01-07 17:28:48 -0800110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 /**
112 * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed
113 * to be inflated.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700114 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 */
116 public interface Filter {
117 /**
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700118 * Hook to allow clients of the LayoutInflater to restrict the set of Views
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119 * that are allowed to be inflated.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700120 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 * @param clazz The class object for the View that is about to be inflated
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700122 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 * @return True if this class is allowed to be inflated, or false otherwise
124 */
Gilles Debunnee6ac8b92010-06-17 10:55:04 -0700125 @SuppressWarnings("unchecked")
126 boolean onLoadClass(Class clazz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 public interface Factory {
130 /**
131 * Hook you can supply that is called when inflating from a LayoutInflater.
132 * You can use this to customize the tag names available in your XML
133 * layout files.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700134 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 * <p>
136 * Note that it is good practice to prefix these custom names with your
137 * package (i.e., com.coolcompany.apps) to avoid conflicts with system
138 * names.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700139 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 * @param name Tag name to be inflated.
141 * @param context The context the view is being created in.
142 * @param attrs Inflation attributes as specified in XML file.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700143 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 * @return View Newly created view. Return null for the default
145 * behavior.
146 */
147 public View onCreateView(String name, Context context, AttributeSet attrs);
148 }
149
Dianne Hackborn625ac272010-09-17 18:29:22 -0700150 public interface Factory2 extends Factory {
151 /**
152 * Version of {@link #onCreateView(String, Context, AttributeSet)}
153 * that also supplies the parent that the view created view will be
154 * placed in.
155 *
156 * @param parent The parent that the created view will be placed
157 * in; <em>note that this may be null</em>.
158 * @param name Tag name to be inflated.
159 * @param context The context the view is being created in.
160 * @param attrs Inflation attributes as specified in XML file.
161 *
162 * @return View Newly created view. Return null for the default
163 * behavior.
164 */
165 public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
166 }
167
168 private static class FactoryMerger implements Factory2 {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 private final Factory mF1, mF2;
Dianne Hackborn625ac272010-09-17 18:29:22 -0700170 private final Factory2 mF12, mF22;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700171
Dianne Hackborn625ac272010-09-17 18:29:22 -0700172 FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 mF1 = f1;
174 mF2 = f2;
Dianne Hackborn625ac272010-09-17 18:29:22 -0700175 mF12 = f12;
176 mF22 = f22;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 public View onCreateView(String name, Context context, AttributeSet attrs) {
180 View v = mF1.onCreateView(name, context, attrs);
181 if (v != null) return v;
182 return mF2.onCreateView(name, context, attrs);
183 }
Dianne Hackborn625ac272010-09-17 18:29:22 -0700184
185 public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
186 View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
187 : mF1.onCreateView(name, context, attrs);
188 if (v != null) return v;
189 return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
190 : mF2.onCreateView(name, context, attrs);
191 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700193
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 /**
195 * Create a new LayoutInflater instance associated with a particular Context.
196 * Applications will almost always want to use
197 * {@link Context#getSystemService Context.getSystemService()} to retrieve
198 * the standard {@link Context#LAYOUT_INFLATER_SERVICE Context.INFLATER_SERVICE}.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700199 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 * @param context The Context in which this LayoutInflater will create its
201 * Views; most importantly, this supplies the theme from which the default
202 * values for their attributes are retrieved.
203 */
204 protected LayoutInflater(Context context) {
205 mContext = context;
206 }
207
208 /**
209 * Create a new LayoutInflater instance that is a copy of an existing
210 * LayoutInflater, optionally with its Context changed. For use in
211 * implementing {@link #cloneInContext}.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700212 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 * @param original The original LayoutInflater to copy.
214 * @param newContext The new Context to use.
215 */
216 protected LayoutInflater(LayoutInflater original, Context newContext) {
217 mContext = newContext;
218 mFactory = original.mFactory;
Dianne Hackborn625ac272010-09-17 18:29:22 -0700219 mFactory2 = original.mFactory2;
Dianne Hackborn420829e2011-01-28 11:30:35 -0800220 mPrivateFactory = original.mPrivateFactory;
Dan Sandler0c7bb332014-09-18 22:11:18 -0400221 setFilter(original.mFilter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 /**
225 * Obtains the LayoutInflater from the given context.
226 */
227 public static LayoutInflater from(Context context) {
228 LayoutInflater LayoutInflater =
229 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
230 if (LayoutInflater == null) {
231 throw new AssertionError("LayoutInflater not found.");
232 }
233 return LayoutInflater;
234 }
235
236 /**
237 * Create a copy of the existing LayoutInflater object, with the copy
238 * pointing to a different Context than the original. This is used by
239 * {@link ContextThemeWrapper} to create a new LayoutInflater to go along
240 * with the new Context theme.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700241 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800242 * @param newContext The new Context to associate with the new LayoutInflater.
243 * May be the same as the original Context if desired.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700244 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 * @return Returns a brand spanking new LayoutInflater object associated with
246 * the given Context.
247 */
248 public abstract LayoutInflater cloneInContext(Context newContext);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700249
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 /**
251 * Return the context we are running in, for access to resources, class
252 * loader, etc.
253 */
254 public Context getContext() {
255 return mContext;
256 }
257
258 /**
Dianne Hackborn625ac272010-09-17 18:29:22 -0700259 * Return the current {@link Factory} (or null). This is called on each element
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 * name. If the factory returns a View, add that to the hierarchy. If it
261 * returns null, proceed to call onCreateView(name).
262 */
263 public final Factory getFactory() {
264 return mFactory;
265 }
266
267 /**
Dianne Hackborn625ac272010-09-17 18:29:22 -0700268 * Return the current {@link Factory2}. Returns null if no factory is set
269 * or the set factory does not implement the {@link Factory2} interface.
270 * This is called on each element
271 * name. If the factory returns a View, add that to the hierarchy. If it
272 * returns null, proceed to call onCreateView(name).
273 */
274 public final Factory2 getFactory2() {
275 return mFactory2;
276 }
277
278 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 * Attach a custom Factory interface for creating views while using
280 * this LayoutInflater. This must not be null, and can only be set once;
281 * after setting, you can not change the factory. This is
282 * called on each element name as the xml is parsed. If the factory returns
283 * a View, that is added to the hierarchy. If it returns null, the next
284 * factory default {@link #onCreateView} method is called.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700285 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 * <p>If you have an existing
287 * LayoutInflater and want to add your own factory to it, use
288 * {@link #cloneInContext} to clone the existing instance and then you
289 * can use this function (once) on the returned new instance. This will
290 * merge your own factory with whatever factory the original instance is
291 * using.
292 */
293 public void setFactory(Factory factory) {
294 if (mFactorySet) {
295 throw new IllegalStateException("A factory has already been set on this LayoutInflater");
296 }
297 if (factory == null) {
298 throw new NullPointerException("Given factory can not be null");
299 }
300 mFactorySet = true;
301 if (mFactory == null) {
302 mFactory = factory;
303 } else {
Dianne Hackborn625ac272010-09-17 18:29:22 -0700304 mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
305 }
306 }
307
308 /**
309 * Like {@link #setFactory}, but allows you to set a {@link Factory2}
310 * interface.
311 */
312 public void setFactory2(Factory2 factory) {
313 if (mFactorySet) {
314 throw new IllegalStateException("A factory has already been set on this LayoutInflater");
315 }
316 if (factory == null) {
317 throw new NullPointerException("Given factory can not be null");
318 }
319 mFactorySet = true;
320 if (mFactory == null) {
321 mFactory = mFactory2 = factory;
322 } else {
Adam Powell371a8092014-06-20 12:51:12 -0700323 mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 }
325 }
326
327 /**
Dianne Hackborn420829e2011-01-28 11:30:35 -0800328 * @hide for use by framework
329 */
330 public void setPrivateFactory(Factory2 factory) {
Adam Powell371a8092014-06-20 12:51:12 -0700331 if (mPrivateFactory == null) {
332 mPrivateFactory = factory;
333 } else {
334 mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
335 }
Dianne Hackborn420829e2011-01-28 11:30:35 -0800336 }
337
338 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 * @return The {@link Filter} currently used by this LayoutInflater to restrict the set of Views
340 * that are allowed to be inflated.
341 */
342 public Filter getFilter() {
343 return mFilter;
344 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700345
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 /**
347 * Sets the {@link Filter} to by this LayoutInflater. If a view is attempted to be inflated
348 * which is not allowed by the {@link Filter}, the {@link #inflate(int, ViewGroup)} call will
349 * throw an {@link InflateException}. This filter will replace any previous filter set on this
350 * LayoutInflater.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700351 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 * @param filter The Filter which restricts the set of Views that are allowed to be inflated.
353 * This filter will replace any previous filter set on this LayoutInflater.
354 */
355 public void setFilter(Filter filter) {
356 mFilter = filter;
357 if (filter != null) {
358 mFilterMap = new HashMap<String, Boolean>();
359 }
360 }
361
362 /**
363 * Inflate a new view hierarchy from the specified xml resource. Throws
364 * {@link InflateException} if there is an error.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700365 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 * @param resource ID for an XML layout resource to load (e.g.,
367 * <code>R.layout.main_page</code>)
368 * @param root Optional view to be the parent of the generated hierarchy.
369 * @return The root View of the inflated hierarchy. If root was supplied,
370 * this is the root View; otherwise it is the root of the inflated
371 * XML file.
372 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700373 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 return inflate(resource, root, root != null);
375 }
376
377 /**
378 * Inflate a new view hierarchy from the specified xml node. Throws
379 * {@link InflateException} if there is an error. *
380 * <p>
381 * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
382 * reasons, view inflation relies heavily on pre-processing of XML files
383 * that is done at build time. Therefore, it is not currently possible to
384 * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700385 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 * @param parser XML dom node containing the description of the view
387 * hierarchy.
388 * @param root Optional view to be the parent of the generated hierarchy.
389 * @return The root View of the inflated hierarchy. If root was supplied,
390 * this is the root View; otherwise it is the root of the inflated
391 * XML file.
392 */
Scott Kennedydb5fd422015-01-15 11:56:33 -0800393 public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 return inflate(parser, root, root != null);
395 }
396
397 /**
398 * Inflate a new view hierarchy from the specified xml resource. Throws
399 * {@link InflateException} if there is an error.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700400 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 * @param resource ID for an XML layout resource to load (e.g.,
402 * <code>R.layout.main_page</code>)
403 * @param root Optional view to be the parent of the generated hierarchy (if
404 * <em>attachToRoot</em> is true), or else simply an object that
405 * provides a set of LayoutParams values for root of the returned
406 * hierarchy (if <em>attachToRoot</em> is false.)
407 * @param attachToRoot Whether the inflated hierarchy should be attached to
408 * the root parameter? If false, root is only used to create the
409 * correct subclass of LayoutParams for the root view in the XML.
410 * @return The root View of the inflated hierarchy. If root was supplied and
411 * attachToRoot is true, this is root; otherwise it is the root of
412 * the inflated XML file.
413 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700414 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
Alan Viverette0810b632014-05-01 14:42:56 -0700415 final Resources res = getContext().getResources();
416 if (DEBUG) {
417 Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
418 + Integer.toHexString(resource) + ")");
419 }
420
421 final XmlResourceParser parser = res.getLayout(resource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 try {
423 return inflate(parser, root, attachToRoot);
424 } finally {
425 parser.close();
426 }
427 }
428
429 /**
430 * Inflate a new view hierarchy from the specified XML node. Throws
431 * {@link InflateException} if there is an error.
432 * <p>
433 * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
434 * reasons, view inflation relies heavily on pre-processing of XML files
435 * that is done at build time. Therefore, it is not currently possible to
436 * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700437 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 * @param parser XML dom node containing the description of the view
439 * hierarchy.
440 * @param root Optional view to be the parent of the generated hierarchy (if
441 * <em>attachToRoot</em> is true), or else simply an object that
442 * provides a set of LayoutParams values for root of the returned
443 * hierarchy (if <em>attachToRoot</em> is false.)
444 * @param attachToRoot Whether the inflated hierarchy should be attached to
445 * the root parameter? If false, root is only used to create the
446 * correct subclass of LayoutParams for the root view in the XML.
447 * @return The root View of the inflated hierarchy. If root was supplied and
448 * attachToRoot is true, this is root; otherwise it is the root of
449 * the inflated XML file.
450 */
Scott Kennedydb5fd422015-01-15 11:56:33 -0800451 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452 synchronized (mConstructorArgs) {
Romain Guy09f7b932013-04-10 11:42:44 -0700453 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
454
Alan Viverette6194d722015-03-20 15:49:06 -0700455 final Context inflaterContext = mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 final AttributeSet attrs = Xml.asAttributeSet(parser);
Alan Viverette6194d722015-03-20 15:49:06 -0700457 Context lastContext = (Context) mConstructorArgs[0];
458 mConstructorArgs[0] = inflaterContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 View result = root;
460
461 try {
462 // Look for the root node.
463 int type;
464 while ((type = parser.next()) != XmlPullParser.START_TAG &&
465 type != XmlPullParser.END_DOCUMENT) {
466 // Empty
467 }
468
469 if (type != XmlPullParser.START_TAG) {
470 throw new InflateException(parser.getPositionDescription()
471 + ": No start tag found!");
472 }
473
474 final String name = parser.getName();
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 if (DEBUG) {
477 System.out.println("**************************");
478 System.out.println("Creating root view: "
479 + name);
480 System.out.println("**************************");
481 }
482
483 if (TAG_MERGE.equals(name)) {
484 if (root == null || !attachToRoot) {
485 throw new InflateException("<merge /> can be used only with a valid "
486 + "ViewGroup root and attachToRoot=true");
487 }
488
Alan Viverette6194d722015-03-20 15:49:06 -0700489 rInflate(parser, root, inflaterContext, attrs, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 } else {
491 // Temp is the root view that was found in the xml
Alan Viverette6194d722015-03-20 15:49:06 -0700492 final View temp = createViewFromTag(root, name, inflaterContext, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493
494 ViewGroup.LayoutParams params = null;
495
496 if (root != null) {
497 if (DEBUG) {
498 System.out.println("Creating params from root: " +
499 root);
500 }
501 // Create layout params that match root, if supplied
502 params = root.generateLayoutParams(attrs);
503 if (!attachToRoot) {
504 // Set the layout params for temp if we are not
505 // attaching. (If we are, we use addView, below)
506 temp.setLayoutParams(params);
507 }
508 }
509
510 if (DEBUG) {
511 System.out.println("-----> start inflating children");
512 }
Alan Viverette6194d722015-03-20 15:49:06 -0700513
514 // Inflate all children under temp against its context.
515 rInflateChildren(parser, temp, attrs, true);
516
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 if (DEBUG) {
518 System.out.println("-----> done inflating children");
519 }
520
521 // We are supposed to attach all the views we found (int temp)
522 // to root. Do that now.
523 if (root != null && attachToRoot) {
524 root.addView(temp, params);
525 }
526
527 // Decide whether to return the root that was passed in or the
528 // top view found in xml.
529 if (root == null || !attachToRoot) {
530 result = temp;
531 }
532 }
533
534 } catch (XmlPullParserException e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400535 final InflateException ie = new InflateException(e.getMessage(), e);
536 ie.setStackTrace(EMPTY_STACK_TRACE);
537 throw ie;
Alan Viverette93795052015-03-09 15:32:50 -0700538 } catch (Exception e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400539 final InflateException ie = new InflateException(parser.getPositionDescription()
540 + ": " + e.getMessage(), e);
541 ie.setStackTrace(EMPTY_STACK_TRACE);
542 throw ie;
Dianne Hackborn9dae48e2010-08-26 10:20:01 -0700543 } finally {
544 // Don't retain static reference on context.
545 mConstructorArgs[0] = lastContext;
546 mConstructorArgs[1] = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547
John Reck465e1a32015-10-19 12:46:09 -0700548 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
549 }
Romain Guy09f7b932013-04-10 11:42:44 -0700550
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 return result;
552 }
553 }
554
Mathew Inwood4985c5c2015-12-31 12:28:54 +0000555 private static final ClassLoader BOOT_CLASS_LOADER = LayoutInflater.class.getClassLoader();
556
557 private final boolean verifyClassLoader(Constructor<? extends View> constructor) {
558 final ClassLoader constructorLoader = constructor.getDeclaringClass().getClassLoader();
559 if (constructorLoader == BOOT_CLASS_LOADER) {
560 // fast path for boot class loader (most common case?) - always ok
561 return true;
562 }
563 // in all normal cases (no dynamic code loading), we will exit the following loop on the
564 // first iteration (i.e. when the declaring classloader is the contexts class loader).
565 ClassLoader cl = mContext.getClassLoader();
566 do {
567 if (constructorLoader == cl) {
568 return true;
569 }
570 cl = cl.getParent();
571 } while (cl != null);
572 return false;
573 }
574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 /**
576 * Low-level function for instantiating a view by name. This attempts to
577 * instantiate a view class of the given <var>name</var> found in this
578 * LayoutInflater's ClassLoader.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700579 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 * <p>
581 * There are two things that can happen in an error case: either the
582 * exception describing the error will be thrown, or a null will be
583 * returned. You must deal with both possibilities -- the former will happen
584 * the first time createView() is called for a class of a particular name,
585 * the latter every time there-after for that class name.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700586 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 * @param name The full name of the class to be instantiated.
588 * @param attrs The XML attributes supplied for this instance.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700589 *
Gilles Debunne30301932010-06-16 18:32:00 -0700590 * @return View The newly instantiated view, or null.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 */
592 public final View createView(String name, String prefix, AttributeSet attrs)
593 throws ClassNotFoundException, InflateException {
Gilles Debunne30301932010-06-16 18:32:00 -0700594 Constructor<? extends View> constructor = sConstructorMap.get(name);
Mathew Inwood4985c5c2015-12-31 12:28:54 +0000595 if (constructor != null && !verifyClassLoader(constructor)) {
596 constructor = null;
597 sConstructorMap.remove(name);
598 }
Gilles Debunne30301932010-06-16 18:32:00 -0700599 Class<? extends View> clazz = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800600
601 try {
Romain Guy09f7b932013-04-10 11:42:44 -0700602 Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
603
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604 if (constructor == null) {
605 // Class not found in the cache, see if it's real, and try to add it
Romain Guyd03b8802009-09-16 14:36:16 -0700606 clazz = mContext.getClassLoader().loadClass(
Gilles Debunne30301932010-06-16 18:32:00 -0700607 prefix != null ? (prefix + name) : name).asSubclass(View.class);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700608
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 if (mFilter != null && clazz != null) {
610 boolean allowed = mFilter.onLoadClass(clazz);
611 if (!allowed) {
612 failNotAllowed(name, prefix, attrs);
613 }
614 }
615 constructor = clazz.getConstructor(mConstructorSignature);
Alan Viverette904de2e2015-05-04 10:32:57 -0700616 constructor.setAccessible(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 sConstructorMap.put(name, constructor);
618 } else {
619 // If we have a filter, apply it to cached constructor
620 if (mFilter != null) {
621 // Have we seen this name before?
622 Boolean allowedState = mFilterMap.get(name);
623 if (allowedState == null) {
624 // New class -- remember whether it is allowed
Romain Guyd03b8802009-09-16 14:36:16 -0700625 clazz = mContext.getClassLoader().loadClass(
Gilles Debunne30301932010-06-16 18:32:00 -0700626 prefix != null ? (prefix + name) : name).asSubclass(View.class);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700627
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
629 mFilterMap.put(name, allowed);
630 if (!allowed) {
631 failNotAllowed(name, prefix, attrs);
632 }
633 } else if (allowedState.equals(Boolean.FALSE)) {
634 failNotAllowed(name, prefix, attrs);
635 }
636 }
637 }
638
Jason Monk9a376bc2017-05-10 09:52:10 -0400639 Object lastContext = mConstructorArgs[0];
640 if (mConstructorArgs[0] == null) {
641 // Fill in the context if not already within inflation.
642 mConstructorArgs[0] = mContext;
643 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 Object[] args = mConstructorArgs;
645 args[1] = attrs;
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700646
647 final View view = constructor.newInstance(args);
648 if (view instanceof ViewStub) {
Alan Viverettea9ddb8d2014-09-17 18:14:32 -0700649 // Use the same context when inflating ViewStub later.
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700650 final ViewStub viewStub = (ViewStub) view;
Alan Viverettea9ddb8d2014-09-17 18:14:32 -0700651 viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700652 }
Jason Monk9a376bc2017-05-10 09:52:10 -0400653 mConstructorArgs[0] = lastContext;
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700654 return view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800655
656 } catch (NoSuchMethodException e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400657 final InflateException ie = new InflateException(attrs.getPositionDescription()
658 + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
659 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 throw ie;
661
Gilles Debunne30301932010-06-16 18:32:00 -0700662 } catch (ClassCastException e) {
663 // If loaded class is not a View subclass
Alan Viverette8f124812015-09-25 15:17:05 -0400664 final InflateException ie = new InflateException(attrs.getPositionDescription()
665 + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
666 ie.setStackTrace(EMPTY_STACK_TRACE);
Gilles Debunne30301932010-06-16 18:32:00 -0700667 throw ie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 } catch (ClassNotFoundException e) {
669 // If loadClass fails, we should propagate the exception.
670 throw e;
671 } catch (Exception e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400672 final InflateException ie = new InflateException(
673 attrs.getPositionDescription() + ": Error inflating class "
674 + (clazz == null ? "<unknown>" : clazz.getName()), e);
675 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 throw ie;
Romain Guy09f7b932013-04-10 11:42:44 -0700677 } finally {
678 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 }
680 }
681
682 /**
Gilles Debunne30301932010-06-16 18:32:00 -0700683 * Throw an exception because the specified class is not allowed to be inflated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 */
685 private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
Romain Guy9c1223a2011-05-17 14:25:49 -0700686 throw new InflateException(attrs.getPositionDescription()
Alan Viverette8f124812015-09-25 15:17:05 -0400687 + ": Class not allowed to be inflated "+ (prefix != null ? (prefix + name) : name));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 }
689
690 /**
691 * This routine is responsible for creating the correct subclass of View
692 * given the xml element name. Override it to handle custom view objects. If
693 * you override this in your subclass be sure to call through to
694 * super.onCreateView(name) for names you do not recognize.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700695 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 * @param name The fully qualified class name of the View to be create.
697 * @param attrs An AttributeSet of attributes to apply to the View.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700698 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 * @return View The View created.
700 */
701 protected View onCreateView(String name, AttributeSet attrs)
702 throws ClassNotFoundException {
703 return createView(name, "android.view.", attrs);
704 }
705
Dianne Hackborn625ac272010-09-17 18:29:22 -0700706 /**
707 * Version of {@link #onCreateView(String, AttributeSet)} that also
Chet Haase430742f2013-04-12 11:18:36 -0700708 * takes the future parent of the view being constructed. The default
Dianne Hackborn625ac272010-09-17 18:29:22 -0700709 * implementation simply calls {@link #onCreateView(String, AttributeSet)}.
710 *
711 * @param parent The future parent of the returned view. <em>Note that
712 * this may be null.</em>
713 * @param name The fully qualified class name of the View to be create.
714 * @param attrs An AttributeSet of attributes to apply to the View.
715 *
716 * @return View The View created.
717 */
718 protected View onCreateView(View parent, String name, AttributeSet attrs)
719 throws ClassNotFoundException {
720 return onCreateView(name, attrs);
721 }
722
Alan Viverette24927f22014-01-07 17:28:48 -0800723 /**
Alan Viverette6194d722015-03-20 15:49:06 -0700724 * Convenience method for calling through to the five-arg createViewFromTag
725 * method. This method passes {@code false} for the {@code ignoreThemeAttr}
726 * argument and should be used for everything except {@code &gt;include>}
727 * tag parsing.
728 */
729 private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
730 return createViewFromTag(parent, name, context, attrs, false);
731 }
732
733 /**
Alan Viverette24927f22014-01-07 17:28:48 -0800734 * Creates a view from a tag name using the supplied attribute set.
735 * <p>
Alan Viverette6194d722015-03-20 15:49:06 -0700736 * <strong>Note:</strong> Default visibility so the BridgeInflater can
737 * override it.
738 *
739 * @param parent the parent view, used to inflate layout params
740 * @param name the name of the XML tag used to define the view
741 * @param context the inflation context for the view, typically the
742 * {@code parent} or base layout inflater context
743 * @param attrs the attribute set for the XML tag used to define the view
744 * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}
745 * attribute (if set) for the view being inflated,
746 * {@code false} otherwise
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 */
Alan Viverette6194d722015-03-20 15:49:06 -0700748 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
749 boolean ignoreThemeAttr) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750 if (name.equals("view")) {
751 name = attrs.getAttributeValue(null, "class");
752 }
753
Alan Viverette6194d722015-03-20 15:49:06 -0700754 // Apply a theme wrapper, if allowed and one is specified.
755 if (!ignoreThemeAttr) {
756 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
757 final int themeResId = ta.getResourceId(0, 0);
758 if (themeResId != 0) {
759 context = new ContextThemeWrapper(context, themeResId);
760 }
761 ta.recycle();
Alan Viverette24927f22014-01-07 17:28:48 -0800762 }
763
Alan Viverette24927f22014-01-07 17:28:48 -0800764 if (name.equals(TAG_1995)) {
765 // Let's party like it's 1995!
Alan Viverette6194d722015-03-20 15:49:06 -0700766 return new BlinkLayout(context, attrs);
Alan Viverette24927f22014-01-07 17:28:48 -0800767 }
768
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 try {
Dianne Hackborn625ac272010-09-17 18:29:22 -0700770 View view;
Alan Viverette24927f22014-01-07 17:28:48 -0800771 if (mFactory2 != null) {
Alan Viverette6194d722015-03-20 15:49:06 -0700772 view = mFactory2.onCreateView(parent, name, context, attrs);
Alan Viverette24927f22014-01-07 17:28:48 -0800773 } else if (mFactory != null) {
Alan Viverette6194d722015-03-20 15:49:06 -0700774 view = mFactory.onCreateView(name, context, attrs);
Alan Viverette24927f22014-01-07 17:28:48 -0800775 } else {
776 view = null;
777 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778
Dianne Hackborn420829e2011-01-28 11:30:35 -0800779 if (view == null && mPrivateFactory != null) {
Alan Viverette6194d722015-03-20 15:49:06 -0700780 view = mPrivateFactory.onCreateView(parent, name, context, attrs);
Dianne Hackborn420829e2011-01-28 11:30:35 -0800781 }
Alan Viverette24927f22014-01-07 17:28:48 -0800782
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 if (view == null) {
Alan Viverette24927f22014-01-07 17:28:48 -0800784 final Object lastContext = mConstructorArgs[0];
Alan Viverette6194d722015-03-20 15:49:06 -0700785 mConstructorArgs[0] = context;
Alan Viverette24927f22014-01-07 17:28:48 -0800786 try {
787 if (-1 == name.indexOf('.')) {
788 view = onCreateView(parent, name, attrs);
789 } else {
790 view = createView(name, null, attrs);
791 }
792 } finally {
793 mConstructorArgs[0] = lastContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 }
795 }
796
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 return view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798 } catch (InflateException e) {
799 throw e;
800
801 } catch (ClassNotFoundException e) {
Alan Viverette6194d722015-03-20 15:49:06 -0700802 final InflateException ie = new InflateException(attrs.getPositionDescription()
Alan Viverette8f124812015-09-25 15:17:05 -0400803 + ": Error inflating class " + name, e);
804 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 throw ie;
806
807 } catch (Exception e) {
Alan Viverette6194d722015-03-20 15:49:06 -0700808 final InflateException ie = new InflateException(attrs.getPositionDescription()
Alan Viverette8f124812015-09-25 15:17:05 -0400809 + ": Error inflating class " + name, e);
810 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 throw ie;
812 }
813 }
814
815 /**
Alan Viverette6194d722015-03-20 15:49:06 -0700816 * Recursive method used to inflate internal (non-root) children. This
817 * method calls through to {@link #rInflate} using the parent context as
818 * the inflation context.
819 * <strong>Note:</strong> Default visibility so the BridgeInflater can
820 * call it.
821 */
822 final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
823 boolean finishInflate) throws XmlPullParserException, IOException {
824 rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
825 }
826
827 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 * Recursive method used to descend down the xml hierarchy and instantiate
829 * views, instantiate their children, and then call onFinishInflate().
Alan Viverette6194d722015-03-20 15:49:06 -0700830 * <p>
831 * <strong>Note:</strong> Default visibility so the BridgeInflater can
832 * override it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 */
Alan Viverette6194d722015-03-20 15:49:06 -0700834 void rInflate(XmlPullParser parser, View parent, Context context,
835 AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836
837 final int depth = parser.getDepth();
838 int type;
Evan Rosky37df2db2017-01-24 16:35:52 -0800839 boolean pendingRequestFocus = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840
841 while (((type = parser.next()) != XmlPullParser.END_TAG ||
842 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
843
844 if (type != XmlPullParser.START_TAG) {
845 continue;
846 }
847
848 final String name = parser.getName();
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700849
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 if (TAG_REQUEST_FOCUS.equals(name)) {
Evan Rosky37df2db2017-01-24 16:35:52 -0800851 pendingRequestFocus = true;
852 consumeChildElements(parser);
Alan Viverette451a3412014-02-11 18:08:46 -0800853 } else if (TAG_TAG.equals(name)) {
854 parseViewTag(parser, parent, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 } else if (TAG_INCLUDE.equals(name)) {
856 if (parser.getDepth() == 0) {
857 throw new InflateException("<include /> cannot be the root element");
858 }
Alan Viverette6194d722015-03-20 15:49:06 -0700859 parseInclude(parser, context, parent, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 } else if (TAG_MERGE.equals(name)) {
861 throw new InflateException("<merge /> must be the root element");
862 } else {
Alan Viverette6194d722015-03-20 15:49:06 -0700863 final View view = createViewFromTag(parent, name, context, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 final ViewGroup viewGroup = (ViewGroup) parent;
865 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
Alan Viverette6194d722015-03-20 15:49:06 -0700866 rInflateChildren(parser, view, attrs, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 viewGroup.addView(view, params);
868 }
869 }
870
Evan Rosky37df2db2017-01-24 16:35:52 -0800871 if (pendingRequestFocus) {
872 parent.restoreDefaultFocus();
873 }
874
Alan Viverette6194d722015-03-20 15:49:06 -0700875 if (finishInflate) {
876 parent.onFinishInflate();
877 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 }
879
Alan Viverette451a3412014-02-11 18:08:46 -0800880 /**
Alan Viverette451a3412014-02-11 18:08:46 -0800881 * Parses a <code>&lt;tag&gt;</code> element and sets a keyed tag on the
882 * containing View.
883 */
884 private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs)
885 throws XmlPullParserException, IOException {
Alan Viverette6194d722015-03-20 15:49:06 -0700886 final Context context = view.getContext();
887 final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag);
888 final int key = ta.getResourceId(R.styleable.ViewTag_id, 0);
889 final CharSequence value = ta.getText(R.styleable.ViewTag_value);
Alan Viverette451a3412014-02-11 18:08:46 -0800890 view.setTag(key, value);
891 ta.recycle();
892
Alan Viverette6194d722015-03-20 15:49:06 -0700893 consumeChildElements(parser);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 }
895
Alan Viverette6194d722015-03-20 15:49:06 -0700896 private void parseInclude(XmlPullParser parser, Context context, View parent,
897 AttributeSet attrs) throws XmlPullParserException, IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 int type;
899
900 if (parent instanceof ViewGroup) {
Alan Viverette6194d722015-03-20 15:49:06 -0700901 // Apply a theme wrapper, if requested. This is sort of a weird
902 // edge case, since developers think the <include> overwrites
903 // values in the AttributeSet of the included View. So, if the
904 // included View has a theme attribute, we'll need to ignore it.
Alan Viverette33e3cda2014-12-17 15:43:29 -0800905 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
906 final int themeResId = ta.getResourceId(0, 0);
Alan Viverette6194d722015-03-20 15:49:06 -0700907 final boolean hasThemeOverride = themeResId != 0;
908 if (hasThemeOverride) {
Alan Viverette33e3cda2014-12-17 15:43:29 -0800909 context = new ContextThemeWrapper(context, themeResId);
910 }
911 ta.recycle();
912
913 // If the layout is pointing to a theme attribute, we have to
914 // massage the value to get a resource identifier out of it.
915 int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 if (layout == 0) {
Alan Viverette33e3cda2014-12-17 15:43:29 -0800917 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
Alan Viverette6194d722015-03-20 15:49:06 -0700918 if (value == null || value.length() <= 0) {
Alan Viverette33e3cda2014-12-17 15:43:29 -0800919 throw new InflateException("You must specify a layout in the"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 + " include tag: <include layout=\"@layout/layoutID\" />");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 }
Alan Viverette33e3cda2014-12-17 15:43:29 -0800922
Alan Viverettea7cec30f2017-05-23 11:33:42 -0400923 // Attempt to resolve the "?attr/name" string to an attribute
924 // within the default (e.g. application) package.
925 layout = context.getResources().getIdentifier(
926 value.substring(1), "attr", context.getPackageName());
927
Alan Viverette33e3cda2014-12-17 15:43:29 -0800928 }
929
930 // The layout might be referencing a theme attribute.
931 if (mTempValue == null) {
932 mTempValue = new TypedValue();
933 }
934 if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) {
935 layout = mTempValue.resourceId;
936 }
937
938 if (layout == 0) {
939 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
940 throw new InflateException("You must specify a valid layout "
941 + "reference. The layout ID " + value + " is not valid.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 } else {
Alan Viverette6194d722015-03-20 15:49:06 -0700943 final XmlResourceParser childParser = context.getResources().getLayout(layout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944
945 try {
946 final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
947
948 while ((type = childParser.next()) != XmlPullParser.START_TAG &&
949 type != XmlPullParser.END_DOCUMENT) {
950 // Empty.
951 }
952
953 if (type != XmlPullParser.START_TAG) {
954 throw new InflateException(childParser.getPositionDescription() +
955 ": No start tag found!");
956 }
957
958 final String childName = childParser.getName();
959
960 if (TAG_MERGE.equals(childName)) {
Alan Viverette6194d722015-03-20 15:49:06 -0700961 // The <merge> tag doesn't support android:theme, so
962 // nothing special to do here.
963 rInflate(childParser, parent, context, childAttrs, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964 } else {
Alan Viverette6194d722015-03-20 15:49:06 -0700965 final View view = createViewFromTag(parent, childName,
966 context, childAttrs, hasThemeOverride);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 final ViewGroup group = (ViewGroup) parent;
968
Alan Viverettee8489cd2015-02-03 14:40:45 -0800969 final TypedArray a = context.obtainStyledAttributes(
970 attrs, R.styleable.Include);
971 final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
972 final int visibility = a.getInt(R.styleable.Include_visibility, -1);
Alan Viverettee8489cd2015-02-03 14:40:45 -0800973 a.recycle();
974
Alan Viverette84aa2fb2015-04-30 12:33:54 -0700975 // We try to load the layout params set in the <include /> tag.
976 // If the parent can't generate layout params (ex. missing width
977 // or height for the framework ViewGroups, though this is not
978 // necessarily true of all ViewGroups) then we expect it to throw
979 // a runtime exception.
980 // We catch this exception and set localParams accordingly: true
981 // means we successfully loaded layout params from the <include>
982 // tag, false means we need to rely on the included layout params.
Dave Burke579e1402012-10-18 20:41:55 -0700983 ViewGroup.LayoutParams params = null;
Alan Viverette84aa2fb2015-04-30 12:33:54 -0700984 try {
985 params = group.generateLayoutParams(attrs);
986 } catch (RuntimeException e) {
987 // Ignore, just fail over to child attrs.
Dave Burke579e1402012-10-18 20:41:55 -0700988 }
Alan Viverettee8489cd2015-02-03 14:40:45 -0800989 if (params == null) {
990 params = group.generateLayoutParams(childAttrs);
991 }
992 view.setLayoutParams(params);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993
994 // Inflate all children.
Alan Viverette6194d722015-03-20 15:49:06 -0700995 rInflateChildren(childParser, view, childAttrs, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 if (id != View.NO_ID) {
998 view.setId(id);
999 }
1000
1001 switch (visibility) {
1002 case 0:
1003 view.setVisibility(View.VISIBLE);
1004 break;
1005 case 1:
1006 view.setVisibility(View.INVISIBLE);
1007 break;
1008 case 2:
1009 view.setVisibility(View.GONE);
1010 break;
1011 }
1012
1013 group.addView(view);
1014 }
1015 } finally {
1016 childParser.close();
1017 }
1018 }
1019 } else {
1020 throw new InflateException("<include /> can only be used inside of a ViewGroup");
1021 }
1022
Alan Viverette6194d722015-03-20 15:49:06 -07001023 LayoutInflater.consumeChildElements(parser);
1024 }
1025
1026 /**
1027 * <strong>Note:</strong> default visibility so that
1028 * LayoutInflater_Delegate can call it.
1029 */
1030 final static void consumeChildElements(XmlPullParser parser)
1031 throws XmlPullParserException, IOException {
1032 int type;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 final int currentDepth = parser.getDepth();
1034 while (((type = parser.next()) != XmlPullParser.END_TAG ||
1035 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
1036 // Empty
1037 }
Romain Guy9c1223a2011-05-17 14:25:49 -07001038 }
1039
1040 private static class BlinkLayout extends FrameLayout {
1041 private static final int MESSAGE_BLINK = 0x42;
1042 private static final int BLINK_DELAY = 500;
1043
1044 private boolean mBlink;
1045 private boolean mBlinkState;
1046 private final Handler mHandler;
1047
1048 public BlinkLayout(Context context, AttributeSet attrs) {
1049 super(context, attrs);
1050 mHandler = new Handler(new Handler.Callback() {
1051 @Override
1052 public boolean handleMessage(Message msg) {
1053 if (msg.what == MESSAGE_BLINK) {
1054 if (mBlink) {
1055 mBlinkState = !mBlinkState;
1056 makeBlink();
1057 }
1058 invalidate();
1059 return true;
1060 }
1061 return false;
1062 }
1063 });
1064 }
1065
1066 private void makeBlink() {
1067 Message message = mHandler.obtainMessage(MESSAGE_BLINK);
1068 mHandler.sendMessageDelayed(message, BLINK_DELAY);
1069 }
1070
1071 @Override
1072 protected void onAttachedToWindow() {
1073 super.onAttachedToWindow();
1074
1075 mBlink = true;
1076 mBlinkState = true;
1077
1078 makeBlink();
1079 }
1080
1081 @Override
1082 protected void onDetachedFromWindow() {
1083 super.onDetachedFromWindow();
1084
1085 mBlink = false;
1086 mBlinkState = true;
1087
1088 mHandler.removeMessages(MESSAGE_BLINK);
1089 }
1090
1091 @Override
1092 protected void dispatchDraw(Canvas canvas) {
1093 if (mBlinkState) {
1094 super.dispatchDraw(canvas);
1095 }
1096 }
1097 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098}