@cloudcare/cocos-sdk 0.1.0-alpha.1

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 (161) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +31 -0
  3. package/THIRD_PARTY_NOTICES +5 -0
  4. package/bin/cli.cjs +127 -0
  5. package/creator2/package.json +4 -0
  6. package/creator3/package.json +5 -0
  7. package/dist/creator2/core/auto-tracking.d.ts +35 -0
  8. package/dist/creator2/core/auto-tracking.d.ts.map +1 -0
  9. package/dist/creator2/core/auto-tracking.js +337 -0
  10. package/dist/creator2/core/auto-tracking.js.map +1 -0
  11. package/dist/creator2/core/client.d.ts +74 -0
  12. package/dist/creator2/core/client.d.ts.map +1 -0
  13. package/dist/creator2/core/client.js +176 -0
  14. package/dist/creator2/core/client.js.map +1 -0
  15. package/dist/creator2/core/index.d.ts +3 -0
  16. package/dist/creator2/core/index.d.ts.map +1 -0
  17. package/dist/creator2/core/index.js +6 -0
  18. package/dist/creator2/core/index.js.map +1 -0
  19. package/dist/creator2/core/modules.d.ts +159 -0
  20. package/dist/creator2/core/modules.d.ts.map +1 -0
  21. package/dist/creator2/core/modules.js +319 -0
  22. package/dist/creator2/core/modules.js.map +1 -0
  23. package/dist/creator2/core/replay-budget.d.ts +28 -0
  24. package/dist/creator2/core/replay-budget.d.ts.map +1 -0
  25. package/dist/creator2/core/replay-budget.js +113 -0
  26. package/dist/creator2/core/replay-budget.js.map +1 -0
  27. package/dist/creator2/core/replay-diagnostics.d.ts +36 -0
  28. package/dist/creator2/core/replay-diagnostics.d.ts.map +1 -0
  29. package/dist/creator2/core/replay-diagnostics.js +40 -0
  30. package/dist/creator2/core/replay-diagnostics.js.map +1 -0
  31. package/dist/creator2/core/replay-encoding.d.ts +31 -0
  32. package/dist/creator2/core/replay-encoding.d.ts.map +1 -0
  33. package/dist/creator2/core/replay-encoding.js +63 -0
  34. package/dist/creator2/core/replay-encoding.js.map +1 -0
  35. package/dist/creator2/core/replay-pixels.d.ts +3 -0
  36. package/dist/creator2/core/replay-pixels.d.ts.map +1 -0
  37. package/dist/creator2/core/replay-pixels.js +16 -0
  38. package/dist/creator2/core/replay-pixels.js.map +1 -0
  39. package/dist/creator2/core/replay.d.ts +97 -0
  40. package/dist/creator2/core/replay.d.ts.map +1 -0
  41. package/dist/creator2/core/replay.js +578 -0
  42. package/dist/creator2/core/replay.js.map +1 -0
  43. package/dist/creator2/core/transport.d.ts +16 -0
  44. package/dist/creator2/core/transport.d.ts.map +1 -0
  45. package/dist/creator2/core/transport.js +33 -0
  46. package/dist/creator2/core/transport.js.map +1 -0
  47. package/dist/creator2/core/types.d.ts +260 -0
  48. package/dist/creator2/core/types.d.ts.map +1 -0
  49. package/dist/creator2/core/types.js +3 -0
  50. package/dist/creator2/core/types.js.map +1 -0
  51. package/dist/creator2/core/validation.d.ts +14 -0
  52. package/dist/creator2/core/validation.d.ts.map +1 -0
  53. package/dist/creator2/core/validation.js +81 -0
  54. package/dist/creator2/core/validation.js.map +1 -0
  55. package/dist/creator2/core/version.d.ts +9 -0
  56. package/dist/creator2/core/version.d.ts.map +1 -0
  57. package/dist/creator2/core/version.js +14 -0
  58. package/dist/creator2/core/version.js.map +1 -0
  59. package/dist/creator2/creator2/auto.d.ts +9 -0
  60. package/dist/creator2/creator2/auto.d.ts.map +1 -0
  61. package/dist/creator2/creator2/auto.js +65 -0
  62. package/dist/creator2/creator2/auto.js.map +1 -0
  63. package/dist/creator2/creator2/capture.d.ts +17 -0
  64. package/dist/creator2/creator2/capture.d.ts.map +1 -0
  65. package/dist/creator2/creator2/capture.js +162 -0
  66. package/dist/creator2/creator2/capture.js.map +1 -0
  67. package/dist/creator2/creator2/index.d.ts +12 -0
  68. package/dist/creator2/creator2/index.d.ts.map +1 -0
  69. package/dist/creator2/creator2/index.js +41 -0
  70. package/dist/creator2/creator2/index.js.map +1 -0
  71. package/dist/creator2/creator2/transport.d.ts +7 -0
  72. package/dist/creator2/creator2/transport.d.ts.map +1 -0
  73. package/dist/creator2/creator2/transport.js +34 -0
  74. package/dist/creator2/creator2/transport.js.map +1 -0
  75. package/dist/creator3/core/auto-tracking.d.ts +35 -0
  76. package/dist/creator3/core/auto-tracking.d.ts.map +1 -0
  77. package/dist/creator3/core/auto-tracking.js +247 -0
  78. package/dist/creator3/core/auto-tracking.js.map +1 -0
  79. package/dist/creator3/core/client.d.ts +74 -0
  80. package/dist/creator3/core/client.d.ts.map +1 -0
  81. package/dist/creator3/core/client.js +158 -0
  82. package/dist/creator3/core/client.js.map +1 -0
  83. package/dist/creator3/core/index.d.ts +3 -0
  84. package/dist/creator3/core/index.d.ts.map +1 -0
  85. package/dist/creator3/core/index.js +2 -0
  86. package/dist/creator3/core/index.js.map +1 -0
  87. package/dist/creator3/core/modules.d.ts +159 -0
  88. package/dist/creator3/core/modules.d.ts.map +1 -0
  89. package/dist/creator3/core/modules.js +293 -0
  90. package/dist/creator3/core/modules.js.map +1 -0
  91. package/dist/creator3/core/replay-budget.d.ts +28 -0
  92. package/dist/creator3/core/replay-budget.d.ts.map +1 -0
  93. package/dist/creator3/core/replay-budget.js +105 -0
  94. package/dist/creator3/core/replay-budget.js.map +1 -0
  95. package/dist/creator3/core/replay-diagnostics.d.ts +36 -0
  96. package/dist/creator3/core/replay-diagnostics.d.ts.map +1 -0
  97. package/dist/creator3/core/replay-diagnostics.js +36 -0
  98. package/dist/creator3/core/replay-diagnostics.js.map +1 -0
  99. package/dist/creator3/core/replay-encoding.d.ts +31 -0
  100. package/dist/creator3/core/replay-encoding.d.ts.map +1 -0
  101. package/dist/creator3/core/replay-encoding.js +62 -0
  102. package/dist/creator3/core/replay-encoding.js.map +1 -0
  103. package/dist/creator3/core/replay-pixels.d.ts +3 -0
  104. package/dist/creator3/core/replay-pixels.d.ts.map +1 -0
  105. package/dist/creator3/core/replay-pixels.js +13 -0
  106. package/dist/creator3/core/replay-pixels.js.map +1 -0
  107. package/dist/creator3/core/replay.d.ts +97 -0
  108. package/dist/creator3/core/replay.d.ts.map +1 -0
  109. package/dist/creator3/core/replay.js +520 -0
  110. package/dist/creator3/core/replay.js.map +1 -0
  111. package/dist/creator3/core/transport.d.ts +16 -0
  112. package/dist/creator3/core/transport.d.ts.map +1 -0
  113. package/dist/creator3/core/transport.js +27 -0
  114. package/dist/creator3/core/transport.js.map +1 -0
  115. package/dist/creator3/core/types.d.ts +260 -0
  116. package/dist/creator3/core/types.d.ts.map +1 -0
  117. package/dist/creator3/core/types.js +2 -0
  118. package/dist/creator3/core/types.js.map +1 -0
  119. package/dist/creator3/core/validation.d.ts +14 -0
  120. package/dist/creator3/core/validation.d.ts.map +1 -0
  121. package/dist/creator3/core/validation.js +75 -0
  122. package/dist/creator3/core/validation.js.map +1 -0
  123. package/dist/creator3/core/version.d.ts +9 -0
  124. package/dist/creator3/core/version.d.ts.map +1 -0
  125. package/dist/creator3/core/version.js +11 -0
  126. package/dist/creator3/core/version.js.map +1 -0
  127. package/dist/creator3/creator3/auto.d.ts +9 -0
  128. package/dist/creator3/creator3/auto.d.ts.map +1 -0
  129. package/dist/creator3/creator3/auto.js +45 -0
  130. package/dist/creator3/creator3/auto.js.map +1 -0
  131. package/dist/creator3/creator3/capture.d.ts +17 -0
  132. package/dist/creator3/creator3/capture.d.ts.map +1 -0
  133. package/dist/creator3/creator3/capture.js +117 -0
  134. package/dist/creator3/creator3/capture.js.map +1 -0
  135. package/dist/creator3/creator3/index.d.ts +12 -0
  136. package/dist/creator3/creator3/index.d.ts.map +1 -0
  137. package/dist/creator3/creator3/index.js +23 -0
  138. package/dist/creator3/creator3/index.js.map +1 -0
  139. package/dist/creator3/creator3/replay-render-cycle.d.ts +9 -0
  140. package/dist/creator3/creator3/replay-render-cycle.d.ts.map +1 -0
  141. package/dist/creator3/creator3/replay-render-cycle.js +12 -0
  142. package/dist/creator3/creator3/replay-render-cycle.js.map +1 -0
  143. package/dist/creator3/creator3/transport.d.ts +8 -0
  144. package/dist/creator3/creator3/transport.d.ts.map +1 -0
  145. package/dist/creator3/creator3/transport.js +24 -0
  146. package/dist/creator3/creator3/transport.js.map +1 -0
  147. package/dist/creator3/package.json +1 -0
  148. package/extensions/creator2/install-native.cjs +333 -0
  149. package/extensions/creator2/main.js +23 -0
  150. package/extensions/creator2/package.json +6 -0
  151. package/extensions/creator3/dist/builder.js +9 -0
  152. package/extensions/creator3/dist/hooks.js +70 -0
  153. package/extensions/creator3/dist/main.js +4 -0
  154. package/extensions/creator3/install-native.cjs +333 -0
  155. package/extensions/creator3/package.json +10 -0
  156. package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosBridge.java +532 -0
  157. package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosBridgeProvider.java +70 -0
  158. package/native/ios/FTCocosBridge.h +10 -0
  159. package/native/ios/FTCocosBridge.m +470 -0
  160. package/native/ios/FTCocosBridge.podspec +18 -0
  161. package/package.json +60 -0
