Merge change Id5af7cf2 into eclair

* changes:
  Add xdpi and ydpi to layout-configs XML Schema.
This commit is contained in:
Android (Google) Code Review
2009-09-29 18:05:10 -04:00
4 changed files with 283 additions and 137 deletions

View File

@@ -94,8 +94,13 @@ public class LayoutConfigsXsd {
public static final String NODE_SCREEN_DIMENSION = "screen-dimension"; //$NON-NLS-1$ public static final String NODE_SCREEN_DIMENSION = "screen-dimension"; //$NON-NLS-1$
/** The screen-dimension element has 2 size element children. */
public static final String NODE_SIZE = "size"; //$NON-NLS-1$ public static final String NODE_SIZE = "size"; //$NON-NLS-1$
public static final String NODE_XPDI = "xdpi"; //$NON-NLS-1$
public static final String NODE_YDPI = "ydpi"; //$NON-NLS-1$
/** /**
* The "name" attribute, used by both the "device" and the "config" * The "name" attribute, used by both the "device" and the "config"
* elements. It represents the user-interface name of these objects. * elements. It represents the user-interface name of these objects.

View File

@@ -24,7 +24,7 @@
<xsd:element name="layout-configs"> <xsd:element name="layout-configs">
<xsd:annotation> <xsd:annotation>
<xsd:documentation> <xsd:documentation xml:lang="en">
The "layout-configs" element is the root element of this schema. The "layout-configs" element is the root element of this schema.
It must contain one or more "device" elements that each define the configurations It must contain one or more "device" elements that each define the configurations
@@ -41,7 +41,7 @@
<xsd:element name="device" minOccurs="1" maxOccurs="unbounded"> <xsd:element name="device" minOccurs="1" maxOccurs="unbounded">
<xsd:annotation> <xsd:annotation>
<xsd:documentation> <xsd:documentation xml:lang="en">
A device element must contain at most one "default" element A device element must contain at most one "default" element
followed by one or more "config" elements. followed by one or more "config" elements.
@@ -78,10 +78,14 @@
--> -->
<xsd:complexType name="parametersType"> <xsd:complexType name="parametersType">
<xsd:annotation> <xsd:annotation>
<xsd:documentation> <xsd:documentation xml:lang="en">
The parametersType define all the parameters that can happen either in a The parametersType define all the parameters that can happen either in a
"default" element or in a named "config" element. "default" element or in a named "config" element.
Each parameter element can appear once at most. Each parameter element can appear once at most.
Parameters here are the same as those used to specify alternate Android
resources, as documented by
http://d.android.com/guide/topics/resources/resources-i18n.html#AlternateResources
</xsd:documentation> </xsd:documentation>
</xsd:annotation> </xsd:annotation>
@@ -89,6 +93,11 @@
<!-- parametersType says that 0..1 of each of these elements must be declared. --> <!-- parametersType says that 0..1 of each of these elements must be declared. -->
<xsd:element name="screen-size" minOccurs="0"> <xsd:element name="screen-size" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies that the configuration is for a particular class of screen.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType> <xsd:simpleType>
<xsd:restriction base="xsd:token"> <xsd:restriction base="xsd:token">
<xsd:enumeration value="small" /> <xsd:enumeration value="small" />
@@ -99,6 +108,14 @@
</xsd:element> </xsd:element>
<xsd:element name="screen-ratio" minOccurs="0"> <xsd:element name="screen-ratio" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies that the configuration is for a taller/wider than traditional
screen. This is based purely on the aspect ration of the screen: QVGA,
HVGA, and VGA are notlong; WQVGA, WVGA, FWVGA are long. Note that long
may mean either wide or tall, depending on the current orientation.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType> <xsd:simpleType>
<xsd:restriction base="xsd:token"> <xsd:restriction base="xsd:token">
<xsd:enumeration value="long" /> <xsd:enumeration value="long" />
@@ -108,6 +125,12 @@
</xsd:element> </xsd:element>
<xsd:element name="screen-orientation" minOccurs="0"> <xsd:element name="screen-orientation" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies that the configuration is for a screen that is tall (port) or
wide (land).
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType> <xsd:simpleType>
<xsd:restriction base="xsd:token"> <xsd:restriction base="xsd:token">
<xsd:enumeration value="port" /> <xsd:enumeration value="port" />
@@ -118,6 +141,17 @@
</xsd:element> </xsd:element>
<xsd:element name="pixel-density" minOccurs="0"> <xsd:element name="pixel-density" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies the screen density the configuration is defined for. The medium
density of traditional HVGA screens (mdpi) is defined to be approximately
160dpi; low density (ldpi) is 120, and high density (hdpi) is 240. There
is thus a 4:3 scaling factor between each density, so a 9x9 bitmap in ldpi
would be 12x12 is mdpi and 16x16 in hdpi.
The special nodpi density that can be used in resource qualifiers is not
a valid keyword here.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType> <xsd:simpleType>
<xsd:restriction base="xsd:token"> <xsd:restriction base="xsd:token">
<xsd:enumeration value="ldpi" /> <xsd:enumeration value="ldpi" />
@@ -128,6 +162,11 @@
</xsd:element> </xsd:element>
<xsd:element name="touch-type" minOccurs="0"> <xsd:element name="touch-type" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies the touch type the configuration is defined for.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType> <xsd:simpleType>
<xsd:restriction base="xsd:token"> <xsd:restriction base="xsd:token">
<xsd:enumeration value="notouch" /> <xsd:enumeration value="notouch" />
@@ -138,6 +177,12 @@
</xsd:element> </xsd:element>
<xsd:element name="keyboard-state" minOccurs="0"> <xsd:element name="keyboard-state" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
If your configuration uses a soft keyboard, use the keyssoft value.
If it doesn't and has a real keyboard, use keysexposed or keyshidden.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType> <xsd:simpleType>
<xsd:restriction base="xsd:token"> <xsd:restriction base="xsd:token">
<xsd:enumeration value="keysexposed" /> <xsd:enumeration value="keysexposed" />
@@ -148,6 +193,11 @@
</xsd:element> </xsd:element>
<xsd:element name="text-input-method" minOccurs="0"> <xsd:element name="text-input-method" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies the primary text input method the configuration is designed for.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType> <xsd:simpleType>
<xsd:restriction base="xsd:token"> <xsd:restriction base="xsd:token">
<xsd:enumeration value="nokeys" /> <xsd:enumeration value="nokeys" />
@@ -158,6 +208,12 @@
</xsd:element> </xsd:element>
<xsd:element name="nav-method" minOccurs="0"> <xsd:element name="nav-method" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies the primary non-touchscreen navigation method the configuration
is designed for.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType> <xsd:simpleType>
<xsd:restriction base="xsd:token"> <xsd:restriction base="xsd:token">
<xsd:enumeration value="dpad" /> <xsd:enumeration value="dpad" />
@@ -169,6 +225,11 @@
</xsd:element> </xsd:element>
<xsd:element name="screen-dimension" minOccurs="0"> <xsd:element name="screen-dimension" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies the device screen resolution, in pixels.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType> <xsd:complexType>
<xsd:sequence minOccurs="2" maxOccurs="2"> <xsd:sequence minOccurs="2" maxOccurs="2">
@@ -181,6 +242,33 @@
</xsd:sequence> </xsd:sequence>
</xsd:complexType> </xsd:complexType>
</xsd:element> </xsd:element>
<xsd:element name="xdpi" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies the actual density in X of the device screen.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:float">
<xsd:minExclusive value="0" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
<xsd:element name="ydpi" minOccurs="0">
<xsd:annotation>
<xsd:documentation xml:lang="en">
Specifies the actual density in Y of the device screen.
</xsd:documentation>
</xsd:annotation>
<xsd:simpleType>
<xsd:restriction base="xsd:float">
<xsd:minExclusive value="0" />
</xsd:restriction>
</xsd:simpleType>
</xsd:element>
</xsd:all> </xsd:all>
</xsd:complexType> </xsd:complexType>

View File

@@ -104,6 +104,42 @@ public class TestLayoutConfisXsd extends TestCase {
// --- Helpers ------------ // --- Helpers ------------
/** An helper that validates a string against an expected regexp. */
private void assertRegex(String expectedRegexp, String actualString) {
assertNotNull(actualString);
assertTrue(
String.format("Regexp Assertion Failed:\nExpected: %s\nActual: %s\n",
expectedRegexp, actualString),
actualString.matches(expectedRegexp));
}
public void checkFailure(String document, String regexp) throws Exception {
Source source = new StreamSource(new StringReader(document));
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = LayoutConfigsXsd.getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse expression referring to this grammar rule
assertRegex(regexp, e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
}
public void checkSuccess(String document) throws Exception {
Source source = new StreamSource(new StringReader(document));
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = LayoutConfigsXsd.getValidator(null);
validator.validate(source);
handler.verify();
}
// --- Tests ------------
/** Validate a valid sample using an InputStream */ /** Validate a valid sample using an InputStream */
public void testValidateLocalRepositoryFile() throws Exception { public void testValidateLocalRepositoryFile() throws Exception {
@@ -117,171 +153,180 @@ public class TestLayoutConfisXsd extends TestCase {
handler.verify(); handler.verify();
} }
/** An helper that validates a string against an expected regexp. */
private void assertRegex(String expectedRegexp, String actualString) {
assertNotNull(actualString);
assertTrue(
String.format("Regexp Assertion Failed:\nExpected: %s\nActual: %s\n",
expectedRegexp, actualString),
actualString.matches(expectedRegexp));
}
// --- Tests ------------
/** A document should at least have a root to be valid */ /** A document should at least have a root to be valid */
public void testEmptyXml() throws Exception { public void testEmptyXml() throws Exception {
String document = "<?xml version=\"1.0\"?>"; checkFailure(
// document
"<?xml version=\"1.0\"?>",
Source source = new StreamSource(new StringReader(document)); // expected failure
"Premature end of file.*");
CaptureErrorHandler handler = new CaptureErrorHandler();
Validator validator = LayoutConfigsXsd.getValidator(handler);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect to get this specific exception message
assertRegex("Premature end of file.*", e.getMessage());
return;
}
// We shouldn't get here
handler.verify();
fail();
} }
/** A document with an unknown element. */ /** A document with an unknown element. */
public void testUnknownContentXml() throws Exception { public void testUnknownContentXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" + checkFailure(
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" + // document
"<d:unknown />" + "<?xml version=\"1.0\"?>" +
"</d:layout-configs>"; "<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
"<d:unknown />" +
"</d:layout-configs>",
Source source = new StreamSource(new StringReader(document)); // expected failure
"cvc-complex-type.2.4.a: Invalid content was found.*");
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = LayoutConfigsXsd.getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse expression referring to this grammar rule
assertRegex("cvc-complex-type.2.4.a: Invalid content was found.*", e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
} }
/** A document with an missing attribute in a device element. */ /** A document with an missing attribute in a device element. */
public void testIncompleteContentXml() throws Exception { public void testIncompleteContentXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" + checkFailure(
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" + // document
"<d:device />" + "<?xml version=\"1.0\"?>" +
"</d:layout-configs>"; "<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
"<d:device />" +
"</d:layout-configs>",
Source source = new StreamSource(new StringReader(document)); // expected failure
"cvc-complex-type.4: Attribute 'name' must appear on element 'd:device'.");
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = LayoutConfigsXsd.getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse error referring to this grammar rule
assertRegex("cvc-complex-type.4: Attribute 'name' must appear on element 'd:device'.", e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
} }
/** A document with a root element containing no device element is not valid. */ /** A document with a root element containing no device element is not valid. */
public void testEmptyRootXml() throws Exception { public void testEmptyRootXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" + checkFailure(
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" />"; // document
"<?xml version=\"1.0\"?>" +
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" />",
Source source = new StreamSource(new StringReader(document)); // expected failure
"cvc-complex-type.2.4.b: The content of element 'd:layout-configs' is not complete.*");
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = LayoutConfigsXsd.getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse expression referring to this grammar rule
assertRegex("cvc-complex-type.2.4.b: The content of element 'd:layout-configs' is not complete.*", e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
} }
/** A document with an empty device element is not valid. */ /** A document with an empty device element is not valid. */
public void testEmptyDeviceXml() throws Exception { public void testEmptyDeviceXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" + checkFailure(
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" + // document
"<d:device name=\"foo\"/>" + "<?xml version=\"1.0\"?>" +
"</d:layout-configs>"; "<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
"<d:device name=\"foo\"/>" +
"</d:layout-configs>",
Source source = new StreamSource(new StringReader(document)); // expected failure
"cvc-complex-type.2.4.b: The content of element 'd:device' is not complete.*");
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = LayoutConfigsXsd.getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse error referring to this grammar rule
assertRegex("cvc-complex-type.2.4.b: The content of element 'd:device' is not complete.*", e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
} }
/** A document with two default elements in a device element is not valid. */ /** A document with two default elements in a device element is not valid. */
public void testTwoDefaultsXml() throws Exception { public void testTwoDefaultsXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" + checkFailure(
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" + // document
"<d:device name=\"foo\">" + "<?xml version=\"1.0\"?>" +
" <d:default />" + "<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
" <d:default />" + "<d:device name=\"foo\">" +
"</d:device>" + " <d:default />" +
"</d:layout-configs>"; " <d:default />" +
"</d:device>" +
"</d:layout-configs>",
Source source = new StreamSource(new StringReader(document)); // expected failure
"cvc-complex-type.2.4.a: Invalid content was found starting with element 'd:default'.*");
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = LayoutConfigsXsd.getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse error referring to this grammar rule
assertRegex("cvc-complex-type.2.4.a: Invalid content was found starting with element 'd:default'.*", e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
} }
/** The default elements must be defined before the config one. It's invalid if after. */ /** The default elements must be defined before the config one. It's invalid if after. */
public void testDefaultConfigOrderXml() throws Exception { public void testDefaultConfigOrderXml() throws Exception {
String document = "<?xml version=\"1.0\"?>" + checkFailure(
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" + // document
"<d:device name=\"foo\">" + "<?xml version=\"1.0\"?>" +
" <d:config name=\"must-be-after-default\" />" + "<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
" <d:default />" + "<d:device name=\"foo\">" +
"</d:device>" + " <d:config name=\"must-be-after-default\" />" +
"</d:layout-configs>"; " <d:default />" +
"</d:device>" +
"</d:layout-configs>",
Source source = new StreamSource(new StringReader(document)); // expected failure
"cvc-complex-type.2.4.a: Invalid content was found starting with element 'd:default'.*");
// don't capture the validator errors, we want it to fail and catch the exception
Validator validator = LayoutConfigsXsd.getValidator(null);
try {
validator.validate(source);
} catch (SAXParseException e) {
// We expect a parse error referring to this grammar rule
assertRegex("cvc-complex-type.2.4.a: Invalid content was found starting with element 'd:default'.*", e.getMessage());
return;
}
// If we get here, the validator has not failed as we expected it to.
fail();
} }
/** Screen dimension cannot be 0. */
public void testScreenDimZeroXml() throws Exception {
checkFailure(
// document
"<?xml version=\"1.0\"?>" +
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
"<d:device name=\"foo\">" +
" <d:default>" +
" <d:screen-dimension> <d:size>0</d:size> <d:size>1</d:size> </d:screen-dimension>" +
" </d:default>" +
"</d:device>" +
"</d:layout-configs>",
// expected failure
"cvc-minInclusive-valid: Value '0' is not facet-valid with respect to minInclusive '1'.*");
}
/** Screen dimension cannot be negative. */
public void testScreenDimNegativeXml() throws Exception {
checkFailure(
// document
"<?xml version=\"1.0\"?>" +
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
"<d:device name=\"foo\">" +
" <d:default>" +
" <d:screen-dimension> <d:size>-5</d:size> <d:size>1</d:size> </d:screen-dimension>" +
" </d:default>" +
"</d:device>" +
"</d:layout-configs>",
// expected failure
"cvc-minInclusive-valid: Value '-5' is not facet-valid with respect to minInclusive '1'.*");
}
/** X/Y dpi cannot be 0. */
public void testXDpiZeroXml() throws Exception {
checkFailure(
// document
"<?xml version=\"1.0\"?>" +
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
"<d:device name=\"foo\">" +
" <d:default>" +
" <d:xdpi>0</d:xdpi>" +
" </d:default>" +
"</d:device>" +
"</d:layout-configs>",
// expected failure
"cvc-minExclusive-valid: Value '0' is not facet-valid with respect to minExclusive '0.0E1'.*");
}
/** X/Y dpi cannot be negative. */
public void testXDpiNegativeXml() throws Exception {
checkFailure(
// document
"<?xml version=\"1.0\"?>" +
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
"<d:device name=\"foo\">" +
" <d:default>" +
" <d:xdpi>-3.1415926538</d:xdpi>" +
" </d:default>" +
"</d:device>" +
"</d:layout-configs>",
// expected failure
"cvc-minExclusive-valid: Value '-3.1415926538' is not facet-valid with respect to minExclusive '0.0E1'.*");
}
/** WHitespace around token is accepted by the schema. */
public void testTokenWhitespaceXml() throws Exception {
checkSuccess(
// document
"<?xml version=\"1.0\"?>" +
"<d:layout-configs xmlns:d=\"http://schemas.android.com/sdk/android/layout-configs/1\" >" +
"<d:device name=\"foo\">" +
" <d:config name='foo'>" +
" <d:screen-ratio> \n long \r </d:screen-ratio>" +
" </d:config>" +
"</d:device>" +
"</d:layout-configs>");
}
} }

View File

@@ -32,6 +32,8 @@
<d:size>240</d:size> <!-- 2 * int>0 --> <d:size>240</d:size> <!-- 2 * int>0 -->
<d:size>480</d:size> <d:size>480</d:size>
</d:screen-dimension> </d:screen-dimension>
<d:xdpi>180.81234</d:xdpi>
<d:ydpi>180.81234</d:ydpi>
</d:default> </d:default>
<d:config name="Portrait"> <d:config name="Portrait">
@@ -117,8 +119,14 @@
<d:nav-method>nonav</d:nav-method> <d:nav-method>nonav</d:nav-method>
</d:config> </d:config>
</d:device> <d:config name="xdpi">
<d:xdpi>240</d:xdpi>
</d:config>
<d:config name="ydpi">
<d:ydpi>2800</d:ydpi>
</d:config>
</d:device>
<d:device name="SomePhone"> <!-- 1..n --> <d:device name="SomePhone"> <!-- 1..n -->
<d:config name="screen-size-normal"> <d:config name="screen-size-normal">