Vibration parser for open TypedXmlPullParser
This allows clients to pass in an already-open parser to read a vibration effect. This will be helpful in cases where a vibration XML may be part of a larger XML. Bug: 291128479 Bug: 185779088 Test: atest VibrationEffectXmlSerializationTest Change-Id: I630589684a0b8361dfc9b7b5c1025d191b9b588c
This commit is contained in:
@@ -28,7 +28,6 @@ import com.android.internal.vibrator.persistence.VibrationEffectXmlParser;
|
||||
import com.android.internal.vibrator.persistence.XmlConstants;
|
||||
import com.android.internal.vibrator.persistence.XmlParserException;
|
||||
import com.android.internal.vibrator.persistence.XmlReader;
|
||||
import com.android.internal.vibrator.persistence.XmlSerializedVibration;
|
||||
import com.android.modules.utils.TypedXmlPullParser;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
@@ -178,25 +177,62 @@ public final class VibrationXmlParser {
|
||||
// Ensure XML starts with expected root tag.
|
||||
XmlReader.readDocumentStartTag(parser, XmlConstants.TAG_VIBRATION);
|
||||
|
||||
int parserFlags = 0;
|
||||
if ((flags & FLAG_ALLOW_HIDDEN_APIS) != 0) {
|
||||
parserFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
|
||||
}
|
||||
|
||||
// Parse root tag as a vibration effect.
|
||||
XmlSerializedVibration<VibrationEffect> serializedVibration =
|
||||
VibrationEffectXmlParser.parseTag(parser, parserFlags);
|
||||
VibrationEffect effect = parseTag(parser, flags);
|
||||
|
||||
// Ensure XML ends after root tag is consumed.
|
||||
XmlReader.readDocumentEndTag(parser);
|
||||
|
||||
return serializedVibration.deserialize();
|
||||
} catch (XmlParserException e) {
|
||||
return effect;
|
||||
} catch (XmlParserException | VibrationXmlParserException e) {
|
||||
Slog.w(TAG, "Error parsing vibration XML", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses XML content from given open {@link TypedXmlPullParser} into a {@link VibrationEffect}.
|
||||
*
|
||||
* <p>The provided parser should be pointing to a start of a valid vibration XML (i.e. to a
|
||||
* start <vibration> tag). No other parser position, including start of document, is considered
|
||||
* valid.
|
||||
*
|
||||
* <p>This method parses as long as it reads a valid vibration XML, and until an end vibration
|
||||
* tag. After a successful parsing, the parser will point to the end vibration tag (i.e. to a
|
||||
* </vibration> tag).
|
||||
*
|
||||
* @throws IOException error parsing from given {@link TypedXmlPullParser}.
|
||||
* @throws VibrationXmlParserException if the XML tag cannot be parsed into a
|
||||
* {@link VibrationEffect}. The given {@code parser} might be pointing to a child XML tag
|
||||
* that caused the parser failure.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public static VibrationEffect parseTag(@NonNull TypedXmlPullParser parser, @Flags int flags)
|
||||
throws IOException, VibrationXmlParserException {
|
||||
int parserFlags = 0;
|
||||
if ((flags & VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS) != 0) {
|
||||
parserFlags |= XmlConstants.FLAG_ALLOW_HIDDEN_APIS;
|
||||
}
|
||||
try {
|
||||
return VibrationEffectXmlParser.parseTag(parser, parserFlags).deserialize();
|
||||
} catch (XmlParserException e) {
|
||||
throw new VibrationXmlParserException("Error parsing vibration effect.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an error while parsing a vibration XML input.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final class VibrationXmlParserException extends Exception {
|
||||
private VibrationXmlParserException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
||||
private VibrationXmlParser() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,14 @@ import static org.junit.Assert.assertThrows;
|
||||
import android.os.VibrationEffect;
|
||||
import android.os.vibrator.PrebakedSegment;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.util.Xml;
|
||||
|
||||
import com.android.modules.utils.TypedXmlPullParser;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
@@ -64,6 +68,61 @@ public class VibrationEffectXmlSerializationTest {
|
||||
assertThat(isSupportedMimeType("application/vnd.android.vibration+xml")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseTag_succeedAndParserPointsToEndVibrationTag() throws Exception {
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
.addPrimitive(PRIMITIVE_CLICK)
|
||||
.addPrimitive(PRIMITIVE_TICK, 0.2497f)
|
||||
.compose();
|
||||
String xml = "<vibration>"
|
||||
+ "<primitive-effect name=\"click\"/>"
|
||||
+ "<primitive-effect name=\"tick\" scale=\"0.2497\"/>"
|
||||
+ "</vibration>";
|
||||
VibrationEffect effect2 = VibrationEffect.startComposition()
|
||||
.addPrimitive(PRIMITIVE_LOW_TICK, 1f, 356)
|
||||
.addPrimitive(PRIMITIVE_SPIN, 0.6364f, 7)
|
||||
.compose();
|
||||
String xml2 = "<vibration>"
|
||||
+ "<primitive-effect name=\"low_tick\" delayMs=\"356\"/>"
|
||||
+ "<primitive-effect name=\"spin\" scale=\"0.6364\" delayMs=\"7\"/>"
|
||||
+ "</vibration>";
|
||||
|
||||
TypedXmlPullParser parser = createXmlPullParser(xml);
|
||||
assertParseTagSucceeds(parser, effect);
|
||||
parser.next();
|
||||
assertEndOfDocument(parser);
|
||||
|
||||
// Test no-issues when an end-tag follows the vibration XML.
|
||||
// To test this, starting with the corresponding "start-tag" is necessary.
|
||||
parser = createXmlPullParser("<next-tag>" + xml + "</next-tag>");
|
||||
// Move the parser once to point to the "<vibration> tag.
|
||||
parser.next();
|
||||
assertParseTagSucceeds(parser, effect);
|
||||
parser.next();
|
||||
assertEndTag(parser, "next-tag");
|
||||
|
||||
parser = createXmlPullParser(xml + "<next-tag>");
|
||||
assertParseTagSucceeds(parser, effect);
|
||||
parser.next();
|
||||
assertStartTag(parser, "next-tag");
|
||||
|
||||
parser = createXmlPullParser(xml + xml2);
|
||||
assertParseTagSucceeds(parser, effect);
|
||||
parser.next();
|
||||
assertParseTagSucceeds(parser, effect2);
|
||||
parser.next();
|
||||
assertEndOfDocument(parser);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseTag_badXml_throwsException() throws Exception {
|
||||
assertParseTagFails(
|
||||
"<vibration>random text<primitive-effect name=\"click\"/></vibration>");
|
||||
assertParseTagFails("<bad-tag><primitive-effect name=\"click\"/></vibration>");
|
||||
assertParseTagFails("<primitive-effect name=\"click\"/></vibration>");
|
||||
assertParseTagFails("<vibration><primitive-effect name=\"click\"/>");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrimitives_allSucceed() throws IOException {
|
||||
VibrationEffect effect = VibrationEffect.startComposition()
|
||||
@@ -172,6 +231,41 @@ public class VibrationEffectXmlSerializationTest {
|
||||
assertThat(parse(xml, /* flags= */ 0)).isEqualTo(effect);
|
||||
}
|
||||
|
||||
private TypedXmlPullParser createXmlPullParser(String xml) throws Exception {
|
||||
TypedXmlPullParser parser = Xml.newFastPullParser();
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||
parser.setInput(new StringReader(xml));
|
||||
parser.next(); // read START_DOCUMENT
|
||||
return parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts parsing vibration from an open TypedXmlPullParser succeeds, and that the parser
|
||||
* points to the end "vibration" tag.
|
||||
*/
|
||||
private void assertParseTagSucceeds(
|
||||
TypedXmlPullParser parser, VibrationEffect effect) throws Exception {
|
||||
assertThat(parseTag(parser)).isEqualTo(effect);
|
||||
|
||||
assertThat(parser.getEventType()).isEqualTo(XmlPullParser.END_TAG);
|
||||
assertThat(parser.getName()).isEqualTo("vibration");
|
||||
}
|
||||
|
||||
private void assertEndTag(TypedXmlPullParser parser, String expectedTagName) throws Exception {
|
||||
assertThat(parser.getName()).isEqualTo(expectedTagName);
|
||||
assertThat(parser.getEventType()).isEqualTo(parser.END_TAG);
|
||||
}
|
||||
|
||||
private void assertStartTag(TypedXmlPullParser parser, String expectedTagName)
|
||||
throws Exception {
|
||||
assertThat(parser.getName()).isEqualTo(expectedTagName);
|
||||
assertThat(parser.getEventType()).isEqualTo(parser.START_TAG);
|
||||
}
|
||||
|
||||
private void assertEndOfDocument(TypedXmlPullParser parser) throws Exception {
|
||||
assertThat(parser.getEventType()).isEqualTo(parser.END_DOCUMENT);
|
||||
}
|
||||
|
||||
private void assertHiddenApisParserSucceeds(String xml, VibrationEffect effect)
|
||||
throws IOException {
|
||||
assertThat(parse(xml, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS)).isEqualTo(effect);
|
||||
@@ -183,6 +277,12 @@ public class VibrationEffectXmlSerializationTest {
|
||||
() -> serialize(effect, /* flags= */ 0));
|
||||
}
|
||||
|
||||
private void assertParseTagFails(String xml) {
|
||||
assertThrows("Expected parsing to fail for " + xml,
|
||||
VibrationXmlParser.VibrationXmlParserException.class,
|
||||
() -> parseTag(createXmlPullParser(xml)));
|
||||
}
|
||||
|
||||
private void assertPublicApisSerializerSucceeds(VibrationEffect effect,
|
||||
String... expectedSegments) throws IOException {
|
||||
assertSerializationContainsSegments(serialize(effect, /* flags= */ 0), expectedSegments);
|
||||
@@ -214,6 +314,11 @@ public class VibrationEffectXmlSerializationTest {
|
||||
return VibrationXmlParser.parse(new StringReader(xml), flags);
|
||||
}
|
||||
|
||||
private static VibrationEffect parseTag(TypedXmlPullParser parser)
|
||||
throws IOException, VibrationXmlParser.VibrationXmlParserException {
|
||||
return VibrationXmlParser.parseTag(parser, VibrationXmlParser.FLAG_ALLOW_HIDDEN_APIS);
|
||||
}
|
||||
|
||||
private static String serialize(VibrationEffect effect, @VibrationXmlSerializer.Flags int flags)
|
||||
throws IOException {
|
||||
StringWriter writer = new StringWriter();
|
||||
|
||||
Reference in New Issue
Block a user