@bits-innovate/react-native-vstarcam 1.0.37 → 1.0.38

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.
@@ -35,21 +35,9 @@ import org.json.JSONObject;
35
35
  *
36
36
  * Implements P2P camera connectivity using the VStarCam SDK.
37
37
  * Uses JNI calls to the native OKSMARTPPCS library via JNIApi class.
38
- *
39
- * JNI API Methods (discovered via reflection):
40
- * - create(String did, String serverParam) -> long clientPtr
41
- * - connect(long clientPtr, int timeout, String serverParam, int connectType)
42
- * - login(long clientPtr, String username, String password)
43
- * - disconnect(long clientPtr)
44
- * - destroy(long clientPtr)
45
- * - writeCgi(long clientPtr, String cgi, int timeout)
46
- * - init(ClientStateListener, ClientCommandListener, ClientReleaseListener)
47
38
  */
48
39
  @ReactModule(name = "VStarCam")
49
40
  public class VStarCamModule extends ReactContextBaseJavaModule {
50
- static {
51
- Log.d("VStarCamModule", "VStarCamModule class loaded by JVM");
52
- }
53
41
  private static final String TAG = "VStarCamModule";
54
42
  public static final String MODULE_NAME = "VStarCam";
55
43
 
@@ -60,7 +48,6 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
60
48
  private static final java.util.concurrent.atomic.AtomicInteger nextClientPtr = new java.util.concurrent.atomic.AtomicInteger(1000);
61
49
 
62
50
  // Static proxy references to prevent GC.
63
- // If the native SDK holds these, Java must not collect them.
64
51
  private static Object stateListenerProxy;
65
52
  private static Object commandListenerProxy;
66
53
  private static Object releaseListenerProxy;
@@ -103,11 +90,6 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
103
90
  if (did == null || did.length() < 4) {
104
91
  return SERVICE_PARAM_MAP.get("DEFAULT");
105
92
  }
106
-
107
- // Special case: If it was converted from an ACAE/ACxx virtual UID,
108
- // it might work better with the vstarcam2018 parameters even if the real prefix is VSTJ/VSTN.
109
- // However, we'll try prefix-specific first.
110
-
111
93
  String prefix = did.substring(0, 4).toUpperCase();
112
94
  String param = SERVICE_PARAM_MAP.get(prefix);
113
95
  if (param == null) {
@@ -226,41 +208,11 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
226
208
  jniApiClass = Class.forName("com.vstarcam.JNIApi");
227
209
  Log.d(TAG, "JNIApi class found: " + jniApiClass.getName());
228
210
 
229
- // Log JNIApi methods to find hidden functionality (Sound Wave, SmartConfig, etc.)
230
- Log.d(TAG, "=== JNIApi Methods ===");
231
- for (Method m : jniApiClass.getDeclaredMethods()) {
232
- StringBuilder sb = new StringBuilder();
233
- sb.append(" -> ").append(m.getReturnType().getSimpleName()).append(" ");
234
- sb.append(m.getName()).append("(");
235
- Class<?>[] params = m.getParameterTypes();
236
- for (int i = 0; i < params.length; i++) {
237
- sb.append(params[i].getName());
238
- if (i < params.length - 1) sb.append(", ");
239
- }
240
- sb.append(")");
241
- Log.d(TAG, sb.toString());
242
- }
243
- Log.d(TAG, "======================");
244
-
245
- // Load listener interfaces using the same ClassLoader as JNIApi for consistency
211
+ // Load listener interfaces
246
212
  stateListenerClass = Class.forName("com.vstarcam.app_p2p_api.ClientStateListener", true, jniApiClass.getClassLoader());
247
213
  commandListenerClass = Class.forName("com.vstarcam.app_p2p_api.ClientCommandListener", true, jniApiClass.getClassLoader());
248
214
  releaseListenerClass = Class.forName("com.vstarcam.app_p2p_api.ClientReleaseListener", true, jniApiClass.getClassLoader());
249
- Log.d(TAG, "Listener interfaces loaded from ClassLoader: " + jniApiClass.getClassLoader());
250
-
251
- // Log all methods on each listener for debugging
252
- Log.d(TAG, "ClientStateListener methods:");
253
- for (Method m : stateListenerClass.getDeclaredMethods()) {
254
- Log.d(TAG, " -> " + m.getReturnType().getSimpleName() + " " + m.getName() + "(" + java.util.Arrays.toString(m.getParameterTypes()) + ")");
255
- }
256
- Log.d(TAG, "ClientCommandListener methods:");
257
- for (Method m : commandListenerClass.getDeclaredMethods()) {
258
- Log.d(TAG, " -> " + m.getReturnType().getSimpleName() + " " + m.getName() + "(" + java.util.Arrays.toString(m.getParameterTypes()) + ")");
259
- }
260
- Log.d(TAG, "ClientReleaseListener methods:");
261
- for (Method m : releaseListenerClass.getDeclaredMethods()) {
262
- Log.d(TAG, " -> " + m.getReturnType().getSimpleName() + " " + m.getName() + "(" + java.util.Arrays.toString(m.getParameterTypes()) + ")");
263
- }
215
+ Log.d(TAG, "Listener interfaces loaded");
264
216
 
265
217
  // Initialize P2P system with listeners
266
218
  initializeP2P();
@@ -290,7 +242,7 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
290
242
  try {
291
243
  String methodName = method.getName();
292
244
 
293
- // Handle standard Object methods to avoid crashes on hashCode/toString calls via JNI
245
+ // Handle standard Object methods
294
246
  if (method.getDeclaringClass() == Object.class) {
295
247
  if (methodName.equals("equals")) return proxy == args[0];
296
248
  if (methodName.equals("hashCode")) return System.identityHashCode(proxy);
@@ -317,7 +269,7 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
317
269
  } catch (Exception e) {
318
270
  Log.e(TAG, "Error in stateListener callback", e);
319
271
  }
320
- return null; // void stateListener
272
+ return null;
321
273
  }
322
274
  }
323
275
  );
