Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f38b2c62 authored by George Mount's avatar George Mount
Browse files

Open API for onCreateView and createView with Context

Fixes: 123768619

LayoutInflater's mConstructorArgs was being accessed for two
reasons: reading and writing the context. When onCreateView()
was called, the inflation context was not being passed, so
if the developer wanted to get it, they had to read it from
mConstructorArgs. When the developer wanted to create a view,
with createView(), the developer could not adjust the view's
constructor context.

This CL creates versions of those two classes that take
Context as a parameter to remove the need for accessing
mConstructorArgs.

Test: ran LayoutInflaterTest
Test: Ia40341c24998be13205a72386d217d038a3bcf93
Change-Id: Ic2e990567512dd051f3d4f6c842398c71d6817b5
parent 0a64976a
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -49560,6 +49560,7 @@ package android.view {
    ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context);
    method public abstract android.view.LayoutInflater cloneInContext(android.content.Context);
    method public final android.view.View createView(String, String, android.util.AttributeSet) throws java.lang.ClassNotFoundException, android.view.InflateException;
    method @Nullable public final android.view.View createView(@NonNull android.content.Context, @NonNull String, @Nullable String, @Nullable android.util.AttributeSet) throws java.lang.ClassNotFoundException, android.view.InflateException;
    method public static android.view.LayoutInflater from(android.content.Context);
    method public android.content.Context getContext();
    method public final android.view.LayoutInflater.Factory getFactory();
@@ -49571,6 +49572,7 @@ package android.view {
    method public android.view.View inflate(org.xmlpull.v1.XmlPullParser, @Nullable android.view.ViewGroup, boolean);
    method protected android.view.View onCreateView(String, android.util.AttributeSet) throws java.lang.ClassNotFoundException;
    method protected android.view.View onCreateView(android.view.View, String, android.util.AttributeSet) throws java.lang.ClassNotFoundException;
    method @Nullable public android.view.View onCreateView(@NonNull android.content.Context, @Nullable android.view.View, @NonNull String, @Nullable android.util.AttributeSet) throws java.lang.ClassNotFoundException;
    method public void setFactory(android.view.LayoutInflater.Factory);
    method public void setFactory2(android.view.LayoutInflater.Factory2);
    method public void setFilter(android.view.LayoutInflater.Filter);
+75 −18
Original line number Diff line number Diff line
@@ -42,14 +42,16 @@ import android.widget.FrameLayout;
import com.android.internal.R;

import dalvik.system.PathClassLoader;
import java.io.File;
import java.lang.reflect.Method;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Objects;

/**
 * Instantiates a layout XML file into its corresponding {@link android.view.View}
@@ -110,7 +112,12 @@ public abstract class LayoutInflater {
    // The classloader includes the generated compiled_view.dex file.
    private ClassLoader mPrecompiledClassLoader;

    @UnsupportedAppUsage
    /**
     * This is not a public API. Two APIs are now available to alleviate the need to access
     * this directly: {@link #createView(Context, String, String, AttributeSet)} and
     * {@link #onCreateView(Context, View, String, AttributeSet)}.
     */
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
    final Object[] mConstructorArgs = new Object[2];

    @UnsupportedAppUsage
@@ -721,6 +728,32 @@ public abstract class LayoutInflater {
        } while (cl != null);
        return false;
    }
    /**
     * Low-level function for instantiating a view by name. This attempts to
     * instantiate a view class of the given <var>name</var> found in this
     * LayoutInflater's ClassLoader. To use an explicit Context in the View
     * constructor, use {@link #createView(Context, String, String, AttributeSet)} instead.
     *
     * <p>
     * There are two things that can happen in an error case: either the
     * exception describing the error will be thrown, or a null will be
     * returned. You must deal with both possibilities -- the former will happen
     * the first time createView() is called for a class of a particular name,
     * the latter every time there-after for that class name.
     *
     * @param name The full name of the class to be instantiated.
     * @param attrs The XML attributes supplied for this instance.
     *
     * @return View The newly instantiated view, or null.
     */
    public final View createView(String name, String prefix, AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Context context = (Context) mConstructorArgs[0];
        if (context == null) {
            context = mContext;
        }
        return createView(context, name, prefix, attrs);
    }

    /**
     * Low-level function for instantiating a view by name. This attempts to
@@ -734,13 +767,18 @@ public abstract class LayoutInflater {
     * the first time createView() is called for a class of a particular name,
     * the latter every time there-after for that class name.
     *
     * @param viewContext The context used as the context parameter of the View constructor
     * @param name The full name of the class to be instantiated.
     * @param attrs The XML attributes supplied for this instance.
     *
     * @return View The newly instantiated view, or null.
     */
    public final View createView(String name, String prefix, AttributeSet attrs)
    @Nullable
    public final View createView(@NonNull Context viewContext, @NonNull String name,
            @Nullable String prefix, @Nullable AttributeSet attrs)
            throws ClassNotFoundException, InflateException {
        Objects.requireNonNull(viewContext);
        Objects.requireNonNull(name);
        Constructor<? extends View> constructor = sConstructorMap.get(name);
        if (constructor != null && !verifyClassLoader(constructor)) {
            constructor = null;
@@ -787,22 +825,21 @@ public abstract class LayoutInflater {
            }

            Object lastContext = mConstructorArgs[0];
            if (mConstructorArgs[0] == null) {
                // Fill in the context if not already within inflation.
                mConstructorArgs[0] = mContext;
            }
            mConstructorArgs[0] = viewContext;
            Object[] args = mConstructorArgs;
            args[1] = attrs;

            try {
                final View view = constructor.newInstance(args);
                if (view instanceof ViewStub) {
                    // Use the same context when inflating ViewStub later.
                    final ViewStub viewStub = (ViewStub) view;
                    viewStub.setLayoutInflater(cloneInContext((Context) args[0]));
                }
            mConstructorArgs[0] = lastContext;
                return view;

            } finally {
                mConstructorArgs[0] = lastContext;
            }
        } catch (NoSuchMethodException e) {
            final InflateException ie = new InflateException(attrs.getPositionDescription()
                    + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e);
@@ -870,6 +907,26 @@ public abstract class LayoutInflater {
        return onCreateView(name, attrs);
    }

    /**
     * Version of {@link #onCreateView(View, String, AttributeSet)} that also
     * takes the inflation context.  The default
     * implementation simply calls {@link #onCreateView(View, String, AttributeSet)}.
     *
     * @param viewContext The Context to be used as a constructor parameter for the View
     * @param parent The future parent of the returned view.  <em>Note that
     * this may be null.</em>
     * @param name The fully qualified class name of the View to be create.
     * @param attrs An AttributeSet of attributes to apply to the View.
     *
     * @return View The View created.
     */
    @Nullable
    public View onCreateView(@NonNull Context viewContext, @Nullable View parent,
            @NonNull String name, @Nullable AttributeSet attrs)
            throws ClassNotFoundException {
        return onCreateView(parent, name, attrs);
    }

    /**
     * Convenience method for calling through to the five-arg createViewFromTag
     * method. This method passes {@code false} for the {@code ignoreThemeAttr}
@@ -921,9 +978,9 @@ public abstract class LayoutInflater {
                mConstructorArgs[0] = context;
                try {
                    if (-1 == name.indexOf('.')) {
                        view = onCreateView(parent, name, attrs);
                        view = onCreateView(context, parent, name, attrs);
                    } else {
                        view = createView(name, null, attrs);
                        view = createView(context, name, null, attrs);
                    }
                } finally {
                    mConstructorArgs[0] = lastContext;