@onekeyfe/react-native-jpush 6.7.0-alpha.445 → 6.7.0-alpha.464

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 (24) hide show
  1. package/android/fcm/src/test/java/com/onekey/jpush/fcm/FcmAdapterTest.java +212 -0
  2. package/android/src/main/cpp/cpp-adapter.cpp +41 -1
  3. package/android/src/main/java/cn/jiguang/core/JCoreCrypto.java +6 -9
  4. package/android/src/main/java/cn/jiguang/core/JCoreStorage.java +14 -2
  5. package/android/src/main/java/cn/jiguang/core/JCoreTcpEngine.java +476 -348
  6. package/android/src/main/java/cn/jiguang/core/JCoreWireCodec.java +1 -0
  7. package/android/src/main/java/cn/jiguang/plugins/core/JCoreModule.java +13 -2
  8. package/android/src/main/java/cn/jiguang/plugins/push/JPushModule.java +38 -2
  9. package/android/src/main/java/cn/jiguang/plugins/push/helper/JPushHelper.java +46 -15
  10. package/android/src/main/java/cn/jiguang/plugins/push/receiver/JPushModuleReceiver.java +0 -3
  11. package/android/src/main/java/cn/jpush/android/api/JPushInterface.java +0 -1
  12. package/android/src/main/java/cn/jpush/android/local/ActionHelper.java +223 -272
  13. package/android/src/main/java/cn/jpush/android/service/JNotifyActivity.java +1 -1
  14. package/android/src/main/java/com/margelo/nitro/reactnativejpush/ReactNativeJpush.kt +51 -16
  15. package/android/src/test/java/com/margelo/nitro/reactnativejpush/ReactNativeJpushProtocolTest.java +76 -32
  16. package/ios/bridge/RCTJPushModule.m +215 -150
  17. package/ios/jcore/JCOREConfiguration.h +5 -0
  18. package/ios/jcore/JCOREConfiguration.m +44 -4
  19. package/ios/jcore/JCORECrypto.m +10 -12
  20. package/ios/jcore/JCORETcpSession.m +197 -45
  21. package/ios/jcore/JCOREWireProtocol.m +20 -7
  22. package/ios/jpush/JPUSHNotificationManager.m +7 -1
  23. package/ios/jpush/JPUSHService.m +7 -9
  24. package/package.json +1 -1
@@ -22,7 +22,7 @@ public class JNotifyActivity extends Activity {
22
22
 
23
23
  private void handleIntent(Intent intent) {
24
24
  if (intent != null) {
25
- ActionHelper.getInstance().handleNotificationIntent(getApplicationContext(), intent);
25
+ ActionHelper.getInstance().handleNotificationIntent(this, intent);
26
26
  }
27
27
  }
28
28
  }
@@ -5,6 +5,8 @@ import android.app.NotificationManager
5
5
  import android.content.Context
6
6
  import android.net.Uri
7
7
  import android.os.Build
8
+ import androidx.annotation.Keep
9
+ import com.facebook.jni.HybridData
8
10
  import com.facebook.proguard.annotations.DoNotStrip
9
11
  import com.facebook.react.bridge.ReactApplicationContext
10
12
  import com.margelo.nitro.NitroModules
@@ -25,26 +27,56 @@ import org.json.JSONObject
25
27
 
26
28
  @DoNotStrip
