1fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes/*
2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 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
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.core.view;
18fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
194bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.content.Context;
20fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.os.Build;
214bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.util.AttributeSet;
224bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.util.Log;
23fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banesimport android.view.LayoutInflater;
244bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport android.view.View;
254bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
269dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikasimport androidx.annotation.NonNull;
279dede51868bbbe16aadcd65e04860bea8ea50e05Aurimas Liutikas
284bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikasimport java.lang.reflect.Field;
29fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
30fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes/**
31645e5c8aa6b31961c5f73f3d30bb5261d5e04aebKirill Grouchnikov * Helper for accessing features in {@link LayoutInflater}.
32fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes */
33c5847d13e40f5d52459f5c0dab32dc08f1a9a683Chris Banespublic final class LayoutInflaterCompat {
344bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    private static final String TAG = "LayoutInflaterCompatHC";
35fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
364bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    private static Field sLayoutInflaterFactory2Field;
374bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    private static boolean sCheckedField;
384bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
394bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    @SuppressWarnings("deprecation")
404bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    static class Factory2Wrapper implements LayoutInflater.Factory2 {
414bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        final LayoutInflaterFactory mDelegateFactory;
424bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
434bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        Factory2Wrapper(LayoutInflaterFactory delegateFactory) {
444bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            mDelegateFactory = delegateFactory;
454bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
46fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
47fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        @Override
484bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public View onCreateView(String name, Context context, AttributeSet attrs) {
494bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            return mDelegateFactory.onCreateView(null, name, context, attrs);
50fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        }
5156594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki
5256594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki        @Override
534bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public View onCreateView(View parent, String name, Context context,
544bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                AttributeSet attributeSet) {
554bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            return mDelegateFactory.onCreateView(parent, name, context, attributeSet);
564bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
574bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
58fa79d6fc148a0642f240377b8ce82acfee7bb890Aurimas Liutikas        @Override
594bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        public String toString() {
604bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            return getClass().getName() + "{" + mDelegateFactory + "}";
6156594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki        }
62fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    }
63fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
644bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    /**
654bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * For APIs < 21, there was a framework bug that prevented a LayoutInflater's
664bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * Factory2 from being merged properly if set after a cloneInContext from a LayoutInflater
674bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * that already had a Factory2 registered. We work around that bug here. If we can't we
684bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * log an error.
694bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     */
7004356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton    private static void forceSetFactory2(LayoutInflater inflater, LayoutInflater.Factory2 factory) {
714bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        if (!sCheckedField) {
724bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            try {
734bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                sLayoutInflaterFactory2Field = LayoutInflater.class.getDeclaredField("mFactory2");
744bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                sLayoutInflaterFactory2Field.setAccessible(true);
754bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            } catch (NoSuchFieldException e) {
764bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                Log.e(TAG, "forceSetFactory2 Could not find field 'mFactory2' on class "
774bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                        + LayoutInflater.class.getName()
784bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                        + "; inflation may have unexpected results.", e);
794bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            }
804bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            sCheckedField = true;
814bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        }
824bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas        if (sLayoutInflaterFactory2Field != null) {
834bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            try {
844bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                sLayoutInflaterFactory2Field.set(inflater, factory);
854bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            } catch (IllegalAccessException e) {
864bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                Log.e(TAG, "forceSetFactory2 could not set the Factory2 on LayoutInflater "
874bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas                        + inflater + "; inflation may have unexpected results.", e);
884bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            }
89fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes        }
90fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    }
91fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
92fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    /*
93fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * Hide the constructor.
94fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     */
95fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    private LayoutInflaterCompat() {
96fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    }
97fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
98fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    /**
99fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * Attach a custom Factory interface for creating views while using
100fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * this LayoutInflater. This must not be null, and can only be set once;
101fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * after setting, you can not change the factory.
102fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     *
103fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     * @see LayoutInflater#setFactory(android.view.LayoutInflater.Factory)
1044bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     *
1054bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * @deprecated Use {@link #setFactory2(LayoutInflater, LayoutInflater.Factory2)} instead to set
1064bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * and {@link LayoutInflater#getFactory2()} to get the factory.
107fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes     */
1084bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    @Deprecated
1094bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    public static void setFactory(
1104bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            @NonNull LayoutInflater inflater, @NonNull LayoutInflaterFactory factory) {
11104356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        if (Build.VERSION.SDK_INT >= 21) {
11204356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            inflater.setFactory2(factory != null ? new Factory2Wrapper(factory) : null);
11304356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        } else {
11404356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            final LayoutInflater.Factory2 factory2 = factory != null
11504356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                    ? new Factory2Wrapper(factory) : null;
11604356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            inflater.setFactory2(factory2);
11704356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton
11804356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            final LayoutInflater.Factory f = inflater.getFactory();
11904356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            if (f instanceof LayoutInflater.Factory2) {
12004356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                // The merged factory is now set to getFactory(), but not getFactory2() (pre-v21).
12104356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                // We will now try and force set the merged factory to mFactory2
12204356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
12304356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            } else {
12404356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                // Else, we will force set the original wrapped Factory2
12504356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                forceSetFactory2(inflater, factory2);
12604356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            }
12704356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        }
128fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes    }
129fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes
13056594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki    /**
1314bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * Attach a custom {@link LayoutInflater.Factory2} for creating views while using
1324bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * this {@link LayoutInflater}. This must not be null, and can only be set once;
1334bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * after setting, you can not change the factory.
1344bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     *
1354bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * @see LayoutInflater#setFactory2(android.view.LayoutInflater.Factory2)
1364bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     */
1374bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    public static void setFactory2(
1384bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas            @NonNull LayoutInflater inflater, @NonNull LayoutInflater.Factory2 factory) {
13904356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        inflater.setFactory2(factory);
14004356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton
14104356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        if (Build.VERSION.SDK_INT < 21) {
14204356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            final LayoutInflater.Factory f = inflater.getFactory();
14304356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            if (f instanceof LayoutInflater.Factory2) {
14404356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                // The merged factory is now set to getFactory(), but not getFactory2() (pre-v21).
14504356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                // We will now try and force set the merged factory to mFactory2
14604356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                forceSetFactory2(inflater, (LayoutInflater.Factory2) f);
14704356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            } else {
14804356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                // Else, we will force set the original wrapped Factory2
14904356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton                forceSetFactory2(inflater, factory);
15004356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            }
15104356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        }
1524bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    }
1534bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas
1544bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    /**
15556594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * Return the current {@link LayoutInflaterFactory} (or null). This is
15656594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * called on each element name. If the factory returns a View, add that
15756594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * to the hierarchy. If it returns null, proceed to call onCreateView(name).
15856594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     *
15956594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * @return The {@link LayoutInflaterFactory} associated with the
16056594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * {@link LayoutInflater}. Will be {@code null} if the inflater does not
16156594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * have a {@link LayoutInflaterFactory} but a raw {@link LayoutInflater.Factory}.
16256594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     * @see LayoutInflater#getFactory()
1634bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     *
1644bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * @deprecated Use {@link #setFactory2(LayoutInflater, LayoutInflater.Factory2)} to set and
1654bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas     * {@link LayoutInflater#getFactory2()} to get the factory.
16656594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki     */
1674bf8c3d1aeb944a993c946db770604b55f981341Aurimas Liutikas    @Deprecated
16856594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki    public static LayoutInflaterFactory getFactory(LayoutInflater inflater) {
16904356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        LayoutInflater.Factory factory = inflater.getFactory();
17004356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        if (factory instanceof Factory2Wrapper) {
17104356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton            return ((Factory2Wrapper) factory).mDelegateFactory;
17204356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        }
17304356993793e8870eca6ffca9b408c1b7ff74803Jake Wharton        return null;
17456594b0cf74a9f71c52980044283ddb0e9e6f47cYuichi Araki    }
175fd1eb27a3700de31507de34fd1bcc51830fe876cChris Banes}
176