@capacitor/background-runner 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CapacitorBackgroundRunner.podspec +17 -0
  2. package/README.md +310 -0
  3. package/android/build.gradle +67 -0
  4. package/android/src/main/AndroidManifest.xml +12 -0
  5. package/android/src/main/java/io/ionic/backgroundrunner/plugin/BackgroundRunner.kt +166 -0
  6. package/android/src/main/java/io/ionic/backgroundrunner/plugin/BackgroundRunnerPlugin.kt +102 -0
  7. package/android/src/main/java/io/ionic/backgroundrunner/plugin/CoroutineHelper.kt +21 -0
  8. package/android/src/main/java/io/ionic/backgroundrunner/plugin/RunnerConfig.kt +33 -0
  9. package/android/src/main/java/io/ionic/backgroundrunner/plugin/RunnerWorker.kt +46 -0
  10. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Device.kt +99 -0
  11. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Geolocation.kt +55 -0
  12. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/KV.kt +29 -0
  13. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/NetworkStatus.kt +10 -0
  14. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Notification.kt +59 -0
  15. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/Notifications.kt +158 -0
  16. package/android/src/main/java/io/ionic/backgroundrunner/plugin/api/TimedNotificationPublisher.kt +40 -0
  17. package/android/src/main/libs/android-js-engine-release.aar +0 -0
  18. package/android/src/main/res/.gitkeep +0 -0
  19. package/dist/docs.json +208 -0
  20. package/dist/esm/apis.d.ts +52 -0
  21. package/dist/esm/apis.js +2 -0
  22. package/dist/esm/apis.js.map +1 -0
  23. package/dist/esm/definitions.d.ts +82 -0
  24. package/dist/esm/definitions.js +3 -0
  25. package/dist/esm/definitions.js.map +1 -0
  26. package/dist/esm/index.d.ts +5 -0
  27. package/dist/esm/index.js +8 -0
  28. package/dist/esm/index.js.map +1 -0
  29. package/dist/esm/web.d.ts +8 -0
  30. package/dist/esm/web.js +16 -0
  31. package/dist/esm/web.js.map +1 -0
  32. package/dist/plugin.cjs.js +32 -0
  33. package/dist/plugin.cjs.js.map +1 -0
  34. package/dist/plugin.js +35 -0
  35. package/dist/plugin.js.map +1 -0
  36. package/ios/Plugin/BackgroundRunner.swift +223 -0
  37. package/ios/Plugin/BackgroundRunnerErrors.swift +8 -0
  38. package/ios/Plugin/BackgroundRunnerPlugin.h +10 -0
  39. package/ios/Plugin/BackgroundRunnerPlugin.m +11 -0
  40. package/ios/Plugin/BackgroundRunnerPlugin.swift +161 -0
  41. package/ios/Plugin/CapacitorAPI/Device.swift +116 -0
  42. package/ios/Plugin/CapacitorAPI/Geolocation.swift +190 -0
  43. package/ios/Plugin/CapacitorAPI/KV.swift +22 -0
  44. package/ios/Plugin/CapacitorAPI/Notifications.swift +165 -0
  45. package/ios/Plugin/CapacitorAPI/Reachability.swift +395 -0
  46. package/ios/Plugin/CapacitorAPI/Wearable.swift +48 -0
  47. package/ios/Plugin/Context+CapacitorAPI.swift +11 -0
  48. package/ios/Plugin/Info.plist +24 -0
  49. package/ios/Plugin/RunnerConfig.swift +36 -0
  50. package/ios/Plugin/RunnerEngine/Context.swift +151 -0
  51. package/ios/Plugin/RunnerEngine/Errors.swift +8 -0
  52. package/ios/Plugin/RunnerEngine/JSConsole.swift +38 -0
  53. package/ios/Plugin/RunnerEngine/JSCrypto.swift +38 -0
  54. package/ios/Plugin/RunnerEngine/JSError.swift +33 -0
  55. package/ios/Plugin/RunnerEngine/JSFetch.swift +50 -0
  56. package/ios/Plugin/RunnerEngine/JSResponse.swift +69 -0
  57. package/ios/Plugin/RunnerEngine/JSTextDecoder.swift +60 -0
  58. package/ios/Plugin/RunnerEngine/JSTextEncoder.swift +12 -0
  59. package/ios/Plugin/RunnerEngine/Runner.swift +23 -0
  60. package/ios/Plugin/WatchConnectivtySessionDelegate.swift +34 -0
  61. package/package.json +81 -0
