@psync/notifee 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/LICENSE +44 -0
- package/README.md +142 -0
- package/RNNotifee.podspec +39 -0
- package/RNNotifeeCore.podspec +33 -0
- package/android/.editorconfig +10 -0
- package/android/build.gradle +116 -0
- package/android/gradle.properties +1 -0
- package/android/lint.xml +5 -0
- package/android/proguard-rules.pro +82 -0
- package/android/settings.gradle +1 -0
- package/android/src/main/AndroidManifest.xml +12 -0
- package/android/src/main/java/io/invertase/notifee/HeadlessTask.java +431 -0
- package/android/src/main/java/io/invertase/notifee/NotifeeApiModule.java +365 -0
- package/android/src/main/java/io/invertase/notifee/NotifeeEventSubscriber.java +118 -0
- package/android/src/main/java/io/invertase/notifee/NotifeeInitProvider.java +55 -0
- package/android/src/main/java/io/invertase/notifee/NotifeePackage.java +30 -0
- package/android/src/main/java/io/invertase/notifee/NotifeeReactUtils.java +135 -0
- package/dist/NotifeeApiModule.d.ts +53 -0
- package/dist/NotifeeApiModule.js +609 -0
- package/dist/NotifeeApiModule.js.map +1 -0
- package/dist/NotifeeJSEventEmitter.d.ts +3 -0
- package/dist/NotifeeJSEventEmitter.js +14 -0
- package/dist/NotifeeJSEventEmitter.js.map +1 -0
- package/dist/NotifeeNativeError.d.ts +15 -0
- package/dist/NotifeeNativeError.js +59 -0
- package/dist/NotifeeNativeError.js.map +1 -0
- package/dist/NotifeeNativeModule.d.ts +14 -0
- package/dist/NotifeeNativeModule.js +42 -0
- package/dist/NotifeeNativeModule.js.map +1 -0
- package/dist/NotifeeNativeModule.web.d.ts +12 -0
- package/dist/NotifeeNativeModule.web.js +18 -0
- package/dist/NotifeeNativeModule.web.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/types/Library.d.ts +21 -0
- package/dist/types/Library.js +6 -0
- package/dist/types/Library.js.map +1 -0
- package/dist/types/Module.d.ts +576 -0
- package/dist/types/Module.js +6 -0
- package/dist/types/Module.js.map +1 -0
- package/dist/types/Notification.d.ts +462 -0
- package/dist/types/Notification.js +111 -0
- package/dist/types/Notification.js.map +1 -0
- package/dist/types/NotificationAndroid.d.ts +1311 -0
- package/dist/types/NotificationAndroid.js +409 -0
- package/dist/types/NotificationAndroid.js.map +1 -0
- package/dist/types/NotificationIOS.d.ts +539 -0
- package/dist/types/NotificationIOS.js +92 -0
- package/dist/types/NotificationIOS.js.map +1 -0
- package/dist/types/NotificationWeb.d.ts +5 -0
- package/dist/types/NotificationWeb.js +6 -0
- package/dist/types/NotificationWeb.js.map +1 -0
- package/dist/types/PowerManagerInfo.d.ts +36 -0
- package/dist/types/PowerManagerInfo.js +6 -0
- package/dist/types/PowerManagerInfo.js.map +1 -0
- package/dist/types/Trigger.d.ts +124 -0
- package/dist/types/Trigger.js +51 -0
- package/dist/types/Trigger.js.map +1 -0
- package/dist/utils/id.d.ts +1 -0
- package/dist/utils/id.js +15 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/index.d.ts +16 -0
- package/dist/utils/index.js +51 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/validate.d.ts +15 -0
- package/dist/utils/validate.js +90 -0
- package/dist/utils/validate.js.map +1 -0
- package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfo.d.ts +2 -0
- package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfo.js +51 -0
- package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfo.js.map +1 -0
- package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfoPerson.d.ts +2 -0
- package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfoPerson.js +27 -0
- package/dist/validators/iosCommunicationInfo/validateIOSCommunicationInfoPerson.js.map +1 -0
- package/dist/validators/validate.d.ts +25 -0
- package/dist/validators/validate.js +74 -0
- package/dist/validators/validate.js.map +1 -0
- package/dist/validators/validateAndroidAction.d.ts +2 -0
- package/dist/validators/validateAndroidAction.js +52 -0
- package/dist/validators/validateAndroidAction.js.map +1 -0
- package/dist/validators/validateAndroidChannel.d.ts +2 -0
- package/dist/validators/validateAndroidChannel.js +155 -0
- package/dist/validators/validateAndroidChannel.js.map +1 -0
- package/dist/validators/validateAndroidChannelGroup.d.ts +2 -0
- package/dist/validators/validateAndroidChannelGroup.js +42 -0
- package/dist/validators/validateAndroidChannelGroup.js.map +1 -0
- package/dist/validators/validateAndroidFullScreenAction.d.ts +2 -0
- package/dist/validators/validateAndroidFullScreenAction.js +50 -0
- package/dist/validators/validateAndroidFullScreenAction.js.map +1 -0
- package/dist/validators/validateAndroidInput.d.ts +2 -0
- package/dist/validators/validateAndroidInput.js +54 -0
- package/dist/validators/validateAndroidInput.js.map +1 -0
- package/dist/validators/validateAndroidNotification.d.ts +2 -0
- package/dist/validators/validateAndroidNotification.js +547 -0
- package/dist/validators/validateAndroidNotification.js.map +1 -0
- package/dist/validators/validateAndroidPressAction.d.ts +2 -0
- package/dist/validators/validateAndroidPressAction.js +50 -0
- package/dist/validators/validateAndroidPressAction.js.map +1 -0
- package/dist/validators/validateAndroidStyle.d.ts +22 -0
- package/dist/validators/validateAndroidStyle.js +230 -0
- package/dist/validators/validateAndroidStyle.js.map +1 -0
- package/dist/validators/validateIOSAttachment.d.ts +6 -0
- package/dist/validators/validateIOSAttachment.js +100 -0
- package/dist/validators/validateIOSAttachment.js.map +1 -0
- package/dist/validators/validateIOSCategory.d.ts +2 -0
- package/dist/validators/validateIOSCategory.js +124 -0
- package/dist/validators/validateIOSCategory.js.map +1 -0
- package/dist/validators/validateIOSCategoryAction.d.ts +2 -0
- package/dist/validators/validateIOSCategoryAction.js +59 -0
- package/dist/validators/validateIOSCategoryAction.js.map +1 -0
- package/dist/validators/validateIOSInput.d.ts +2 -0
- package/dist/validators/validateIOSInput.js +32 -0
- package/dist/validators/validateIOSInput.js.map +1 -0
- package/dist/validators/validateIOSNotification.d.ts +2 -0
- package/dist/validators/validateIOSNotification.js +211 -0
- package/dist/validators/validateIOSNotification.js.map +1 -0
- package/dist/validators/validateIOSPermissions.d.ts +2 -0
- package/dist/validators/validateIOSPermissions.js +62 -0
- package/dist/validators/validateIOSPermissions.js.map +1 -0
- package/dist/validators/validateNotification.d.ts +11 -0
- package/dist/validators/validateNotification.js +129 -0
- package/dist/validators/validateNotification.js.map +1 -0
- package/dist/validators/validateTrigger.d.ts +2 -0
- package/dist/validators/validateTrigger.js +110 -0
- package/dist/validators/validateTrigger.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/ios/NotifeeCore/Info.plist +22 -0
- package/ios/NotifeeCore/NotifeeCore+NSNotificationCenter.h +28 -0
- package/ios/NotifeeCore/NotifeeCore+NSNotificationCenter.m +76 -0
- package/ios/NotifeeCore/NotifeeCore+NSURLSession.h +25 -0
- package/ios/NotifeeCore/NotifeeCore+NSURLSession.m +55 -0
- package/ios/NotifeeCore/NotifeeCore+UNUserNotificationCenter.h +44 -0
- package/ios/NotifeeCore/NotifeeCore+UNUserNotificationCenter.m +270 -0
- package/ios/NotifeeCore/NotifeeCore.h +118 -0
- package/ios/NotifeeCore/NotifeeCore.m +843 -0
- package/ios/NotifeeCore/NotifeeCoreDelegateHolder.h +34 -0
- package/ios/NotifeeCore/NotifeeCoreDelegateHolder.m +70 -0
- package/ios/NotifeeCore/NotifeeCoreDownloadDelegate.h +39 -0
- package/ios/NotifeeCore/NotifeeCoreDownloadDelegate.m +68 -0
- package/ios/NotifeeCore/NotifeeCoreExtensionHelper.h +38 -0
- package/ios/NotifeeCore/NotifeeCoreExtensionHelper.m +224 -0
- package/ios/NotifeeCore/NotifeeCoreUtil.h +81 -0
- package/ios/NotifeeCore/NotifeeCoreUtil.m +834 -0
- package/ios/RNNotifee/NotifeeApiModule.h +25 -0
- package/ios/RNNotifee/NotifeeApiModule.m +371 -0
- package/ios/RNNotifee/NotifeeExtensionHelper.h +37 -0
- package/ios/RNNotifee/NotifeeExtensionHelper.m +37 -0
- package/ios/RNNotifee.xcodeproj/project.pbxproj +318 -0
- package/ios/RNNotifee.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/RNNotifee.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
- package/jest-mock.js +161 -0
- package/package.json +74 -0
- package/react-native.config.js +11 -0
- package/src/NotifeeApiModule.ts +827 -0
- package/src/NotifeeJSEventEmitter.ts +10 -0
- package/src/NotifeeNativeError.ts +68 -0
- package/src/NotifeeNativeModule.ts +54 -0
- package/src/NotifeeNativeModule.web.ts +24 -0
- package/src/index.ts +33 -0
- package/src/types/Library.ts +28 -0
- package/src/types/Module.ts +629 -0
- package/src/types/Notification.ts +518 -0
- package/src/types/NotificationAndroid.ts +1478 -0
- package/src/types/NotificationIOS.ts +683 -0
- package/src/types/NotificationWeb.ts +5 -0
- package/src/types/PowerManagerInfo.ts +43 -0
- package/src/types/Trigger.ts +136 -0
- package/src/utils/id.ts +13 -0
- package/src/utils/index.ts +46 -0
- package/src/utils/validate.ts +90 -0
- package/src/validators/iosCommunicationInfo/validateIOSCommunicationInfo.ts +61 -0
- package/src/validators/iosCommunicationInfo/validateIOSCommunicationInfoPerson.ts +33 -0
- package/src/validators/validate.ts +85 -0
- package/src/validators/validateAndroidAction.ts +54 -0
- package/src/validators/validateAndroidChannel.ts +188 -0
- package/src/validators/validateAndroidChannelGroup.ts +49 -0
- package/src/validators/validateAndroidFullScreenAction.ts +65 -0
- package/src/validators/validateAndroidInput.ts +67 -0
- package/src/validators/validateAndroidNotification.ts +734 -0
- package/src/validators/validateAndroidPressAction.ts +65 -0
- package/src/validators/validateAndroidStyle.ts +315 -0
- package/src/validators/validateIOSAttachment.ts +135 -0
- package/src/validators/validateIOSCategory.ts +150 -0
- package/src/validators/validateIOSCategoryAction.ts +65 -0
- package/src/validators/validateIOSInput.ts +38 -0
- package/src/validators/validateIOSNotification.ts +296 -0
- package/src/validators/validateIOSPermissions.ts +78 -0
- package/src/validators/validateNotification.ts +156 -0
- package/src/validators/validateTrigger.ts +142 -0
- package/src/version.ts +2 -0
- package/tsconfig.json +23 -0
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This software has been integrated, with great appreciation, from the
|
|
3
|
+
* react-native-background-geolocation library, and was originally authored by
|
|
4
|
+
* Chris Scott @ TransistorSoft. It is published in that repository under this license,
|
|
5
|
+
* included here in it's entirety
|
|
6
|
+
*
|
|
7
|
+
* https://github.com/transistorsoft/react-native-background-geolocation/blob/master/LICENSE
|
|
8
|
+
*
|
|
9
|
+
* ----
|
|
10
|
+
*
|
|
11
|
+
* The MIT License (MIT)
|
|
12
|
+
*
|
|
13
|
+
* Copyright (c) 2015 Chris Scott
|
|
14
|
+
*
|
|
15
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
16
|
+
* of this software and associated documentation files (the "Software"), to deal
|
|
17
|
+
* in the Software without restriction, including without limitation the rights
|
|
18
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
19
|
+
* copies of the Software, and to permit persons to whom the Software is
|
|
20
|
+
* furnished to do so, subject to the following conditions:
|
|
21
|
+
*
|
|
22
|
+
* The above copyright notice and this permission notice shall be included in all
|
|
23
|
+
* copies or substantial portions of the Software.
|
|
24
|
+
*
|
|
25
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
26
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
27
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
28
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
29
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
30
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
31
|
+
* SOFTWARE.
|
|
32
|
+
*/
|
|
33
|
+
package io.invertase.notifee;
|
|
34
|
+
|
|
35
|
+
import android.annotation.SuppressLint;
|
|
36
|
+
import android.content.Context;
|
|
37
|
+
import android.os.Handler;
|
|
38
|
+
import android.os.Looper;
|
|
39
|
+
import android.util.Log;
|
|
40
|
+
import androidx.annotation.NonNull;
|
|
41
|
+
import androidx.annotation.Nullable;
|
|
42
|
+
import app.notifee.core.EventSubscriber;
|
|
43
|
+
import com.facebook.infer.annotation.Assertions;
|
|
44
|
+
import com.facebook.react.ReactApplication;
|
|
45
|
+
import com.facebook.react.ReactInstanceEventListener;
|
|
46
|
+
import com.facebook.react.ReactInstanceManager;
|
|
47
|
+
import com.facebook.react.ReactNativeHost;
|
|
48
|
+
import com.facebook.react.bridge.ReactContext;
|
|
49
|
+
import com.facebook.react.bridge.UiThreadUtil;
|
|
50
|
+
import com.facebook.react.bridge.WritableMap;
|
|
51
|
+
import com.facebook.react.jstasks.HeadlessJsTaskConfig;
|
|
52
|
+
import com.facebook.react.jstasks.HeadlessJsTaskContext;
|
|
53
|
+
import com.facebook.react.jstasks.HeadlessJsTaskEventListener;
|
|
54
|
+
import java.lang.reflect.Method;
|
|
55
|
+
import java.util.ArrayList;
|
|
56
|
+
import java.util.List;
|
|
57
|
+
import java.util.concurrent.atomic.AtomicBoolean;
|
|
58
|
+
import java.util.concurrent.atomic.AtomicInteger;
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* The BackgroundGeolocation SDK creates a single instance of this class (via reflection upon Config.headlessJobService)
|
|
62
|
+
* The SDK delivers events to this instance by dropping them onto EventBus (see @Subscribe). Because this instance is subscribed
|
|
63
|
+
* into EventBus, it's protected from GC.
|
|
64
|
+
*
|
|
65
|
+
* Because there's only one instance of this class, we have to be mindful that we can possibly receive events rapidly,
|
|
66
|
+
* so we store them in a Queue (#mTaskQueue).
|
|
67
|
+
*
|
|
68
|
+
* We also have to be mindful that it's a heavy operation to do the initial launch the ReactNative Host, so several events
|
|
69
|
+
* might build up in the queue before the Host is finally launched, when we drain the queue (see #drainTaskQueue).
|
|
70
|
+
*
|
|
71
|
+
* For finally sending events to the client, we wrap the RN HeadlessJsTaskConfig with our own TaskConfig class. This class
|
|
72
|
+
* adds our own auto-incremented "taskId" field to maintain a mapping between our taskId and RN's. See #invokeStartTask.
|
|
73
|
+
* This class appends our custom taskId into the the event params sent to Javascript, for the following purpose:
|
|
74
|
+
*
|
|
75
|
+
* ```javascript
|
|
76
|
+
* const BackgroundGeolocationHeadlessTask = async (event) => {
|
|
77
|
+
* console.log('[HeadlessTask] taskId: ', event.taskId); // <-- here's our custom taskId.
|
|
78
|
+
*
|
|
79
|
+
* await doWork(); // <-- perform some arbitrarily long process (eg: http request).
|
|
80
|
+
*
|
|
81
|
+
* BackgroundGeolocation.finishHeadlessTask(event.taskId); // <-- $$$ Here's the money $$$
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* See "Here's the $money" above: We want to signal back to this native HeadlessTask instance , that our JS task is now complete.
|
|
86
|
+
* This is a pretty easy task to do via EventBus -- Just create a new instance of FinishHeadlessTaskEvent(params.taskId);
|
|
87
|
+
*
|
|
88
|
+
* The code then looks up a TaskConfig from mEventQueue using the given event.taskId.
|
|
89
|
+
*
|
|
90
|
+
* All this extra fussing with taking care to finish our RN HeadlessTasks seems to be more important with RN's "new architecture", where before,
|
|
91
|
+
* RN seemed to automatically complete its tasks seemingly when the Javascript function stopped executing. This is why it was always so
|
|
92
|
+
* important to await one's Promises and do all the work before the the last line of the function executed.
|
|
93
|
+
*
|
|
94
|
+
* Now, the Javascript must explicitly call back to the native side to say "I'M DONE" -- BackgroundGeolocation.finishHeadlessTask(taskId)
|
|
95
|
+
*
|
|
96
|
+
* Created by chris on 2018-01-23.
|
|
97
|
+
*/
|
|
98
|
+
|
|
99
|
+
public class HeadlessTask {
|
|
100
|
+
private static final String HEADLESS_TASK_NAME = "NotifeeHeadlessJS";
|
|
101
|
+
// Hard-coded time-limit for headless-tasks is 60000 @todo configurable?
|
|
102
|
+
private static final int TASK_TIMEOUT = 60000;
|
|
103
|
+
private static final AtomicInteger sLastTaskId = new AtomicInteger(0);
|
|
104
|
+
|
|
105
|
+
static synchronized int getNextTaskId() {
|
|
106
|
+
return sLastTaskId.incrementAndGet();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private final List<TaskConfig> mTaskQueue = new ArrayList<>();
|
|
110
|
+
private final AtomicBoolean mIsReactContextInitialized = new AtomicBoolean(false);
|
|
111
|
+
private final AtomicBoolean mWillDrainTaskQueue = new AtomicBoolean(false);
|
|
112
|
+
private final AtomicBoolean mIsInitializingReactContext = new AtomicBoolean(false);
|
|
113
|
+
private final AtomicBoolean mIsHeadlessJsTaskListenerRegistered = new AtomicBoolean(false);
|
|
114
|
+
|
|
115
|
+
public void stopAllTasks() {
|
|
116
|
+
for (TaskConfig task : mTaskQueue) {
|
|
117
|
+
onFinishHeadlessTask(task.getTaskId());
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* EventBus Receiver. Called when developer runs BackgroundGeolocation.finishHeadlessTask(taskId)
|
|
123
|
+
* from within their registered HeadlessTask. Calls RN's HeadlessJsTaskContext.finishTask(taskId);
|
|
124
|
+
*
|
|
125
|
+
* @param taskId the taskId returned from startTask
|
|
126
|
+
*/
|
|
127
|
+
public void onFinishHeadlessTask(int taskId) {
|
|
128
|
+
if (!mIsReactContextInitialized.get()) {
|
|
129
|
+
Log.w(HEADLESS_TASK_NAME, taskId + " found no ReactContext");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
ReactContext reactContext = getReactContext(EventSubscriber.getContext());
|
|
133
|
+
if (reactContext != null) {
|
|
134
|
+
|
|
135
|
+
synchronized (mTaskQueue) {
|
|
136
|
+
// Locate the TaskConfig instance by our local taskId.
|
|
137
|
+
TaskConfig taskConfig = null;
|
|
138
|
+
for (TaskConfig config : mTaskQueue) {
|
|
139
|
+
if (config.getTaskId() == taskId) {
|
|
140
|
+
taskConfig = config;
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (taskConfig != null) {
|
|
145
|
+
HeadlessJsTaskContext headlessJsTaskContext =
|
|
146
|
+
HeadlessJsTaskContext.getInstance(reactContext);
|
|
147
|
+
// Tell RN we're done using the mapped getReactTaskId().
|
|
148
|
+
headlessJsTaskContext.finishTask(taskConfig.getReactTaskId());
|
|
149
|
+
} else {
|
|
150
|
+
Log.w(HEADLESS_TASK_NAME, "Failed to find task: " + taskId);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
Log.w(
|
|
155
|
+
HEADLESS_TASK_NAME,
|
|
156
|
+
"Failed to finishHeadlessTask: "
|
|
157
|
+
+ taskId
|
|
158
|
+
+ " -- HeadlessTask onFinishHeadlessTask failed to find a ReactContext. This is"
|
|
159
|
+
+ " unexpected");
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Start a task. This method handles starting a new React instance if required.
|
|
165
|
+
*
|
|
166
|
+
* <p>Has to be called on the UI thread.
|
|
167
|
+
*
|
|
168
|
+
* @param taskConfig describes what task to start and the parameters to pass to it
|
|
169
|
+
*/
|
|
170
|
+
public void startTask(Context context, final TaskConfig taskConfig) throws AssertionError {
|
|
171
|
+
UiThreadUtil.assertOnUiThread();
|
|
172
|
+
|
|
173
|
+
// push this HeadlessEvent onto the taskQueue, to be drained once the React context is finished
|
|
174
|
+
// initializing,
|
|
175
|
+
// or executed immediately if Context exists currently.
|
|
176
|
+
synchronized (mTaskQueue) {
|
|
177
|
+
mTaskQueue.add(taskConfig);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!mIsReactContextInitialized.get()) {
|
|
181
|
+
createReactContextAndScheduleTask(context);
|
|
182
|
+
} else {
|
|
183
|
+
invokeStartTask(getReactContext(context), taskConfig);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private synchronized void invokeStartTask(
|
|
188
|
+
ReactContext reactContext, final TaskConfig taskConfig) {
|
|
189
|
+
if (taskConfig.mReactTaskId > 0) {
|
|
190
|
+
Log.w(HEADLESS_TASK_NAME, "Task already invoked <IGNORED>: " + this);
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
final HeadlessJsTaskContext headlessJsTaskContext =
|
|
194
|
+
HeadlessJsTaskContext.getInstance(reactContext);
|
|
195
|
+
try {
|
|
196
|
+
if (mIsHeadlessJsTaskListenerRegistered.compareAndSet(false, true)) {
|
|
197
|
+
// Register the RN HeadlessJSTaskEventListener just once.
|
|
198
|
+
// This inline-listener is handy here as a closure around the HeadlessJsTaskContext.
|
|
199
|
+
// Otherwise, we'd have to store this Context to an instance var.
|
|
200
|
+
// The only purpose of this listener is to clear events from the queue. The RN task is
|
|
201
|
+
// assumed to have had its .stopTask(taskId) method called, which is why this event has
|
|
202
|
+
// fired.
|
|
203
|
+
// WARNING: this listener seems to receive events from ANY OTHER plugin's Headless events.
|
|
204
|
+
// TODO we might use a LifeCycle event-listener here to remove the listener when the app is
|
|
205
|
+
// launched to foreground.
|
|
206
|
+
headlessJsTaskContext.addTaskEventListener(
|
|
207
|
+
new HeadlessJsTaskEventListener() {
|
|
208
|
+
@Override
|
|
209
|
+
public void onHeadlessJsTaskStart(int taskId) {}
|
|
210
|
+
|
|
211
|
+
@Override
|
|
212
|
+
public void onHeadlessJsTaskFinish(int taskId) {
|
|
213
|
+
synchronized (mTaskQueue) {
|
|
214
|
+
if (mTaskQueue.isEmpty()) {
|
|
215
|
+
// Nothing in queue? This event cannot be for us.
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
// Query our queue for this event...
|
|
219
|
+
TaskConfig taskConfig = null;
|
|
220
|
+
for (TaskConfig config : mTaskQueue) {
|
|
221
|
+
if (config.getReactTaskId() == taskId) {
|
|
222
|
+
taskConfig = config;
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (taskConfig != null) {
|
|
227
|
+
// Clear it from the Queue.
|
|
228
|
+
Log.d(HEADLESS_TASK_NAME, "completed taskId: " + taskConfig.getTaskId());
|
|
229
|
+
mTaskQueue.remove(taskConfig);
|
|
230
|
+
if (taskConfig.getCallback() != null) {
|
|
231
|
+
taskConfig.getCallback().call();
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
Log.w(HEADLESS_TASK_NAME, "Failed to find taskId: " + taskId);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
// Finally: the actual launch of the RN headless-task!
|
|
241
|
+
int taskId = headlessJsTaskContext.startTask(taskConfig.getTaskConfig());
|
|
242
|
+
// Provide the RN taskId to our private TaskConfig instance, mapping the RN taskId to our
|
|
243
|
+
// TaskConfig's internal taskId.
|
|
244
|
+
taskConfig.setReactTaskId(taskId);
|
|
245
|
+
Log.d(HEADLESS_TASK_NAME, "launched taskId: " + taskId);
|
|
246
|
+
} catch (IllegalStateException e) {
|
|
247
|
+
Log.e(HEADLESS_TASK_NAME, e.getMessage(), e);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
public static ReactNativeHost getReactNativeHost(Context context) {
|
|
252
|
+
return ((ReactApplication) context.getApplicationContext()).getReactNativeHost();
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** Get the {ReactHost} used by this app. ure and returns null if not. */
|
|
256
|
+
public static @Nullable Object getReactHost(Context context) {
|
|
257
|
+
context = context.getApplicationContext();
|
|
258
|
+
try {
|
|
259
|
+
Method getReactHost = context.getClass().getMethod("getReactHost");
|
|
260
|
+
return getReactHost.invoke(context);
|
|
261
|
+
} catch (Exception e) {
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
@SuppressLint("VisibleForTests")
|
|
267
|
+
public static ReactContext getReactContext(Context context) {
|
|
268
|
+
if (isBridgelessArchitectureEnabled()) {
|
|
269
|
+
Object reactHost = getReactHost(context);
|
|
270
|
+
Assertions.assertNotNull(reactHost, "getReactHost() is null in New Architecture");
|
|
271
|
+
try {
|
|
272
|
+
assert reactHost != null;
|
|
273
|
+
Method getCurrentReactContext = reactHost.getClass().getMethod("getCurrentReactContext");
|
|
274
|
+
return (ReactContext) getCurrentReactContext.invoke(reactHost);
|
|
275
|
+
} catch (Exception e) {
|
|
276
|
+
Log.e(HEADLESS_TASK_NAME, "Reflection error getCurrentReactContext: " + e.getMessage(), e);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
final ReactInstanceManager reactInstanceManager =
|
|
280
|
+
getReactNativeHost(context).getReactInstanceManager();
|
|
281
|
+
return reactInstanceManager.getCurrentReactContext();
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private void createReactContextAndScheduleTask(Context context) {
|
|
285
|
+
|
|
286
|
+
// ReactContext could have already been initialized by another plugin (eg: background-fetch).
|
|
287
|
+
// If we get a non-null ReactContext here, we're good to go!
|
|
288
|
+
ReactContext reactContext = getReactContext(context);
|
|
289
|
+
if (reactContext != null && !mIsInitializingReactContext.get()) {
|
|
290
|
+
mIsReactContextInitialized.set(true);
|
|
291
|
+
drainTaskQueue(reactContext);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (mIsInitializingReactContext.compareAndSet(false, true)) {
|
|
295
|
+
Log.d(HEADLESS_TASK_NAME, "initialize ReactContext");
|
|
296
|
+
final Object reactHost = getReactHost(context);
|
|
297
|
+
if (isBridgelessArchitectureEnabled()) { // NEW arch
|
|
298
|
+
ReactInstanceEventListener callback =
|
|
299
|
+
new ReactInstanceEventListener() {
|
|
300
|
+
@Override
|
|
301
|
+
public void onReactContextInitialized(@NonNull ReactContext reactContext) {
|
|
302
|
+
mIsReactContextInitialized.set(true);
|
|
303
|
+
drainTaskQueue(reactContext);
|
|
304
|
+
try {
|
|
305
|
+
assert reactHost != null;
|
|
306
|
+
Method removeReactInstanceEventListener =
|
|
307
|
+
reactHost
|
|
308
|
+
.getClass()
|
|
309
|
+
.getMethod(
|
|
310
|
+
"removeReactInstanceEventListener", ReactInstanceEventListener.class);
|
|
311
|
+
removeReactInstanceEventListener.invoke(reactHost, this);
|
|
312
|
+
} catch (Exception e) {
|
|
313
|
+
Log.e(HEADLESS_TASK_NAME, "reflection error A: " + e, e);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
try {
|
|
318
|
+
assert reactHost != null;
|
|
319
|
+
Method addReactInstanceEventListener =
|
|
320
|
+
reactHost
|
|
321
|
+
.getClass()
|
|
322
|
+
.getMethod("addReactInstanceEventListener", ReactInstanceEventListener.class);
|
|
323
|
+
addReactInstanceEventListener.invoke(reactHost, callback);
|
|
324
|
+
Method startReactHost = reactHost.getClass().getMethod("start");
|
|
325
|
+
startReactHost.invoke(reactHost);
|
|
326
|
+
} catch (Exception e) {
|
|
327
|
+
Log.e(HEADLESS_TASK_NAME, "reflection error ReactHost start: " + e.getMessage(), e);
|
|
328
|
+
}
|
|
329
|
+
} else { // OLD arch
|
|
330
|
+
final ReactInstanceManager reactInstanceManager =
|
|
331
|
+
getReactNativeHost(context).getReactInstanceManager();
|
|
332
|
+
reactInstanceManager.addReactInstanceEventListener(
|
|
333
|
+
new ReactInstanceEventListener() {
|
|
334
|
+
@Override
|
|
335
|
+
public void onReactContextInitialized(@NonNull ReactContext reactContext) {
|
|
336
|
+
mIsReactContextInitialized.set(true);
|
|
337
|
+
drainTaskQueue(reactContext);
|
|
338
|
+
reactInstanceManager.removeReactInstanceEventListener(this);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
reactInstanceManager.createReactContextInBackground();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Invokes HeadlessEvents queued while waiting for the ReactContext to initialize.
|
|
348
|
+
*
|
|
349
|
+
* @param reactContext context to use for task invocation
|
|
350
|
+
*/
|
|
351
|
+
private void drainTaskQueue(final ReactContext reactContext) {
|
|
352
|
+
if (mWillDrainTaskQueue.compareAndSet(false, true)) {
|
|
353
|
+
new Handler(Looper.getMainLooper())
|
|
354
|
+
.postDelayed(
|
|
355
|
+
() -> {
|
|
356
|
+
synchronized (mTaskQueue) {
|
|
357
|
+
for (TaskConfig taskConfig : mTaskQueue) {
|
|
358
|
+
invokeStartTask(reactContext, taskConfig);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
},
|
|
362
|
+
500);
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Return true if this app is running with RN's bridgeless architecture. Cheers to @mikehardy for
|
|
368
|
+
* this idea.
|
|
369
|
+
*
|
|
370
|
+
* @return true if new arch bridgeless mode is enabled
|
|
371
|
+
*/
|
|
372
|
+
public static boolean isBridgelessArchitectureEnabled() {
|
|
373
|
+
try {
|
|
374
|
+
Class<?> entryPoint =
|
|
375
|
+
Class.forName("com.facebook.react.defaults.DefaultNewArchitectureEntryPoint");
|
|
376
|
+
Method bridgelessEnabled = entryPoint.getMethod("getBridgelessEnabled");
|
|
377
|
+
Object result = bridgelessEnabled.invoke(null);
|
|
378
|
+
return (result == Boolean.TRUE);
|
|
379
|
+
} catch (Exception e) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/** Wrapper for a client event. Inserts our custom taskId into the RN ClientEvent params. */
|
|
385
|
+
public static class TaskConfig {
|
|
386
|
+
private final String mTaskName;
|
|
387
|
+
private final long mTaskTimeout;
|
|
388
|
+
private final GenericCallback mCallback;
|
|
389
|
+
private final int mTaskId;
|
|
390
|
+
private int mReactTaskId;
|
|
391
|
+
private final WritableMap mParams;
|
|
392
|
+
|
|
393
|
+
public TaskConfig(
|
|
394
|
+
String taskName,
|
|
395
|
+
long taskTimeout,
|
|
396
|
+
WritableMap params,
|
|
397
|
+
@Nullable GenericCallback taskCompletionCallback) {
|
|
398
|
+
mTaskName = taskName;
|
|
399
|
+
mTaskTimeout = taskTimeout;
|
|
400
|
+
mCallback = taskCompletionCallback;
|
|
401
|
+
mTaskId = getNextTaskId();
|
|
402
|
+
// Insert our custom taskId for users to call finishHeadlessTask(taskId) with.
|
|
403
|
+
params.putInt("taskId", mTaskId);
|
|
404
|
+
mParams = params;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
public void setReactTaskId(int taskId) {
|
|
408
|
+
mReactTaskId = taskId;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
public int getTaskId() {
|
|
412
|
+
return mTaskId;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
public int getReactTaskId() {
|
|
416
|
+
return mReactTaskId;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
public @Nullable GenericCallback getCallback() {
|
|
420
|
+
return mCallback;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
public HeadlessJsTaskConfig getTaskConfig() {
|
|
424
|
+
return new HeadlessJsTaskConfig(mTaskName, mParams, mTaskTimeout, true);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
public interface GenericCallback {
|
|
429
|
+
void call();
|
|
430
|
+
}
|
|
431
|
+
}
|