ShadowAssetManager.java revision 412aae3601b2e679995187e7c9c40f39c486e688
1773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Trivipackage org.robolectric.shadows;
2773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Trivi
3773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport static android.os.Build.VERSION_CODES.KITKAT_WATCH;
4773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport static android.os.Build.VERSION_CODES.LOLLIPOP;
5773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport static org.robolectric.RuntimeEnvironment.castNativePtr;
6773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport static org.robolectric.Shadows.shadowOf;
7773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Trivi
8773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.content.res.AssetFileDescriptor;
9773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.content.res.AssetManager;
10773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.content.res.AssetManager.AssetInputStream;
11773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.content.res.Resources;
12773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.content.res.TypedArray;
13773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.content.res.XmlResourceParser;
14773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.os.Build;
15773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.os.Build.VERSION_CODES;
16773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.os.ParcelFileDescriptor;
17773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport android.util.AttributeSet;
18daf661248ff6ea2e21799e5114c78b7c1d49630eGlenn Kastenimport android.util.SparseArray;
196a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Triviimport android.util.TypedValue;
20f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Triviimport com.google.common.collect.Ordering;
21773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Triviimport java.io.ByteArrayInputStream;
221ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Triviimport java.io.File;
23a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.io.FileInputStream;
24a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.io.FileNotFoundException;
25a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.io.FileOutputStream;
26a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.io.IOException;
27a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.io.InputStream;
28a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.nio.file.Files;
29a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.ArrayList;
30a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.Arrays;
31a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.Collection;
32a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.Collections;
33a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.HashMap;
34a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.List;
35a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.Map;
36a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.Set;
37a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.concurrent.CopyOnWriteArraySet;
38a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.zip.ZipEntry;
39a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport java.util.zip.ZipInputStream;
40a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport javax.annotation.Nonnull;
41a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.RuntimeEnvironment;
42a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.android.XmlResourceParserImpl;
43a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.annotation.HiddenApi;
44a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.annotation.Implementation;
45a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.annotation.Implements;
46a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.annotation.RealObject;
47a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.annotation.Resetter;
48a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.AttrData;
49a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.AttributeResource;
50a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.EmptyStyle;
51a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.FileTypedResource;
52a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.Fs;
53a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.FsFile;
54a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.ResName;
55a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.ResType;
56a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.ResourceIds;
57a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.ResourceTable;
58a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.Style;
59a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.StyleData;
60a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.StyleResolver;
61a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.ThemeStyleSet;
62a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.TypedResource;
63a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.android.ResTable_config;
64a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.res.builder.XmlBlock;
65a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.util.Logger;
66a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.util.ReflectionHelpers;
67a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Triviimport org.robolectric.util.ReflectionHelpers.ClassParameter;
68a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi
69a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi@Implements(AssetManager.class)
70a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivipublic class ShadowAssetManager {
71a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi
72a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  public static final int STYLE_NUM_ENTRIES = 6;
73a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  public static final int STYLE_TYPE = 0;
74a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  public static final int STYLE_DATA = 1;
756a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public static final int STYLE_ASSET_COOKIE = 2;
766a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public static final int STYLE_RESOURCE_ID = 3;
776a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public static final int STYLE_CHANGING_CONFIGURATIONS = 4;
786a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public static final int STYLE_DENSITY = 5;
796a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
806a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public static final Ordering<String> ATTRIBUTE_TYPE_PRECIDENCE =
816a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      Ordering.explicit(
826a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "reference",
836a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "color",
846a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "boolean",
856a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "integer",
866a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "fraction",
876a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "dimension",
886a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "float",
896a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "enum",
906a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "flag",
916a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          "string");
926a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
936a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  boolean strictErrors = false;
946a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
956a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  private static long nextInternalThemeId = 1000;
966a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  private static final Map<Long, NativeTheme> nativeThemes = new HashMap<>();
97a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  private ResourceTable resourceTable;
98f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
99f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  ResTable_config config = new ResTable_config();
100f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  private Set<FsFile> assetDirs = new CopyOnWriteArraySet<>();
101f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
102f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  class NativeTheme {
103f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    private ThemeStyleSet themeStyleSet;
104f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
105f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    public NativeTheme(ThemeStyleSet themeStyleSet) {
106f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      this.themeStyleSet = themeStyleSet;
107f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    }
108f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
109f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    public ShadowAssetManager getShadowAssetManager() {
110f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      return ShadowAssetManager.this;
1116a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
112f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  }
113f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
1146a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  @RealObject
1156a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  AssetManager realObject;
116f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
117f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  private void convertAndFill(AttributeResource attribute, TypedValue outValue, ResTable_config config, boolean resolveRefs) {
118f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    if (attribute.isNull()) {
119f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      outValue.type = TypedValue.TYPE_NULL;
120f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      outValue.data = TypedValue.DATA_NULL_UNDEFINED;
121f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      return;
122f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    } else if (attribute.isEmpty()) {
123f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      outValue.type = TypedValue.TYPE_NULL;
1246a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      outValue.data = TypedValue.DATA_NULL_EMPTY;
1256a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      return;
126f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    }
127f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
128f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    // short-circuit Android caching of loaded resources cuz our string positions don't remain stable...
129f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    outValue.assetCookie = Converter.getNextStringCookie();
130f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    outValue.changingConfigurations = 0;
131f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
132f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    // TODO: Handle resource and style references
133f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    if (attribute.isStyleReference()) {
134f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      return;
135f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    }
136f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
137f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    while (attribute.isResourceReference()) {
138f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      Integer resourceId;
13906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      ResName resName = attribute.getResourceReference();
140f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      if (attribute.getReferenceResId() != null) {
141f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        resourceId = attribute.getReferenceResId();
142f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      } else {
14306a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi        resourceId = resourceTable.getResourceId(resName);
144f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      }
145f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
14606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      if (resourceId == null) {
147f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        throw new Resources.NotFoundException("unknown resource " + resName);
148f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      }
149f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      outValue.type = TypedValue.TYPE_REFERENCE;
150f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      if (!resolveRefs) {
151f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi          // Just return the resourceId if resolveRefs is false.
152f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi          outValue.data = resourceId;
153f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi          return;
154f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      }
155f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
156f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      outValue.resourceId = resourceId;
157f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
158f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      TypedResource dereferencedRef = resourceTable.getValue(resName, config);
15906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      if (dereferencedRef == null) {
160f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        Logger.strict("couldn't resolve %s from %s", resName.getFullyQualifiedName(), attribute);
16106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi        return;
16206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      } else {
16306a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi        if (dereferencedRef.isFile()) {
16406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          outValue.type = TypedValue.TYPE_STRING;
16506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          outValue.data = 0;
16606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          outValue.assetCookie = Converter.getNextStringCookie();
16706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          outValue.string = dereferencedRef.asString();
16806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          return;
16906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi        } else if (dereferencedRef.getData() instanceof String) {
17006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          attribute = new AttributeResource(attribute.resName, dereferencedRef.asString(), resName.packageName);
17106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          if (attribute.isResourceReference()) {
17206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi            continue;
17306a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          }
17406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          if (resolveRefs) {
17506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi            Converter.getConverter(dereferencedRef.getResType()).fillTypedValue(attribute.value, outValue);
17606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi            return;
17706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          }
17806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi        }
17906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      }
18006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      break;
18106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    }
18206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
183f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    if (attribute.isNull()) {
18406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      outValue.type = TypedValue.TYPE_NULL;
18506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      return;
186f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    }
187f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
188f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    TypedResource attrTypeData = resourceTable.getValue(attribute.resName, config);
189f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    if (attrTypeData != null) {
190f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      AttrData attrData = (AttrData) attrTypeData.getData();
191f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      String format = attrData.getFormat();
192f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      String[] types = format.split("\\|");
193f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      Arrays.sort(types, ATTRIBUTE_TYPE_PRECIDENCE);
194f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      for (String type : types) {
195f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        if ("reference".equals(type)) continue; // already handled above
196f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        Converter converter = Converter.getConverterFor(attrData, type);
197f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
1986a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        if (converter != null) {
19906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          if (converter.fillTypedValue(attribute.value, outValue)) {
20006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi            return;
20106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi          }
20206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi        }
20306a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      }
20406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    } else {
20506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      /**
20606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi       * In cases where the runtime framework doesn't know this attribute, e.g: viewportHeight (added in 21) on a
20706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi       * KitKat runtine, then infer the attribute type from the value.
20806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi       *
20906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi       * TODO: When we are able to pass the SDK resources from the build environment then we can remove this
21006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi       * and replace the NullResourceLoader with simple ResourceProvider that only parses attribute type information.
21106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi       */
21206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      ResType resType = ResType.inferFromValue(attribute.value);
21306a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      Converter.getConverter(resType).fillTypedValue(attribute.value, outValue);
21406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    }
21506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  }
21606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
21706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  @Implementation
21806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  public void __constructor__() {
21906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    resourceTable = RuntimeEnvironment.getAppResourceTable();
22006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  }
22106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
22206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  @Implementation
22306a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  public void __constructor__(boolean isSystem) {
22406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    resourceTable = isSystem ? RuntimeEnvironment.getSystemResourceTable() : RuntimeEnvironment.getAppResourceTable();
22506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  }
22606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
22706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  public ResourceTable getResourceTable() {
22806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    return resourceTable;
22906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  }
23006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
23106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  @HiddenApi @Implementation
23206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  public CharSequence getResourceText(int ident) {
23306a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    TypedResource value = getAndResolve(ident, config, true);
23406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    if (value == null) return null;
23506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    return (CharSequence) value.getData();
23606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  }
23706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
23806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  @HiddenApi @Implementation
23906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  public CharSequence getResourceBagText(int ident, int bagEntryId) {
24006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    throw new UnsupportedOperationException(); // todo
24106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  }
2426a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
2436a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  @HiddenApi @Implementation
2446a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public String[] getResourceStringArray(final int id) {
245a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    CharSequence[] resourceTextArray = getResourceTextArray(id);
246a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    if (resourceTextArray == null) return null;
247a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    String[] strings = new String[resourceTextArray.length];
248acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten    for (int i = 0; i < strings.length; i++) {
249c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      strings[i] = resourceTextArray[i].toString();
250acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten    }
251acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten    return strings;
252c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
253c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
254c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  @HiddenApi @Implementation
255c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public int getResourceIdentifier(String name, String defType, String defPackage) {
256c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    Integer resourceId = resourceTable.getResourceId(ResName.qualifyResName(name, defPackage, defType));
257c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    return resourceId == null ? 0 : resourceId;
258c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
2596fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi
2606fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi  @HiddenApi @Implementation
261c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public boolean getResourceValue(int ident, int density, TypedValue outValue, boolean resolveRefs) {
262c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    TypedResource value = getAndResolve(ident, config, resolveRefs);
263c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    if (value == null) return false;
264c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
265c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    getConverter(value).fillTypedValue(value.getData(), outValue);
266c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    return true;
267c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
268c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
269c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  private Converter getConverter(TypedResource value) {
270c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    if (value instanceof FileTypedResource.Image
271c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        || (value instanceof FileTypedResource
272c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi            && ((FileTypedResource) value).getFsFile().getName().endsWith(".xml"))) {
273c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      return new Converter.FromFilePath();
274c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    }
275c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    return Converter.getConverter(value.getResType());
276c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
277c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
278c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  @HiddenApi @Implementation
279c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public CharSequence[] getResourceTextArray(int resId) {
280a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    TypedResource value = getAndResolve(resId, config, true);
281c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    if (value == null) return null;
282c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    List<TypedResource> items = getConverter(value).getItems(value);
283c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    CharSequence[] charSequences = new CharSequence[items.size()];
284c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    for (int i = 0; i < items.size(); i++) {
285c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      TypedResource typedResource = resolve(items.get(i), config, resId);
286c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      charSequences[i] = getConverter(typedResource).asCharSequence(typedResource);
287c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    }
288c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    return charSequences;
289c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
290c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
291c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  @HiddenApi @Implementation(maxSdk = KITKAT_WATCH)
292c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public boolean getThemeValue(int themePtr, int ident, TypedValue outValue, boolean resolveRefs) {
293c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    return getThemeValue((long) themePtr, ident, outValue, resolveRefs);
294c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
295c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
296c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  @HiddenApi @Implementation(minSdk = LOLLIPOP)
297c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public boolean getThemeValue(long themePtr, int ident, TypedValue outValue, boolean resolveRefs) {
298c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    ResName resName = resourceTable.getResName(ident);
299c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
300c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    ThemeStyleSet themeStyleSet = getNativeTheme(themePtr).themeStyleSet;
301c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    AttributeResource attrValue = themeStyleSet.getAttrValue(resName);
302c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    while(attrValue != null && attrValue.isStyleReference()) {
303c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      ResName attrResName = attrValue.getStyleReference();
304c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      if (attrValue.resName.equals(attrResName)) {
305c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi          Logger.info("huh... circular reference for %s?", attrResName.getFullyQualifiedName());
306c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi          return false;
307c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      }
308c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      attrValue = themeStyleSet.getAttrValue(attrResName);
309f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    }
310c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    if (attrValue != null) {
311c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      convertAndFill(attrValue, outValue, config, resolveRefs);
312c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      return true;
313c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    }
314c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    return false;
315c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
316c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
317c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  @HiddenApi @Implementation
318c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public void ensureStringBlocks() {
319c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
320c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
321c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  @Implementation
322c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public final InputStream open(String fileName) throws IOException {
323c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    return findAssetFile(fileName).getInputStream();
324c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
325c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
326c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  @Implementation
327c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public final InputStream open(String fileName, int accessMode) throws IOException {
328c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    return findAssetFile(fileName).getInputStream();
329c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
330c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
331c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  @Implementation
332c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public final AssetFileDescriptor openFd(String fileName) throws IOException {
333c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    File file = new File(findAssetFile(fileName).getPath());
334c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    if (file.getPath().startsWith("jar")) {
335c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      file = getFileFromZip(file);
336c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    }
337c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    ParcelFileDescriptor parcelFileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
338c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    return new AssetFileDescriptor(parcelFileDescriptor, 0, file.length());
339c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
340c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
341c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  private FsFile findAssetFile(String fileName) throws IOException {
342c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    for (FsFile assetDir : getAllAssetsDirectories()) {
3436fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      FsFile assetFile = assetDir.join(fileName);
344c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      if (assetFile.exists()) {
345c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        return assetFile;
3466fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      }
347c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    }
348c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
349acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten    throw new FileNotFoundException("Asset file " + fileName + " not found");
350c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  }
351c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
352c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  /**
3536fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi   * Extract an asset from a zipped up assets provided by the build system, this is required because there is no
3546fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi   * way to get a FileDescriptor from a zip entry. This is a temporary measure for Bazel which can be removed
3556fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi   * once binary resources are supported.
3566fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi   */
3576fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi  private static File getFileFromZip(File file) {
3586fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    File fileFromZip = null;
3596fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    String pathString = file.getPath();
3606fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    String zipFile = pathString.substring(pathString.indexOf(":") + 1, pathString.indexOf("!"));
3616fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    String filePathInsideZip = pathString.split("!")[1].substring(1);
3626fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    byte[] buffer = new byte[1024];
3636fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    try {
3646fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      File outputDir = Files.createTempDirectory("robolectric_assets").toFile();
3656fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile));
3666fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      ZipEntry ze = zis.getNextEntry();
3676fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      while (ze != null) {
3686fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi        String currentFilename = ze.getName();
3696fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi        if (!currentFilename.equals(filePathInsideZip)) {
3706fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi          ze = zis.getNextEntry();
3716fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi          continue;
372c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        }
373c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        fileFromZip = new File(outputDir + File.separator + currentFilename);
374c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        new File(fileFromZip.getParent()).mkdirs();
375c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        FileOutputStream fos = new FileOutputStream(fileFromZip);
376c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        int len;
377c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        while ((len = zis.read(buffer)) > 0) {
378c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi          fos.write(buffer, 0, len);
379c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        }
380c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        fos.close();
381c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi        break;
382c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      }
383c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      zis.closeEntry();
384c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      zis.close();
385c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    } catch (IOException e) {
386c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      throw new RuntimeException(e);
387773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Trivi    }
388a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    return fileFromZip;
3891ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  }
3901ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
3911ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  @Implementation
3926a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public final String[] list(String path) throws IOException {
3938f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    List<String> assetFiles = new ArrayList<>();
3946a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
3956a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    for (FsFile assetsDir : getAllAssetsDirectories()) {
3966a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      FsFile file;
3976a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      if (path.isEmpty()) {
3983a413f1863daa026ed2b9fc9eac01e1341116cdbGlenn Kasten        file = assetsDir;
3991ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      } else {
4001ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi        file = assetsDir.join(path);
4018f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi      }
4021ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
4031ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      if (file.isDirectory()) {
4041ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi        Collections.addAll(assetFiles, file.listFileNames());
4051ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      }
4061ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    }
4071ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    return assetFiles.toArray(new String[assetFiles.size()]);
4081ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  }
4091ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
4101ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  @HiddenApi @Implementation
4111ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  public final InputStream openNonAsset(int cookie, String fileName, int accessMode) throws IOException {
4121ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    final ResName resName = qualifyFromNonAssetFileName(fileName);
4131ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
4141ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    final FileTypedResource typedResource =
4151ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi        (FileTypedResource) resourceTable.getValue(resName, config);
4161ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
4173a413f1863daa026ed2b9fc9eac01e1341116cdbGlenn Kasten    if (typedResource == null) {
4181ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      throw new IOException("Unable to find resource for " + fileName);
4191ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    }
4201ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
4211ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    InputStream stream;
4221ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    if (accessMode == AssetManager.ACCESS_STREAMING) {
4231ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      stream = typedResource.getFsFile().getInputStream();
4241ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    } else {
4251ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      stream = new ByteArrayInputStream(typedResource.getFsFile().getBytes());
4261ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    }
4271ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
4281ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    // BEGIN-INTERNAL
4291ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    if (RuntimeEnvironment.getApiLevel() >= Build.VERSION_CODES.P) {
4301ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      // Camouflage the InputStream as an AssetInputStream so subsequent instanceof checks pass.
4311ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      AssetInputStream ais = ReflectionHelpers.callConstructor(AssetInputStream.class,
4321ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi          ClassParameter.from(AssetManager.class, realObject),
4331ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi          ClassParameter.from(long.class, 0));
4341ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
4353a413f1863daa026ed2b9fc9eac01e1341116cdbGlenn Kasten      ShadowAssetInputStream sais = shadowOf(ais);
4361ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      sais.setDelegate(stream);
4371ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      sais.setNinePatch(fileName.toLowerCase().endsWith(".9.png"));
4381ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      stream = ais;
43906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    }
4408f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    // END-INTERNAL
4413a413f1863daa026ed2b9fc9eac01e1341116cdbGlenn Kasten
4423a413f1863daa026ed2b9fc9eac01e1341116cdbGlenn Kasten    return stream;
4431ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  }
4441ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
4451ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  private ResName qualifyFromNonAssetFileName(String fileName) {
4461ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    // Resources from a jar belong to the "android" namespace, except when they come from "resource_files.zip"
4471ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    // when they are application resources produced by Bazel.
4488f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    if (fileName.startsWith("jar:") && !fileName.contains("resource_files.zip")) {
44906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      // Must remove "jar:" prefix, or else qualifyFromFilePath fails on Windows
4508f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi      return ResName.qualifyFromFilePath("android", fileName.replaceFirst("jar:", ""));
4518f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    } else {
4528f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi      return ResName.qualifyFromFilePath(RuntimeEnvironment.application.getPackageName(), fileName);
4536a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
4546a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
4551ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
4566a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  @HiddenApi @Implementation
4576a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public final AssetFileDescriptor openNonAssetFd(int cookie, String fileName) throws IOException {
4588f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    throw new UnsupportedOperationException();
4598f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi  }
4608f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi
4618f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi  @Implementation
4628f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi  public final XmlResourceParser openXmlResourceParser(int cookie, String fileName) throws IOException {
4636a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    XmlBlock xmlBlock = XmlBlock.create(Fs.fileFromPath(fileName), resourceTable.getPackageName());
4646a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    if (xmlBlock == null) {
4658f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi      throw new Resources.NotFoundException(fileName);
4666a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
4676a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    return getXmlResourceParser(resourceTable, xmlBlock, resourceTable.getPackageName());
4686a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
4696a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
4706a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public XmlResourceParser loadXmlResourceParser(int resId, String type) throws Resources.NotFoundException {
4716a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    ResName resName = getResName(resId);
4728f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    ResName resolvedResName = resolveResName(resName, config);
4738f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    if (resolvedResName == null) {
4748f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi      throw new RuntimeException("couldn't resolve " + resName.getFullyQualifiedName());
4758f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    }
4768f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    resName = resolvedResName;
4776a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
4786a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    XmlBlock block = resourceTable.getXml(resName, config);
4798f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    if (block == null) {
4806a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      throw new Resources.NotFoundException(resName.getFullyQualifiedName());
4816a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
4826a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
4836a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    ResourceTable resourceProvider = ResourceIds.isFrameworkResource(resId) ? RuntimeEnvironment.getSystemResourceTable() : RuntimeEnvironment.getCompileTimeResourceTable();
4846a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
4858f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi    return getXmlResourceParser(resourceProvider, block, resName.packageName);
4868f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi  }
4878f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi
4888f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi  private XmlResourceParser getXmlResourceParser(ResourceTable resourceProvider, XmlBlock block, String packageName) {
4893a413f1863daa026ed2b9fc9eac01e1341116cdbGlenn Kasten    return new XmlResourceParserImpl(block.getDocument(), block.getFilename(), block.getPackageName(),
4908f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi        packageName, resourceProvider);
4913a413f1863daa026ed2b9fc9eac01e1341116cdbGlenn Kasten  }
4928f4f78fd27806e013065e675a7cf056172d9b6dcJean-Michel Trivi
4936a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  @HiddenApi @Implementation
4946a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public int addAssetPath(String path) {
4956a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    assetDirs.add(Fs.newFile(path));
4966a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    return 1;
4976a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
4986a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
4996a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  @HiddenApi @Implementation
5001ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  public boolean isUpToDate() {
5011ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    return true;
5021ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  }
5031ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
5041ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi  @HiddenApi @Implementation
505a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  public void setLocale(String locale) {
506a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  }
507a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi
508a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  @Implementation
509a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  public String[] getLocales() {
510a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    return new String[0]; // todo
511a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  }
512a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi
513a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  @HiddenApi @Implementation(maxSdk = VERSION_CODES.N_MR1)
514a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  public void setConfiguration(int mcc, int mnc, String locale,
515a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi      int orientation, int touchscreen, int density, int keyboard,
516a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi      int keyboardHidden, int navigation, int screenWidth, int screenHeight,
517a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi      int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
518a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi      int screenLayout, int uiMode, int majorVersion) {
519a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    setConfiguration(mcc, mnc, locale,
520a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi        orientation, touchscreen, density, keyboard,
521acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten        keyboardHidden, navigation, screenWidth, screenHeight,
52272a04d8e9e059dad969d166a6a70491fe1e65970Glenn Kasten        smallestScreenWidthDp, screenWidthDp, screenHeightDp,
523773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Trivi        screenLayout, uiMode, 0, majorVersion);
524acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten  }
525acd88797a1d3b8225bab888d29036e245f275be5Glenn Kasten
526c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  @HiddenApi @Implementation(minSdk = VERSION_CODES.O)
527c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi  public void setConfiguration(int mcc, int mnc, String locale,
528a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi      int orientation, int touchscreen, int density, int keyboard,
529a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi      int keyboardHidden, int navigation, int screenWidth, int screenHeight,
530c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp,
531c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi      int screenLayout, int uiMode, int colorMode, int majorVersion) {
532c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    // AssetManager* am = assetManagerForJavaObject(env, clazz);
533c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
534c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    ResTable_config config = new ResTable_config();
535c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
536c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    // Constants duplicated from Java class android.content.res.Configuration.
537a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    final int kScreenLayoutRoundMask = 0x300;
538a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    final int kScreenLayoutRoundShift = 8;
539c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
5401ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    config.mcc = mcc;
54106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    config.mnc = mnc;
54206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    config.orientation = orientation;
543c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    config.touchscreen = touchscreen;
544a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    config.density = density;
5456fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    config.keyboard = keyboard;
5463a413f1863daa026ed2b9fc9eac01e1341116cdbGlenn Kasten    config.inputFlags = keyboardHidden;
547c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    config.navigation = navigation;
54806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    config.screenWidth = screenWidth;
54906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    config.screenHeight = screenHeight;
55006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    config.smallestScreenWidthDp = smallestScreenWidthDp;
551c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    config.screenWidthDp = screenWidthDp;
552c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    config.screenHeightDp = screenHeightDp;
553c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    config.screenLayout = screenLayout;
55406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    config.uiMode = uiMode;
555c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    // config.colorMode = colorMode; // todo
556c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    config.sdkVersion = majorVersion;
557c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi    config.minorVersion = 0;
558c116ab2a033ee7dc78cfd458defe38d4528383a8Jean-Michel Trivi
559773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Trivi    // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
560773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Trivi    // in C++. We must extract the round qualifier out of the Java screenLayout and put it
561773e0429cbb9e85b4f1c6eb5a095ccd7b57f5ba4Jean-Michel Trivi    // into screenLayout2.
562a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    config.screenLayout2 =
563a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi            (byte)((screenLayout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
5646fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi
565a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    if (locale != null) {
566a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi      config.setBcp47Locale(locale);
5676fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    }
568a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    // am->setConfiguration(config, locale8);
569a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi
570a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    this.config = config;
5716a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
5721ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi
573a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  @HiddenApi @Implementation
574a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  public int[] getArrayIntResource(int resId) {
575a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    TypedResource value = getAndResolve(resId, config, true);
576a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    if (value == null) return null;
577a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    List<TypedResource> items = getConverter(value).getItems(value);
578a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    int[] ints = new int[items.size()];
579a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    for (int i = 0; i < items.size(); i++) {
580a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi      TypedResource typedResource = resolve(items.get(i), config, resId);
581f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      ints[i] = getConverter(typedResource).asInt(typedResource);
582a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    }
583a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    return ints;
584a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  }
585a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi
5861ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi protected TypedArray getTypedArrayResource(Resources resources, int resId) {
587a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    TypedResource value = getAndResolve(resId, config, true);
5881ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    if (value == null) {
589a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi      return null;
5901ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi    }
591a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    List<TypedResource> items = getConverter(value).getItems(value);
592f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    return getTypedArray(resources, items, resId);
5936a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
594a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi
595a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi  private TypedArray getTypedArray(Resources resources, List<TypedResource> typedResources, int resId) {
596a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    final CharSequence[] stringData = new CharSequence[typedResources.size()];
597a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi    final int totalLen = typedResources.size() * ShadowAssetManager.STYLE_NUM_ENTRIES;
5986fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    final int[] data = new int[totalLen];
59906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
60006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    for (int i = 0; i < typedResources.size(); i++) {
601f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      final int offset = i * ShadowAssetManager.STYLE_NUM_ENTRIES;
602f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      TypedResource typedResource = typedResources.get(i);
6036fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi
6046fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      // Classify the item.
6056fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      int type = getResourceType(typedResource);
606f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      if (type == -1) {
607f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        // This type is unsupported; leave empty.
608f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        continue;
609f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      }
610f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
611f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      final TypedValue typedValue = new TypedValue();
612f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
6136fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      if (type == TypedValue.TYPE_REFERENCE) {
6146fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi        final String reference = typedResource.asString();
6156fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi        ResName refResName = AttributeResource.getResourceReference(reference,
6166fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi            typedResource.getXmlContext().getPackageName(), null);
617f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        typedValue.resourceId = resourceTable.getResourceId(refResName);
618f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        typedValue.data = typedValue.resourceId;
619f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        typedResource = resolve(typedResource, config, typedValue.resourceId);
620f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
62106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi        if (typedResource != null) {
6226fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi          // Reclassify to a non-reference type.
623a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi          type = getResourceType(typedResource);
624a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi          if (type == TypedValue.TYPE_ATTRIBUTE) {
625a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi            type = TypedValue.TYPE_REFERENCE;
626a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi          } else if (type == -1) {
627a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi            // This type is unsupported; leave empty.
628a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi            continue;
629a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi          }
630a852e9eca77c64fcba11eb590bec7a11aca5fe16Jean-Michel Trivi        }
63148913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi      }
63248913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi
6336a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      if (type == TypedValue.TYPE_ATTRIBUTE) {
6346a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        final String reference = typedResource.asString();
6356a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        final ResName attrResName = AttributeResource.getStyleReference(reference,
6366a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi            typedResource.getXmlContext().getPackageName(), "attr");
6376a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        typedValue.data = resourceTable.getResourceId(attrResName);
6386a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      }
6396a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
6406a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      if (typedResource != null && type != TypedValue.TYPE_NULL && type != TypedValue.TYPE_ATTRIBUTE) {
641f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        getConverter(typedResource).fillTypedValue(typedResource.getData(), typedValue);
64206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      }
64306a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
64406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      data[offset + ShadowAssetManager.STYLE_TYPE] = type;
64506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      data[offset + ShadowAssetManager.STYLE_RESOURCE_ID] = typedValue.resourceId;
64606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      data[offset + ShadowAssetManager.STYLE_DATA] = typedValue.data;
64706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      data[offset + ShadowAssetManager.STYLE_ASSET_COOKIE] = typedValue.assetCookie;
6486a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      data[offset + ShadowAssetManager.STYLE_CHANGING_CONFIGURATIONS] = typedValue.changingConfigurations;
6496a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      data[offset + ShadowAssetManager.STYLE_DENSITY] = typedValue.density;
6506a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      stringData[i] = typedResource == null ? null : typedResource.asString();
6516a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
652f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
653f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    int[] indices = new int[typedResources.size() + 1]; /* keep zeroed out */
654f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    return ShadowTypedArray.create(resources, null, data, indices, typedResources.size(), stringData);
655f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  }
656f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
65706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  private int getResourceType(TypedResource typedResource) {
6586fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    if (typedResource == null) {
659f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      return -1;
660f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    }
6616fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    final ResType resType = typedResource.getResType();
6626a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    int type;
6636a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    if (typedResource.getData() == null || resType == ResType.NULL) {
6646a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      type = TypedValue.TYPE_NULL;
6656a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    } else if (typedResource.isReference()) {
6666a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      type = TypedValue.TYPE_REFERENCE;
66706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    } else if (resType == ResType.STYLE) {
66806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      type = TypedValue.TYPE_ATTRIBUTE;
66906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    } else if (resType == ResType.CHAR_SEQUENCE || resType == ResType.DRAWABLE) {
67006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      type = TypedValue.TYPE_STRING;
67106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    } else if (resType == ResType.INTEGER) {
6726a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      type = TypedValue.TYPE_INT_DEC;
6736a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    } else if (resType == ResType.FLOAT || resType == ResType.FRACTION) {
6746a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      type = TypedValue.TYPE_FLOAT;
6756a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    } else if (resType == ResType.BOOLEAN) {
6766a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      type = TypedValue.TYPE_INT_BOOLEAN;
67772a04d8e9e059dad969d166a6a70491fe1e65970Glenn Kasten    } else if (resType == ResType.DIMEN) {
67872a04d8e9e059dad969d166a6a70491fe1e65970Glenn Kasten      type = TypedValue.TYPE_DIMENSION;
67906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    } else if (resType == ResType.COLOR) {
68048913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi      type = TypedValue.TYPE_INT_COLOR_ARGB8;
68148913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi    } else if (resType == ResType.TYPED_ARRAY || resType == ResType.CHAR_SEQUENCE_ARRAY) {
6821ae30e37f39fcfe7937a707b789e49a7d68112baJean-Michel Trivi      type = TypedValue.TYPE_REFERENCE;
68348913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi    } else {
68448913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi      type = -1;
68548913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi    }
686f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    return type;
68748913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi  }
68848913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi
68948913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi  @HiddenApi @Implementation
690f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  public Number createTheme() {
69148913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi    synchronized (nativeThemes) {
69248913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi      long nativePtr = nextInternalThemeId++;
69348913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi      nativeThemes.put(nativePtr, new NativeTheme(new ThemeStyleSet()));
694f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      return castNativePtr(nativePtr);
69548913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi    }
69648913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi  }
69706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
69848913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi  private static NativeTheme getNativeTheme(Resources.Theme theme) {
69948913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi    return getNativeTheme(shadowOf(theme).getNativePtr());
70048913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi  }
7016fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi
7026fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi  private static NativeTheme getNativeTheme(long themePtr) {
7036fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    NativeTheme nativeTheme;
704f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    synchronized (nativeThemes) {
7056fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      nativeTheme = nativeThemes.get(themePtr);
7066fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    }
7076fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    if (nativeTheme == null) {
70806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi      throw new RuntimeException("no theme " + themePtr + " found in AssetManager");
70906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    }
71006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    return nativeTheme;
71106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  }
71206a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
71306a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  @HiddenApi @Implementation(maxSdk = KITKAT_WATCH)
71406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  public void releaseTheme(int themePtr) {
71506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    releaseTheme((long) themePtr);
71606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  }
71706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
71806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  @HiddenApi @Implementation(minSdk = LOLLIPOP)
71906a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  public void releaseTheme(long themePtr) {
72006a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    synchronized (nativeThemes) {
7216fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi      nativeThemes.remove(themePtr);
7226fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    }
7236fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi  }
72406a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi
72506a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  @HiddenApi @Implementation(maxSdk = KITKAT_WATCH)
72606a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  public static void applyThemeStyle(int themePtr, int styleRes, boolean force) {
72706a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi    applyThemeStyle((long) themePtr, styleRes, force);
72806a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  }
7296fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi
7306fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi  @HiddenApi @Implementation(minSdk = LOLLIPOP)
73106a1b91fb42d3ecc9da725e673b56ca849b9b9a4Jean-Michel Trivi  public static void applyThemeStyle(long themePtr, int styleRes, boolean force) {
7326fff2c605cdc46a10037e011d8fb47702ae70c37Jean-Michel Trivi    NativeTheme nativeTheme = getNativeTheme(themePtr);
73348913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi    Style style = nativeTheme.getShadowAssetManager().resolveStyle(styleRes, null);
73448913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi    nativeTheme.themeStyleSet.apply(style, force);
73548913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi}
73648913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi
73748913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi  @HiddenApi @Implementation(maxSdk = KITKAT_WATCH)
73848913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi  public static void copyTheme(int destPtr, int sourcePtr) {
73948913d4519d5112319c4277d4966435fec2f551cJean-Michel Trivi    copyTheme((long) destPtr, (long) sourcePtr);
7406a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
7416a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
7426a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  @HiddenApi @Implementation(minSdk = LOLLIPOP)
7436a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  public static void copyTheme(long destPtr, long sourcePtr) {
7446a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    NativeTheme destNativeTheme = getNativeTheme(destPtr);
7456a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    NativeTheme sourceNativeTheme = getNativeTheme(sourcePtr);
7466a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    destNativeTheme.themeStyleSet = sourceNativeTheme.themeStyleSet.copy();
7476a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
748f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
749f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  /////////////////////////
7506a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
7516a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  Style resolveStyle(int resId, Style themeStyleSet) {
752f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    return resolveStyle(getResName(resId), themeStyleSet);
7536a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
7546a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
755f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  private Style resolveStyle(@Nonnull ResName themeStyleName, Style themeStyleSet) {
756f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    TypedResource themeStyleResource = resourceTable.getValue(themeStyleName, config);
757f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    if (themeStyleResource == null) return null;
7586a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    StyleData themeStyleData = (StyleData) themeStyleResource.getData();
7596a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    if (themeStyleSet == null) {
760f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      themeStyleSet = new ThemeStyleSet();
7616a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
7626a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    return new StyleResolver(resourceTable, shadowOf(AssetManager.getSystem()).getResourceTable(),
7636a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        themeStyleData, themeStyleSet, themeStyleName, config);
7646a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
7656a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
7666a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  private TypedResource getAndResolve(int resId, ResTable_config config, boolean resolveRefs) {
7676a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    TypedResource value = resourceTable.getValue(resId, config);
7686a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    if (resolveRefs) {
7696a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      value = resolve(value, config, resId);
7706a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
7716a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    return value;
7726a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
7736a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
7746a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  TypedResource resolve(TypedResource value, ResTable_config config, int resId) {
7756a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    return resolveResourceValue(value, config, resId);
776f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  }
777f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
778f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  public ResName resolveResName(ResName resName, ResTable_config config) {
779f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    TypedResource value = resourceTable.getValue(resName, config);
780f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    return resolveResource(value, config, resName);
781f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  }
782f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
783f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  // todo: DRY up #resolveResource vs #resolveResourceValue
784f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  private ResName resolveResource(TypedResource value, ResTable_config config, ResName resName) {
785f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    while (value != null && value.isReference()) {
786f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      String s = value.asString();
787f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      if (AttributeResource.isNull(s) || AttributeResource.isEmpty(s)) {
788f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        value = null;
789f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      } else {
790f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        String refStr = s.substring(1).replace("+", "");
791f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        resName = ResName.qualifyResName(refStr, resName);
792f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        value = resourceTable.getValue(resName, config);
7936a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      }
7946a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
7956a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
7966a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    return resName;
7976a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  }
7986a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
7996a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi  private TypedResource resolveResourceValue(TypedResource value, ResTable_config config, ResName resName) {
8006a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    while (value != null && value.isReference()) {
8016a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      String s = value.asString();
8026a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      if (AttributeResource.isNull(s) || AttributeResource.isEmpty(s)) {
803dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi        value = null;
804dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi      } else {
805dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi        String refStr = s.substring(1).replace("+", "");
806dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi        resName = ResName.qualifyResName(refStr, resName);
807dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi        value = resourceTable.getValue(resName, config);
808dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi      }
809dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi    }
810dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi
811dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi    return value;
812dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi  }
813dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi
814f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi  public TypedResource resolveResourceValue(TypedResource value, ResTable_config config, int resId) {
815dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi    ResName resName = getResName(resId);
816dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi    return resolveResourceValue(value, config, resName);
817dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi  }
818dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi
819dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi  private TypedValue buildTypedValue(AttributeSet set, int resId, int defStyleAttr, Style themeStyleSet, int defStyleRes) {
820dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi    /*
821dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi     * When determining the final value of a particular attribute, there are four inputs that come into play:
822dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi     *
823dc181a4a041fe4be7c91b92646b236b6d652f4a3Jean-Michel Trivi     * 1. Any attribute values in the given AttributeSet.
8246a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi     * 2. The style resource specified in the AttributeSet (named "style").
8256a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi     * 3. The default style specified by defStyleAttr and defStyleRes
8266a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi     * 4. The base values in this theme.
8276a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi     */
8286a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    Style defStyleFromAttr = null;
8296a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    Style defStyleFromRes = null;
830f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi    Style styleAttrStyle = null;
831f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi
8326a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    if (defStyleAttr != 0) {
8336a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      // Load the theme attribute for the default style attributes. E.g., attr/buttonStyle
834f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi      ResName defStyleName = getResName(defStyleAttr);
8356a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
8366a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      // Load the style for the default style attribute. E.g. "@style/Widget.Robolectric.Button";
8376a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      AttributeResource defStyleAttribute = themeStyleSet.getAttrValue(defStyleName);
8386a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      if (defStyleAttribute != null) {
8396a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        while (defStyleAttribute.isStyleReference()) {
8406a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          AttributeResource other = themeStyleSet.getAttrValue(defStyleAttribute.getStyleReference());
8416a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          if (other == null) {
8426a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi            throw new RuntimeException("couldn't dereference " + defStyleAttribute);
8436a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          }
8446a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          defStyleAttribute = other;
8456a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        }
8466a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
8476a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        if (defStyleAttribute.isResourceReference()) {
8486a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          ResName defStyleResName = defStyleAttribute.getResourceReference();
8496a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi          defStyleFromAttr = resolveStyle(defStyleResName, themeStyleSet);
8506a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        }
8516a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      }
8526a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
8536a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
854b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi    if (set != null && set.getStyleAttribute() != 0) {
855b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi      ResName styleAttributeResName = getResName(set.getStyleAttribute());
856b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi      while (styleAttributeResName.type.equals("attr")) {
857b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi        AttributeResource attrValue = themeStyleSet.getAttrValue(styleAttributeResName);
858f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        if (attrValue == null) {
859b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi          throw new RuntimeException(
860b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi                  "no value for " + styleAttributeResName.getFullyQualifiedName()
861b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi                      + " in " + themeStyleSet);
862f8acf4b469cdc9d2fe08fb7f6ca007365efc8bc1Jean-Michel Trivi        }
8636a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi        if (attrValue.isResourceReference()) {
864b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi          styleAttributeResName = attrValue.getResourceReference();
865b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi        } else if (attrValue.isStyleReference()) {
866b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi          styleAttributeResName = attrValue.getStyleReference();
867b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi        }
868b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi      }
8696a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi      styleAttrStyle = resolveStyle(styleAttributeResName, themeStyleSet);
8706a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    }
8716a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi
8726a7bf7733e955d4d89204627c34fb357d542a9ecJean-Michel Trivi    if (defStyleRes != 0) {
873b6e3c1093dfbdec29e620e85d0d81cc39395a45fJean-Michel Trivi      ResName resName = getResName(defStyleRes);
874      if (resName.type.equals("attr")) {
875        AttributeResource attributeValue = findAttributeValue(defStyleRes, set, styleAttrStyle, defStyleFromAttr, defStyleFromAttr, themeStyleSet);
876        if (attributeValue != null) {
877          if (attributeValue.isStyleReference()) {
878            resName = themeStyleSet.getAttrValue(attributeValue.getStyleReference()).getResourceReference();
879          } else if (attributeValue.isResourceReference()) {
880            resName = attributeValue.getResourceReference();
881          }
882        }
883      }
884      defStyleFromRes = resolveStyle(resName, themeStyleSet);
885    }
886
887    AttributeResource attribute = findAttributeValue(resId, set, styleAttrStyle, defStyleFromAttr, defStyleFromRes, themeStyleSet);
888    while (attribute != null && attribute.isStyleReference()) {
889      ResName otherAttrName = attribute.getStyleReference();
890      if (attribute.resName.equals(otherAttrName)) {
891        Logger.info("huh... circular reference for %s?", attribute.resName.getFullyQualifiedName());
892        return null;
893      }
894      ResName resName = resourceTable.getResName(resId);
895
896      AttributeResource otherAttr = themeStyleSet.getAttrValue(otherAttrName);
897      if (otherAttr == null) {
898        strictError("no such attr %s in %s while resolving value for %s", attribute.value, themeStyleSet, resName.getFullyQualifiedName());
899        attribute = null;
900      } else {
901        attribute = new AttributeResource(resName, otherAttr.value, otherAttr.contextPackageName);
902      }
903    }
904
905    if (attribute == null || attribute.isNull()) {
906      return null;
907    } else {
908      TypedValue typedValue = new TypedValue();
909      convertAndFill(attribute, typedValue, config, true);
910      return typedValue;
911    }
912  }
913
914  private void strictError(String message, Object... args) {
915    if (strictErrors) {
916      throw new RuntimeException(String.format(message, args));
917    } else {
918      Logger.strict(message, args);
919    }
920  }
921
922  TypedArray attrsToTypedArray(Resources resources, AttributeSet set, int[] attrs, int defStyleAttr, long nativeTheme, int defStyleRes) {
923    CharSequence[] stringData = new CharSequence[attrs.length];
924    int[] data = new int[attrs.length * ShadowAssetManager.STYLE_NUM_ENTRIES];
925    int[] indices = new int[attrs.length + 1];
926    int nextIndex = 0;
927
928    Style themeStyleSet = nativeTheme == 0
929        ? new EmptyStyle()
930        : getNativeTheme(nativeTheme).themeStyleSet;
931
932    for (int i = 0; i < attrs.length; i++) {
933      int offset = i * ShadowAssetManager.STYLE_NUM_ENTRIES;
934
935      TypedValue typedValue = buildTypedValue(set, attrs[i], defStyleAttr, themeStyleSet, defStyleRes);
936      if (typedValue != null) {
937        //noinspection PointlessArithmeticExpression
938        data[offset + ShadowAssetManager.STYLE_TYPE] = typedValue.type;
939        data[offset + ShadowAssetManager.STYLE_DATA] = typedValue.type == TypedValue.TYPE_STRING ? i : typedValue.data;
940        data[offset + ShadowAssetManager.STYLE_ASSET_COOKIE] = typedValue.assetCookie;
941        data[offset + ShadowAssetManager.STYLE_RESOURCE_ID] = typedValue.resourceId;
942        data[offset + ShadowAssetManager.STYLE_CHANGING_CONFIGURATIONS] = typedValue.changingConfigurations;
943        data[offset + ShadowAssetManager.STYLE_DENSITY] = typedValue.density;
944        stringData[i] = typedValue.string;
945
946        indices[nextIndex + 1] = i;
947        nextIndex++;
948      }
949    }
950
951    indices[0] = nextIndex;
952
953    TypedArray typedArray = ShadowTypedArray.create(resources, attrs, data, indices, nextIndex, stringData);
954    if (set != null) {
955      shadowOf(typedArray).positionDescription = set.getPositionDescription();
956    }
957    return typedArray;
958  }
959
960  private AttributeResource findAttributeValue(int resId, AttributeSet attributeSet, Style styleAttrStyle, Style defStyleFromAttr, Style defStyleFromRes, @Nonnull Style themeStyleSet) {
961    if (attributeSet != null) {
962      for (int i = 0; i < attributeSet.getAttributeCount(); i++) {
963        if (attributeSet.getAttributeNameResource(i) == resId && attributeSet.getAttributeValue(i) != null) {
964          String defaultPackageName = ResourceIds.isFrameworkResource(resId) ? "android" : RuntimeEnvironment.application.getPackageName();
965          ResName resName = ResName.qualifyResName(attributeSet.getAttributeName(i), defaultPackageName, "attr");
966          Integer referenceResId = null;
967          if (AttributeResource.isResourceReference(attributeSet.getAttributeValue(i))) {
968            referenceResId = attributeSet.getAttributeResourceValue(i, -1);
969          }
970          return new AttributeResource(resName, attributeSet.getAttributeValue(i), "fixme!!!", referenceResId);
971        }
972      }
973    }
974
975    ResName attrName = resourceTable.getResName(resId);
976    if (attrName == null) return null;
977
978    if (styleAttrStyle != null) {
979      AttributeResource attribute = styleAttrStyle.getAttrValue(attrName);
980      if (attribute != null) {
981        return attribute;
982      }
983    }
984
985    // else if attr in defStyleFromAttr, use its value
986    if (defStyleFromAttr != null) {
987      AttributeResource attribute = defStyleFromAttr.getAttrValue(attrName);
988      if (attribute != null) {
989        return attribute;
990      }
991    }
992
993    if (defStyleFromRes != null) {
994      AttributeResource attribute = defStyleFromRes.getAttrValue(attrName);
995      if (attribute != null) {
996        return attribute;
997      }
998    }
999
1000    // else if attr in theme, use its value
1001    return themeStyleSet.getAttrValue(attrName);
1002  }
1003
1004  Collection<FsFile> getAllAssetsDirectories() {
1005    return assetDirs;
1006  }
1007
1008  @Nonnull private ResName getResName(int id) {
1009    ResName resName = resourceTable.getResName(id);
1010    if (resName == null) {
1011      throw new Resources.NotFoundException("Unable to find resource ID #0x" + Integer.toHexString(id)
1012          + " in packages " + resourceTable);
1013    }
1014    return resName;
1015  }
1016
1017  @Implementation
1018  public String getResourceName(int resid) {
1019    return getResName(resid).getFullyQualifiedName();
1020  }
1021
1022  @Implementation
1023  public String getResourcePackageName(int resid) {
1024    return getResName(resid).packageName;
1025  }
1026
1027  @Implementation
1028  public String getResourceTypeName(int resid) {
1029    return getResName(resid).type;
1030  }
1031
1032  @Implementation
1033  public String getResourceEntryName(int resid) {
1034   return getResName(resid).name;
1035  }
1036
1037  @Implementation
1038  public final SparseArray<String> getAssignedPackageIdentifiers() {
1039    return new SparseArray<>();
1040  }
1041
1042  @Resetter
1043  public static void reset() {
1044    ReflectionHelpers.setStaticField(AssetManager.class, "sSystem", null);
1045  }
1046}
1047