@@ -0,0 +1,21 @@
1
+ package io.ionic.backgroundrunner.plugin
2
+
3
+ import kotlinx.coroutines.flow.StateFlow
4
+ import kotlinx.coroutines.flow.first
5
+
6
+ class ConditionalAwait<T>(
7
+ private val stateFlow: StateFlow<T>,
8
+ private val condition: (T) -> Boolean
9
+ ) {
10
+ suspend fun await(): T {
11
+ val nowValue = stateFlow.value
12
+ return if (condition(nowValue)) {
13
+ nowValue
14
+ } else {
15
+ stateFlow.first { condition(it) }
16
+ }
17
+ }
18
+ }
19
+
20
+ suspend fun <T> StateFlow<T>.conditionalAwait(condition: (T) -> Boolean): T =
21
+ ConditionalAwait(this, condition).await()
@@ -0,0 +1,33 @@
1
+ package io.ionic.backgroundrunner.plugin;
2
+
3
+ import org.json.JSONObject
4
+ import java.lang.Exception
5
+
6
+ class RunnerConfig(configObject: JSONObject) {
7
+ val label: String
8
+ val src: String
9
+ val autoSchedule: Boolean
10
+ val repeats: Boolean
11
+ var event: String
12
+ val interval: Int?
13
+
14
+ init {
15
+ val labelStr = configObject.getString("label")
16
+ if (labelStr.isNullOrEmpty()) {
17
+ throw Exception("runner label is missing or invalid")
18
+ }
19
+
20
+ val srcStr = configObject.getString("src")
21
+ if (srcStr.isNullOrEmpty()) {
22
+ throw Exception("runner source file path is missing or invalid")
23
+ }
24
+
25
+ label = labelStr
26
+ src = srcStr
27
+ autoSchedule = configObject.optBoolean("autoStart", false)
28
+ event = configObject.optString("event")
29
+ repeats = configObject.optBoolean("repeat")
30
+ interval = configObject.optInt("interval", 0)
31
+ }
32
+ }
33
+
@@ -0,0 +1,46 @@
1
+ package io.ionic.backgroundrunner.plugin;
2
+
3
+ import android.content.Context
4
+ import androidx.work.Data
5
+ import androidx.work.Worker
6
+ import androidx.work.WorkerParameters
7
+ import kotlinx.coroutines.flow.MutableStateFlow
8
+ import kotlinx.coroutines.runBlocking
9
+ import org.json.JSONObject
10
+
11
+ class RunnerWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
12
+ override fun doWork(): Result {
13
+ try {
14
+ val label = this.inputData.getString("label") ?: ""
15
+ val src = this.inputData.getString("src") ?: ""
16
+ val event = this.inputData.getString("event") ?: ""
17
+
18
+ if (label.isEmpty() || src.isEmpty() || event.isEmpty()) {
19
+ throw Exception("label is empty")
20
+ }
21
+
22
+ val runnerConfigObj = JSONObject()
23
+ runnerConfigObj.put("label", label)
24
+ runnerConfigObj.put("src", src)
25
+ runnerConfigObj.put("event", event)
26
+ runnerConfigObj.put("autoStart", false)
27
+ runnerConfigObj.put("repeats", false)
28
+ runnerConfigObj.put("interval", 0)
29
+
30
+ val config = RunnerConfig(runnerConfigObj)
31
+
32
+ runBlocking {
33
+ val impl = BackgroundRunner.getInstance(this@RunnerWorker.applicationContext)
34
+ impl.execute(this@RunnerWorker.applicationContext, config, JSONObject())
35
+ }
36
+
37
+ return Result.success()
38
+ } catch (ex: Exception) {
39
+ val data = Data.Builder()
40
+ .putString("error", ex.toString())
41
+ .build()
42
+
43
+ return Result.failure(data)
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,99 @@
1
+ package io.ionic.backgroundrunner.plugin.api
2
+
3
+ import android.content.Context
4
+ import android.content.Intent
5
+ import android.content.IntentFilter
6
+ import android.net.ConnectivityManager
7
+ import android.net.Network
8
+ import android.net.NetworkCapabilities
9
+ import android.os.BatteryManager
10
+ import android.os.Build
11
+ import io.ionic.android_js_engine.api.DeviceAPI
12
+ import org.json.JSONObject
13
+
14
+
15
+ class Device (context: android.content.Context) : DeviceAPI {
16
+ private val context: android.content.Context
17
+ private val connectivityManager: ConnectivityManager
18
+
19
+ init {
20
+ this.context = context
21
+ this.connectivityManager = this.context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager;
22
+ }
23
+
24
+ override fun getBatteryStatus(): String {
25
+ val batteryInfo = JSONObject()
26
+ batteryInfo.put("batteryLevel", this.getBatteryLevel())
27
+ batteryInfo.put("isCharging", this.isCharging())
28
+
29
+ return batteryInfo.toString()
30
+ }
31
+
32
+ override fun getNetworkStatus(): String {
33
+ var status = NetworkStatus()
34
+
35
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
36
+ val activeNetwork: Network? = this.connectivityManager.activeNetwork
37
+ val capabilities: NetworkCapabilities? =
38
+ this.connectivityManager.getNetworkCapabilities(this.connectivityManager.activeNetwork)
39
+ if (activeNetwork != null && capabilities != null) {
40
+ status.connected =
41
+ capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) &&
42
+ capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
43
+ if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
44
+ status.connectionType = NetworkStatus.ConnectionType.WIFI
45
+ } else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
46
+ status.connectionType = NetworkStatus.ConnectionType.CELLULAR
47
+ } else {
48
+ status.connectionType = NetworkStatus.ConnectionType.UNKNOWN
49
+ }
50
+ }
51
+ } else {
52
+ status = getAndParseNetworkInfo()
53
+ }
54
+
55
+ val networkInfo = JSONObject()
56
+ networkInfo.put("connected", status.connected)
57
+ networkInfo.put("connectionType", status.connectionType)
58
+
59
+ return networkInfo.toString()
60
+ }
61
+
62
+ private fun getBatteryLevel(): Float {
63
+ val ifilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
64
+ val batteryStatus = context.registerReceiver(null, ifilter)
65
+ var level = -1
66
+ var scale = -1
67
+ if (batteryStatus != null) {
68
+ level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
69
+ scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
70
+ }
71
+ return level / scale.toFloat()
72
+ }
73
+
74
+ private fun isCharging(): Boolean {
75
+ val ifilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
76
+ val batteryStatus = context.registerReceiver(null, ifilter)
77
+ if (batteryStatus != null) {
78
+ val status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
79
+ return status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL
80
+ }
81
+ return false
82
+ }
83
+
84
+ @SuppressWarnings("deprecation")
85
+ private fun getAndParseNetworkInfo(): NetworkStatus {
86
+ val networkStatus = NetworkStatus()
87
+ val networkInfo = connectivityManager.activeNetworkInfo
88
+ if (networkInfo != null) {
89
+ networkStatus.connected = networkInfo.isConnected
90
+ val typeName = networkInfo.typeName
91
+ if (typeName == "WIFI") {
92
+ networkStatus.connectionType = NetworkStatus.ConnectionType.WIFI
93
+ } else if (typeName == "MOBILE") {
94
+ networkStatus.connectionType = NetworkStatus.ConnectionType.CELLULAR
95
+ }
96
+ }
97
+ return networkStatus
98
+ }
99
+ }
@@ -0,0 +1,55 @@
1
+ package io.ionic.backgroundrunner.plugin.api
2
+
3
+ import android.content.Context
4
+ import android.location.Location
5
+ import android.location.LocationManager
6
+ import androidx.core.location.LocationManagerCompat
7
+ import io.ionic.android_js_engine.api.GeolocationAPI
8
+ import org.json.JSONObject
9
+ import java.lang.Exception
10
+
11
+ class Geolocation(context: android.content.Context) : GeolocationAPI {
12
+ val manager: LocationManager
13
+ val context: android.content.Context
14
+
15
+ init {
16
+ this.context = context
17
+ this.manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
18
+ }
19
+
20
+ companion object {
21
+ fun isEnabled(context: Context): Boolean {
22
+ val manager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
23
+ return LocationManagerCompat.isLocationEnabled(manager);
24
+ }
25
+ }
26
+
27
+ override fun getCurrentPosition(): String? {
28
+ if (!isEnabled(this.context)) {
29
+ throw Exception("Capacitor Geolocation location services not enabled")
30
+ }
31
+
32
+ var lastLocation: Location? = null
33
+
34
+ this.manager.allProviders.forEach {
35
+ val tempLocation = this.manager.getLastKnownLocation(it)
36
+ if (tempLocation != null) {
37
+ if (lastLocation == null || lastLocation!!.elapsedRealtimeNanos > tempLocation.elapsedRealtimeNanos) {
38
+ lastLocation = tempLocation
39
+ }
40
+ }
41
+ }
42
+
43
+ val location = lastLocation ?: return null
44
+
45
+ val locationInfo = JSONObject();
46
+ locationInfo.put("latitude", location.latitude)
47
+ locationInfo.put("longitude", location.longitude)
48
+ locationInfo.put("accuracy", location.accuracy)
49
+ locationInfo.put("altitude", location.altitude)
50
+ locationInfo.put("speed", location.speed)
51
+ locationInfo.put("heading", location.bearing)
52
+
53
+ return locationInfo.toString()
54
+ }
55
+ }
@@ -0,0 +1,29 @@
1
+ package io.ionic.backgroundrunner.plugin.api
2
+
3
+ import android.content.Context
4
+ import android.content.SharedPreferences
5
+ import io.ionic.android_js_engine.api.KVAPI
6
+
7
+ class KV (context: android.content.Context, label: String) : KVAPI {
8
+ private val prefs: SharedPreferences
9
+
10
+ init {
11
+ this.prefs = context.getSharedPreferences(label, Context.MODE_PRIVATE)
12
+ }
13
+
14
+ override fun set(key: String, value: String) {
15
+ val editor: SharedPreferences.Editor = prefs.edit()
16
+ editor.putString(key, value)
17
+ editor.apply()
18
+ }
19
+
20
+ override fun get(key: String): String? {
21
+ return prefs.getString(key, null)
22
+ }
23
+
24
+ override fun remove(key: String) {
25
+ val editor: SharedPreferences.Editor = prefs.edit()
26
+ editor.remove(key)
27
+ editor.apply()
28
+ }
29
+ }
@@ -0,0 +1,10 @@
1
+ package io.ionic.backgroundrunner.plugin.api
2
+
3
+ class NetworkStatus {
4
+ enum class ConnectionType(val connectionType: String) {
5
+ WIFI("wifi"), CELLULAR("cellular"), NONE("none"), UNKNOWN("unknown");
6
+ }
7
+
8
+ var connected = false
9
+ var connectionType = ConnectionType.NONE
10
+ }
@@ -0,0 +1,59 @@
1
+ package io.ionic.backgroundrunner.plugin.api
2
+
3
+ import android.annotation.SuppressLint
4
+ import com.getcapacitor.plugin.util.AssetUtil
5
+ import org.json.JSONObject
6
+ import java.text.SimpleDateFormat
7
+ import java.util.Date
8
+ import java.util.TimeZone
9
+
10
+ @SuppressLint("SimpleDateFormat")
11
+ class Notification(jsonObject: JSONObject) {
12
+ var id: Int
13
+ var title: String
14
+ var body: String
15
+ var ongoing: Boolean
16
+ var autoCancel: Boolean
17
+ var scheduleAt: Date
18
+ var largeBody: String? = null
19
+ var summaryText: String? = null
20
+ var sound: String? = null
21
+ var smallIcon: String? = null
22
+ var largeIcon: String? = null
23
+ var actionTypeID: String? = null
24
+ var extra: HashMap<String, Any>? = null
25
+ var group: String? = null
26
+ var groupSummary: String? = null
27
+ var channelId: String? = null
28
+
29
+ private val jsDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
30
+
31
+ init {
32
+ id = jsonObject.optInt("id", -1)
33
+ title = jsonObject.optString("title", "")
34
+ body = jsonObject.optString("body", "")
35
+ ongoing = jsonObject.optBoolean("ongoing", false)
36
+ autoCancel = jsonObject.optBoolean("autoCancel", false)
37
+
38
+ val scheduleDateString = jsonObject.getString("scheduleAt")
39
+
40
+ val sdf = SimpleDateFormat(jsDateFormat)
41
+ val parsedDate = sdf.parse(scheduleDateString)
42
+
43
+ scheduleAt = parsedDate ?: Date()
44
+ }
45
+
46
+ fun smallIcon(context: android.content.Context, defaultIcon: Int): Int {
47
+ var resId = AssetUtil.RESOURCE_ID_ZERO_VALUE
48
+
49
+ if (smallIcon != null) {
50
+ resId = AssetUtil.getResourceID(context, smallIcon, "drawable")
51
+ }
52
+
53
+ if (resId == AssetUtil.RESOURCE_ID_ZERO_VALUE) {
54
+ resId = defaultIcon
55
+ }
56
+
57
+ return resId
58
+ }
59
+ }
@@ -0,0 +1,158 @@
1
+ package io.ionic.backgroundrunner.plugin.api
2
+
3
+ import android.R.drawable
4
+ import android.app.AlarmManager
5
+ import android.app.NotificationChannel
6
+ import android.app.NotificationManager
7
+ import android.app.PendingIntent
8
+ import android.content.ContentResolver
9
+ import android.content.Context
10
+ import android.content.Intent
11
+ import android.media.AudioAttributes
12
+ import android.net.Uri
13
+ import android.os.Build
14
+ import androidx.core.app.NotificationCompat
15
+ import androidx.core.app.NotificationManagerCompat
16
+ import com.getcapacitor.Logger.config
17
+ import com.getcapacitor.plugin.util.AssetUtil
18
+ import io.ionic.android_js_engine.api.NotificationsAPI
19
+ import org.json.JSONArray
20
+
21
+ class Notifications(context: Context) : NotificationsAPI {
22
+ private val manager: NotificationManagerCompat
23
+ private val context: Context
24
+
25
+ var defaultSmallIconID = AssetUtil.RESOURCE_ID_ZERO_VALUE
26
+ var defaultSoundID = AssetUtil.RESOURCE_ID_ZERO_VALUE
27
+
28
+ init {
29
+ this.context = context
30
+ this.manager = NotificationManagerCompat.from(context)
31
+
32
+ this.createNotificationChannel()
33
+ }
34
+ companion object {
35
+ const val notificationIntentKey = "LocalNotificationId"
36
+ const val defaultNotificationChannelID = "default"
37
+ }
38
+
39
+ override fun schedule(jsonString: String) {
40
+ val notifications = mutableListOf<Notification>()
41
+
42
+ try {
43
+ val options = JSONArray(jsonString)
44
+ for(i in 0 until options.length()) {
45
+ notifications.add(i, Notification(options.getJSONObject(i)))
46
+ }
47
+ } catch(ex: Exception) {
48
+ throw Exception("options must be an array: ${ex.localizedMessage}")
49
+ }
50
+
51
+ notifications.forEach {
52
+ val channelId = it.channelId ?: defaultNotificationChannelID
53
+
54
+ val builder = NotificationCompat.Builder(context, channelId)
55
+ builder.setContentTitle(it.title)
56
+ builder.setContentText(it.body)
57
+ builder.setAutoCancel(it.autoCancel)
58
+ builder.setOngoing(it.ongoing)
59
+ builder.priority = NotificationCompat.PRIORITY_DEFAULT
60
+ builder.setGroupSummary(it.groupSummary != null)
61
+ builder.setSmallIcon(it.smallIcon(this.context, getDefaultSmallIcon()))
62
+
63
+ val largeBody = it.largeBody
64
+ if (largeBody != null) {
65
+ val style = NotificationCompat.BigTextStyle()
66
+ style.bigText(largeBody)
67
+ style.setSummaryText(it.summaryText)
68
+ }
69
+
70
+ scheduleNotification(builder.build(), it)
71
+ }
72
+ }
73
+
74
+ private fun createNotificationChannel() {
75
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
76
+ val name: CharSequence = "Default"
77
+ val description = "Default"
78
+ val importance = NotificationManager.IMPORTANCE_DEFAULT
79
+ val channel = NotificationChannel(defaultNotificationChannelID, name, importance)
80
+ channel.description = description
81
+ val audioAttributes = AudioAttributes.Builder()
82
+ .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
83
+ .setUsage(AudioAttributes.USAGE_ALARM)
84
+ .build()
85
+ val soundUri = this.getDefaultSoundUrl(context)
86
+ if (soundUri != null) {
87
+ channel.setSound(soundUri, audioAttributes)
88
+ }
89
+
90
+ val notificationManager = context.getSystemService(
91
+ NotificationManager::class.java
92
+ )
93
+ notificationManager.createNotificationChannel(channel)
94
+ }
95
+ }
96
+
97
+ private fun getDefaultSoundUrl(context: Context): Uri? {
98
+ val soundId = getDefaultSound(context)
99
+ return if (soundId != AssetUtil.RESOURCE_ID_ZERO_VALUE) {
100
+ Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.packageName + "/" + soundId)
101
+ } else null
102
+ }
103
+
104
+ private fun getDefaultSmallIcon(): Int {
105
+ if (defaultSmallIconID != AssetUtil.RESOURCE_ID_ZERO_VALUE) return defaultSmallIconID
106
+ var resId = AssetUtil.RESOURCE_ID_ZERO_VALUE
107
+ var smallIconConfigResourceName = config.getString("smallIcon")
108
+ smallIconConfigResourceName = AssetUtil.getResourceBaseName(smallIconConfigResourceName)
109
+ if (smallIconConfigResourceName != null) {
110
+ resId = AssetUtil.getResourceID(context, smallIconConfigResourceName, "drawable")
111
+ }
112
+ if (resId == AssetUtil.RESOURCE_ID_ZERO_VALUE) {
113
+ resId = drawable.ic_dialog_info
114
+ }
115
+ defaultSmallIconID = resId
116
+ return resId
117
+ }
118
+
119
+ private fun getDefaultSound(context: Context): Int {
120
+ if (defaultSoundID != AssetUtil.RESOURCE_ID_ZERO_VALUE) return defaultSoundID
121
+ var resId = AssetUtil.RESOURCE_ID_ZERO_VALUE
122
+ var soundConfigResourceName = config.getString("sound")
123
+ soundConfigResourceName = AssetUtil.getResourceBaseName(soundConfigResourceName)
124
+ if (soundConfigResourceName != null) {
125
+ resId = AssetUtil.getResourceID(context, soundConfigResourceName, "raw")
126
+ }
127
+ defaultSoundID = resId
128
+ return resId
129
+ }
130
+
131
+ private fun scheduleNotification(notification: android.app.Notification, request: Notification) {
132
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
133
+ val intent = Intent(context, TimedNotificationPublisher::class.java)
134
+ intent.putExtra(notificationIntentKey, request.id)
135
+ intent.putExtra(TimedNotificationPublisher.NOTIFICATION_KEY, notification)
136
+ var flags = PendingIntent.FLAG_CANCEL_CURRENT
137
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
138
+ flags = flags or PendingIntent.FLAG_MUTABLE
139
+ }
140
+
141
+ val pendingIntent =
142
+ PendingIntent.getBroadcast(context, request.id, intent, flags)
143
+
144
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) {
145
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
146
+ alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, request.scheduleAt.time, pendingIntent);
147
+ } else {
148
+ alarmManager.set(AlarmManager.RTC, request.scheduleAt.time, pendingIntent);
149
+ }
150
+ } else {
151
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
152
+ alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, request.scheduleAt.time, pendingIntent);
153
+ } else {
154
+ alarmManager.setExact(AlarmManager.RTC, request.scheduleAt.time, pendingIntent);
155
+ }
156
+ }
157
+ }
158
+ }
@@ -0,0 +1,40 @@
1
+ package io.ionic.backgroundrunner.plugin.api
2
+
3
+ import android.app.NotificationManager
4
+ import android.content.BroadcastReceiver
5
+ import android.content.Context
6
+ import android.content.Intent
7
+ import android.os.Build
8
+ import com.getcapacitor.Logger
9
+
10
+ class TimedNotificationPublisher : BroadcastReceiver() {
11
+ companion object {
12
+ const val NOTIFICATION_KEY = "NotificationPublisher.notification"
13
+ }
14
+
15
+ override fun onReceive(context: Context?, intent: Intent?) {
16
+ val notificationManager =
17
+ context!!.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
18
+
19
+ val notification: android.app.Notification?
20
+
21
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
22
+ notification = intent!!.getParcelableExtra(NOTIFICATION_KEY, android.app.Notification::class.java)
23
+ } else {
24
+ notification = getParcelableExtraLegacy(intent!!)
25
+ }
26
+
27
+ notification?.`when` = System.currentTimeMillis()
28
+
29
+ val id =
30
+ intent.getIntExtra(Notifications.notificationIntentKey, Int.MIN_VALUE)
31
+ if (id == Int.MIN_VALUE) {
32
+ Logger.error(Logger.tags("LN"), "No valid id supplied", null)
33
+ }
34
+ notificationManager.notify(id, notification)
35
+ }
36
+
37
+ private fun getParcelableExtraLegacy(intent: Intent): android.app.Notification? {
38
+ return intent.getParcelableExtra(NOTIFICATION_KEY)
39
+ }
40
+ }
File without changes