react-native-background-geolocation 4.17.6 → 4.18.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.
Files changed (49) hide show
  1. package/android/build.gradle +8 -2
  2. package/android/libs/com/transistorsoft/{tslocationmanager-v21/3.6.3/tslocationmanager-v21-3.6.3.aar → tslocationmanager/3.6.4/tslocationmanager-3.6.4.aar} +0 -0
  3. package/android/libs/com/transistorsoft/tslocationmanager/3.6.4/tslocationmanager-3.6.4.aar.md5 +1 -0
  4. package/android/libs/com/transistorsoft/tslocationmanager/3.6.4/tslocationmanager-3.6.4.aar.sha1 +1 -0
  5. package/android/libs/com/transistorsoft/tslocationmanager/3.6.4/tslocationmanager-3.6.4.aar.sha256 +1 -0
  6. package/android/libs/com/transistorsoft/tslocationmanager/3.6.4/tslocationmanager-3.6.4.aar.sha512 +1 -0
  7. package/android/libs/com/transistorsoft/tslocationmanager/{3.6.3/tslocationmanager-3.6.3.pom → 3.6.4/tslocationmanager-3.6.4.pom} +1 -1
  8. package/android/libs/com/transistorsoft/tslocationmanager/3.6.4/tslocationmanager-3.6.4.pom.md5 +1 -0
  9. package/android/libs/com/transistorsoft/tslocationmanager/3.6.4/tslocationmanager-3.6.4.pom.sha1 +1 -0
  10. package/android/libs/com/transistorsoft/tslocationmanager/3.6.4/tslocationmanager-3.6.4.pom.sha256 +1 -0
  11. package/android/libs/com/transistorsoft/tslocationmanager/3.6.4/tslocationmanager-3.6.4.pom.sha512 +1 -0
  12. package/android/libs/com/transistorsoft/tslocationmanager/maven-metadata.xml +4 -3
  13. package/android/libs/com/transistorsoft/{tslocationmanager/3.6.3/tslocationmanager-3.6.3.aar → tslocationmanager-v21/3.6.4/tslocationmanager-v21-3.6.4.aar} +0 -0
  14. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.4/tslocationmanager-v21-3.6.4.aar.md5 +1 -0
  15. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.4/tslocationmanager-v21-3.6.4.aar.sha1 +1 -0
  16. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.4/tslocationmanager-v21-3.6.4.aar.sha256 +1 -0
  17. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.4/tslocationmanager-v21-3.6.4.aar.sha512 +1 -0
  18. package/android/libs/com/transistorsoft/tslocationmanager-v21/{3.6.3/tslocationmanager-v21-3.6.3.pom → 3.6.4/tslocationmanager-v21-3.6.4.pom} +1 -1
  19. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.4/tslocationmanager-v21-3.6.4.pom.md5 +1 -0
  20. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.4/tslocationmanager-v21-3.6.4.pom.sha1 +1 -0
  21. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.4/tslocationmanager-v21-3.6.4.pom.sha256 +1 -0
  22. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.4/tslocationmanager-v21-3.6.4.pom.sha512 +1 -0
  23. package/android/libs/com/transistorsoft/tslocationmanager-v21/maven-metadata.xml +4 -3
  24. package/android/proguard-rules.pro +16 -0
  25. package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/HeadlessTask.java +280 -46
  26. package/android/src/main/java/com/transistorsoft/rnbackgroundgeolocation/RNBackgroundGeolocationModule.java +19 -0
  27. package/expo/plugin/build/androidPlugin.js +0 -8
  28. package/expo/plugin/build/androidPlugin.js.map +1 -1
  29. package/package.json +2 -2
  30. package/src/NativeModule.js +12 -1
  31. package/src/declarations/BackgroundGeolocation.d.ts +106 -13
  32. package/src/declarations/interfaces/Config.d.ts +5 -2
  33. package/src/index.js +10 -0
  34. package/android/libs/com/transistorsoft/tslocationmanager/3.6.3/tslocationmanager-3.6.3.aar.md5 +0 -1
  35. package/android/libs/com/transistorsoft/tslocationmanager/3.6.3/tslocationmanager-3.6.3.aar.sha1 +0 -1
  36. package/android/libs/com/transistorsoft/tslocationmanager/3.6.3/tslocationmanager-3.6.3.aar.sha256 +0 -1
  37. package/android/libs/com/transistorsoft/tslocationmanager/3.6.3/tslocationmanager-3.6.3.aar.sha512 +0 -1
  38. package/android/libs/com/transistorsoft/tslocationmanager/3.6.3/tslocationmanager-3.6.3.pom.md5 +0 -1
  39. package/android/libs/com/transistorsoft/tslocationmanager/3.6.3/tslocationmanager-3.6.3.pom.sha1 +0 -1
  40. package/android/libs/com/transistorsoft/tslocationmanager/3.6.3/tslocationmanager-3.6.3.pom.sha256 +0 -1
  41. package/android/libs/com/transistorsoft/tslocationmanager/3.6.3/tslocationmanager-3.6.3.pom.sha512 +0 -1
  42. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.3/tslocationmanager-v21-3.6.3.aar.md5 +0 -1
  43. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.3/tslocationmanager-v21-3.6.3.aar.sha1 +0 -1
  44. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.3/tslocationmanager-v21-3.6.3.aar.sha256 +0 -1
  45. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.3/tslocationmanager-v21-3.6.3.aar.sha512 +0 -1
  46. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.3/tslocationmanager-v21-3.6.3.pom.md5 +0 -1
  47. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.3/tslocationmanager-v21-3.6.3.pom.sha1 +0 -1
  48. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.3/tslocationmanager-v21-3.6.3.pom.sha256 +0 -1
  49. package/android/libs/com/transistorsoft/tslocationmanager-v21/3.6.3/tslocationmanager-v21-3.6.3.pom.sha512 +0 -1
@@ -14,7 +14,7 @@ def DEFAULT_TARGET_SDK_VERSION = 30
14
14
  def DEFAULT_APP_COMPAT_VERSION = "1.6.1"
15
15
 
16
16
  // Plugin dependencies
17
- def DEFAULT_PLAY_SERVICES_LOCATION_VERSION = "20.0.0"
17
+ def DEFAULT_PLAY_SERVICES_LOCATION_VERSION = "20.0.0"
18
18
  def DEFAULT_HMS_LOCATION_VERSION = "6.9.0.300"
