Distinguish unspecified and explicit null values in resources BUG: 17919345 Change-Id: Ic4f04f7dd0f986f58a749b5950d80c1cfdb074ea
diff --git a/api/current.txt b/api/current.txt index fa02c4e..9513d90 100644 --- a/api/current.txt +++ b/api/current.txt
@@ -9260,6 +9260,7 @@ method public int getType(int); method public boolean getValue(int, android.util.TypedValue); method public boolean hasValue(int); + method public boolean hasValueOrEmpty(int); method public int length(); method public android.util.TypedValue peekValue(int); method public void recycle(); @@ -32172,6 +32173,8 @@ field public static final int COMPLEX_UNIT_PX = 0; // 0x0 field public static final int COMPLEX_UNIT_SHIFT = 0; // 0x0 field public static final int COMPLEX_UNIT_SP = 2; // 0x2 + field public static final int DATA_NULL_EMPTY = 1; // 0x1 + field public static final int DATA_NULL_UNDEFINED = 0; // 0x0 field public static final int DENSITY_DEFAULT = 0; // 0x0 field public static final int DENSITY_NONE = 65535; // 0xffff field public static final int TYPE_ATTRIBUTE = 2; // 0x2
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index 73b93c6..02602fb 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java
@@ -807,6 +807,9 @@ /** * Determines whether there is an attribute at <var>index</var>. + * <p> + * <strong>Note:</strong> If the attribute was set to {@code @empty} or + * {@code @undefined}, this method returns {@code false}. * * @param index Index of attribute to retrieve. * @@ -824,6 +827,27 @@ } /** + * Determines whether there is an attribute at <var>index</var>, returning + * {@code true} if the attribute was explicitly set to {@code @empty} and + * {@code false} only if the attribute was undefined. + * + * @param index Index of attribute to retrieve. + * + * @return True if the attribute has a value or is empty, false otherwise. + */ + public boolean hasValueOrEmpty(int index) { + if (mRecycled) { + throw new RuntimeException("Cannot make calls to a recycled instance!"); + } + + index *= AssetManager.STYLE_NUM_ENTRIES; + final int[] data = mData; + final int type = data[index+AssetManager.STYLE_TYPE]; + return type != TypedValue.TYPE_NULL + || data[index+AssetManager.STYLE_DATA] == TypedValue.DATA_NULL_EMPTY; + } + + /** * Retrieve the raw TypedValue for the attribute at <var>index</var> * and return a temporary object holding its data. This object is only * valid until the next call on to {@link TypedArray}.
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java index 931fb81..d42ed03 100644 --- a/core/java/android/util/TypedValue.java +++ b/core/java/android/util/TypedValue.java
@@ -139,6 +139,17 @@ /* ------------------------------------------------------------ */ /** + * {@link #TYPE_NULL} data indicating the value was not specified. + */ + public static final int DATA_NULL_UNDEFINED = 0; + /** + * {@link #TYPE_NULL} data indicating the value was explicitly set to null. + */ + public static final int DATA_NULL_EMPTY = 1; + + /* ------------------------------------------------------------ */ + + /** * If {@link #density} is equal to this value, then the density should be * treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}. */
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 4859ee6..94098c9 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp
@@ -1095,7 +1095,7 @@ // coming from, first XML attributes, then XML style, then default // style, and finally the theme. value.dataType = Res_value::TYPE_NULL; - value.data = 0; + value.data = Res_value::DATA_NULL_UNDEFINED; typeSetFlags = 0; config.density = 0; @@ -1157,6 +1157,7 @@ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { DEBUG_STYLES(ALOGI("-> Setting to @null!")); value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; block = -1; } @@ -1319,7 +1320,7 @@ // coming from, first XML attributes, then XML style, then default // style, and finally the theme. value.dataType = Res_value::TYPE_NULL; - value.data = 0; + value.data = Res_value::DATA_NULL_UNDEFINED; typeSetFlags = 0; config.density = 0; @@ -1403,6 +1404,7 @@ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { DEBUG_STYLES(ALOGI("-> Setting to @null!")); value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; block = kXmlBlock; } @@ -1512,7 +1514,7 @@ // Try to find a value for this attribute... value.dataType = Res_value::TYPE_NULL; - value.data = 0; + value.data = Res_value::DATA_NULL_UNDEFINED; typeSetFlags = 0; config.density = 0; @@ -1548,6 +1550,7 @@ // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; } //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data); @@ -1661,6 +1664,7 @@ // Deal with the special @null value -- it turns back to TYPE_NULL. if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) { value.dataType = Res_value::TYPE_NULL; + value.data = Res_value::DATA_NULL_UNDEFINED; } //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index 6e03b3d..cc3ded5 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml
@@ -1091,16 +1091,16 @@ <!-- Dialog styles --> <style name="AlertDialog.Material" parent="AlertDialog"> - <item name="fullDark">@null</item> - <item name="topDark">@null</item> - <item name="centerDark">@null</item> - <item name="bottomDark">@null</item> - <item name="fullBright">@null</item> - <item name="topBright">@null</item> - <item name="centerBright">@null</item> - <item name="bottomBright">@null</item> - <item name="bottomMedium">@null</item> - <item name="centerMedium">@null</item> + <item name="fullDark">@empty</item> + <item name="topDark">@empty</item> + <item name="centerDark">@empty</item> + <item name="bottomDark">@empty</item> + <item name="fullBright">@empty</item> + <item name="topBright">@empty</item> + <item name="centerBright">@empty</item> + <item name="bottomBright">@empty</item> + <item name="bottomMedium">@empty</item> + <item name="centerMedium">@empty</item> <item name="layout">@layout/alert_dialog_material</item> <item name="listLayout">@layout/select_dialog_material</item> <item name="progressLayout">@layout/progress_dialog_material</item>
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h index ac5eca08..85e442da 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h
@@ -253,7 +253,8 @@ // Type of the data value. enum { - // Contains no data. + // The 'data' is either 0 or 1, specifying this resource is either + // undefined or empty, respectively. TYPE_NULL = 0x00, // The 'data' holds a ResTable_ref, a reference to another resource // table entry. @@ -351,6 +352,14 @@ COMPLEX_MANTISSA_MASK = 0xffffff }; + // Possible data values for TYPE_NULL. + enum { + // The value is not defined. + DATA_NULL_UNDEFINED = 0, + // The value is explicitly defined as empty. + DATA_NULL_EMPTY = 1 + }; + // The data for this item, as interpreted according to dataType. uint32_t data;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index b5d2885..3bbca1a 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp
@@ -4639,8 +4639,15 @@ // It's a reference! if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') { + // Special case @null as undefined. This will be converted by + // AssetManager to TYPE_NULL with data DATA_NULL_UNDEFINED. outValue->data = 0; return true; + } else if (len == 6 && s[1]=='e' && s[2]=='m' && s[3]=='p' && s[4]=='t' && s[5]=='y') { + // Special case @empty as explicitly defined empty value. + outValue->dataType = Res_value::TYPE_NULL; + outValue->data = Res_value::DATA_NULL_EMPTY; + return true; } else { bool createIfNotFound = false; const char16_t* resourceRefName; @@ -6251,7 +6258,14 @@ void ResTable::print_value(const Package* pkg, const Res_value& value) const { if (value.dataType == Res_value::TYPE_NULL) { - printf("(null)\n"); + if (value.data == Res_value::DATA_NULL_UNDEFINED) { + printf("(null)\n"); + } else if (value.data == Res_value::DATA_NULL_EMPTY) { + printf("(null empty)\n"); + } else { + // This should never happen. + printf("(null) 0x%08x\n", value.data); + } } else if (value.dataType == Res_value::TYPE_REFERENCE) { printf("(reference) 0x%08x\n", value.data); } else if (value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) {