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