19
19
  def DEFAULT_OK_HTTP_VERSION = "3.12.13"
20
20
  def DEFAULT_ANDROID_PERMISSIONS_VERSION = "2.1.6"
@@ -42,9 +42,14 @@ android {
42
42
  defaultConfig {
43
43
  minSdkVersion safeExtGet('minSdkVersion', 16)
44
44
  targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
45
+ consumerProguardFiles 'proguard-rules.pro'
45
46
  versionCode 1
46
47
  versionName "1.0"
47
48
  }
49
+ compileOptions {
50
+ sourceCompatibility JavaVersion.VERSION_1_8
51
+ targetCompatibility JavaVersion.VERSION_1_8
52
+ }
48
53
  }
49
54
 
50
55
  repositories{
@@ -98,4 +103,5 @@ dependencies {
98
103
  def workVersion = safeExtGet('workVersion', DEFAULT_WORK_VERSION)
99
104
  def concurrentFuturesVersion = safeExtGet('concurrentFuturesVersion', DEFAULT_CONCURRENT_FUTURES_VERSION)
100
105
  implementation "androidx.work:work-runtime:$workVersion"
101
- implementation "androidx.concurrent:concurrent-futures:$concurrentFuturesVersion"}
106
+ implementation "androidx.concurrent:concurrent-futures:$concurrentFuturesVersion"
107
+ }
@@ -0,0 +1 @@
1
+ d8523b683cf9d70e53317362389b81bebe6f1d9f
@@ -0,0 +1 @@
1
+ 328f8a4a248f6d9d5ae95fad7d996eca1470e7e0d56bad5cba04d0fabf8c6632
@@ -0,0 +1 @@
1
+ 18b6dea8cdb386ac662f7bbeff3a0061fa4855ae63e717db14a6009ed3027090407cc6c0ed97b566a260b57e1f8facbcdeb8b4424c47a27c237d6985878d75dd
@@ -4,6 +4,6 @@
4
4
  <modelVersion>4.0.0</modelVersion>
5
5
  <groupId>com.transistorsoft</groupId>
6
6
  <artifactId>tslocationmanager</artifactId>
7
- <version>3.6.3</version>
7
+ <version>3.6.4</version>
8
8
  <packaging>aar</packaging>
9
9
  </project>
@@ -0,0 +1 @@
1
+ d4c0e936144e9629f09726d231ffb65382d117f8
@@ -0,0 +1 @@
1
+ 29f18c0813aab1f46f264aae982c92a937c752f9d347371d0504db62885c615a
@@ -0,0 +1 @@
1
+ 1710cfc5d6b4638db7eca4ba894a346d024f6041c79cbcdd165c3da68939eac4e1e1cb5c5b4ea7d933d6576fab4db08e79301efb42073186349a90bf00b283ed
@@ -3,14 +3,15 @@
3
3
  <groupId>com.transistorsoft</groupId>
4
4
  <artifactId>tslocationmanager</artifactId>
5
5
  <versioning>
6
- <latest>3.6.3</latest>
7
- <release>3.6.3</release>
6
+ <latest>3.6.4</latest>
7
+ <release>3.6.4</release>
8
8
  <versions>
9
9
  <version>3.6.0</version>
10
10
  <version>3.6.1</version>
11
11
  <version>3.6.2</version>
12
12
  <version>3.6.3</version>
13
+ <version>3.6.4</version>
13
14
  </versions>
14
- <lastUpdated>20241112183131</lastUpdated>
15
+ <lastUpdated>20241203042324</lastUpdated>
15
16
  </versioning>
16
17
  </metadata>
@@ -0,0 +1 @@
1
+ 961d0d90ccb09bf5731a3a297c4e7fbacb9f24d20bdaa9dabc79b729d5a109eb
@@ -0,0 +1 @@
1
+ 01e530f5a7f3db6a9d9d436f345093e5538421d16b663daf9682317829d67bb9a80d4a58de10e485235744793ae039267335c0dc680c54bdc814a7bc31d4f01f
@@ -4,6 +4,6 @@
4
4
  <modelVersion>4.0.0</modelVersion>
5
5
  <groupId>com.transistorsoft</groupId>
6
6
  <artifactId>tslocationmanager-v21</artifactId>
7
- <version>3.6.3</version>
7
+ <version>3.6.4</version>
8
8
  <packaging>aar</packaging>
9
9
  </project>
@@ -0,0 +1 @@
1
+ 7e76a8d9ad740486eed08130a444e02a80959b615d0ae87899af8e85a3e0aa8b
@@ -0,0 +1 @@
1
+ 56a88d4d6a4ca5061f3061bd5bd950a0c6afa271c6bdc5cfa960d77c39092d902736ffc32c53c99c2e55c02ea85f27aeec646e1cc3baceb48160a54dd86fe9b2
@@ -3,14 +3,15 @@
3
3
  <groupId>com.transistorsoft</groupId>
4
4
  <artifactId>tslocationmanager-v21</artifactId>
5
5
  <versioning>
6
- <latest>3.6.3</latest>
7
- <release>3.6.3</release>
6
+ <latest>3.6.4</latest>
7
+ <release>3.6.4</release>
8
8
  <versions>
9
9
  <version>3.6.0</version>
10
10
  <version>3.6.1</version>
11
11
  <version>3.6.2</version>
12
12
  <version>3.6.3</version>
13
+ <version>3.6.4</version>
13
14
  </versions>
14
- <lastUpdated>20241112183131</lastUpdated>
15
+ <lastUpdated>20241203042324</lastUpdated>
15
16
  </versioning>
16
17
  </metadata>
@@ -1,4 +1,20 @@
1
1
  -keepnames class com.facebook.react.ReactActivity
2
+
3
+ # for react-native Headless on new architecture
4
+ -keep class com.facebook.react.defaults.DefaultNewArchitectureEntryPoint {
5
+ public <methods>;
6
+ }
7
+ -keep class com.facebook.react.ReactApplication {
8
+ public <methods>;
9
+ }
10
+ -keep class com.facebook.react.ReactHost {
11
+ public <methods>;
12
+ }
13
+ -keep class * extends com.facebook.react.ReactHost {
14
+ public <methods>;
15
+ }
16
+ -keep class com.facebook.react.fabric.** { *; }
17
+
2
18
  -keepnames class com.transistorsoft.rnbackgroundgeolocation.RNBackgroundGeolocation