@@ -345,12 +297,20 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
345
297
  final byte[] data = (byte[]) args[1];
346
298
  final int length = (Integer) args[2];
347
299
  Log.d(TAG, "Command callback: sdkPtr=" + sdkPtr + ", len=" + length);
348
- handleCommandReceive(sdkPtr, 0, data);
300
+
301
+ if (reactContext != null) {
302
+ reactContext.runOnUiQueueThread(new Runnable() {
303
+ @Override
304
+ public void run() {
305
+ handleCommandReceive(sdkPtr, 0, data);
306
+ }
307
+ });
308
+ }
349
309
  }
350
310
  } catch (Exception e) {
351
311
  Log.e(TAG, "Error in commandListener callback", e);
352
312
  }
353
- return null; // void commandListener
313
+ return null;
354
314
  }
355
315
  }
356
316
  );
@@ -373,19 +333,10 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
373
333
  }
374
334
 
375
335
  Log.d(TAG, "ClientReleaseListener." + methodName + " called");
376
- if (methodName.equals("releaseListener")) {
377
- final long sdkPtr = (Long) args[0];
378
- Log.d(TAG, "Release callback: sdkPtr=" + sdkPtr);
379
-
380
- // info.sdkClientPtr = 0
381
- // NOTE: In 1.0.35 we cleared the pointer here, but it caused "Client not connected"
382
- // because the SDK seems to call this on various events, not just destruction.
383
- // We will only clear it on explicit disconnect/destroy.
384
- }
385
336
  } catch (Exception e) {
386
337
  Log.e(TAG, "Error in releaseListener callback", e);
387
338
  }
388
- return null; // void releaseListener
339
+ return null;
389
340
  }
390
341
  }
391
342
  );
@@ -393,7 +344,6 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
393
344
  // Call JNIApi.init(stateListener, commandListener, releaseListener)
394
345
  Method initMethod = jniApiClass.getMethod("init",
395
346
  stateListenerClass, commandListenerClass, releaseListenerClass);
396
- Log.d(TAG, "Calling JNIApi.init with proxies...");
397
347
  initMethod.invoke(null, stateListenerProxy, commandListenerProxy, releaseListenerProxy);
398
348
 
399
349
  isP2PInitialized = true;
@@ -417,7 +367,7 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
417
367
  sendEvent("onConnectionStateChanged", params);
418
368
 
419
369
  // Update connected state
420
- entry.getValue().isConnected = (state == 2); // ONLINE is 2 in native, 3 in JS (now aligned to 2 in index.ts)
370
+ entry.getValue().isConnected = (state == 3); // ONLINE
421
371
  break;
422
372
  }
423
373
  }
@@ -511,15 +461,6 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
511
461
  return;
512
462
  }
513
463
 
