Home | History | Annotate | Download | only in view
      1 /*
      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 
     17 package android.view;
     18 
     19 import android.annotation.LayoutRes;
     20 import android.annotation.NonNull;
     21 import android.annotation.Nullable;
     22 import android.annotation.SystemService;
     23 import android.annotation.TestApi;
     24 import android.annotation.UnsupportedAppUsage;
     25 import android.content.Context;
     26 import android.content.pm.ApplicationInfo;
     27 import android.content.res.Resources;
     28 import android.content.res.TypedArray;
     29 import android.content.res.XmlResourceParser;
     30 import android.graphics.Canvas;
     31 import android.os.Build;
     32 import android.os.Handler;
     33 import android.os.Message;
     34 import android.os.SystemProperties;
     35 import android.os.Trace;
     36 import android.provider.DeviceConfig;
     37 import android.text.TextUtils;
     38 import android.util.AttributeSet;
     39 import android.util.Log;
     40 import android.util.TypedValue;
     41 import android.util.Xml;
     42 import android.widget.FrameLayout;
     43 
     44 import com.android.internal.R;
     45 
     46 import dalvik.system.PathClassLoader;
     47 
     48 import org.xmlpull.v1.XmlPullParser;
     49 import org.xmlpull.v1.XmlPullParserException;
     50 
     51 import java.io.File;
     52 import java.io.IOException;
     53 import java.lang.reflect.Constructor;
     54 import java.lang.reflect.Method;
     55 import java.util.HashMap;
     56 import java.util.Objects;
     57 
     58 /**
     59  * Instantiates a layout XML file into its corresponding {@link android.view.View}
     60  * objects. It is never used directly. Instead, use
     61  * {@link android.app.Activity#getLayoutInflater()} or
     62  * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance
     63  * that is already hooked up to the current context and correctly configured
     64  * for the device you are running on.
     65  *
     66  * <p>
     67  * To create a new LayoutInflater with an additional {@link Factory} for your
     68  * own views, you can use {@link #cloneInContext} to clone an existing
     69  * ViewFactory, and then call {@link #setFactory} on it to include your
     70  * Factory.
     71  *
     72  * <p>
     73  * For performance reasons, view inflation relies heavily on pre-processing of
     74  * XML files that is done at build time. Therefore, it is not currently possible
     75  * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;
     76  * it only works with an XmlPullParser returned from a compiled resource
     77  * (R.<em>something</em> file.)
     78  */
     79 @SystemService(Context.LAYOUT_INFLATER_SERVICE)
     80 public abstract class LayoutInflater {
     81 
     82     private static final String TAG = LayoutInflater.class.getSimpleName();
     83     private static final boolean DEBUG = false;
     84 
     85     private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex";
     86     /**
     87      * Whether or not we use the precompiled layout.
     88      */
     89     private static final String USE_PRECOMPILED_LAYOUT = "view.precompiled_layout_enabled";
     90 
     91     /** Empty stack trace used to avoid log spam in re-throw exceptions. */
     92     private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
     93 
     94     /**
     95      * This field should be made private, so it is hidden from the SDK.
     96      * {@hide}
     97      */
     98     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     99     protected final Context mContext;
    100 
    101     // these are optional, set by the caller
    102     /**
    103      * If any developer has desire to change this value, they should instead use
    104      * {@link #cloneInContext(Context)} and set the new factory in thew newly-created
    105      * LayoutInflater.
    106      */
    107     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    108     private boolean mFactorySet;
    109     @UnsupportedAppUsage
    110     private Factory mFactory;
    111     @UnsupportedAppUsage
    112     private Factory2 mFactory2;
    113     @UnsupportedAppUsage
    114     private Factory2 mPrivateFactory;
    115     private Filter mFilter;
    116 
    117     // Indicates whether we should try to inflate layouts using a precompiled layout instead of
    118     // inflating from the XML resource.
    119     private boolean mUseCompiledView;
    120     // This variable holds the classloader that will be used to look for precompiled layouts. The
    121     // The classloader includes the generated compiled_view.dex file.
    122     private ClassLoader mPrecompiledClassLoader;
    123 
    124     /**
    125      * This is not a public API. Two APIs are now available to alleviate the need to access
    126      * this directly: {@link #createView(Context, String, String, AttributeSet)} and
    127      * {@link #onCreateView(Context, View, String, AttributeSet)}.
    128      */
    129     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    130     final Object[] mConstructorArgs = new Object[2];
    131 
    132     @UnsupportedAppUsage
    133     static final Class<?>[] mConstructorSignature = new Class[] {
    134             Context.class, AttributeSet.class};
    135 
    136     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769490)
    137     private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
    138             new HashMap<String, Constructor<? extends View>>();
    139 
    140     private HashMap<String, Boolean> mFilterMap;
    141 
    142     private TypedValue mTempValue;
    143 
    144     private static final String TAG_MERGE = "merge";
    145     private static final String TAG_INCLUDE = "include";
    146     private static final String TAG_1995 = "blink";
    147     private static final String TAG_REQUEST_FOCUS = "requestFocus";
    148     private static final String TAG_TAG = "tag";
    149 
    150     private static final String ATTR_LAYOUT = "layout";
    151 
    152     @UnsupportedAppUsage
    153     private static final int[] ATTRS_THEME = new int[] {
    154             com.android.internal.R.attr.theme };
    155 
    156     /**
    157      * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed
    158      * to be inflated.
    159      *
    160      */
    161     public interface Filter {
    162         /**
    163          * Hook to allow clients of the LayoutInflater to restrict the set of Views
    164          * that are allowed to be inflated.
    165          *
    166          * @param clazz The class object for the View that is about to be inflated
    167          *
    168          * @return True if this class is allowed to be inflated, or false otherwise
    169          */
    170         @SuppressWarnings("unchecked")
    171         boolean onLoadClass(Class clazz);
    172     }
    173 
    174     public interface Factory {
    175         /**
    176          * Hook you can supply that is called when inflating from a LayoutInflater.
    177          * You can use this to customize the tag names available in your XML
    178          * layout files.
    179          *
    180          * <p>
    181          * Note that it is good practice to prefix these custom names with your
    182          * package (i.e., com.coolcompany.apps) to avoid conflicts with system
    183          * names.
    184          *
    185          * @param name Tag name to be inflated.
    186          * @param context The context the view is being created in.
    187          * @param attrs Inflation attributes as specified in XML file.
    188          *
    189          * @return View Newly created view. Return null for the default
    190          *         behavior.
    191          */
    192         @Nullable
    193         View onCreateView(@NonNull String name, @NonNull Context context,
    194                 @NonNull AttributeSet attrs);
    195     }
    196 
    197     public interface Factory2 extends Factory {
    198         /**
    199          * Version of {@link #onCreateView(String, Context, AttributeSet)}
    200          * that also supplies the parent that the view created view will be
    201          * placed in.
    202          *
    203          * @param parent The parent that the created view will be placed
    204          * in; <em>note that this may be null</em>.
    205          * @param name Tag name to be inflated.
    206          * @param context The context the view is being created in.
    207          * @param attrs Inflation attributes as specified in XML file.
    208          *
    209          * @return View Newly created view. Return null for the default
    210          *         behavior.
    211          */
    212         @Nullable
    213         View onCreateView(@Nullable View parent, @NonNull String name,
    214                 @NonNull Context context, @NonNull AttributeSet attrs);
    215     }
    216 
    217     private static class FactoryMerger implements Factory2 {
    218         private final Factory mF1, mF2;
    219         private final Factory2 mF12, mF22;
    220 
    221         FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
    222             mF1 = f1;
    223             mF2 = f2;
    224             mF12 = f12;
    225             mF22 = f22;
    226         }
    227 
    228         @Nullable
    229         public View onCreateView(@NonNull String name, @NonNull Context context,
    230                 @NonNull AttributeSet attrs) {
    231             View v = mF1.onCreateView(name, context, attrs);
    232             if (v != null) return v;
    233             return mF2.onCreateView(name, context, attrs);
    234         }
    235 
    236         @Nullable
    237         public View onCreateView(@Nullable View parent, @NonNull String name,
    238                 @NonNull Context context, @NonNull AttributeSet attrs) {
    239             View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
    240                     : mF1.onCreateView(name, context, attrs);
    241             if (v != null) return v;
    242             return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
    243                     : mF2.onCreateView(name, context, attrs);
    244         }
    245     }
    246 
    247     /**
    248      * Create a new LayoutInflater instance associated with a particular Context.
    249      * Applications will almost always want to use
    250      * {@link Context#getSystemService Context.getSystemService()} to retrieve
    251      * the standard {@link Context#LAYOUT_INFLATER_SERVICE Context.INFLATER_SERVICE}.
    252      *
    253      * @param context The Context in which this LayoutInflater will create its
    254      * Views; most importantly, this supplies the theme from which the default
    255      * values for their attributes are retrieved.
    256      */
    257     protected LayoutInflater(Context context) {
    258         mContext = context;
    259         initPrecompiledViews();
    260     }
    261 
    262     /**
    263      * Create a new LayoutInflater instance that is a copy of an existing
    264      * LayoutInflater, optionally with its Context changed.  For use in
    265      * implementing {@link #cloneInContext}.
    266      *
    267      * @param original The original LayoutInflater to copy.
    268      * @param newContext The new Context to use.
    269      */
    270     protected LayoutInflater(LayoutInflater original, Context newContext) {
    271         mContext = newContext;
    272         mFactory = original.mFactory;
    273         mFactory2 = original.mFactory2;
    274         mPrivateFactory = original.mPrivateFactory;
    275         setFilter(original.mFilter);
    276         initPrecompiledViews();
    277     }
    278 
    279     /**
    280      * Obtains the LayoutInflater from the given context.
    281      */
    282     public static LayoutInflater from(Context context) {
    283         LayoutInflater LayoutInflater =
    284                 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    285         if (LayoutInflater == null) {
    286             throw new AssertionError("LayoutInflater not found.");
    287         }
    288         return LayoutInflater;
    289     }
    290 
    291     /**
    292      * Create a copy of the existing LayoutInflater object, with the copy
    293      * pointing to a different Context than the original.  This is used by
    294      * {@link ContextThemeWrapper} to create a new LayoutInflater to go along
    295      * with the new Context theme.
    296      *
    297      * @param newContext The new Context to associate with the new LayoutInflater.
    298      * May be the same as the original Context if desired.
    299      *
    300      * @return Returns a brand spanking new LayoutInflater object associated with
    301      * the given Context.
    302      */
    303     public abstract LayoutInflater cloneInContext(Context newContext);
    304 
    305     /**
    306      * Return the context we are running in, for access to resources, class
    307      * loader, etc.
    308      */
    309     public Context getContext() {
    310         return mContext;
    311     }
    312 
    313     /**
    314      * Return the current {@link Factory} (or null). This is called on each element
    315      * name. If the factory returns a View, add that to the hierarchy. If it
    316      * returns null, proceed to call onCreateView(name).
    317      */
    318     public final Factory getFactory() {
    319         return mFactory;
    320     }
    321 
    322     /**
    323      * Return the current {@link Factory2}.  Returns null if no factory is set
    324      * or the set factory does not implement the {@link Factory2} interface.
    325      * This is called on each element
    326      * name. If the factory returns a View, add that to the hierarchy. If it
    327      * returns null, proceed to call onCreateView(name).
    328      */
    329     public final Factory2 getFactory2() {
    330         return mFactory2;
    331     }
    332 
    333     /**
    334      * Attach a custom Factory interface for creating views while using
    335      * this LayoutInflater.  This must not be null, and can only be set once;
    336      * after setting, you can not change the factory.  This is
    337      * called on each element name as the xml is parsed. If the factory returns
    338      * a View, that is added to the hierarchy. If it returns null, the next
    339      * factory default {@link #onCreateView} method is called.
    340      *
    341      * <p>If you have an existing
    342      * LayoutInflater and want to add your own factory to it, use
    343      * {@link #cloneInContext} to clone the existing instance and then you
    344      * can use this function (once) on the returned new instance.  This will
    345      * merge your own factory with whatever factory the original instance is
    346      * using.
    347      */
    348     public void setFactory(Factory factory) {
    349         if (mFactorySet) {
    350             throw new IllegalStateException("A factory has already been set on this LayoutInflater");
    351         }
    352         if (factory == null) {
    353             throw new NullPointerException("Given factory can not be null");
    354         }
    355         mFactorySet = true;
    356         if (mFactory == null) {
    357             mFactory = factory;
    358         } else {
    359             mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
    360         }
    361     }
    362 
    363     /**
    364      * Like {@link #setFactory}, but allows you to set a {@link Factory2}
    365      * interface.
    366      */
    367     public void setFactory2(Factory2 factory) {
    368         if (mFactorySet) {
    369             throw new IllegalStateException("A factory has already been set on this LayoutInflater");
    370         }
    371         if (factory == null) {
    372             throw new NullPointerException("Given factory can not be null");
    373         }
    374         mFactorySet = true;
    375         if (mFactory == null) {
    376             mFactory = mFactory2 = factory;
    377         } else {
    378             mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
    379         }
    380     }
    381 
    382     /**
    383      * @hide for use by framework
    384      */
    385     @UnsupportedAppUsage
    386     public void setPrivateFactory(Factory2 factory) {
    387         if (mPrivateFactory == null) {
    388             mPrivateFactory = factory;
    389         } else {
    390             mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
    391         }
    392     }
    393 
    394     /**
    395      * @return The {@link Filter} currently used by this LayoutInflater to restrict the set of Views
    396      * that are allowed to be inflated.
    397      */
    398     public Filter getFilter() {
    399         return mFilter;
    400     }
    401 
    402     /**
    403      * Sets the {@link Filter} to by this LayoutInflater. If a view is attempted to be inflated
    404      * which is not allowed by the {@link Filter}, the {@link #inflate(int, ViewGroup)} call will
    405      * throw an {@link InflateException}. This filter will replace any previous filter set on this
    406      * LayoutInflater.
    407      *
    408      * @param filter The Filter which restricts the set of Views that are allowed to be inflated.
    409      *        This filter will replace any previous filter set on this LayoutInflater.
    410      */
    411     public void setFilter(Filter filter) {
    412         mFilter = filter;
    413         if (filter != null) {
    414             mFilterMap = new HashMap<String, Boolean>();
    415         }
    416     }
    417 
    418     private void initPrecompiledViews() {
    419         // Precompiled layouts are not supported in this release.
    420         boolean enabled = false;
    421         initPrecompiledViews(enabled);
    422     }
    423 
    424     private void initPrecompiledViews(boolean enablePrecompiledViews) {
    425         mUseCompiledView = enablePrecompiledViews;
    426 
    427         if (!mUseCompiledView) {
    428             mPrecompiledClassLoader = null;
    429             return;
    430         }
    431 
    432         // Make sure the application allows code generation
    433         ApplicationInfo appInfo = mContext.getApplicationInfo();
    434         if (appInfo.isEmbeddedDexUsed() || appInfo.isPrivilegedApp()) {
    435             mUseCompiledView = false;
    436             return;
    437         }
    438 
    439         // Try to load the precompiled layout file.
    440         try {
    441             mPrecompiledClassLoader = mContext.getClassLoader();
    442             String dexFile = mContext.getCodeCacheDir() + COMPILED_VIEW_DEX_FILE_NAME;
    443             if (new File(dexFile).exists()) {
    444                 mPrecompiledClassLoader = new PathClassLoader(dexFile, mPrecompiledClassLoader);
    445             } else {
    446                 // If the precompiled layout file doesn't exist, then disable precompiled
    447                 // layouts.
    448                 mUseCompiledView = false;
    449             }
    450         } catch (Throwable e) {
    451             if (DEBUG) {
    452                 Log.e(TAG, "Failed to initialized precompiled views layouts", e);
    453             }
    454             mUseCompiledView = false;
    455         }
    456         if (!mUseCompiledView) {
    457             mPrecompiledClassLoader = null;
    458         }
    459     }
    460 
    461     /**
    462      * @hide for use by CTS tests
    463      */
    464     @TestApi
    465     public void setPrecompiledLayoutsEnabledForTesting(boolean enablePrecompiledLayouts) {
    466         initPrecompiledViews(enablePrecompiledLayouts);
    467     }
    468 
    469     /**
    470      * Inflate a new view hierarchy from the specified xml resource. Throws
    471      * {@link InflateException} if there is an error.
    472      *
    473      * @param resource ID for an XML layout resource to load (e.g.,
    474      *        <code>R.layout.main_page</code>)
    475      * @param root Optional view to be the parent of the generated hierarchy.
    476      * @return The root View of the inflated hierarchy. If root was supplied,
    477      *         this is the root View; otherwise it is the root of the inflated
    478      *         XML file.
    479      */
    480     public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
    481         return inflate(resource, root, root != null);
    482     }
    483 
    484     /**
    485      * Inflate a new view hierarchy from the specified xml node. Throws
    486      * {@link InflateException} if there is an error. *
    487      * <p>
    488      * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
    489      * reasons, view inflation relies heavily on pre-processing of XML files
    490      * that is done at build time. Therefore, it is not currently possible to
    491      * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
    492      *
    493      * @param parser XML dom node containing the description of the view
    494      *        hierarchy.
    495      * @param root Optional view to be the parent of the generated hierarchy.
    496      * @return The root View of the inflated hierarchy. If root was supplied,
    497      *         this is the root View; otherwise it is the root of the inflated
    498      *         XML file.
    499      */
    500     public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
    501         return inflate(parser, root, root != null);
    502     }
    503 
    504     /**
    505      * Inflate a new view hierarchy from the specified xml resource. Throws
    506      * {@link InflateException} if there is an error.
    507      *
    508      * @param resource ID for an XML layout resource to load (e.g.,
    509      *        <code>R.layout.main_page</code>)
    510      * @param root Optional view to be the parent of the generated hierarchy (if
    511      *        <em>attachToRoot</em> is true), or else simply an object that
    512      *        provides a set of LayoutParams values for root of the returned
    513      *        hierarchy (if <em>attachToRoot</em> is false.)
    514      * @param attachToRoot Whether the inflated hierarchy should be attached to
    515      *        the root parameter? If false, root is only used to create the
    516      *        correct subclass of LayoutParams for the root view in the XML.
    517      * @return The root View of the inflated hierarchy. If root was supplied and
    518      *         attachToRoot is true, this is root; otherwise it is the root of
    519      *         the inflated XML file.
    520      */
    521     public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
    522         final Resources res = getContext().getResources();
    523         if (DEBUG) {
    524             Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
    525                   + Integer.toHexString(resource) + ")");
    526         }
    527 
    528         View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
    529         if (view != null) {
    530             return view;
    531         }
    532         XmlResourceParser parser = res.getLayout(resource);
    533         try {
    534             return inflate(parser, root, attachToRoot);
    535         } finally {
    536             parser.close();
    537         }
    538     }
    539 
    540     private @Nullable
    541     View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root,
    542         boolean attachToRoot) {
    543         if (!mUseCompiledView) {
    544             return null;
    545         }
    546 
    547         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate (precompiled)");
    548 
    549         // Try to inflate using a precompiled layout.
    550         String pkg = res.getResourcePackageName(resource);
    551         String layout = res.getResourceEntryName(resource);
    552 
    553         try {
    554             Class clazz = Class.forName("" + pkg + ".CompiledView", false, mPrecompiledClassLoader);
    555             Method inflater = clazz.getMethod(layout, Context.class, int.class);
    556             View view = (View) inflater.invoke(null, mContext, resource);
    557 
    558             if (view != null && root != null) {
    559                 // We were able to use the precompiled inflater, but now we need to do some work to
    560                 // attach the view to the root correctly.
    561                 XmlResourceParser parser = res.getLayout(resource);
    562                 try {
    563                     AttributeSet attrs = Xml.asAttributeSet(parser);
    564                     advanceToRootNode(parser);
    565                     ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);
    566 
    567                     if (attachToRoot) {
    568                         root.addView(view, params);
    569                     } else {
    570                         view.setLayoutParams(params);
    571                     }
    572                 } finally {
    573                     parser.close();
    574                 }
    575             }
    576 
    577             return view;
    578         } catch (Throwable e) {
    579             if (DEBUG) {
    580                 Log.e(TAG, "Failed to use precompiled view", e);
    581             }
    582         } finally {
    583             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    584         }
    585         return null;
    586     }
    587 
    588     /**
    589      * Advances the given parser to the first START_TAG. Throws InflateException if no start tag is
    590      * found.
    591      */
    592     private void advanceToRootNode(XmlPullParser parser)
    593         throws InflateException, IOException, XmlPullParserException {
    594         // Look for the root node.
    595         int type;
    596         while ((type = parser.next()) != XmlPullParser.START_TAG &&
    597             type != XmlPullParser.END_DOCUMENT) {
    598             // Empty
    599         }
    600 
    601         if (type != XmlPullParser.START_TAG) {
    602             throw new InflateException(parser.getPositionDescription()
    603                 + ": No start tag found!");
    604         }
    605     }
    606 
    607     /**
    608      * Inflate a new view hierarchy from the specified XML node. Throws
    609      * {@link InflateException} if there is an error.
    610      * <p>
    611      * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
    612      * reasons, view inflation relies heavily on pre-processing of XML files
    613      * that is done at build time. Therefore, it is not currently possible to
    614      * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
    615      *
    616      * @param parser XML dom node containing the description of the view
    617      *        hierarchy.
    618      * @param root Optional view to be the parent of the generated hierarchy (if
    619      *        <em>attachToRoot</em> is true), or else simply an object that
    620      *        provides a set of LayoutParams values for root of the returned
    621      *        hierarchy (if <em>attachToRoot</em> is false.)
    622      * @param attachToRoot Whether the inflated hierarchy should be attached to
    623      *        the root parameter? If false, root is only used to create the
    624      *        correct subclass of LayoutParams for the root view in the XML.
    625      * @return The root View of the inflated hierarchy. If root was supplied and
    626      *         attachToRoot is true, this is root; otherwise it is the root of
    627      *         the inflated XML file.
    628      */
    629     public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
    630         synchronized (mConstructorArgs) {
    631             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
    632 
    633             final Context inflaterContext = mContext;
    634             final AttributeSet attrs = Xml.asAttributeSet(parser);
    635             Context lastContext = (Context) mConstructorArgs[0];
    636             mConstructorArgs[0] = inflaterContext;
    637             View result = root;
    638 
    639             try {
    640                 advanceToRootNode(parser);
    641                 final String name = parser.getName();
    642 
    643                 if (DEBUG) {
    644                     System.out.println("**************************");
    645                     System.out.println("Creating root view: "
    646                             + name);
    647                     System.out.println("**************************");
    648                 }
    649 
    650                 if (TAG_MERGE.equals(name)) {
    651                     if (root == null || !attachToRoot) {
    652                         throw new InflateException("<merge /> can be used only with a valid "
    653                                 + "ViewGroup root and attachToRoot=true");
    654                     }
    655 
    656                     rInflate(parser, root, inflaterContext, attrs, false);
    657                 } else {
    658                     // Temp is the root view that was found in the xml
    659                     final View temp = createViewFromTag(root, name, inflaterContext, attrs);
    660 
    661                     ViewGroup.LayoutParams params = null;
    662 
    663                     if (root != null) {
    664                         if (DEBUG) {
    665                             System.out.println("Creating params from root: " +
    666                                     root);
    667                         }
    668                         // Create layout params that match root, if supplied
    669                         params = root.generateLayoutParams(attrs);
    670                         if (!attachToRoot) {
    671                             // Set the layout params for temp if we are not
    672                             // attaching. (If we are, we use addView, below)
    673                             temp.setLayoutParams(params);
    674                         }
    675                     }
    676 
    677                     if (DEBUG) {
    678                         System.out.println("-----> start inflating children");
    679                     }
    680 
    681                     // Inflate all children under temp against its context.
    682                     rInflateChildren(parser, temp, attrs, true);
    683 
    684                     if (DEBUG) {
    685                         System.out.println("-----> done inflating children");
    686                     }
    687 
    688                     // We are supposed to attach all the views we found (int temp)
    689                     // to root. Do that now.
    690                     if (root != null && attachToRoot) {
    691                         root.addView(temp, params);
    692                     }
    693 
    694                     // Decide whether to return the root that was passed in or the
    695                     // top view found in xml.
    696                     if (root == null || !attachToRoot) {
    697                         result = temp;
    698                     }
    699                 }
    700 
    701             } catch (XmlPullParserException e) {
    702                 final InflateException ie = new InflateException(e.getMessage(), e);
    703                 ie.setStackTrace(EMPTY_STACK_TRACE);
    704                 throw ie;
    705             } catch (Exception e) {
    706                 final InflateException ie = new InflateException(
    707                         getParserStateDescription(inflaterContext, attrs)
    708                         + ": " + e.getMessage(), e);
    709                 ie.setStackTrace(EMPTY_STACK_TRACE);
    710                 throw ie;
    711             } finally {
    712                 // Don't retain static reference on context.
    713                 mConstructorArgs[0] = lastContext;
    714                 mConstructorArgs[1] = null;
    715 
    716                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    717             }
    718 
    719             return result;
    720         }
    721     }
    722 
    723     private static String getParserStateDescription(Context context, AttributeSet attrs) {
    724         int sourceResId = Resources.getAttributeSetSourceResId(attrs);
    725         if (sourceResId == Resources.ID_NULL) {
    726             return attrs.getPositionDescription();
    727         } else {
    728             return attrs.getPositionDescription() + " in "
    729                     + context.getResources().getResourceName(sourceResId);
    730         }
    731     }
    732 
    733     private static final ClassLoader BOOT_CLASS_LOADER = LayoutInflater.class.getClassLoader();
    734 
    735     private final boolean verifyClassLoader(Constructor<? extends View> constructor) {
    736         final ClassLoader constructorLoader = constructor.getDeclaringClass().getClassLoader();
    737         if (constructorLoader == BOOT_CLASS_LOADER) {
    738             // fast path for boot class loader (most common case?) - always ok
    739             return true;
    740         }
    741         // in all normal cases (no dynamic code loading), we will exit the following loop on the
    742         // first iteration (i.e. when the declaring classloader is the contexts class loader).
    743         ClassLoader cl = mContext.getClassLoader();
    744         do {
    745             if (constructorLoader == cl) {
    746                 return true;
    747             }
    748             cl = cl.getParent();
    749         } while (cl != null);
    750         return false;
    751     }
    752     /**
    753      * Low-level function for instantiating a view by name. This attempts to
    754      * instantiate a view class of the given <var>name</var> found in this
    755      * LayoutInflater's ClassLoader. To use an explicit Context in the View
    756      * constructor, use {@link #createView(Context, String, String, AttributeSet)} instead.
    757      *
    758      * <p>
    759      * There are two things that can happen in an error case: either the
    760      * exception describing the error will be thrown, or a null will be
    761      * returned. You must deal with both possibilities -- the former will happen
    762      * the first time createView() is called for a class of a particular name,
    763      * the latter every time there-after for that class name.
    764      *
    765      * @param name The full name of the class to be instantiated.
    766      * @param attrs The XML attributes supplied for this instance.
    767      *
    768      * @return View The newly instantiated view, or null.
    769      */
    770     public final View createView(String name, String prefix, AttributeSet attrs)
    771             throws ClassNotFoundException, InflateException {
    772         Context context = (Context) mConstructorArgs[0];
    773         if (context == null) {
    774             context = mContext;
    775         }
    776         return createView(context, name, prefix, attrs);
    777     }
    778 
    779     /**
    780      * Low-level function for instantiating a view by name. This attempts to
    781      * instantiate a view class of the given <var>name</var> found in this
    782      * LayoutInflater's ClassLoader.
    783      *
    784      * <p>
    785      * There are two things that can happen in an error case: either the
    786      * exception describing the error will be thrown, or a null will be
    787      * returned. You must deal with both possibilities -- the former will happen
    788      * the first time createView() is called for a class of a particular name,
    789      * the latter every time there-after for that class name.
    790      *
    791      * @param viewContext The context used as the context parameter of the View constructor
    792      * @param name The full name of the class to be instantiated.
    793      * @param attrs The XML attributes supplied for this instance.
    794      *
    795      * @return View The newly instantiated view, or null.
    796      */
    797     @Nullable
    798     public final View createView(@NonNull Context viewContext, @NonNull String name,
    799             @Nullable String prefix, @Nullable AttributeSet attrs)
    800             throws ClassNotFoundException, InflateException {
    801         Objects.requireNonNull(viewContext);
    802         Objects.requireNonNull(name);
    803         Constructor<? extends View> constructor = sConstructorMap.get(name);
    804         if (constructor != null && !verifyClassLoader(constructor)) {
    805             constructor = null;
    806             sConstructorMap.remove(name);
    807         }
    808         Class<? extends View> clazz = null;
    809 
    810         try {
    811             Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
    812 
    813             if (constructor == null) {
    814                 // Class not found in the cache, see if it's real, and try to add it
    815                 clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
    816                         mContext.getClassLoader()).asSubclass(View.class);
    817 
    818                 if (mFilter != null && clazz != null) {
    819                     boolean allowed = mFilter.onLoadClass(clazz);
    820                     if (!allowed) {
    821                         failNotAllowed(name, prefix, viewContext, attrs);
    822                     }
    823                 }
    824                 constructor = clazz.getConstructor(mConstructorSignature);
    825                 constructor.setAccessible(true);
    826                 sConstructorMap.put(name, constructor);
    827             } else {
    828                 // If we have a filter, apply it to cached constructor
    829                 if (mFilter != null) {
    830                     // Have we seen this name before?
    831                     Boolean allowedState = mFilterMap.get(name);
    832                     if (allowedState == null) {
    833                         // New class -- remember whether it is allowed
    834                         clazz = Class.forName(prefix != null ? (prefix + name) : name, false,
    835                                 mContext.getClassLoader()).asSubclass(View.class);
    836 
    837                         boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
    838                         mFilterMap.put(name, allowed);
    839                         if (!allowed) {
    840                             failNotAllowed(name, prefix, viewContext, attrs);
    841                         }
    842                     } else if (allowedState.equals(Boolean.FALSE)) {
    843                         failNotAllowed(name, prefix, viewContext, attrs);
    844                     }
    845                 }
    846             }
    847 
    848             Object lastContext = mConstructorArgs[0];
    849             mConstructorArgs[0] = viewContext;
    850             Object[] args = mConstructorArgs;
    851             args[1] = attrs;
    852 
    853             try {
    854                 final View view = constructor.newInstance(args);
    855                 if (view instanceof ViewStub) {
    856                     // Use the same context when inflating ViewStub later.
    857                     final ViewStub viewStub = (ViewStub) view;
    858                     viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
    859                 }
    860                 return view;
    861             } finally {
    862                 mConstructorArgs[0] = lastContext;
    863             }
    864         } catch (NoSuchMethodException e) {
    865             final InflateException ie = new InflateException(
    866                     getParserStateDescription(viewContext, attrs)
    867                     + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
    868             ie.setStackTrace(EMPTY_STACK_TRACE);
    869             throw ie;
    870 
    871         } catch (ClassCastException e) {
    872             // If loaded class is not a View subclass
    873             final InflateException ie = new InflateException(
    874                     getParserStateDescription(viewContext, attrs)
    875                     + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
    876             ie.setStackTrace(EMPTY_STACK_TRACE);
    877             throw ie;
    878         } catch (ClassNotFoundException e) {
    879             // If loadClass fails, we should propagate the exception.
    880             throw e;
    881         } catch (Exception e) {
    882             final InflateException ie = new InflateException(
    883                     getParserStateDescription(viewContext, attrs) + ": Error inflating class "
    884                             + (clazz == null ? "<unknown>" : clazz.getName()), e);
    885             ie.setStackTrace(EMPTY_STACK_TRACE);
    886             throw ie;
    887         } finally {
    888             Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    889         }
    890     }
    891 
    892     /**
    893      * Throw an exception because the specified class is not allowed to be inflated.
    894      */
    895     private void failNotAllowed(String name, String prefix, Context context, AttributeSet attrs) {
    896         throw new InflateException(getParserStateDescription(context, attrs)
    897                 + ": Class not allowed to be inflated "+ (prefix != null ? (prefix + name) : name));
    898     }
    899 
    900     /**
    901      * This routine is responsible for creating the correct subclass of View
    902      * given the xml element name. Override it to handle custom view objects. If
    903      * you override this in your subclass be sure to call through to
    904      * super.onCreateView(name) for names you do not recognize.
    905      *
    906      * @param name The fully qualified class name of the View to be create.
    907      * @param attrs An AttributeSet of attributes to apply to the View.
    908      *
    909      * @return View The View created.
    910      */
    911     protected View onCreateView(String name, AttributeSet attrs)
    912             throws ClassNotFoundException {
    913         return createView(name, "android.view.", attrs);
    914     }
    915 
    916     /**
    917      * Version of {@link #onCreateView(String, AttributeSet)} that also
    918      * takes the future parent of the view being constructed.  The default
    919      * implementation simply calls {@link #onCreateView(String, AttributeSet)}.
    920      *
    921      * @param parent The future parent of the returned view.  <em>Note that
    922      * this may be null.</em>
    923      * @param name The fully qualified class name of the View to be create.
    924      * @param attrs An AttributeSet of attributes to apply to the View.
    925      *
    926      * @return View The View created.
    927      */
    928     protected View onCreateView(View parent, String name, AttributeSet attrs)
    929             throws ClassNotFoundException {
    930         return onCreateView(name, attrs);
    931     }
    932 
    933     /**
    934      * Version of {@link #onCreateView(View, String, AttributeSet)} that also
    935      * takes the inflation context.  The default
    936      * implementation simply calls {@link #onCreateView(View, String, AttributeSet)}.
    937      *
    938      * @param viewContext The Context to be used as a constructor parameter for the View
    939      * @param parent The future parent of the returned view.  <em>Note that
    940      * this may be null.</em>
    941      * @param name The fully qualified class name of the View to be create.
    942      * @param attrs An AttributeSet of attributes to apply to the View.
    943      *
    944      * @return View The View created.
    945      */
    946     @Nullable
    947     public View onCreateView(@NonNull Context viewContext, @Nullable View parent,
    948             @NonNull String name, @Nullable AttributeSet attrs)
    949             throws ClassNotFoundException {
    950         return onCreateView(parent, name, attrs);
    951     }
    952 
    953     /**
    954      * Convenience method for calling through to the five-arg createViewFromTag
    955      * method. This method passes {@code false} for the {@code ignoreThemeAttr}
    956      * argument and should be used for everything except {@code &gt;include>}
    957      * tag parsing.
    958      */
    959     @UnsupportedAppUsage
    960     private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
    961         return createViewFromTag(parent, name, context, attrs, false);
    962     }
    963 
    964     /**
    965      * Creates a view from a tag name using the supplied attribute set.
    966      * <p>
    967      * <strong>Note:</strong> Default visibility so the BridgeInflater can
    968      * override it.
    969      *
    970      * @param parent the parent view, used to inflate layout params
    971      * @param name the name of the XML tag used to define the view
    972      * @param context the inflation context for the view, typically the
    973      *                {@code parent} or base layout inflater context
    974      * @param attrs the attribute set for the XML tag used to define the view
    975      * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}
    976      *                        attribute (if set) for the view being inflated,
    977      *                        {@code false} otherwise
    978      */
    979     @UnsupportedAppUsage
    980     View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
    981             boolean ignoreThemeAttr) {
    982         if (name.equals("view")) {
    983             name = attrs.getAttributeValue(null, "class");
    984         }
    985 
    986         // Apply a theme wrapper, if allowed and one is specified.
    987         if (!ignoreThemeAttr) {
    988             final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
    989             final int themeResId = ta.getResourceId(0, 0);
    990             if (themeResId != 0) {
    991                 context = new ContextThemeWrapper(context, themeResId);
    992             }
    993             ta.recycle();
    994         }
    995 
    996         try {
    997             View view = tryCreateView(parent, name, context, attrs);
    998 
    999             if (view == null) {
   1000                 final Object lastContext = mConstructorArgs[0];
   1001                 mConstructorArgs[0] = context;
   1002                 try {
   1003                     if (-1 == name.indexOf('.')) {
   1004                         view = onCreateView(context, parent, name, attrs);
   1005                     } else {
   1006                         view = createView(context, name, null, attrs);
   1007                     }
   1008                 } finally {
   1009                     mConstructorArgs[0] = lastContext;
   1010                 }
   1011             }
   1012 
   1013             return view;
   1014         } catch (InflateException e) {
   1015             throw e;
   1016 
   1017         } catch (ClassNotFoundException e) {
   1018             final InflateException ie = new InflateException(
   1019                     getParserStateDescription(context, attrs)
   1020                     + ": Error inflating class " + name, e);
   1021             ie.setStackTrace(EMPTY_STACK_TRACE);
   1022             throw ie;
   1023 
   1024         } catch (Exception e) {
   1025             final InflateException ie = new InflateException(
   1026                     getParserStateDescription(context, attrs)
   1027                     + ": Error inflating class " + name, e);
   1028             ie.setStackTrace(EMPTY_STACK_TRACE);
   1029             throw ie;
   1030         }
   1031     }
   1032 
   1033     /**
   1034      * Tries to create a view from a tag name using the supplied attribute set.
   1035      *
   1036      * This method gives the factory provided by {@link LayoutInflater#setFactory} and
   1037      * {@link LayoutInflater#setFactory2} a chance to create a view. However, it does not apply all
   1038      * of the general view creation logic, and thus may return {@code null} for some tags. This
   1039      * method is used by {@link LayoutInflater#inflate} in creating {@code View} objects.
   1040      *
   1041      * @hide for use by precompiled layouts.
   1042      *
   1043      * @param parent the parent view, used to inflate layout params
   1044      * @param name the name of the XML tag used to define the view
   1045      * @param context the inflation context for the view, typically the
   1046      *                {@code parent} or base layout inflater context
   1047      * @param attrs the attribute set for the XML tag used to define the view
   1048      */
   1049     @UnsupportedAppUsage(trackingBug = 122360734)
   1050     @Nullable
   1051     public final View tryCreateView(@Nullable View parent, @NonNull String name,
   1052         @NonNull Context context,
   1053         @NonNull AttributeSet attrs) {
   1054         if (name.equals(TAG_1995)) {
   1055             // Let's party like it's 1995!
   1056             return new BlinkLayout(context, attrs);
   1057         }
   1058 
   1059         View view;
   1060         if (mFactory2 != null) {
   1061             view = mFactory2.onCreateView(parent, name, context, attrs);
   1062         } else if (mFactory != null) {
   1063             view = mFactory.onCreateView(name, context, attrs);
   1064         } else {
   1065             view = null;
   1066         }
   1067 
   1068         if (view == null && mPrivateFactory != null) {
   1069             view = mPrivateFactory.onCreateView(parent, name, context, attrs);
   1070         }
   1071 
   1072         return view;
   1073     }
   1074 
   1075     /**
   1076      * Recursive method used to inflate internal (non-root) children. This
   1077      * method calls through to {@link #rInflate} using the parent context as
   1078      * the inflation context.
   1079      * <strong>Note:</strong> Default visibility so the BridgeInflater can
   1080      * call it.
   1081      */
   1082     final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
   1083             boolean finishInflate) throws XmlPullParserException, IOException {
   1084         rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
   1085     }
   1086 
   1087     /**
   1088      * Recursive method used to descend down the xml hierarchy and instantiate
   1089      * views, instantiate their children, and then call onFinishInflate().
   1090      * <p>
   1091      * <strong>Note:</strong> Default visibility so the BridgeInflater can
   1092      * override it.
   1093      */
   1094     void rInflate(XmlPullParser parser, View parent, Context context,
   1095             AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
   1096 
   1097         final int depth = parser.getDepth();
   1098         int type;
   1099         boolean pendingRequestFocus = false;
   1100 
   1101         while (((type = parser.next()) != XmlPullParser.END_TAG ||
   1102                 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
   1103 
   1104             if (type != XmlPullParser.START_TAG) {
   1105                 continue;
   1106             }
   1107 
   1108             final String name = parser.getName();
   1109 
   1110             if (TAG_REQUEST_FOCUS.equals(name)) {
   1111                 pendingRequestFocus = true;
   1112                 consumeChildElements(parser);
   1113             } else if (TAG_TAG.equals(name)) {
   1114                 parseViewTag(parser, parent, attrs);
   1115             } else if (TAG_INCLUDE.equals(name)) {
   1116                 if (parser.getDepth() == 0) {
   1117                     throw new InflateException("<include /> cannot be the root element");
   1118                 }
   1119                 parseInclude(parser, context, parent, attrs);
   1120             } else if (TAG_MERGE.equals(name)) {
   1121                 throw new InflateException("<merge /> must be the root element");
   1122             } else {
   1123                 final View view = createViewFromTag(parent, name, context, attrs);
   1124                 final ViewGroup viewGroup = (ViewGroup) parent;
   1125                 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
   1126                 rInflateChildren(parser, view, attrs, true);
   1127                 viewGroup.addView(view, params);
   1128             }
   1129         }
   1130 
   1131         if (pendingRequestFocus) {
   1132             parent.restoreDefaultFocus();
   1133         }
   1134 
   1135         if (finishInflate) {
   1136             parent.onFinishInflate();
   1137         }
   1138     }
   1139 
   1140     /**
   1141      * Parses a <code>&lt;tag&gt;</code> element and sets a keyed tag on the
   1142      * containing View.
   1143      */
   1144     private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs)
   1145             throws XmlPullParserException, IOException {
   1146         final Context context = view.getContext();
   1147         final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag);
   1148         final int key = ta.getResourceId(R.styleable.ViewTag_id, 0);
   1149         final CharSequence value = ta.getText(R.styleable.ViewTag_value);
   1150         view.setTag(key, value);
   1151         ta.recycle();
   1152 
   1153         consumeChildElements(parser);
   1154     }
   1155 
   1156     @UnsupportedAppUsage
   1157     private void parseInclude(XmlPullParser parser, Context context, View parent,
   1158             AttributeSet attrs) throws XmlPullParserException, IOException {
   1159         int type;
   1160 
   1161         if (!(parent instanceof ViewGroup)) {
   1162             throw new InflateException("<include /> can only be used inside of a ViewGroup");
   1163         }
   1164 
   1165         // Apply a theme wrapper, if requested. This is sort of a weird
   1166         // edge case, since developers think the <include> overwrites
   1167         // values in the AttributeSet of the included View. So, if the
   1168         // included View has a theme attribute, we'll need to ignore it.
   1169         final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
   1170         final int themeResId = ta.getResourceId(0, 0);
   1171         final boolean hasThemeOverride = themeResId != 0;
   1172         if (hasThemeOverride) {
   1173             context = new ContextThemeWrapper(context, themeResId);
   1174         }
   1175         ta.recycle();
   1176 
   1177         // If the layout is pointing to a theme attribute, we have to
   1178         // massage the value to get a resource identifier out of it.
   1179         int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
   1180         if (layout == 0) {
   1181             final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
   1182             if (value == null || value.length() <= 0) {
   1183                 throw new InflateException("You must specify a layout in the"
   1184                     + " include tag: <include layout=\"@layout/layoutID\" />");
   1185             }
   1186 
   1187             // Attempt to resolve the "?attr/name" string to an attribute
   1188             // within the default (e.g. application) package.
   1189             layout = context.getResources().getIdentifier(
   1190                 value.substring(1), "attr", context.getPackageName());
   1191 
   1192         }
   1193 
   1194         // The layout might be referencing a theme attribute.
   1195         if (mTempValue == null) {
   1196             mTempValue = new TypedValue();
   1197         }
   1198         if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) {
   1199             layout = mTempValue.resourceId;
   1200         }
   1201 
   1202         if (layout == 0) {
   1203             final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
   1204             throw new InflateException("You must specify a valid layout "
   1205                 + "reference. The layout ID " + value + " is not valid.");
   1206         }
   1207 
   1208         final View precompiled = tryInflatePrecompiled(layout, context.getResources(),
   1209             (ViewGroup) parent, /*attachToRoot=*/true);
   1210         if (precompiled == null) {
   1211             final XmlResourceParser childParser = context.getResources().getLayout(layout);
   1212 
   1213             try {
   1214                 final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
   1215 
   1216                 while ((type = childParser.next()) != XmlPullParser.START_TAG &&
   1217                     type != XmlPullParser.END_DOCUMENT) {
   1218                     // Empty.
   1219                 }
   1220 
   1221                 if (type != XmlPullParser.START_TAG) {
   1222                     throw new InflateException(getParserStateDescription(context, childAttrs)
   1223                             + ": No start tag found!");
   1224                 }
   1225 
   1226                 final String childName = childParser.getName();
   1227 
   1228                 if (TAG_MERGE.equals(childName)) {
   1229                     // The <merge> tag doesn't support android:theme, so
   1230                     // nothing special to do here.
   1231                     rInflate(childParser, parent, context, childAttrs, false);
   1232                 } else {
   1233                     final View view = createViewFromTag(parent, childName,
   1234                         context, childAttrs, hasThemeOverride);
   1235                     final ViewGroup group = (ViewGroup) parent;
   1236 
   1237                     final TypedArray a = context.obtainStyledAttributes(
   1238                         attrs, R.styleable.Include);
   1239                     final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
   1240                     final int visibility = a.getInt(R.styleable.Include_visibility, -1);
   1241                     a.recycle();
   1242 
   1243                     // We try to load the layout params set in the <include /> tag.
   1244                     // If the parent can't generate layout params (ex. missing width
   1245                     // or height for the framework ViewGroups, though this is not
   1246                     // necessarily true of all ViewGroups) then we expect it to throw
   1247                     // a runtime exception.
   1248                     // We catch this exception and set localParams accordingly: true
   1249                     // means we successfully loaded layout params from the <include>
   1250                     // tag, false means we need to rely on the included layout params.
   1251                     ViewGroup.LayoutParams params = null;
   1252                     try {
   1253                         params = group.generateLayoutParams(attrs);
   1254                     } catch (RuntimeException e) {
   1255                         // Ignore, just fail over to child attrs.
   1256                     }
   1257                     if (params == null) {
   1258                         params = group.generateLayoutParams(childAttrs);
   1259                     }
   1260                     view.setLayoutParams(params);
   1261 
   1262                     // Inflate all children.
   1263                     rInflateChildren(childParser, view, childAttrs, true);
   1264 
   1265                     if (id != View.NO_ID) {
   1266                         view.setId(id);
   1267                     }
   1268 
   1269                     switch (visibility) {
   1270                         case 0:
   1271                             view.setVisibility(View.VISIBLE);
   1272                             break;
   1273                         case 1:
   1274                             view.setVisibility(View.INVISIBLE);
   1275                             break;
   1276                         case 2:
   1277                             view.setVisibility(View.GONE);
   1278                             break;
   1279                     }
   1280 
   1281                     group.addView(view);
   1282                 }
   1283             } finally {
   1284                 childParser.close();
   1285             }
   1286         }
   1287         LayoutInflater.consumeChildElements(parser);
   1288     }
   1289 
   1290     /**
   1291      * <strong>Note:</strong> default visibility so that
   1292      * LayoutInflater_Delegate can call it.
   1293      */
   1294     final static void consumeChildElements(XmlPullParser parser)
   1295             throws XmlPullParserException, IOException {
   1296         int type;
   1297         final int currentDepth = parser.getDepth();
   1298         while (((type = parser.next()) != XmlPullParser.END_TAG ||
   1299                 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
   1300             // Empty
   1301         }
   1302     }
   1303 
   1304     private static class BlinkLayout extends FrameLayout {
   1305         private static final int MESSAGE_BLINK = 0x42;
   1306         private static final int BLINK_DELAY = 500;
   1307 
   1308         private boolean mBlink;
   1309         private boolean mBlinkState;
   1310         private final Handler mHandler;
   1311 
   1312         public BlinkLayout(Context context, AttributeSet attrs) {
   1313             super(context, attrs);
   1314             mHandler = new Handler(new Handler.Callback() {
   1315                 @Override
   1316                 public boolean handleMessage(Message msg) {
   1317                     if (msg.what == MESSAGE_BLINK) {
   1318                         if (mBlink) {
   1319                             mBlinkState = !mBlinkState;
   1320                             makeBlink();
   1321                         }
   1322                         invalidate();
   1323                         return true;
   1324                     }
   1325                     return false;
   1326                 }
   1327             });
   1328         }
   1329 
   1330         private void makeBlink() {
   1331             Message message = mHandler.obtainMessage(MESSAGE_BLINK);
   1332             mHandler.sendMessageDelayed(message, BLINK_DELAY);
   1333         }
   1334 
   1335         @Override
   1336         protected void onAttachedToWindow() {
   1337             super.onAttachedToWindow();
   1338 
   1339             mBlink = true;
   1340             mBlinkState = true;
   1341 
   1342             makeBlink();
   1343         }
   1344 
   1345         @Override
   1346         protected void onDetachedFromWindow() {
   1347             super.onDetachedFromWindow();
   1348 
   1349             mBlink = false;
   1350             mBlinkState = true;
   1351 
   1352             mHandler.removeMessages(MESSAGE_BLINK);
   1353         }
   1354 
   1355         @Override
   1356         protected void dispatchDraw(Canvas canvas) {
   1357             if (mBlinkState) {
   1358                 super.dispatchDraw(canvas);
   1359             }
   1360         }
   1361     }
   1362 }
   1363