3
19
 
4
20
  -keep class com.transistorsoft** { *; }
@@ -4,19 +4,26 @@ import android.content.Context;
4
4
  import android.os.Handler;
5
5
  import android.os.Looper;
6
6
 
7
+ import androidx.annotation.NonNull;
8
+ import androidx.annotation.Nullable;
9
+
10
+ import com.facebook.infer.annotation.Assertions;
7
11
  import com.facebook.react.ReactApplication;
12
+ import com.facebook.react.ReactInstanceEventListener;
8
13
  import com.facebook.react.ReactInstanceManager;
14
+ import com.facebook.react.ReactNativeHost;
9
15
  import com.facebook.react.bridge.ReactContext;
10
16
  import com.facebook.react.bridge.UiThreadUtil;
11
17
  import com.facebook.react.bridge.WritableMap;
12
18
  import com.facebook.react.bridge.WritableNativeMap;
13
19
  import com.facebook.react.jstasks.HeadlessJsTaskConfig;
14
20
  import com.facebook.react.jstasks.HeadlessJsTaskContext;
15
- import com.facebook.react.jstasks.HeadlessJsTaskEventListener;
16
21
 
17
22
  import org.greenrobot.eventbus.Subscribe;
18
23
 
24
+ import com.facebook.react.jstasks.HeadlessJsTaskEventListener;
19
25
  import com.transistorsoft.locationmanager.adapter.BackgroundGeolocation;
26
+ import com.transistorsoft.locationmanager.event.FinishHeadlessTaskEvent;
20
27
  import com.transistorsoft.locationmanager.event.HeadlessEvent;
21
28
  import com.transistorsoft.locationmanager.logger.TSLog;
22
29
 
@@ -24,20 +31,70 @@ import org.greenrobot.eventbus.ThreadMode;
24
31
  import org.json.JSONException;
25
32
  import org.json.JSONObject;
26
33
 
34
+ import java.lang.reflect.Method;
27
35
  import java.util.ArrayList;
28
36
  import java.util.List;
37
+ import java.util.concurrent.atomic.AtomicBoolean;
38
+ import java.util.concurrent.atomic.AtomicInteger;
29
39
 
30
40
  /**
41
+ * The BackgroundGeolocation SDK creates a single instance of this class (via reflection upon Config.headlessJobService)
42
+ * The SDK delivers events to this instance by dropping them onto EventBus (see @Subscribe). Because this instance is subscribed
43
+ * into EventBus, it's protected from GC.
44
+ *
45
+ * Because there's only one instance of this class, we have to be mindful that we can possibly receive events rapidly,
46
+ * so we store them in a Queue (#mTaskQueue).
47
+ *
48
+ * We also have to be mindful that it's a heavy operation to do the initial launch the ReactNative Host, so several events
49
+ * might build up in the queue before the Host is finally launched, when we drain the queue (see #drainTaskQueue).
50
+ *
51
+ * For finally sending events to the client, we wrap the RN HeadlessJsTaskConfig with our own TaskConfig class. This class
52
+ * adds our own auto-incremented "taskId" field to maintain a mapping between our taskId and RN's. See #invokeStartTask.
53
+ * This class appends our custom taskId into the the event params sent to Javascript, for the following purpose:
54
+ *
55
+ * ```javascript
56
+ * const BackgroundGeolocationHeadlessTask = async (event) => {
57
+ * console.log('[HeadlessTask] taskId: ', event.taskId); // <-- here's our custom taskId.
58
+ *
59
+ * await doWork(); // <-- perform some arbitrarily long process (eg: http request).
60
+ *
61
+ * BackgroundGeolocation.finishHeadlessTask(event.taskId); // <-- $$$ Here's the money $$$
62
+ * }
63
+ * ```
64
+ *
65
+ * See "Here's the $money" above: We want to signal back to this native HeadlessTask instance , that our JS task is now complete.
66
+ * This is a pretty easy task to do via EventBus -- Just create a new instance of FinishHeadlessTaskEvent(params.taskId);
67
+ *
68
+ * The code then looks up a TaskConfig from mEventQueue using the given event.taskId.
69
+ *
70
+ * All this extra fussing with taking care to finish our RN HeadlessTasks seems to be more important with RN's "new architecture", where before,
71
+ * RN seemed to automatically complete its tasks seemingly when the Javascript function stopped executing. This is why it was always so
72
+ * important to await one's Promises and do all the work before the the last line of the function executed.
73
+ *
74
+ * Now, the Javascript must explicitly call back to the native side to say "I'M DONE" -- BackgroundGeolocation.finishHeadlessTask(taskId)
75
+ *
31
76
  * Created by chris on 2018-01-23.
32
77
  */
33
78
 
34
79
  public class HeadlessTask {
35
80
  private static String HEADLESS_TASK_NAME = "BackgroundGeolocation";
36
- // Hard-coded time-limit for headless-tasks is 30000 @todo configurable?
37
- private static int TASK_TIMEOUT = 30000;
81
+ // Hard-coded time-limit for headless-tasks is 60000 @todo configurable?
82
+ private static final int TASK_TIMEOUT = 60000;
83
+ private static final AtomicInteger sLastTaskId = new AtomicInteger(0);
84
+
85
+ synchronized static int getNextTaskId() {
86
+ return sLastTaskId.incrementAndGet();
87
+ }
38
88
 
39
- private final List<Integer> mTasks = new ArrayList<>();
89
+ private final List<TaskConfig> mTaskQueue = new ArrayList<>();
90
+ private final AtomicBoolean mIsReactContextInitialized = new AtomicBoolean(false);
91
+ private final AtomicBoolean mIsInitializingReactContext = new AtomicBoolean(false);
92
+ private final AtomicBoolean mIsHeadlessJsTaskListenerRegistered = new AtomicBoolean(false);
40
93
 
94
+ /**
95
+ * EventBus receiver for a HeadlessTask HeadlessEvent
96
+ * @param event
97
+ */
41
98
  @Subscribe(threadMode = ThreadMode.MAIN)
42
99
  public void onHeadlessEvent(HeadlessEvent event) {
43
100
  String name = event.getName();
@@ -95,19 +152,55 @@ public class HeadlessTask {
95
152
  } catch (JSONException e) {
96
153
  clientEvent.putNull("params");
97
154
  clientEvent.putString("error", e.getMessage());
98
- TSLog.logger.error(TSLog.error(e.getMessage()));
155
+ TSLog.logger.error(TSLog.error(e.getMessage()), e);
99
156
  e.printStackTrace();
100
157
  }
101
158
  }
102
159
 
103
- HeadlessJsTaskConfig config = new HeadlessJsTaskConfig(HEADLESS_TASK_NAME, clientEvent, TASK_TIMEOUT);
104
160
  try {
105
- startTask(event.getContext(), config);
161
+ startTask(event.getContext(), new TaskConfig(clientEvent));
106
162
  } catch (AssertionError e) {
107
163
  TSLog.logger.warn(TSLog.warn("Failed invoke HeadlessTask " + name + ". Task ignored." + e.getMessage()));
108
164
  }
109
165
  }