514
- // Explicitly retain the client pointer to prevent premature release by native SDK
515
- try {
516
- Method retainMethod = jniApiClass.getMethod("retain", long.class);
517
- retainMethod.invoke(null, sdkClientPtr);
518
- Log.d(TAG, "JNIApi.retain called for: " + sdkClientPtr);
519
- } catch (Exception re) {
520
- Log.e(TAG, "Failed to call retain: " + re.getMessage());
521
- }
522
-
523
464
  // Generate a unique internal client ID
524
465
  int ourClientPtr = nextClientPtr.getAndIncrement();
525
466
 
@@ -565,12 +506,11 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
565
506
  params.putInt("state", 1); // CONNECTING
566
507
  sendEvent("onConnectionStateChanged", params);
567
508
 
568
- // CRITICAL: For virtual UIDs (like ACAE...), always use the ORIGINAL device ID
569
- // to look up service parameters. Even if it's converted to a VSTJ/VSTN real ID,
570
- // it often must connect via the specific vstarcam2018/2019 servers matched to its prefix.
509
+ // Use realClientId for service param lookup (it has the correct prefix like VSTN)
510
+ String realId = clientInfo.realClientId != null ? clientInfo.realClientId : clientInfo.deviceId;
571
511
  String param = (serverParam != null && !serverParam.isEmpty())
572
512
  ? serverParam
573
- : getServiceParam(clientInfo.deviceId);
513
+ : getServiceParam(realId);
574
514
 
575
515
  // Default connectType to 126 (from V1 app) if not specified
576
516
  // connectType 126 = P2P/Relay combination that works reliably
@@ -586,21 +526,6 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
586
526
  try {
587
527
  Object result = connectMethod.invoke(null, clientInfo.sdkClientPtr, 15, param, actualConnectType);
588
528
  Log.d(TAG, "JNIApi.connect result: " + result);
589
-
590
- int res = (Integer) result;
591
- Log.d(TAG, "JNIApi.connect result: " + res);
592
-
593
- // CRITICAL: In some P2P SDKs, handles can be negative (like pointers).
594
- // We also saw '7' as a successful handle.
595
- // We will only reject if it's a specific known error code (usually -1 or -2, but we'll be permissive).
596
- if (res == -1) {
597
- Log.w(TAG, "JNIApi.connect failed with code: " + res);
598
- promise.reject("CONNECT_FAILED", "SDK connect error code: " + res);
599
- return;
600
- }
601
-
602
- // Code >= 0 or other negative values might be a success (Session ID or State)
603
- Log.d(TAG, "JNIApi.connect successful or handle returned: " + res);
604
529
  } catch (java.lang.reflect.InvocationTargetException ite) {
605
530
  Log.e(TAG, "JNIApi.connect threw exception: " + ite.getCause(), ite.getCause());
606
531
  throw ite;
@@ -669,6 +594,8 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
669
594
  loginResult = (res >= 0 || res < -2); // Permissive: only reject -1, -2 as common errors
670
595
  } else if (result != null) {
671
596
  loginResult = true; // Non-null result usually means success
597
+ } else {
598
+ loginResult = true; // result == null (void return) means success if no exception
672
599
  }
673
600
  } catch (java.lang.reflect.InvocationTargetException ite) {
674
601
  Throwable cause = ite.getCause();
@@ -915,10 +842,6 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
915
842
  } catch (Exception e) {
916
843
  Log.e(TAG, "checkMode JNI call failed", e);
917
844
  }
918
- } else {
919
- Log.w(TAG, "checkMode skipped: clientInfo=" + clientInfo +
920
- ", jniApiClass=" + (jniApiClass != null) +
921
- ", sdkPtr=" + (clientInfo != null ? clientInfo.sdkClientPtr : "null"));
922
845
  }
923
846
 
924
847
  // Mode > 0 means connected (1=P2P, 2=Relay, 3=Socket)
@@ -963,82 +886,4 @@ public class VStarCamModule extends ReactContextBaseJavaModule {
963
886
  }
964
887
  promise.resolve(result);
965
888
  }
