mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-04-27 11:56:05 +02:00
fix(notification): scheduled notifications not working (#909)
* fix(notification): scheduled notifications not working * do not use toJSON since it doesnt work on isolation pattern * fmt
This commit is contained in:
committed by
GitHub
parent
61edbbec0a
commit
8dea78ac7d
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"notification": patch
|
||||
"notification-js": patch
|
||||
---
|
||||
|
||||
Fixes deserialization and implementation bugs with scheduled notifications on Android.
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
@@ -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>()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -17,6 +17,7 @@ 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
|
||||
@@ -24,11 +25,25 @@ 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 {
|
||||
@@ -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) {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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 interval(
|
||||
interval: ScheduleInterval,
|
||||
allowWhileIdle = false,
|
||||
): Schedule {
|
||||
return {
|
||||
at: undefined,
|
||||
interval: { interval, allowWhileIdle },
|
||||
every: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
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 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 },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
if("__TAURI__"in window){var __TAURI_PLUGIN_NOTIFICATION__=function(n){"use strict";function e(n,e,i,t){if("a"===i&&!t)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?n!==e||!t:!e.has(n))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?t:"a"===i?t.call(n):t?t.value:e.get(n)}var i,t,o,r;"function"==typeof SuppressedError&&SuppressedError;class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),this.id=function(n,e=!1){return window.__TAURI_INTERNALS__.transformCallback(n,e)}((n=>{e(this,i,"f").call(this,n)}))}set onmessage(n){!function(n,e,i,t,o){if("m"===t)throw new TypeError("Private method is not writable");if("a"===t&&!o)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof e?n!==e||!o:!e.has(n))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===t?o.call(n,i):o?o.value=i:e.set(n,i)}(this,i,n,"f")}get onmessage(){return e(this,i,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}i=new WeakMap;class c{constructor(n,e,i){this.plugin=n,this.event=e,this.channelId=i}async unregister(){return u(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function s(n,e,i){const t=new a;return t.onmessage=i,u(`plugin:${n}|register_listener`,{event:e,handler:t}).then((()=>new c(n,e,t.id)))}async function u(n,e={},i){return window.__TAURI_INTERNALS__.invoke(n,e,i)}n.ScheduleEvery=void 0,(t=n.ScheduleEvery||(n.ScheduleEvery={})).Year="year",t.Month="month",t.TwoWeeks="twoWeeks",t.Week="week",t.Day="day",t.Hour="hour",t.Minute="minute",t.Second="second";class l{constructor(n){this.schedule=n}toJSON(){return JSON.stringify(this.schedule)}static at(n,e=!1,i=!1){return new l({at:{date:n,repeating:e,allowWhileIdle:i}})}static interval(n,e=!1){return new l({interval:{interval:n,allowWhileIdle:e}})}static every(n,e,i=!1){return new l({every:{interval:n,count:e,allowWhileIdle:i}})}}return n.Importance=void 0,(o=n.Importance||(n.Importance={}))[o.None=0]="None",o[o.Min=1]="Min",o[o.Low=2]="Low",o[o.Default=3]="Default",o[o.High=4]="High",n.Visibility=void 0,(r=n.Visibility||(n.Visibility={}))[r.Secret=-1]="Secret",r[r.Private=0]="Private",r[r.Public=1]="Public",n.Schedule=l,n.active=async function(){return u("plugin:notification|get_active")},n.cancel=async function(n){return u("plugin:notification|cancel",{notifications:n})},n.cancelAll=async function(){return u("plugin:notification|cancel")},n.channels=async function(){return u("plugin:notification|listChannels")},n.createChannel=async function(n){return u("plugin:notification|create_channel",{...n})},n.isPermissionGranted=async function(){return"default"!==window.Notification.permission?Promise.resolve("granted"===window.Notification.permission):u("plugin:notification|is_permission_granted")},n.onAction=async function(n){return s("notification","actionPerformed",n)},n.onNotificationReceived=async function(n){return s("notification","notification",n)},n.pending=async function(){return u("plugin:notification|get_pending")},n.registerActionTypes=async function(n){return u("plugin:notification|register_action_types",{types:n})},n.removeActive=async function(n){return u("plugin:notification|remove_active",{notifications:n})},n.removeAllActive=async function(){return u("plugin:notification|remove_active")},n.removeChannel=async function(n){return u("plugin:notification|delete_channel",{id:n})},n.requestPermission=async function(){return window.Notification.requestPermission()},n.sendNotification=function(n){"string"==typeof n?new window.Notification(n):new window.Notification(n.title,n)},n}({});Object.defineProperty(window.__TAURI__,"notification",{value:__TAURI_PLUGIN_NOTIFICATION__})}
|
||||
if("__TAURI__"in window){var __TAURI_PLUGIN_NOTIFICATION__=function(n){"use strict";function e(n,e,i,t){if("a"===i&&!t)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?n!==e||!t:!e.has(n))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?t:"a"===i?t.call(n):t?t.value:e.get(n)}var i,t,o,r;"function"==typeof SuppressedError&&SuppressedError;class a{constructor(){this.__TAURI_CHANNEL_MARKER__=!0,i.set(this,(()=>{})),this.id=function(n,e=!1){return window.__TAURI_INTERNALS__.transformCallback(n,e)}((n=>{e(this,i,"f").call(this,n)}))}set onmessage(n){!function(n,e,i,t,o){if("m"===t)throw new TypeError("Private method is not writable");if("a"===t&&!o)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof e?n!==e||!o:!e.has(n))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===t?o.call(n,i):o?o.value=i:e.set(n,i)}(this,i,n,"f")}get onmessage(){return e(this,i,"f")}toJSON(){return`__CHANNEL__:${this.id}`}}i=new WeakMap;class c{constructor(n,e,i){this.plugin=n,this.event=e,this.channelId=i}async unregister(){return u(`plugin:${this.plugin}|remove_listener`,{event:this.event,channelId:this.channelId})}}async function s(n,e,i){const t=new a;return t.onmessage=i,u(`plugin:${n}|register_listener`,{event:e,handler:t}).then((()=>new c(n,e,t.id)))}async function u(n,e={},i){return window.__TAURI_INTERNALS__.invoke(n,e,i)}n.ScheduleEvery=void 0,(t=n.ScheduleEvery||(n.ScheduleEvery={})).Year="year",t.Month="month",t.TwoWeeks="twoWeeks",t.Week="week",t.Day="day",t.Hour="hour",t.Minute="minute",t.Second="second";return n.Importance=void 0,(o=n.Importance||(n.Importance={}))[o.None=0]="None",o[o.Min=1]="Min",o[o.Low=2]="Low",o[o.Default=3]="Default",o[o.High=4]="High",n.Visibility=void 0,(r=n.Visibility||(n.Visibility={}))[r.Secret=-1]="Secret",r[r.Private=0]="Private",r[r.Public=1]="Public",n.Schedule=class{static at(n,e=!1,i=!1){return{at:{date:n,repeating:e,allowWhileIdle:i},interval:void 0,every:void 0}}static interval(n,e=!1){return{at:void 0,interval:{interval:n,allowWhileIdle:e},every:void 0}}static every(n,e,i=!1){return{at:void 0,interval:void 0,every:{interval:n,count:e,allowWhileIdle:i}}}},n.active=async function(){return u("plugin:notification|get_active")},n.cancel=async function(n){return u("plugin:notification|cancel",{notifications:n})},n.cancelAll=async function(){return u("plugin:notification|cancel")},n.channels=async function(){return u("plugin:notification|listChannels")},n.createChannel=async function(n){return u("plugin:notification|create_channel",{...n})},n.isPermissionGranted=async function(){return"default"!==window.Notification.permission?Promise.resolve("granted"===window.Notification.permission):u("plugin:notification|is_permission_granted")},n.onAction=async function(n){return s("notification","actionPerformed",n)},n.onNotificationReceived=async function(n){return s("notification","notification",n)},n.pending=async function(){return u("plugin:notification|get_pending")},n.registerActionTypes=async function(n){return u("plugin:notification|register_action_types",{types:n})},n.removeActive=async function(n){return u("plugin:notification|remove_active",{notifications:n})},n.removeAllActive=async function(){return u("plugin:notification|remove_active")},n.removeChannel=async function(n){return u("plugin:notification|delete_channel",{id:n})},n.requestPermission=async function(){return window.Notification.requestPermission()},n.sendNotification=function(n){"string"==typeof n?new window.Notification(n):new window.Notification(n.title,n)},n}({});Object.defineProperty(window.__TAURI__,"notification",{value:__TAURI_PLUGIN_NOTIFICATION__})}
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -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",
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user