110
166
 
167
+ /**
168
+ * EventBus Receiver. Called when developer runs BackgroundGeolocation.finishHeadlessTask(taskId) from within their registered HeadlessTask.
169
+ * Calls RN's HeadlessJsTaskContext.finishTask(taskId);
170
+ * @param event
171
+ */
172
+ @Subscribe(threadMode = ThreadMode.MAIN)
173
+ public void onFinishHeadlessTask(FinishHeadlessTaskEvent event) {
174
+ if (!mIsReactContextInitialized.get()) {
175
+ TSLog.logger.warn(TSLog.warn("BackgroundGeolocation.finishHeadlessTask:" + event.getTaskId() + " found no ReactContext"));
176
+ return;
177
+ }
178
+ ReactContext reactContext = getReactContext(event.getContext());
179
+ if (reactContext != null) {
180
+ int taskId = event.getTaskId();
181
+
182
+ synchronized (mTaskQueue) {
183
+ // Locate the TaskConfig instance by our local taskId.
184
+ TaskConfig taskConfig = null;
185
+ for (TaskConfig config : mTaskQueue) {
186
+ if (config.getTaskId() == taskId) {
187
+ taskConfig = config;
188
+ break;
189
+ }
190
+ }
191
+ if (taskConfig != null) {
192
+ HeadlessJsTaskContext headlessJsTaskContext = HeadlessJsTaskContext.getInstance(reactContext);
193
+ // Tell RN we're done using the mapped getReactTaskId().
194
+ headlessJsTaskContext.finishTask(taskConfig.getReactTaskId());
195
+ } else {
196
+ TSLog.logger.warn(TSLog.warn("Failed to find task: " + taskId));
197
+ }
198
+ }
199
+ } else {
200
+ TSLog.logger.warn(TSLog.warn("Failed to finishHeadlessTask: " + event.getTaskId() + " -- HeadlessTask onFinishHeadlessTask failed to find a ReactContext. This is unexpected"));
201
+ }
202
+ }
203
+
111
204
  /**
112
205
  * Start a task. This method handles starting a new React instance if required.
113
206
  *
@@ -115,61 +208,202 @@ public class HeadlessTask {
115
208
  *
116
209
  * @param taskConfig describes what task to start and the parameters to pass to it
117
210
  */
118
- private void startTask(Context context, final HeadlessJsTaskConfig taskConfig) throws AssertionError {
119
- ReactApplication reactApplication = ((ReactApplication) context);
120
-
211
+ private void startTask(Context context, final TaskConfig taskConfig) throws AssertionError {
121
212
  UiThreadUtil.assertOnUiThread();
122
- final ReactInstanceManager reactInstanceManager = reactApplication.getReactNativeHost().getReactInstanceManager();
123
-
124
- ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
125
- if (reactContext == null) {
126
- reactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
127
- @Override public void onReactContextInitialized(final ReactContext reactContext) {
128
- // Hack to fix unknown problem executing asynchronous BackgroundTask when ReactContext is created *first time*. Fixed by adding short delay before #invokeStartTask
129
- new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
130
- @Override
131
- public void run() {
132
- invoke(reactContext, taskConfig);
133
- }
134
- }, 500);
135
- reactInstanceManager.removeReactInstanceEventListener(this);
136
- }
137
- });
138
- if (!reactInstanceManager.hasStartedCreatingInitialContext()) {
139
- reactInstanceManager.createReactContextInBackground();
140
- }
213
+
214
+ // push this HeadlessEvent onto the taskQueue, to be drained once the React context is finished initializing,
215
+ // or executed immediately if Context exists currently.
216
+ synchronized (mTaskQueue) {
217
+ mTaskQueue.add(taskConfig);
218
+ }
219
+
220
+ if (!mIsReactContextInitialized.get()) {
221
+ createReactContextAndScheduleTask(context);
141
222
  } else {
142
- invoke(reactContext, taskConfig);
223
+ invokeStartTask(getReactContext(context), taskConfig);
143
224
  }
144
225
  }
145
226
 
