Loading core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java +49 −6 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.internal.vibrator.persistence; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DELAY_MS; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DELAY_TYPE; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_NAME; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_SCALE; import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE; Loading @@ -25,9 +26,11 @@ import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITI import android.annotation.NonNull; import android.annotation.Nullable; import android.os.VibrationEffect; import android.os.vibrator.Flags; import android.os.vibrator.PrimitiveSegment; import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveDelayType; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; Loading @@ -46,18 +49,27 @@ final class SerializedCompositionPrimitive implements SerializedSegment { private final PrimitiveEffectName mPrimitiveName; private final float mPrimitiveScale; private final int mPrimitiveDelayMs; @Nullable private final PrimitiveDelayType mDelayType; SerializedCompositionPrimitive(PrimitiveEffectName primitiveName, float scale, int delayMs) { SerializedCompositionPrimitive(PrimitiveEffectName primitiveName, float scale, int delayMs, @Nullable PrimitiveDelayType delayType) { mPrimitiveName = primitiveName; mPrimitiveScale = scale; mPrimitiveDelayMs = delayMs; mDelayType = delayType; } @Override public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) { if (Flags.primitiveCompositionAbsoluteDelay() && mDelayType != null) { composition.addPrimitive(mPrimitiveName.getPrimitiveId(), mPrimitiveScale, mPrimitiveDelayMs, mDelayType.getDelayType()); } else { composition.addPrimitive(mPrimitiveName.getPrimitiveId(), mPrimitiveScale, mPrimitiveDelayMs); } } @Override public void write(@NonNull TypedXmlSerializer serializer) throws IOException { Loading @@ -72,6 +84,12 @@ final class SerializedCompositionPrimitive implements SerializedSegment { serializer.attributeInt(NAMESPACE, ATTRIBUTE_DELAY_MS, mPrimitiveDelayMs); } if (Flags.primitiveCompositionAbsoluteDelay() && mDelayType != null) { if (mDelayType.getDelayType() != PrimitiveSegment.DEFAULT_DELAY_TYPE) { serializer.attribute(NAMESPACE, ATTRIBUTE_DELAY_TYPE, mDelayType.toString()); } } serializer.endTag(NAMESPACE, TAG_PRIMITIVE_EFFECT); } Loading @@ -81,6 +99,7 @@ final class SerializedCompositionPrimitive implements SerializedSegment { + "name=" + mPrimitiveName + ", scale=" + mPrimitiveScale + ", delayMs=" + mPrimitiveDelayMs + ", delayType=" + mDelayType + '}'; } Loading @@ -91,8 +110,14 @@ final class SerializedCompositionPrimitive implements SerializedSegment { static SerializedCompositionPrimitive parseNext(@NonNull TypedXmlPullParser parser) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_PRIMITIVE_EFFECT); if (Flags.primitiveCompositionAbsoluteDelay()) { XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_NAME, ATTRIBUTE_DELAY_MS, ATTRIBUTE_SCALE, ATTRIBUTE_DELAY_TYPE); } else { XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_NAME, ATTRIBUTE_DELAY_MS, ATTRIBUTE_SCALE); } PrimitiveEffectName primitiveName = parsePrimitiveName( parser.getAttributeValue(NAMESPACE, ATTRIBUTE_NAME)); Loading @@ -100,11 +125,13 @@ final class SerializedCompositionPrimitive implements SerializedSegment { parser, ATTRIBUTE_SCALE, 0, 1, PrimitiveSegment.DEFAULT_SCALE); int delayMs = XmlReader.readAttributeIntNonNegative( parser, ATTRIBUTE_DELAY_MS, PrimitiveSegment.DEFAULT_DELAY_MILLIS); PrimitiveDelayType delayType = parseDelayType( parser.getAttributeValue(NAMESPACE, ATTRIBUTE_DELAY_TYPE)); // Consume tag XmlReader.readEndTag(parser); return new SerializedCompositionPrimitive(primitiveName, scale, delayMs); return new SerializedCompositionPrimitive(primitiveName, scale, delayMs, delayType); } @NonNull Loading @@ -119,5 +146,21 @@ final class SerializedCompositionPrimitive implements SerializedSegment { } return effectName; } @Nullable private static PrimitiveDelayType parseDelayType(@Nullable String name) throws XmlParserException { if (name == null) { return null; } if (!Flags.primitiveCompositionAbsoluteDelay()) { throw new XmlParserException("Unexpected primitive delay type " + name); } PrimitiveDelayType delayType = PrimitiveDelayType.findByName(name); if (delayType == null) { throw new XmlParserException("Unexpected primitive delay type " + name); } return delayType; } } } core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java +14 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.os.vibrator.VibrationEffectSegment; import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment; import com.android.internal.vibrator.persistence.XmlConstants.PredefinedEffectName; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveDelayType; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName; import java.util.List; Loading Loading @@ -170,8 +171,20 @@ public final class VibrationEffectXmlSerializer { XmlValidator.checkSerializerCondition(primitiveName != null, "Unsupported primitive effect id %s", primitive.getPrimitiveId()); PrimitiveDelayType delayType = null; if (Flags.primitiveCompositionAbsoluteDelay()) { delayType = PrimitiveDelayType.findByType(primitive.getDelayType()); XmlValidator.checkSerializerCondition(delayType != null, "Unsupported primitive delay type %s", primitive.getDelayType()); } else { XmlValidator.checkSerializerCondition( primitive.getDelayType() == PrimitiveSegment.DEFAULT_DELAY_TYPE, "Unsupported primitive delay type %s", primitive.getDelayType()); } return new SerializedCompositionPrimitive( primitiveName, primitive.getScale(), primitive.getDelay()); primitiveName, primitive.getScale(), primitive.getDelay(), delayType); } private static int toAmplitudeInt(float amplitude) { Loading core/java/com/android/internal/vibrator/persistence/XmlConstants.java +52 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.VibrationEffect; import android.os.VibrationEffect.Composition.DelayType; import android.os.VibrationEffect.Composition.PrimitiveType; import java.lang.annotation.Retention; Loading Loading @@ -51,6 +52,7 @@ public final class XmlConstants { public static final String ATTRIBUTE_AMPLITUDE = "amplitude"; public static final String ATTRIBUTE_SCALE = "scale"; public static final String ATTRIBUTE_DELAY_MS = "delayMs"; public static final String ATTRIBUTE_DELAY_TYPE = "delayType"; public static final String VALUE_AMPLITUDE_DEFAULT = "default"; Loading Loading @@ -87,7 +89,7 @@ public final class XmlConstants { /** * Return the {@link PrimitiveEffectName} that represents given primitive id, or null if * none of the available names maps to the given id. * none of the available names map to the given id. */ @Nullable public static PrimitiveEffectName findById(int primitiveId) { Loading Loading @@ -200,4 +202,53 @@ public final class XmlConstants { return name().toLowerCase(Locale.ROOT); } } /** Represent supported values for attribute delay type in {@link #TAG_PRIMITIVE_EFFECT} */ public enum PrimitiveDelayType { PAUSE(VibrationEffect.Composition.DELAY_TYPE_PAUSE), RELATIVE_START_OFFSET(VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET); @DelayType private final int mDelayType; PrimitiveDelayType(@DelayType int type) { mDelayType = type; } /** * Return the {@link PrimitiveEffectName} that represents given primitive id, or null if * none of the available names maps to the given id. */ @Nullable public static PrimitiveDelayType findByType(int delayType) { for (PrimitiveDelayType type : PrimitiveDelayType.values()) { if (type.mDelayType == delayType) { return type; } } return null; } /** * Return the {@link PrimitiveEffectName} that represents given primitive name, or null if * none of the available names maps to the given name. */ @Nullable public static PrimitiveDelayType findByName(@NonNull String delayType) { try { return PrimitiveDelayType.valueOf(delayType.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { return null; } } @DelayType public int getDelayType() { return mDelayType; } @Override public String toString() { return name().toLowerCase(Locale.ROOT); } } } core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java +80 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.os.vibrator.persistence; import static android.os.VibrationEffect.Composition.DELAY_TYPE_PAUSE; import static android.os.VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET; import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK; import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK; import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN; Loading @@ -31,6 +33,7 @@ import android.os.PersistableBundle; import android.os.VibrationEffect; import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; Loading Loading @@ -437,7 +440,7 @@ public class VibrationEffectXmlSerializationTest { @Test @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS) public void testVendorEffect_featureFlagEnabled_allSucceed() throws Exception { public void testVendorEffect_allSucceed() throws Exception { PersistableBundle vendorData = new PersistableBundle(); vendorData.putInt("id", 1); vendorData.putDouble("scale", 0.5); Loading Loading @@ -476,7 +479,7 @@ public class VibrationEffectXmlSerializationTest { @Test @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS) public void testInvalidVendorEffect_featureFlagEnabled_allFail() throws IOException { public void testInvalidVendorEffect_allFail() throws IOException { String emptyTag = "<vibration-effect><vendor-effect/></vibration-effect>"; assertPublicApisParserFails(emptyTag); assertHiddenApisParserFails(emptyTag); Loading Loading @@ -526,6 +529,81 @@ public class VibrationEffectXmlSerializationTest { assertHiddenApisSerializerFails(vendorEffect); } @Test @EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) public void testPrimitiveDelayType_allSucceed() throws Exception { VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET) .addPrimitive(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE) .compose(); String xml = """ <vibration-effect> <primitive-effect name="tick" delayType="relative_start_offset"/> <primitive-effect name="click" scale="0.123" delayMs="10"/> </vibration-effect> """; assertPublicApisParserSucceeds(xml, effect); assertPublicApisSerializerSucceeds(effect, "tick", "click"); // Delay type pause is not serialized, as it's the default one assertPublicApisSerializerSucceeds(effect, "relative_start_offset", "click"); assertPublicApisRoundTrip(effect); assertHiddenApisParserSucceeds(xml, effect); assertHiddenApisSerializerSucceeds(effect, "tick", "click"); assertHiddenApisRoundTrip(effect); // Check PersistableBundle from round-trip VibrationEffect.Composed parsedEffect = ((VibrationEffect.Composed) parseVibrationEffect( serialize(effect), /* flags= */ 0)); assertThat(parsedEffect.getRepeatIndex()).isEqualTo(-1); assertThat(parsedEffect.getSegments()).containsExactly( new PrimitiveSegment(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET), new PrimitiveSegment(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE)) .inOrder(); } @Test @EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) public void testPrimitiveInvalidDelayType_allFail() { String emptyAttribute = """ <vibration-effect> <primitive-effect name="tick" delayType=""/> </vibration-effect> """; assertPublicApisParserFails(emptyAttribute); assertHiddenApisParserFails(emptyAttribute); String invalidString = """ <vibration-effect> <primitive-effect name="tick" delayType="invalid"/> </vibration-effect> """; assertPublicApisParserFails(invalidString); assertHiddenApisParserFails(invalidString); } @Test @DisableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) public void testPrimitiveDelayType_featureFlagDisabled_allFail() { VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET) .addPrimitive(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE) .compose(); String xml = """ <vibration-effect> <primitive-effect name="tick" delayType="relative_start_offset"/> <primitive-effect name="click" scale="0.123" delayMs="10" delayType="pause"/> </vibration-effect> """; assertPublicApisParserFails(xml); assertPublicApisSerializerFails(effect); assertHiddenApisParserFails(xml); assertHiddenApisSerializerFails(effect); } private void assertPublicApisParserFails(String xml) { assertThrows("Expected parseVibrationEffect to fail for " + xml, VibrationXmlParser.ParseFailedException.class, Loading core/xsd/vibrator/vibration/schema/current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -15,12 +15,20 @@ package com.android.internal.vibrator.persistence { enum_constant public static final com.android.internal.vibrator.persistence.PredefinedEffectName tick; } public enum PrimitiveDelayType { method public String getRawName(); enum_constant public static final com.android.internal.vibrator.persistence.PrimitiveDelayType pause; enum_constant public static final com.android.internal.vibrator.persistence.PrimitiveDelayType relative_start_offset; } public class PrimitiveEffect { ctor public PrimitiveEffect(); method public java.math.BigInteger getDelayMs(); method public com.android.internal.vibrator.persistence.PrimitiveDelayType getDelayType(); method public com.android.internal.vibrator.persistence.PrimitiveEffectName getName(); method public float getScale(); method public void setDelayMs(java.math.BigInteger); method public void setDelayType(com.android.internal.vibrator.persistence.PrimitiveDelayType); method public void setName(com.android.internal.vibrator.persistence.PrimitiveEffectName); method public void setScale(float); } Loading Loading
core/java/com/android/internal/vibrator/persistence/SerializedCompositionPrimitive.java +49 −6 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.internal.vibrator.persistence; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DELAY_MS; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DELAY_TYPE; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_NAME; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_SCALE; import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE; Loading @@ -25,9 +26,11 @@ import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITI import android.annotation.NonNull; import android.annotation.Nullable; import android.os.VibrationEffect; import android.os.vibrator.Flags; import android.os.vibrator.PrimitiveSegment; import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveDelayType; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; Loading @@ -46,18 +49,27 @@ final class SerializedCompositionPrimitive implements SerializedSegment { private final PrimitiveEffectName mPrimitiveName; private final float mPrimitiveScale; private final int mPrimitiveDelayMs; @Nullable private final PrimitiveDelayType mDelayType; SerializedCompositionPrimitive(PrimitiveEffectName primitiveName, float scale, int delayMs) { SerializedCompositionPrimitive(PrimitiveEffectName primitiveName, float scale, int delayMs, @Nullable PrimitiveDelayType delayType) { mPrimitiveName = primitiveName; mPrimitiveScale = scale; mPrimitiveDelayMs = delayMs; mDelayType = delayType; } @Override public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) { if (Flags.primitiveCompositionAbsoluteDelay() && mDelayType != null) { composition.addPrimitive(mPrimitiveName.getPrimitiveId(), mPrimitiveScale, mPrimitiveDelayMs, mDelayType.getDelayType()); } else { composition.addPrimitive(mPrimitiveName.getPrimitiveId(), mPrimitiveScale, mPrimitiveDelayMs); } } @Override public void write(@NonNull TypedXmlSerializer serializer) throws IOException { Loading @@ -72,6 +84,12 @@ final class SerializedCompositionPrimitive implements SerializedSegment { serializer.attributeInt(NAMESPACE, ATTRIBUTE_DELAY_MS, mPrimitiveDelayMs); } if (Flags.primitiveCompositionAbsoluteDelay() && mDelayType != null) { if (mDelayType.getDelayType() != PrimitiveSegment.DEFAULT_DELAY_TYPE) { serializer.attribute(NAMESPACE, ATTRIBUTE_DELAY_TYPE, mDelayType.toString()); } } serializer.endTag(NAMESPACE, TAG_PRIMITIVE_EFFECT); } Loading @@ -81,6 +99,7 @@ final class SerializedCompositionPrimitive implements SerializedSegment { + "name=" + mPrimitiveName + ", scale=" + mPrimitiveScale + ", delayMs=" + mPrimitiveDelayMs + ", delayType=" + mDelayType + '}'; } Loading @@ -91,8 +110,14 @@ final class SerializedCompositionPrimitive implements SerializedSegment { static SerializedCompositionPrimitive parseNext(@NonNull TypedXmlPullParser parser) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_PRIMITIVE_EFFECT); if (Flags.primitiveCompositionAbsoluteDelay()) { XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_NAME, ATTRIBUTE_DELAY_MS, ATTRIBUTE_SCALE, ATTRIBUTE_DELAY_TYPE); } else { XmlValidator.checkTagHasNoUnexpectedAttributes(parser, ATTRIBUTE_NAME, ATTRIBUTE_DELAY_MS, ATTRIBUTE_SCALE); } PrimitiveEffectName primitiveName = parsePrimitiveName( parser.getAttributeValue(NAMESPACE, ATTRIBUTE_NAME)); Loading @@ -100,11 +125,13 @@ final class SerializedCompositionPrimitive implements SerializedSegment { parser, ATTRIBUTE_SCALE, 0, 1, PrimitiveSegment.DEFAULT_SCALE); int delayMs = XmlReader.readAttributeIntNonNegative( parser, ATTRIBUTE_DELAY_MS, PrimitiveSegment.DEFAULT_DELAY_MILLIS); PrimitiveDelayType delayType = parseDelayType( parser.getAttributeValue(NAMESPACE, ATTRIBUTE_DELAY_TYPE)); // Consume tag XmlReader.readEndTag(parser); return new SerializedCompositionPrimitive(primitiveName, scale, delayMs); return new SerializedCompositionPrimitive(primitiveName, scale, delayMs, delayType); } @NonNull Loading @@ -119,5 +146,21 @@ final class SerializedCompositionPrimitive implements SerializedSegment { } return effectName; } @Nullable private static PrimitiveDelayType parseDelayType(@Nullable String name) throws XmlParserException { if (name == null) { return null; } if (!Flags.primitiveCompositionAbsoluteDelay()) { throw new XmlParserException("Unexpected primitive delay type " + name); } PrimitiveDelayType delayType = PrimitiveDelayType.findByName(name); if (delayType == null) { throw new XmlParserException("Unexpected primitive delay type " + name); } return delayType; } } }
core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java +14 −1 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import android.os.vibrator.VibrationEffectSegment; import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment; import com.android.internal.vibrator.persistence.XmlConstants.PredefinedEffectName; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveDelayType; import com.android.internal.vibrator.persistence.XmlConstants.PrimitiveEffectName; import java.util.List; Loading Loading @@ -170,8 +171,20 @@ public final class VibrationEffectXmlSerializer { XmlValidator.checkSerializerCondition(primitiveName != null, "Unsupported primitive effect id %s", primitive.getPrimitiveId()); PrimitiveDelayType delayType = null; if (Flags.primitiveCompositionAbsoluteDelay()) { delayType = PrimitiveDelayType.findByType(primitive.getDelayType()); XmlValidator.checkSerializerCondition(delayType != null, "Unsupported primitive delay type %s", primitive.getDelayType()); } else { XmlValidator.checkSerializerCondition( primitive.getDelayType() == PrimitiveSegment.DEFAULT_DELAY_TYPE, "Unsupported primitive delay type %s", primitive.getDelayType()); } return new SerializedCompositionPrimitive( primitiveName, primitive.getScale(), primitive.getDelay()); primitiveName, primitive.getScale(), primitive.getDelay(), delayType); } private static int toAmplitudeInt(float amplitude) { Loading
core/java/com/android/internal/vibrator/persistence/XmlConstants.java +52 −1 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.VibrationEffect; import android.os.VibrationEffect.Composition.DelayType; import android.os.VibrationEffect.Composition.PrimitiveType; import java.lang.annotation.Retention; Loading Loading @@ -51,6 +52,7 @@ public final class XmlConstants { public static final String ATTRIBUTE_AMPLITUDE = "amplitude"; public static final String ATTRIBUTE_SCALE = "scale"; public static final String ATTRIBUTE_DELAY_MS = "delayMs"; public static final String ATTRIBUTE_DELAY_TYPE = "delayType"; public static final String VALUE_AMPLITUDE_DEFAULT = "default"; Loading Loading @@ -87,7 +89,7 @@ public final class XmlConstants { /** * Return the {@link PrimitiveEffectName} that represents given primitive id, or null if * none of the available names maps to the given id. * none of the available names map to the given id. */ @Nullable public static PrimitiveEffectName findById(int primitiveId) { Loading Loading @@ -200,4 +202,53 @@ public final class XmlConstants { return name().toLowerCase(Locale.ROOT); } } /** Represent supported values for attribute delay type in {@link #TAG_PRIMITIVE_EFFECT} */ public enum PrimitiveDelayType { PAUSE(VibrationEffect.Composition.DELAY_TYPE_PAUSE), RELATIVE_START_OFFSET(VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET); @DelayType private final int mDelayType; PrimitiveDelayType(@DelayType int type) { mDelayType = type; } /** * Return the {@link PrimitiveEffectName} that represents given primitive id, or null if * none of the available names maps to the given id. */ @Nullable public static PrimitiveDelayType findByType(int delayType) { for (PrimitiveDelayType type : PrimitiveDelayType.values()) { if (type.mDelayType == delayType) { return type; } } return null; } /** * Return the {@link PrimitiveEffectName} that represents given primitive name, or null if * none of the available names maps to the given name. */ @Nullable public static PrimitiveDelayType findByName(@NonNull String delayType) { try { return PrimitiveDelayType.valueOf(delayType.toUpperCase(Locale.ROOT)); } catch (IllegalArgumentException e) { return null; } } @DelayType public int getDelayType() { return mDelayType; } @Override public String toString() { return name().toLowerCase(Locale.ROOT); } } }
core/tests/vibrator/src/android/os/vibrator/persistence/VibrationEffectXmlSerializationTest.java +80 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package android.os.vibrator.persistence; import static android.os.VibrationEffect.Composition.DELAY_TYPE_PAUSE; import static android.os.VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET; import static android.os.VibrationEffect.Composition.PRIMITIVE_CLICK; import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK; import static android.os.VibrationEffect.Composition.PRIMITIVE_SPIN; Loading @@ -31,6 +33,7 @@ import android.os.PersistableBundle; import android.os.VibrationEffect; import android.os.vibrator.Flags; import android.os.vibrator.PrebakedSegment; import android.os.vibrator.PrimitiveSegment; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; Loading Loading @@ -437,7 +440,7 @@ public class VibrationEffectXmlSerializationTest { @Test @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS) public void testVendorEffect_featureFlagEnabled_allSucceed() throws Exception { public void testVendorEffect_allSucceed() throws Exception { PersistableBundle vendorData = new PersistableBundle(); vendorData.putInt("id", 1); vendorData.putDouble("scale", 0.5); Loading Loading @@ -476,7 +479,7 @@ public class VibrationEffectXmlSerializationTest { @Test @EnableFlags(Flags.FLAG_VENDOR_VIBRATION_EFFECTS) public void testInvalidVendorEffect_featureFlagEnabled_allFail() throws IOException { public void testInvalidVendorEffect_allFail() throws IOException { String emptyTag = "<vibration-effect><vendor-effect/></vibration-effect>"; assertPublicApisParserFails(emptyTag); assertHiddenApisParserFails(emptyTag); Loading Loading @@ -526,6 +529,81 @@ public class VibrationEffectXmlSerializationTest { assertHiddenApisSerializerFails(vendorEffect); } @Test @EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) public void testPrimitiveDelayType_allSucceed() throws Exception { VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET) .addPrimitive(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE) .compose(); String xml = """ <vibration-effect> <primitive-effect name="tick" delayType="relative_start_offset"/> <primitive-effect name="click" scale="0.123" delayMs="10"/> </vibration-effect> """; assertPublicApisParserSucceeds(xml, effect); assertPublicApisSerializerSucceeds(effect, "tick", "click"); // Delay type pause is not serialized, as it's the default one assertPublicApisSerializerSucceeds(effect, "relative_start_offset", "click"); assertPublicApisRoundTrip(effect); assertHiddenApisParserSucceeds(xml, effect); assertHiddenApisSerializerSucceeds(effect, "tick", "click"); assertHiddenApisRoundTrip(effect); // Check PersistableBundle from round-trip VibrationEffect.Composed parsedEffect = ((VibrationEffect.Composed) parseVibrationEffect( serialize(effect), /* flags= */ 0)); assertThat(parsedEffect.getRepeatIndex()).isEqualTo(-1); assertThat(parsedEffect.getSegments()).containsExactly( new PrimitiveSegment(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET), new PrimitiveSegment(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE)) .inOrder(); } @Test @EnableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) public void testPrimitiveInvalidDelayType_allFail() { String emptyAttribute = """ <vibration-effect> <primitive-effect name="tick" delayType=""/> </vibration-effect> """; assertPublicApisParserFails(emptyAttribute); assertHiddenApisParserFails(emptyAttribute); String invalidString = """ <vibration-effect> <primitive-effect name="tick" delayType="invalid"/> </vibration-effect> """; assertPublicApisParserFails(invalidString); assertHiddenApisParserFails(invalidString); } @Test @DisableFlags(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY) public void testPrimitiveDelayType_featureFlagDisabled_allFail() { VibrationEffect effect = VibrationEffect.startComposition() .addPrimitive(PRIMITIVE_TICK, 1.0f, 0, DELAY_TYPE_RELATIVE_START_OFFSET) .addPrimitive(PRIMITIVE_CLICK, 0.123f, 10, DELAY_TYPE_PAUSE) .compose(); String xml = """ <vibration-effect> <primitive-effect name="tick" delayType="relative_start_offset"/> <primitive-effect name="click" scale="0.123" delayMs="10" delayType="pause"/> </vibration-effect> """; assertPublicApisParserFails(xml); assertPublicApisSerializerFails(effect); assertHiddenApisParserFails(xml); assertHiddenApisSerializerFails(effect); } private void assertPublicApisParserFails(String xml) { assertThrows("Expected parseVibrationEffect to fail for " + xml, VibrationXmlParser.ParseFailedException.class, Loading
core/xsd/vibrator/vibration/schema/current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -15,12 +15,20 @@ package com.android.internal.vibrator.persistence { enum_constant public static final com.android.internal.vibrator.persistence.PredefinedEffectName tick; } public enum PrimitiveDelayType { method public String getRawName(); enum_constant public static final com.android.internal.vibrator.persistence.PrimitiveDelayType pause; enum_constant public static final com.android.internal.vibrator.persistence.PrimitiveDelayType relative_start_offset; } public class PrimitiveEffect { ctor public PrimitiveEffect(); method public java.math.BigInteger getDelayMs(); method public com.android.internal.vibrator.persistence.PrimitiveDelayType getDelayType(); method public com.android.internal.vibrator.persistence.PrimitiveEffectName getName(); method public float getScale(); method public void setDelayMs(java.math.BigInteger); method public void setDelayType(com.android.internal.vibrator.persistence.PrimitiveDelayType); method public void setName(com.android.internal.vibrator.persistence.PrimitiveEffectName); method public void setScale(float); } Loading