966
-
967
- /**
968
- * Get device info via get_status.cgi
969
- */
970
- @ReactMethod
971
- public void clientGetDeviceInfo(int clientPtr, Promise promise) {
972
- executor.execute(() -> {
973
- try {
974
- ClientInfo clientInfo = clients.get(clientPtr);
975
- if (clientInfo == null || clientInfo.sdkClientPtr == 0) {
976
- promise.reject("E_NOT_CONNECTED", "Client not connected");
977
- return;
978
- }
979
-
980
- // Use get_status.cgi to check if camera is responsive on network
981
- String cgi = "get_status.cgi?loginuse=admin&loginpas=888888";
982
- if (clientInfo.username != null) {
983
- cgi = String.format("get_status.cgi?loginuse=%s&loginpas=%s",
984
- clientInfo.username, clientInfo.password);
985
- }
986
-
987
- Method writeCgiMethod = jniApiClass.getMethod("writeCgi", long.class, String.class, int.class);
988
- Log.d(TAG, "Verifying network: " + cgi);
989
- Object result = writeCgiMethod.invoke(null, clientInfo.sdkClientPtr, cgi, 5);
990
-
991
- boolean writeSuccess = (result instanceof Boolean) ? (Boolean) result : true;
992
- if (!writeSuccess) {
993
- Log.w(TAG, "Network verification failed: writeCgi returned false");
994
- promise.reject("E_NOT_RESPONSIVE", "Camera not responsive to commands");
995
- return;
996
- }
997
-
998
- // If we reach here, the CGI command was at least sent.
999
- // We'll return a basic object representing success for now.
1000
- WritableMap response = Arguments.createMap();
1001
- response.putString("serialNumber", clientInfo.deviceId);
1002
- response.putString("model", "VStarCam Device");
1003
- response.putBoolean("responsive", true);
1004
- promise.resolve(response);
1005
- } catch (Exception e) {
1006
- Log.e(TAG, "clientGetDeviceInfo failed", e);
1007
- promise.reject("E_GET_INFO_FAILED", e.getMessage());
1008
- }
1009
- });
1010
- }
1011
-
1012
- /**
1013
- * Get the current Wi-Fi SSID of the Android device
1014
- */
1015
- @ReactMethod
1016
- public void getWiFiSSID(Promise promise) {
1017
- try {
1018
- android.net.wifi.WifiManager wifiManager = (android.net.wifi.WifiManager)
1019
- reactContext.getApplicationContext().getSystemService(android.content.Context.WIFI_SERVICE);
1020
-
1021
- if (wifiManager != null) {
1022
- android.net.wifi.WifiInfo info = wifiManager.getConnectionInfo();
1023
- if (info != null) {
1024
- String ssid = info.getSSID();
1025
- // Android returns SSID with quotes, e.g. "MyNetwork", remove them
1026
- if (ssid != null && ssid.length() > 1 && ssid.startsWith("\"") && ssid.endsWith("\"")) {
1027
- ssid = ssid.substring(1, ssid.length() - 1);
1028
- }
1029
- // Handle "unknown ssid" case
1030
- if ("<unknown ssid>".equals(ssid)) {
1031
- promise.resolve(null);
1032
- } else {
1033
- promise.resolve(ssid);
1034
- }
1035
- return;
1036
- }
1037
- }
1038
- promise.resolve(null);
1039
- } catch (Exception e) {
1040
- Log.e(TAG, "Failed to get WiFi SSID", e);
1041
- promise.resolve(null);
1042
- }
1043
- }
1044
889
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bits-innovate/react-native-vstarcam",
3
- "version": "1.0.37",
3
+ "version": "1.0.38",
4
4
  "description": "React Native bridge for VStarCam P2P SDK",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
package/src/index.ts CHANGED
@@ -95,17 +95,17 @@ const VStarCamModule = NativeModules.VStarCam
95
95
 
96
96
  // Connection states
97
97
  export enum ConnectionState {
98
- CONNECTING = 0,
99
- INITIALIZING = 1,
100
- ONLINE = 2,
101
- CONNECT_FAILED = 3,
102
- DISCONNECTED = 4,
103
- INVALID_ID = 5,
104
- OFFLINE = 6,
105
- TIMEOUT = 7,
106
- WRONG_PWD = 8,
107
- INVALID_CLIENT = 9,
108
- MAX_SESSION = 10,
98
+ CONNECTING = 1,
99
+ INITIALIZING = 2,
100
+ ONLINE = 3,
101
+ CONNECT_FAILED = 4,
102
+ DISCONNECTED = 5,
103
+ INVALID_ID = 6,
104
+ OFFLINE = 7,
105
+ TIMEOUT = 8,
106
+ WRONG_PWD = 9,
107
+ INVALID_CLIENT = 10,
108
+ MAX_SESSION = 11,
109
109
  }
110
110
 
111
111
  // Connection modes