LayoutInflaterCompat.java revision fa79d6fc148a0642f240377b8ce82acfee7bb890
1fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes/*
2fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * Copyright (C) 2014 The Android Open Source Project
3fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes *
4fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * Licensed under the Apache License, Version 2.0 (the "License");
5fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * you may not use this file except in compliance with the License.
6fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * You may obtain a copy of the License at
7fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes *
8fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes *      http://www.apache.org/licenses/LICENSE-2.0
9fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes *
10fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * Unless required by applicable law or agreed to in writing, software
11fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * distributed under the License is distributed on an "AS IS" BASIS,
12fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * See the License for the specific language governing permissions and
14fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * limitations under the License.
15fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes */
16fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
17fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banespackage android.support.v4.view;
18fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
194bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.content.Context;
20fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.os.Build;
214bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.support.annotation.NonNull;
224bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.support.annotation.RequiresApi;
234bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.util.AttributeSet;
244bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.util.Log;
25fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.view.LayoutInflater;
264bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.view.View;
274bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
284bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport java.lang.reflect.Field;
29fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
30fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes/**
314bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas * Helper for accessing features in {@link LayoutInflater}
32fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes * introduced after API level 4 in a backwards compatible fashion.
33fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes */
34c5847d13e40f5d52459f5c0dab32dc08f1a9a683Chris Banespublic final class LayoutInflaterCompat {
354bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    private static final String TAG = "LayoutInflaterCompatHC";
36fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
374bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    private static Field sLayoutInflaterFactory2Field;
384bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    private static boolean sCheckedField;
394bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
404bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    @SuppressWarnings("deprecation")
414bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    static class Factory2Wrapper implements LayoutInflater.Factory2 {
424bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        final LayoutInflaterFactory mDelegateFactory;
434bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
444bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        Factory2Wrapper(LayoutInflaterFactory delegateFactory) {
454bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            mDelegateFactory = delegateFactory;
464bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
47fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
48fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        @Override
494bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public View onCreateView(String name, Context context, AttributeSet attrs) {
504bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            return mDelegateFactory.onCreateView(null, name, context, attrs);
51fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        }
5256594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki
5356594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki        @Override
544bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public View onCreateView(View parent, String name, Context context,
554bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                AttributeSet attributeSet) {
564bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            return mDelegateFactory.onCreateView(parent, name, context, attributeSet);
574bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
584bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
59fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas        @Override
604bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public String toString() {
614bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            return getClass().getName() + "{" + mDelegateFactory + "}";
6256594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki        }
63fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    }
64fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
654bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    /**
664bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * For APIs < 21, there was a framework bug that prevented a LayoutInflater's
674bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * Factory2 from being merged properly if set after a cloneInContext from a LayoutInflater
684bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * that already had a Factory2 registered. We work around that bug here. If we can't we
694bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * log an error.
704bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     */
714bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    static void forceSetFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) {
724bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        if (!sCheckedField) {
734bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            try {
744bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                sLayoutInflaterFactory2Field = LayoutInflater.class.getDeclaredField("mFactory2");
754bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                sLayoutInflaterFactory2Field.setAccessible(true);
764bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            } catch (NoSuchFieldException e) {
774bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                Log.e(TAG, "forceSetFactory2 Could not find field 'mFactory2' on class "
784bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                        + LayoutInflater.class.getName()
794bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                        + "; inflation may have unexpected results.", e);
804bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            }
814bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            sCheckedField = true;
824bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
834bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        if (sLayoutInflaterFactory2Field != null) {
844bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            try {
854bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                sLayoutInflaterFactory2Field.set(inflater, factory);
864bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            } catch (IllegalAccessException e) {
874bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                Log.e(TAG, "forceSetFactory2 could not set the Factory2 on LayoutInflater "
884bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                        + inflater + "; inflation may have unexpected results.", e);
894bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            }
90fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        }
91fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    }
92fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
934bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    static class LayoutInflaterCompatBaseImpl {
944bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        @SuppressWarnings("deprecation")
954bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
964bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            final LayoutInflater.Factory2 factory2 = factory != null
974bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                    ? new Factory2Wrapper(factory) : null;
984bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            setFactory2(inflater, factory2);
994bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
1004bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
1014bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public void setFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) {
1024bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            inflater.setFactory2(factory);
1034bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
1044bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            final LayoutInflater.Factory f = inflater.getFactory();
1054bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            if (f instanceof LayoutInflater.Factory2) {
1064bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                // The merged factory is now set to getFactory(), but not getFactory2() (pre-v21).
1074bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                // We will now try and force set the merged factory to mFactory2
1084bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
1094bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            } else {
1104bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                // Else, we will force set the original wrapped Factory2
1114bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                forceSetFactory2(inflater, factory);
1124bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            }
1134bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
1144bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
1154bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        @SuppressWarnings("deprecation")
1164bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public LayoutInflaterFactory getFactory(LayoutInflater inflater) {
1174bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            LayoutInflater.Factory factory = inflater.getFactory();
1184bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            if (factory instanceof Factory2Wrapper) {
1194bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                return ((Factory2Wrapper) factory).mDelegateFactory;
1204bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            }
1214bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            return null;
1224bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
1234bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    }
1244bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
1254bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    @RequiresApi(21)
1264bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    static class LayoutInflaterCompatApi21Impl extends LayoutInflaterCompatBaseImpl {
1274bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        @SuppressWarnings("deprecation")
1284bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        @Override
1294bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public void setFactory(LayoutInflater inflater, LayoutInflaterFactory factory) {
1304bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            inflater.setFactory2(factory != null ? new Factory2Wrapper(factory) : null);
1314bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
1324bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
13310850534c9e2949857133437cf1cd9af82721ab1Adam Powell        @Override
1344bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public void setFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) {
1354bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            inflater.setFactory2(factory);
13610850534c9e2949857133437cf1cd9af82721ab1Adam Powell        }
13710850534c9e2949857133437cf1cd9af82721ab1Adam Powell    }
13810850534c9e2949857133437cf1cd9af82721ab1Adam Powell
1394bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    static final LayoutInflaterCompatBaseImpl IMPL;
140fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    static {
141c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette        if (Build.VERSION.SDK_INT >= 21) {
1424bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            IMPL = new LayoutInflaterCompatApi21Impl();
143fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        } else {
1444bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            IMPL = new LayoutInflaterCompatBaseImpl();
145fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        }
146fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    }
147fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
148fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    /*
149fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * Hide the constructor.
150fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     */
151fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    private LayoutInflaterCompat() {
152fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    }
153fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
154fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    /**
155fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * Attach a custom Factory interface for creating views while using
156fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * this LayoutInflater. This must not be null, and can only be set once;
157fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * after setting, you can not change the factory.
158fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     *
159fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * @see LayoutInflater#setFactory(android.view.LayoutInflater.Factory)
1604bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     *
1614bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * @deprecated Use {@link #setFactory2(LayoutInflater, LayoutInflater.Factory2)} instead to set
1624bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * and {@link LayoutInflater#getFactory2()} to get the factory.
163fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     */
1644bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    @Deprecated
1654bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    public static void setFactory(
1664bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            @NonNull LayoutInflater inflater, @NonNull LayoutInflaterFactory factory) {
167fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        IMPL.setFactory(inflater, factory);
168fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    }
169fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
17056594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki    /**
1714bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * Attach a custom {@link LayoutInflater.Factory2} for creating views while using
1724bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * this {@link LayoutInflater}. This must not be null, and can only be set once;
1734bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * after setting, you can not change the factory.
1744bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     *
1754bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * @see LayoutInflater#setFactory2(android.view.LayoutInflater.Factory2)
1764bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     */
1774bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    public static void setFactory2(
1784bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            @NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
1794bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        IMPL.setFactory2(inflater, factory);
1804bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    }
1814bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
1824bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    /**
18356594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * Return the current {@link LayoutInflaterFactory} (or null). This is
18456594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * called on each element name. If the factory returns a View, add that
18556594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * to the hierarchy. If it returns null, proceed to call onCreateView(name).
18656594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     *
18756594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * @return The {@link LayoutInflaterFactory} associated with the
18856594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * {@link LayoutInflater}. Will be {@code null} if the inflater does not
18956594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * have a {@link LayoutInflaterFactory} but a raw {@link LayoutInflater.Factory}.
19056594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * @see LayoutInflater#getFactory()
1914bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     *
1924bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * @deprecated Use {@link #setFactory2(LayoutInflater, LayoutInflater.Factory2)} to set and
1934bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * {@link LayoutInflater#getFactory2()} to get the factory.
19456594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     */
1954bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    @Deprecated
19656594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki    public static LayoutInflaterFactory getFactory(LayoutInflater inflater) {
19756594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki        return IMPL.getFactory(inflater);
19856594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki    }
199fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes}
200