From cf47c8ef6d67e7c6e53c36549745b3e5b406394a Mon Sep 17 00:00:00 2001 From: Lucas Nogueira Date: Mon, 1 May 2023 14:54:32 -0300 Subject: [PATCH] add missing APIs --- .../android/src/main/AndroidManifest.xml | 21 ++++- .../android/src/main/java/Notification.kt | 85 +------------------ .../src/main/java/NotificationSchedule.kt | 2 +- .../src/main/java/TauriNotificationManager.kt | 65 ++++++++++---- plugins/notification/guest-js/index.ts | 8 ++ 5 files changed, 80 insertions(+), 101 deletions(-) diff --git a/plugins/notification/android/src/main/AndroidManifest.xml b/plugins/notification/android/src/main/AndroidManifest.xml index 9a40236b..986d5f85 100644 --- a/plugins/notification/android/src/main/AndroidManifest.xml +++ b/plugins/notification/android/src/main/AndroidManifest.xml @@ -1,3 +1,20 @@ - - + + + + + + + + + + + + + + + diff --git a/plugins/notification/android/src/main/java/Notification.kt b/plugins/notification/android/src/main/java/Notification.kt index 9492901b..f0f78972 100644 --- a/plugins/notification/android/src/main/java/Notification.kt +++ b/plugins/notification/android/src/main/java/Notification.kt @@ -30,6 +30,8 @@ class Notification { var schedule: NotificationSchedule? = null var channelId: String? = null var source: String? = null + var visibility: Int? = null + var number: Int? = null fun getSound(context: Context, defaultSound: Int): String? { var soundPath: String? = null @@ -86,87 +88,6 @@ class Notification { val isScheduled = schedule != null - override fun toString(): String { - return "Notification{" + - "title='" + - title + - '\'' + - ", body='" + - body + - '\'' + - ", id=" + - id + - ", sound='" + - sound + - '\'' + - ", smallIcon='" + - smallIcon + - '\'' + - ", iconColor='" + - iconColor + - '\'' + - ", actionTypeId='" + - actionTypeId + - '\'' + - ", group='" + - group + - '\'' + - ", extra=" + - extra + - ", attachments=" + - attachments + - ", schedule=" + - schedule + - ", groupSummary=" + - isGroupSummary + - ", ongoing=" + - isOngoing + - ", autoCancel=" + - isAutoCancel + - '}' - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other == null || javaClass != other.javaClass) return false - val that = other as Notification - if (if (title != null) title != that.title else that.title != null) return false - if (if (body != null) body != that.body else that.body != null) return false - if (if (largeBody != null) largeBody != that.largeBody else that.largeBody != null) return false - if (id != that.id) return false - if (if (sound != null) sound != that.sound else that.sound != null) return false - if (if (smallIcon != null) smallIcon != that.smallIcon else that.smallIcon != null) return false - if (if (largeIcon != null) largeIcon != that.largeIcon else that.largeIcon != null) return false - if (if (iconColor != null) iconColor != that.iconColor else that.iconColor != null) return false - if (if (actionTypeId != null) actionTypeId != that.actionTypeId else that.actionTypeId != null) return false - if (if (group != null) group != that.group else that.group != null) return false - if (if (extra != null) extra != that.extra else that.extra != null) return false - if (if (attachments != null) attachments != that.attachments else that.attachments != null) return false - if (if (inboxLines != null) inboxLines != that.inboxLines else that.inboxLines != null) return false - if (isGroupSummary != that.isGroupSummary) return false - if (isOngoing != that.isOngoing) return false - if (isAutoCancel != that.isAutoCancel) return false - return if (schedule != null) schedule?.equals(that.schedule) ?: false else that.schedule == null - } - - override fun hashCode(): Int { - var result = if (title != null) title.hashCode() else 0 - result = 31 * result + if (body != null) body.hashCode() else 0 - result = 31 * result + id.hashCode() - result = 31 * result + if (sound != null) sound.hashCode() else 0 - result = 31 * result + if (smallIcon != null) smallIcon.hashCode() else 0 - result = 31 * result + if (iconColor != null) iconColor.hashCode() else 0 - result = 31 * result + if (actionTypeId != null) actionTypeId.hashCode() else 0 - result = 31 * result + if (group != null) group.hashCode() else 0 - result = 31 * result + java.lang.Boolean.hashCode(isGroupSummary) - result = 31 * result + java.lang.Boolean.hashCode(isOngoing) - result = 31 * result + java.lang.Boolean.hashCode(isAutoCancel) - result = 31 * result + if (extra != null) extra.hashCode() else 0 - result = 31 * result + if (attachments != null) attachments.hashCode() else 0 - result = 31 * result + if (schedule != null) schedule.hashCode() else 0 - return result - } - companion object { fun fromJson(jsonNotification: JSONObject): Notification { val notification: JSObject = try { @@ -205,6 +126,8 @@ class Notification { notification.extra = jsonObject.getJSObject("extra") notification.isOngoing = jsonObject.getBoolean("ongoing", false) notification.isAutoCancel = jsonObject.getBoolean("autoCancel", true) + notification.visibility = jsonObject.getInteger("visibility") + notification.number = jsonObject.getInteger("number") try { val inboxLines = jsonObject.getJSONArray("inboxLines") val inboxStringList: MutableList = ArrayList() diff --git a/plugins/notification/android/src/main/java/NotificationSchedule.kt b/plugins/notification/android/src/main/java/NotificationSchedule.kt index 916d4254..c2f0952b 100644 --- a/plugins/notification/android/src/main/java/NotificationSchedule.kt +++ b/plugins/notification/android/src/main/java/NotificationSchedule.kt @@ -31,7 +31,7 @@ fun getIntervalTime(interval: NotificationInterval, count: Int): Long { sealed class ScheduleKind { // At specific moment of time (with repeating option) - class At(val date: Date, val repeating: Boolean): ScheduleKind() + class At(var date: Date, val repeating: Boolean): ScheduleKind() class Interval(val interval: DateMatch): ScheduleKind() class Every(val interval: NotificationInterval, val count: Int): ScheduleKind() } diff --git a/plugins/notification/android/src/main/java/TauriNotificationManager.kt b/plugins/notification/android/src/main/java/TauriNotificationManager.kt index ab5dc3de..072e8dfc 100644 --- a/plugins/notification/android/src/main/java/TauriNotificationManager.kt +++ b/plugins/notification/android/src/main/java/TauriNotificationManager.kt @@ -1,6 +1,5 @@ package app.tauri.notification -import android.Manifest import android.annotation.SuppressLint import android.app.Activity import android.app.AlarmManager @@ -11,12 +10,12 @@ import android.content.BroadcastReceiver import android.content.ContentResolver import android.content.Context import android.content.Intent -import android.content.pm.PackageManager import android.graphics.Color import android.media.AudioAttributes import android.net.Uri import android.os.Build -import androidx.core.app.ActivityCompat +import android.os.Build.VERSION.SDK_INT +import android.os.UserManager import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.core.app.RemoteInput @@ -38,7 +37,7 @@ const val DEFAULT_PRESS_ACTION = "tap" class TauriNotificationManager( private val storage: NotificationStorage, - private val activity: Activity, + private val activity: Activity?, private val context: Context, private val config: JSObject ) { @@ -140,12 +139,9 @@ class TauriNotificationManager( // TODO Progressbar support // TODO System categories (DO_NOT_DISTURB etc.) - // TODO control visibility by flag Notification.VISIBILITY_PRIVATE - // TODO Group notifications (setGroup, setGroupSummary, setNumber) // TODO use NotificationCompat.MessagingStyle for latest API // TODO expandable notification NotificationCompat.MessagingStyle // TODO media style notification support NotificationCompat.MediaStyle - // TODO custom small/large icons @SuppressLint("MissingPermission") private fun buildNotification( notificationManager: NotificationManagerCompat, @@ -198,7 +194,7 @@ class TauriNotificationManager( mBuilder.setSubText(notification.summary) } } - mBuilder.setVisibility(NotificationCompat.VISIBILITY_PRIVATE) + mBuilder.setVisibility(notification.visibility ?: NotificationCompat.VISIBILITY_PRIVATE) mBuilder.setOnlyAlertOnce(true) mBuilder.setSmallIcon(notification.getSmallIcon(context, getDefaultSmallIcon(context))) mBuilder.setLargeIcon(notification.getLargeIcon(context)) @@ -221,13 +217,6 @@ class TauriNotificationManager( // val notificationJson = JSObject(notification.source ?: "") } catch (_: JSONException) { } - if (ActivityCompat.checkSelfPermission( - activity, - Manifest.permission.POST_NOTIFICATIONS - ) != PackageManager.PERMISSION_GRANTED - ) { - return - } notificationManager.notify(notification.id, buildNotification) } } @@ -297,7 +286,12 @@ class TauriNotificationManager( } private fun buildIntent(notification: Notification, action: String?): Intent { - val intent = Intent(context, activity.javaClass) + val intent = if (activity != null) { + Intent(context, activity.javaClass) + } else { + val packageName = context.packageName + context.packageManager.getLaunchIntentForPackage(packageName)!! + } intent.action = Intent.ACTION_MAIN intent.addCategory(Intent.CATEGORY_LAUNCHER) intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP @@ -537,4 +531,41 @@ class TimedNotificationPublisher : BroadcastReceiver() { var NOTIFICATION_KEY = "NotificationPublisher.notification" var CRON_KEY = "NotificationPublisher.cron" } -} \ No newline at end of file +} + +class LocalNotificationRestoreReceiver : BroadcastReceiver() { + @SuppressLint("ObsoleteSdkInt") + override fun onReceive(context: Context, intent: Intent) { + if (SDK_INT >= Build.VERSION_CODES.N) { + val um = context.getSystemService( + UserManager::class.java + ) + if (um == null || !um.isUserUnlocked) return + } + val storage = NotificationStorage(context) + val ids = storage.getSavedNotificationIds() + val notifications = mutableListOf() + val updatedNotifications = mutableListOf() + for (id in ids) { + val notification = storage.getSavedNotification(id) ?: continue + val schedule = notification.schedule + if (schedule != null && schedule.kind is ScheduleKind.At) { + val at: Date = schedule.kind.date + if (at.before(Date())) { + // modify the scheduled date in order to show notifications that would have been delivered while device was off. + val newDateTime = Date().time + 15 * 1000 + schedule.kind.date = Date(newDateTime) + updatedNotifications.add(notification) + } + } + notifications.add(notification) + } + if (updatedNotifications.size > 0) { + storage.appendNotifications(updatedNotifications) + } + + // TODO: deserialize configuration + val notificationManager = TauriNotificationManager(storage, null, context, JSObject()) + notificationManager.schedule(notifications) + } +} diff --git a/plugins/notification/guest-js/index.ts b/plugins/notification/guest-js/index.ts index ad021cb4..fe83fd59 100644 --- a/plugins/notification/guest-js/index.ts +++ b/plugins/notification/guest-js/index.ts @@ -131,6 +131,14 @@ interface Options { * Changes the notification presentation to be silent on iOS (no badge, no sound, not listed). */ silent?: boolean + /** + * Notification visibility. + */ + visibility?: Visibility + /** + * Sets the number of items this notification represents on Android. + */ + number?: number } type ScheduleInterval = {