react-native-notify-kit 9.1.22 → 9.2.0
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.
- package/README.md +5 -2
- package/android/build.gradle +67 -35
- package/android/proguard-rules.pro +14 -1
- package/android/schemas/app.notifee.core.database.NotifeeCoreDatabase/1.json +44 -0
- package/android/schemas/app.notifee.core.database.NotifeeCoreDatabase/2.json +51 -0
- package/android/src/androidTest/java/app/notifee/core/ExampleInstrumentedTest.java +25 -0
- package/android/src/androidTest/java/app/notifee/core/database/NotifeeCoreDatabaseTest.java +55 -0
- package/android/src/main/AndroidManifest.xml +71 -4
- package/android/src/main/java/app/notifee/core/AlarmPermissionBroadcastReceiver.java +25 -0
- package/android/src/main/java/app/notifee/core/BlockStateBroadcastReceiver.java +164 -0
- package/android/src/main/java/app/notifee/core/ChannelManager.java +350 -0
- package/android/src/main/java/app/notifee/core/ContextHolder.java +33 -0
- package/android/src/main/java/app/notifee/core/EventBus.java +63 -0
- package/android/src/main/java/app/notifee/core/EventSubscriber.java +82 -0
- package/android/src/main/java/app/notifee/core/ForegroundService.java +347 -0
- package/android/src/main/java/app/notifee/core/InitProvider.java +93 -0
- package/android/src/main/java/app/notifee/core/KeepForSdk.java +26 -0
- package/android/src/main/java/app/notifee/core/Logger.java +68 -0
- package/android/src/main/java/app/notifee/core/Notifee.java +570 -0
- package/android/src/main/java/app/notifee/core/NotifeeAlarmManager.java +352 -0
- package/android/src/main/java/app/notifee/core/NotificationAlarmReceiver.java +42 -0
- package/android/src/main/java/app/notifee/core/NotificationManager.java +939 -0
- package/android/src/main/java/app/notifee/core/NotificationPendingIntent.java +191 -0
- package/android/src/main/java/app/notifee/core/NotificationReceiverActivity.java +39 -0
- package/android/src/main/java/app/notifee/core/NotificationReceiverHandler.java +144 -0
- package/android/src/main/java/app/notifee/core/Preferences.java +79 -0
- package/android/src/main/java/app/notifee/core/RebootBroadcastReceiver.java +39 -0
- package/android/src/main/java/app/notifee/core/ReceiverService.java +281 -0
- package/android/src/main/java/app/notifee/core/Worker.java +87 -0
- package/android/src/main/java/app/notifee/core/database/NotifeeCoreDatabase.java +77 -0
- package/android/src/main/java/app/notifee/core/database/WorkDataDao.java +52 -0
- package/android/src/main/java/app/notifee/core/database/WorkDataEntity.java +68 -0
- package/android/src/main/java/app/notifee/core/database/WorkDataRepository.java +107 -0
- package/android/src/main/java/app/notifee/core/event/BlockStateEvent.java +102 -0
- package/android/src/main/java/app/notifee/core/event/ForegroundServiceEvent.java +48 -0
- package/android/src/main/java/app/notifee/core/event/InitialNotificationEvent.java +50 -0
- package/android/src/main/java/app/notifee/core/event/LogEvent.java +74 -0
- package/android/src/main/java/app/notifee/core/event/MainComponentEvent.java +33 -0
- package/android/src/main/java/app/notifee/core/event/NotificationEvent.java +107 -0
- package/android/src/main/java/app/notifee/core/interfaces/EventListener.java +39 -0
- package/android/src/main/java/app/notifee/core/interfaces/MethodCallResult.java +27 -0
- package/android/src/main/java/app/notifee/core/model/ChannelGroupModel.java +48 -0
- package/android/src/main/java/app/notifee/core/model/ChannelModel.java +126 -0
- package/android/src/main/java/app/notifee/core/model/IntervalTriggerModel.java +63 -0
- package/android/src/main/java/app/notifee/core/model/NotificationAndroidActionModel.java +125 -0
- package/android/src/main/java/app/notifee/core/model/NotificationAndroidModel.java +688 -0
- package/android/src/main/java/app/notifee/core/model/NotificationAndroidPressActionModel.java +150 -0
- package/android/src/main/java/app/notifee/core/model/NotificationAndroidStyleModel.java +336 -0
- package/android/src/main/java/app/notifee/core/model/NotificationModel.java +72 -0
- package/android/src/main/java/app/notifee/core/model/TimestampTriggerModel.java +206 -0
- package/android/src/main/java/app/notifee/core/model/package-info.java +4 -0
- package/android/src/main/java/app/notifee/core/utility/AlarmUtils.java +52 -0
- package/android/src/main/java/app/notifee/core/utility/Callbackable.java +12 -0
- package/android/src/main/java/app/notifee/core/utility/ColorUtils.java +62 -0
- package/android/src/main/java/app/notifee/core/utility/ExtendedListenableFuture.java +84 -0
- package/android/src/main/java/app/notifee/core/utility/IntentUtils.java +146 -0
- package/android/src/main/java/app/notifee/core/utility/ObjectUtils.java +191 -0
- package/android/src/main/java/app/notifee/core/utility/PowerManagerUtils.java +338 -0
- package/android/src/main/java/app/notifee/core/utility/ResourceUtils.java +308 -0
- package/android/src/main/java/app/notifee/core/utility/TextUtils.java +28 -0
- package/android/src/main/java/app/notifee/core/utility/package-info.java +4 -0
- package/android/src/test/java/app/notifee/core/model/NotificationAndroidPressActionModelTest.java +56 -0
- package/android/src/test/java/app/notifee/core/model/TimestampTriggerModelTest.java +44 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +2 -2
- package/src/version.ts +1 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.aar +0 -0
- package/android/libs/app/notifee/core/202108261754/core-202108261754.aar.md5 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.aar.sha1 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.aar.sha256 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.aar.sha512 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.module +0 -146
- package/android/libs/app/notifee/core/202108261754/core-202108261754.module.md5 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.module.sha1 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.module.sha256 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.module.sha512 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.pom +0 -64
- package/android/libs/app/notifee/core/202108261754/core-202108261754.pom.md5 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.pom.sha1 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.pom.sha256 +0 -1
- package/android/libs/app/notifee/core/202108261754/core-202108261754.pom.sha512 +0 -1
- package/android/libs/app/notifee/core/maven-metadata.xml +0 -13
- package/android/libs/app/notifee/core/maven-metadata.xml.md5 +0 -1
- package/android/libs/app/notifee/core/maven-metadata.xml.sha1 +0 -1
- package/android/libs/app/notifee/core/maven-metadata.xml.sha256 +0 -1
- package/android/libs/app/notifee/core/maven-metadata.xml.sha512 +0 -1
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ This fork fills the gap: it preserves all of Notifee's advanced features, migrat
|
|
|
42
42
|
- License: `Apache-2.0`
|
|
43
43
|
- Full changelog: [CHANGELOG.md](CHANGELOG.md)
|
|
44
44
|
|
|
45
|
-
The native core (NotifeeCore) is
|
|
45
|
+
The native core (NotifeeCore) is compiled from source as part of the bridge module (since 9.2.0) and the public API is **100% compatible** with the original `@notifee/react-native` — migration is a safe, drop-in replacement.
|
|
46
46
|
|
|
47
47
|
## Installation
|
|
48
48
|
|
|
@@ -251,8 +251,9 @@ This fork is a complete migration to React Native's **New Architecture**:
|
|
|
251
251
|
- **iOS bridge uses Objective-C++** with `NativeNotifeeModuleSpecJSI` TurboModule conformance
|
|
252
252
|
- **Minimum React Native 0.73**, development target **0.84**
|
|
253
253
|
- **Toolchain**: Yarn 4, Node 22+, Java 17, compileSdk/targetSdk 35
|
|
254
|
+
- **Single Android module** — the original Notifee shipped a pre-compiled AAR bundled inside the npm tarball under a frozen Maven coordinate; this fork compiles the core from source as part of the React Native bridge module on every consumer build. Eliminates the `FAIL_ON_PROJECT_REPOS` issue on RN 0.74+ and the Gradle cache staleness bug that could serve outdated bytecode after `yarn upgrade`.
|
|
254
255
|
- **Core notification logic (NotifeeCore) is unchanged** — the public API is fully compatible with the original Notifee
|
|
255
|
-
- **
|
|
256
|
+
- **20 upstream bugs fixed** — see [Bugs Fixed from Upstream Notifee](#bugs-fixed-from-upstream-notifee) below
|
|
256
257
|
- **Reliable trigger notifications** — AlarmManager is the default backend instead of WorkManager, with automatic fallback when exact alarm permission is not granted
|
|
257
258
|
- **New API: `setNotificationConfig()`** — opt-out flag to prevent Notifee from intercepting iOS remote notification handlers (see [New APIs](#new-apis) below)
|
|
258
259
|
|
|
@@ -281,6 +282,8 @@ This fork fixes the following bugs that were never resolved in the original Noti
|
|
|
281
282
|
| `!=` reference equality on String comparison in `NotificationPendingIntent` (latent — would activate when `getLaunchActivity()` returns a non-null value for `id=default`) | Android | Pre-existing (latent) | 9.1.19 |
|
|
282
283
|
| `pressAction.launchActivity` not defaulted at native layer when `pressAction.id === 'default'` | Android | N/A (defense-in-depth) | 9.1.19 |
|
|
283
284
|
| Duplicate symbols linker error when using NSE (`$NotifeeExtension = true`) with static frameworks — `NotifeeExtensionHelper` compiled by both `RNNotifee` and `RNNotifeeCore` pods | iOS | Pre-existing | 9.1.22 |
|
|
285
|
+
| `FAIL_ON_PROJECT_REPOS` rejection on RN 0.74+ — library injected a Maven repository into the consumer's `rootProject.allprojects` block, rejected by `dependencyResolutionManagement` mode | Android | N/A (architectural) | 9.2.0 |
|
|
286
|
+
| Stale Gradle cache could serve outdated AAR bytecode after `yarn upgrade` — same Maven coordinate reused across releases violated Gradle's coordinate-immutability assumption | Android | N/A (architectural) | 9.2.0 |
|
|
284
287
|
|
|
285
288
|
> **Note for apps requiring guaranteed exact alarms (alarm clocks, timers, calendars):**
|
|
286
289
|
> Add `<uses-permission android:name="android.permission.USE_EXACT_ALARM" />` to your app's
|
package/android/build.gradle
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import org.gradle.internal.jvm.Jvm
|
|
2
2
|
|
|
3
3
|
buildscript {
|
|
4
4
|
// The Android Gradle plugin is only required when opening the android folder stand-alone.
|
|
@@ -23,24 +23,21 @@ plugins {
|
|
|
23
23
|
id "org.jetbrains.kotlin.android"
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
ext {
|
|
27
|
+
jvmVersion = Jvm.current().javaVersion.majorVersion
|
|
28
|
+
if (jvmVersion != "8" && jvmVersion != "11" && jvmVersion != "17" && jvmVersion != "21") {
|
|
29
|
+
println "\n\n\n"
|
|
30
|
+
println "**************************************************************************************************************"
|
|
31
|
+
println "\n\n\n"
|
|
32
|
+
println "ERROR: Notifee builds with JVM LTS and current releases (currently major version 11, 17, or 21)."
|
|
33
|
+
println " Incompatible major version detected: '" + jvmVersion + "'"
|
|
34
|
+
println "\n\n\n"
|
|
35
|
+
println "**************************************************************************************************************"
|
|
36
|
+
println "\n\n\n"
|
|
37
|
+
System.exit(1)
|
|
37
38
|
}
|
|
38
|
-
return null
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
def notifeeDir = findNodeModulePath(projectDir, "react-native-notify-kit") ?: "$rootDir/../node_modules/react-native-notify-kit"
|
|
42
|
-
println ":${project.name} react-native-notify-kit found at $notifeeDir"
|
|
43
|
-
|
|
44
41
|
if (project.hasProperty('reactNativeProjects')) {
|
|
45
42
|
reactNativeProjects.each { dependent ->
|
|
46
43
|
project.evaluationDependsOn(dependent)
|
|
@@ -57,6 +54,15 @@ android {
|
|
|
57
54
|
minSdk = 24
|
|
58
55
|
targetSdk = 35
|
|
59
56
|
multiDexEnabled true
|
|
57
|
+
|
|
58
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
59
|
+
|
|
60
|
+
javaCompileOptions {
|
|
61
|
+
annotationProcessorOptions {
|
|
62
|
+
arguments = ["room.schemaLocation": "$projectDir/schemas".toString(),
|
|
63
|
+
eventBusIndex: 'app.notifee.core.EventBusIndex']
|
|
64
|
+
}
|
|
65
|
+
}
|
|
60
66
|
}
|
|
61
67
|
|
|
62
68
|
lint {
|
|
@@ -76,7 +82,28 @@ android {
|
|
|
76
82
|
sourceSets {
|
|
77
83
|
main {
|
|
78
84
|
kotlin.srcDirs += 'src/main/kotlin'
|
|
85
|
+
java.srcDirs += 'src/main/java'
|
|
79
86
|
}
|
|
87
|
+
androidTest.assets.srcDirs += files("$projectDir/schemas".toString())
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
testOptions {
|
|
91
|
+
unitTests.returnDefaultValues = true
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
testOptions.unitTests.all {
|
|
95
|
+
testLogging {
|
|
96
|
+
events "failed", "skipped"
|
|
97
|
+
showStackTraces = true
|
|
98
|
+
exceptionFormat = "full"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
maxParallelForks = (System.getenv("CI") == "true") ? 1 : Runtime.runtime.availableProcessors().intdiv(2) ?: 1
|
|
102
|
+
forkEvery = 40
|
|
103
|
+
maxHeapSize = "2048m"
|
|
104
|
+
minHeapSize = "1024m"
|
|
105
|
+
systemProperties['junit.jupiter.execution.parallel.enabled'] = true
|
|
106
|
+
systemProperties['junit.jupiter.execution.parallel.mode.default'] = "concurrent"
|
|
80
107
|
}
|
|
81
108
|
|
|
82
109
|
buildTypes {
|
|
@@ -92,34 +119,39 @@ repositories {
|
|
|
92
119
|
}
|
|
93
120
|
|
|
94
121
|
dependencies {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
122
|
+
// --- Core dependencies (formerly in android/build.gradle) ---
|
|
123
|
+
api 'androidx.annotation:annotation:1.3.0' // https://developer.android.com/jetpack/androidx/releases/annotation
|
|
124
|
+
api 'androidx.concurrent:concurrent-futures:1.1.0' // https://developer.android.com/jetpack/androidx/releases/concurrent
|
|
125
|
+
api 'androidx.work:work-runtime:2.11.1' // https://developer.android.com/jetpack/androidx/releases/work
|
|
126
|
+
implementation 'com.facebook.fresco:fresco:2.6.0' // https://github.com/facebook/fresco/releases
|
|
101
127
|
implementation("com.google.guava:guava:33.5.0-android") // https://github.com/google/guava
|
|
102
|
-
|
|
103
|
-
implementation 'androidx.
|
|
104
|
-
implementation 'org.greenrobot:eventbus:3.3.1' // https://github.com/greenrobot/EventBus/releases
|
|
105
|
-
implementation 'androidx.lifecycle:lifecycle-process:2.3.1'
|
|
128
|
+
compileOnly 'org.checkerframework:checker-qual:3.43.0' // required by Guava's nullness annotations
|
|
129
|
+
implementation 'androidx.core:core:1.6.0'
|
|
106
130
|
|
|
107
131
|
// NotifeeCoreDatabase_Impl is generated against Room 2.8.4's RoomOpenDelegate API
|
|
108
132
|
// (stable since Room 2.6.0). If bumping this version, verify the generated _Impl class
|
|
109
133
|
// is still binary-compatible by running yarn smoke:android. A future Room 3.0 with
|
|
110
|
-
// breaking changes will require regenerating
|
|
111
|
-
|
|
134
|
+
// breaking changes will require regenerating the core database sources.
|
|
135
|
+
def room_version = '2.8.4' // https://developer.android.com/jetpack/androidx/releases/room
|
|
136
|
+
api "androidx.room:room-runtime:$room_version"
|
|
112
137
|
api 'androidx.sqlite:sqlite:2.6.2' // https://developer.android.com/jetpack/androidx/releases/sqlite
|
|
113
138
|
api 'androidx.sqlite:sqlite-framework:2.6.2'
|
|
139
|
+
annotationProcessor "androidx.room:room-compiler:$room_version"
|
|
140
|
+
|
|
141
|
+
def eventbus_version = '3.3.1' // https://github.com/greenrobot/EventBus/releases
|
|
142
|
+
api "org.greenrobot:eventbus:$eventbus_version"
|
|
143
|
+
annotationProcessor "org.greenrobot:eventbus-annotation-processor:$eventbus_version"
|
|
114
144
|
|
|
145
|
+
// --- Bridge dependencies ---
|
|
146
|
+
implementation 'androidx.lifecycle:lifecycle-process:2.3.1'
|
|
115
147
|
//noinspection GradleDynamicVersion
|
|
116
148
|
implementation 'com.facebook.react:react-android:+'
|
|
117
|
-
}
|
|
118
149
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
150
|
+
// --- Test dependencies ---
|
|
151
|
+
testImplementation 'junit:junit:4.13.2' // https://github.com/junit-team/junit4/releases
|
|
152
|
+
testImplementation 'org.robolectric:robolectric:4.14.1' // https://github.com/robolectric/robolectric/releases
|
|
153
|
+
androidTestImplementation 'androidx.test.ext:junit:1.1.3' // https://developer.android.com/jetpack/androidx/releases/test
|
|
154
|
+
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' // see above
|
|
155
|
+
androidTestImplementation "androidx.room:room-testing:$room_version"
|
|
156
|
+
androidTestImplementation "androidx.test:monitor:1.4.0"
|
|
125
157
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# --- Bridge classes ---
|
|
2
|
+
|
|
1
3
|
-keep class io.invertase.notifee.NotifeeEventSubscriber
|
|
2
4
|
-keep class io.invertase.notifee.NotifeeInitProvider
|
|
3
5
|
-keepnames class io.invertase.notifee.NotifeePackage
|
|
@@ -12,8 +14,18 @@
|
|
|
12
14
|
-keep class * extends com.facebook.react.ReactHost { *; }
|
|
13
15
|
-keepnames class com.facebook.react.ReactActivity
|
|
14
16
|
|
|
17
|
+
# --- Core classes ---
|
|
18
|
+
|
|
19
|
+
# InitProvider is subclassed by the RN bridge module (NotifeeInitProvider).
|
|
20
|
+
# R8 must not finalize its methods, otherwise the bridge cannot override onCreate().
|
|
21
|
+
-keep class app.notifee.core.InitProvider { *; }
|
|
22
|
+
-keeppackagenames app.notifee.core.**
|
|
23
|
+
|
|
24
|
+
# --- Annotations ---
|
|
25
|
+
|
|
15
26
|
# Preserve all annotations.
|
|
16
27
|
-keepattributes *Annotation*
|
|
28
|
+
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,EnclosingMethod
|
|
17
29
|
|
|
18
30
|
# Keep the classes/members we need for client functionality.
|
|
19
31
|
-keep @interface androidx.annotation.Keep { *; }
|
|
@@ -35,6 +47,8 @@
|
|
|
35
47
|
@app.notifee.core.KeepForSdk <methods>;
|
|
36
48
|
}
|
|
37
49
|
|
|
50
|
+
# --- Class/method preservation ---
|
|
51
|
+
|
|
38
52
|
# Preserve all .class method names.
|
|
39
53
|
-keepclassmembernames class * {
|
|
40
54
|
java.lang.Class class$(java.lang.String);
|
|
@@ -72,4 +86,3 @@
|
|
|
72
86
|
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
|
|
73
87
|
<init>(java.lang.Throwable);
|
|
74
88
|
}
|
|
75
|
-
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion": 1,
|
|
3
|
+
"database": {
|
|
4
|
+
"version": 1,
|
|
5
|
+
"identityHash": "f3fdf2a542ef241fa353c4b3be121211",
|
|
6
|
+
"entities": [
|
|
7
|
+
{
|
|
8
|
+
"tableName": "work_data",
|
|
9
|
+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `notification` BLOB, `trigger` BLOB, PRIMARY KEY(`id`))",
|
|
10
|
+
"fields": [
|
|
11
|
+
{
|
|
12
|
+
"fieldPath": "id",
|
|
13
|
+
"columnName": "id",
|
|
14
|
+
"affinity": "TEXT",
|
|
15
|
+
"notNull": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"fieldPath": "notification",
|
|
19
|
+
"columnName": "notification",
|
|
20
|
+
"affinity": "BLOB",
|
|
21
|
+
"notNull": false
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"fieldPath": "trigger",
|
|
25
|
+
"columnName": "trigger",
|
|
26
|
+
"affinity": "BLOB",
|
|
27
|
+
"notNull": false
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"primaryKey": {
|
|
31
|
+
"columnNames": ["id"],
|
|
32
|
+
"autoGenerate": false
|
|
33
|
+
},
|
|
34
|
+
"indices": [],
|
|
35
|
+
"foreignKeys": []
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"views": [],
|
|
39
|
+
"setupQueries": [
|
|
40
|
+
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
|
41
|
+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f3fdf2a542ef241fa353c4b3be121211')"
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formatVersion": 1,
|
|
3
|
+
"database": {
|
|
4
|
+
"version": 2,
|
|
5
|
+
"identityHash": "24b2477514809255df232947ce7928c4",
|
|
6
|
+
"entities": [
|
|
7
|
+
{
|
|
8
|
+
"tableName": "work_data",
|
|
9
|
+
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `notification` BLOB, `trigger` BLOB, `with_alarm_manager` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))",
|
|
10
|
+
"fields": [
|
|
11
|
+
{
|
|
12
|
+
"fieldPath": "id",
|
|
13
|
+
"columnName": "id",
|
|
14
|
+
"affinity": "TEXT",
|
|
15
|
+
"notNull": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"fieldPath": "notification",
|
|
19
|
+
"columnName": "notification",
|
|
20
|
+
"affinity": "BLOB",
|
|
21
|
+
"notNull": false
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"fieldPath": "trigger",
|
|
25
|
+
"columnName": "trigger",
|
|
26
|
+
"affinity": "BLOB",
|
|
27
|
+
"notNull": false
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"fieldPath": "withAlarmManager",
|
|
31
|
+
"columnName": "with_alarm_manager",
|
|
32
|
+
"affinity": "INTEGER",
|
|
33
|
+
"notNull": true,
|
|
34
|
+
"defaultValue": "0"
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
"primaryKey": {
|
|
38
|
+
"columnNames": ["id"],
|
|
39
|
+
"autoGenerate": false
|
|
40
|
+
},
|
|
41
|
+
"indices": [],
|
|
42
|
+
"foreignKeys": []
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"views": [],
|
|
46
|
+
"setupQueries": [
|
|
47
|
+
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
|
48
|
+
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '24b2477514809255df232947ce7928c4')"
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package app.notifee.core;
|
|
2
|
+
|
|
3
|
+
import static org.junit.Assert.*;
|
|
4
|
+
|
|
5
|
+
import android.content.Context;
|
|
6
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
7
|
+
import androidx.test.platform.app.InstrumentationRegistry;
|
|
8
|
+
import org.junit.Test;
|
|
9
|
+
import org.junit.runner.RunWith;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Instrumented test, which will execute on an Android device.
|
|
13
|
+
*
|
|
14
|
+
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
|
15
|
+
*/
|
|
16
|
+
@RunWith(AndroidJUnit4.class)
|
|
17
|
+
public class ExampleInstrumentedTest {
|
|
18
|
+
@Test
|
|
19
|
+
public void useAppContext() {
|
|
20
|
+
// Context of the app under test.
|
|
21
|
+
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
|
22
|
+
|
|
23
|
+
assertEquals("app.notifee.core.test", appContext.getPackageName());
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
package app.notifee.core.database;
|
|
2
|
+
|
|
3
|
+
import static app.notifee.core.database.NotifeeCoreDatabase.MIGRATION_1_2;
|
|
4
|
+
|
|
5
|
+
import androidx.room.Room;
|
|
6
|
+
import androidx.room.migration.Migration;
|
|
7
|
+
import androidx.room.testing.MigrationTestHelper;
|
|
8
|
+
import androidx.sqlite.db.SupportSQLiteDatabase;
|
|
9
|
+
import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory;
|
|
10
|
+
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
11
|
+
import androidx.test.platform.app.InstrumentationRegistry;
|
|
12
|
+
import java.io.IOException;
|
|
13
|
+
import org.junit.Rule;
|
|
14
|
+
import org.junit.Test;
|
|
15
|
+
import org.junit.runner.RunWith;
|
|
16
|
+
|
|
17
|
+
@RunWith(AndroidJUnit4.class)
|
|
18
|
+
public class NotifeeCoreDatabaseTest {
|
|
19
|
+
private static final String TEST_DB = "migration-test";
|
|
20
|
+
|
|
21
|
+
@Rule public MigrationTestHelper helper;
|
|
22
|
+
|
|
23
|
+
public NotifeeCoreDatabaseTest() {
|
|
24
|
+
helper =
|
|
25
|
+
new MigrationTestHelper(
|
|
26
|
+
InstrumentationRegistry.getInstrumentation(),
|
|
27
|
+
NotifeeCoreDatabase.class.getCanonicalName(),
|
|
28
|
+
new FrameworkSQLiteOpenHelperFactory());
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Test
|
|
32
|
+
public void migrateAll() throws IOException {
|
|
33
|
+
// Create earliest version of the database.
|
|
34
|
+
|
|
35
|
+
SupportSQLiteDatabase db = helper.createDatabase(TEST_DB, 1);
|
|
36
|
+
db.delete("work_data", null, null);
|
|
37
|
+
db.close();
|
|
38
|
+
|
|
39
|
+
// Open latest version of the database. Room will validate the schema
|
|
40
|
+
// once all migrations execute.
|
|
41
|
+
NotifeeCoreDatabase appDb =
|
|
42
|
+
Room.databaseBuilder(
|
|
43
|
+
InstrumentationRegistry.getInstrumentation().getTargetContext(),
|
|
44
|
+
NotifeeCoreDatabase.class,
|
|
45
|
+
TEST_DB)
|
|
46
|
+
.addMigrations(ALL_MIGRATIONS)
|
|
47
|
+
.build();
|
|
48
|
+
appDb.workDao().getAllWithAlarmManager(true);
|
|
49
|
+
appDb.getOpenHelper().getWritableDatabase();
|
|
50
|
+
appDb.close();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Array of all migrations
|
|
54
|
+
private static final Migration[] ALL_MIGRATIONS = new Migration[] {MIGRATION_1_2};
|
|
55
|
+
}
|
|
@@ -1,10 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
|
|
3
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
|
4
|
+
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
5
|
+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
|
6
|
+
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
|
7
|
+
<uses-permission android:name="android.permission.VIBRATE" />
|
|
8
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
9
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
10
|
+
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
|
11
|
+
<uses-permission android:name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" android:maxSdkVersion="30" />
|
|
12
|
+
<!-- For Xiaomi devices to enable heads-up notifications as default (https://github.com/invertase/notifee/issues/296) -->
|
|
13
|
+
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" android:minSdkVersion="23" />
|
|
4
14
|
|
|
5
15
|
<application>
|
|
16
|
+
<!-- Receiver Service -->
|
|
17
|
+
<service
|
|
18
|
+
android:name="app.notifee.core.ReceiverService"
|
|
19
|
+
android:exported="false" />
|
|
20
|
+
|
|
21
|
+
<activity
|
|
22
|
+
android:name="app.notifee.core.NotificationReceiverActivity"
|
|
23
|
+
android:noHistory="true"
|
|
24
|
+
android:excludeFromRecents="true"
|
|
25
|
+
android:taskAffinity=""
|
|
26
|
+
android:theme="@android:style/Theme.Translucent.NoTitleBar"
|
|
27
|
+
android:exported="true" />
|
|
28
|
+
|
|
29
|
+
<!-- Foreground Service -->
|
|
30
|
+
<service
|
|
31
|
+
android:name="app.notifee.core.ForegroundService"
|
|
32
|
+
android:exported="false" />
|
|
33
|
+
|
|
34
|
+
<receiver
|
|
35
|
+
android:name="app.notifee.core.RebootBroadcastReceiver"
|
|
36
|
+
android:exported="false">
|
|
37
|
+
<intent-filter>
|
|
38
|
+
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
39
|
+
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
|
40
|
+
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
|
|
41
|
+
</intent-filter>
|
|
42
|
+
</receiver>
|
|
43
|
+
|
|
44
|
+
<receiver
|
|
45
|
+
android:name="app.notifee.core.AlarmPermissionBroadcastReceiver"
|
|
46
|
+
android:exported="true">
|
|
47
|
+
<intent-filter>
|
|
48
|
+
<action android:name="android.app.action.SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED" />
|
|
49
|
+
</intent-filter>
|
|
50
|
+
</receiver>
|
|
51
|
+
|
|
52
|
+
<receiver
|
|
53
|
+
android:name="app.notifee.core.NotificationAlarmReceiver"
|
|
54
|
+
android:exported="false">
|
|
55
|
+
<intent-filter>
|
|
56
|
+
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
|
57
|
+
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
|
58
|
+
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
|
|
59
|
+
</intent-filter>
|
|
60
|
+
</receiver>
|
|
61
|
+
|
|
62
|
+
<!-- Broadcast Receiver -->
|
|
63
|
+
<receiver
|
|
64
|
+
android:name="app.notifee.core.BlockStateBroadcastReceiver"
|
|
65
|
+
android:exported="false">
|
|
66
|
+
<intent-filter>
|
|
67
|
+
<action android:name="android.app.action.APP_BLOCK_STATE_CHANGED" />
|
|
68
|
+
<action android:name="android.app.action.NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED" />
|
|
69
|
+
<action android:name="android.app.action.NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED" />
|
|
70
|
+
</intent-filter>
|
|
71
|
+
</receiver>
|
|
72
|
+
|
|
6
73
|
<provider
|
|
7
|
-
android:name=".NotifeeInitProvider"
|
|
74
|
+
android:name="io.invertase.notifee.NotifeeInitProvider"
|
|
8
75
|
android:authorities="${applicationId}.notifee-init-provider"
|
|
9
76
|
android:exported="false"
|
|
10
77
|
android:initOrder="-100" />
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package app.notifee.core;
|
|
2
|
+
|
|
3
|
+
import static android.app.AlarmManager.ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED;
|
|
4
|
+
|
|
5
|
+
import android.content.BroadcastReceiver;
|
|
6
|
+
import android.content.Context;
|
|
7
|
+
import android.content.Intent;
|
|
8
|
+
import android.util.Log;
|
|
9
|
+
|
|
10
|
+
public class AlarmPermissionBroadcastReceiver extends BroadcastReceiver {
|
|
11
|
+
@Override
|
|
12
|
+
public void onReceive(Context context, Intent intent) {
|
|
13
|
+
|
|
14
|
+
if (intent.getAction().equals(ACTION_SCHEDULE_EXACT_ALARM_PERMISSION_STATE_CHANGED)) {
|
|
15
|
+
PendingResult pendingResult = goAsync();
|
|
16
|
+
Log.i("AlarmPermissionReceiver", "Received alarm permission state changed event");
|
|
17
|
+
|
|
18
|
+
if (ContextHolder.getApplicationContext() == null) {
|
|
19
|
+
ContextHolder.setApplicationContext(context.getApplicationContext());
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
new NotifeeAlarmManager().rescheduleNotifications(pendingResult);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
package app.notifee.core;
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Copyright (c) 2016-present Invertase Limited & Contributors
|
|
5
|
+
*
|
|
6
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
* you may not use this library except in compliance with the License.
|
|
8
|
+
* You may obtain a copy of the License at
|
|
9
|
+
*
|
|
10
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
*
|
|
12
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
* See the License for the specific language governing permissions and
|
|
16
|
+
* limitations under the License.
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import android.app.NotificationManager;
|
|
21
|
+
import android.content.BroadcastReceiver;
|
|
22
|
+
import android.content.Context;
|
|
23
|
+
import android.content.Intent;
|
|
24
|
+
import android.os.Build;
|
|
25
|
+
import android.os.Bundle;
|
|
26
|
+
import androidx.annotation.Keep;
|
|
27
|
+
import androidx.concurrent.futures.CallbackToFutureAdapter;
|
|
28
|
+
import androidx.work.Data;
|
|
29
|
+
import androidx.work.ExistingWorkPolicy;
|
|
30
|
+
import androidx.work.ListenableWorker.Result;
|
|
31
|
+
import androidx.work.OneTimeWorkRequest;
|
|
32
|
+
import androidx.work.WorkManager;
|
|
33
|
+
import app.notifee.core.event.BlockStateEvent;
|
|
34
|
+
import app.notifee.core.interfaces.MethodCallResult;
|
|
35
|
+
import app.notifee.core.utility.ObjectUtils;
|
|
36
|
+
import java.util.concurrent.TimeUnit;
|
|
37
|
+
|
|
38
|
+
public class BlockStateBroadcastReceiver extends BroadcastReceiver {
|
|
39
|
+
private static final String TAG = "BlockState";
|
|
40
|
+
private static final String KEY_TYPE = "type";
|
|
41
|
+
private static final String KEY_BLOCKED = "blocked";
|
|
42
|
+
private static final String KEY_CHANNEL_GROUP = "channelOrGroupId";
|
|
43
|
+
|
|
44
|
+
@Keep
|
|
45
|
+
public BlockStateBroadcastReceiver() {}
|
|
46
|
+
|
|
47
|
+
static void doWork(Data workData, CallbackToFutureAdapter.Completer<Result> completer) {
|
|
48
|
+
Logger.v(TAG, "starting background work");
|
|
49
|
+
|
|
50
|
+
final boolean blocked = workData.getBoolean(KEY_BLOCKED, false);
|
|
51
|
+
final int type = workData.getInt(KEY_TYPE, BlockStateEvent.TYPE_APP_BLOCKED);
|
|
52
|
+
final ObjectUtils.TypedCallback<Bundle> sendEventCallback =
|
|
53
|
+
bundle -> {
|
|
54
|
+
final MethodCallResult<Void> methodCallResult =
|
|
55
|
+
(e, aVoid) -> {
|
|
56
|
+
if (e != null) {
|
|
57
|
+
Logger.e(TAG, "background work failed with error: ", e);
|
|
58
|
+
completer.set(Result.failure());
|
|
59
|
+
} else {
|
|
60
|
+
Logger.v(TAG, "background work completed successfully");
|
|
61
|
+
completer.set(Result.success());
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
BlockStateEvent blockStateEvent =
|
|
66
|
+
new BlockStateEvent(type, bundle, blocked, methodCallResult);
|
|
67
|
+
|
|
68
|
+
EventBus.post(blockStateEvent);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
if (type == BlockStateEvent.TYPE_APP_BLOCKED) {
|
|
72
|
+
sendEventCallback.call(null);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
MethodCallResult<Bundle> methodCallResult =
|
|
77
|
+
(e, aBundle) -> {
|
|
78
|
+
if (e != null) {
|
|
79
|
+
Logger.e(TAG, "Failed getting channel or channel group bundle, received error: ", e);
|
|
80
|
+
completer.set(Result.success());
|
|
81
|
+
} else {
|
|
82
|
+
sendEventCallback.call(aBundle);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
String channelOrGroupId = workData.getString(KEY_CHANNEL_GROUP);
|
|
87
|
+
if (type == BlockStateEvent.TYPE_CHANNEL_BLOCKED) {
|
|
88
|
+
Notifee.getInstance().getChannel(channelOrGroupId, methodCallResult);
|
|
89
|
+
} else if (type == BlockStateEvent.TYPE_CHANNEL_GROUP_BLOCKED) {
|
|
90
|
+
Notifee.getInstance().getChannelGroup(channelOrGroupId, methodCallResult);
|
|
91
|
+
} else {
|
|
92
|
+
Logger.e(TAG, "unknown block state work type");
|
|
93
|
+
completer.set(Result.success());
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Override
|
|
98
|
+
public void onReceive(Context context, Intent intent) {
|
|
99
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
String action = intent.getAction();
|
|
104
|
+
if (action == null) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// To prevent a race condition
|
|
109
|
+
// See https://github.com/notifee/react-native-notifee/issues/237
|
|
110
|
+
if (ContextHolder.getApplicationContext() == null) {
|
|
111
|
+
ContextHolder.setApplicationContext(context.getApplicationContext());
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
String uniqueWorkId = action;
|
|
115
|
+
Data.Builder workDataBuilder = new Data.Builder();
|
|
116
|
+
workDataBuilder.putString(Worker.KEY_WORK_TYPE, Worker.WORK_TYPE_BLOCK_STATE_RECEIVER);
|
|
117
|
+
|
|
118
|
+
switch (action) {
|
|
119
|
+
case NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED:
|
|
120
|
+
workDataBuilder.putInt(KEY_TYPE, BlockStateEvent.TYPE_APP_BLOCKED);
|
|
121
|
+
break;
|
|
122
|
+
case NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED:
|
|
123
|
+
workDataBuilder.putInt(KEY_TYPE, BlockStateEvent.TYPE_CHANNEL_BLOCKED);
|
|
124
|
+
String channelId = intent.getStringExtra(NotificationManager.EXTRA_NOTIFICATION_CHANNEL_ID);
|
|
125
|
+
workDataBuilder.putString(KEY_CHANNEL_GROUP, channelId);
|
|
126
|
+
uniqueWorkId += "." + channelId;
|
|
127
|
+
break;
|
|
128
|
+
case NotificationManager.ACTION_NOTIFICATION_CHANNEL_GROUP_BLOCK_STATE_CHANGED:
|
|
129
|
+
workDataBuilder.putInt(KEY_TYPE, BlockStateEvent.TYPE_CHANNEL_GROUP_BLOCKED);
|
|
130
|
+
String channelGroupId =
|
|
131
|
+
intent.getStringExtra(NotificationManager.EXTRA_NOTIFICATION_CHANNEL_GROUP_ID);
|
|
132
|
+
workDataBuilder.putString(KEY_CHANNEL_GROUP, channelGroupId);
|
|
133
|
+
uniqueWorkId += "." + channelGroupId;
|
|
134
|
+
break;
|
|
135
|
+
default:
|
|
136
|
+
Logger.d(TAG, "unknown intent action received, ignoring.");
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
workDataBuilder.putBoolean(
|
|
141
|
+
KEY_BLOCKED, intent.getBooleanExtra(NotificationManager.EXTRA_BLOCKED_STATE, false));
|
|
142
|
+
|
|
143
|
+
// a second delay to debounce events coming from a user spam toggling block states
|
|
144
|
+
OneTimeWorkRequest.Builder builder =
|
|
145
|
+
new OneTimeWorkRequest.Builder(Worker.class)
|
|
146
|
+
.setInitialDelay(1, TimeUnit.SECONDS)
|
|
147
|
+
.setInputData(workDataBuilder.build());
|
|
148
|
+
|
|
149
|
+
// On-going issue with WorkManager.getInstance(context)
|
|
150
|
+
// https://issuetracker.google.com/issues/135858602
|
|
151
|
+
try {
|
|
152
|
+
WorkManager.getInstance(ContextHolder.getApplicationContext())
|
|
153
|
+
.enqueueUniqueWork(uniqueWorkId, ExistingWorkPolicy.REPLACE, builder.build());
|
|
154
|
+
} catch (IllegalStateException e) {
|
|
155
|
+
Logger.e(TAG, "Error while calling WorkManager.getInstance", e);
|
|
156
|
+
|
|
157
|
+
if (ContextHolder.getApplicationContext() == null) {
|
|
158
|
+
Logger.e(TAG, "Application Context is null");
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
Logger.v(TAG, "scheduled new background work with id " + uniqueWorkId);
|
|
163
|
+
}
|
|
164
|
+
}
|