146
- private void invoke(ReactContext reactContext, final HeadlessJsTaskConfig taskConfig) {
227
+ synchronized private void invokeStartTask(ReactContext reactContext, final TaskConfig taskConfig) {
147
228
  final HeadlessJsTaskContext headlessJsTaskContext = HeadlessJsTaskContext.getInstance(reactContext);
148
-
149
- synchronized(mTasks) {
150
- if (mTasks.isEmpty()) {
229
+ try {
230
+ if (mIsHeadlessJsTaskListenerRegistered.compareAndSet(false, true)) {
231
+ // Register the RN HeadlessJSTaskEventListener just once.
232
+ // This inline-listener is handy here as a closure around the HeadlessJsTaskContext.
233
+ // Otherwise, we'd have to store this Context to an instance var.
234
+ // The only purpose of this listener is to clear events from the queue. The RN task is assumed to have had its .stopTask(taskId) method called, which is why this event has fired.
235
+ // WARNING: this listener seems to receive events from ANY OTHER plugin's Headless events.
236
+ // TODO we might use a LifeCycle event-listener here to remove the listener when the app is launched to foreground.
151
237
  headlessJsTaskContext.addTaskEventListener(new HeadlessJsTaskEventListener() {
152
- @Override public void onHeadlessJsTaskStart(int taskId) {
153
- TSLog.logger.debug("taskId: " + taskId);
154
- }
238
+ @Override public void onHeadlessJsTaskStart(int taskId) {}
155
239
  @Override public void onHeadlessJsTaskFinish(int taskId) {
156
- TSLog.logger.debug("taskId: " + taskId);
157
- synchronized (mTasks) {
158
- if (!mTasks.isEmpty()) {
159
- mTasks.remove(0);
240
+ synchronized (mTaskQueue) {
241
+ if (mTaskQueue.isEmpty()) {
242
+ // Nothing in queue? This events cannot be for us.
243
+ return;
244
+ }
245
+ // Query our queue for this event...
246
+ TaskConfig taskConfig = null;
247
+ for (TaskConfig config : mTaskQueue) {
248
+ if (config.getReactTaskId() == taskId) {
249
+ taskConfig = config;
250
+ break;
251
+ }
160
252
  }
161
- if (mTasks.isEmpty()) {
162
- headlessJsTaskContext.removeTaskEventListener(this);
253
+ if (taskConfig != null) {
254
+ // Clear it from the Queue.
255
+ TSLog.logger.debug("taskId: " + taskConfig.getTaskId());
256
+ mTaskQueue.remove(taskConfig);
257
+ } else {
258
+ TSLog.logger.warn(TSLog.warn("Failed to find taskId: " + taskId));
163
259
  }
164
260
  }
165
261
  }
166
262
  });
167
263
  }
264
+ // Finally: the actual launch of the RN headless-task!
265
+ int taskId = headlessJsTaskContext.startTask(taskConfig.getTaskConfig());
266
+ // Provide the RN taskId to our private TaskConfig instance, mapping the RN taskId to our TaskConfig's internal taskId.
267
+ taskConfig.setReactTaskId(taskId);
268
+ TSLog.logger.debug("taskId: " + taskId);
269
+ } catch (IllegalStateException e) {
270
+ TSLog.logger.error(TSLog.error(e.getMessage()), e);
271
+ }
272
+ }
273
+
274
+ protected ReactNativeHost getReactNativeHost(Context context) {
275
+ return ((ReactApplication) context.getApplicationContext()).getReactNativeHost();
276
+ }
277
+
278
+ /**
279
+ * Get the {ReactHost} used by this app. ure and returns null if not.
280
+ */
281
+ private @Nullable Object getReactHost(Context context) {
282
+ context = context.getApplicationContext();
283
+ try {
284
+ Method getReactHost = context.getClass().getMethod("getReactHost");
285
+ return getReactHost.invoke(context);
286
+ } catch (Exception e) {
287
+ return null;
288
+ }
289
+ }
290
+
291
+ private ReactContext getReactContext(Context context) {
292
+ if (isBridglessArchitectureEnabled()) {
293
+ Object reactHost = getReactHost(context);
294
+ Assertions.assertNotNull(reactHost, "getReactHost() is null in New Architecture");
168
295
  try {
169
- mTasks.add(headlessJsTaskContext.startTask(taskConfig));
170
- } catch (IllegalStateException e) {
171
- TSLog.logger.error(TSLog.error(e.getMessage()));
296
+ Method getCurrentReactContext = reactHost.getClass().getMethod("getCurrentReactContext");
297
+ return (ReactContext) getCurrentReactContext.invoke(reactHost);
298
+ } catch (Exception e) {
299
+ TSLog.logger.error(TSLog.error("[HeadlessTask] Reflection error getCurrentReactContext: " + e.getMessage()), e);
172
300
  }
173
301
  }
302
+ final ReactInstanceManager reactInstanceManager = getReactNativeHost(context).getReactInstanceManager();
303
+ return reactInstanceManager.getCurrentReactContext();
304
+ }
305
+
306
+ private void createReactContextAndScheduleTask(Context context) {
307
+
308
+ // ReactContext could have already been initialized by another plugin (eg: background-fetch).
309
+ // If we get a non-null ReactContext here, we're good to go!
310
+ ReactContext reactContext = getReactContext(context);
311
+ if (reactContext != null && !mIsInitializingReactContext.get()) {
312
+ mIsReactContextInitialized.set(true);
313
+ drainTaskQueue(reactContext);
314
+ return;
315
+ }
316
+ if (mIsInitializingReactContext.compareAndSet(false, true)) {
317
+ TSLog.logger.debug("initialize ReactContext");
318
+ final Object reactHost = getReactHost(context);
319
+ if (isBridglessArchitectureEnabled()) { // NEW arch
320
+ ReactInstanceEventListener callback = new ReactInstanceEventListener() {
321
+ @Override
322
+ public void onReactContextInitialized(@NonNull ReactContext reactContext) {
323
+ mIsReactContextInitialized.set(true);
324
+ drainTaskQueue(reactContext);
325
+ try {
326
+ Method removeReactInstanceEventListener = reactHost.getClass().getMethod("removeReactInstanceEventListener", ReactInstanceEventListener.class);
327
+ removeReactInstanceEventListener.invoke(reactHost, this);
328
+ } catch (Exception e) {
329
+ TSLog.logger.error(TSLog.error("HeadlessTask reflection error A: " + e), e);
330
+ }
331
+ }
332
+ };
333
+ try {
334
+ Method addReactInstanceEventListener = reactHost.getClass().getMethod("addReactInstanceEventListener", ReactInstanceEventListener.class);
335
+ addReactInstanceEventListener.invoke(reactHost, callback);
336
+ Method startReactHost = reactHost.getClass().getMethod("start");
337
+ startReactHost.invoke(reactHost);
338
+ } catch (Exception e) {
339
+ TSLog.logger.error(TSLog.error("HeadlessTask reflection error ReactHost start: " + e.getMessage()), e);
340
+ }
341
+ } else { // OLD arch
342
+ final ReactInstanceManager reactInstanceManager = getReactNativeHost(context).getReactInstanceManager();
343
+ reactInstanceManager.addReactInstanceEventListener(new ReactInstanceEventListener() {
344
+ @Override
345
+ public void onReactContextInitialized(@NonNull ReactContext reactContext) {
346
+ mIsReactContextInitialized.set(true);
347
+ drainTaskQueue(reactContext);
348
+ reactInstanceManager.removeReactInstanceEventListener(this);
349
+ }
350
+ });
351
+ reactInstanceManager.createReactContextInBackground();
352
+ }
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Invokes HeadlessEvents queued while waiting for the ReactContext to initialize.
358
+ * @param reactContext
359
+ */
360
+ private void drainTaskQueue(final ReactContext reactContext) {
361
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
362
+ synchronized (mTaskQueue) {
363
+ for (TaskConfig taskConfig : mTaskQueue) {
364
+ invokeStartTask(reactContext, taskConfig);
365
+ }
366
+ }
367
+ }, 500);
368
+ }
369
+
370
+ /**
371
+ * Return true if this app is running with RN's bridgeless architecture.
372
+ * Cheers to @mikehardy for this idea.
373
+ * @return
374
+ */
375
+ private boolean isBridglessArchitectureEnabled() {
376
+ try {
377
+ Class<?> entryPoint = Class.forName("com.facebook.react.defaults.DefaultNewArchitectureEntryPoint");
378
+ Method bridgelessEnabled = entryPoint.getMethod("getBridgelessEnabled");
379
+ Object result = bridgelessEnabled.invoke(null);
380
+ return (result == Boolean.TRUE);
381
+ } catch (Exception e) {
382
+ return false;
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Wrapper for a client event. Inserts our custom taskId into the RN ClientEvent params.
388
+ */
389
+ private static class TaskConfig {
390
+ private final int mTaskId;
391
+ private int mReactTaskId;
392
+ private final WritableMap mParams;
393
+ public TaskConfig(WritableMap params) {
394
+ mTaskId = getNextTaskId();
395
+ // Insert our custom taskId for users to call BackgroundGeolocation.finishHeadlessTask(taskId) with.
396
+ params.putInt("taskId", mTaskId);
397
+ mParams = params;
398
+ }
399
+ public void setReactTaskId(int taskId) {
400
+ mReactTaskId = taskId;
401
+ }
402
+ public int getTaskId() { return mTaskId; }
403
+ public int getReactTaskId() { return mReactTaskId; }
404
+
405
+ public HeadlessJsTaskConfig getTaskConfig() {
406
+ return new HeadlessJsTaskConfig(HEADLESS_TASK_NAME, mParams, TASK_TIMEOUT);
407
+ }
174
408
  }
175
409
  }
@@ -23,10 +23,13 @@ import com.facebook.react.bridge.WritableNativeArray;
23
23
  import com.facebook.react.bridge.WritableNativeMap;
24
24
  import com.facebook.react.modules.core.RCTNativeAppEventEmitter;
25
25
 
26
+ import org.greenrobot.eventbus.EventBus;
26
27
  import org.json.JSONArray;
27
28
  import org.json.JSONException;
28
29
  import org.json.JSONObject;
29
30
 
31
+ import com.transistorsoft.locationmanager.event.FinishHeadlessTaskEvent;
32
+ import com.transistorsoft.locationmanager.event.HeadlessEvent;
30
33
  import com.transistorsoft.xms.g.common.ExtensionApiAvailability;
31
34
 
32
35
  import com.transistorsoft.locationmanager.adapter.BackgroundGeolocation;
@@ -822,6 +825,22 @@ public class RNBackgroundGeolocationModule extends ReactContextBaseJavaModule im
822
825
  success.invoke(taskId);
823
826
  }
824
827
 
828
+ @ReactMethod
829
+ public void finishHeadlessTask(int taskId, Callback success, Callback failure) {
830
+ EventBus eventBus = EventBus.getDefault();
831
+ if (!eventBus.hasSubscriberForEvent(HeadlessEvent.class)) {
832
+ // This shouldn't happen. In order for this method to even be called, it MUST have been executed from within the consumer's
833
+ // registered HeadlessTask. In order for that headless-task to have even be called, HeadlessEvent MUST have been registered with EventBus.
834
+ String message = "finishHeadlessTask failed to find an EventBus subscriber for HeadlessEvent";
835
+ TSLog.error(TSLog.warn(message));
836
+ failure.invoke(message);
837
+ return;
838
+ }
839
+ FinishHeadlessTaskEvent event = new FinishHeadlessTaskEvent(getReactApplicationContext(), taskId);
840
+ eventBus.post(event);
841
+ success.invoke();
842
+ }
843
+
825
844
  @ReactMethod
826
845
  public void getTransistorToken(String orgname, String username, String url, final Callback success, final Callback failure) {
827
846
 
@@ -83,14 +83,6 @@ const applyAppGradle = (buildGradle) => {
83
83
  offset: 1,
84
84
  comment: "//",
85
85
  }).contents;
86
- buildGradle = (0, generateCode_1.mergeContents)({
87
- tag: `${MODULE_NAME}-proguard`,
88
- src: buildGradle,
89
- newSrc: `\t proguardFiles "\${background_geolocation.projectDir}/proguard-rules.pro"`,
90
- anchor: /\"proguard-rules.pro\"/,
91
- offset: 1,
92
- comment: "//",
93
- }).contents;
94
86
  return buildGradle;
95
87
  };
96
88
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"androidPlugin.js","sourceRoot":"","sources":["../src/androidPlugin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"androidPlugin.js","sourceRoot":"","sources":["../src/androidPlugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-background-geolocation",
3
- "version": "4.17.6",
3
+ "version": "4.18.0",
4
4
  "description": "The most sophisticated cross-platform background location-tracking & geofencing module with battery-conscious motion-detection intelligence",
5
5
  "scripts": {
6
6
  "build": "yarn run build:expo",
@@ -30,7 +30,7 @@
30
30
  },
31
31
  "homepage": "https://www.transistorsoft.com/shop/products/react-native-background-geolocation",
32
32
  "dependencies": {
33
- "react-native-background-fetch": "~4.2.5"
33
+ "react-native-background-fetch": "~4.2.6"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@expo/config-plugins": "^4.1.0",
@@ -309,7 +309,6 @@ export default class NativeModule {
309
309
  });
310
310
  }
311
311
 
312
- // TODO Rename native methods #finish -> #stopBackgroundTask
313
312
  static stopBackgroundTask(taskId) {
314
313
  return new Promise((resolve, reject) => {
315
314
  if (!taskId) {
@@ -322,6 +321,18 @@ export default class NativeModule {
322
321
  });
323
322
  }
324
323
 
324
+ static finishHeadlessTask(taskId) {
325
+ return new Promise((resolve, reject) => {
326
+ if (!taskId) {
327
+ reject('INVALID_TASK_ID: ' + taskId);
328
+ return;
329
+ }
330
+ let success = (taskId) => { resolve(taskId) }
331
+ let failure = (error) => { reject(error) }
332
+ RNBackgroundGeolocation.finishHeadlessTask(taskId, success, failure);
333
+ });
334
+ }
335
+
325
336
  /**
326
337
  * Geolocation Methods
327
338
  */
@@ -674,30 +674,74 @@ declare module "react-native-background-geolocation" {
674
674
  *
675
675
  * ### ⚠️ Warning:
676
676
  * - You __must__ `registerHeadlessTask` in your application root file (eg: `index.js`).
677
+ *
678
+ * ### ⚠️ Warning:
679
+ * - Your `function` __must__ be declared as `async`.
677
680
  *
678
681
  * @example
679
682
  * ```typescript
680
683
  * const BackgroundGeolocationHeadlessTask = async (event) => {
681
- * let params = event.params;
682
- * console.log("[BackgroundGeolocation HeadlessTask] -", event.name, params);
683
- *
684
- * switch (event.name) {
685
- * case "heartbeat":
686
- * // Use await for async tasks
687
- * let location = await BackgroundGeolocation.getCurrentPosition({
688
- * samples: 1,
689
- * persist: false
690
- * });
691
- * console.log("[BackgroundGeolocation HeadlessTask] - getCurrentPosition:", location);
692
- * break;
693
- * }
684
+ * const params = event.params;
685
+ * console.log("[BackgroundGeolocation HeadlessTask] -", event.name, params);
686
+ *
687
+ * switch (event.name) {
688
+ * case "heartbeat":
689
+ * // Use await for async tasks
690
+ * const location = await BackgroundGeolocation.getCurrentPosition({
691
+ * samples: 1,
692
+ * persist: false
693
+ * });
694
+ * console.log("[BackgroundGeolocation HeadlessTask] - getCurrentPosition:", location);
695
+ * break;
696
+ * }
697
+ * // Signal completion of our RN HeadlessTask.
698
+ * BackgroundGeolocation.finishHeadlessTask(taskId);
694
699
  * }
695
700
  *
696
701
  * BackgroundGeolocation.registerHeadlessTask(BackgroundGeolocationHeadlessTask);
697
702
  * ```
698
703
  *
704
+ * ### Debugging
705
+ *
706
+ * While implementing your headless-task It's crucial to observe your Android logs in a terminal via
707
+ *
708
+ * ```bash
709
+ * $ adb logcat *:S TSLocationManager:V ReactNativeJS:V
710
+ *
711
+ * TSLocationManager: [c.t.r.HeadlessTask onHeadlessEvent] 💀 event: connectivitychange
712
+ * TSLocationManager: [c.t.r.HeadlessTask createReactContextAndScheduleTask] initialize ReactContext
713
+ * TSLocationManager: [c.t.r.HeadlessTask onHeadlessEvent] 💀 event: providerchange
714
+ * TSLocationManager: [c.t.r.HeadlessTask onHeadlessEvent] 💀 event: terminate
715
+ * ReactNativeJS: '[BGGeoHeadlessTask] ', 'connectivitychange', taskId: 1
716
+ * TSLocationManager: [c.t.r.HeadlessTask invokeStartTask] taskId: 1
717
+ * TSLocationManager: [c.t.r.HeadlessTask invokeStartTask] taskId: 2
718
+ * TSLocationManager: [c.t.r.HeadlessTask invokeStartTask] taskId: 3
719
+ * ReactNativeJS: '[BGGeoHeadlessTask] ', 'providerchange', taskId: 2
720
+ * ReactNativeJS: '[BGGeoHeadlessTask] ', 'terminate', taskId: 3
721
+ * TSLocationManager: [c.t.l.u.BackgroundTaskManager$Task start] ⏳ startBackgroundTask: 1
722
+ * TSLocationManager: [c.t.l.u.BackgroundTaskManager$Task start] ⏳ startBackgroundTask: 2
723
+ * ReactNativeJS: *** [doWork] START
724
+ * ReactNativeJS: *** [doWork] START
725
+ * TSLocationManager: [c.t.l.u.BackgroundTaskManager$Task start] ⏳ startBackgroundTask: 3
726
+ * ReactNativeJS: *** [doWork] START
727
+ * .
728
+ * .
729
+ * .
730
+ * ReactNativeJS: *** [doWork] FINISH
731
+ * ReactNativeJS: *** [doWork] FINISH
732
+ * ReactNativeJS: *** [doWork] FINISH
733
+ * TSLocationManager: [c.t.l.u.BackgroundTaskManager$Task stop] ⏳ stopBackgroundTask: 1
734
+ * TSLocationManager: [c.t.l.u.BackgroundTaskManager$Task stop] ⏳ stopBackgroundTask: 2
735
+ * TSLocationManager: [c.t.l.u.BackgroundTaskManager$Task stop] ⏳ stopBackgroundTask: 3
736
+ * TSLocationManager: [c.t.r.HeadlessTask$1 onHeadlessJsTaskFinish] taskId: 1
737
+ * TSLocationManager: [c.t.r.HeadlessTask$1 onHeadlessJsTaskFinish] taskId: 2
738
+ * TSLocationManager: [c.t.r.HeadlessTask$1 onHeadlessJsTaskFinish] taskId: 3
739
+ * ```
740
+ *
699
741
  * ### ℹ️ See also:
700
742
  * - 📘 [Android Headless Mode](github:wiki/Android-Headless-Mode).
743
+ * - [[BackgroundGeolocation.finishHeadlessTask]]
744
+ * - [[Config.enableHeadless]]
701
745
  *
702
746
  */
703
747
  static registerHeadlessTask(callback:(event:Object)=> Promise<void>): void;
@@ -1032,6 +1076,55 @@ declare module "react-native-background-geolocation" {
1032
1076
  */
1033
1077
  static finish(taskId: number, success?: Function, failure?: Function): Promise<number>;
1034
1078
 
1079
+ /**
1080
+ * __[Android-only]__ Signals completion of an Android headless-task (see [[Config.enableHeadless]])
1081
+ *
1082
+ * @example
1083
+ * ```typescript
1084
+ * const bgGeoHeadlessTask = async (event) => {
1085
+ * // the name of this event (eg: "location", "motionchange", "http")
1086
+ * const eventName = event.name;
1087
+ * // our event-data from the BG Geo SDK.
1088
+ * const params = event.params;
1089
+ * // IMPORTANT: This task's ID
1090
+ * const taskId = event.taskId;
1091
+ *
1092
+ * console.log('[BGGeoHeadlessTask] ', eventName, taskId, params);
1093
+ *
1094
+ * // Fake a long-running task (eg: HTTP request).
1095
+ * await doWork();
1096
+ *
1097
+ * // Signal completion of our RN HeadlessTask.
1098
+ * BackgroundGeolocation.finishHeadlessTask(taskId);
1099
+ * }
1100
+ *
1101
+ * // Example "work" function where you might perform a long-running task (such as an HTTP request).
1102
+ * // Uses a simple JS setTimeout timer to simulate work.
1103
+ * const doWork = async () => {
1104
+ * return new Promise(async (resolve, reject) => {
1105
+ * // Start a BGGeo backgroundTask for performing long-running task (such as HTTP request)
1106
+ * const bgTaskId = await BackgroundGeolocation.startBackgroundTask();
1107
+ * console.log('*** [doWork] START');
1108
+ * setTimeout(() => {
1109
+ * console.log('*** [doWork] FINISH');
1110
+ * // Signal completion of our bg-task.
1111
+ * BackgroundGeolocation.stopBackgroundTask(bgTaskId);
1112
+ * resolve();
1113
+ * }, 5000);
1114
+ * });
1115
+ * }
1116
+ *
1117
+ * // Register the HeadlessTask with RN.
1118
+ * BackgroundGeolocation.registerHeadlessTask(bgGeoHeadlessTask);
1119
+ *
1120
+ * ```
1121
+ * ### ℹ️ See also:
1122
+ * - 📘 [Android Headless Mode](github:wiki/Android-Headless-Mode).
1123
+ * - [[BackgroundGeolocation.registerHeadlessTask]]
1124
+ * - [[Config.enableHeadless]]
1125
+ */
1126
+ static finishHeadlessTask(taskId: number, success?: Function, failure?: Function): Promise<number>;
1127
+
1035
1128
  /**
1036
1129
  * Retrieves the current [[Location]].
1037
1130
  *
@@ -2250,13 +2250,16 @@ declare module "react-native-background-geolocation" {
2250
2250
  * __`[Android only]`__ Enables "Headless" operation allowing you to respond to events after you app has been terminated with [[stopOnTerminate]] __`false`__.
2251
2251
  * @break
2252
2252
  *
2253
- * Defaults to __`false`__. In this Android terminated state, where only the plugin's foreground-service remains running, you can respond to all the plugin's events with your own callback.
2253
+ * Defaults to __`false`__. In this Android terminated state, where only the plugin's foreground-service remains running, you can respond to all the plugin's events with your own callback. For more information, see [[BackgroundGeolocation.registerHeadlessTask]].
2254
2254
  *
2255
2255
  * ### ℹ️ Note:
2256
2256
  * - Requires [[stopOnTerminate]] __`false`__.
2257
2257
  * - If you've configured [[stopOnTerminate]] __`false`__, [[BackgroundGeolocation]] will continue to record locations (and post them to your configured [[url]]) *regardless of* __`enabledHeadless: true`__. You should enable this option *only if* you wish to perform some custom work during the headless state (for example, posting a local notification).
2258
- * - For more information, see the Wiki [Android Headless Mode](github:wiki/Android-Headless-Mode).
2259
2258
  *
2259
+ * ### ℹ️ See also:
2260
+ * - 📘 [Android Headless Mode](github:wiki/Android-Headless-Mode).
2261
+ * - [[BackgroundGeolocation.registerHeadlessTask]]
2262
+ * - [[BackgroundGeolocation.finishHeadlessTask]]
2260
2263
  */
2261
2264
  enableHeadless?: boolean;
2262
2265
 
package/src/index.js CHANGED
@@ -380,6 +380,16 @@ export default class BackgroundGeolocation {
380
380
  static finish(taskId, success, failure) {
381
381
  this.stopBackgroundTask.apply(this, arguments);
382
382
  }
383
+ /**
384
+ * Signal completion of a react-native headless-task. This helps RN free-up resources allocated to the headless-task execution.
385
+ */
386
+ static finishHeadlessTask(taskId, success, failure) {
387
+ if (arguments.length == 1) {
388
+ return NativeModule.finishHeadlessTask(taskId);
389
+ } else {
390
+ NativeModule.finishHeadlessTask(taskId).then(success).catch(failure);
391
+ }
392
+ }
383
393
  /**
384
394
  * Toggle motion-state between stationary <-> moving
385
395
  */
@@ -1 +0,0 @@
1
- 5941e344c5f1805782c9ab2b3d8d73a4ab248e4c
@@ -1 +0,0 @@
1
- 9ca7098540ac237b9b843c2a365fc9ae4ed1152b7613af9e8a86c1132efb60bc
@@ -1 +0,0 @@
1
- 26b7c825a14409d3106c8327826a5e6b52965533686d33801e951f1ba30ca9bc0f4b451d08bdb02e80da102cb8ee96cef4174ffac825ca91f1cc6e044b11b3d3
@@ -1 +0,0 @@
1
- 02cea8fc14a328cdb0ea3ae1bf24531633f0894e
@@ -1 +0,0 @@
1
- 84d33712aff68004a83fc684edfbc789e75c9f5dee6e518c5d65a26f2470400a
@@ -1 +0,0 @@
1
- 8ac0ece17f7b793e497951b0f3559868b921abf71ec5104f6b03c8cafdd5c8be340a00b3a2c4e4aa7d0aeeeb4b52ffc7c31303a34a8ae9d3b6b18e66893b7ae3
@@ -1 +0,0 @@
1
- b1ff74273787634c743e423601d64f165fcf70ec
@@ -1 +0,0 @@
1
- 52d7b676b1632f68b847821a4c298b5e3f5b92c22f043e404fb6b028cb8caf59
@@ -1 +0,0 @@
1
- ded64b948711582c957b144560e4c5e6d148cbba1b5fdcb5dace23c5c1f5f254190704f194185434dc5c0fd4a5274541e668615a6b5cfc66ed5d8312315acebf
@@ -1 +0,0 @@
1
- 7ba36349cd28cfc7ce6a4850fae8428ca2dccae4
@@ -1 +0,0 @@
1
- 87332ef41c56163ba7b7a03be1f0bd3bfa7acdbcf79ec4abc4f0d445165f27ac
@@ -1 +0,0 @@
1
- bbac4028863c96f7f6dc52e4fbfbe5212b01dee05e0aae5cff08442e907557d8f40c4d04c4c0858fe97f939ee978f38b646c58746addd66f93074ec25257390e