blob: 2e27737782fac9b18308071ea1aa1fdb148ccb2a [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;
Ian Lake0c807f42018-09-12 14:03:27 -070020import 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;
Eric Holk25aa4ed2019-01-18 11:49:55 -080023import android.annotation.TestApi;
Mathew Inwooda570dee2018-08-17 14:56:00 +010024import android.annotation.UnsupportedAppUsage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Context;
Eric Holk987c7a92019-01-17 10:51:17 -080026import android.content.pm.ApplicationInfo;
Alan Viverette0810b632014-05-01 14:42:56 -070027import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.res.TypedArray;
29import android.content.res.XmlResourceParser;
Alan Viverettee8489cd2015-02-03 14:40:45 -080030import android.graphics.Canvas;
Cătălin Tudor0c0e82a2018-10-18 18:32:49 +010031import android.os.Build;
Alan Viverettee8489cd2015-02-03 14:40:45 -080032import android.os.Handler;
33import android.os.Message;
Eric Holk928bbac2019-01-02 10:38:54 -080034import android.os.SystemProperties;
Alan Viverettee8489cd2015-02-03 14:40:45 -080035import android.os.Trace;
Mathieu Chartier743b3632019-02-07 13:14:19 -080036import android.provider.DeviceConfig;
37import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import android.util.AttributeSet;
Alan Viverette0810b632014-05-01 14:42:56 -070039import android.util.Log;
Alan Viverettee8489cd2015-02-03 14:40:45 -080040import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.util.Xml;
Alan Viverettee8489cd2015-02-03 14:40:45 -080042import android.widget.FrameLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070044import com.android.internal.R;
45
Eric Holk928bbac2019-01-02 10:38:54 -080046import dalvik.system.PathClassLoader;
George Mountf38b2c62019-02-06 16:29:27 -080047
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070048import org.xmlpull.v1.XmlPullParser;
49import org.xmlpull.v1.XmlPullParserException;
50
George Mountf38b2c62019-02-06 16:29:27 -080051import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import java.io.IOException;
53import java.lang.reflect.Constructor;
George Mountf38b2c62019-02-06 16:29:27 -080054import java.lang.reflect.Method;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import java.util.HashMap;
George Mountf38b2c62019-02-06 16:29:27 -080056import java.util.Objects;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
58/**
Scott Main93dc6422012-02-24 12:04:06 -080059 * Instantiates a layout XML file into its corresponding {@link android.view.View}
60 * objects. It is never used directly. Instead, use
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061 * {@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
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060064 * for the device you are running on.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070065 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 * <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.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -070071 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 * <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.)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 */
Jeff Sharkeyd86b8fe2017-06-02 17:36:26 -060079@SystemService(Context.LAYOUT_INFLATER_SERVICE)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080public abstract class LayoutInflater {
Alan Viverette33e3cda2014-12-17 15:43:29 -080081
Alan Viverette0810b632014-05-01 14:42:56 -070082 private static final String TAG = LayoutInflater.class.getSimpleName();
83 private static final boolean DEBUG = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
Eric Holk928bbac2019-01-02 10:38:54 -080085 private static final String COMPILED_VIEW_DEX_FILE_NAME = "/compiled_view.dex";
86
Alan Viverette8f124812015-09-25 15:17:05 -040087 /** Empty stack trace used to avoid log spam in re-throw exceptions. */
88 private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
89
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 /**
91 * This field should be made private, so it is hidden from the SDK.
92 * {@hide}
93 */
Cătălin Tudor0c0e82a2018-10-18 18:32:49 +010094 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 protected final Context mContext;
96
97 // these are optional, set by the caller
George Mount38432882019-02-07 08:36:54 -080098 /**
99 * If any developer has desire to change this value, they should instead use
100 * {@link #cloneInContext(Context)} and set the new factory in thew newly-created
101 * LayoutInflater.
102 */
103 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 private boolean mFactorySet;
Mathew Inwooda570dee2018-08-17 14:56:00 +0100105 @UnsupportedAppUsage
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 private Factory mFactory;
Mathew Inwooda570dee2018-08-17 14:56:00 +0100107 @UnsupportedAppUsage
Dianne Hackborn625ac272010-09-17 18:29:22 -0700108 private Factory2 mFactory2;
Mathew Inwooda570dee2018-08-17 14:56:00 +0100109 @UnsupportedAppUsage
Dianne Hackborn420829e2011-01-28 11:30:35 -0800110 private Factory2 mPrivateFactory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 private Filter mFilter;
112
Eric Holk928bbac2019-01-02 10:38:54 -0800113 // Indicates whether we should try to inflate layouts using a precompiled layout instead of
114 // inflating from the XML resource.
115 private boolean mUseCompiledView;
116 // This variable holds the classloader that will be used to look for precompiled layouts. The
117 // The classloader includes the generated compiled_view.dex file.
118 private ClassLoader mPrecompiledClassLoader;
119
George Mountf38b2c62019-02-06 16:29:27 -0800120 /**
121 * This is not a public API. Two APIs are now available to alleviate the need to access
122 * this directly: {@link #createView(Context, String, String, AttributeSet)} and
123 * {@link #onCreateView(Context, View, String, AttributeSet)}.
124 */
125 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
Xavier Ducrohet7f9f99ea2011-08-11 10:16:17 -0700126 final Object[] mConstructorArgs = new Object[2];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127
Mathew Inwooda570dee2018-08-17 14:56:00 +0100128 @UnsupportedAppUsage
Xavier Ducrohet7f9f99ea2011-08-11 10:16:17 -0700129 static final Class<?>[] mConstructorSignature = new Class[] {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 Context.class, AttributeSet.class};
131
Cătălin Tudore61fc252019-02-12 10:28:47 +0000132 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769490)
Gilles Debunne30301932010-06-16 18:32:00 -0700133 private static final HashMap<String, Constructor<? extends View>> sConstructorMap =
134 new HashMap<String, Constructor<? extends View>>();
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700135
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 private HashMap<String, Boolean> mFilterMap;
137
Alan Viverette33e3cda2014-12-17 15:43:29 -0800138 private TypedValue mTempValue;
139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 private static final String TAG_MERGE = "merge";
141 private static final String TAG_INCLUDE = "include";
Romain Guy9c1223a2011-05-17 14:25:49 -0700142 private static final String TAG_1995 = "blink";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 private static final String TAG_REQUEST_FOCUS = "requestFocus";
Alan Viverette451a3412014-02-11 18:08:46 -0800144 private static final String TAG_TAG = "tag";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145
Alan Viverette33e3cda2014-12-17 15:43:29 -0800146 private static final String ATTR_LAYOUT = "layout";
147
Mathew Inwooda570dee2018-08-17 14:56:00 +0100148 @UnsupportedAppUsage
Alan Viveretteef259e42014-01-24 17:20:12 -0800149 private static final int[] ATTRS_THEME = new int[] {
150 com.android.internal.R.attr.theme };
Alan Viverette24927f22014-01-07 17:28:48 -0800151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 /**
153 * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed
154 * to be inflated.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700155 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 */
157 public interface Filter {
158 /**
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700159 * Hook to allow clients of the LayoutInflater to restrict the set of Views
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800160 * that are allowed to be inflated.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700161 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 * @param clazz The class object for the View that is about to be inflated
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700163 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 * @return True if this class is allowed to be inflated, or false otherwise
165 */
Gilles Debunnee6ac8b92010-06-17 10:55:04 -0700166 @SuppressWarnings("unchecked")
167 boolean onLoadClass(Class clazz);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 public interface Factory {
171 /**
172 * Hook you can supply that is called when inflating from a LayoutInflater.
173 * You can use this to customize the tag names available in your XML
174 * layout files.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700175 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 * <p>
177 * Note that it is good practice to prefix these custom names with your
178 * package (i.e., com.coolcompany.apps) to avoid conflicts with system
179 * names.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700180 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 * @param name Tag name to be inflated.
182 * @param context The context the view is being created in.
183 * @param attrs Inflation attributes as specified in XML file.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700184 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 * @return View Newly created view. Return null for the default
186 * behavior.
187 */
Ian Lake0c807f42018-09-12 14:03:27 -0700188 @Nullable
189 View onCreateView(@NonNull String name, @NonNull Context context,
190 @NonNull AttributeSet attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 }
192
Dianne Hackborn625ac272010-09-17 18:29:22 -0700193 public interface Factory2 extends Factory {
194 /**
195 * Version of {@link #onCreateView(String, Context, AttributeSet)}
196 * that also supplies the parent that the view created view will be
197 * placed in.
198 *
199 * @param parent The parent that the created view will be placed
200 * in; <em>note that this may be null</em>.
201 * @param name Tag name to be inflated.
202 * @param context The context the view is being created in.
203 * @param attrs Inflation attributes as specified in XML file.
204 *
205 * @return View Newly created view. Return null for the default
206 * behavior.
207 */
Ian Lake0c807f42018-09-12 14:03:27 -0700208 @Nullable
209 View onCreateView(@Nullable View parent, @NonNull String name,
210 @NonNull Context context, @NonNull AttributeSet attrs);
Dianne Hackborn625ac272010-09-17 18:29:22 -0700211 }
212
213 private static class FactoryMerger implements Factory2 {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 private final Factory mF1, mF2;
Dianne Hackborn625ac272010-09-17 18:29:22 -0700215 private final Factory2 mF12, mF22;
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700216
Dianne Hackborn625ac272010-09-17 18:29:22 -0700217 FactoryMerger(Factory f1, Factory2 f12, Factory f2, Factory2 f22) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 mF1 = f1;
219 mF2 = f2;
Dianne Hackborn625ac272010-09-17 18:29:22 -0700220 mF12 = f12;
221 mF22 = f22;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700223
Ian Lake0c807f42018-09-12 14:03:27 -0700224 @Nullable
225 public View onCreateView(@NonNull String name, @NonNull Context context,
226 @NonNull AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800227 View v = mF1.onCreateView(name, context, attrs);
228 if (v != null) return v;
229 return mF2.onCreateView(name, context, attrs);
230 }
Dianne Hackborn625ac272010-09-17 18:29:22 -0700231
Ian Lake0c807f42018-09-12 14:03:27 -0700232 @Nullable
233 public View onCreateView(@Nullable View parent, @NonNull String name,
234 @NonNull Context context, @NonNull AttributeSet attrs) {
Dianne Hackborn625ac272010-09-17 18:29:22 -0700235 View v = mF12 != null ? mF12.onCreateView(parent, name, context, attrs)
236 : mF1.onCreateView(name, context, attrs);
237 if (v != null) return v;
238 return mF22 != null ? mF22.onCreateView(parent, name, context, attrs)
239 : mF2.onCreateView(name, context, attrs);
240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 /**
244 * Create a new LayoutInflater instance associated with a particular Context.
245 * Applications will almost always want to use
246 * {@link Context#getSystemService Context.getSystemService()} to retrieve
247 * the standard {@link Context#LAYOUT_INFLATER_SERVICE Context.INFLATER_SERVICE}.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700248 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 * @param context The Context in which this LayoutInflater will create its
250 * Views; most importantly, this supplies the theme from which the default
251 * values for their attributes are retrieved.
252 */
253 protected LayoutInflater(Context context) {
254 mContext = context;
Eric Holk928bbac2019-01-02 10:38:54 -0800255 initPrecompiledViews();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 }
257
258 /**
259 * Create a new LayoutInflater instance that is a copy of an existing
260 * LayoutInflater, optionally with its Context changed. For use in
261 * implementing {@link #cloneInContext}.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700262 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 * @param original The original LayoutInflater to copy.
264 * @param newContext The new Context to use.
265 */
266 protected LayoutInflater(LayoutInflater original, Context newContext) {
267 mContext = newContext;
268 mFactory = original.mFactory;
Dianne Hackborn625ac272010-09-17 18:29:22 -0700269 mFactory2 = original.mFactory2;
Dianne Hackborn420829e2011-01-28 11:30:35 -0800270 mPrivateFactory = original.mPrivateFactory;
Dan Sandler0c7bb332014-09-18 22:11:18 -0400271 setFilter(original.mFilter);
Eric Holk928bbac2019-01-02 10:38:54 -0800272 initPrecompiledViews();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700274
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 /**
276 * Obtains the LayoutInflater from the given context.
277 */
278 public static LayoutInflater from(Context context) {
279 LayoutInflater LayoutInflater =
280 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
281 if (LayoutInflater == null) {
282 throw new AssertionError("LayoutInflater not found.");
283 }
284 return LayoutInflater;
285 }
286
287 /**
288 * Create a copy of the existing LayoutInflater object, with the copy
289 * pointing to a different Context than the original. This is used by
290 * {@link ContextThemeWrapper} to create a new LayoutInflater to go along
291 * with the new Context theme.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700292 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 * @param newContext The new Context to associate with the new LayoutInflater.
294 * May be the same as the original Context if desired.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700295 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 * @return Returns a brand spanking new LayoutInflater object associated with
297 * the given Context.
298 */
299 public abstract LayoutInflater cloneInContext(Context newContext);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700300
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 /**
302 * Return the context we are running in, for access to resources, class
303 * loader, etc.
304 */
305 public Context getContext() {
306 return mContext;
307 }
308
309 /**
Dianne Hackborn625ac272010-09-17 18:29:22 -0700310 * Return the current {@link Factory} (or null). This is called on each element
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 * name. If the factory returns a View, add that to the hierarchy. If it
312 * returns null, proceed to call onCreateView(name).
313 */
314 public final Factory getFactory() {
315 return mFactory;
316 }
317
318 /**
Dianne Hackborn625ac272010-09-17 18:29:22 -0700319 * Return the current {@link Factory2}. Returns null if no factory is set
320 * or the set factory does not implement the {@link Factory2} interface.
321 * This is called on each element
322 * name. If the factory returns a View, add that to the hierarchy. If it
323 * returns null, proceed to call onCreateView(name).
324 */
325 public final Factory2 getFactory2() {
326 return mFactory2;
327 }
328
329 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 * Attach a custom Factory interface for creating views while using
331 * this LayoutInflater. This must not be null, and can only be set once;
332 * after setting, you can not change the factory. This is
333 * called on each element name as the xml is parsed. If the factory returns
334 * a View, that is added to the hierarchy. If it returns null, the next
335 * factory default {@link #onCreateView} method is called.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700336 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 * <p>If you have an existing
338 * LayoutInflater and want to add your own factory to it, use
339 * {@link #cloneInContext} to clone the existing instance and then you
340 * can use this function (once) on the returned new instance. This will
341 * merge your own factory with whatever factory the original instance is
342 * using.
343 */
344 public void setFactory(Factory factory) {
345 if (mFactorySet) {
346 throw new IllegalStateException("A factory has already been set on this LayoutInflater");
347 }
348 if (factory == null) {
349 throw new NullPointerException("Given factory can not be null");
350 }
351 mFactorySet = true;
352 if (mFactory == null) {
353 mFactory = factory;
354 } else {
Dianne Hackborn625ac272010-09-17 18:29:22 -0700355 mFactory = new FactoryMerger(factory, null, mFactory, mFactory2);
356 }
357 }
358
359 /**
360 * Like {@link #setFactory}, but allows you to set a {@link Factory2}
361 * interface.
362 */
363 public void setFactory2(Factory2 factory) {
364 if (mFactorySet) {
365 throw new IllegalStateException("A factory has already been set on this LayoutInflater");
366 }
367 if (factory == null) {
368 throw new NullPointerException("Given factory can not be null");
369 }
370 mFactorySet = true;
371 if (mFactory == null) {
372 mFactory = mFactory2 = factory;
373 } else {
Adam Powell371a8092014-06-20 12:51:12 -0700374 mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 }
376 }
377
378 /**
Dianne Hackborn420829e2011-01-28 11:30:35 -0800379 * @hide for use by framework
380 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100381 @UnsupportedAppUsage
Dianne Hackborn420829e2011-01-28 11:30:35 -0800382 public void setPrivateFactory(Factory2 factory) {
Adam Powell371a8092014-06-20 12:51:12 -0700383 if (mPrivateFactory == null) {
384 mPrivateFactory = factory;
385 } else {
386 mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
387 }
Dianne Hackborn420829e2011-01-28 11:30:35 -0800388 }
389
390 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 * @return The {@link Filter} currently used by this LayoutInflater to restrict the set of Views
392 * that are allowed to be inflated.
393 */
394 public Filter getFilter() {
395 return mFilter;
396 }
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 /**
399 * Sets the {@link Filter} to by this LayoutInflater. If a view is attempted to be inflated
400 * which is not allowed by the {@link Filter}, the {@link #inflate(int, ViewGroup)} call will
401 * throw an {@link InflateException}. This filter will replace any previous filter set on this
402 * LayoutInflater.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700403 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 * @param filter The Filter which restricts the set of Views that are allowed to be inflated.
405 * This filter will replace any previous filter set on this LayoutInflater.
406 */
407 public void setFilter(Filter filter) {
408 mFilter = filter;
409 if (filter != null) {
410 mFilterMap = new HashMap<String, Boolean>();
411 }
412 }
413
Eric Holk928bbac2019-01-02 10:38:54 -0800414 private void initPrecompiledViews() {
Mathieu Chartier743b3632019-02-07 13:14:19 -0800415 // Use the device config if enabled, otherwise default to the system property.
416 String usePrecompiledLayout = null;
417 try {
418 usePrecompiledLayout = DeviceConfig.getProperty(
419 DeviceConfig.Runtime.NAMESPACE,
420 DeviceConfig.Runtime.USE_PRECOMPILED_LAYOUT);
421 } catch (Exception e) {
422 // May be caused by permission errors reading the property (i.e. instant apps).
423 }
424 boolean enabled = false;
425 if (TextUtils.isEmpty(usePrecompiledLayout)) {
426 enabled = SystemProperties.getBoolean(
427 DeviceConfig.Runtime.USE_PRECOMPILED_LAYOUT,
428 false);
429 } else {
430 enabled = Boolean.parseBoolean(usePrecompiledLayout);
431 }
432 initPrecompiledViews(enabled);
Eric Holk25aa4ed2019-01-18 11:49:55 -0800433 }
434
435 private void initPrecompiledViews(boolean enablePrecompiledViews) {
436 mUseCompiledView = enablePrecompiledViews;
437
Eric Holk987c7a92019-01-17 10:51:17 -0800438 if (!mUseCompiledView) {
Eric Holk25aa4ed2019-01-18 11:49:55 -0800439 mPrecompiledClassLoader = null;
Eric Holk987c7a92019-01-17 10:51:17 -0800440 return;
441 }
442
443 // Make sure the application allows code generation
444 ApplicationInfo appInfo = mContext.getApplicationInfo();
Victor Hsiehfa9df0b2019-01-29 12:48:36 -0800445 if (appInfo.isEmbeddedDexUsed() || appInfo.isPrivilegedApp()) {
Eric Holk987c7a92019-01-17 10:51:17 -0800446 mUseCompiledView = false;
447 return;
448 }
449
450 // Try to load the precompiled layout file.
Eric Holk928bbac2019-01-02 10:38:54 -0800451 try {
Eric Holk987c7a92019-01-17 10:51:17 -0800452 mPrecompiledClassLoader = mContext.getClassLoader();
453 String dexFile = mContext.getCodeCacheDir() + COMPILED_VIEW_DEX_FILE_NAME;
454 if (new File(dexFile).exists()) {
455 mPrecompiledClassLoader = new PathClassLoader(dexFile, mPrecompiledClassLoader);
456 } else {
457 // If the precompiled layout file doesn't exist, then disable precompiled
458 // layouts.
459 mUseCompiledView = false;
Eric Holk928bbac2019-01-02 10:38:54 -0800460 }
461 } catch (Throwable e) {
462 if (DEBUG) {
463 Log.e(TAG, "Failed to initialized precompiled views layouts", e);
464 }
465 mUseCompiledView = false;
466 }
Eric Holk25aa4ed2019-01-18 11:49:55 -0800467 if (!mUseCompiledView) {
468 mPrecompiledClassLoader = null;
469 }
470 }
471
472 /**
473 * @hide for use by CTS tests
474 */
475 @TestApi
476 public void setPrecompiledLayoutsEnabledForTesting(boolean enablePrecompiledLayouts) {
477 initPrecompiledViews(enablePrecompiledLayouts);
Eric Holk928bbac2019-01-02 10:38:54 -0800478 }
479
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 /**
481 * Inflate a new view hierarchy from the specified xml resource. Throws
482 * {@link InflateException} if there is an error.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700483 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 * @param resource ID for an XML layout resource to load (e.g.,
485 * <code>R.layout.main_page</code>)
486 * @param root Optional view to be the parent of the generated hierarchy.
487 * @return The root View of the inflated hierarchy. If root was supplied,
488 * this is the root View; otherwise it is the root of the inflated
489 * XML file.
490 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700491 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800492 return inflate(resource, root, root != null);
493 }
494
495 /**
496 * Inflate a new view hierarchy from the specified xml node. Throws
497 * {@link InflateException} if there is an error. *
498 * <p>
499 * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
500 * reasons, view inflation relies heavily on pre-processing of XML files
501 * that is done at build time. Therefore, it is not currently possible to
502 * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700503 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 * @param parser XML dom node containing the description of the view
505 * hierarchy.
506 * @param root Optional view to be the parent of the generated hierarchy.
507 * @return The root View of the inflated hierarchy. If root was supplied,
508 * this is the root View; otherwise it is the root of the inflated
509 * XML file.
510 */
Scott Kennedydb5fd422015-01-15 11:56:33 -0800511 public View inflate(XmlPullParser parser, @Nullable ViewGroup root) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 return inflate(parser, root, root != null);
513 }
514
515 /**
516 * Inflate a new view hierarchy from the specified xml resource. Throws
517 * {@link InflateException} if there is an error.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700518 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 * @param resource ID for an XML layout resource to load (e.g.,
520 * <code>R.layout.main_page</code>)
521 * @param root Optional view to be the parent of the generated hierarchy (if
522 * <em>attachToRoot</em> is true), or else simply an object that
523 * provides a set of LayoutParams values for root of the returned
524 * hierarchy (if <em>attachToRoot</em> is false.)
525 * @param attachToRoot Whether the inflated hierarchy should be attached to
526 * the root parameter? If false, root is only used to create the
527 * correct subclass of LayoutParams for the root view in the XML.
528 * @return The root View of the inflated hierarchy. If root was supplied and
529 * attachToRoot is true, this is root; otherwise it is the root of
530 * the inflated XML file.
531 */
Tor Norbye7b9c9122013-05-30 16:48:33 -0700532 public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
Alan Viverette0810b632014-05-01 14:42:56 -0700533 final Resources res = getContext().getResources();
534 if (DEBUG) {
535 Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
Eric Holk928bbac2019-01-02 10:38:54 -0800536 + Integer.toHexString(resource) + ")");
Alan Viverette0810b632014-05-01 14:42:56 -0700537 }
538
Eric Holk928bbac2019-01-02 10:38:54 -0800539 View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
540 if (view != null) {
541 return view;
542 }
543 XmlResourceParser parser = res.getLayout(resource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 try {
545 return inflate(parser, root, attachToRoot);
546 } finally {
547 parser.close();
548 }
549 }
550
Eric Holk928bbac2019-01-02 10:38:54 -0800551 private @Nullable
552 View tryInflatePrecompiled(@LayoutRes int resource, Resources res, @Nullable ViewGroup root,
553 boolean attachToRoot) {
554 if (!mUseCompiledView) {
555 return null;
556 }
557
558 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate (precompiled)");
559
560 // Try to inflate using a precompiled layout.
561 String pkg = res.getResourcePackageName(resource);
562 String layout = res.getResourceEntryName(resource);
563
564 try {
565 Class clazz = mPrecompiledClassLoader.loadClass("" + pkg + ".CompiledView");
566 Method inflater = clazz.getMethod(layout, Context.class, int.class);
567 View view = (View) inflater.invoke(null, mContext, resource);
568
569 if (view != null && root != null) {
570 // We were able to use the precompiled inflater, but now we need to do some work to
571 // attach the view to the root correctly.
572 XmlResourceParser parser = res.getLayout(resource);
573 try {
574 AttributeSet attrs = Xml.asAttributeSet(parser);
575 advanceToRootNode(parser);
576 ViewGroup.LayoutParams params = root.generateLayoutParams(attrs);
577
578 if (attachToRoot) {
579 root.addView(view, params);
580 } else {
581 view.setLayoutParams(params);
582 }
583 } finally {
584 parser.close();
585 }
586 }
587
588 return view;
589 } catch (Throwable e) {
590 if (DEBUG) {
591 Log.e(TAG, "Failed to use precompiled view", e);
592 }
593 } finally {
594 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
595 }
596 return null;
597 }
598
599 /**
600 * Advances the given parser to the first START_TAG. Throws InflateException if no start tag is
601 * found.
602 */
603 private void advanceToRootNode(XmlPullParser parser)
604 throws InflateException, IOException, XmlPullParserException {
605 // Look for the root node.
606 int type;
607 while ((type = parser.next()) != XmlPullParser.START_TAG &&
608 type != XmlPullParser.END_DOCUMENT) {
609 // Empty
610 }
611
612 if (type != XmlPullParser.START_TAG) {
613 throw new InflateException(parser.getPositionDescription()
614 + ": No start tag found!");
615 }
616 }
617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 /**
619 * Inflate a new view hierarchy from the specified XML node. Throws
620 * {@link InflateException} if there is an error.
621 * <p>
622 * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
623 * reasons, view inflation relies heavily on pre-processing of XML files
624 * that is done at build time. Therefore, it is not currently possible to
625 * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700626 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 * @param parser XML dom node containing the description of the view
628 * hierarchy.
629 * @param root Optional view to be the parent of the generated hierarchy (if
630 * <em>attachToRoot</em> is true), or else simply an object that
631 * provides a set of LayoutParams values for root of the returned
632 * hierarchy (if <em>attachToRoot</em> is false.)
633 * @param attachToRoot Whether the inflated hierarchy should be attached to
634 * the root parameter? If false, root is only used to create the
635 * correct subclass of LayoutParams for the root view in the XML.
636 * @return The root View of the inflated hierarchy. If root was supplied and
637 * attachToRoot is true, this is root; otherwise it is the root of
638 * the inflated XML file.
639 */
Scott Kennedydb5fd422015-01-15 11:56:33 -0800640 public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 synchronized (mConstructorArgs) {
Romain Guy09f7b932013-04-10 11:42:44 -0700642 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate");
643
Alan Viverette6194d722015-03-20 15:49:06 -0700644 final Context inflaterContext = mContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 final AttributeSet attrs = Xml.asAttributeSet(parser);
Alan Viverette6194d722015-03-20 15:49:06 -0700646 Context lastContext = (Context) mConstructorArgs[0];
647 mConstructorArgs[0] = inflaterContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648 View result = root;
649
650 try {
Eric Holk928bbac2019-01-02 10:38:54 -0800651 advanceToRootNode(parser);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 final String name = parser.getName();
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700653
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800654 if (DEBUG) {
655 System.out.println("**************************");
656 System.out.println("Creating root view: "
657 + name);
658 System.out.println("**************************");
659 }
660
661 if (TAG_MERGE.equals(name)) {
662 if (root == null || !attachToRoot) {
663 throw new InflateException("<merge /> can be used only with a valid "
664 + "ViewGroup root and attachToRoot=true");
665 }
666
Alan Viverette6194d722015-03-20 15:49:06 -0700667 rInflate(parser, root, inflaterContext, attrs, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 } else {
669 // Temp is the root view that was found in the xml
Alan Viverette6194d722015-03-20 15:49:06 -0700670 final View temp = createViewFromTag(root, name, inflaterContext, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671
672 ViewGroup.LayoutParams params = null;
673
674 if (root != null) {
675 if (DEBUG) {
676 System.out.println("Creating params from root: " +
677 root);
678 }
679 // Create layout params that match root, if supplied
680 params = root.generateLayoutParams(attrs);
681 if (!attachToRoot) {
682 // Set the layout params for temp if we are not
683 // attaching. (If we are, we use addView, below)
684 temp.setLayoutParams(params);
685 }
686 }
687
688 if (DEBUG) {
689 System.out.println("-----> start inflating children");
690 }
Alan Viverette6194d722015-03-20 15:49:06 -0700691
692 // Inflate all children under temp against its context.
693 rInflateChildren(parser, temp, attrs, true);
694
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 if (DEBUG) {
696 System.out.println("-----> done inflating children");
697 }
698
699 // We are supposed to attach all the views we found (int temp)
700 // to root. Do that now.
701 if (root != null && attachToRoot) {
702 root.addView(temp, params);
703 }
704
705 // Decide whether to return the root that was passed in or the
706 // top view found in xml.
707 if (root == null || !attachToRoot) {
708 result = temp;
709 }
710 }
711
712 } catch (XmlPullParserException e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400713 final InflateException ie = new InflateException(e.getMessage(), e);
714 ie.setStackTrace(EMPTY_STACK_TRACE);
715 throw ie;
Alan Viverette93795052015-03-09 15:32:50 -0700716 } catch (Exception e) {
Aurimas Liutikas10c42162019-03-06 17:40:49 -0800717 final InflateException ie = new InflateException(
718 getParserStateDescription(inflaterContext, attrs)
Alan Viverette8f124812015-09-25 15:17:05 -0400719 + ": " + e.getMessage(), e);
720 ie.setStackTrace(EMPTY_STACK_TRACE);
721 throw ie;
Dianne Hackborn9dae48e2010-08-26 10:20:01 -0700722 } finally {
723 // Don't retain static reference on context.
724 mConstructorArgs[0] = lastContext;
725 mConstructorArgs[1] = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726
John Reck465e1a32015-10-19 12:46:09 -0700727 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
728 }
Romain Guy09f7b932013-04-10 11:42:44 -0700729
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 return result;
731 }
732 }
733
Aurimas Liutikas10c42162019-03-06 17:40:49 -0800734 private static String getParserStateDescription(Context context, AttributeSet attrs) {
735 int sourceResId = Resources.getAttributeSetSourceResId(attrs);
736 if (sourceResId == Resources.ID_NULL) {
737 return attrs.getPositionDescription();
738 } else {
739 return attrs.getPositionDescription() + " in "
740 + context.getResources().getResourceName(sourceResId);
741 }
742 }
743
Mathew Inwood4985c5c2015-12-31 12:28:54 +0000744 private static final ClassLoader BOOT_CLASS_LOADER = LayoutInflater.class.getClassLoader();
745
746 private final boolean verifyClassLoader(Constructor<? extends View> constructor) {
747 final ClassLoader constructorLoader = constructor.getDeclaringClass().getClassLoader();
748 if (constructorLoader == BOOT_CLASS_LOADER) {
749 // fast path for boot class loader (most common case?) - always ok
750 return true;
751 }
752 // in all normal cases (no dynamic code loading), we will exit the following loop on the
753 // first iteration (i.e. when the declaring classloader is the contexts class loader).
754 ClassLoader cl = mContext.getClassLoader();
755 do {
756 if (constructorLoader == cl) {
757 return true;
758 }
759 cl = cl.getParent();
760 } while (cl != null);
761 return false;
762 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 /**
764 * Low-level function for instantiating a view by name. This attempts to
765 * instantiate a view class of the given <var>name</var> found in this
George Mountf38b2c62019-02-06 16:29:27 -0800766 * LayoutInflater's ClassLoader. To use an explicit Context in the View
767 * constructor, use {@link #createView(Context, String, String, AttributeSet)} instead.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700768 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 * <p>
770 * There are two things that can happen in an error case: either the
771 * exception describing the error will be thrown, or a null will be
772 * returned. You must deal with both possibilities -- the former will happen
773 * the first time createView() is called for a class of a particular name,
774 * the latter every time there-after for that class name.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700775 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 * @param name The full name of the class to be instantiated.
777 * @param attrs The XML attributes supplied for this instance.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700778 *
Gilles Debunne30301932010-06-16 18:32:00 -0700779 * @return View The newly instantiated view, or null.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 */
781 public final View createView(String name, String prefix, AttributeSet attrs)
782 throws ClassNotFoundException, InflateException {
George Mountf38b2c62019-02-06 16:29:27 -0800783 Context context = (Context) mConstructorArgs[0];
784 if (context == null) {
785 context = mContext;
786 }
787 return createView(context, name, prefix, attrs);
788 }
789
790 /**
791 * Low-level function for instantiating a view by name. This attempts to
792 * instantiate a view class of the given <var>name</var> found in this
793 * LayoutInflater's ClassLoader.
794 *
795 * <p>
796 * There are two things that can happen in an error case: either the
797 * exception describing the error will be thrown, or a null will be
798 * returned. You must deal with both possibilities -- the former will happen
799 * the first time createView() is called for a class of a particular name,
800 * the latter every time there-after for that class name.
801 *
802 * @param viewContext The context used as the context parameter of the View constructor
803 * @param name The full name of the class to be instantiated.
804 * @param attrs The XML attributes supplied for this instance.
805 *
806 * @return View The newly instantiated view, or null.
807 */
808 @Nullable
809 public final View createView(@NonNull Context viewContext, @NonNull String name,
810 @Nullable String prefix, @Nullable AttributeSet attrs)
811 throws ClassNotFoundException, InflateException {
812 Objects.requireNonNull(viewContext);
813 Objects.requireNonNull(name);
Gilles Debunne30301932010-06-16 18:32:00 -0700814 Constructor<? extends View> constructor = sConstructorMap.get(name);
Mathew Inwood4985c5c2015-12-31 12:28:54 +0000815 if (constructor != null && !verifyClassLoader(constructor)) {
816 constructor = null;
817 sConstructorMap.remove(name);
818 }
Gilles Debunne30301932010-06-16 18:32:00 -0700819 Class<? extends View> clazz = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800820
821 try {
Romain Guy09f7b932013-04-10 11:42:44 -0700822 Trace.traceBegin(Trace.TRACE_TAG_VIEW, name);
823
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 if (constructor == null) {
825 // Class not found in the cache, see if it's real, and try to add it
Romain Guyd03b8802009-09-16 14:36:16 -0700826 clazz = mContext.getClassLoader().loadClass(
Gilles Debunne30301932010-06-16 18:32:00 -0700827 prefix != null ? (prefix + name) : name).asSubclass(View.class);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700828
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800829 if (mFilter != null && clazz != null) {
830 boolean allowed = mFilter.onLoadClass(clazz);
831 if (!allowed) {
Aurimas Liutikas10c42162019-03-06 17:40:49 -0800832 failNotAllowed(name, prefix, viewContext, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 }
834 }
835 constructor = clazz.getConstructor(mConstructorSignature);
Alan Viverette904de2e2015-05-04 10:32:57 -0700836 constructor.setAccessible(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 sConstructorMap.put(name, constructor);
838 } else {
839 // If we have a filter, apply it to cached constructor
840 if (mFilter != null) {
841 // Have we seen this name before?
842 Boolean allowedState = mFilterMap.get(name);
843 if (allowedState == null) {
844 // New class -- remember whether it is allowed
Romain Guyd03b8802009-09-16 14:36:16 -0700845 clazz = mContext.getClassLoader().loadClass(
Gilles Debunne30301932010-06-16 18:32:00 -0700846 prefix != null ? (prefix + name) : name).asSubclass(View.class);
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700847
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
849 mFilterMap.put(name, allowed);
850 if (!allowed) {
Aurimas Liutikas10c42162019-03-06 17:40:49 -0800851 failNotAllowed(name, prefix, viewContext, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 }
853 } else if (allowedState.equals(Boolean.FALSE)) {
Aurimas Liutikas10c42162019-03-06 17:40:49 -0800854 failNotAllowed(name, prefix, viewContext, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 }
856 }
857 }
858
Jason Monk9a376bc2017-05-10 09:52:10 -0400859 Object lastContext = mConstructorArgs[0];
George Mountf38b2c62019-02-06 16:29:27 -0800860 mConstructorArgs[0] = viewContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 Object[] args = mConstructorArgs;
862 args[1] = attrs;
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700863
George Mountf38b2c62019-02-06 16:29:27 -0800864 try {
865 final View view = constructor.newInstance(args);
866 if (view instanceof ViewStub) {
867 // Use the same context when inflating ViewStub later.
868 final ViewStub viewStub = (ViewStub) view;
869 viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
870 }
871 return view;
872 } finally {
873 mConstructorArgs[0] = lastContext;
Jeff Sharkeyb27b7a12012-04-02 21:07:29 -0700874 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 } catch (NoSuchMethodException e) {
Aurimas Liutikas10c42162019-03-06 17:40:49 -0800876 final InflateException ie = new InflateException(
877 getParserStateDescription(viewContext, attrs)
Alan Viverette8f124812015-09-25 15:17:05 -0400878 + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
879 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800880 throw ie;
881
Gilles Debunne30301932010-06-16 18:32:00 -0700882 } catch (ClassCastException e) {
883 // If loaded class is not a View subclass
Aurimas Liutikas10c42162019-03-06 17:40:49 -0800884 final InflateException ie = new InflateException(
885 getParserStateDescription(viewContext, attrs)
Alan Viverette8f124812015-09-25 15:17:05 -0400886 + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e);
887 ie.setStackTrace(EMPTY_STACK_TRACE);
Gilles Debunne30301932010-06-16 18:32:00 -0700888 throw ie;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 } catch (ClassNotFoundException e) {
890 // If loadClass fails, we should propagate the exception.
891 throw e;
892 } catch (Exception e) {
Alan Viverette8f124812015-09-25 15:17:05 -0400893 final InflateException ie = new InflateException(
Aurimas Liutikas10c42162019-03-06 17:40:49 -0800894 getParserStateDescription(viewContext, attrs) + ": Error inflating class "
Alan Viverette8f124812015-09-25 15:17:05 -0400895 + (clazz == null ? "<unknown>" : clazz.getName()), e);
896 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 throw ie;
Romain Guy09f7b932013-04-10 11:42:44 -0700898 } finally {
899 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 }
901 }
902
903 /**
Gilles Debunne30301932010-06-16 18:32:00 -0700904 * Throw an exception because the specified class is not allowed to be inflated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 */
Aurimas Liutikas10c42162019-03-06 17:40:49 -0800906 private void failNotAllowed(String name, String prefix, Context context, AttributeSet attrs) {
907 throw new InflateException(getParserStateDescription(context, attrs)
Alan Viverette8f124812015-09-25 15:17:05 -0400908 + ": Class not allowed to be inflated "+ (prefix != null ? (prefix + name) : name));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 }
910
911 /**
912 * This routine is responsible for creating the correct subclass of View
913 * given the xml element name. Override it to handle custom view objects. If
914 * you override this in your subclass be sure to call through to
915 * super.onCreateView(name) for names you do not recognize.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700916 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 * @param name The fully qualified class name of the View to be create.
918 * @param attrs An AttributeSet of attributes to apply to the View.
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -0700919 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 * @return View The View created.
921 */
922 protected View onCreateView(String name, AttributeSet attrs)
923 throws ClassNotFoundException {
924 return createView(name, "android.view.", attrs);
925 }
926
Dianne Hackborn625ac272010-09-17 18:29:22 -0700927 /**
928 * Version of {@link #onCreateView(String, AttributeSet)} that also
Chet Haase430742f2013-04-12 11:18:36 -0700929 * takes the future parent of the view being constructed. The default
Dianne Hackborn625ac272010-09-17 18:29:22 -0700930 * implementation simply calls {@link #onCreateView(String, AttributeSet)}.
931 *
932 * @param parent The future parent of the returned view. <em>Note that
933 * this may be null.</em>
934 * @param name The fully qualified class name of the View to be create.
935 * @param attrs An AttributeSet of attributes to apply to the View.
936 *
937 * @return View The View created.
938 */
939 protected View onCreateView(View parent, String name, AttributeSet attrs)
940 throws ClassNotFoundException {
941 return onCreateView(name, attrs);
942 }
943
Alan Viverette24927f22014-01-07 17:28:48 -0800944 /**
George Mountf38b2c62019-02-06 16:29:27 -0800945 * Version of {@link #onCreateView(View, String, AttributeSet)} that also
946 * takes the inflation context. The default
947 * implementation simply calls {@link #onCreateView(View, String, AttributeSet)}.
948 *
949 * @param viewContext The Context to be used as a constructor parameter for the View
950 * @param parent The future parent of the returned view. <em>Note that
951 * this may be null.</em>
952 * @param name The fully qualified class name of the View to be create.
953 * @param attrs An AttributeSet of attributes to apply to the View.
954 *
955 * @return View The View created.
956 */
957 @Nullable
958 public View onCreateView(@NonNull Context viewContext, @Nullable View parent,
959 @NonNull String name, @Nullable AttributeSet attrs)
960 throws ClassNotFoundException {
961 return onCreateView(parent, name, attrs);
962 }
963
964 /**
Alan Viverette6194d722015-03-20 15:49:06 -0700965 * Convenience method for calling through to the five-arg createViewFromTag
966 * method. This method passes {@code false} for the {@code ignoreThemeAttr}
967 * argument and should be used for everything except {@code &gt;include>}
968 * tag parsing.
969 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100970 @UnsupportedAppUsage
Alan Viverette6194d722015-03-20 15:49:06 -0700971 private View createViewFromTag(View parent, String name, Context context, AttributeSet attrs) {
972 return createViewFromTag(parent, name, context, attrs, false);
973 }
974
975 /**
Alan Viverette24927f22014-01-07 17:28:48 -0800976 * Creates a view from a tag name using the supplied attribute set.
977 * <p>
Alan Viverette6194d722015-03-20 15:49:06 -0700978 * <strong>Note:</strong> Default visibility so the BridgeInflater can
979 * override it.
980 *
981 * @param parent the parent view, used to inflate layout params
982 * @param name the name of the XML tag used to define the view
983 * @param context the inflation context for the view, typically the
984 * {@code parent} or base layout inflater context
985 * @param attrs the attribute set for the XML tag used to define the view
986 * @param ignoreThemeAttr {@code true} to ignore the {@code android:theme}
987 * attribute (if set) for the view being inflated,
988 * {@code false} otherwise
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 */
Mathew Inwooda570dee2018-08-17 14:56:00 +0100990 @UnsupportedAppUsage
Alan Viverette6194d722015-03-20 15:49:06 -0700991 View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
992 boolean ignoreThemeAttr) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 if (name.equals("view")) {
994 name = attrs.getAttributeValue(null, "class");
995 }
996
Alan Viverette6194d722015-03-20 15:49:06 -0700997 // Apply a theme wrapper, if allowed and one is specified.
998 if (!ignoreThemeAttr) {
999 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
1000 final int themeResId = ta.getResourceId(0, 0);
1001 if (themeResId != 0) {
1002 context = new ContextThemeWrapper(context, themeResId);
1003 }
1004 ta.recycle();
Alan Viverette24927f22014-01-07 17:28:48 -08001005 }
1006
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001007 try {
Eric Holk29d0cd62018-12-14 10:27:29 -08001008 View view = tryCreateView(parent, name, context, attrs);
Alan Viverette24927f22014-01-07 17:28:48 -08001009
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 if (view == null) {
Alan Viverette24927f22014-01-07 17:28:48 -08001011 final Object lastContext = mConstructorArgs[0];
Alan Viverette6194d722015-03-20 15:49:06 -07001012 mConstructorArgs[0] = context;
Alan Viverette24927f22014-01-07 17:28:48 -08001013 try {
1014 if (-1 == name.indexOf('.')) {
George Mountf38b2c62019-02-06 16:29:27 -08001015 view = onCreateView(context, parent, name, attrs);
Alan Viverette24927f22014-01-07 17:28:48 -08001016 } else {
George Mountf38b2c62019-02-06 16:29:27 -08001017 view = createView(context, name, null, attrs);
Alan Viverette24927f22014-01-07 17:28:48 -08001018 }
1019 } finally {
1020 mConstructorArgs[0] = lastContext;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 }
1022 }
1023
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 return view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 } catch (InflateException e) {
1026 throw e;
1027
1028 } catch (ClassNotFoundException e) {
Aurimas Liutikas10c42162019-03-06 17:40:49 -08001029 final InflateException ie = new InflateException(
1030 getParserStateDescription(context, attrs)
Alan Viverette8f124812015-09-25 15:17:05 -04001031 + ": Error inflating class " + name, e);
1032 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 throw ie;
1034
1035 } catch (Exception e) {
Aurimas Liutikas10c42162019-03-06 17:40:49 -08001036 final InflateException ie = new InflateException(
1037 getParserStateDescription(context, attrs)
Alan Viverette8f124812015-09-25 15:17:05 -04001038 + ": Error inflating class " + name, e);
1039 ie.setStackTrace(EMPTY_STACK_TRACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 throw ie;
1041 }
1042 }
1043
1044 /**
Eric Holk29d0cd62018-12-14 10:27:29 -08001045 * Tries to create a view from a tag name using the supplied attribute set.
1046 *
1047 * This method gives the factory provided by {@link LayoutInflater#setFactory} and
1048 * {@link LayoutInflater#setFactory2} a chance to create a view. However, it does not apply all
1049 * of the general view creation logic, and thus may return {@code null} for some tags. This
1050 * method is used by {@link LayoutInflater#inflate} in creating {@code View} objects.
1051 *
1052 * @hide for use by precompiled layouts.
1053 *
1054 * @param parent the parent view, used to inflate layout params
1055 * @param name the name of the XML tag used to define the view
1056 * @param context the inflation context for the view, typically the
1057 * {@code parent} or base layout inflater context
1058 * @param attrs the attribute set for the XML tag used to define the view
1059 */
1060 @UnsupportedAppUsage(trackingBug = 122360734)
1061 @Nullable
1062 public final View tryCreateView(@Nullable View parent, @NonNull String name,
1063 @NonNull Context context,
1064 @NonNull AttributeSet attrs) {
1065 if (name.equals(TAG_1995)) {
1066 // Let's party like it's 1995!
1067 return new BlinkLayout(context, attrs);
1068 }
1069
1070 View view;
1071 if (mFactory2 != null) {
1072 view = mFactory2.onCreateView(parent, name, context, attrs);
1073 } else if (mFactory != null) {
1074 view = mFactory.onCreateView(name, context, attrs);
1075 } else {
1076 view = null;
1077 }
1078
1079 if (view == null && mPrivateFactory != null) {
1080 view = mPrivateFactory.onCreateView(parent, name, context, attrs);
1081 }
1082
1083 return view;
1084 }
1085
1086 /**
Alan Viverette6194d722015-03-20 15:49:06 -07001087 * Recursive method used to inflate internal (non-root) children. This
1088 * method calls through to {@link #rInflate} using the parent context as
1089 * the inflation context.
1090 * <strong>Note:</strong> Default visibility so the BridgeInflater can
1091 * call it.
1092 */
1093 final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,
1094 boolean finishInflate) throws XmlPullParserException, IOException {
1095 rInflate(parser, parent, parent.getContext(), attrs, finishInflate);
1096 }
1097
1098 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 * Recursive method used to descend down the xml hierarchy and instantiate
1100 * views, instantiate their children, and then call onFinishInflate().
Alan Viverette6194d722015-03-20 15:49:06 -07001101 * <p>
1102 * <strong>Note:</strong> Default visibility so the BridgeInflater can
1103 * override it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 */
Alan Viverette6194d722015-03-20 15:49:06 -07001105 void rInflate(XmlPullParser parser, View parent, Context context,
1106 AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107
1108 final int depth = parser.getDepth();
1109 int type;
Evan Rosky37df2db2017-01-24 16:35:52 -08001110 boolean pendingRequestFocus = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111
1112 while (((type = parser.next()) != XmlPullParser.END_TAG ||
1113 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
1114
1115 if (type != XmlPullParser.START_TAG) {
1116 continue;
1117 }
1118
1119 final String name = parser.getName();
Aurimas Liutikas67e2ae82016-10-11 18:17:42 -07001120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 if (TAG_REQUEST_FOCUS.equals(name)) {
Evan Rosky37df2db2017-01-24 16:35:52 -08001122 pendingRequestFocus = true;
1123 consumeChildElements(parser);
Alan Viverette451a3412014-02-11 18:08:46 -08001124 } else if (TAG_TAG.equals(name)) {
1125 parseViewTag(parser, parent, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 } else if (TAG_INCLUDE.equals(name)) {
1127 if (parser.getDepth() == 0) {
1128 throw new InflateException("<include /> cannot be the root element");
1129 }
Alan Viverette6194d722015-03-20 15:49:06 -07001130 parseInclude(parser, context, parent, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 } else if (TAG_MERGE.equals(name)) {
1132 throw new InflateException("<merge /> must be the root element");
1133 } else {
Alan Viverette6194d722015-03-20 15:49:06 -07001134 final View view = createViewFromTag(parent, name, context, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 final ViewGroup viewGroup = (ViewGroup) parent;
1136 final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
Alan Viverette6194d722015-03-20 15:49:06 -07001137 rInflateChildren(parser, view, attrs, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 viewGroup.addView(view, params);
1139 }
1140 }
1141
Evan Rosky37df2db2017-01-24 16:35:52 -08001142 if (pendingRequestFocus) {
1143 parent.restoreDefaultFocus();
1144 }
1145
Alan Viverette6194d722015-03-20 15:49:06 -07001146 if (finishInflate) {
1147 parent.onFinishInflate();
1148 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 }
1150
Alan Viverette451a3412014-02-11 18:08:46 -08001151 /**
Alan Viverette451a3412014-02-11 18:08:46 -08001152 * Parses a <code>&lt;tag&gt;</code> element and sets a keyed tag on the
1153 * containing View.
1154 */
1155 private void parseViewTag(XmlPullParser parser, View view, AttributeSet attrs)
1156 throws XmlPullParserException, IOException {
Alan Viverette6194d722015-03-20 15:49:06 -07001157 final Context context = view.getContext();
1158 final TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ViewTag);
1159 final int key = ta.getResourceId(R.styleable.ViewTag_id, 0);
1160 final CharSequence value = ta.getText(R.styleable.ViewTag_value);
Alan Viverette451a3412014-02-11 18:08:46 -08001161 view.setTag(key, value);
1162 ta.recycle();
1163
Alan Viverette6194d722015-03-20 15:49:06 -07001164 consumeChildElements(parser);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001165 }
1166
Mathew Inwooda570dee2018-08-17 14:56:00 +01001167 @UnsupportedAppUsage
Alan Viverette6194d722015-03-20 15:49:06 -07001168 private void parseInclude(XmlPullParser parser, Context context, View parent,
1169 AttributeSet attrs) throws XmlPullParserException, IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 int type;
1171
Eric Holkbfc68702019-01-02 11:11:24 -08001172 if (!(parent instanceof ViewGroup)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 throw new InflateException("<include /> can only be used inside of a ViewGroup");
1174 }
1175
Eric Holkbfc68702019-01-02 11:11:24 -08001176 // Apply a theme wrapper, if requested. This is sort of a weird
1177 // edge case, since developers think the <include> overwrites
1178 // values in the AttributeSet of the included View. So, if the
1179 // included View has a theme attribute, we'll need to ignore it.
1180 final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
1181 final int themeResId = ta.getResourceId(0, 0);
1182 final boolean hasThemeOverride = themeResId != 0;
1183 if (hasThemeOverride) {
1184 context = new ContextThemeWrapper(context, themeResId);
1185 }
1186 ta.recycle();
1187
1188 // If the layout is pointing to a theme attribute, we have to
1189 // massage the value to get a resource identifier out of it.
1190 int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
1191 if (layout == 0) {
1192 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
1193 if (value == null || value.length() <= 0) {
1194 throw new InflateException("You must specify a layout in the"
1195 + " include tag: <include layout=\"@layout/layoutID\" />");
1196 }
1197
1198 // Attempt to resolve the "?attr/name" string to an attribute
1199 // within the default (e.g. application) package.
1200 layout = context.getResources().getIdentifier(
1201 value.substring(1), "attr", context.getPackageName());
1202
1203 }
1204
1205 // The layout might be referencing a theme attribute.
1206 if (mTempValue == null) {
1207 mTempValue = new TypedValue();
1208 }
1209 if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) {
1210 layout = mTempValue.resourceId;
1211 }
1212
1213 if (layout == 0) {
1214 final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
1215 throw new InflateException("You must specify a valid layout "
1216 + "reference. The layout ID " + value + " is not valid.");
1217 }
1218
Eric Holk928bbac2019-01-02 10:38:54 -08001219 final View precompiled = tryInflatePrecompiled(layout, context.getResources(),
1220 (ViewGroup) parent, /*attachToRoot=*/true);
1221 if (precompiled == null) {
1222 final XmlResourceParser childParser = context.getResources().getLayout(layout);
Eric Holkbfc68702019-01-02 11:11:24 -08001223
Eric Holk928bbac2019-01-02 10:38:54 -08001224 try {
1225 final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
Eric Holkbfc68702019-01-02 11:11:24 -08001226
Eric Holk928bbac2019-01-02 10:38:54 -08001227 while ((type = childParser.next()) != XmlPullParser.START_TAG &&
1228 type != XmlPullParser.END_DOCUMENT) {
1229 // Empty.
1230 }
1231
1232 if (type != XmlPullParser.START_TAG) {
Aurimas Liutikas10c42162019-03-06 17:40:49 -08001233 throw new InflateException(getParserStateDescription(context, childAttrs)
1234 + ": No start tag found!");
Eric Holk928bbac2019-01-02 10:38:54 -08001235 }
1236
1237 final String childName = childParser.getName();
1238
1239 if (TAG_MERGE.equals(childName)) {
1240 // The <merge> tag doesn't support android:theme, so
1241 // nothing special to do here.
1242 rInflate(childParser, parent, context, childAttrs, false);
1243 } else {
1244 final View view = createViewFromTag(parent, childName,
1245 context, childAttrs, hasThemeOverride);
1246 final ViewGroup group = (ViewGroup) parent;
1247
1248 final TypedArray a = context.obtainStyledAttributes(
1249 attrs, R.styleable.Include);
1250 final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
1251 final int visibility = a.getInt(R.styleable.Include_visibility, -1);
1252 a.recycle();
1253
1254 // We try to load the layout params set in the <include /> tag.
1255 // If the parent can't generate layout params (ex. missing width
1256 // or height for the framework ViewGroups, though this is not
1257 // necessarily true of all ViewGroups) then we expect it to throw
1258 // a runtime exception.
1259 // We catch this exception and set localParams accordingly: true
1260 // means we successfully loaded layout params from the <include>
1261 // tag, false means we need to rely on the included layout params.
1262 ViewGroup.LayoutParams params = null;
1263 try {
1264 params = group.generateLayoutParams(attrs);
1265 } catch (RuntimeException e) {
1266 // Ignore, just fail over to child attrs.
1267 }
1268 if (params == null) {
1269 params = group.generateLayoutParams(childAttrs);
1270 }
1271 view.setLayoutParams(params);
1272
1273 // Inflate all children.
1274 rInflateChildren(childParser, view, childAttrs, true);
1275
1276 if (id != View.NO_ID) {
1277 view.setId(id);
1278 }
1279
1280 switch (visibility) {
1281 case 0:
1282 view.setVisibility(View.VISIBLE);
1283 break;
1284 case 1:
1285 view.setVisibility(View.INVISIBLE);
1286 break;
1287 case 2:
1288 view.setVisibility(View.GONE);
1289 break;
1290 }
1291
1292 group.addView(view);
1293 }
1294 } finally {
1295 childParser.close();
Eric Holkbfc68702019-01-02 11:11:24 -08001296 }
Eric Holkbfc68702019-01-02 11:11:24 -08001297 }
Alan Viverette6194d722015-03-20 15:49:06 -07001298 LayoutInflater.consumeChildElements(parser);
1299 }
1300
1301 /**
1302 * <strong>Note:</strong> default visibility so that
1303 * LayoutInflater_Delegate can call it.
1304 */
1305 final static void consumeChildElements(XmlPullParser parser)
1306 throws XmlPullParserException, IOException {
1307 int type;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308 final int currentDepth = parser.getDepth();
1309 while (((type = parser.next()) != XmlPullParser.END_TAG ||
1310 parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
1311 // Empty
1312 }
Romain Guy9c1223a2011-05-17 14:25:49 -07001313 }
1314
1315 private static class BlinkLayout extends FrameLayout {
1316 private static final int MESSAGE_BLINK = 0x42;
1317 private static final int BLINK_DELAY = 500;
1318
1319 private boolean mBlink;
1320 private boolean mBlinkState;
1321 private final Handler mHandler;
1322
1323 public BlinkLayout(Context context, AttributeSet attrs) {
1324 super(context, attrs);
1325 mHandler = new Handler(new Handler.Callback() {
1326 @Override
1327 public boolean handleMessage(Message msg) {
1328 if (msg.what == MESSAGE_BLINK) {
1329 if (mBlink) {
1330 mBlinkState = !mBlinkState;
1331 makeBlink();
1332 }
1333 invalidate();
1334 return true;
1335 }
1336 return false;
1337 }
1338 });
1339 }
1340
1341 private void makeBlink() {
1342 Message message = mHandler.obtainMessage(MESSAGE_BLINK);
1343 mHandler.sendMessageDelayed(message, BLINK_DELAY);
1344 }
1345
1346 @Override
1347 protected void onAttachedToWindow() {
1348 super.onAttachedToWindow();
1349
1350 mBlink = true;
1351 mBlinkState = true;
1352
1353 makeBlink();
1354 }
1355
1356 @Override
1357 protected void onDetachedFromWindow() {
1358 super.onDetachedFromWindow();
1359
1360 mBlink = false;
1361 mBlinkState = true;
1362
1363 mHandler.removeMessages(MESSAGE_BLINK);
1364 }
1365
1366 @Override
1367 protected void dispatchDraw(Canvas canvas) {
1368 if (mBlinkState) {
1369 super.dispatchDraw(canvas);
1370 }
1371 }
1372 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373}