1fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski/*
2fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * Copyright (C) 2016 The Android Open Source Project
3fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski *
4fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * use this file except in compliance with the License. You may obtain a copy of
6fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * the License at
7fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski *
8fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * http://www.apache.org/licenses/LICENSE-2.0
9fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski *
10fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * Unless required by applicable law or agreed to in writing, software
11fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * License for the specific language governing permissions and limitations under
14fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * the License.
15fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski */
16fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskipackage android.content.res;
17fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
18ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viveretteimport org.xmlpull.v1.XmlPullParser;
19ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viveretteimport org.xmlpull.v1.XmlPullParserException;
20ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette
21fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.animation.Animator;
22fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.animation.StateListAnimator;
23fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.annotation.AnyRes;
24fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.annotation.AttrRes;
25fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.annotation.NonNull;
26fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.annotation.Nullable;
27fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.annotation.PluralsRes;
28fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.annotation.RawRes;
29fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.annotation.StyleRes;
30fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.annotation.StyleableRes;
31fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.content.pm.ActivityInfo;
32ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viveretteimport android.content.pm.ActivityInfo.Config;
33fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.content.res.Resources.NotFoundException;
34fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.graphics.drawable.ColorDrawable;
35fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.graphics.drawable.Drawable;
36fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.icu.text.PluralRules;
37fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.os.Build;
3823cbe85610f780134cc77dd4a54732a22ed6e86eYohei Yukawaimport android.os.LocaleList;
39fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.os.Trace;
40fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.util.AttributeSet;
41fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.util.DisplayMetrics;
42fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.util.Log;
43fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.util.LongSparseArray;
44fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.util.Slog;
45fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.util.TypedValue;
46fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport android.util.Xml;
474ece3d6bb18a609afcd0e82f0340b7d36ba24eeaAdam Lesinskiimport android.view.Display;
484ece3d6bb18a609afcd0e82f0340b7d36ba24eeaAdam Lesinskiimport android.view.DisplayAdjustments;
49fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
50fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport java.io.InputStream;
51fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport java.util.Arrays;
52fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskiimport java.util.Locale;
53fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
54fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski/**
55082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski * The implementation of Resource access. This class contains the AssetManager and all caches
56082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski * associated with it.
57082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski *
58082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski * {@link Resources} is just a thing wrapper around this class. When a configuration change
59082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski * occurs, clients can retain the same {@link Resources} reference because the underlying
60082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski * {@link ResourcesImpl} object will be updated or re-created.
61082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski *
62fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski * @hide
63fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski */
64fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinskipublic class ResourcesImpl {
65fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    static final String TAG = "Resources";
66fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
67fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final boolean DEBUG_LOAD = false;
68fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final boolean DEBUG_CONFIG = false;
69fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final boolean TRACE_FOR_PRELOAD = false;
70fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final boolean TRACE_FOR_MISS_PRELOAD = false;
71fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
72ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette    private static final int LAYOUT_DIR_CONFIG = ActivityInfo.activityInfoConfigJavaToNative(
73fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            ActivityInfo.CONFIG_LAYOUT_DIRECTION);
74fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
75fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final int ID_OTHER = 0x01000004;
76fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
77fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final Object sSync = new Object();
78fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
79fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static boolean sPreloaded;
80fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private boolean mPreloading;
81fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
82fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    // Information about preloaded resources.  Note that they are not
83fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    // protected by a lock, because while preloading in zygote we are all
84fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    // single-threaded, and after that these are immutable.
85fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables;
86fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables
87fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            = new LongSparseArray<>();
88fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final LongSparseArray<android.content.res.ConstantState<ComplexColor>>
89fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            sPreloadedComplexColors = new LongSparseArray<>();
90fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
91fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /** Lock object used to protect access to caches and configuration. */
92fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final Object mAccessLock = new Object();
93fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
94fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    // These are protected by mAccessLock.
95fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final Configuration mTmpConfig = new Configuration();
96fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final DrawableCache mDrawableCache = new DrawableCache();
97fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final DrawableCache mColorDrawableCache = new DrawableCache();
98fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final ConfigurationBoundResourceCache<ComplexColor> mComplexColorCache =
99fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            new ConfigurationBoundResourceCache<>();
100fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final ConfigurationBoundResourceCache<Animator> mAnimatorCache =
101fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            new ConfigurationBoundResourceCache<>();
102fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache =
103fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            new ConfigurationBoundResourceCache<>();
104fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
105fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /** Size of the cyclical cache used to map XML files to blocks. */
106fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static final int XML_BLOCK_CACHE_SIZE = 4;
107fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
108fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    // Cyclical cache used for recently-accessed XML files.
109fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private int mLastCachedXmlBlockIndex = -1;
110fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final int[] mCachedXmlBlockCookies = new int[XML_BLOCK_CACHE_SIZE];
111fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final String[] mCachedXmlBlockFiles = new String[XML_BLOCK_CACHE_SIZE];
112fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[XML_BLOCK_CACHE_SIZE];
113fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
114fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
115fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    final AssetManager mAssets;
1164ece3d6bb18a609afcd0e82f0340b7d36ba24eeaAdam Lesinski    private final DisplayMetrics mMetrics = new DisplayMetrics();
1178e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski    private final DisplayAdjustments mDisplayAdjustments;
118fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
119fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private PluralRules mPluralRule;
120fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
121fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private final Configuration mConfiguration = new Configuration();
122fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
123fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    static {
124fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        sPreloadedDrawables = new LongSparseArray[2];
125fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        sPreloadedDrawables[0] = new LongSparseArray<>();
126fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        sPreloadedDrawables[1] = new LongSparseArray<>();
127fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
128fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
129fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
130fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Creates a new ResourcesImpl object with CompatibilityInfo.
131fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     *
132fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @param assets Previously created AssetManager.
133fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @param metrics Current display metrics to consider when
134fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     *                selecting/computing resource values.
135fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @param config Desired device configuration to consider when
136fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     *               selecting/computing resource values (optional).
1378e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski     * @param displayAdjustments this resource's Display override and compatibility info.
1388e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski     *                           Must not be null.
139fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
1404ece3d6bb18a609afcd0e82f0340b7d36ba24eeaAdam Lesinski    public ResourcesImpl(@NonNull AssetManager assets, @Nullable DisplayMetrics metrics,
1418e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski            @Nullable Configuration config, @NonNull DisplayAdjustments displayAdjustments) {
142fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        mAssets = assets;
143fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        mMetrics.setToDefaults();
1448e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski        mDisplayAdjustments = displayAdjustments;
1458e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski        updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
146fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        mAssets.ensureStringBlocks();
147fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
148fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1494ece3d6bb18a609afcd0e82f0340b7d36ba24eeaAdam Lesinski    public DisplayAdjustments getDisplayAdjustments() {
1504ece3d6bb18a609afcd0e82f0340b7d36ba24eeaAdam Lesinski        return mDisplayAdjustments;
1514ece3d6bb18a609afcd0e82f0340b7d36ba24eeaAdam Lesinski    }
1524ece3d6bb18a609afcd0e82f0340b7d36ba24eeaAdam Lesinski
153082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski    public AssetManager getAssets() {
154fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return mAssets;
155fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
156fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
157fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    DisplayMetrics getDisplayMetrics() {
158fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (DEBUG_CONFIG) Slog.v(TAG, "Returning DisplayMetrics: " + mMetrics.widthPixels
159fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                + "x" + mMetrics.heightPixels + " " + mMetrics.density);
160fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return mMetrics;
161fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
162fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
163fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    Configuration getConfiguration() {
164fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return mConfiguration;
165fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
166fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
167fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    Configuration[] getSizeConfigurations() {
168fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return mAssets.getSizeConfigurations();
169fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
170fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
171fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    CompatibilityInfo getCompatibilityInfo() {
1728e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski        return mDisplayAdjustments.getCompatibilityInfo();
173fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
174fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
175fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private PluralRules getPluralRule() {
176fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        synchronized (sSync) {
177fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (mPluralRule == null) {
178fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
179fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
180fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return mPluralRule;
181fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
182fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
183fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
184fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
185fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throws NotFoundException {
186fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
187fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (found) {
188fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return;
189fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
190fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
191fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
192fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
193fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    void getValueForDensity(@AnyRes int id, int density, TypedValue outValue,
194082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski            boolean resolveRefs) throws NotFoundException {
195fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        boolean found = mAssets.getResourceValue(id, density, outValue, resolveRefs);
196fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (found) {
197fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return;
198fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
199fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
200fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
201fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
202fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    void getValue(String name, TypedValue outValue, boolean resolveRefs)
203fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throws NotFoundException {
204fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        int id = getIdentifier(name, "string", null);
205fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (id != 0) {
206fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            getValue(id, outValue, resolveRefs);
207fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return;
208fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
209fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException("String resource name " + name);
210fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
211fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
212fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    int getIdentifier(String name, String defType, String defPackage) {
213fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (name == null) {
214fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throw new NullPointerException("name is null");
215fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
216fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        try {
217fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return Integer.parseInt(name);
218fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        } catch (Exception e) {
219fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // Ignore
220fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
221fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return mAssets.getResourceIdentifier(name, defType, defPackage);
222fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
223fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
224fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @NonNull
225fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    String getResourceName(@AnyRes int resid) throws NotFoundException {
226fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        String str = mAssets.getResourceName(resid);
227fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (str != null) return str;
228fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException("Unable to find resource ID #0x"
229fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                + Integer.toHexString(resid));
230fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
231fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
232fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @NonNull
233fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    String getResourcePackageName(@AnyRes int resid) throws NotFoundException {
234fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        String str = mAssets.getResourcePackageName(resid);
235fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (str != null) return str;
236fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException("Unable to find resource ID #0x"
237fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                + Integer.toHexString(resid));
238fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
239fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
240fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @NonNull
241fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    String getResourceTypeName(@AnyRes int resid) throws NotFoundException {
242fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        String str = mAssets.getResourceTypeName(resid);
243fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (str != null) return str;
244fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException("Unable to find resource ID #0x"
245fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                + Integer.toHexString(resid));
246fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
247fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
248fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @NonNull
249fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    String getResourceEntryName(@AnyRes int resid) throws NotFoundException {
250fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        String str = mAssets.getResourceEntryName(resid);
251fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (str != null) return str;
252fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException("Unable to find resource ID #0x"
253fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                + Integer.toHexString(resid));
254fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
255fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
256fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @NonNull
257fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    CharSequence getQuantityText(@PluralsRes int id, int quantity) throws NotFoundException {
258fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        PluralRules rule = getPluralRule();
259fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        CharSequence res = mAssets.getResourceBagText(id,
260fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                attrForQuantityCode(rule.select(quantity)));
261fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (res != null) {
262fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return res;
263fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
264fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        res = mAssets.getResourceBagText(id, ID_OTHER);
265fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (res != null) {
266fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return res;
267fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
268fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
269fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                + " quantity=" + quantity
270fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                + " item=" + rule.select(quantity));
271fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
272fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
273fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static int attrForQuantityCode(String quantityCode) {
274fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        switch (quantityCode) {
275fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            case PluralRules.KEYWORD_ZERO: return 0x01000005;
276fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            case PluralRules.KEYWORD_ONE:  return 0x01000006;
277fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            case PluralRules.KEYWORD_TWO:  return 0x01000007;
278fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            case PluralRules.KEYWORD_FEW:  return 0x01000008;
279fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            case PluralRules.KEYWORD_MANY: return 0x01000009;
280fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            default:                       return ID_OTHER;
281fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
282fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
283fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
284fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @NonNull
285fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    AssetFileDescriptor openRawResourceFd(@RawRes int id, TypedValue tempValue)
286fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throws NotFoundException {
287fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        getValue(id, tempValue, true);
288fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        try {
289fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return mAssets.openNonAssetFd(tempValue.assetCookie, tempValue.string.toString());
290fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        } catch (Exception e) {
291fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throw new NotFoundException("File " + tempValue.string.toString() + " from drawable "
292fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    + "resource ID #0x" + Integer.toHexString(id), e);
293fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
294fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
295fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
296fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @NonNull
297fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    InputStream openRawResource(@RawRes int id, TypedValue value) throws NotFoundException {
298fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        getValue(id, value, true);
299fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        try {
300fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
301fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    AssetManager.ACCESS_STREAMING);
302fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        } catch (Exception e) {
303f135b271bfaa5a23f3af5a15ce59fd4ffb44a6beChristopher Tate            // Note: value.string might be null
304f135b271bfaa5a23f3af5a15ce59fd4ffb44a6beChristopher Tate            NotFoundException rnf = new NotFoundException("File "
305f135b271bfaa5a23f3af5a15ce59fd4ffb44a6beChristopher Tate                    + (value.string == null ? "(null)" : value.string.toString())
306f135b271bfaa5a23f3af5a15ce59fd4ffb44a6beChristopher Tate                    + " from drawable resource ID #0x" + Integer.toHexString(id));
307fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            rnf.initCause(e);
308fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throw rnf;
309fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
310fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
311fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
312fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    ConfigurationBoundResourceCache<Animator> getAnimatorCache() {
313fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return mAnimatorCache;
314fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
315fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
316fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    ConfigurationBoundResourceCache<StateListAnimator> getStateListAnimatorCache() {
317fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return mStateListAnimatorCache;
318fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
319fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
320082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski    public void updateConfiguration(Configuration config, DisplayMetrics metrics,
321082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski                                    CompatibilityInfo compat) {
322991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesImpl#updateConfiguration");
323991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski        try {
324991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski            synchronized (mAccessLock) {
325991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                if (false) {
326991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    Slog.i(TAG, "**** Updating config of " + this + ": old config is "
3278e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski                            + mConfiguration + " old compat is "
3288e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski                            + mDisplayAdjustments.getCompatibilityInfo());
329991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    Slog.i(TAG, "**** Updating config of " + this + ": new config is "
330991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                            + config + " new compat is " + compat);
331991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                }
332991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                if (compat != null) {
3338e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski                    mDisplayAdjustments.setCompatibilityInfo(compat);
334991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                }
335991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                if (metrics != null) {
336991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    mMetrics.setTo(metrics);
337991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                }
338991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                // NOTE: We should re-arrange this code to create a Display
339991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                // with the CompatibilityInfo that is used everywhere we deal
340991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                // with the display in relation to this app, rather than
341991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                // doing the conversion here.  This impl should be okay because
342991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                // we make sure to return a compatible display in the places
343991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                // where there are public APIs to retrieve the display...  but
344b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                // it would be cleaner and more maintainable to just be
345991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                // consistently dealing with a compatible display everywhere in
346991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                // the framework.
3478e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski                mDisplayAdjustments.getCompatibilityInfo().applyToDisplayMetrics(mMetrics);
348991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski
349991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                final @Config int configChanges = calcConfigChanges(config);
350991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski
351b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                // If even after the update there are no Locales set, grab the default locales.
352991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                LocaleList locales = mConfiguration.getLocales();
353991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                if (locales.isEmpty()) {
354b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                    locales = LocaleList.getDefault();
355991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    mConfiguration.setLocales(locales);
356991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                }
357b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski
358b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
359b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                    if (locales.size() > 1) {
360b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                        // The LocaleList has changed. We must query the AssetManager's available
361b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                        // Locales and figure out the best matching Locale in the new LocaleList.
362b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                        String[] availableLocales = mAssets.getNonSystemLocales();
363b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                        if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
364b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                            // No app defined locales, so grab the system locales.
365b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                            availableLocales = mAssets.getLocales();
366b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                            if (LocaleList.isPseudoLocalesOnly(availableLocales)) {
367b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                                availableLocales = null;
368b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                            }
369b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                        }
370b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski
371b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                        if (availableLocales != null) {
372b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                            final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
373b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                                    availableLocales);
374b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                            if (bestLocale != null && bestLocale != locales.get(0)) {
375b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                                mConfiguration.setLocales(new LocaleList(bestLocale, locales));
376b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                            }
377b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                        }
378b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                    }
379b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                }
380b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski
381991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
382991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    mMetrics.densityDpi = mConfiguration.densityDpi;
383991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    mMetrics.density =
384991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                            mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
385991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                }
386991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
387991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski
388991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                final int width, height;
389991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                if (mMetrics.widthPixels >= mMetrics.heightPixels) {
390991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    width = mMetrics.widthPixels;
391991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    height = mMetrics.heightPixels;
392991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                } else {
393991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    //noinspection SuspiciousNameCombination
394991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    width = mMetrics.heightPixels;
395991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    //noinspection SuspiciousNameCombination
396991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    height = mMetrics.widthPixels;
397991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                }
398991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski
399991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                final int keyboardHidden;
400991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                if (mConfiguration.keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
401991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        && mConfiguration.hardKeyboardHidden
402991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        == Configuration.HARDKEYBOARDHIDDEN_YES) {
403991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
404991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                } else {
405991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    keyboardHidden = mConfiguration.keyboardHidden;
406991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                }
407991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski
408991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
409b61e405397200f78b1c652143cba7c751df05a00Adam Lesinski                        adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
410991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        mConfiguration.orientation,
411991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        mConfiguration.touchscreen,
412991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        mConfiguration.densityDpi, mConfiguration.keyboard,
413991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        keyboardHidden, mConfiguration.navigation, width, height,
414991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        mConfiguration.smallestScreenWidthDp,
415991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
416991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        mConfiguration.screenLayout, mConfiguration.uiMode,
417991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                        Build.VERSION.RESOURCES_SDK_INT);
418991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski
419991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                if (DEBUG_CONFIG) {
420991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    Slog.i(TAG, "**** Updating config of " + this + ": final config is "
4218e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski                            + mConfiguration + " final compat is "
4228e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski                            + mDisplayAdjustments.getCompatibilityInfo());
423991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                }
424991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski
425991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                mDrawableCache.onConfigurationChange(configChanges);
426991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                mColorDrawableCache.onConfigurationChange(configChanges);
427991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                mComplexColorCache.onConfigurationChange(configChanges);
428991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                mAnimatorCache.onConfigurationChange(configChanges);
429991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                mStateListAnimatorCache.onConfigurationChange(configChanges);
430991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski
431991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                flushLayoutCache();
432fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
433991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski            synchronized (sSync) {
434991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                if (mPluralRule != null) {
435991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                    mPluralRule = PluralRules.forLocale(mConfiguration.getLocales().get(0));
436991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski                }
437fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
438991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski        } finally {
439991357fe25b3addabf85b871df3f4098fc4b833bAdam Lesinski            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
440fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
441fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
442fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
443fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
444ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette     * Applies the new configuration, returning a bitmask of the changes
445ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette     * between the old and new configurations.
446ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette     *
447ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette     * @param config the new configuration
448ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette     * @return bitmask of config changes
449fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
450ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette    public @Config int calcConfigChanges(@Nullable Configuration config) {
451ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette        if (config == null) {
452ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette            // If there is no configuration, assume all flags have changed.
453ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette            return 0xFFFFFFFF;
454ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette        }
455fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
456ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette        mTmpConfig.setTo(config);
457ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette        int density = config.densityDpi;
458ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette        if (density == Configuration.DENSITY_DPI_UNDEFINED) {
459ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette            density = mMetrics.noncompatDensityDpi;
460ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette        }
461fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
4628e8d23214a71d8813ebd3676b192924c530cb913Adam Lesinski        mDisplayAdjustments.getCompatibilityInfo().applyToConfiguration(density, mTmpConfig);
463ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette
464ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette        if (mTmpConfig.getLocales().isEmpty()) {
465ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette            mTmpConfig.setLocales(LocaleList.getDefault());
466fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
467ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette        return mConfiguration.updateFrom(mTmpConfig);
468fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
469fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
470fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
471fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * {@code Locale.toLanguageTag} will transform the obsolete (and deprecated)
472fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * language codes "in", "ji" and "iw" to "id", "yi" and "he" respectively.
473fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     *
474fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * All released versions of android prior to "L" used the deprecated language
475fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * tags, so we will need to support them for backwards compatibility.
476fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     *
477fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Note that this conversion needs to take place *after* the call to
478fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * {@code toLanguageTag} because that will convert all the deprecated codes to
479fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * the new ones, even if they're set manually.
480fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
481fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private static String adjustLanguageTag(String languageTag) {
482fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final int separator = languageTag.indexOf('-');
483fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final String language;
484fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final String remainder;
485fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
486fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (separator == -1) {
487fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            language = languageTag;
488fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            remainder = "";
489fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        } else {
490fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            language = languageTag.substring(0, separator);
491fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            remainder = languageTag.substring(separator);
492fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
493fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
494fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return Locale.adjustLanguageCode(language) + remainder;
495fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
496fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
497fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
498fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Call this to remove all cached loaded layout resources from the
499fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Resources object.  Only intended for use with performance testing
500fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * tools.
501fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
502fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    public void flushLayoutCache() {
503fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        synchronized (mCachedXmlBlocks) {
504fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Arrays.fill(mCachedXmlBlockCookies, 0);
505fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Arrays.fill(mCachedXmlBlockFiles, null);
506fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
507fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
508fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            for (int i = 0; i < XML_BLOCK_CACHE_SIZE; i++) {
509fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final XmlBlock oldBlock = cachedXmlBlocks[i];
510fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (oldBlock != null) {
511fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    oldBlock.close();
512fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
513fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
514fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Arrays.fill(cachedXmlBlocks, null);
515fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
516fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
517fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
518fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @Nullable
519fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    Drawable loadDrawable(Resources wrapper, TypedValue value, int id, Resources.Theme theme,
520082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski            boolean useCache) throws NotFoundException {
521fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        try {
522fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (TRACE_FOR_PRELOAD) {
523fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                // Log only framework resources
524fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if ((id >>> 24) == 0x1) {
525fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final String name = getResourceName(id);
526fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    if (name != null) {
527fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        Log.d("PreloadDrawable", name);
528fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    }
529fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
530fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
531fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
532fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            final boolean isColorDrawable;
533fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            final DrawableCache caches;
534fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            final long key;
535fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
536fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
537fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                isColorDrawable = true;
538fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                caches = mColorDrawableCache;
539fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                key = value.data;
540fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } else {
541fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                isColorDrawable = false;
542fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                caches = mDrawableCache;
543fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                key = (((long) value.assetCookie) << 32) | value.data;
544fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
545fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
546fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // First, check whether we have a cached version of this drawable
547fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // that was inflated against the specified theme. Skip the cache if
548fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // we're currently preloading or we're not using the cache.
549fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (!mPreloading && useCache) {
550fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final Drawable cachedDrawable = caches.getInstance(key, wrapper, theme);
551fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (cachedDrawable != null) {
552fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    return cachedDrawable;
553fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
554fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
555fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
556fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // Next, check preloaded drawables. Preloaded drawables may contain
557fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // unresolved theme attributes.
558fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            final Drawable.ConstantState cs;
559fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (isColorDrawable) {
560fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                cs = sPreloadedColorDrawables.get(key);
561fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } else {
562fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key);
563fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
564fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
565fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Drawable dr;
566fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (cs != null) {
567fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                dr = cs.newDrawable(wrapper);
568fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } else if (isColorDrawable) {
569fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                dr = new ColorDrawable(value.data);
570fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } else {
571fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                dr = loadDrawableForCookie(wrapper, value, id, null);
572fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
573fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
574fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // Determine if the drawable has unresolved theme attributes. If it
575fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // does, we'll need to apply a theme and store it in a theme-specific
576fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // cache.
577fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            final boolean canApplyTheme = dr != null && dr.canApplyTheme();
578fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (canApplyTheme && theme != null) {
579fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                dr = dr.mutate();
580fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                dr.applyTheme(theme);
581fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                dr.clearMutated();
582fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
583fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
584fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // If we were able to obtain a drawable, store it in the appropriate
585fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // cache: preload, not themed, null theme, or theme-specific. Don't
586fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // pollute the cache with drawables loaded from a foreign density.
587fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (dr != null && useCache) {
588fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                dr.setChangingConfigurations(value.changingConfigurations);
589fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                cacheDrawable(value, isColorDrawable, caches, theme, canApplyTheme, key, dr);
590fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
591fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
592fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return dr;
593fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        } catch (Exception e) {
594fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            String name;
595fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            try {
596fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                name = getResourceName(id);
597fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } catch (NotFoundException e2) {
598fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                name = "(missing name)";
599fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
600fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
601fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // The target drawable might fail to load for any number of
602fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // reasons, but we always want to include the resource name.
603fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // Since the client already expects this method to throw a
604fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // NotFoundException, just throw one of those.
605fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            final NotFoundException nfe = new NotFoundException("Drawable " + name
606fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    + " with resource ID #0x" + Integer.toHexString(id), e);
607fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            nfe.setStackTrace(new StackTraceElement[0]);
608fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throw nfe;
609fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
610fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
611fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
612fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private void cacheDrawable(TypedValue value, boolean isColorDrawable, DrawableCache caches,
613082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski            Resources.Theme theme, boolean usesTheme, long key, Drawable dr) {
614fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final Drawable.ConstantState cs = dr.getConstantState();
615fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (cs == null) {
616fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return;
617fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
618fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
619fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (mPreloading) {
620fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            final int changingConfigs = cs.getChangingConfigurations();
621fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (isColorDrawable) {
622fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (verifyPreloadConfig(changingConfigs, 0, value.resourceId, "drawable")) {
623fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    sPreloadedColorDrawables.put(key, cs);
624fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
625fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } else {
626fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (verifyPreloadConfig(
627fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        changingConfigs, LAYOUT_DIR_CONFIG, value.resourceId, "drawable")) {
628fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    if ((changingConfigs & LAYOUT_DIR_CONFIG) == 0) {
629fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        // If this resource does not vary based on layout direction,
630fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        // we can put it in all of the preload maps.
631fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        sPreloadedDrawables[0].put(key, cs);
632fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        sPreloadedDrawables[1].put(key, cs);
633fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    } else {
634fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        // Otherwise, only in the layout dir we loaded it for.
635fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        sPreloadedDrawables[mConfiguration.getLayoutDirection()].put(key, cs);
636fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    }
637fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
638fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
639fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        } else {
640fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mAccessLock) {
641fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                caches.put(key, theme, cs, usesTheme);
642fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
643fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
644fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
645fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
646ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette    private boolean verifyPreloadConfig(@Config int changingConfigurations,
647ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette            @Config int allowVarying, @AnyRes int resourceId, @Nullable String name) {
648fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        // We allow preloading of resources even if they vary by font scale (which
649fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        // doesn't impact resource selection) or density (which we handle specially by
650fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        // simply turning off all preloading), as well as any other configs specified
651fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        // by the caller.
652fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (((changingConfigurations&~(ActivityInfo.CONFIG_FONT_SCALE |
653fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                ActivityInfo.CONFIG_DENSITY)) & ~allowVarying) != 0) {
654fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            String resName;
655fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            try {
656fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                resName = getResourceName(resourceId);
657fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } catch (NotFoundException e) {
658fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                resName = "?";
659fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
660fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // This should never happen in production, so we should log a
661fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // warning even if we're not debugging.
662fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Log.w(TAG, "Preloaded " + name + " resource #0x"
663fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    + Integer.toHexString(resourceId)
664fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    + " (" + resName + ") that varies with configuration!!");
665fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return false;
666fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
667fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (TRACE_FOR_PRELOAD) {
668fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            String resName;
669fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            try {
670fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                resName = getResourceName(resourceId);
671fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } catch (NotFoundException e) {
672fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                resName = "?";
673fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
674fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Log.w(TAG, "Preloading " + name + " resource #0x"
675fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    + Integer.toHexString(resourceId)
676fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    + " (" + resName + ")");
677fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
678fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return true;
679fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
680fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
681fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
682fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Loads a drawable from XML or resources stream.
683fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
684fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private Drawable loadDrawableForCookie(Resources wrapper, TypedValue value, int id,
685082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski            Resources.Theme theme) {
686fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (value.string == null) {
687fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throw new NotFoundException("Resource \"" + getResourceName(id) + "\" ("
688fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    + Integer.toHexString(id) + ") is not a Drawable (color or path): " + value);
689fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
690fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
691fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final String file = value.string.toString();
692fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
693fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (TRACE_FOR_MISS_PRELOAD) {
694fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // Log only framework resources
695fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if ((id >>> 24) == 0x1) {
696fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final String name = getResourceName(id);
697fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (name != null) {
698fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    Log.d(TAG, "Loading framework drawable #" + Integer.toHexString(id)
699fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                            + ": " + name + " at " + file);
700fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
701fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
702fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
703fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
704fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (DEBUG_LOAD) {
705fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file);
706fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
707fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
708fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final Drawable dr;
709fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
710fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
711fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        try {
712fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (file.endsWith(".xml")) {
713fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final XmlResourceParser rp = loadXmlResourceParser(
714fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        file, id, value.assetCookie, "drawable");
715fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                dr = Drawable.createFromXml(wrapper, rp, theme);
716fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                rp.close();
717fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } else {
718fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final InputStream is = mAssets.openNonAsset(
719fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        value.assetCookie, file, AssetManager.ACCESS_STREAMING);
720fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                dr = Drawable.createFromResourceStream(wrapper, value, is, file, null);
721fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                is.close();
722fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
723fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        } catch (Exception e) {
724fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
725fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            final NotFoundException rnf = new NotFoundException(
726fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id));
727fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            rnf.initCause(e);
728fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throw rnf;
729fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
730fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
731fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
732fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return dr;
733fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
734fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
735fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
736fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Given the value and id, we can get the XML filename as in value.data, based on that, we
737fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * first try to load CSL from the cache. If not found, try to get from the constant state.
738fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Last, parse the XML and generate the CSL.
739fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
740fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private ComplexColor loadComplexColorFromName(Resources wrapper, Resources.Theme theme,
741082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski            TypedValue value, int id) {
742fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final long key = (((long) value.assetCookie) << 32) | value.data;
743fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final ConfigurationBoundResourceCache<ComplexColor> cache = mComplexColorCache;
744fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        ComplexColor complexColor = cache.getInstance(key, wrapper, theme);
745fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (complexColor != null) {
746fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return complexColor;
747fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
748fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
749fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final android.content.res.ConstantState<ComplexColor> factory =
750fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                sPreloadedComplexColors.get(key);
751fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
752fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (factory != null) {
753fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            complexColor = factory.newInstance(wrapper, theme);
754fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
755fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (complexColor == null) {
756fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            complexColor = loadComplexColorForCookie(wrapper, value, id, theme);
757fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
758fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
759fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (complexColor != null) {
7600b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette            complexColor.setBaseChangingConfigurations(value.changingConfigurations);
7610b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette
762fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (mPreloading) {
7630b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette                if (verifyPreloadConfig(complexColor.getChangingConfigurations(),
7640b9295d06750dc6da032a2b2092e2c500c65393fAlan Viverette                        0, value.resourceId, "color")) {
765fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    sPreloadedComplexColors.put(key, complexColor.getConstantState());
766fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
767fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } else {
768fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                cache.put(key, theme, complexColor.getConstantState());
769fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
770fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
771fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return complexColor;
772fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
773fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
774fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @Nullable
775fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    ComplexColor loadComplexColor(Resources wrapper, @NonNull TypedValue value, int id,
776082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski            Resources.Theme theme) {
777fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (TRACE_FOR_PRELOAD) {
778fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // Log only framework resources
779fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if ((id >>> 24) == 0x1) {
780fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final String name = getResourceName(id);
781fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (name != null) android.util.Log.d("loadComplexColor", name);
782fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
783fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
784fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
785fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final long key = (((long) value.assetCookie) << 32) | value.data;
786fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
787fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        // Handle inline color definitions.
788fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
789fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
790fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return getColorStateListFromInt(value, key);
791fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
792fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
793fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final String file = value.string.toString();
794fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
795fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        ComplexColor complexColor;
796fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (file.endsWith(".xml")) {
797fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            try {
798fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                complexColor = loadComplexColorFromName(wrapper, theme, value, id);
799fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } catch (Exception e) {
800fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final NotFoundException rnf = new NotFoundException(
801fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        "File " + file + " from complex color resource ID #0x"
802fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                                + Integer.toHexString(id));
803fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                rnf.initCause(e);
804fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                throw rnf;
805fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
806fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        } else {
807fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throw new NotFoundException(
808fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    "File " + file + " from drawable resource ID #0x"
809fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                            + Integer.toHexString(id) + ": .xml extension required");
810fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
811fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
812fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return complexColor;
813fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
814fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
815fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @Nullable
816fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    ColorStateList loadColorStateList(Resources wrapper, TypedValue value, int id,
817082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski            Resources.Theme theme)
818fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throws NotFoundException {
819fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (TRACE_FOR_PRELOAD) {
820fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // Log only framework resources
821fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if ((id >>> 24) == 0x1) {
822fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final String name = getResourceName(id);
823fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (name != null) android.util.Log.d("PreloadColorStateList", name);
824fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
825fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
826fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
827fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final long key = (((long) value.assetCookie) << 32) | value.data;
828fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
829fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        // Handle inline color definitions.
830fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT
831fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                && value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
832fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return getColorStateListFromInt(value, key);
833fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
834fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
835fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        ComplexColor complexColor = loadComplexColorFromName(wrapper, theme, value, id);
836fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (complexColor != null && complexColor instanceof ColorStateList) {
837fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return (ColorStateList) complexColor;
838fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
839fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
840fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException(
841fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                "Can't find ColorStateList from drawable resource ID #0x"
842fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        + Integer.toHexString(id));
843fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
844fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
845fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @NonNull
846fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private ColorStateList getColorStateListFromInt(@NonNull TypedValue value, long key) {
847fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        ColorStateList csl;
848fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final android.content.res.ConstantState<ComplexColor> factory =
849fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                sPreloadedComplexColors.get(key);
850fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (factory != null) {
851fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return (ColorStateList) factory.newInstance();
852fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
853fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
854fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        csl = ColorStateList.valueOf(value.data);
855fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
856fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (mPreloading) {
857fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId,
858fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    "color")) {
859fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                sPreloadedComplexColors.put(key, csl.getConstantState());
860fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
861fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
862fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
863fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return csl;
864fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
865fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
866fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
867fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Load a ComplexColor based on the XML file content. The result can be a GradientColor or
868fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * ColorStateList. Note that pure color will be wrapped into a ColorStateList.
869fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     *
870fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * We deferred the parser creation to this function b/c we need to differentiate b/t gradient
871fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * and selector tag.
872fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     *
873fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @return a ComplexColor (GradientColor or ColorStateList) based on the XML file content.
874fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
875fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @Nullable
876fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    private ComplexColor loadComplexColorForCookie(Resources wrapper, TypedValue value, int id,
877082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski            Resources.Theme theme) {
878fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (value.string == null) {
879fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throw new UnsupportedOperationException(
880fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    "Can't convert to ComplexColor: type=0x" + value.type);
881fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
882fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
883fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        final String file = value.string.toString();
884fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
885fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (TRACE_FOR_MISS_PRELOAD) {
886fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            // Log only framework resources
887fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if ((id >>> 24) == 0x1) {
888fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final String name = getResourceName(id);
889fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (name != null) {
890fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    Log.d(TAG, "Loading framework ComplexColor #" + Integer.toHexString(id)
891fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                            + ": " + name + " at " + file);
892fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
893fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
894fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
895fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
896fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (DEBUG_LOAD) {
897fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Log.v(TAG, "Loading ComplexColor for cookie " + value.assetCookie + ": " + file);
898fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
899fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
900fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        ComplexColor complexColor = null;
901fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
902fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file);
903fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (file.endsWith(".xml")) {
904fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            try {
905fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final XmlResourceParser parser = loadXmlResourceParser(
906fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        file, id, value.assetCookie, "ComplexColor");
907fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
908fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final AttributeSet attrs = Xml.asAttributeSet(parser);
909fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                int type;
910fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                while ((type = parser.next()) != XmlPullParser.START_TAG
911fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        && type != XmlPullParser.END_DOCUMENT) {
912fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    // Seek parser to start tag.
913fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
914fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (type != XmlPullParser.START_TAG) {
915fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    throw new XmlPullParserException("No start tag found");
916fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
917fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
918fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final String name = parser.getName();
919fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (name.equals("gradient")) {
920fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    complexColor = GradientColor.createFromXmlInner(wrapper, parser, attrs, theme);
921fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                } else if (name.equals("selector")) {
922fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    complexColor = ColorStateList.createFromXmlInner(wrapper, parser, attrs, theme);
923fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
924fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                parser.close();
925fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } catch (Exception e) {
926fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
927fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final NotFoundException rnf = new NotFoundException(
928fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        "File " + file + " from ComplexColor resource ID #0x"
929fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                                + Integer.toHexString(id));
930fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                rnf.initCause(e);
931fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                throw rnf;
932fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
933fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        } else {
934fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
935fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throw new NotFoundException(
936fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    "File " + file + " from drawable resource ID #0x"
937fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                            + Integer.toHexString(id) + ": .xml extension required");
938fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
939fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
940fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
941fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return complexColor;
942fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
943fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
944fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
945fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Loads an XML parser for the specified file.
946fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     *
947fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @param file the path for the XML file to parse
948fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @param id the resource identifier for the file
949fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @param assetCookie the asset cookie for the file
950fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @param type the type of resource (used for logging)
951fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @return a parser for the specified XML file
952fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * @throws NotFoundException if the file could not be loaded
953fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
954fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    @NonNull
955082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski    XmlResourceParser loadXmlResourceParser(@NonNull String file, @AnyRes int id, int assetCookie,
956082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski            @NonNull String type)
957fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            throws NotFoundException {
958fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (id != 0) {
959fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            try {
960fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                synchronized (mCachedXmlBlocks) {
961fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final int[] cachedXmlBlockCookies = mCachedXmlBlockCookies;
962fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final String[] cachedXmlBlockFiles = mCachedXmlBlockFiles;
963fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final XmlBlock[] cachedXmlBlocks = mCachedXmlBlocks;
964fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    // First see if this block is in our cache.
965fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final int num = cachedXmlBlockFiles.length;
966fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    for (int i = 0; i < num; i++) {
967fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        if (cachedXmlBlockCookies[i] == assetCookie && cachedXmlBlockFiles[i] != null
968fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                                && cachedXmlBlockFiles[i].equals(file)) {
969fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                            return cachedXmlBlocks[i].newParser();
970fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        }
971fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    }
972fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
973fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    // Not in the cache, create a new block and put it at
974fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    // the next slot in the cache.
975fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final XmlBlock block = mAssets.openXmlBlockAsset(assetCookie, file);
976fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    if (block != null) {
977fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        final int pos = (mLastCachedXmlBlockIndex + 1) % num;
978fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        mLastCachedXmlBlockIndex = pos;
979fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        final XmlBlock oldBlock = cachedXmlBlocks[pos];
980fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        if (oldBlock != null) {
981fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                            oldBlock.close();
982fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        }
983fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        cachedXmlBlockCookies[pos] = assetCookie;
984fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        cachedXmlBlockFiles[pos] = file;
985fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        cachedXmlBlocks[pos] = block;
986fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        return block.newParser();
987fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    }
988fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
989fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            } catch (Exception e) {
990fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final NotFoundException rnf = new NotFoundException("File " + file
991fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        + " from xml type " + type + " resource ID #0x" + Integer.toHexString(id));
992fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                rnf.initCause(e);
993fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                throw rnf;
994fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
995fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
996fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
997fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        throw new NotFoundException("File " + file + " from xml type " + type + " resource ID #0x"
998fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                + Integer.toHexString(id));
999fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
1000fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1001fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
1002fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Start preloading of resource data using this Resources object.  Only
1003fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * for use by the zygote process for loading common system resources.
1004fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * {@hide}
1005fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
1006fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    public final void startPreloading() {
1007fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        synchronized (sSync) {
1008fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            if (sPreloaded) {
1009fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                throw new IllegalStateException("Resources already preloaded");
1010fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1011fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            sPreloaded = true;
1012fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            mPreloading = true;
1013fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            mConfiguration.densityDpi = DisplayMetrics.DENSITY_DEVICE;
1014fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            updateConfiguration(null, null, null);
1015fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1016fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
1017fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1018fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    /**
1019fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * Called by zygote when it is done preloading resources, to change back
1020fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     * to normal Resources operation.
1021fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski     */
1022fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    void finishPreloading() {
1023fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        if (mPreloading) {
1024fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            mPreloading = false;
1025fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            flushLayoutCache();
1026fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1027fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
1028fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1029fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    LongSparseArray<Drawable.ConstantState> getPreloadedDrawables() {
1030fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return sPreloadedDrawables[0];
1031fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
1032fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1033fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    ThemeImpl newThemeImpl() {
1034fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        return new ThemeImpl();
1035fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
1036fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1037082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski    /**
1038082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski     * Creates a new ThemeImpl which is already set to the given Resources.ThemeKey.
1039082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski     */
1040082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski    ThemeImpl newThemeImpl(Resources.ThemeKey key) {
1041082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski        ThemeImpl impl = new ThemeImpl();
1042082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski        impl.mKey.setTo(key);
1043082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski        impl.rebase();
1044082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski        return impl;
1045082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski    }
1046082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski
1047fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    public class ThemeImpl {
1048fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        /**
1049fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski         * Unique key for the series of styles applied to this theme.
1050fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski         */
1051fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        private final Resources.ThemeKey mKey = new Resources.ThemeKey();
1052fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1053fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        @SuppressWarnings("hiding")
1054fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        private final AssetManager mAssets;
1055fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        private final long mTheme;
1056fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1057fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        /**
1058fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski         * Resource identifier for the theme.
1059fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski         */
1060fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        private int mThemeResId = 0;
1061fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1062fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        /*package*/ ThemeImpl() {
1063fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            mAssets = ResourcesImpl.this.mAssets;
1064fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            mTheme = mAssets.createTheme();
1065fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1066fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1067fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        @Override
1068fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        protected void finalize() throws Throwable {
1069fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            super.finalize();
1070fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            mAssets.releaseTheme(mTheme);
1071fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1072fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1073fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        /*package*/ Resources.ThemeKey getKey() {
1074fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return mKey;
1075fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1076fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1077fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        /*package*/ long getNativeTheme() {
1078fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return mTheme;
1079fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1080fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1081fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        /*package*/ int getAppliedStyleResId() {
1082fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return mThemeResId;
1083fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1084fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1085fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        void applyStyle(int resId, boolean force) {
1086fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mKey) {
1087fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                AssetManager.applyThemeStyle(mTheme, resId, force);
1088fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1089fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                mThemeResId = resId;
1090fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                mKey.append(resId, force);
1091fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1092fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1093fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1094fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        void setTo(ThemeImpl other) {
1095fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mKey) {
1096fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                synchronized (other.mKey) {
1097fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    AssetManager.copyTheme(mTheme, other.mTheme);
1098fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1099fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    mThemeResId = other.mThemeResId;
1100fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    mKey.setTo(other.getKey());
1101fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
1102fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1103fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1104fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1105fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        @NonNull
1106fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        TypedArray obtainStyledAttributes(@NonNull Resources.Theme wrapper,
1107082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski                AttributeSet set,
1108082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski                @StyleableRes int[] attrs,
1109082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski                @AttrRes int defStyleAttr,
1110082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski                @StyleRes int defStyleRes) {
1111fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mKey) {
1112fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final int len = attrs.length;
1113fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
1114fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1115fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                // XXX note that for now we only work with compiled XML files.
1116fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                // To support generic XML files we will need to manually parse
1117fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                // out the attributes from the XML file (applying type information
1118fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                // contained in the resources and such).
1119fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final XmlBlock.Parser parser = (XmlBlock.Parser) set;
1120fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                AssetManager.applyStyle(mTheme, defStyleAttr, defStyleRes,
1121fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        parser != null ? parser.mParseState : 0,
1122fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        attrs, array.mData, array.mIndices);
1123fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                array.mTheme = wrapper;
1124fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                array.mXml = parser;
1125fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1126fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                return array;
1127fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1128fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1129fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1130fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        @NonNull
1131fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        TypedArray resolveAttributes(@NonNull Resources.Theme wrapper,
1132082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski                @NonNull int[] values,
1133082614c6a57a115ee0c5975e3579bf34a178c0f8Adam Lesinski                @NonNull int[] attrs) {
1134fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mKey) {
1135fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final int len = attrs.length;
1136fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                if (values == null || len != values.length) {
1137fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    throw new IllegalArgumentException(
1138fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                            "Base attribute values must the same length as attrs");
1139fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
1140fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1141fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);
1142fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                AssetManager.resolveAttrs(mTheme, 0, 0, values, attrs, array.mData, array.mIndices);
1143fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                array.mTheme = wrapper;
1144fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                array.mXml = null;
1145fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                return array;
1146fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1147fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1148fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1149fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        boolean resolveAttribute(int resid, TypedValue outValue, boolean resolveRefs) {
1150fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mKey) {
1151fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                return mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
1152fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1153fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1154fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1155fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        int[] getAllAttributes() {
1156fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            return mAssets.getStyleAttributes(getAppliedStyleResId());
1157fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1158fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1159ac85f90466dd60d2af8ffc3942d503a0de606726Alan Viverette        @Config int getChangingConfigurations() {
1160fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mKey) {
1161fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final int nativeChangingConfig =
1162fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        AssetManager.getThemeChangingConfigurations(mTheme);
1163fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
1164fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1165fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1166fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1167fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        public void dump(int priority, String tag, String prefix) {
1168fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mKey) {
1169fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                AssetManager.dumpTheme(mTheme, priority, tag, prefix);
1170fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1171fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1172fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1173fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        String[] getTheme() {
1174fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mKey) {
1175fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final int N = mKey.mCount;
1176fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                final String[] themes = new String[N * 2];
1177fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                for (int i = 0, j = N - 1; i < themes.length; i += 2, --j) {
1178fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final int resId = mKey.mResId[j];
1179fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final boolean forced = mKey.mForce[j];
1180fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    try {
1181fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        themes[i] = getResourceName(resId);
1182fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    } catch (NotFoundException e) {
1183fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                        themes[i] = Integer.toHexString(i);
1184fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    }
1185fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    themes[i + 1] = forced ? "forced" : "not forced";
1186fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
1187fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                return themes;
1188fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1189fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1190fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1191fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        /**
1192fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski         * Rebases the theme against the parent Resource object's current
1193fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski         * configuration by re-applying the styles passed to
1194fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski         * {@link #applyStyle(int, boolean)}.
1195fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski         */
1196fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        void rebase() {
1197fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            synchronized (mKey) {
1198fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                AssetManager.clearTheme(mTheme);
1199fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski
1200fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                // Reapply the same styles in the same order.
1201fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                for (int i = 0; i < mKey.mCount; i++) {
1202fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final int resId = mKey.mResId[i];
1203fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    final boolean force = mKey.mForce[i];
1204fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                    AssetManager.applyThemeStyle(mTheme, resId, force);
1205fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski                }
1206fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski            }
1207fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski        }
1208fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski    }
1209fb302ccd8e0610a09691ea5503ff8111dc7a2e41Adam Lesinski}
1210