Skip to content

Commit 3dd7497

Browse files
Implement cross-platform push completion handling for background tasks (opt-in).
Added Display.notifyPushCompletion() to manually signal the completion of a background push task. This mechanism is OPT-IN via the `delayPushCompletion` (or `android.delayPushCompletion`/`ios.delayPushCompletion`) build hint. If the hint is present and true: - Android: Acquires a `PARTIAL_WAKE_LOCK` upon receiving a push, preventing the device from sleeping until `notifyPushCompletion()` is called (or timeout). - iOS: Ensures the `remote-notification` background mode is enabled and delays firing the system completion handler until `notifyPushCompletion()` is called. The builders (`IPhoneBuilder`, `AndroidGradleBuilder`) have been updated to check for this hint and automatically: 1. Inject the permission (Android) or capability (iOS) into the native project configuration. 2. Inject the `delayPushCompletion` property into the runtime environment so the logic in `PushNotificationService` and `IOSImplementation` activates. Updated the developer guide to document this new feature.
1 parent 90c491a commit 3dd7497

File tree

3 files changed

+48
-0
lines changed

3 files changed

+48
-0
lines changed

docs/developer-guide/Push-Notifications.asciidoc

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,44 @@ NOTE: On iOS, hidden push messages (push type 2) will not be delivered when the
8787

8888
TIP: You can set the `android.background_push_handling` build hint to "true" to deliver push messages on Android when the app is minimized (running in the background). There is no equivalent setting on other platforms currently.
8989

90+
[[delay-push-completion]]
91+
=== Handling Long-Running Background Push Tasks
92+
93+
By default, the platform signals the completion of the push processing immediately after your `push(String)` callback returns. However, some tasks, such as playing audio (e.g. for Push-To-Talk apps), require more time to complete. If the app is in the background, the OS might suspend the app before the task completes if it thinks the push handling is finished.
94+
95+
To handle this, you can opt-in to manually signaling the completion of the push task using the `delayPushCompletion` build hint.
96+
97+
1. Add the `delayPushCompletion=true` build hint to your `codenameone_settings.properties`.
98+
2. In your `push(String)` callback, start your asynchronous task.
99+
3. When your task is complete, call `Display.getInstance().notifyPushCompletion()`.
100+
101+
Example:
102+
103+
[source,java]
104+
----
105+
public void push(String message) {
106+
if (isAudioMessage(message)) {
107+
// Play audio asynchronously
108+
playAudio(message, new Runnable() {
109+
public void run() {
110+
// Audio finished playing
111+
Display.getInstance().notifyPushCompletion();
112+
}
113+
});
114+
} else {
115+
// For standard messages, we can notify immediately or let it timeout (safest to notify)
116+
Display.getInstance().notifyPushCompletion();
117+
}
118+
}
119+
----
120+
121+
**How it works:**
122+
123+
* **Android:** The system acquires a `PARTIAL_WAKE_LOCK` when the push is received, keeping the CPU running even if the screen is off. Calling `notifyPushCompletion()` releases this lock. The lock has a safety timeout (e.g., 30 seconds) to prevent battery drain if you forget to call it.
124+
* **iOS:** The system delays calling the completion handler passed to the push delegate. This gives your app background execution time. Calling `notifyPushCompletion()` invokes the system completion handler.
125+
126+
NOTE: If you enable this feature, you **must** call `Display.getInstance().notifyPushCompletion()` in all code paths of your `push()` callback to ensure the device can sleep/suspend properly.
127+
90128

91129
=== Testing Push Support
92130

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/AndroidGradleBuilder.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3898,6 +3898,10 @@ private String createOnDestroyCode(BuildRequest request) {
38983898

38993899
private String createPostInitCode(BuildRequest request) {
39003900
String retVal = "";
3901+
if (request.getArg("android.delayPushCompletion", "false").equals("true") ||
3902+
request.getArg("delayPushCompletion", "false").equals("true")) {
3903+
retVal += "Display.getInstance().setProperty(\"android.delayPushCompletion\", \"true\");\n";
3904+
}
39013905
return retVal;
39023906
}
39033907

maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,8 +956,14 @@ public void usesClassMethod(String cls, String method) {
956956
+ " private boolean stopped = false;\n";
957957

958958
stubSourceCode += decodeFunction();
959+
String delayPushCompletion = "";
960+
if ("true".equals(request.getArg("ios.delayPushCompletion", "false")) ||
961+
"true".equals(request.getArg("delayPushCompletion", "false"))) {
962+
delayPushCompletion = " Display.getInstance().setProperty(\"ios.delayPushCompletion\", \"true\");\n";
963+
}
959964
stubSourceCode += " public void run() {\n"
960965
+ " Display.getInstance().setProperty(\"package_name\", PACKAGE_NAME);\n"
966+
+ delayPushCompletion
961967
+ " Display.getInstance().setProperty(\"AppVersion\", APPLICATION_VERSION);\n"
962968
+ " Display.getInstance().setProperty(\"AppName\", APPLICATION_NAME);\n"
963969
+ newStorage

0 commit comments

Comments
 (0)