From b3c05834dc0a901e06bde16ea82d5d5f7abf31c8 Mon Sep 17 00:00:00 2001 From: Francesc Bruguera Date: Sat, 17 Aug 2024 23:00:16 +0200 Subject: [PATCH] Add support for starting a Live Activity --- .../pushy/apns/util/ApnsPayloadBuilder.java | 62 +++++++++++++++++++ .../pushy/apns/util/LiveActivityEvent.java | 4 ++ .../apns/util/ApnsPayloadBuilderTest.java | 45 ++++++++++++++ 3 files changed, 111 insertions(+) diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/util/ApnsPayloadBuilder.java b/pushy/src/main/java/com/eatthepath/pushy/apns/util/ApnsPayloadBuilder.java index c2819f9e2..bb479ee21 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/util/ApnsPayloadBuilder.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/util/ApnsPayloadBuilder.java @@ -82,12 +82,15 @@ public abstract class ApnsPayloadBuilder { private boolean preferStringRepresentationForAlerts = false; private LiveActivityEvent event = null; + private String attributesType = null; + private Integer inputPushToken = null; private Instant timestamp = null; private Instant dismissalDate = null; private Map contentState = null; + private Map attributes = null; private static final String APS_KEY = "aps"; private static final String ALERT_KEY = "alert"; @@ -123,6 +126,9 @@ public abstract class ApnsPayloadBuilder { private static final String DISMISSAL_DATE_KEY = "dismissal-date"; private static final String EVENT_KEY = "event"; private static final String CONTENT_STATE_KEY = "content-state"; + private static final String INPUT_PUSH_TOKEN_KEY = "input-push-token"; + private static final String ATTRIBUTES_TYPE_KEY = "attributes-type"; + private static final String ATTRIBUTES_KEY = "attributes"; public static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -747,6 +753,24 @@ public ApnsPayloadBuilder setContentState(final Map contentState return this; } + /** + *

Sets the attributes object that will be used to start the Live Activities.

+ * + *

The precise strategy for serializing the values of custom properties is defined by the specific + * {@code ApnsPayloadBuilder} implementation.

+ * + * @param attributes attributes object + * + * @return a reference to this payload builder + * + * @see + * Starting and updating Live Activities with ActivityKit push notifications + */ + public ApnsPayloadBuilder setAttributes(final Map attributes) { + this.attributes = attributes; + return this; + } + /** *

Sets whether the notification payload will be used for updating the Live Activity * or for ending it.

@@ -778,6 +802,32 @@ public ApnsPayloadBuilder setTimestamp(final Instant timestamp) { return this; } + /** + *

Sets the flag to wake up the app and receive a push token to send updates to the started Live Activity.

+ * + * @return a reference to this payload builder + * + * @see + * Starting and updating Live Activities with ActivityKit push notifications + */ + public ApnsPayloadBuilder setInputPushToken() { + this.inputPushToken = 1; + return this; + } + + /** + *

Sets the type of the content inside attributes that will be used to start the Live Activity..

+ * + * @return a reference to this payload builder + * + * @see + * Starting and updating Live Activities with ActivityKit push notifications + */ + public ApnsPayloadBuilder setAttributesType(final String attributesType) { + this.attributesType = attributesType; + return this; + } + /** *

Sets a timestamp for the push notification payload. The timestamp is used to discard older * push notifications. According to Apple's documentation:

@@ -869,6 +919,10 @@ protected Map buildPayloadMap() { aps.put(CONTENT_STATE_KEY, this.contentState); } + if (this.attributes != null) { + aps.put(ATTRIBUTES_KEY, this.attributes); + } + if (this.dismissalDate != null) { aps.put(DISMISSAL_DATE_KEY, this.dismissalDate.getEpochSecond()); } @@ -949,6 +1003,14 @@ protected Map buildPayloadMap() { aps.put(ALERT_KEY, alert); } + if (this.attributesType != null) { + aps.put(ATTRIBUTES_TYPE_KEY, this.attributesType); + } + + if (this.inputPushToken != null) { + aps.put(INPUT_PUSH_TOKEN_KEY, this.inputPushToken); + } + payload.put(APS_KEY, aps); } diff --git a/pushy/src/main/java/com/eatthepath/pushy/apns/util/LiveActivityEvent.java b/pushy/src/main/java/com/eatthepath/pushy/apns/util/LiveActivityEvent.java index ccd1460bb..50711d920 100644 --- a/pushy/src/main/java/com/eatthepath/pushy/apns/util/LiveActivityEvent.java +++ b/pushy/src/main/java/com/eatthepath/pushy/apns/util/LiveActivityEvent.java @@ -30,6 +30,10 @@ * @since 0.15.2 */ public enum LiveActivityEvent { + /** + * Used to start a Live Activity. + */ + START("start"), /** * Used to update a Live Activity. */ diff --git a/pushy/src/test/java/com/eatthepath/pushy/apns/util/ApnsPayloadBuilderTest.java b/pushy/src/test/java/com/eatthepath/pushy/apns/util/ApnsPayloadBuilderTest.java index d40dd5009..5ce11bb99 100644 --- a/pushy/src/test/java/com/eatthepath/pushy/apns/util/ApnsPayloadBuilderTest.java +++ b/pushy/src/test/java/com/eatthepath/pushy/apns/util/ApnsPayloadBuilderTest.java @@ -682,6 +682,51 @@ void testAddContentStateProperty() { assertEquals(subMap, serializedContentState.get(keyForMapValue)); } + @Test + void testAddAttributesProperty() { + final String keyForStringValue = "string"; + final String stringValue = "Hello"; + + final String keyForLongValue = "integer"; + final long longValue = 12; + + final String keyForMapValue = "map"; + final Map attributes = new HashMap<>(); + final Map subMap = new HashMap<>(); + subMap.put("boolean", true); + attributes.put(keyForLongValue, longValue); + attributes.put(keyForStringValue, stringValue); + attributes.put(keyForMapValue, subMap); + + this.builder.setAttributes(attributes); + + final Map aps = this.extractApsObjectFromPayloadString(this.builder.build()); + final Map serializedContentState = (Map) aps.get("attributes"); + + assertEquals(stringValue, serializedContentState.get(keyForStringValue)); + assertEquals(longValue, serializedContentState.get(keyForLongValue)); + assertEquals(subMap, serializedContentState.get(keyForMapValue)); + } + + @Test + void setAttributesType() { + final String attributesType = "my-attributes-type"; + this.builder.setAttributesType(attributesType); + + final Map aps = this.extractApsObjectFromPayloadString(this.builder.build()); + + assertEquals(attributesType, aps.get("attributes-type")); + } + + @Test + void setInputPushToken() { + this.builder.setInputPushToken(); + + final Map aps = this.extractApsObjectFromPayloadString(this.builder.build()); + + assertEquals(1L, aps.get("input-push-token")); + } + @SuppressWarnings("unchecked") private Map extractApsObjectFromPayloadString(final String payloadString) { final Map payload;