blob: f2259b045c0a7932910dd83931b964ce71067d0a [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;
Eric Holk29d0cd62018-12-14 10:27:29 -080020import android.annotation.NonNull;
Alan Viverettee8489cd2015-02-03 14:40:45 -080021import android.annotation.Nullable;
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060022import android.annotation.SystemService;
Mathew Inwoode5ad5982018-08-17 15:07:52 +010023import android.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Alan Viverette0810b632014-05-01 14:42:56 -070025import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.res.TypedArray;
27import android.content.res.XmlResourceParser;
Alan Viverettee8489cd2015-02-03 14:40:45 -080028import android.graphics.Canvas;
29import android.os.Handler;
30import android.os.Message;
Eric Holk928bbac2019-01-02 10:38:54 -080031import android.os.SystemProperties;
Alan Viverettee8489cd2015-02-03 14:40:45 -080032import android.os.Trace;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.util.AttributeSet;
Alan Viverette0810b632014-05-01 14:42:56 -070034import android.util.Log;
Alan Viverettee8489cd2015-02-03 14:40:45 -080035import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.util.Xml;
Alan Viverettee8489cd2015-02-03 14:40:45 -080037import android.widget.FrameLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070039import com.android.internal.R;
40
Eric Holk928bbac2019-01-02 10:38:54 -080041import dalvik.system.PathClassLoader;
42import java.io.File;
43import java.lang.reflect.Method;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070044import org.xmlpull.v1.XmlPullParser;
45import org.xmlpull.v1.XmlPullParserException;
46
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import java.io.IOException;
48import java.lang.reflect.Constructor;
49import java.util.HashMap;
50
51/**
Scott Main93dc6422012-02-24 12:04:06 -080052 * Instantiates a layout XML file into its corresponding {@link android.view.View}
53 * objects. It is never used directly. Instead, use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054 * {@link android.app.Activity#getLayoutInflater()} or
55 * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance
56 * that is already hooked up to the current context and correctly configured
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060057 * for the device you are running on.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070058 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059 * <p>
60 * To create a new LayoutInflater with an additional {@link Factory} for your
61 * own views, you can use {@link #cloneInContext} to clone an existing
62 * ViewFactory, and then call {@link #setFactory} on it to include your
63 * Factory.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070064 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 * <p>
66 * For performance reasons, view inflation relies heavily on pre-processing of
67 * XML files that is done at build time. Therefore, it is not currently possible
68 * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;
69 * it only works with an XmlPullParser returned from a compiled resource
70 * (R.<em>something</em> file.)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060072@SystemService(Context.LAYOUT_INFLATER_SERVICE)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073public abstract class LayoutInflater {
Alan Viverette33e3cda2014-12-17 15:43:29 -080074
Alan Viverette0810b632014-05-01 14:42:56 -070075 private static final String TAG = LayoutInflater.class.getSimpleName();
76 private static final boolean DEBUG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
Eric Holk928bbac2019-01-02 10:38:54 -080078 private static final String USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY
79 = "view.precompiled_layout_enabled";
80 private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex";
81
Alan Viverette8f124812015-09-25 15:17:05 -040082 /** Empty stack trace used to avoid log spam in re-throw exceptions. */
83 private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
84
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 /**
86 * This field should be made private, so it is hidden from the SDK.
87 * {@hide}
88 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +010089 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 protected final Context mContext;
91
92 // these are optional, set by the caller
Mathew Inwoode5ad5982018-08-17 15:07:52 +010093 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 private boolean mFactorySet;
Mathew Inwoode5ad5982018-08-17 15:07:52 +010095 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 private Factory mFactory;
Mathew Inwoode5ad5982018-08-17 15:07:52 +010097 @UnsupportedAppUsage
Dianne Hackborn625ac272010-09-17 18:29:22 -070098 private Factory2 mFactory2;
Mathew Inwoode5ad5982018-08-17 15:07:52 +010099 @UnsupportedAppUsage
Dianne Hackborn420829e2011-01-28 11:30:35 -0800100 private Factory2 mPrivateFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 private Filter mFilter;
102
Eric Holk928bbac2019-01-02 10:38:54 -0800103 // Indicates whether we should try to inflate layouts using a precompiled layout instead of
104 // inflating from the XML resource.
105 private boolean mUseCompiledView;
106 // This variable holds the classloader that will be used to look for precompiled layouts. The
107 // The classloader includes the generated compiled_view.dex file.
108 private ClassLoader mPrecompiledClassLoader;
109
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100110 @UnsupportedAppUsage
Xavier Ducrohet7f9f99ea2011-08-11 10:16:17 -0700111 final Object[] mConstructorArgs = new Object[2];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100113 @UnsupportedAppUsage
Xavier Ducrohet7f9f99ea2011-08-11 10:16:17 -0700114 static final Class<?>[] mConstructorSignature = new Class[] {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 Context.class, AttributeSet.class};
116
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100117 @UnsupportedAppUsage
Gilles Debunne30301932010-06-16 18:32:00 -0700118 private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
119 new HashMap<String, Constructor<? extends View>>();
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 private HashMap<String, Boolean> mFilterMap;
122
Alan Viverette33e3cda2014-12-17 15:43:29 -0800123 private TypedValue mTempValue;
124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 private static final String TAG_MERGE = "merge";
126 private static final String TAG_INCLUDE = "include";
Romain Guy9c1223a2011-05-17 14:25:49 -0700127 private static final String TAG_1995 = "blink";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 private static final String TAG_REQUEST_FOCUS = "requestFocus";
Alan Viverette451a3412014-02-11 18:08:46 -0800129 private static final String TAG_TAG = "tag";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130
Alan Viverette33e3cda2014-12-17 15:43:29 -0800131 private static final String ATTR_LAYOUT = "layout";
132
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100133 @UnsupportedAppUsage
Alan Viveretteef259e42014-01-24 17:20:12 -0800134 private static final int[] ATTRS_THEME = new int[] {
135 com.android.internal.R.attr.theme };
Alan Viverette24927f22014-01-07 17:28:48 -0800136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 /**
138 * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed
139 * to be inflated.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700140 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 */
142 public interface Filter {
143 /**
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700144 * Hook to allow clients of the LayoutInflater to restrict the set of Views
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 * that are allowed to be inflated.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700146 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 * @param clazz The class object for the View that is about to be inflated
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700148 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 * @return True if this class is allowed to be inflated, or false otherwise
150 */
Gilles Debunnee6ac8b92010-06-17 10:55:04 -0700151 @SuppressWarnings("unchecked")
152 boolean onLoadClass(Class clazz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700154
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 public interface Factory {
156 /**
157 * Hook you can supply that is called when inflating from a LayoutInflater.
158 * You can use this to customize the tag names available in your XML
159 * layout files.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700160 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 * <p>
162 * Note that it is good practice to prefix these custom names with your
163 * package (i.e., com.coolcompany.apps) to avoid conflicts with system
164 * names.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700165 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 * @param name Tag name to be inflated.
167 * @param context The context the view is being created in.
168 * @param attrs Inflation attributes as specified in XML file.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700169 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 * @return View Newly created view. Return null for the default
171 * behavior.
172 */
173 public View onCreateView(String name, Context context, AttributeSet attrs);
174 }
175
Dianne Hackborn625ac272010-09-17 18:29:22 -0700176 public interface Factory2 extends Factory {
177 /**
178 * Version of {@link #onCreateView(String, Context, AttributeSet)}
179 * that also supplies the parent that the view created view will be
180 * placed in.
181 *
182 * @param parent The parent that the created view will be placed
183 * in; <em>note that this may be null</em>.
184 * @param name Tag name to be inflated.
185 * @param context The context the view is being created in.
186 * @param attrs Inflation attributes as specified in XML file.
187 *
188 * @return View Newly created view. Return null for the default
189 * behavior.
190 */
191 public View onCreateView(View parent, String name, Context context, AttributeSet attrs);
192 }
193
194 private static class FactoryMerger implements Factory2 {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 private final Factory mF1, mF2;
Dianne Hackborn625ac272010-09-17 18:29:22 -0700196 private final Factory2 mF12, mF22;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700197
Dianne Hackborn625ac272010-09-17 18:29:22 -0700198 FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 mF1 = f1;
200 mF2 = f2;
Dianne Hackborn625ac272010-09-17 18:29:22 -0700201 mF12 = f12;
202 mF22 = f22;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 public View onCreateView(String name, Context context, AttributeSet attrs) {
206 View v = mF1.onCreateView(name, context, attrs);
207 if (v != null) return v;
208 return mF2.onCreateView(name, context, attrs);
209 }
Dianne Hackborn625ac272010-09-17 18:29:22 -0700210
211 public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
212 View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
213 : mF1.onCreateView(name, context, attrs);
214 if (v != null) return v;
215 return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
216 : mF2.onCreateView(name, context, attrs);
217 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700219
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 /**
221 * Create a new LayoutInflater instance associated with a particular Context.
222 * Applications will almost always want to use
223 * {@link Context#getSystemService Context.getSystemService()} to retrieve
224 * the standard {@link Context#LAYOUT_INFLATER_SERVICE Context.INFLATER_SERVICE}.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700225 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 * @param context The Context in which this LayoutInflater will create its
227 * Views; most importantly, this supplies the theme from which the default
228 * values for their attributes are retrieved.
229 */
230 protected LayoutInflater(Context context) {
231 mContext = context;
Eric Holk928bbac2019-01-02 10:38:54 -0800232 initPrecompiledViews();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 }
234
235 /**
236 * Create a new LayoutInflater instance that is a copy of an existing
237 * LayoutInflater, optionally with its Context changed. For use in
238 * implementing {@link #cloneInContext}.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700239 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 * @param original The original LayoutInflater to copy.
241 * @param newContext The new Context to use.
242 */
243 protected LayoutInflater(LayoutInflater original, Context newContext) {
244 mContext = newContext;
245 mFactory = original.mFactory;
Dianne Hackborn625ac272010-09-17 18:29:22 -0700246 mFactory2 = original.mFactory2;
Dianne Hackborn420829e2011-01-28 11:30:35 -0800247 mPrivateFactory = original.mPrivateFactory;
Dan Sandler0c7bb332014-09-18 22:11:18 -0400248 setFilter(original.mFilter);
Eric Holk928bbac2019-01-02 10:38:54 -0800249 initPrecompiledViews();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700251
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800252 /**
253 * Obtains the LayoutInflater from the given context.
254 */
255 public static LayoutInflater from(Context context) {
256 LayoutInflater LayoutInflater =
257 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
258 if (LayoutInflater == null) {
259 throw new AssertionError("LayoutInflater not found.");
260 }
261 return LayoutInflater;
262 }
263
264 /**
265 * Create a copy of the existing LayoutInflater object, with the copy
266 * pointing to a different Context than the original. This is used by
267 * {@link ContextThemeWrapper} to create a new LayoutInflater to go along
268 * with the new Context theme.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700269 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 * @param newContext The new Context to associate with the new LayoutInflater.
271 * May be the same as the original Context if desired.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700272 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 * @return Returns a brand spanking new LayoutInflater object associated with
274 * the given Context.
275 */
276 public abstract LayoutInflater cloneInContext(Context newContext);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700277
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 /**
279 * Return the context we are running in, for access to resources, class
280 * loader, etc.
281 */
282 public Context getContext() {
283 return mContext;
284 }
285
286 /**
Dianne Hackborn625ac272010-09-17 18:29:22 -0700287 * Return the current {@link Factory} (or null). This is called on each element
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 * name. If the factory returns a View, add that to the hierarchy. If it
289 * returns null, proceed to call onCreateView(name).
290 */
291 public final Factory getFactory() {
292 return mFactory;
293 }
294
295 /**
Dianne Hackborn625ac272010-09-17 18:29:22 -0700296 * Return the current {@link Factory2}. Returns null if no factory is set
297 * or the set factory does not implement the {@link Factory2} interface.
298 * This is called on each element
299 * name. If the factory returns a View, add that to the hierarchy. If it
300 * returns null, proceed to call onCreateView(name).
301 */
302 public final Factory2 getFactory2() {
303 return mFactory2;
304 }
305
306 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 * Attach a custom Factory interface for creating views while using
308 * this LayoutInflater. This must not be null, and can only be set once;
309 * after setting, you can not change the factory. This is
310 * called on each element name as the xml is parsed. If the factory returns
311 * a View, that is added to the hierarchy. If it returns null, the next
312 * factory default {@link #onCreateView} method is called.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700313 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 * <p>If you have an existing
315 * LayoutInflater and want to add your own factory to it, use
316 * {@link #cloneInContext} to clone the existing instance and then you
317 * can use this function (once) on the returned new instance. This will
318 * merge your own factory with whatever factory the original instance is
319 * using.
320 */
321 public void setFactory(Factory factory) {
322 if (mFactorySet) {
323 throw new IllegalStateException("A factory has already been set on this LayoutInflater");
324 }
325 if (factory == null) {
326 throw new NullPointerException("Given factory can not be null");
327 }
328 mFactorySet = true;
329 if (mFactory == null) {
330 mFactory = factory;
331 } else {
Dianne Hackborn625ac272010-09-17 18:29:22 -0700332 mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
333 }
334 }
335
336 /**
337 * Like {@link #setFactory}, but allows you to set a {@link Factory2}
338 * interface.
339 */
340 public void setFactory2(Factory2 factory) {
341 if (mFactorySet) {
342 throw new IllegalStateException("A factory has already been set on this LayoutInflater");
343 }
344 if (factory == null) {
345 throw new NullPointerException("Given factory can not be null");
346 }
347 mFactorySet = true;
348 if (mFactory == null) {
349 mFactory = mFactory2 = factory;
350 } else {
Adam Powell371a8092014-06-20 12:51:12 -0700351 mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 }
353 }
354
355 /**
Dianne Hackborn420829e2011-01-28 11:30:35 -0800356 * @hide for use by framework
357 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100358 @UnsupportedAppUsage
Dianne Hackborn420829e2011-01-28 11:30:35 -0800359 public void setPrivateFactory(Factory2 factory) {
Adam Powell371a8092014-06-20 12:51:12 -0700360 if (mPrivateFactory == null) {
361 mPrivateFactory = factory;
362 } else {
363 mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
364 }
Dianne Hackborn420829e2011-01-28 11:30:35 -0800365 }
366
367 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 * @return The {@link Filter} currently used by this LayoutInflater to restrict the set of Views
369 * that are allowed to be inflated.
370 */
371 public Filter getFilter() {
372 return mFilter;
373 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700374
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 /**
376 * Sets the {@link Filter} to by this LayoutInflater. If a view is attempted to be inflated
377 * which is not allowed by the {@link Filter}, the {@link #inflate(int, ViewGroup)} call will
378 * throw an {@link InflateException}. This filter will replace any previous filter set on this
379 * LayoutInflater.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700380 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 * @param filter The Filter which restricts the set of Views that are allowed to be inflated.
382 * This filter will replace any previous filter set on this LayoutInflater.
383 */
384 public void setFilter(Filter filter) {
385 mFilter = filter;
386 if (filter != null) {
387 mFilterMap = new HashMap<String, Boolean>();
388 }
389 }
390
Eric Holk928bbac2019-01-02 10:38:54 -0800391 private void initPrecompiledViews() {
392 try {
393 mUseCompiledView =
394 SystemProperties.getBoolean(USE_PRECOMPILED_LAYOUT_SYSTEM_PROPERTY, false);
395 if (mUseCompiledView) {
396 mPrecompiledClassLoader = mContext.getClassLoader();
397 String dexFile = mContext.getCodeCacheDir() + COMPILED_VIEW_DEX_FILE_NAME;
398 if (new File(dexFile).exists()) {
399 mPrecompiledClassLoader = new PathClassLoader(dexFile, mPrecompiledClassLoader);
400 } else {
401 // If the precompiled layout file doesn't exist, then disable precompiled
402 // layouts.
403 mUseCompiledView = false;
404 }
405 }
406 } catch (Throwable e) {
407 if (DEBUG) {
408 Log.e(TAG, "Failed to initialized precompiled views layouts", e);
409 }
410 mUseCompiledView = false;
411 }
412 }
413
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800414 /**
415 * Inflate a new view hierarchy from the specified xml resource. Throws
416 * {@link InflateException} if there is an error.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700417 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 * @param resource ID for an XML layout resource to load (e.g.,
419 * <code>R.layout.main_page</code>)
420 * @param root Optional view to be the parent of the generated hierarchy.
421 * @return The root View of the inflated hierarchy. If root was supplied,
422 * this is the root View; otherwise it is the root of the inflated
423 * XML file.
424 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700425 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 return inflate(resource, root, root != null);
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.
441 * @return The root View of the inflated hierarchy. If root was supplied,
442 * this is the root View; otherwise it is the root of the inflated
443 * XML file.
444 */
Scott Kennedydb5fd422015-01-15 11:56:33 -0800445 public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 return inflate(parser, root, root != null);
447 }
448
449 /**
450 * Inflate a new view hierarchy from the specified xml resource. Throws
451 * {@link InflateException} if there is an error.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700452 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 * @param resource ID for an XML layout resource to load (e.g.,
454 * <code>R.layout.main_page</code>)
455 * @param root Optional view to be the parent of the generated hierarchy (if
456 * <em>attachToRoot</em> is true), or else simply an object that
457 * provides a set of LayoutParams values for root of the returned
458 * hierarchy (if <em>attachToRoot</em> is false.)
459 * @param attachToRoot Whether the inflated hierarchy should be attached to
460 * the root parameter? If false, root is only used to create the
461 * correct subclass of LayoutParams for the root view in the XML.
462 * @return The root View of the inflated hierarchy. If root was supplied and
463 * attachToRoot is true, this is root; otherwise it is the root of
464 * the inflated XML file.
465 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700466 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
Alan Viverette0810b632014-05-01 14:42:56 -0700467 final Resources res = getContext().getResources();
468 if (DEBUG) {
469 Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
Eric Holk928bbac2019-01-02 10:38:54 -0800470 + Integer.toHexString(resource) + ")");
Alan Viverette0810b632014-05-01 14:42:56 -0700471 }
472
Eric Holk928bbac2019-01-02 10:38:54 -0800473 View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
474 if (view != null) {
475 return view;
476 }
477 XmlResourceParser parser = res.getLayout(resource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 try {
479 return inflate(parser, root, attachToRoot);
480 } finally {
481 parser.close();
482 }
483 }
484
Eric Holk928bbac2019-01-02 10:38:54 -0800485 private @Nullable
486 View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root,
487 boolean attachToRoot) {
488 if (!mUseCompiledView) {
489 return null;
490 }
491
492 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate (precompiled)");
493
494 // Try to inflate using a precompiled layout.
495 String pkg = res.getResourcePackageName(resource);
496 String layout = res.getResourceEntryName(resource);
497
498 try {
499 Class clazz = mPrecompiledClassLoader.loadClass("" + pkg + ".CompiledView");
500 Method inflater = clazz.getMethod(layout, Context.class, int.class);
501 View view = (View) inflater.invoke(null, mContext, resource);
502
503 if (view != null && root != null) {
504 // We were able to use the precompiled inflater, but now we need to do some work to
505 // attach the view to the root correctly.
506 XmlResourceParser parser = res.getLayout(resource);
507 try {
508 AttributeSet attrs = Xml.asAttributeSet(parser);
509 advanceToRootNode(parser);
510 ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);
511
512 if (attachToRoot) {
513 root.addView(view, params);
514 } else {
515 view.setLayoutParams(params);
516 }
517 } finally {
518 parser.close();
519 }
520 }
521
522 return view;
523 } catch (Throwable e) {
524 if (DEBUG) {
525 Log.e(TAG, "Failed to use precompiled view", e);
526 }
527 } finally {
528 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
529 }
530 return null;
531 }
532
533 /**
534 * Advances the given parser to the first START_TAG. Throws InflateException if no start tag is
535 * found.
536 */
537 private void advanceToRootNode(XmlPullParser parser)
538 throws InflateException, IOException, XmlPullParserException {
539 // Look for the root node.
540 int type;
541 while ((type = parser.next()) != XmlPullParser.START_TAG &&
542 type != XmlPullParser.END_DOCUMENT) {
543 // Empty
544 }
545
546 if (type != XmlPullParser.START_TAG) {
547 throw new InflateException(parser.getPositionDescription()
548 + ": No start tag found!");
549 }
550 }
551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 /**
553 * Inflate a new view hierarchy from the specified XML node. Throws
554 * {@link InflateException} if there is an error.
555 * <p>
556 * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
557 * reasons, view inflation relies heavily on pre-processing of XML files
558 * that is done at build time. Therefore, it is not currently possible to
559 * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700560 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800561 * @param parser XML dom node containing the description of the view
562 * hierarchy.
563 * @param root Optional view to be the parent of the generated hierarchy (if
564 * <em>attachToRoot</em> is true), or else simply an object that
565 * provides a set of LayoutParams values for root of the returned
566 * hierarchy (if <em>attachToRoot</em> is false.)
567 * @param attachToRoot Whether the inflated hierarchy should be attached to
568 * the root parameter? If false, root is only used to create the
569 * correct subclass of LayoutParams for the root view in the XML.
570 * @return The root View of the inflated hierarchy. If root was supplied and
571 * attachToRoot is true, this is root; otherwise it is the root of
572 * the inflated XML file.
573 */
Scott Kennedydb5fd422015-01-15 11:56:33 -0800574 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800575 synchronized (mConstructorArgs) {
Romain Guy09f7b932013-04-10 11:42:44 -0700576 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
577
Alan Viverette6194d722015-03-20 15:49:06 -0700578 final Context inflaterContext = mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 final AttributeSet attrs = Xml.asAttributeSet(parser);
Alan Viverette6194d722015-03-20 15:49:06 -0700580 Context lastContext = (Context) mConstructorArgs[0];
581 mConstructorArgs[0] = inflaterContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 View result = root;
583
584 try {
Eric Holk928bbac2019-01-02 10:38:54 -0800585 advanceToRootNode(parser);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800586 final String name = parser.getName();
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700587
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 if (DEBUG) {
589 System.out.println("**************************");
590 System.out.println("Creating root view: "
591 + name);
592 System.out.println("**************************");
593 }
594
595 if (TAG_MERGE.equals(name)) {
596 if (root == null || !attachToRoot) {
597 throw new InflateException("<merge /> can be used only with a valid "
598 + "ViewGroup root and attachToRoot=true");
599 }
600
Alan Viverette6194d722015-03-20 15:49:06 -0700601 rInflate(parser, root, inflaterContext, attrs, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 } else {
603 // Temp is the root view that was found in the xml
Alan Viverette6194d722015-03-20 15:49:06 -0700604 final View temp = createViewFromTag(root, name, inflaterContext, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605
606 ViewGroup.LayoutParams params = null;
607
608 if (root != null) {
609 if (DEBUG) {
610 System.out.println("Creating params from root: " +
611 root);
612 }
613 // Create layout params that match root, if supplied
614 params = root.generateLayoutParams(attrs);
615 if (!attachToRoot) {
616 // Set the layout params for temp if we are not
617 // attaching. (If we are, we use addView, below)
618 temp.setLayoutParams(params);
619 }
620 }
621
622 if (DEBUG) {
623 System.out.println("-----> start inflating children");
624 }
Alan Viverette6194d722015-03-20 15:49:06 -0700625
626 // Inflate all children under temp against its context.
627 rInflateChildren(parser, temp, attrs, true);
628
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 if (DEBUG) {
630 System.out.println("-----> done inflating children");
631 }
632
633 // We are supposed to attach all the views we found (int temp)
634 // to root. Do that now.
635 if (root != null && attachToRoot) {
636 root.addView(temp, params);
637 }
638
639 // Decide whether to return the root that was passed in or the
640 // top view found in xml.
641 if (root == null || !attachToRoot) {
642 result = temp;
643 }
644 }
645
646 } catch (XmlPullParserException e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400647 final InflateException ie = new InflateException(e.getMessage(), e);
648 ie.setStackTrace(EMPTY_STACK_TRACE);
649 throw ie;
Alan Viverette93795052015-03-09 15:32:50 -0700650 } catch (Exception e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400651 final InflateException ie = new InflateException(parser.getPositionDescription()
652 + ": " + e.getMessage(), e);
653 ie.setStackTrace(EMPTY_STACK_TRACE);
654 throw ie;
Dianne Hackborn9dae48e2010-08-26 10:20:01 -0700655 } finally {
656 // Don't retain static reference on context.
657 mConstructorArgs[0] = lastContext;
658 mConstructorArgs[1] = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800659
John Reck465e1a32015-10-19 12:46:09 -0700660 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
661 }
Romain Guy09f7b932013-04-10 11:42:44 -0700662
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 return result;
664 }
665 }
666
Mathew Inwood4985c5c2015-12-31 12:28:54 +0000667 private static final ClassLoader BOOT_CLASS_LOADER = LayoutInflater.class.getClassLoader();
668
669 private final boolean verifyClassLoader(Constructor<? extends View> constructor) {
670 final ClassLoader constructorLoader = constructor.getDeclaringClass().getClassLoader();
671 if (constructorLoader == BOOT_CLASS_LOADER) {
672 // fast path for boot class loader (most common case?) - always ok
673 return true;
674 }
675 // in all normal cases (no dynamic code loading), we will exit the following loop on the
676 // first iteration (i.e. when the declaring classloader is the contexts class loader).
677 ClassLoader cl = mContext.getClassLoader();
678 do {
679 if (constructorLoader == cl) {
680 return true;
681 }
682 cl = cl.getParent();
683 } while (cl != null);
684 return false;
685 }
686
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800687 /**
688 * Low-level function for instantiating a view by name. This attempts to
689 * instantiate a view class of the given <var>name</var> found in this
690 * LayoutInflater's ClassLoader.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700691 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 * <p>
693 * There are two things that can happen in an error case: either the
694 * exception describing the error will be thrown, or a null will be
695 * returned. You must deal with both possibilities -- the former will happen
696 * the first time createView() is called for a class of a particular name,
697 * the latter every time there-after for that class name.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700698 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 * @param name The full name of the class to be instantiated.
700 * @param attrs The XML attributes supplied for this instance.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700701 *
Gilles Debunne30301932010-06-16 18:32:00 -0700702 * @return View The newly instantiated view, or null.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 */
704 public final View createView(String name, String prefix, AttributeSet attrs)
705 throws ClassNotFoundException, InflateException {
Gilles Debunne30301932010-06-16 18:32:00 -0700706 Constructor<? extends View> constructor = sConstructorMap.get(name);
Mathew Inwood4985c5c2015-12-31 12:28:54 +0000707 if (constructor != null && !verifyClassLoader(constructor)) {
708 constructor = null;
709 sConstructorMap.remove(name);
710 }
Gilles Debunne30301932010-06-16 18:32:00 -0700711 Class<? extends View> clazz = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712
713 try {
Romain Guy09f7b932013-04-10 11:42:44 -0700714 Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
715
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 if (constructor == null) {
717 // Class not found in the cache, see if it's real, and try to add it
Romain Guyd03b8802009-09-16 14:36:16 -0700718 clazz = mContext.getClassLoader().loadClass(
Gilles Debunne30301932010-06-16 18:32:00 -0700719 prefix != null ? (prefix + name) : name).asSubclass(View.class);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700720
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 if (mFilter != null && clazz != null) {
722 boolean allowed = mFilter.onLoadClass(clazz);
723 if (!allowed) {
724 failNotAllowed(name, prefix, attrs);
725 }
726 }
727 constructor = clazz.getConstructor(mConstructorSignature);
Alan Viverette904de2e2015-05-04 10:32:57 -0700728 constructor.setAccessible(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 sConstructorMap.put(name, constructor);
730 } else {
731 // If we have a filter, apply it to cached constructor
732 if (mFilter != null) {
733 // Have we seen this name before?
734 Boolean allowedState = mFilterMap.get(name);
735 if (allowedState == null) {
736 // New class -- remember whether it is allowed
Romain Guyd03b8802009-09-16 14:36:16 -0700737 clazz = mContext.getClassLoader().loadClass(
Gilles Debunne30301932010-06-16 18:32:00 -0700738 prefix != null ? (prefix + name) : name).asSubclass(View.class);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700739
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
741 mFilterMap.put(name, allowed);
742 if (!allowed) {
743 failNotAllowed(name, prefix, attrs);
744 }
745 } else if (allowedState.equals(Boolean.FALSE)) {
746 failNotAllowed(name, prefix, attrs);
747 }
748 }
749 }
750
Jason Monk9a376bc2017-05-10 09:52:10 -0400751 Object lastContext = mConstructorArgs[0];
752 if (mConstructorArgs[0] == null) {
753 // Fill in the context if not already within inflation.
754 mConstructorArgs[0] = mContext;
755 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 Object[] args = mConstructorArgs;
757 args[1] = attrs;
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700758
759 final View view = constructor.newInstance(args);
760 if (view instanceof ViewStub) {
Alan Viverettea9ddb8d2014-09-17 18:14:32 -0700761 // Use the same context when inflating ViewStub later.
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700762 final ViewStub viewStub = (ViewStub) view;
Alan Viverettea9ddb8d2014-09-17 18:14:32 -0700763 viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700764 }
Jason Monk9a376bc2017-05-10 09:52:10 -0400765 mConstructorArgs[0] = lastContext;
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700766 return view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767
768 } catch (NoSuchMethodException e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400769 final InflateException ie = new InflateException(attrs.getPositionDescription()
770 + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
771 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 throw ie;
773
Gilles Debunne30301932010-06-16 18:32:00 -0700774 } catch (ClassCastException e) {
775 // If loaded class is not a View subclass
Alan Viverette8f124812015-09-25 15:17:05 -0400776 final InflateException ie = new InflateException(attrs.getPositionDescription()
777 + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
778 ie.setStackTrace(EMPTY_STACK_TRACE);
Gilles Debunne30301932010-06-16 18:32:00 -0700779 throw ie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 } catch (ClassNotFoundException e) {
781 // If loadClass fails, we should propagate the exception.
782 throw e;
783 } catch (Exception e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400784 final InflateException ie = new InflateException(
785 attrs.getPositionDescription() + ": Error inflating class "
786 + (clazz == null ? "<unknown>" : clazz.getName()), e);
787 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 throw ie;
Romain Guy09f7b932013-04-10 11:42:44 -0700789 } finally {
790 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 }
792 }
793
794 /**
Gilles Debunne30301932010-06-16 18:32:00 -0700795 * Throw an exception because the specified class is not allowed to be inflated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 */
797 private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
Romain Guy9c1223a2011-05-17 14:25:49 -0700798 throw new InflateException(attrs.getPositionDescription()
Alan Viverette8f124812015-09-25 15:17:05 -0400799 + ": Class not allowed to be inflated "+ (prefix != null ? (prefix + name) : name));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 }
801
802 /**
803 * This routine is responsible for creating the correct subclass of View
804 * given the xml element name. Override it to handle custom view objects. If
805 * you override this in your subclass be sure to call through to
806 * super.onCreateView(name) for names you do not recognize.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700807 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 * @param name The fully qualified class name of the View to be create.
809 * @param attrs An AttributeSet of attributes to apply to the View.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700810 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 * @return View The View created.
812 */
813 protected View onCreateView(String name, AttributeSet attrs)
814 throws ClassNotFoundException {
815 return createView(name, "android.view.", attrs);
816 }
817
Dianne Hackborn625ac272010-09-17 18:29:22 -0700818 /**
819 * Version of {@link #onCreateView(String, AttributeSet)} that also
Chet Haase430742f2013-04-12 11:18:36 -0700820 * takes the future parent of the view being constructed. The default
Dianne Hackborn625ac272010-09-17 18:29:22 -0700821 * implementation simply calls {@link #onCreateView(String, AttributeSet)}.
822 *
823 * @param parent The future parent of the returned view. <em>Note that
824 * this may be null.</em>
825 * @param name The fully qualified class name of the View to be create.
826 * @param attrs An AttributeSet of attributes to apply to the View.
827 *
828 * @return View The View created.
829 */
830 protected View onCreateView(View parent, String name, AttributeSet attrs)
831 throws ClassNotFoundException {
832 return onCreateView(name, attrs);
833 }
834
Alan Viverette24927f22014-01-07 17:28:48 -0800835 /**
Alan Viverette6194d722015-03-20 15:49:06 -0700836 * Convenience method for calling through to the five-arg createViewFromTag
837 * method. This method passes {@code false} for the {@code ignoreThemeAttr}
838 * argument and should be used for everything except {@code &gt;include>}
839 * tag parsing.
840 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100841 @UnsupportedAppUsage
Alan Viverette6194d722015-03-20 15:49:06 -0700842 private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
843 return createViewFromTag(parent, name, context, attrs, false);
844 }
845
846 /**
Alan Viverette24927f22014-01-07 17:28:48 -0800847 * Creates a view from a tag name using the supplied attribute set.
848 * <p>
Alan Viverette6194d722015-03-20 15:49:06 -0700849 * <strong>Note:</strong> Default visibility so the BridgeInflater can
850 * override it.
851 *
852 * @param parent the parent view, used to inflate layout params
853 * @param name the name of the XML tag used to define the view
854 * @param context the inflation context for the view, typically the
855 * {@code parent} or base layout inflater context
856 * @param attrs the attribute set for the XML tag used to define the view
857 * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}
858 * attribute (if set) for the view being inflated,
859 * {@code false} otherwise
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 */
Mathew Inwoode5ad5982018-08-17 15:07:52 +0100861 @UnsupportedAppUsage
Alan Viverette6194d722015-03-20 15:49:06 -0700862 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
863 boolean ignoreThemeAttr) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 if (name.equals("view")) {
865 name = attrs.getAttributeValue(null, "class");
866 }
867
Alan Viverette6194d722015-03-20 15:49:06 -0700868 // Apply a theme wrapper, if allowed and one is specified.
869 if (!ignoreThemeAttr) {
870 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
871 final int themeResId = ta.getResourceId(0, 0);
872 if (themeResId != 0) {
873 context = new ContextThemeWrapper(context, themeResId);
874 }
875 ta.recycle();
Alan Viverette24927f22014-01-07 17:28:48 -0800876 }
877
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 try {
Eric Holk29d0cd62018-12-14 10:27:29 -0800879 View view = tryCreateView(parent, name, context, attrs);
Alan Viverette24927f22014-01-07 17:28:48 -0800880
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 if (view == null) {
Alan Viverette24927f22014-01-07 17:28:48 -0800882 final Object lastContext = mConstructorArgs[0];
Alan Viverette6194d722015-03-20 15:49:06 -0700883 mConstructorArgs[0] = context;
Alan Viverette24927f22014-01-07 17:28:48 -0800884 try {
885 if (-1 == name.indexOf('.')) {
886 view = onCreateView(parent, name, attrs);
887 } else {
888 view = createView(name, null, attrs);
889 }
890 } finally {
891 mConstructorArgs[0] = lastContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 }
893 }
894
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 return view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 } catch (InflateException e) {
897 throw e;
898
899 } catch (ClassNotFoundException e) {
Alan Viverette6194d722015-03-20 15:49:06 -0700900 final InflateException ie = new InflateException(attrs.getPositionDescription()
Alan Viverette8f124812015-09-25 15:17:05 -0400901 + ": Error inflating class " + name, e);
902 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 throw ie;
904
905 } catch (Exception e) {
Alan Viverette6194d722015-03-20 15:49:06 -0700906 final InflateException ie = new InflateException(attrs.getPositionDescription()
Alan Viverette8f124812015-09-25 15:17:05 -0400907 + ": Error inflating class " + name, e);
908 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 throw ie;
910 }
911 }
912
913 /**
Eric Holk29d0cd62018-12-14 10:27:29 -0800914 * Tries to create a view from a tag name using the supplied attribute set.
915 *
916 * This method gives the factory provided by {@link LayoutInflater#setFactory} and
917 * {@link LayoutInflater#setFactory2} a chance to create a view. However, it does not apply all
918 * of the general view creation logic, and thus may return {@code null} for some tags. This
919 * method is used by {@link LayoutInflater#inflate} in creating {@code View} objects.
920 *
921 * @hide for use by precompiled layouts.
922 *
923 * @param parent the parent view, used to inflate layout params
924 * @param name the name of the XML tag used to define the view
925 * @param context the inflation context for the view, typically the
926 * {@code parent} or base layout inflater context
927 * @param attrs the attribute set for the XML tag used to define the view
928 */
929 @UnsupportedAppUsage(trackingBug = 122360734)
930 @Nullable
931 public final View tryCreateView(@Nullable View parent, @NonNull String name,
932 @NonNull Context context,
933 @NonNull AttributeSet attrs) {
934 if (name.equals(TAG_1995)) {
935 // Let's party like it's 1995!
936 return new BlinkLayout(context, attrs);
937 }
938
939 View view;
940 if (mFactory2 != null) {
941 view = mFactory2.onCreateView(parent, name, context, attrs);
942 } else if (mFactory != null) {
943 view = mFactory.onCreateView(name, context, attrs);
944 } else {
945 view = null;
946 }
947
948 if (view == null && mPrivateFactory != null) {
949 view = mPrivateFactory.onCreateView(parent, name, context, attrs);
950 }
951
952 return view;
953 }
954
955 /**
Alan Viverette6194d722015-03-20 15:49:06 -0700956 * Recursive method used to inflate internal (non-root) children. This
957 * method calls through to {@link #rInflate} using the parent context as
958 * the inflation context.
959 * <strong>Note:</strong> Default visibility so the BridgeInflater can
960 * call it.
961 */
962 final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
963 boolean finishInflate) throws XmlPullParserException, IOException {
964 rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
965 }
966
967 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 * Recursive method used to descend down the xml hierarchy and instantiate
969 * views, instantiate their children, and then call onFinishInflate().
Alan Viverette6194d722015-03-20 15:49:06 -0700970 * <p>
971 * <strong>Note:</strong> Default visibility so the BridgeInflater can
972 * override it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 */
Alan Viverette6194d722015-03-20 15:49:06 -0700974 void rInflate(XmlPullParser parser, View parent, Context context,
975 AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976
977 final int depth = parser.getDepth();
978 int type;
Evan Rosky37df2db2017-01-24 16:35:52 -0800979 boolean pendingRequestFocus = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980
981 while (((type = parser.next()) != XmlPullParser.END_TAG ||
982 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
983
984 if (type != XmlPullParser.START_TAG) {
985 continue;
986 }
987
988 final String name = parser.getName();
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700989
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 if (TAG_REQUEST_FOCUS.equals(name)) {
Evan Rosky37df2db2017-01-24 16:35:52 -0800991 pendingRequestFocus = true;
992 consumeChildElements(parser);
Alan Viverette451a3412014-02-11 18:08:46 -0800993 } else if (TAG_TAG.equals(name)) {
994 parseViewTag(parser, parent, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 } else if (TAG_INCLUDE.equals(name)) {
996 if (parser.getDepth() == 0) {
997 throw new InflateException("<include /> cannot be the root element");
998 }
Alan Viverette6194d722015-03-20 15:49:06 -0700999 parseInclude(parser, context, parent, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 } else if (TAG_MERGE.equals(name)) {
1001 throw new InflateException("<merge /> must be the root element");
1002 } else {
Alan Viverette6194d722015-03-20 15:49:06 -07001003 final View view = createViewFromTag(parent, name, context, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 final ViewGroup viewGroup = (ViewGroup) parent;
1005 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
Alan Viverette6194d722015-03-20 15:49:06 -07001006 rInflateChildren(parser, view, attrs, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 viewGroup.addView(view, params);
1008 }
1009 }
1010
Evan Rosky37df2db2017-01-24 16:35:52 -08001011 if (pendingRequestFocus) {
1012 parent.restoreDefaultFocus();
1013 }
1014
Alan Viverette6194d722015-03-20 15:49:06 -07001015 if (finishInflate) {
1016 parent.onFinishInflate();
1017 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 }
1019
Alan Viverette451a3412014-02-11 18:08:46 -08001020 /**
Alan Viverette451a3412014-02-11 18:08:46 -08001021 * Parses a <code>&lt;tag&gt;</code> element and sets a keyed tag on the
1022 * containing View.
1023 */
1024 private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs)
1025 throws XmlPullParserException, IOException {
Alan Viverette6194d722015-03-20 15:49:06 -07001026 final Context context = view.getContext();
1027 final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag);
1028 final int key = ta.getResourceId(R.styleable.ViewTag_id, 0);
1029 final CharSequence value = ta.getText(R.styleable.ViewTag_value);
Alan Viverette451a3412014-02-11 18:08:46 -08001030 view.setTag(key, value);
1031 ta.recycle();
1032
Alan Viverette6194d722015-03-20 15:49:06 -07001033 consumeChildElements(parser);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 }
1035
Mathew Inwoode5ad5982018-08-17 15:07:52 +01001036 @UnsupportedAppUsage
Alan Viverette6194d722015-03-20 15:49:06 -07001037 private void parseInclude(XmlPullParser parser, Context context, View parent,
1038 AttributeSet attrs) throws XmlPullParserException, IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 int type;
1040
Eric Holkbfc68702019-01-02 11:11:24 -08001041 if (!(parent instanceof ViewGroup)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 throw new InflateException("<include /> can only be used inside of a ViewGroup");
1043 }
1044
Eric Holkbfc68702019-01-02 11:11:24 -08001045 // Apply a theme wrapper, if requested. This is sort of a weird
1046 // edge case, since developers think the <include> overwrites
1047 // values in the AttributeSet of the included View. So, if the
1048 // included View has a theme attribute, we'll need to ignore it.
1049 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
1050 final int themeResId = ta.getResourceId(0, 0);
1051 final boolean hasThemeOverride = themeResId != 0;
1052 if (hasThemeOverride) {
1053 context = new ContextThemeWrapper(context, themeResId);
1054 }
1055 ta.recycle();
1056
1057 // If the layout is pointing to a theme attribute, we have to
1058 // massage the value to get a resource identifier out of it.
1059 int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
1060 if (layout == 0) {
1061 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
1062 if (value == null || value.length() <= 0) {
1063 throw new InflateException("You must specify a layout in the"
1064 + " include tag: <include layout=\"@layout/layoutID\" />");
1065 }
1066
1067 // Attempt to resolve the "?attr/name" string to an attribute
1068 // within the default (e.g. application) package.
1069 layout = context.getResources().getIdentifier(
1070 value.substring(1), "attr", context.getPackageName());
1071
1072 }
1073
1074 // The layout might be referencing a theme attribute.
1075 if (mTempValue == null) {
1076 mTempValue = new TypedValue();
1077 }
1078 if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) {
1079 layout = mTempValue.resourceId;
1080 }
1081
1082 if (layout == 0) {
1083 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
1084 throw new InflateException("You must specify a valid layout "
1085 + "reference. The layout ID " + value + " is not valid.");
1086 }
1087
Eric Holk928bbac2019-01-02 10:38:54 -08001088 final View precompiled = tryInflatePrecompiled(layout, context.getResources(),
1089 (ViewGroup) parent, /*attachToRoot=*/true);
1090 if (precompiled == null) {
1091 final XmlResourceParser childParser = context.getResources().getLayout(layout);
Eric Holkbfc68702019-01-02 11:11:24 -08001092
Eric Holk928bbac2019-01-02 10:38:54 -08001093 try {
1094 final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
Eric Holkbfc68702019-01-02 11:11:24 -08001095
Eric Holk928bbac2019-01-02 10:38:54 -08001096 while ((type = childParser.next()) != XmlPullParser.START_TAG &&
1097 type != XmlPullParser.END_DOCUMENT) {
1098 // Empty.
1099 }
1100
1101 if (type != XmlPullParser.START_TAG) {
1102 throw new InflateException(childParser.getPositionDescription() +
1103 ": No start tag found!");
1104 }
1105
1106 final String childName = childParser.getName();
1107
1108 if (TAG_MERGE.equals(childName)) {
1109 // The <merge> tag doesn't support android:theme, so
1110 // nothing special to do here.
1111 rInflate(childParser, parent, context, childAttrs, false);
1112 } else {
1113 final View view = createViewFromTag(parent, childName,
1114 context, childAttrs, hasThemeOverride);
1115 final ViewGroup group = (ViewGroup) parent;
1116
1117 final TypedArray a = context.obtainStyledAttributes(
1118 attrs, R.styleable.Include);
1119 final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
1120 final int visibility = a.getInt(R.styleable.Include_visibility, -1);
1121 a.recycle();
1122
1123 // We try to load the layout params set in the <include /> tag.
1124 // If the parent can't generate layout params (ex. missing width
1125 // or height for the framework ViewGroups, though this is not
1126 // necessarily true of all ViewGroups) then we expect it to throw
1127 // a runtime exception.
1128 // We catch this exception and set localParams accordingly: true
1129 // means we successfully loaded layout params from the <include>
1130 // tag, false means we need to rely on the included layout params.
1131 ViewGroup.LayoutParams params = null;
1132 try {
1133 params = group.generateLayoutParams(attrs);
1134 } catch (RuntimeException e) {
1135 // Ignore, just fail over to child attrs.
1136 }
1137 if (params == null) {
1138 params = group.generateLayoutParams(childAttrs);
1139 }
1140 view.setLayoutParams(params);
1141
1142 // Inflate all children.
1143 rInflateChildren(childParser, view, childAttrs, true);
1144
1145 if (id != View.NO_ID) {
1146 view.setId(id);
1147 }
1148
1149 switch (visibility) {
1150 case 0:
1151 view.setVisibility(View.VISIBLE);
1152 break;
1153 case 1:
1154 view.setVisibility(View.INVISIBLE);
1155 break;
1156 case 2:
1157 view.setVisibility(View.GONE);
1158 break;
1159 }
1160
1161 group.addView(view);
1162 }
1163 } finally {
1164 childParser.close();
Eric Holkbfc68702019-01-02 11:11:24 -08001165 }
Eric Holkbfc68702019-01-02 11:11:24 -08001166 }
Alan Viverette6194d722015-03-20 15:49:06 -07001167 LayoutInflater.consumeChildElements(parser);
1168 }
1169
1170 /**
1171 * <strong>Note:</strong> default visibility so that
1172 * LayoutInflater_Delegate can call it.
1173 */
1174 final static void consumeChildElements(XmlPullParser parser)
1175 throws XmlPullParserException, IOException {
1176 int type;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 final int currentDepth = parser.getDepth();
1178 while (((type = parser.next()) != XmlPullParser.END_TAG ||
1179 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
1180 // Empty
1181 }
Romain Guy9c1223a2011-05-17 14:25:49 -07001182 }
1183
1184 private static class BlinkLayout extends FrameLayout {
1185 private static final int MESSAGE_BLINK = 0x42;
1186 private static final int BLINK_DELAY = 500;
1187
1188 private boolean mBlink;
1189 private boolean mBlinkState;
1190 private final Handler mHandler;
1191
1192 public BlinkLayout(Context context, AttributeSet attrs) {
1193 super(context, attrs);
1194 mHandler = new Handler(new Handler.Callback() {
1195 @Override
1196 public boolean handleMessage(Message msg) {
1197 if (msg.what == MESSAGE_BLINK) {
1198 if (mBlink) {
1199 mBlinkState = !mBlinkState;
1200 makeBlink();
1201 }
1202 invalidate();
1203 return true;
1204 }
1205 return false;
1206 }
1207 });
1208 }
1209
1210 private void makeBlink() {
1211 Message message = mHandler.obtainMessage(MESSAGE_BLINK);
1212 mHandler.sendMessageDelayed(message, BLINK_DELAY);
1213 }
1214
1215 @Override
1216 protected void onAttachedToWindow() {
1217 super.onAttachedToWindow();
1218
1219 mBlink = true;
1220 mBlinkState = true;
1221
1222 makeBlink();
1223 }
1224
1225 @Override
1226 protected void onDetachedFromWindow() {
1227 super.onDetachedFromWindow();
1228
1229 mBlink = false;
1230 mBlinkState = true;
1231
1232 mHandler.removeMessages(MESSAGE_BLINK);
1233 }
1234
1235 @Override
1236 protected void dispatchDraw(Canvas canvas) {
1237 if (mBlinkState) {
1238 super.dispatchDraw(canvas);
1239 }
1240 }
1241 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001242}