27
29
  class ReactNativeJpush : HybridReactNativeJpushSpec() {
30
+ private val listenerLock = Any()
31
+ private val ownedListenerIds = mutableSetOf<Int>()
32
+ private var released = false
33
+ private var ownedContext: ReactApplicationContext? = null
34
+
35
+ @Keep
36
+ @DoNotStrip
37
+ protected class OwnedCxxPart(javaPart: ReactNativeJpush) : CxxPart(javaPart) {
38
+ external override fun initHybrid(): HybridData
39
+ }
40
+
41
+ override fun createCxxPart(): CxxPart = OwnedCxxPart(this)
42
+
43
+ override fun dispose() {
44
+ releaseOwnedListeners()
45
+ super.dispose()
46
+ }
47
+
48
+ @Keep
49
+ @DoNotStrip
50
+ fun releaseOwnedListeners() {
51
+ val ids: Set<Int>
52
+ val context: ReactApplicationContext?
53
+ synchronized(listenerLock) {
54
+ if (released) return
55
+ released = true
56
+ ids = ownedListenerIds.toSet()
57
+ ownedListenerIds.clear()
58
+ context = ownedContext
59
+ ownedContext = null
60
+ }
61
+ ids.forEach(JPushHelper::unregisterNitroListener)
62
+ if (context != null) JPushModule.releaseReactContext(context)
63
+ }
64
+
28
65
  init {
29
66
  getReactContext()
30
67
  }
31
68
 
32
69
  private fun getReactContext(): ReactApplicationContext? {
33
- val nitroCtx = NitroModules.applicationContext
34
- if (nitroCtx != null) {
35
- JPushModule.registerReactContext(nitroCtx)
36
- }
37
- val ctx = JPushModule.reactContext ?: nitroCtx ?: JCoreModule.reactContext
38
- if (ctx != null) {
39
- JPushModule.registerReactContext(ctx)
40
- if (JPushModule.reactContext == null) {
41
- JPushModule.reactContext = ctx
42
- }
43
- if (JCoreModule.reactContext == null) {
44
- JCoreModule.reactContext = ctx
70
+ synchronized(listenerLock) {
71
+ if (released) return null
72
+ ownedContext?.let { return it }
73
+ val ctx = NitroModules.applicationContext ?: JPushModule.reactContext ?: JCoreModule.reactContext
74
+ if (ctx != null) {
75
+ ownedContext = ctx
76
+ JPushModule.retainReactContext(ctx)
45
77
  }
78
+ return ctx
46
79
  }
47
- return ctx
48
80
  }
49
81
 
50
82
  private fun getAppContext(): Context? {
@@ -443,13 +475,16 @@ class ReactNativeJpush : HybridReactNativeJpushSpec() {
443
475
  override fun addEventListener(
444
476
  callback: (eventName: String, payloadJson: String) -> Unit
445
477
  ): Double {
446
- val id = JPushHelper.registerNitroListener { eventName, payloadJson ->
447
- callback(eventName, payloadJson)
478
+ synchronized(listenerLock) { if (released) return 0.0 }
479
+ val id = JPushHelper.registerNitroListener { eventName, payloadJson -> callback(eventName, payloadJson) }
480
+ synchronized(listenerLock) {
481
+ if (released) JPushHelper.unregisterNitroListener(id) else ownedListenerIds.add(id)
448
482
  }
449
483
  return id.toDouble()
450
484
  }
451
485
 
452
486
  override fun removeEventListener(id: Double) {
453
- JPushHelper.unregisterNitroListener(id.toInt())
487
+ val owned = synchronized(listenerLock) { ownedListenerIds.remove(id.toInt()) }
488
+ if (owned) JPushHelper.unregisterNitroListener(id.toInt())
454
489
  }
455
490
  }
@@ -347,28 +347,60 @@ public class ReactNativeJpushProtocolTest {
347
347
  JCoreTcpEngine.getInstance().stop(null);
348
348
  JCoreStorage.setPushStopped(null, false);
349
349
 
350
- // 2f. Verify delayed addLocalNotification scheduling and removeLocalNotification cancellation
351
- ActionHelper.getInstance().clearActiveNotificationIds();
352
- JPushLocalNotification cancelledLn = new JPushLocalNotification();
353
- cancelledLn.setNotificationId(77001L);
354
- cancelledLn.setTitle("CancelledTitle");
355
- cancelledLn.setContent("CancelledContent");
356
- cancelledLn.setBroadcastTime(300L);
357
- JPushInterface.addLocalNotification(null, cancelledLn);
358
- assertTrue(!ActionHelper.getInstance().getActiveNotificationIds().contains(Integer.valueOf(77001)));
359
- JPushInterface.removeLocalNotification(null, 77001L);
360
-
361
- JPushLocalNotification firedLn = new JPushLocalNotification();
362
- firedLn.setNotificationId(77002L);
363
- firedLn.setTitle("FiredTitle");
364
- firedLn.setContent("FiredContent");
365
- firedLn.setBroadcastTime(70L);
366
- JPushInterface.addLocalNotification(null, firedLn);
367
- assertTrue(!ActionHelper.getInstance().getActiveNotificationIds().contains(Integer.valueOf(77002)));
368
- Thread.sleep(360L);
369
- assertTrue(!ActionHelper.getInstance().getActiveNotificationIds().contains(Integer.valueOf(77001)));
370
- assertTrue(ActionHelper.getInstance().getActiveNotificationIds().contains(Integer.valueOf(77002)));
371
- ActionHelper.getInstance().clearActiveNotificationIds();
350
+ // 2f. Verify Java scheduler ownership/cancellation; plain android.jar cannot prove Intent delivery or OS display.
351
+ final ActionHelper localHelper = ActionHelper.getInstance();
352
+ localHelper.clearActiveNotificationIds();
353
+ java.lang.reflect.Field pendingField = ActionHelper.class.getDeclaredField("pendingLocalNotifications");
354
+ pendingField.setAccessible(true);
355
+ @SuppressWarnings("unchecked")
356
+ Map<Integer, java.util.concurrent.ScheduledFuture<?>> pendingLocal =
357
+ (Map<Integer, java.util.concurrent.ScheduledFuture<?>>) pendingField.get(localHelper);
358
+ java.lang.reflect.Field schedulerField = ActionHelper.class.getDeclaredField("localNotificationScheduler");
359
+ schedulerField.setAccessible(true);
360
+ java.util.concurrent.ScheduledExecutorService scheduler =
361
+ (java.util.concurrent.ScheduledExecutorService) schedulerField.get(localHelper);
362
+ final CountDownLatch schedulerHeld = new CountDownLatch(1), releaseScheduler = new CountDownLatch(1);
363
+ scheduler.execute(new Runnable() {
364
+ @Override public void run() {
365
+ schedulerHeld.countDown();
366
+ try { releaseScheduler.await(); }
367
+ catch (InterruptedException interrupted) { Thread.currentThread().interrupt(); }
368
+ }
369
+ });
370
+ try {
371
+ assertTrue(schedulerHeld.await(1000L, TimeUnit.MILLISECONDS));
372
+ JPushLocalNotification cancelledLn = new JPushLocalNotification();
373
+ cancelledLn.setNotificationId(77001L);
374
+ cancelledLn.setTitle("CancelledTitle");
375
+ cancelledLn.setContent("CancelledContent");
376
+ cancelledLn.setBroadcastTime(300L);
377
+ JPushInterface.addLocalNotification(null, cancelledLn);
378
+ java.util.concurrent.ScheduledFuture<?> cancelledFuture = pendingLocal.get(Integer.valueOf(77001));
379
+ assertNotNull(cancelledFuture);
380
+ assertTrue(!cancelledFuture.isDone());
381
+ JPushInterface.removeLocalNotification(null, 77001L);
382
+ assertTrue(cancelledFuture.isCancelled());
383
+ assertTrue(!pendingLocal.containsKey(Integer.valueOf(77001)));
384
+
385
+ JPushLocalNotification firedLn = new JPushLocalNotification();
386
+ firedLn.setNotificationId(77002L);
387
+ firedLn.setTitle("FiredTitle");
388
+ firedLn.setContent("FiredContent");
389
+ firedLn.setBroadcastTime(70L);
390
+ JPushInterface.addLocalNotification(null, firedLn);
391
+ java.util.concurrent.ScheduledFuture<?> firedFuture = pendingLocal.get(Integer.valueOf(77002));
392
+ assertNotNull(firedFuture);
393
+ assertTrue(!firedFuture.isDone());
394
+ releaseScheduler.countDown();
395
+ long schedulerDeadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(1000L);
396
+ while (!firedFuture.isDone() && System.nanoTime() < schedulerDeadline) Thread.sleep(5L);
397
+ assertTrue(firedFuture.isDone() && !firedFuture.isCancelled());
398
+ assertTrue(!pendingLocal.containsKey(Integer.valueOf(77002)));
399
+ assertTrue(cancelledFuture.isCancelled());
400
+ } finally {
401
+ releaseScheduler.countDown();
402
+ localHelper.clearActiveNotificationIds();
403
+ }
372
404
 
373
405
  // 2g. Verify ActionHelper server response parsing (getAlias, getAllTags, checkTagBindState)
374
406
  // and rollback on non-zero statusCode for Tag/Alias/Property/MobileNumber
@@ -406,6 +438,7 @@ public class ReactNativeJpushProtocolTest {
406
438
  if (req == null) {
407
439
  break;
408
440
  }
441
+ int sequence = JCoreWireCodec.extractJsonInt(JCoreWireCodec.parseJsonCommandPayload(req.body), "seq", -1);
409
442
  if (req.command == JCoreWireCodec.CMD_HEARTBEAT) {
410
443
  out.write(JCoreWireCodec.encodeResponseFrame(
411
444
  JCoreWireCodec.CMD_HEARTBEAT,
@@ -418,7 +451,7 @@ public class ReactNativeJpushProtocolTest {
418
451
  ));
419
452
  out.flush();
420
453
  } else if (req.command == JCoreWireCodec.CMD_ALIAS_OP) {
421
- if (req.requestId == 301L) {
454
+ if (sequence == 301) {
422
455
  byte[] body = JCoreWireCodec.buildJsonCommandPayload("{\"alias\":\"srv_alias_301\"}");
423
456
  out.write(JCoreWireCodec.encodeResponseFrame(
424
457
  JCoreWireCodec.CMD_ALIAS_OP,
@@ -443,7 +476,7 @@ public class ReactNativeJpushProtocolTest {
443
476
  }
444
477
  out.flush();
445
478
  } else if (req.command == JCoreWireCodec.CMD_TAG_OP) {
446
- if (req.requestId == 303L) {
479
+ if (sequence == 303) {
447
480
  byte[] body = JCoreWireCodec.buildJsonCommandPayload("{\"tags\":[\"srv_t1\",\"srv_t2\"]}");
448
481
  out.write(JCoreWireCodec.encodeResponseFrame(
449
482
  JCoreWireCodec.CMD_TAG_OP,
@@ -454,7 +487,7 @@ public class ReactNativeJpushProtocolTest {
454
487
  0,
455
488
  body
456
489
  ));
457
- } else if (req.requestId == 305L) {
490
+ } else if (sequence == 305) {
458
491
  byte[] body = JCoreWireCodec.buildJsonCommandPayload("{\"bind\":true}");
459
492
  out.write(JCoreWireCodec.encodeResponseFrame(
460
493
  JCoreWireCodec.CMD_TAG_OP,
@@ -485,7 +518,7 @@ public class ReactNativeJpushProtocolTest {
485
518
  req.requestId,
486
519
  6601,
487
520
  opUid,
488
- 6002,
521
+ sequence >= 401 && sequence <= 403 ? 0 : 6002,
489
522
  new byte[0]
490
523
  ));
491
524
  out.flush();
@@ -576,19 +609,22 @@ public class ReactNativeJpushProtocolTest {
576
609
 
577
610
  JPushHelper.unregisterNitroListener(opListenerId);
578
611
  JPushHelper.clearNitroListeners();
579
- JCoreTcpEngine.getInstance().setRequireServerAckForOperations(false);
580
- JCoreTcpEngine.getInstance().setCustomEndpoint("", 0);
581
- JCoreTcpEngine.getInstance().stop(null);
582
- JCoreStorage.clearRegistration(null);
583
612
 
584
- // 2h. Verify JPushInterface.setProperties / deleteProperties / cleanProperties emit TagAliasEvent with pros
613
+ // 2h. Verify acknowledged property mutations commit before their TagAliasEvent with pros
585
614
  // via JPushModuleReceiver.onPropertyOperatorResult, and JPushBroadcastReceiver ignores intents with
586
615
  // "jpush_already_dispatched" == true while JPushModuleReceiver.onNotifyMessageOpened clears stale NOTIFICATION_BUNDLE.
587
- final List<String> propEvents = new java.util.ArrayList<String>();
616
+ final List<String> propEvents = java.util.Collections.synchronizedList(new java.util.ArrayList<String>());
617
+ final CountDownLatch setPropertyAck = new CountDownLatch(1);
618
+ final CountDownLatch deletePropertyAck = new CountDownLatch(1);
619
+ final CountDownLatch cleanPropertyAck = new CountDownLatch(1);
588
620
  int propListenerId = JPushHelper.registerNitroListener(new JPushHelper.NitroEventListener() {
589
621
  @Override
590
622
  public void onEvent(String eventName, String payloadJson) {
623
+ if (!"TagAliasEvent".equals(eventName)) return;
591
624
  propEvents.add(eventName + ":" + payloadJson);
625
+ if (payloadJson.contains("401")) setPropertyAck.countDown();
626
+ if (payloadJson.contains("402")) deletePropertyAck.countDown();
627
+ if (payloadJson.contains("403")) cleanPropertyAck.countDown();
592
628
  }
593
629
  });
594
630
  ActionHelper.getInstance().registerReceiver(receiver);
@@ -596,16 +632,19 @@ public class ReactNativeJpushProtocolTest {
596
632
  setPropsInput.put("region", "apac");
597
633
  setPropsInput.put("plan", "enterprise");
598
634
  JPushInterface.setProperties(null, 401, setPropsInput);
635
+ assertTrue(setPropertyAck.await(3000L, TimeUnit.MILLISECONDS));
599
636
  assertEquals("apac", JCoreStorage.getProperties(null).get("region"));
600
637
  assertEquals("enterprise", JCoreStorage.getProperties(null).get("plan"));
601
638
 
602
639
  Map<String, Object> delPropsInput = new LinkedHashMap<String, Object>();
603
640
  delPropsInput.put("region", "");
604
641
  JPushInterface.deleteProperties(null, 402, delPropsInput);
642
+ assertTrue(deletePropertyAck.await(3000L, TimeUnit.MILLISECONDS));
605
643
  assertTrue(!JCoreStorage.getProperties(null).containsKey("region"));
606
644
  assertEquals("enterprise", JCoreStorage.getProperties(null).get("plan"));
607
645
 
608
646
  JPushInterface.cleanProperties(null, 403);
647
+ assertTrue(cleanPropertyAck.await(3000L, TimeUnit.MILLISECONDS));
609
648
  assertTrue(JCoreStorage.getProperties(null).isEmpty());
610
649
 
611
650
  assertEquals(3L, (long) propEvents.size());
@@ -614,6 +653,11 @@ public class ReactNativeJpushProtocolTest {
614
653
  assertTrue(propEvents.get(2).startsWith("TagAliasEvent:") && propEvents.get(2).contains("403"));
615
654
  JPushHelper.unregisterNitroListener(propListenerId);
616
655
  JPushHelper.clearNitroListeners();
656
+ JCoreTcpEngine.getInstance().setRequireServerAckForOperations(false);
657
+ JCoreTcpEngine.getInstance().setCustomEndpoint("", 0);
658
+ JCoreTcpEngine.getInstance().stop(null);
659
+ JCoreStorage.clearRegistration(null);
660
+
617
661
 
618
662
  android.os.Bundle staleBundle = new android.os.Bundle();
619
663
  cn.jiguang.plugins.push.receiver.JPushBroadcastReceiver.NOTIFICATION_BUNDLE = staleBundle;