@@ -0,0 +1,532 @@
1
+ package com.ft.sdk.cocos;
2
+
3
+ import android.app.Activity;
4
+ import android.app.ActivityManager;
5
+ import android.app.Application;
6
+ import android.content.Context;
7
+ import android.net.Uri;
8
+ import android.os.Build;
9
+ import android.os.Bundle;
10
+ import android.os.Process;
11
+
12
+ import com.ft.sdk.DetectFrequency;
13
+ import com.ft.sdk.FTLogger;
14
+ import com.ft.sdk.FTLoggerConfig;
15
+ import com.ft.sdk.FTRUMConfig;
16
+ import com.ft.sdk.FTRUMGlobalManager;
17
+ import com.ft.sdk.FTSDKConfig;
18
+ import com.ft.sdk.FTSdk;
19
+ import com.ft.sdk.FTTraceConfig;
20
+ import com.ft.sdk.FTTraceManager;
21
+ import com.ft.sdk.LogCacheDiscard;
22
+ import com.ft.sdk.SessionReplayManager;
23
+ import com.ft.sdk.TraceType;
24
+ import com.ft.sdk.garble.bean.AppState;
25
+ import com.ft.sdk.garble.bean.NetStatusBean;
26
+ import com.ft.sdk.garble.bean.ResourceParams;
27
+ import com.ft.sdk.garble.bean.UserData;
28
+ import com.ft.sdk.sessionreplay.FTSessionReplayConfig;
29
+ import com.ft.sdk.sessionreplay.SessionReplayInternalCallback;
30
+ import com.ft.sdk.sessionreplay.TouchPrivacy;
31
+
32
+ import org.json.JSONArray;
33
+ import org.json.JSONObject;
34
+
35
+ import java.io.ByteArrayOutputStream;
36
+ import java.io.File;
37
+ import java.io.FileInputStream;
38
+ import java.lang.reflect.InvocationTargetException;
39
+ import java.lang.reflect.Method;
40
+ import java.util.ArrayList;
41
+ import java.util.HashMap;
42
+ import java.util.Iterator;
43
+ import java.util.List;
44
+ import java.util.Locale;
45
+ import java.util.Map;
46
+
47
+ /** Cocos reflection entry point. All calls and responses are JSON envelopes. */
48
+ public final class FTCocosBridge {
49
+ private static final String COCOS_SDK_VERSION_KEY = "sdk_package_cocos";
50
+
51
+ private FTCocosBridge() {
52
+ }
53
+
54
+ public static String invoke(String method, String payload) {
55
+ String forwarded = forwardToMainProcess(method, payload);
56
+ if (forwarded != null) return forwarded;
57
+ return invokeLocal(method, payload);
58
+ }
59
+
60
+ static String invokeLocal(String method, String payload) {
61
+ try {
62
+ Object value = dispatch(method, payload == null || payload.isEmpty()
63
+ ? new JSONObject() : new JSONObject(payload));
64
+ JSONObject response = new JSONObject();
65
+ response.put("ok", true);
66
+ if (value != null) response.put("value", JSONObject.wrap(value));
67
+ return response.toString();
68
+ } catch (Throwable error) {
69
+ JSONObject response = new JSONObject();
70
+ try {
71
+ response.put("ok", false);
72
+ response.put("error", error.getClass().getSimpleName() + ": " + error.getMessage());
73
+ } catch (Throwable ignored) {
74
+ return "{\"ok\":false,\"error\":\"Native bridge failure\"}";
75
+ }
76
+ return response.toString();
77
+ }
78
+ }
79
+
80
+ private static String forwardToMainProcess(String method, String payload) {
81
+ Activity activity = resolveActivity();
82
+ if (activity == null || !isSecondaryProcess(activity)) return null;
83
+
84
+ try {
85
+ Bundle arguments = new Bundle();
86
+ arguments.putString(FTCocosBridgeProvider.ARGUMENT_PAYLOAD, payload);
87
+ Uri uri = Uri.parse("content://" + activity.getPackageName()
88
+ + FTCocosBridgeProvider.AUTHORITY_SUFFIX);
89
+ Bundle result = activity.getContentResolver().call(
90
+ uri,
91
+ FTCocosBridgeProvider.METHOD_INVOKE,
92
+ method,
93
+ arguments);
94
+ return result == null
95
+ ? null
96
+ : result.getString(FTCocosBridgeProvider.RESULT_RESPONSE);
97
+ } catch (IllegalArgumentException providerUnavailable) {
98
+ // Existing integrations may not have registered the optional bridge
99
+ // provider yet. Preserve their same-process behavior.
100
+ return null;
101
+ }
102
+ }
103
+
104
+ private static boolean isSecondaryProcess(Context context) {
105
+ String currentProcess = currentProcessName(context);
106
+ String mainProcess = context.getApplicationInfo().processName;
107
+ return currentProcess != null && mainProcess != null && !mainProcess.equals(currentProcess);
108
+ }
109
+
110
+ private static String currentProcessName(Context context) {
111
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
112
+ return Application.getProcessName();
113
+ }
114
+ ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
115
+ if (manager == null) return null;
116
+ List<ActivityManager.RunningAppProcessInfo> processes = manager.getRunningAppProcesses();
117
+ if (processes == null) return null;
118
+ int currentPid = Process.myPid();
119
+ for (ActivityManager.RunningAppProcessInfo process : processes) {
120
+ if (process.pid == currentPid) return process.processName;
121
+ }
122
+ return null;
123
+ }
124
+
125
+ private static Object dispatch(String method, JSONObject payload) throws Exception {
126
+ switch (method) {
127
+ case "hybrid.attach":
128
+ attachHybrid(payload);
129
+ return null;
130
+ case "hybrid.setExternalRecorderActive":
131
+ setExternalRecorderActive(payload.getBoolean("active"));
132
+ return null;
133
+ case "sdk.configure":
134
+ configureSdk(payload);
135
+ return null;
136
+ case "sdk.bindUser":
137
+ bindUser(payload);
138
+ return null;
139
+ case "sdk.unbindUser":
140
+ FTSdk.unbindRumUserData();
141
+ return null;
142
+ case "sdk.shutdown":
143
+ FTSdk.shutDown();
144
+ return null;
145
+ case "rum.configure":
146
+ configureRum(payload);
147
+ return null;
148
+ case "rum.startView":
149
+ FTRUMGlobalManager.get().startView(payload.getString("name"), map(payload.optJSONObject("attributes")));
150
+ return null;
151
+ case "rum.stopView":
152
+ FTRUMGlobalManager.get().stopView(map(payload.optJSONObject("attributes")));
153
+ return null;
154
+ case "rum.addAction":
155
+ FTRUMGlobalManager.get().addAction(payload.getString("name"), payload.optString("type", "click"), map(payload.optJSONObject("attributes")));
156
+ return null;
157
+ case "rum.startAction":
158
+ FTRUMGlobalManager.get().startAction(payload.getString("name"), payload.optString("type", "click"), map(payload.optJSONObject("attributes")));
159
+ return null;
160
+ case "rum.addError":
161
+ FTRUMGlobalManager.get().addError(
162
+ payload.optString("stack", ""),
163
+ payload.optString("message", ""),
164
+ payload.optString("type", "cocos_error"),
165
+ AppState.getValueFrom(payload.optString("state", "run")),
166
+ map(payload.optJSONObject("attributes")));
167
+ return null;
168
+ case "rum.addLongTask":
169
+ FTRUMGlobalManager.get().addLongTask(payload.optString("stack", ""), payload.optLong("durationNs"), map(payload.optJSONObject("attributes")));
170
+ return null;
171
+ case "rum.startResource":
172
+ FTRUMGlobalManager.get().startResource(payload.getString("key"), map(payload.optJSONObject("attributes")));
173
+ return null;
174
+ case "rum.stopResource":
175
+ FTRUMGlobalManager.get().stopResource(payload.getString("key"), map(payload.optJSONObject("attributes")));
176
+ return null;
177
+ case "rum.addResource":
178
+ addResource(payload);
179
+ return null;
180
+ case "logger.configure":
181
+ configureLogger(payload);
182
+ return null;
183
+ case "logger.log":
184
+ FTLogger.getInstance().logBackground(
185
+ payload.optString("content", ""),
186
+ payload.optString("level", "info"),
187
+ map(payload.optJSONObject("attributes")));
188
+ return null;
189
+ case "trace.configure":
190
+ configureTrace(payload);
191
+ return null;
192
+ case "trace.getHeaders":
193
+ return FTTraceManager.get().getTraceHeader(
194
+ payload.optString("resourceKey", ""), payload.getString("url"));
195
+ case "replay.configure":
196
+ configureReplay(payload);
197
+ return null;
198
+ case "replay.getContext":
199
+ return SessionReplayManager.get().getCurrentFlutterRumContext();
200
+ case "replay.saveImage":
201
+ return saveReplayImage(payload);
202
+ case "replay.saveImageV2":
203
+ return saveReplayImageV2(payload);
204
+ case "replay.writeSegment":
205
+ SessionReplayManager.get().writeExternalSegment(
206
+ payload.getString("segment"), payload.getString("viewId"));
207
+ return null;
208
+ case "replay.setRecordCount":
209
+ SessionReplayManager.get().setExternalRecordCount(
210
+ payload.getString("viewId"), payload.getLong("count"));
211
+ return null;
212
+ case "replay.stop":
213
+ return null;
214
+ default:
215
+ throw new IllegalArgumentException("Unknown bridge method: " + method);
216
+ }
217
+ }
218
+
219
+ private static void attachHybrid(JSONObject json) throws Exception {
220
+ ensureNativeHostInitialized();
221
+ if (hasText(json, "sdkVersion")) {
222
+ FTSdk.appendGlobalContext(COCOS_SDK_VERSION_KEY, json.getString("sdkVersion"));
223
+ }
224
+ if (json.optBoolean("requiresReplay", false)) hybridRecorderSwitchMethod();
225
+ }
226
+
227
+ private static void ensureNativeHostInitialized() {
228
+ if (FTSdk.get() == null) {
229
+ throw new IllegalStateException(
230
+ "The native host must install the native SDK before FTCocosSDK.attach()");
231
+ }
232
+ }
233
+
234
+ private static Method hybridRecorderSwitchMethod() {
235
+ try {
236
+ return SessionReplayManager.class.getMethod(
237
+ "setExternalRecorderActive", boolean.class);
238
+ } catch (NoSuchMethodException error) {
239
+ throw new IllegalStateException(
240
+ "Android Session Replay does not support Hybrid recorder switching; "
241
+ + "upgrade the Native SDK");
242
+ }
243
+ }
244
+
245
+ private static void setExternalRecorderActive(boolean active) throws Exception {
246
+ ensureNativeHostInitialized();
247
+ try {
248
+ hybridRecorderSwitchMethod().invoke(SessionReplayManager.get(), active);
249
+ } catch (InvocationTargetException error) {
250
+ Throwable cause = error.getCause();
251
+ if (cause instanceof Exception) throw (Exception) cause;
252
+ throw error;
253
+ }
254
+ }
255
+
256
+ private static void configureSdk(JSONObject json) throws Exception {
257
+ FTSDKConfig config;
258
+ if (hasText(json, "datakitUrl")) {
259
+ config = FTSDKConfig.builder(json.getString("datakitUrl"));
260
+ } else if (hasText(json, "datawayUrl") && hasText(json, "clientToken")) {
261
+ config = FTSDKConfig.builder(json.getString("datawayUrl"), json.getString("clientToken"));
262
+ } else {
263
+ throw new IllegalArgumentException("datakitUrl or datawayUrl/clientToken is required");
264
+ }
265
+ if (json.has("debug")) config.setDebug(json.getBoolean("debug"));
266
+ if (hasText(json, "env")) config.setEnv(json.getString("env"));
267
+ if (hasText(json, "serviceName")) config.setServiceName(json.getString("serviceName"));
268
+ addStringContext(json.optJSONObject("globalContext"), new StringContextSetter() {
269
+ @Override public void set(String key, String value) { config.addGlobalContext(key, value); }
270
+ });
271
+ FTSdk.install(config);
272
+ }
273
+
274
+ private static void bindUser(JSONObject json) throws Exception {
275
+ if (!json.has("userName") && !json.has("userEmail") && !json.has("extra")) {
276
+ FTSdk.bindRumUserData(json.getString("userId"));
277
+ return;
278
+ }
279
+ UserData data = new UserData();
280
+ data.setId(json.getString("userId"));
281
+ if (hasText(json, "userName")) data.setName(json.getString("userName"));
282
+ if (hasText(json, "userEmail")) data.setEmail(json.getString("userEmail"));
283
+ JSONObject extra = json.optJSONObject("extra");
284
+ if (extra != null) data.setExts(stringMap(extra));
285
+ FTSdk.bindRumUserData(data);
286
+ }
287
+
288
+ private static void configureRum(JSONObject json) throws Exception {
289
+ String appId = json.optString("androidAppId", "");
290
+ if (appId.isEmpty()) throw new IllegalArgumentException("rum.androidAppId is required on Android");
291
+ final FTRUMConfig config = new FTRUMConfig().setRumAppId(appId);
292
+ if (json.has("sampleRate")) config.setSamplingRate((float) json.getDouble("sampleRate"));
293
+ if (json.has("sessionOnErrorSampleRate")) config.setSessionErrorSampleRate((float) json.getDouble("sessionOnErrorSampleRate"));
294
+ if (json.has("enableNativeUserAction")) config.setEnableTraceUserAction(json.getBoolean("enableNativeUserAction"));
295
+ if (json.has("enableNativeUserView")) config.setEnableTraceUserView(json.getBoolean("enableNativeUserView"));
296
+ if (json.has("enableNativeUserResource")) config.setEnableTraceUserResource(json.getBoolean("enableNativeUserResource"));
297
+ if (json.has("enableNativeCrash")) config.setEnableTrackAppCrash(json.getBoolean("enableNativeCrash"));
298
+ if (json.has("enableNativeAnr")) config.setEnableTrackAppANR(json.getBoolean("enableNativeAnr"));
299
+ if (json.has("enableNativeUiBlock")) {
300
+ boolean enabled = json.getBoolean("enableNativeUiBlock");
301
+ if (json.has("nativeUiBlockDurationMs")) config.setEnableTrackAppUIBlock(enabled, json.getLong("nativeUiBlockDurationMs"));
302
+ else config.setEnableTrackAppUIBlock(enabled);
303
+ }
304
+ if (json.has("errorMonitorType")) config.setExtraMonitorTypeWithError(json.getInt("errorMonitorType"));
305
+ if (json.has("deviceMetricsMonitorType")) {
306
+ config.setDeviceMetricsMonitorType(json.getInt("deviceMetricsMonitorType"), detectFrequency(json.optString("detectFrequency", "normal")));
307
+ }
308
+ addStringContext(json.optJSONObject("globalContext"), new StringContextSetter() {
309
+ @Override public void set(String key, String value) { config.addGlobalContext(key, value); }
310
+ });
311
+ FTSdk.initRUMWithConfig(config);
312
+ }
313
+
314
+ private static void configureLogger(JSONObject json) throws Exception {
315
+ final FTLoggerConfig config = new FTLoggerConfig();
316
+ if (json.has("sampleRate")) config.setSamplingRate((float) json.getDouble("sampleRate"));
317
+ if (json.has("enableLinkRumData")) config.setEnableLinkRumData(json.getBoolean("enableLinkRumData"));
318
+ if (json.has("enableCustomLog")) config.setEnableCustomLog(json.getBoolean("enableCustomLog"));
319
+ if (json.has("printCustomLogToConsole")) config.setPrintCustomLogToConsole(json.getBoolean("printCustomLogToConsole"));
320
+ if (json.has("logCacheLimitCount")) config.setLogCacheLimitCount(json.getInt("logCacheLimitCount"));
321
+ if ("discardOldest".equals(json.optString("discardStrategy"))) config.setLogCacheDiscardStrategy(LogCacheDiscard.DISCARD_OLDEST);
322
+ JSONArray levels = json.optJSONArray("logLevelFilters");
323
+ if (levels != null) config.setLogLevelFilters(stringArray(levels));
324
+ addStringContext(json.optJSONObject("globalContext"), new StringContextSetter() {
325
+ @Override public void set(String key, String value) { config.addGlobalContext(key, value); }
326
+ });
327
+ FTSdk.initLogWithConfig(config);
328
+ }
329
+
330
+ private static void configureTrace(JSONObject json) throws Exception {
331
+ FTTraceConfig config = new FTTraceConfig();
332
+ if (json.has("sampleRate")) config.setSamplingRate((float) json.getDouble("sampleRate"));
333
+ if (json.has("enableLinkRumData")) config.setEnableLinkRUMData(json.getBoolean("enableLinkRumData"));
334
+ if (json.has("enableNativeAutoTrace")) config.setEnableAutoTrace(json.getBoolean("enableNativeAutoTrace"));
335
+ if (hasText(json, "traceType")) config.setTraceType(traceType(json.getString("traceType")));
336
+ FTSdk.initTraceWithConfig(config);
337
+ }
338
+
339
+ private static void configureReplay(JSONObject json) throws Exception {
340
+ FTSessionReplayConfig config = new FTSessionReplayConfig();
341
+ if (json.has("sampleRate")) config.setSampleRate((float) json.getDouble("sampleRate"));
342
+ if (json.has("sessionOnErrorSampleRate")) config.setSessionReplayOnErrorSampleRate((float) json.getDouble("sessionOnErrorSampleRate"));
343
+ if ("show".equals(json.optString("touchPrivacy"))) config.setTouchPrivacy(TouchPrivacy.SHOW);
344
+ config.setInternalCallback(new SessionReplayInternalCallback() {
345
+ @Override public Activity getCurrentActivity() { return resolveActivity(); }
346
+ });
347
+ Method externalMode = FTSessionReplayConfig.class.getDeclaredMethod("setExternalRecorderMode", boolean.class);
348
+ externalMode.setAccessible(true);
349
+ externalMode.invoke(config, true);
350
+ FTSdk.initSessionReplayConfig(config);
351
+ }
352
+
353
+ private static void addResource(JSONObject payload) throws Exception {
354
+ JSONObject content = payload.getJSONObject("content");
355
+ ResourceParams params = new ResourceParams();
356
+ params.url = content.optString("url", "");
357
+ params.resourceMethod = content.optString("httpMethod", "GET");
358
+ params.responseBody = content.optString("responseBody", "");
359
+ params.resourceStatus = content.optInt("statusCode", 0);
360
+ params.responseContentType = content.optString("responseContentType", "");
361
+ params.responseContentEncoding = content.optString("responseContentEncoding", "");
362
+ params.requestHeaderMap = headerMap(content.optJSONObject("requestHeaders"));
363
+ params.responseHeaderMap = headerMap(content.optJSONObject("responseHeaders"));
364
+
365
+ JSONObject metrics = payload.optJSONObject("metrics");
366
+ NetStatusBean status = new NetStatusBean();
367
+ if (metrics != null) {
368
+ status.callStartTime = metric(metrics, "fetchStartTime");
369
+ status.tcpStartTime = metric(metrics, "tcpStartTime");
370
+ status.tcpEndTime = metric(metrics, "tcpEndTime");
371
+ status.dnsStartTime = metric(metrics, "dnsStartTime");
372
+ status.dnsEndTime = metric(metrics, "dnsEndTime");
373
+ status.headerStartTime = metric(metrics, "responseStartTime");
374
+ status.bodyStartTime = metric(metrics, "responseStartTime");
375
+ status.bodyEndTime = metric(metrics, "responseEndTime");
376
+ status.sslStartTime = metric(metrics, "sslStartTime");
377
+ status.sslEndTime = metric(metrics, "sslEndTime");
378
+ }
379
+ FTRUMGlobalManager.get().addResource(payload.getString("key"), params, status);
380
+ }
381
+
382
+ private static String saveReplayImage(JSONObject json) throws Exception {
383
+ byte[] bytes = readFile(new File(json.getString("path")));
384
+ return SessionReplayManager.get().saveFlutterImageResource(bytes, json.getInt("width"), json.getInt("height"));
385
+ }
386
+
387
+ private static Object saveReplayImageV2(JSONObject json) throws Exception {
388
+ byte[] bytes = readFile(new File(json.getString("path")));
389
+ Method method;
390
+ try {
391
+ method = SessionReplayManager.class.getMethod(
392
+ "saveExternalImageResourceV2",
393
+ byte[].class,
394
+ int.class,
395
+ int.class,
396
+ float.class,
397
+ int.class);
398
+ } catch (NoSuchMethodException unavailable) {
399
+ throw new IllegalArgumentException("Unknown bridge method: replay.saveImageV2");
400
+ }
401
+ try {
402
+ return method.invoke(
403
+ SessionReplayManager.get(),
404
+ bytes,
405
+ json.getInt("width"),
406
+ json.getInt("height"),
407
+ (float) json.optDouble("quality", 0.45),
408
+ json.optInt("maxFrameBytes", 40 * 1024));
409
+ } catch (InvocationTargetException error) {
410
+ Throwable cause = error.getCause();
411
+ if (cause instanceof Exception) throw (Exception) cause;
412
+ throw error;
413
+ }
414
+ }
415
+
416
+ private static Activity resolveActivity() {
417
+ String[] classNames = {"com.cocos.lib.CocosActivity", "org.cocos2dx.lib.Cocos2dxActivity"};
418
+ String[] methodNames = {"getContext", "getActivity"};
419
+ for (String className : classNames) {
420
+ for (String methodName : methodNames) {
421
+ try {
422
+ Object value = Class.forName(className).getMethod(methodName).invoke(null);
423
+ if (value instanceof Activity) return (Activity) value;
424
+ if (value instanceof Context && ((Context) value).getApplicationContext() instanceof Activity) {
425
+ return (Activity) ((Context) value).getApplicationContext();
426
+ }
427
+ } catch (Throwable ignored) {
428
+ }
429
+ }
430
+ }
431
+ return null;
432
+ }
433
+
434
+ private static TraceType traceType(String value) {
435
+ if ("zipkinMultiHeader".equals(value)) return TraceType.ZIPKIN_MULTI_HEADER;
436
+ if ("zipkinSingleHeader".equals(value)) return TraceType.ZIPKIN_SINGLE_HEADER;
437
+ if ("traceparent".equals(value)) return TraceType.TRACEPARENT;
438
+ if ("skywalking".equals(value)) return TraceType.SKYWALKING;
439
+ if ("jaeger".equals(value)) return TraceType.JAEGER;
440
+ return TraceType.DDTRACE;
441
+ }
442
+
443
+ private static DetectFrequency detectFrequency(String value) {
444
+ if ("frequent".equals(value)) return DetectFrequency.FREQUENT;
445
+ if ("rare".equals(value)) return DetectFrequency.RARE;
446
+ return DetectFrequency.DEFAULT;
447
+ }
448
+
449
+ private static boolean hasText(JSONObject json, String key) {
450
+ return json.has(key) && !json.optString(key, "").isEmpty();
451
+ }
452
+
453
+ private interface StringContextSetter { void set(String key, String value); }
454
+
455
+ private static void addStringContext(JSONObject json, StringContextSetter setter) throws Exception {
456
+ if (json == null) return;
457
+ Iterator<String> keys = json.keys();
458
+ while (keys.hasNext()) {
459
+ String key = keys.next();
460
+ setter.set(key, json.optString(key, ""));
461
+ }
462
+ }
463
+
464
+ private static HashMap<String, String> stringMap(JSONObject json) throws Exception {
465
+ HashMap<String, String> result = new HashMap<>();
466
+ Iterator<String> keys = json.keys();
467
+ while (keys.hasNext()) {
468
+ String key = keys.next();
469
+ result.put(key, json.optString(key, ""));
470
+ }
471
+ return result;
472
+ }
473
+
474
+ private static HashMap<String, Object> map(JSONObject json) throws Exception {
475
+ HashMap<String, Object> result = new HashMap<>();
476
+ if (json == null) return result;
477
+ Iterator<String> keys = json.keys();
478
+ while (keys.hasNext()) {
479
+ String key = keys.next();
480
+ result.put(key, jsonValue(json.get(key)));
481
+ }
482
+ return result;
483
+ }
484
+
485
+ private static Object jsonValue(Object value) throws Exception {
486
+ if (value == JSONObject.NULL) return null;
487
+ if (value instanceof JSONObject) return map((JSONObject) value);
488
+ if (value instanceof JSONArray) {
489
+ JSONArray array = (JSONArray) value;
490
+ ArrayList<Object> result = new ArrayList<>();
491
+ for (int i = 0; i < array.length(); i++) result.add(jsonValue(array.get(i)));
492
+ return result;
493
+ }
494
+ return value;
495
+ }
496
+
497
+ private static HashMap<String, List<String>> headerMap(JSONObject json) throws Exception {
498
+ HashMap<String, List<String>> result = new HashMap<>();
499
+ if (json == null) return result;
500
+ Iterator<String> keys = json.keys();
501
+ while (keys.hasNext()) {
502
+ String key = keys.next();
503
+ ArrayList<String> values = new ArrayList<>();
504
+ values.add(json.optString(key, ""));
505
+ result.put(key, values);
506
+ }
507
+ return result;
508
+ }
509
+
510
+ private static String[] stringArray(JSONArray array) throws Exception {
511
+ String[] result = new String[array.length()];
512
+ for (int i = 0; i < array.length(); i++) result[i] = array.getString(i);
513
+ return result;
514
+ }
515
+
516
+ private static long metric(JSONObject json, String name) {
517
+ return json.has(name) ? json.optLong(name, -1) : -1;
518
+ }
519
+
520
+ private static byte[] readFile(File file) throws Exception {
521
+ FileInputStream input = new FileInputStream(file);
522
+ try {
523
+ ByteArrayOutputStream output = new ByteArrayOutputStream((int) Math.min(file.length(), Integer.MAX_VALUE));
524
+ byte[] buffer = new byte[16 * 1024];
525
+ int read;
526
+ while ((read = input.read(buffer)) >= 0) output.write(buffer, 0, read);
527
+ return output.toByteArray();
528
+ } finally {
529
+ input.close();
530
+ }
531
+ }
532
+ }
@@ -0,0 +1,70 @@
1
+ package com.ft.sdk.cocos;
2
+
3
+ import android.content.ContentProvider;
4
+ import android.content.ContentValues;
5
+ import android.database.Cursor;
6
+ import android.net.Uri;
7
+ import android.os.Bundle;
8
+
9
+ import androidx.annotation.NonNull;
10
+ import androidx.annotation.Nullable;
11
+
12
+ /**
13
+ * App-private bridge that lets a Cocos engine process use the native SDK owned
14
+ * by the application's main process.
15
+ */
16
+ public final class FTCocosBridgeProvider extends ContentProvider {
17
+ static final String AUTHORITY_SUFFIX = ".ft.cocos.bridge";
18
+ static final String METHOD_INVOKE = "invoke";
19
+ static final String ARGUMENT_PAYLOAD = "payload";
20
+ static final String RESULT_RESPONSE = "response";
21
+
22
+ @Override
23
+ public boolean onCreate() {
24
+ return true;
25
+ }
26
+
27
+ @Nullable
28
+ @Override
29
+ public Bundle call(@NonNull String method, @Nullable String argument, @Nullable Bundle extras) {
30
+ if (!METHOD_INVOKE.equals(method) || argument == null) {
31
+ return super.call(method, argument, extras);
32
+ }
33
+ String payload = extras == null ? null : extras.getString(ARGUMENT_PAYLOAD);
34
+ Bundle result = new Bundle();
35
+ result.putString(RESULT_RESPONSE, FTCocosBridge.invokeLocal(argument, payload));
36
+ return result;
37
+ }
38
+
39
+ @Nullable
40
+ @Override
41
+ public String getType(@NonNull Uri uri) {
42
+ return null;
43
+ }
44
+
45
+ @Nullable
46
+ @Override
47
+ public Cursor query(@NonNull Uri uri, @Nullable String[] projection,
48
+ @Nullable String selection, @Nullable String[] selectionArgs,
49
+ @Nullable String sortOrder) {
50
+ return null;
51
+ }
52
+
53
+ @Nullable
54
+ @Override
55
+ public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
56
+ return null;
57
+ }
58
+
59
+ @Override
60
+ public int delete(@NonNull Uri uri, @Nullable String selection,
61
+ @Nullable String[] selectionArgs) {
62
+ return 0;
63
+ }
64
+
65
+ @Override
66
+ public int update(@NonNull Uri uri, @Nullable ContentValues values,
67
+ @Nullable String selection, @Nullable String[] selectionArgs) {
68
+ return 0;
69
+ }
70
+ }
@@ -0,0 +1,10 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ /** Cocos JSB reflection entry point. */
6
+ @interface FTCocosBridge : NSObject
7
+ + (NSString *)invoke:(NSString *)method payload:(NSString *)payload;
8
+ @end
9
+
10
+ NS_ASSUME_NONNULL_END