Loading core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java +9 −2 Original line number Diff line number Diff line Loading @@ -22,7 +22,8 @@ import android.annotation.TestApi; import android.os.VibrationEffect; import android.util.Xml; import com.android.internal.vibrator.persistence.VibrationEffectXmlSerializer; import com.android.internal.vibrator.persistence.LegacyVibrationEffectXmlSerializer; import com.android.internal.vibrator.persistence.VibrationEffectSerializer; import com.android.internal.vibrator.persistence.XmlConstants; import com.android.internal.vibrator.persistence.XmlSerializedVibration; import com.android.internal.vibrator.persistence.XmlSerializerException; Loading Loading @@ -123,7 +124,13 @@ public final class VibrationXmlSerializer { } try { serializedVibration = VibrationEffectXmlSerializer.serialize(effect, serializerFlags); if (android.os.vibrator.Flags.normalizedPwleEffects()) { serializedVibration = VibrationEffectSerializer.serialize(effect, serializerFlags); } else { serializedVibration = LegacyVibrationEffectXmlSerializer.serialize(effect, serializerFlags); } XmlValidator.checkSerializedVibration(serializedVibration, effect); } catch (XmlSerializerException e) { // Serialization failed or created incomplete representation, fail before writing. Loading core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java→core/java/com/android/internal/vibrator/persistence/LegacyVibrationEffectXmlSerializer.java +1 −1 Original line number Diff line number Diff line Loading @@ -53,7 +53,7 @@ import java.util.List; * * @hide */ public final class VibrationEffectXmlSerializer { public final class LegacyVibrationEffectXmlSerializer { /** * Creates a serialized representation of the input {@code vibration}. Loading core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java +22 −21 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import com.android.modules.utils.TypedXmlSerializer; import java.io.IOException; import java.util.Arrays; import java.util.function.BiConsumer; /** * Serialized representation of a waveform effect created via Loading Loading @@ -144,7 +145,7 @@ final class SerializedAmplitudeStepWaveform implements SerializedSegment { // Read all nested tag that is not a repeating tag as a waveform entry. while (XmlReader.readNextTagWithin(parser, outerDepth) && !TAG_REPEATING.equals(parser.getName())) { parseWaveformEntry(parser, waveformBuilder); parseWaveformEntry(parser, waveformBuilder::addDurationAndAmplitude); } // If found a repeating tag, read its content. Loading @@ -162,6 +163,25 @@ final class SerializedAmplitudeStepWaveform implements SerializedSegment { return waveformBuilder.build(); } static void parseWaveformEntry(TypedXmlPullParser parser, BiConsumer<Integer, Integer> builder) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_WAVEFORM_ENTRY); XmlValidator.checkTagHasNoUnexpectedAttributes( parser, ATTRIBUTE_DURATION_MS, ATTRIBUTE_AMPLITUDE); String rawAmplitude = parser.getAttributeValue(NAMESPACE, ATTRIBUTE_AMPLITUDE); int amplitude = VALUE_AMPLITUDE_DEFAULT.equals(rawAmplitude) ? VibrationEffect.DEFAULT_AMPLITUDE : XmlReader.readAttributeIntInRange( parser, ATTRIBUTE_AMPLITUDE, 0, VibrationEffect.MAX_AMPLITUDE); int durationMs = XmlReader.readAttributeIntNonNegative(parser, ATTRIBUTE_DURATION_MS); builder.accept(durationMs, amplitude); // Consume tag XmlReader.readEndTag(parser); } private static void parseRepeating(TypedXmlPullParser parser, Builder waveformBuilder) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_REPEATING); Loading @@ -172,7 +192,7 @@ final class SerializedAmplitudeStepWaveform implements SerializedSegment { boolean hasEntry = false; int outerDepth = parser.getDepth(); while (XmlReader.readNextTagWithin(parser, outerDepth)) { parseWaveformEntry(parser, waveformBuilder); parseWaveformEntry(parser, waveformBuilder::addDurationAndAmplitude); hasEntry = true; } Loading @@ -182,24 +202,5 @@ final class SerializedAmplitudeStepWaveform implements SerializedSegment { // Consume tag XmlReader.readEndTag(parser, TAG_REPEATING, outerDepth); } private static void parseWaveformEntry(TypedXmlPullParser parser, Builder waveformBuilder) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_WAVEFORM_ENTRY); XmlValidator.checkTagHasNoUnexpectedAttributes( parser, ATTRIBUTE_DURATION_MS, ATTRIBUTE_AMPLITUDE); String rawAmplitude = parser.getAttributeValue(NAMESPACE, ATTRIBUTE_AMPLITUDE); int amplitude = VALUE_AMPLITUDE_DEFAULT.equals(rawAmplitude) ? VibrationEffect.DEFAULT_AMPLITUDE : XmlReader.readAttributeIntInRange( parser, ATTRIBUTE_AMPLITUDE, 0, VibrationEffect.MAX_AMPLITUDE); int durationMs = XmlReader.readAttributeIntNonNegative(parser, ATTRIBUTE_DURATION_MS); waveformBuilder.addDurationAndAmplitude(durationMs, amplitude); // Consume tag XmlReader.readEndTag(parser); } } } core/java/com/android/internal/vibrator/persistence/SerializedRepeatingEffect.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.vibrator.persistence; import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_BASIC_ENVELOPE_EFFECT; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREAMBLE; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREDEFINED_EFFECT; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITIVE_EFFECT; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_REPEATING; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_REPEATING_EFFECT; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENTRY; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENVELOPE_EFFECT; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.VibrationEffect; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Serialized representation of a repeating effect created via * {@link VibrationEffect#createRepeatingEffect}. * * @hide */ public class SerializedRepeatingEffect implements SerializedComposedEffect.SerializedSegment { @Nullable private final SerializedComposedEffect mSerializedPreamble; @NonNull private final SerializedComposedEffect mSerializedRepeating; SerializedRepeatingEffect(@Nullable SerializedComposedEffect serializedPreamble, @NonNull SerializedComposedEffect serializedRepeating) { mSerializedPreamble = serializedPreamble; mSerializedRepeating = serializedRepeating; } @Override public void write(@NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(NAMESPACE, TAG_REPEATING_EFFECT); if (mSerializedPreamble != null) { serializer.startTag(NAMESPACE, TAG_PREAMBLE); mSerializedPreamble.writeContent(serializer); serializer.endTag(NAMESPACE, TAG_PREAMBLE); } serializer.startTag(NAMESPACE, TAG_REPEATING); mSerializedRepeating.writeContent(serializer); serializer.endTag(NAMESPACE, TAG_REPEATING); serializer.endTag(NAMESPACE, TAG_REPEATING_EFFECT); } @Override public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) { if (mSerializedPreamble != null) { composition.addEffect( VibrationEffect.createRepeatingEffect(mSerializedPreamble.deserialize(), mSerializedRepeating.deserialize())); return; } composition.addEffect( VibrationEffect.createRepeatingEffect(mSerializedRepeating.deserialize())); } @Override public String toString() { return "SerializedRepeatingEffect{" + "preamble=" + mSerializedPreamble + ", repeating=" + mSerializedRepeating + '}'; } static final class Builder { private SerializedComposedEffect mPreamble; private SerializedComposedEffect mRepeating; void setPreamble(SerializedComposedEffect effect) { mPreamble = effect; } void setRepeating(SerializedComposedEffect effect) { mRepeating = effect; } boolean hasRepeatingSegment() { return mRepeating != null; } SerializedRepeatingEffect build() { return new SerializedRepeatingEffect(mPreamble, mRepeating); } } /** Parser implementation for {@link SerializedRepeatingEffect}. */ static final class Parser { @NonNull static SerializedRepeatingEffect parseNext(@NonNull TypedXmlPullParser parser, @XmlConstants.Flags int flags) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_REPEATING_EFFECT); XmlValidator.checkTagHasNoUnexpectedAttributes(parser); Builder builder = new Builder(); int outerDepth = parser.getDepth(); boolean hasNestedTag = XmlReader.readNextTagWithin(parser, outerDepth); if (hasNestedTag && TAG_PREAMBLE.equals(parser.getName())) { builder.setPreamble(parseEffect(parser, TAG_PREAMBLE, flags)); hasNestedTag = XmlReader.readNextTagWithin(parser, outerDepth); } XmlValidator.checkParserCondition(hasNestedTag, "Missing %s tag in %s", TAG_REPEATING, TAG_REPEATING_EFFECT); builder.setRepeating(parseEffect(parser, TAG_REPEATING, flags)); XmlValidator.checkParserCondition(builder.hasRepeatingSegment(), "Unexpected %s tag with no repeating segment", TAG_REPEATING_EFFECT); // Consume tag XmlReader.readEndTag(parser, TAG_REPEATING_EFFECT, outerDepth); return builder.build(); } private static SerializedComposedEffect parseEffect(TypedXmlPullParser parser, String tagName, int flags) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, tagName); XmlValidator.checkTagHasNoUnexpectedAttributes(parser); int vibrationTagDepth = parser.getDepth(); XmlValidator.checkParserCondition( XmlReader.readNextTagWithin(parser, vibrationTagDepth), "Unsupported empty %s tag", tagName); SerializedComposedEffect effect; switch (parser.getName()) { case TAG_PREDEFINED_EFFECT: effect = new SerializedComposedEffect( SerializedPredefinedEffect.Parser.parseNext(parser, flags)); break; case TAG_PRIMITIVE_EFFECT: effect = parsePrimitiveEffects(parser, vibrationTagDepth); break; case TAG_WAVEFORM_ENTRY: effect = parseWaveformEntries(parser, vibrationTagDepth); break; case TAG_WAVEFORM_ENVELOPE_EFFECT: effect = new SerializedComposedEffect( SerializedWaveformEnvelopeEffect.Parser.parseNext(parser, flags)); break; case TAG_BASIC_ENVELOPE_EFFECT: effect = new SerializedComposedEffect( SerializedBasicEnvelopeEffect.Parser.parseNext(parser, flags)); break; default: throw new XmlParserException("Unexpected tag " + parser.getName() + " in vibration tag " + tagName); } // Consume tag XmlReader.readEndTag(parser, tagName, vibrationTagDepth); return effect; } private static SerializedComposedEffect parsePrimitiveEffects(TypedXmlPullParser parser, int vibrationTagDepth) throws IOException, XmlParserException { List<SerializedComposedEffect.SerializedSegment> primitives = new ArrayList<>(); do { // First primitive tag already open primitives.add(SerializedCompositionPrimitive.Parser.parseNext(parser)); } while (XmlReader.readNextTagWithin(parser, vibrationTagDepth)); return new SerializedComposedEffect(primitives.toArray( new SerializedComposedEffect.SerializedSegment[ primitives.size()])); } private static SerializedComposedEffect parseWaveformEntries(TypedXmlPullParser parser, int vibrationTagDepth) throws IOException, XmlParserException { SerializedWaveformEffectEntries.Builder waveformBuilder = new SerializedWaveformEffectEntries.Builder(); do { // First waveform-entry tag already open SerializedWaveformEffectEntries .Parser.parseWaveformEntry(parser, waveformBuilder); } while (XmlReader.readNextTagWithin(parser, vibrationTagDepth)); XmlValidator.checkParserCondition(waveformBuilder.hasNonZeroDuration(), "Unexpected %s tag with total duration zero", TAG_WAVEFORM_ENTRY); return new SerializedComposedEffect(waveformBuilder.build()); } } } core/java/com/android/internal/vibrator/persistence/SerializedWaveformEffectEntries.java 0 → 100644 +121 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.vibrator.persistence; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_AMPLITUDE; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DURATION_MS; import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENTRY; import static com.android.internal.vibrator.persistence.XmlConstants.VALUE_AMPLITUDE_DEFAULT; import android.annotation.NonNull; import android.os.VibrationEffect; import android.util.IntArray; import android.util.LongArray; import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import java.io.IOException; import java.util.Arrays; /** * Serialized representation of a list of waveform entries created via * {@link VibrationEffect#createWaveform(long[], int[], int)}. * * @hide */ final class SerializedWaveformEffectEntries implements SerializedSegment { @NonNull private final long[] mTimings; @NonNull private final int[] mAmplitudes; private SerializedWaveformEffectEntries(@NonNull long[] timings, @NonNull int[] amplitudes) { mTimings = timings; mAmplitudes = amplitudes; } @Override public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) { composition.addEffect(VibrationEffect.createWaveform(mTimings, mAmplitudes, -1)); } @Override public void write(@NonNull TypedXmlSerializer serializer) throws IOException { for (int i = 0; i < mTimings.length; i++) { serializer.startTag(NAMESPACE, TAG_WAVEFORM_ENTRY); if (mAmplitudes[i] == VibrationEffect.DEFAULT_AMPLITUDE) { serializer.attribute(NAMESPACE, ATTRIBUTE_AMPLITUDE, VALUE_AMPLITUDE_DEFAULT); } else { serializer.attributeInt(NAMESPACE, ATTRIBUTE_AMPLITUDE, mAmplitudes[i]); } serializer.attributeLong(NAMESPACE, ATTRIBUTE_DURATION_MS, mTimings[i]); serializer.endTag(NAMESPACE, TAG_WAVEFORM_ENTRY); } } @Override public String toString() { return "SerializedWaveformEffectEntries{" + "timings=" + Arrays.toString(mTimings) + ", amplitudes=" + Arrays.toString(mAmplitudes) + '}'; } /** Builder for {@link SerializedWaveformEffectEntries}. */ static final class Builder { private final LongArray mTimings = new LongArray(); private final IntArray mAmplitudes = new IntArray(); void addDurationAndAmplitude(long durationMs, int amplitude) { mTimings.add(durationMs); mAmplitudes.add(amplitude); } boolean hasNonZeroDuration() { for (int i = 0; i < mTimings.size(); i++) { if (mTimings.get(i) > 0) { return true; } } return false; } SerializedWaveformEffectEntries build() { return new SerializedWaveformEffectEntries( mTimings.toArray(), mAmplitudes.toArray()); } } /** Parser implementation for the {@link XmlConstants#TAG_WAVEFORM_ENTRY}. */ static final class Parser { /** Parses a single {@link XmlConstants#TAG_WAVEFORM_ENTRY} into the builder. */ public static void parseWaveformEntry(TypedXmlPullParser parser, Builder waveformBuilder) throws XmlParserException, IOException { SerializedAmplitudeStepWaveform.Parser.parseWaveformEntry(parser, waveformBuilder::addDurationAndAmplitude); } } } Loading
core/java/android/os/vibrator/persistence/VibrationXmlSerializer.java +9 −2 Original line number Diff line number Diff line Loading @@ -22,7 +22,8 @@ import android.annotation.TestApi; import android.os.VibrationEffect; import android.util.Xml; import com.android.internal.vibrator.persistence.VibrationEffectXmlSerializer; import com.android.internal.vibrator.persistence.LegacyVibrationEffectXmlSerializer; import com.android.internal.vibrator.persistence.VibrationEffectSerializer; import com.android.internal.vibrator.persistence.XmlConstants; import com.android.internal.vibrator.persistence.XmlSerializedVibration; import com.android.internal.vibrator.persistence.XmlSerializerException; Loading Loading @@ -123,7 +124,13 @@ public final class VibrationXmlSerializer { } try { serializedVibration = VibrationEffectXmlSerializer.serialize(effect, serializerFlags); if (android.os.vibrator.Flags.normalizedPwleEffects()) { serializedVibration = VibrationEffectSerializer.serialize(effect, serializerFlags); } else { serializedVibration = LegacyVibrationEffectXmlSerializer.serialize(effect, serializerFlags); } XmlValidator.checkSerializedVibration(serializedVibration, effect); } catch (XmlSerializerException e) { // Serialization failed or created incomplete representation, fail before writing. Loading
core/java/com/android/internal/vibrator/persistence/VibrationEffectXmlSerializer.java→core/java/com/android/internal/vibrator/persistence/LegacyVibrationEffectXmlSerializer.java +1 −1 Original line number Diff line number Diff line Loading @@ -53,7 +53,7 @@ import java.util.List; * * @hide */ public final class VibrationEffectXmlSerializer { public final class LegacyVibrationEffectXmlSerializer { /** * Creates a serialized representation of the input {@code vibration}. Loading
core/java/com/android/internal/vibrator/persistence/SerializedAmplitudeStepWaveform.java +22 −21 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import com.android.modules.utils.TypedXmlSerializer; import java.io.IOException; import java.util.Arrays; import java.util.function.BiConsumer; /** * Serialized representation of a waveform effect created via Loading Loading @@ -144,7 +145,7 @@ final class SerializedAmplitudeStepWaveform implements SerializedSegment { // Read all nested tag that is not a repeating tag as a waveform entry. while (XmlReader.readNextTagWithin(parser, outerDepth) && !TAG_REPEATING.equals(parser.getName())) { parseWaveformEntry(parser, waveformBuilder); parseWaveformEntry(parser, waveformBuilder::addDurationAndAmplitude); } // If found a repeating tag, read its content. Loading @@ -162,6 +163,25 @@ final class SerializedAmplitudeStepWaveform implements SerializedSegment { return waveformBuilder.build(); } static void parseWaveformEntry(TypedXmlPullParser parser, BiConsumer<Integer, Integer> builder) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_WAVEFORM_ENTRY); XmlValidator.checkTagHasNoUnexpectedAttributes( parser, ATTRIBUTE_DURATION_MS, ATTRIBUTE_AMPLITUDE); String rawAmplitude = parser.getAttributeValue(NAMESPACE, ATTRIBUTE_AMPLITUDE); int amplitude = VALUE_AMPLITUDE_DEFAULT.equals(rawAmplitude) ? VibrationEffect.DEFAULT_AMPLITUDE : XmlReader.readAttributeIntInRange( parser, ATTRIBUTE_AMPLITUDE, 0, VibrationEffect.MAX_AMPLITUDE); int durationMs = XmlReader.readAttributeIntNonNegative(parser, ATTRIBUTE_DURATION_MS); builder.accept(durationMs, amplitude); // Consume tag XmlReader.readEndTag(parser); } private static void parseRepeating(TypedXmlPullParser parser, Builder waveformBuilder) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_REPEATING); Loading @@ -172,7 +192,7 @@ final class SerializedAmplitudeStepWaveform implements SerializedSegment { boolean hasEntry = false; int outerDepth = parser.getDepth(); while (XmlReader.readNextTagWithin(parser, outerDepth)) { parseWaveformEntry(parser, waveformBuilder); parseWaveformEntry(parser, waveformBuilder::addDurationAndAmplitude); hasEntry = true; } Loading @@ -182,24 +202,5 @@ final class SerializedAmplitudeStepWaveform implements SerializedSegment { // Consume tag XmlReader.readEndTag(parser, TAG_REPEATING, outerDepth); } private static void parseWaveformEntry(TypedXmlPullParser parser, Builder waveformBuilder) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_WAVEFORM_ENTRY); XmlValidator.checkTagHasNoUnexpectedAttributes( parser, ATTRIBUTE_DURATION_MS, ATTRIBUTE_AMPLITUDE); String rawAmplitude = parser.getAttributeValue(NAMESPACE, ATTRIBUTE_AMPLITUDE); int amplitude = VALUE_AMPLITUDE_DEFAULT.equals(rawAmplitude) ? VibrationEffect.DEFAULT_AMPLITUDE : XmlReader.readAttributeIntInRange( parser, ATTRIBUTE_AMPLITUDE, 0, VibrationEffect.MAX_AMPLITUDE); int durationMs = XmlReader.readAttributeIntNonNegative(parser, ATTRIBUTE_DURATION_MS); waveformBuilder.addDurationAndAmplitude(durationMs, amplitude); // Consume tag XmlReader.readEndTag(parser); } } }
core/java/com/android/internal/vibrator/persistence/SerializedRepeatingEffect.java 0 → 100644 +215 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.vibrator.persistence; import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_BASIC_ENVELOPE_EFFECT; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREAMBLE; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PREDEFINED_EFFECT; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_PRIMITIVE_EFFECT; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_REPEATING; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_REPEATING_EFFECT; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENTRY; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENVELOPE_EFFECT; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.VibrationEffect; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Serialized representation of a repeating effect created via * {@link VibrationEffect#createRepeatingEffect}. * * @hide */ public class SerializedRepeatingEffect implements SerializedComposedEffect.SerializedSegment { @Nullable private final SerializedComposedEffect mSerializedPreamble; @NonNull private final SerializedComposedEffect mSerializedRepeating; SerializedRepeatingEffect(@Nullable SerializedComposedEffect serializedPreamble, @NonNull SerializedComposedEffect serializedRepeating) { mSerializedPreamble = serializedPreamble; mSerializedRepeating = serializedRepeating; } @Override public void write(@NonNull TypedXmlSerializer serializer) throws IOException { serializer.startTag(NAMESPACE, TAG_REPEATING_EFFECT); if (mSerializedPreamble != null) { serializer.startTag(NAMESPACE, TAG_PREAMBLE); mSerializedPreamble.writeContent(serializer); serializer.endTag(NAMESPACE, TAG_PREAMBLE); } serializer.startTag(NAMESPACE, TAG_REPEATING); mSerializedRepeating.writeContent(serializer); serializer.endTag(NAMESPACE, TAG_REPEATING); serializer.endTag(NAMESPACE, TAG_REPEATING_EFFECT); } @Override public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) { if (mSerializedPreamble != null) { composition.addEffect( VibrationEffect.createRepeatingEffect(mSerializedPreamble.deserialize(), mSerializedRepeating.deserialize())); return; } composition.addEffect( VibrationEffect.createRepeatingEffect(mSerializedRepeating.deserialize())); } @Override public String toString() { return "SerializedRepeatingEffect{" + "preamble=" + mSerializedPreamble + ", repeating=" + mSerializedRepeating + '}'; } static final class Builder { private SerializedComposedEffect mPreamble; private SerializedComposedEffect mRepeating; void setPreamble(SerializedComposedEffect effect) { mPreamble = effect; } void setRepeating(SerializedComposedEffect effect) { mRepeating = effect; } boolean hasRepeatingSegment() { return mRepeating != null; } SerializedRepeatingEffect build() { return new SerializedRepeatingEffect(mPreamble, mRepeating); } } /** Parser implementation for {@link SerializedRepeatingEffect}. */ static final class Parser { @NonNull static SerializedRepeatingEffect parseNext(@NonNull TypedXmlPullParser parser, @XmlConstants.Flags int flags) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, TAG_REPEATING_EFFECT); XmlValidator.checkTagHasNoUnexpectedAttributes(parser); Builder builder = new Builder(); int outerDepth = parser.getDepth(); boolean hasNestedTag = XmlReader.readNextTagWithin(parser, outerDepth); if (hasNestedTag && TAG_PREAMBLE.equals(parser.getName())) { builder.setPreamble(parseEffect(parser, TAG_PREAMBLE, flags)); hasNestedTag = XmlReader.readNextTagWithin(parser, outerDepth); } XmlValidator.checkParserCondition(hasNestedTag, "Missing %s tag in %s", TAG_REPEATING, TAG_REPEATING_EFFECT); builder.setRepeating(parseEffect(parser, TAG_REPEATING, flags)); XmlValidator.checkParserCondition(builder.hasRepeatingSegment(), "Unexpected %s tag with no repeating segment", TAG_REPEATING_EFFECT); // Consume tag XmlReader.readEndTag(parser, TAG_REPEATING_EFFECT, outerDepth); return builder.build(); } private static SerializedComposedEffect parseEffect(TypedXmlPullParser parser, String tagName, int flags) throws XmlParserException, IOException { XmlValidator.checkStartTag(parser, tagName); XmlValidator.checkTagHasNoUnexpectedAttributes(parser); int vibrationTagDepth = parser.getDepth(); XmlValidator.checkParserCondition( XmlReader.readNextTagWithin(parser, vibrationTagDepth), "Unsupported empty %s tag", tagName); SerializedComposedEffect effect; switch (parser.getName()) { case TAG_PREDEFINED_EFFECT: effect = new SerializedComposedEffect( SerializedPredefinedEffect.Parser.parseNext(parser, flags)); break; case TAG_PRIMITIVE_EFFECT: effect = parsePrimitiveEffects(parser, vibrationTagDepth); break; case TAG_WAVEFORM_ENTRY: effect = parseWaveformEntries(parser, vibrationTagDepth); break; case TAG_WAVEFORM_ENVELOPE_EFFECT: effect = new SerializedComposedEffect( SerializedWaveformEnvelopeEffect.Parser.parseNext(parser, flags)); break; case TAG_BASIC_ENVELOPE_EFFECT: effect = new SerializedComposedEffect( SerializedBasicEnvelopeEffect.Parser.parseNext(parser, flags)); break; default: throw new XmlParserException("Unexpected tag " + parser.getName() + " in vibration tag " + tagName); } // Consume tag XmlReader.readEndTag(parser, tagName, vibrationTagDepth); return effect; } private static SerializedComposedEffect parsePrimitiveEffects(TypedXmlPullParser parser, int vibrationTagDepth) throws IOException, XmlParserException { List<SerializedComposedEffect.SerializedSegment> primitives = new ArrayList<>(); do { // First primitive tag already open primitives.add(SerializedCompositionPrimitive.Parser.parseNext(parser)); } while (XmlReader.readNextTagWithin(parser, vibrationTagDepth)); return new SerializedComposedEffect(primitives.toArray( new SerializedComposedEffect.SerializedSegment[ primitives.size()])); } private static SerializedComposedEffect parseWaveformEntries(TypedXmlPullParser parser, int vibrationTagDepth) throws IOException, XmlParserException { SerializedWaveformEffectEntries.Builder waveformBuilder = new SerializedWaveformEffectEntries.Builder(); do { // First waveform-entry tag already open SerializedWaveformEffectEntries .Parser.parseWaveformEntry(parser, waveformBuilder); } while (XmlReader.readNextTagWithin(parser, vibrationTagDepth)); XmlValidator.checkParserCondition(waveformBuilder.hasNonZeroDuration(), "Unexpected %s tag with total duration zero", TAG_WAVEFORM_ENTRY); return new SerializedComposedEffect(waveformBuilder.build()); } } }
core/java/com/android/internal/vibrator/persistence/SerializedWaveformEffectEntries.java 0 → 100644 +121 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.vibrator.persistence; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_AMPLITUDE; import static com.android.internal.vibrator.persistence.XmlConstants.ATTRIBUTE_DURATION_MS; import static com.android.internal.vibrator.persistence.XmlConstants.NAMESPACE; import static com.android.internal.vibrator.persistence.XmlConstants.TAG_WAVEFORM_ENTRY; import static com.android.internal.vibrator.persistence.XmlConstants.VALUE_AMPLITUDE_DEFAULT; import android.annotation.NonNull; import android.os.VibrationEffect; import android.util.IntArray; import android.util.LongArray; import com.android.internal.vibrator.persistence.SerializedComposedEffect.SerializedSegment; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import java.io.IOException; import java.util.Arrays; /** * Serialized representation of a list of waveform entries created via * {@link VibrationEffect#createWaveform(long[], int[], int)}. * * @hide */ final class SerializedWaveformEffectEntries implements SerializedSegment { @NonNull private final long[] mTimings; @NonNull private final int[] mAmplitudes; private SerializedWaveformEffectEntries(@NonNull long[] timings, @NonNull int[] amplitudes) { mTimings = timings; mAmplitudes = amplitudes; } @Override public void deserializeIntoComposition(@NonNull VibrationEffect.Composition composition) { composition.addEffect(VibrationEffect.createWaveform(mTimings, mAmplitudes, -1)); } @Override public void write(@NonNull TypedXmlSerializer serializer) throws IOException { for (int i = 0; i < mTimings.length; i++) { serializer.startTag(NAMESPACE, TAG_WAVEFORM_ENTRY); if (mAmplitudes[i] == VibrationEffect.DEFAULT_AMPLITUDE) { serializer.attribute(NAMESPACE, ATTRIBUTE_AMPLITUDE, VALUE_AMPLITUDE_DEFAULT); } else { serializer.attributeInt(NAMESPACE, ATTRIBUTE_AMPLITUDE, mAmplitudes[i]); } serializer.attributeLong(NAMESPACE, ATTRIBUTE_DURATION_MS, mTimings[i]); serializer.endTag(NAMESPACE, TAG_WAVEFORM_ENTRY); } } @Override public String toString() { return "SerializedWaveformEffectEntries{" + "timings=" + Arrays.toString(mTimings) + ", amplitudes=" + Arrays.toString(mAmplitudes) + '}'; } /** Builder for {@link SerializedWaveformEffectEntries}. */ static final class Builder { private final LongArray mTimings = new LongArray(); private final IntArray mAmplitudes = new IntArray(); void addDurationAndAmplitude(long durationMs, int amplitude) { mTimings.add(durationMs); mAmplitudes.add(amplitude); } boolean hasNonZeroDuration() { for (int i = 0; i < mTimings.size(); i++) { if (mTimings.get(i) > 0) { return true; } } return false; } SerializedWaveformEffectEntries build() { return new SerializedWaveformEffectEntries( mTimings.toArray(), mAmplitudes.toArray()); } } /** Parser implementation for the {@link XmlConstants#TAG_WAVEFORM_ENTRY}. */ static final class Parser { /** Parses a single {@link XmlConstants#TAG_WAVEFORM_ENTRY} into the builder. */ public static void parseWaveformEntry(TypedXmlPullParser parser, Builder waveformBuilder) throws XmlParserException, IOException { SerializedAmplitudeStepWaveform.Parser.parseWaveformEntry(parser, waveformBuilder::addDurationAndAmplitude); } } }