Repository: jchambers/pushy
Pull request #894 (2021-06-24 13:06:53)

Added support for iOS 15 interruption level
Add support for iOS 15 interruption-level attribute. Solves https://github.com/jchambers/pushy/issues/893. 

s1g53gv: @jchambers I'm unsure if you have notifications enabled for the comments, so I'll bump you here too: let us know if the change meet your expectations :)

Patch:
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 a1204626..82d531ec 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
@@ -39,6 +39,42 @@ import java.util.*;
 @SuppressWarnings({"UnusedReturnValue", "unused"})
 public abstract class ApnsPayloadBuilder {
 
+    /**
+     * Interruption levels.
+     * 
+     * According to <a href="https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/notifications/">Apple's Human Interface Guidelines</a>:
+     * {@code PASSIVE}. Information people can view at their leisure, like a restaurant recommendation.
+     * {@code ACTIVE}. Information people might appreciate knowing about when it arrives, like a score update on their favorite sports team.
+     * {@code TIME_SENSITIVE}. Information that directly impacts the user and requires their immediate attention, like an account security issue or a package delivery.
+     * {@code CRITICAL}. Urgent information about personal health and public safety that directly impacts the user and demands their immediate attention. Critical notifications are extremely rare and typically come from governmental and public agencies or healthcare apps. You must get an entitlement to use the Critical interruption level.
+     */
+    public enum InterruptionLevel {
+        PASSIVE {
+            @Override
+            public String toString() {
+                return "passive";
+            }
+        },
+        ACTIVE {
+            @Override
+            public String toString() {
+                return "active";
+            }
+        },
+        TIME_SENSITIVE {
+            @Override
+            public String toString() {
+                return "time-sensitive";
+            }
+        },
+        CRITICAL {
+            @Override
+            public String toString() {
+                return "critical";
+            }
+        }
+    }
+
     private String alertBody = null;
 
     private String localizedAlertKey = null;
@@ -72,6 +108,8 @@ public abstract class ApnsPayloadBuilder {
     private String summaryArgument = null;
     private Integer summaryArgumentCount = null;
 
+    private InterruptionLevel interruptionLevel = null;
+
     private String[] urlArguments = null;
 
     private final HashMap<String, Object> customProperties = new HashMap<>();
@@ -90,6 +128,7 @@ public abstract class ApnsPayloadBuilder {
     private static final String SUMMARY_ARGUMENT_KEY = "summary-arg";
     private static final String SUMMARY_ARGUMENT_COUNT_KEY = "summary-arg-count";
     private static final String URL_ARGS_KEY = "url-args";
+    private static final String INTERRUPTION_LEVEL_KEY = "interruption-level";
 
     private static final String ALERT_TITLE_KEY = "title";
     private static final String ALERT_TITLE_LOC_KEY = "title-loc-key";
@@ -113,7 +152,7 @@ public abstract class ApnsPayloadBuilder {
      *
      * @see ApnsPayloadBuilder#setSoundFileName(String)
      */
-    public static final String DEFAULT_SOUND_FILENAME = "default";
+    public static final String DEFAULT_SOUND_FILENAME = "default"; 
 
     /**
     /**
@@ -605,6 +644,22 @@ public abstract class ApnsPayloadBuilder {
         return this;
     }
 
+    /**
+     * <p>Sets the interruption level for the notification.</p>
+     * 
+     * @param interruptionLevel the interruption level.
+     *
+     * @see InterruptionLevel
+     * 
+     * @since 0.15
+     */
+    public ApnsPayloadBuilder setInterruptionLevel(final InterruptionLevel interruptionLevel) {
+        
+        this.interruptionLevel = interruptionLevel;
+
+        return this;
+    }
+
     /**
      * <p>Sets the list of arguments to populate placeholders in the {@code urlFormatString} associated with a Safari
      * push notification. Has no effect for non-Safari notifications. According to the Notification Programming Guide
@@ -729,6 +784,10 @@ public abstract class ApnsPayloadBuilder {
                 aps.put(URL_ARGS_KEY, this.urlArguments);
             }
 
+            if (this.interruptionLevel != null) {
+                aps.put(INTERRUPTION_LEVEL_KEY, this.interruptionLevel.toString());
+            }
+
             final Map<String, Object> alert = new HashMap<>();
             {
                 if (this.alertBody != null) {


Test patch:
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 69a2b511..85f39008 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
@@ -493,6 +493,21 @@ public abstract class ApnsPayloadBuilderTest {
 
         assertEquals(argumentSummaryCount, ((Number) alert.get("summary-arg-count")).intValue());
     }
+    
+    @Test
+    void testSetInterruptionLevel() {
+        
+    	final ApnsPayloadBuilder.InterruptionLevel argumentInterruptionLevel = ApnsPayloadBuilder.InterruptionLevel.TIME_SENSITIVE;
+    	
+        this.builder.setInterruptionLevel(argumentInterruptionLevel);
+
+        final Map<String, Object> aps = this.extractApsObjectFromPayloadString(this.builder.build());
+
+        @SuppressWarnings("unchecked")
+        final String interruptionLevel = (String) aps.get("interruption-level");
+
+        assertEquals("time-sensitive", interruptionLevel);
+    }
 
     @Test
     void testSetSummaryArgumentCountNonPositive() {