Skip to content

Commit

Permalink
fix(notification): scheduled notifications not working (#909)
Browse files Browse the repository at this point in the history
* fix(notification): scheduled notifications not working

* do not use toJSON since it doesnt work on isolation pattern

* fmt
  • Loading branch information
lucasfernog authored Jan 24, 2024
1 parent 61edbbe commit 8dea78a
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 56 deletions.
6 changes: 6 additions & 0 deletions .changes/fix-scheduled-notification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"notification": patch
"notification-js": patch
---

Fixes deserialization and implementation bugs with scheduled notifications on Android.
4 changes: 2 additions & 2 deletions examples/api/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import process from "process";
// https://vitejs.dev/config/
export default defineConfig(async () => {
const host =
process.env.TAURI_PLATFORM === "android" ||
process.env.TAURI_PLATFORM === "ios"
process.env.TAURI_ENV_PLATFORM === "android" ||
process.env.TAURI_ENV_PLATFORM === "ios"
? await internalIpV4()
: "localhost";
return {
Expand Down
7 changes: 4 additions & 3 deletions plugins/notification/android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
</intent-filter>
</receiver>
</application>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
</manifest>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
</manifest>
2 changes: 0 additions & 2 deletions plugins/notification/android/src/main/java/Notification.kt
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ class Notification {
return null
}

val isScheduled = schedule != null

companion object {
fun buildNotificationPendingList(notifications: List<Notification>): List<PendingNotification> {
val pendingNotifications = mutableListOf<PendingNotification>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ class NotificationPlugin(private val activity: Activity): Plugin(activity) {
@PermissionCallback
private fun permissionsCallback(invoke: Invoke) {
val permissionsResultJSON = JSObject()
permissionsResultJSON.put("display", getPermissionState())
permissionsResultJSON.put("permissionState", getPermissionState())
invoke.resolve(permissionsResultJSON)
}

Expand Down
42 changes: 36 additions & 6 deletions plugins/notification/android/src/main/java/NotificationSchedule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
package app.tauri.notification

import android.annotation.SuppressLint
import android.content.ClipData.Item
import android.text.format.DateUtils
import com.fasterxml.jackson.annotation.JsonFormat
import com.fasterxml.jackson.annotation.JsonProperty
import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.core.JsonParser
import com.fasterxml.jackson.core.JsonProcessingException
Expand All @@ -17,18 +17,33 @@ import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.SerializerProvider
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
import com.fasterxml.jackson.databind.annotation.JsonSerialize
import com.fasterxml.jackson.databind.deser.std.StdDeserializer
import com.fasterxml.jackson.databind.ser.std.StdSerializer
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import java.util.TimeZone


const val JS_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"

enum class NotificationInterval {
Year, Month, TwoWeeks, Week, Day, Hour, Minute, Second
@JsonProperty("year")
Year,
@JsonProperty("month")
Month,
@JsonProperty("twoWeeks")
TwoWeeks,
@JsonProperty("week")
Week,
@JsonProperty("day")
Day,
@JsonProperty("hour")
Hour,
@JsonProperty("minute")
Minute,
@JsonProperty("second")
Second
}

fun getIntervalTime(interval: NotificationInterval, count: Int): Long {
Expand All @@ -50,9 +65,24 @@ fun getIntervalTime(interval: NotificationInterval, count: Int): Long {
@JsonSerialize(using = NotificationScheduleSerializer::class)
sealed class NotificationSchedule {
// At specific moment of time (with repeating option)
class At(@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = JS_DATE_FORMAT) var date: Date, val repeating: Boolean = false, val allowWhileIdle: Boolean = false): NotificationSchedule()
class Interval(val interval: DateMatch, val allowWhileIdle: Boolean = false): NotificationSchedule()
class Every(val interval: NotificationInterval, val count: Int = 0, val allowWhileIdle: Boolean = false): NotificationSchedule()
@JsonDeserialize
class At: NotificationSchedule() {
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = JS_DATE_FORMAT)
lateinit var date: Date
var repeating: Boolean = false
var allowWhileIdle: Boolean = false
}
@JsonDeserialize
class Interval: NotificationSchedule() {
lateinit var interval: DateMatch
var allowWhileIdle: Boolean = false
}
@JsonDeserialize
class Every: NotificationSchedule() {
lateinit var interval: NotificationInterval
var count: Int = 0
var allowWhileIdle: Boolean = false
}

fun isRemovable(): Boolean {
return when (this) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class NotificationStorage(private val context: Context, private val jsonMapper:
val storage = getStorage(NOTIFICATION_STORE_ID)
val editor = storage.edit()
for (request in localNotifications) {
if (request.isScheduled) {
if (request.schedule != null) {
val key: String = request.id.toString()
editor.putString(key, request.sourceJson.toString())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class TauriNotificationManager(
createActionIntents(notification, mBuilder)
// notificationId is a unique int for each notification that you must define
val buildNotification = mBuilder.build()
if (notification.isScheduled) {
if (notification.schedule != null) {
triggerScheduledNotification(buildNotification, notification)
} else {
notificationManager.notify(notification.id, buildNotification)
Expand Down Expand Up @@ -473,7 +473,7 @@ class TimedNotificationPublisher : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val notification = if (SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getParcelableExtra(
NOTIFICATION_KEY,
android.app.Notification::class.java
Expand Down
68 changes: 38 additions & 30 deletions plugins/notification/guest-js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,49 +163,57 @@ enum ScheduleEvery {
Second = "second",
}

type ScheduleData =
| {
at: {
class Schedule {
at:
| {
date: Date;
repeating: boolean;
allowWhileIdle: boolean;
};
}
| {
interval: {
}
| undefined;
interval:
| {
interval: ScheduleInterval;
allowWhileIdle: boolean;
};
}
| {
every: {
}
| undefined;
every:
| {
interval: ScheduleEvery;
count: number;
allowWhileIdle: boolean;
};
}
| undefined;

static at(date: Date, repeating = false, allowWhileIdle = false): Schedule {
return {
at: { date, repeating, allowWhileIdle },
interval: undefined,
every: undefined,
};

class Schedule {
schedule: ScheduleData;

private constructor(schedule: ScheduleData) {
this.schedule = schedule;
}

toJSON(): string {
return JSON.stringify(this.schedule);
}

static at(date: Date, repeating = false, allowWhileIdle = false) {
return new Schedule({ at: { date, repeating, allowWhileIdle } });
}

static interval(interval: ScheduleInterval, allowWhileIdle = false) {
return new Schedule({ interval: { interval, allowWhileIdle } });
static interval(
interval: ScheduleInterval,
allowWhileIdle = false,
): Schedule {
return {
at: undefined,
interval: { interval, allowWhileIdle },
every: undefined,
};
}

static every(kind: ScheduleEvery, count: number, allowWhileIdle = false) {
return new Schedule({ every: { interval: kind, count, allowWhileIdle } });
static every(
kind: ScheduleEvery,
count: number,
allowWhileIdle = false,
): Schedule {
return {
at: undefined,
interval: undefined,
every: { interval: kind, count, allowWhileIdle },
};
}
}

Expand Down
2 changes: 1 addition & 1 deletion plugins/notification/src/api-iife.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 11 additions & 8 deletions plugins/notification/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ impl Display for ScheduleEvery {
f,
"{}",
match self {
Self::Year => "Year",
Self::Month => "Month",
Self::TwoWeeks => "TwoWeeks",
Self::Week => "Week",
Self::Day => "Day",
Self::Hour => "Hour",
Self::Minute => "Minute",
Self::Second => "Second",
Self::Year => "year",
Self::Month => "month",
Self::TwoWeeks => "twoWeeks",
Self::Week => "week",
Self::Day => "day",
Self::Hour => "hour",
Self::Minute => "minute",
Self::Second => "second",
}
)
}
Expand Down Expand Up @@ -96,6 +96,7 @@ impl<'de> Deserialize<'de> for ScheduleEvery {
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Schedule {
#[serde(rename_all = "camelCase")]
At {
#[serde(
serialize_with = "iso8601::serialize",
Expand All @@ -107,11 +108,13 @@ pub enum Schedule {
#[serde(default)]
allow_while_idle: bool,
},
#[serde(rename_all = "camelCase")]
Interval {
interval: ScheduleInterval,
#[serde(default)]
allow_while_idle: bool,
},
#[serde(rename_all = "camelCase")]
Every {
interval: ScheduleEvery,
count: u8,
Expand Down

0 comments on commit 8dea78a

Please sign in to comment.