Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
# Change Log

## 29.1.0-rc.3

* Added: first background subscription auto-requests `POST_NOTIFICATIONS` (Android 13+) from the visible activity
* Fixed: `onOpen`/`onClose` callbacks fire correctly when joining or leaving the background host
* Fixed: notification taps are recorded after launching the app, so tap-opened screens land on top

## 29.1.0-rc.2

* Added: `getInitialNotification()`, `onNotificationOpened()`, and the `PushNotificationOpened` data class (topic + data)
* Added: `onOpen`/`onClose` callbacks now fire for background push connections
* Added: push auth falls back to the client's cookie-store session
* Fixed: a foreground message with no live callback now still posts a notification
* Updated: notification taps route through an internal `PushOpenActivity`

## 29.1.0-rc.1

* Updated: regenerated with sdk-generator 5.5.0; no SDK code changes

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Describe the push behavior changes in the release notes

This release changes SDK code substantially, including suppressing foreground notifications by default and adding notifyInForeground and background-permission APIs. Saying there are no SDK code changes hides a user-visible behavior change and the option consumers need to restore notifications.

Prompt To Fix With AI
This is a comment left during a code review.
Path: CHANGELOG.md
Line: 5

Comment:
**Describe the push behavior changes in the release notes**

This release changes SDK code substantially, including suppressing foreground notifications by default and adding `notifyInForeground` and background-permission APIs. Saying there are no SDK code changes hides a user-visible behavior change and the option consumers need to restore notifications.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

🟡 Minor · documentation · Reply if this doesn't apply.


## 29.1.0-rc.0

* Added: optional `method` param on `Client.chunkedUpload()` to set the upload HTTP method
Expand Down
68 changes: 59 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ repositories {
Next, add the dependency to your project's `build.gradle(.kts)` file:

```groovy
implementation("io.appwrite:sdk-for-android:29.1.0-rc.0")
implementation("io.appwrite:sdk-for-android:29.1.0-rc.3")
```

### Maven
Expand All @@ -49,7 +49,7 @@ Add this to your project's `pom.xml` file:
<dependency>
<groupId>io.appwrite</groupId>
<artifactId>sdk-for-android</artifactId>
<version>29.1.0-rc.0</version>
<version>29.1.0-rc.3</version>
</dependency>
</dependencies>
```
Expand Down Expand Up @@ -77,19 +77,54 @@ app every 15 to 60 seconds to reconnect, and the broker replays what was sent in
(`retry = true`). It reconnects with the credential saved at subscribe time, so use a session
(`client.setSession(...)`), not a short-lived JWT.

When no in-app callback is listening, each message is posted as a notification that opens
While the app is not on screen, each message is also posted as a notification that opens
your launch activity with `Push.EXTRA_TOPIC` and `Push.EXTRA_PAYLOAD` in its extras. It shows the
title, body and image sent with `createPush`, falling back to the subscription's `title` and the
raw payload for other messages. On Android 13 and later it needs the `POST_NOTIFICATIONS`
permission: call `Push.requestNotificationPermission(activity)` before subscribing. Set its
permission: the first background subscription asks for it from the visible activity (the one
`Push` is created with, else the next one to resume), or call
`Push.requestNotificationPermission(activity)` to ask yourself. Set its
icon with `<meta-data android:name="io.appwrite.push.notification_icon" android:resource="@drawable/..." />`
in your `<application>`. To handle these messages in code, subclass `PushReceiver` and declare
in your `<application>`. While the app is on screen your callback shows the message, so no
notification is posted unless the subscription passes `notifyInForeground = true`. To handle
messages that arrive while no callback is listening in code, subclass `PushReceiver` and declare
it with the `io.appwrite.push.MESSAGE` action (see its docs).

Doze delays the scheduled runs. They are more punctual when your app may schedule exact
alarms (`SCHEDULE_EXACT_ALARM`) or is exempt from battery optimisation; both are your app's
choice and subject to Google Play policy, and the SDK uses them when granted. For immediate delivery even after a kill and
during Doze, call `push.setForeground(true)` from the foreground: it adds a foreground service
#### Delivery while the app is closed

Messages sent while the app is closed arrive at the next scheduled wake-up. While the device is
awake that is about every 15 seconds, or about every 60 seconds once exact alarms are allowed;
without exact alarms the wake-ups are inexact, so battery saver can defer them further. In Doze
(screen off and idle for a while) Android limits background alarms, exact ones included, to about
one every nine minutes, so a closed app can take several minutes to receive a message: allowing
exact alarms makes wake-ups punctual, it does not lift Doze. For immediate delivery, also in
Doze, use `push.setForeground(true)` (see below).

The SDK uses exact alarms on its own whenever the app may schedule them. To allow it:

1. Declare the permissions in your app's `AndroidManifest.xml`. Both are optional and subject to
Google Play policy: `SCHEDULE_EXACT_ALARM` needs a declaration in the Play Console, and
`USE_EXACT_ALARM` is reserved for alarm, clock and calendar apps (the SDK does not use it).

```xml
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
```

2. On Android 13 and later the user has to allow exact alarms, under Settings > Apps > Special app
access > Alarms & reminders. Android 12 grants a declared `SCHEDULE_EXACT_ALARM`
automatically, and older versions need nothing. Check with `Push.backgroundStatus(context)`: when `bestEffort` is
true, explain why to the user, then from a user action open that screen with `Push.requestExactAlarms(context)`, or
ask for the battery-optimisation exemption with `Push.requestIgnoreBatteryOptimizations(context)`. Both return false when there is
nothing to ask, including when the permission is not declared. The SDK never opens these
screens on its own.

If the user force-stops the app (Settings > Force stop, and on some devices swiping it away from
recents), Android cancels its alarms and jobs: nothing is delivered until the app is opened
again, and the broker then replays what was sent meanwhile.

For immediate delivery even after a kill and during Doze, call `push.setForeground(true)` from
the foreground: it adds a foreground service
with a quiet ongoing notification. If you never enable it, you can remove the service from
your merged manifest, so the Play Console does not ask you to declare it:

Expand All @@ -98,6 +133,21 @@ your merged manifest, so the Play Console does not ask you to declare it:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" tools:node="remove" />
```

#### Opening a tapped notification

Read the `data` sent with `createPush` when the user taps a background notification:

```kotlin
// The tap that launched the app (reported once, so call it at startup).
push.getInitialNotification()?.let { openSale(it.data["saleId"]) }

// Taps while the app is running, including in the background.
val stop = push.onNotificationOpened { opened -> openSale(opened.data["saleId"]) }
```

A tap made while nothing listens is kept for `getInitialNotification()`. The tap intent still
carries `Push.EXTRA_TOPIC` and `Push.EXTRA_PAYLOAD` for apps that read their activity's intent.


## Getting Started

Expand Down
7 changes: 7 additions & 0 deletions library/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" />

<application>
<activity
android:name="io.appwrite.services.PushOpenActivity"
android:excludeFromRecents="true"
android:exported="false"
android:noHistory="true"
android:taskAffinity=""
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<service
android:name="io.appwrite.services.PushService"
android:exported="false"
Expand Down
2 changes: 1 addition & 1 deletion library/src/main/java/io/appwrite/Client.kt
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class Client @JvmOverloads constructor(
"x-sdk-name" to "Android",
"x-sdk-platform" to "client",
"x-sdk-language" to "android",
"x-sdk-version" to "29.1.0-rc.0",
"x-sdk-version" to "29.1.0-rc.3",
"x-appwrite-response-format" to "2.3.0"
)
config = mutableMapOf()
Expand Down
Loading