APIs for Visibility triggers and setting trigger to Lottie
Adding Builders for OnVisible and OnVisibleOnce triggers. AVDs already
support this on the renderer side.
Added API for Lottie to be able to set trigger (load, visibile, visible
once). Renderer implementation will follow.
Fixes: 390346039
Test: Added
Relnote: "Added API option to set different triggers for Lottie animations. Additionally, added API for triggers fired when layout is visible"
Change-Id: I8272de3ae3986ea04debedec2820496fe19e062e
diff --git a/wear/protolayout/protolayout-proto/src/main/proto/resources.proto b/wear/protolayout/protolayout-proto/src/main/proto/resources.proto
index 7e04e8a..edfbff0 100644
--- a/wear/protolayout/protolayout-proto/src/main/proto/resources.proto
+++ b/wear/protolayout/protolayout-proto/src/main/proto/resources.proto
@@ -155,6 +155,9 @@
//
// If not set, the animation will play on load.</setter>
androidx.wear.protolayout.expression.proto.DynamicFloat progress = 2;
+
+ // The trigger to start the animation.
+ Trigger start_trigger = 3;
}
// An image resource, which can be used by layouts. This holds multiple
diff --git a/wear/protolayout/protolayout/api/current.txt b/wear/protolayout/protolayout/api/current.txt
index 21886f5..ba2171c 100644
--- a/wear/protolayout/protolayout/api/current.txt
+++ b/wear/protolayout/protolayout/api/current.txt
@@ -1309,12 +1309,14 @@
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class ResourceBuilders.AndroidLottieResourceByResId {
method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
method @RawRes public int getRawResourceId();
+ method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
}
public static final class ResourceBuilders.AndroidLottieResourceByResId.Builder {
ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public ResourceBuilders.AndroidLottieResourceByResId.Builder(@RawRes int);
method public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId build();
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId.Builder setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId.Builder setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger);
}
@SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
@@ -1434,6 +1436,24 @@
public final class TriggerBuilders {
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnLoadTrigger();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleOnceTrigger();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleTrigger();
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleOnceTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ }
+
+ public static final class TriggerBuilders.OnVisibleOnceTrigger.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public TriggerBuilders.OnVisibleOnceTrigger.Builder();
+ method public androidx.wear.protolayout.TriggerBuilders.OnVisibleOnceTrigger build();
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ }
+
+ public static final class TriggerBuilders.OnVisibleTrigger.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public TriggerBuilders.OnVisibleTrigger.Builder();
+ method public androidx.wear.protolayout.TriggerBuilders.OnVisibleTrigger build();
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static interface TriggerBuilders.Trigger {
diff --git a/wear/protolayout/protolayout/api/restricted_current.txt b/wear/protolayout/protolayout/api/restricted_current.txt
index 21886f5..ba2171c 100644
--- a/wear/protolayout/protolayout/api/restricted_current.txt
+++ b/wear/protolayout/protolayout/api/restricted_current.txt
@@ -1309,12 +1309,14 @@
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public static final class ResourceBuilders.AndroidLottieResourceByResId {
method public androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat? getProgress();
method @RawRes public int getRawResourceId();
+ method public androidx.wear.protolayout.TriggerBuilders.Trigger? getStartTrigger();
}
public static final class ResourceBuilders.AndroidLottieResourceByResId.Builder {
ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public ResourceBuilders.AndroidLottieResourceByResId.Builder(@RawRes int);
method public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId build();
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId.Builder setProgress(androidx.wear.protolayout.expression.DynamicBuilders.DynamicFloat);
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=500) public androidx.wear.protolayout.ResourceBuilders.AndroidLottieResourceByResId.Builder setStartTrigger(androidx.wear.protolayout.TriggerBuilders.Trigger);
}
@SuppressCompatibility @androidx.wear.protolayout.expression.ProtoLayoutExperimental @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class ResourceBuilders.AndroidSeekableAnimatedImageResourceByResId {
@@ -1434,6 +1436,24 @@
public final class TriggerBuilders {
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnConditionMetTrigger(androidx.wear.protolayout.expression.DynamicBuilders.DynamicBool);
method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnLoadTrigger();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleOnceTrigger();
+ method @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static androidx.wear.protolayout.TriggerBuilders.Trigger createOnVisibleTrigger();
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleOnceTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ }
+
+ public static final class TriggerBuilders.OnVisibleOnceTrigger.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public TriggerBuilders.OnVisibleOnceTrigger.Builder();
+ method public androidx.wear.protolayout.TriggerBuilders.OnVisibleOnceTrigger build();
+ }
+
+ @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static final class TriggerBuilders.OnVisibleTrigger implements androidx.wear.protolayout.TriggerBuilders.Trigger {
+ }
+
+ public static final class TriggerBuilders.OnVisibleTrigger.Builder {
+ ctor @androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public TriggerBuilders.OnVisibleTrigger.Builder();
+ method public androidx.wear.protolayout.TriggerBuilders.OnVisibleTrigger build();
}
@androidx.wear.protolayout.expression.RequiresSchemaVersion(major=1, minor=200) public static interface TriggerBuilders.Trigger {
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
index 0fff0a2..a51c996 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/ResourceBuilders.java
@@ -520,6 +520,15 @@
}
}
+ /** Gets the trigger to start the animation. */
+ public @Nullable Trigger getStartTrigger() {
+ if (mImpl.hasStartTrigger()) {
+ return TriggerBuilders.triggerFromProto(mImpl.getStartTrigger());
+ } else {
+ return null;
+ }
+ }
+
/** Creates a new wrapper instance from the proto. */
@RestrictTo(Scope.LIBRARY_GROUP)
public static @NonNull AndroidLottieResourceByResId fromProto(
@@ -540,6 +549,8 @@
+ getRawResourceId()
+ ", progress="
+ getProgress()
+ + ", startTrigger="
+ + getStartTrigger()
+ "}";
}
@@ -590,6 +601,13 @@
return this;
}
+ /** Sets the trigger to start the animation. */
+ @RequiresSchemaVersion(major = 1, minor = 500)
+ public @NonNull Builder setStartTrigger(@NonNull Trigger startTrigger) {
+ mImpl.setStartTrigger(startTrigger.toTriggerProto());
+ return this;
+ }
+
/** Builds an instance from accumulated values. */
public @NonNull AndroidLottieResourceByResId build() {
return AndroidLottieResourceByResId.fromProto(mImpl.build());
diff --git a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
index 1a96d0e..d05a53c 100644
--- a/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
+++ b/wear/protolayout/protolayout/src/main/java/androidx/wear/protolayout/TriggerBuilders.java
@@ -48,6 +48,160 @@
return new OnConditionMetTrigger.Builder().setCondition(dynamicBool).build();
}
+ /**
+ * Creates a {@link Trigger} that fires *every time* the layout becomes visible.
+ *
+ * <p>As opposed to {@link #createOnLoadTrigger()}, this will wait until layout is fully visible
+ * before firing a trigger.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public static @NonNull Trigger createOnVisibleTrigger() {
+ return new OnVisibleTrigger.Builder().build();
+ }
+
+ /**
+ * Creates a {@link Trigger} that fires the first time that layout becomes visible.
+ *
+ * <p>As opposed to {@link #createOnVisibleTrigger()}, this will only be fired the first time
+ * that the layout becomes visible.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public static @NonNull Trigger createOnVisibleOnceTrigger() {
+ return new OnVisibleOnceTrigger.Builder().build();
+ }
+
+ /** Triggers when the layout visibility state turns from invisible to fully visible. */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public static final class OnVisibleTrigger implements Trigger {
+ private final TriggerProto.OnVisibleTrigger mImpl;
+ private final @Nullable Fingerprint mFingerprint;
+
+ OnVisibleTrigger(TriggerProto.OnVisibleTrigger impl, @Nullable Fingerprint fingerprint) {
+ this.mImpl = impl;
+ this.mFingerprint = fingerprint;
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public @Nullable Fingerprint getFingerprint() {
+ return mFingerprint;
+ }
+
+ /** Creates a new wrapper instance from the proto. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public static @NonNull OnVisibleTrigger fromProto(
+ TriggerProto.@NonNull OnVisibleTrigger proto, @Nullable Fingerprint fingerprint) {
+ return new OnVisibleTrigger(proto, fingerprint);
+ }
+
+ static @NonNull OnVisibleTrigger fromProto(TriggerProto.@NonNull OnVisibleTrigger proto) {
+ return fromProto(proto, null);
+ }
+
+ /** Returns the internal proto instance. */
+ TriggerProto.@NonNull OnVisibleTrigger toProto() {
+ return mImpl;
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public TriggerProto.@NonNull Trigger toTriggerProto() {
+ return TriggerProto.Trigger.newBuilder().setOnVisibleTrigger(mImpl).build();
+ }
+
+ @Override
+ public @NonNull String toString() {
+ return "OnVisibleTrigger";
+ }
+
+ /** Builder for {@link OnVisibleTrigger}. */
+ @SuppressWarnings("HiddenSuperclass")
+ public static final class Builder implements Trigger.Builder {
+ private final TriggerProto.OnVisibleTrigger.Builder mImpl =
+ TriggerProto.OnVisibleTrigger.newBuilder();
+ private final Fingerprint mFingerprint = new Fingerprint(1416366796);
+
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public Builder() {}
+
+ /** Builds an instance from accumulated values. */
+ @Override
+ public @NonNull OnVisibleTrigger build() {
+ return new OnVisibleTrigger(mImpl.build(), mFingerprint);
+ }
+ }
+ }
+
+ /**
+ * Triggers only once when the layout visibility state turns from invisible to fully visible for
+ * the first time.
+ */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public static final class OnVisibleOnceTrigger implements Trigger {
+ private final TriggerProto.OnVisibleOnceTrigger mImpl;
+ private final @Nullable Fingerprint mFingerprint;
+
+ OnVisibleOnceTrigger(
+ TriggerProto.OnVisibleOnceTrigger impl, @Nullable Fingerprint fingerprint) {
+ this.mImpl = impl;
+ this.mFingerprint = fingerprint;
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public @Nullable Fingerprint getFingerprint() {
+ return mFingerprint;
+ }
+
+ /** Creates a new wrapper instance from the proto. */
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public static @NonNull OnVisibleOnceTrigger fromProto(
+ TriggerProto.@NonNull OnVisibleOnceTrigger proto,
+ @Nullable Fingerprint fingerprint) {
+ return new OnVisibleOnceTrigger(proto, fingerprint);
+ }
+
+ static @NonNull OnVisibleOnceTrigger fromProto(
+ TriggerProto.@NonNull OnVisibleOnceTrigger proto) {
+ return fromProto(proto, null);
+ }
+
+ /** Returns the internal proto instance. */
+ TriggerProto.@NonNull OnVisibleOnceTrigger toProto() {
+ return mImpl;
+ }
+
+ @Override
+ @RestrictTo(Scope.LIBRARY_GROUP)
+ public TriggerProto.@NonNull Trigger toTriggerProto() {
+ return TriggerProto.Trigger.newBuilder().setOnVisibleOnceTrigger(mImpl).build();
+ }
+
+ @Override
+ public @NonNull String toString() {
+ return "OnVisibleOnceTrigger";
+ }
+
+ /** Builder for {@link OnVisibleOnceTrigger}. */
+ @SuppressWarnings("HiddenSuperclass")
+ public static final class Builder implements Trigger.Builder {
+ private final TriggerProto.OnVisibleOnceTrigger.Builder mImpl =
+ TriggerProto.OnVisibleOnceTrigger.newBuilder();
+ private final Fingerprint mFingerprint = new Fingerprint(-1661457257);
+
+ /** Creates an instance of {@link Builder}. */
+ @RequiresSchemaVersion(major = 1, minor = 200)
+ public Builder() {}
+
+ /** Builds an instance from accumulated values. */
+ @Override
+ public @NonNull OnVisibleOnceTrigger build() {
+ return new OnVisibleOnceTrigger(mImpl.build(), mFingerprint);
+ }
+ }
+ }
+
/** Triggers immediately when the layout is loaded / reloaded. */
@RequiresSchemaVersion(major = 1, minor = 200)
static final class OnLoadTrigger implements Trigger {
@@ -225,6 +379,12 @@
@RestrictTo(Scope.LIBRARY_GROUP)
public static @NonNull Trigger triggerFromProto(
TriggerProto.@NonNull Trigger proto, @Nullable Fingerprint fingerprint) {
+ if (proto.hasOnVisibleTrigger()) {
+ return OnVisibleTrigger.fromProto(proto.getOnVisibleTrigger(), fingerprint);
+ }
+ if (proto.hasOnVisibleOnceTrigger()) {
+ return OnVisibleOnceTrigger.fromProto(proto.getOnVisibleOnceTrigger(), fingerprint);
+ }
if (proto.hasOnLoadTrigger()) {
return OnLoadTrigger.fromProto(proto.getOnLoadTrigger(), fingerprint);
}
diff --git a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ResourceBuildersTest.java b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ResourceBuildersTest.java
index 3591875..c0256c3 100644
--- a/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ResourceBuildersTest.java
+++ b/wear/protolayout/protolayout/src/test/java/androidx/wear/protolayout/ResourceBuildersTest.java
@@ -74,9 +74,25 @@
.setProgress(DynamicBuilders.DynamicFloat.from(new AppDataKey<>(stateKey)))
.build();
- ResourceProto.AndroidLottieResourceByResId avdProto = lottieResource.toProto();
+ ResourceProto.AndroidLottieResourceByResId lottieProto = lottieResource.toProto();
- assertThat(avdProto.getRawResourceId()).isEqualTo(RESOURCE_ID);
- assertThat(avdProto.getProgress().getStateSource().getSourceKey()).isEqualTo(stateKey);
+ assertThat(lottieProto.getRawResourceId()).isEqualTo(RESOURCE_ID);
+ assertThat(lottieProto.getProgress().getStateSource().getSourceKey()).isEqualTo(stateKey);
+ }
+
+ @Test
+ public void lottieAnimation_hasTrigger() {
+ ResourceBuilders.AndroidLottieResourceByResId lottieResource =
+ new ResourceBuilders.AndroidLottieResourceByResId.Builder(RESOURCE_ID)
+ .setStartTrigger(TriggerBuilders.createOnVisibleTrigger())
+ .build();
+
+ ResourceProto.AndroidLottieResourceByResId lottieProto = lottieResource.toProto();
+
+ assertThat(lottieProto.getRawResourceId()).isEqualTo(RESOURCE_ID);
+ assertThat(lottieProto.hasStartTrigger()).isTrue();
+ assertThat(lottieProto.getStartTrigger().hasOnVisibleTrigger()).isTrue();
+ assertThat(lottieProto.getStartTrigger().hasOnVisibleOnceTrigger()).isFalse();
+ assertThat(lottieProto.getStartTrigger().hasOnLoadTrigger()).isFalse();
}
}