@truewatchtech/cocos-sdk 0.1.0-alpha.4 → 0.1.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -1
- package/bin/cli.cjs +48 -18
- package/bin/legacy-asset-hashes.json +32 -0
- package/bin/managed-assets.cjs +67 -0
- package/dist/creator2/core/client.d.ts +55 -7
- package/dist/creator2/core/client.d.ts.map +1 -1
- package/dist/creator2/core/client.js +74 -28
- package/dist/creator2/core/client.js.map +1 -1
- package/dist/creator2/core/index.d.ts +1 -1
- package/dist/creator2/core/index.d.ts.map +1 -1
- package/dist/creator2/core/index.js.map +1 -1
- package/dist/creator2/core/plugin-api.d.ts +15 -0
- package/dist/creator2/core/plugin-api.d.ts.map +1 -0
- package/dist/creator2/core/plugin-api.js +3 -0
- package/dist/creator2/core/plugin-api.js.map +1 -0
- package/dist/creator2/core/transport.d.ts +20 -0
- package/dist/creator2/core/transport.d.ts.map +1 -1
- package/dist/creator2/core/transport.js.map +1 -1
- package/dist/creator2/core/types.d.ts +0 -78
- package/dist/creator2/core/types.d.ts.map +1 -1
- package/dist/creator2/core/validation.d.ts +0 -11
- package/dist/creator2/core/validation.d.ts.map +1 -1
- package/dist/creator2/core/validation.js +0 -63
- package/dist/creator2/core/validation.js.map +1 -1
- package/dist/creator2/core/version.d.ts +1 -1
- package/dist/creator2/core/version.js +1 -1
- package/dist/creator2/creator2/auto.d.ts +1 -3
- package/dist/creator2/creator2/auto.d.ts.map +1 -1
- package/dist/creator2/creator2/auto.js +0 -25
- package/dist/creator2/creator2/auto.js.map +1 -1
- package/dist/creator2/creator2/index.d.ts +0 -7
- package/dist/creator2/creator2/index.d.ts.map +1 -1
- package/dist/creator2/creator2/index.js +1 -13
- package/dist/creator2/creator2/index.js.map +1 -1
- package/dist/creator2/creator2/transport.d.ts +0 -1
- package/dist/creator2/creator2/transport.d.ts.map +1 -1
- package/dist/creator2/creator2/transport.js +0 -4
- package/dist/creator2/creator2/transport.js.map +1 -1
- package/dist/creator3/core/client.d.ts +55 -7
- package/dist/creator3/core/client.d.ts.map +1 -1
- package/dist/creator3/core/client.js +72 -28
- package/dist/creator3/core/client.js.map +1 -1
- package/dist/creator3/core/index.d.ts +1 -1
- package/dist/creator3/core/index.d.ts.map +1 -1
- package/dist/creator3/core/index.js.map +1 -1
- package/dist/creator3/core/plugin-api.d.ts +15 -0
- package/dist/creator3/core/plugin-api.d.ts.map +1 -0
- package/dist/creator3/core/plugin-api.js +2 -0
- package/dist/creator3/core/plugin-api.js.map +1 -0
- package/dist/creator3/core/transport.d.ts +20 -0
- package/dist/creator3/core/transport.d.ts.map +1 -1
- package/dist/creator3/core/transport.js.map +1 -1
- package/dist/creator3/core/types.d.ts +0 -78
- package/dist/creator3/core/types.d.ts.map +1 -1
- package/dist/creator3/core/validation.d.ts +0 -11
- package/dist/creator3/core/validation.d.ts.map +1 -1
- package/dist/creator3/core/validation.js +0 -61
- package/dist/creator3/core/validation.js.map +1 -1
- package/dist/creator3/core/version.d.ts +1 -1
- package/dist/creator3/core/version.js +1 -1
- package/dist/creator3/creator3/auto.d.ts +1 -3
- package/dist/creator3/creator3/auto.d.ts.map +1 -1
- package/dist/creator3/creator3/auto.js +1 -24
- package/dist/creator3/creator3/auto.js.map +1 -1
- package/dist/creator3/creator3/index.d.ts +0 -7
- package/dist/creator3/creator3/index.d.ts.map +1 -1
- package/dist/creator3/creator3/index.js +1 -12
- package/dist/creator3/creator3/index.js.map +1 -1
- package/dist/creator3/creator3/transport.d.ts +0 -1
- package/dist/creator3/creator3/transport.d.ts.map +1 -1
- package/dist/creator3/creator3/transport.js +0 -4
- package/dist/creator3/creator3/transport.js.map +1 -1
- package/extensions/creator2/install-native.cjs +118 -28
- package/extensions/creator2/install-spm.cjs +198 -0
- package/extensions/creator2/legacy-asset-hashes.json +32 -0
- package/extensions/creator2/managed-assets.cjs +67 -0
- package/extensions/creator3/install-native.cjs +118 -28
- package/extensions/creator3/install-spm.cjs +198 -0
- package/extensions/creator3/legacy-asset-hashes.json +32 -0
- package/extensions/creator3/managed-assets.cjs +67 -0
- package/native/android/src/main/java/com/ft/sdk/cocos/FTBridgeTransport.java +85 -0
- package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosBridge.java +2 -192
- package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosBridgeModules.java +9 -0
- package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosBridgeProvider.java +1 -1
- package/native/ios/FTCocosBridge.m +8 -203
- package/native/ios/FTCocosBridge.podspec +3 -4
- package/native/ios/Package.swift +25 -0
- package/native/ios/include/FTCocosBridge.h +2 -0
- package/package.json +10 -5
- package/plugin-api/package.json +4 -0
- package/components/creator2/ReplayPrivacy.ts +0 -23
- package/components/creator3/ReplayPrivacy.ts +0 -25
- package/dist/creator2/core/replay-async-transport.d.ts +0 -4
- package/dist/creator2/core/replay-async-transport.d.ts.map +0 -1
- package/dist/creator2/core/replay-async-transport.js +0 -67
- package/dist/creator2/core/replay-async-transport.js.map +0 -1
- package/dist/creator2/core/replay-budget.d.ts +0 -28
- package/dist/creator2/core/replay-budget.d.ts.map +0 -1
- package/dist/creator2/core/replay-budget.js +0 -113
- package/dist/creator2/core/replay-budget.js.map +0 -1
- package/dist/creator2/core/replay-diagnostics.d.ts +0 -36
- package/dist/creator2/core/replay-diagnostics.d.ts.map +0 -1
- package/dist/creator2/core/replay-diagnostics.js +0 -40
- package/dist/creator2/core/replay-diagnostics.js.map +0 -1
- package/dist/creator2/core/replay-encoding.d.ts +0 -31
- package/dist/creator2/core/replay-encoding.d.ts.map +0 -1
- package/dist/creator2/core/replay-encoding.js +0 -63
- package/dist/creator2/core/replay-encoding.js.map +0 -1
- package/dist/creator2/core/replay-file.d.ts +0 -5
- package/dist/creator2/core/replay-file.d.ts.map +0 -1
- package/dist/creator2/core/replay-file.js +0 -101
- package/dist/creator2/core/replay-file.js.map +0 -1
- package/dist/creator2/core/replay-pixels.d.ts +0 -3
- package/dist/creator2/core/replay-pixels.d.ts.map +0 -1
- package/dist/creator2/core/replay-pixels.js +0 -16
- package/dist/creator2/core/replay-pixels.js.map +0 -1
- package/dist/creator2/core/replay-privacy-nodes.d.ts +0 -14
- package/dist/creator2/core/replay-privacy-nodes.d.ts.map +0 -1
- package/dist/creator2/core/replay-privacy-nodes.js +0 -18
- package/dist/creator2/core/replay-privacy-nodes.js.map +0 -1
- package/dist/creator2/core/replay-privacy.d.ts +0 -15
- package/dist/creator2/core/replay-privacy.d.ts.map +0 -1
- package/dist/creator2/core/replay-privacy.js +0 -36
- package/dist/creator2/core/replay-privacy.js.map +0 -1
- package/dist/creator2/core/replay.d.ts +0 -99
- package/dist/creator2/core/replay.d.ts.map +0 -1
- package/dist/creator2/core/replay.js +0 -611
- package/dist/creator2/core/replay.js.map +0 -1
- package/dist/creator2/creator2/capture.d.ts +0 -17
- package/dist/creator2/creator2/capture.d.ts.map +0 -1
- package/dist/creator2/creator2/capture.js +0 -168
- package/dist/creator2/creator2/capture.js.map +0 -1
- package/dist/creator3/core/replay-async-transport.d.ts +0 -4
- package/dist/creator3/core/replay-async-transport.d.ts.map +0 -1
- package/dist/creator3/core/replay-async-transport.js +0 -16
- package/dist/creator3/core/replay-async-transport.js.map +0 -1
- package/dist/creator3/core/replay-budget.d.ts +0 -28
- package/dist/creator3/core/replay-budget.d.ts.map +0 -1
- package/dist/creator3/core/replay-budget.js +0 -105
- package/dist/creator3/core/replay-budget.js.map +0 -1
- package/dist/creator3/core/replay-diagnostics.d.ts +0 -36
- package/dist/creator3/core/replay-diagnostics.d.ts.map +0 -1
- package/dist/creator3/core/replay-diagnostics.js +0 -36
- package/dist/creator3/core/replay-diagnostics.js.map +0 -1
- package/dist/creator3/core/replay-encoding.d.ts +0 -31
- package/dist/creator3/core/replay-encoding.d.ts.map +0 -1
- package/dist/creator3/core/replay-encoding.js +0 -62
- package/dist/creator3/core/replay-encoding.js.map +0 -1
- package/dist/creator3/core/replay-file.d.ts +0 -5
- package/dist/creator3/core/replay-file.d.ts.map +0 -1
- package/dist/creator3/core/replay-file.js +0 -31
- package/dist/creator3/core/replay-file.js.map +0 -1
- package/dist/creator3/core/replay-pixels.d.ts +0 -3
- package/dist/creator3/core/replay-pixels.d.ts.map +0 -1
- package/dist/creator3/core/replay-pixels.js +0 -13
- package/dist/creator3/core/replay-pixels.js.map +0 -1
- package/dist/creator3/core/replay-privacy-nodes.d.ts +0 -14
- package/dist/creator3/core/replay-privacy-nodes.d.ts.map +0 -1
- package/dist/creator3/core/replay-privacy-nodes.js +0 -15
- package/dist/creator3/core/replay-privacy-nodes.js.map +0 -1
- package/dist/creator3/core/replay-privacy.d.ts +0 -15
- package/dist/creator3/core/replay-privacy.d.ts.map +0 -1
- package/dist/creator3/core/replay-privacy.js +0 -33
- package/dist/creator3/core/replay-privacy.js.map +0 -1
- package/dist/creator3/core/replay.d.ts +0 -99
- package/dist/creator3/core/replay.d.ts.map +0 -1
- package/dist/creator3/core/replay.js +0 -534
- package/dist/creator3/core/replay.js.map +0 -1
- package/dist/creator3/creator3/capture.d.ts +0 -17
- package/dist/creator3/creator3/capture.d.ts.map +0 -1
- package/dist/creator3/creator3/capture.js +0 -112
- package/dist/creator3/creator3/capture.js.map +0 -1
- package/dist/creator3/creator3/replay-render-cycle.d.ts +0 -9
- package/dist/creator3/creator3/replay-render-cycle.d.ts.map +0 -1
- package/dist/creator3/creator3/replay-render-cycle.js +0 -12
- package/dist/creator3/creator3/replay-render-cycle.js.map +0 -1
- package/native/android/src/main/java/com/ft/sdk/cocos/FTCocosReplayImageJobs.java +0 -55
- package/native/ios/FTCocosReplayImageJobs.h +0 -11
- package/native/ios/FTCocosReplayImageJobs.m +0 -54
- package/native/shared/FTReplayFileBridge.h +0 -62
- package/native/shared/FTReplayFileWorker.h +0 -88
|
@@ -0,0 +1,85 @@
|
|
|
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
|
+
import java.util.List;
|
|
12
|
+
|
|
13
|
+
/** Shared process routing. Optional modules are selected by the generated allowlist. */
|
|
14
|
+
public final class FTBridgeTransport {
|
|
15
|
+
private FTBridgeTransport() {}
|
|
16
|
+
public static String invoke(String endpoint, String method, String payload) {
|
|
17
|
+
String forwarded = forwardToMainProcess(endpoint, method, payload);
|
|
18
|
+
return forwarded != null ? forwarded : FTCocosBridgeModules.invokeLocal(endpoint, method, payload);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private static String forwardToMainProcess(String endpoint, String method, String payload) {
|
|
22
|
+
Activity activity = resolveActivity();
|
|
23
|
+
if (activity == null || !isSecondaryProcess(activity)) return null;
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
Bundle arguments = new Bundle();
|
|
27
|
+
arguments.putString("endpoint", endpoint);
|
|
28
|
+
arguments.putString(FTCocosBridgeProvider.ARGUMENT_PAYLOAD, payload);
|
|
29
|
+
Uri uri = Uri.parse("content://" + activity.getPackageName()
|
|
30
|
+
+ FTCocosBridgeProvider.AUTHORITY_SUFFIX);
|
|
31
|
+
Bundle result = activity.getContentResolver().call(
|
|
32
|
+
uri,
|
|
33
|
+
FTCocosBridgeProvider.METHOD_INVOKE,
|
|
34
|
+
method,
|
|
35
|
+
arguments);
|
|
36
|
+
return result == null
|
|
37
|
+
? null
|
|
38
|
+
: result.getString(FTCocosBridgeProvider.RESULT_RESPONSE);
|
|
39
|
+
} catch (IllegalArgumentException providerUnavailable) {
|
|
40
|
+
// Existing integrations may not have registered the optional bridge
|
|
41
|
+
// provider yet. Preserve their same-process behavior.
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private static boolean isSecondaryProcess(Context context) {
|
|
47
|
+
String currentProcess = currentProcessName(context);
|
|
48
|
+
String mainProcess = context.getApplicationInfo().processName;
|
|
49
|
+
return currentProcess != null && mainProcess != null && !mainProcess.equals(currentProcess);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private static String currentProcessName(Context context) {
|
|
53
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
54
|
+
return Application.getProcessName();
|
|
55
|
+
}
|
|
56
|
+
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
|
57
|
+
if (manager == null) return null;
|
|
58
|
+
List<ActivityManager.RunningAppProcessInfo> processes = manager.getRunningAppProcesses();
|
|
59
|
+
if (processes == null) return null;
|
|
60
|
+
int currentPid = Process.myPid();
|
|
61
|
+
for (ActivityManager.RunningAppProcessInfo process : processes) {
|
|
62
|
+
if (process.pid == currentPid) return process.processName;
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
public static Activity resolveActivity() {
|
|
68
|
+
String[] classNames = {"com.cocos.lib.CocosActivity", "org.cocos2dx.lib.Cocos2dxActivity"};
|
|
69
|
+
String[] methodNames = {"getContext", "getActivity"};
|
|
70
|
+
for (String className : classNames) {
|
|
71
|
+
for (String methodName : methodNames) {
|
|
72
|
+
try {
|
|
73
|
+
Object value = Class.forName(className).getMethod(methodName).invoke(null);
|
|
74
|
+
if (value instanceof Activity) return (Activity) value;
|
|
75
|
+
if (value instanceof Context && ((Context) value).getApplicationContext() instanceof Activity) {
|
|
76
|
+
return (Activity) ((Context) value).getApplicationContext();
|
|
77
|
+
}
|
|
78
|
+
} catch (Throwable ignored) {
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
}
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
package com.ft.sdk.cocos;
|
|
2
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
3
|
|
|
12
4
|
import com.ft.sdk.DetectFrequency;
|
|
13
5
|
import com.ft.sdk.FTLogger;
|
|
@@ -19,24 +11,15 @@ import com.ft.sdk.FTSdk;
|
|
|
19
11
|
import com.ft.sdk.FTTraceConfig;
|
|
20
12
|
import com.ft.sdk.FTTraceManager;
|
|
21
13
|
import com.ft.sdk.LogCacheDiscard;
|
|
22
|
-
import com.ft.sdk.SessionReplayManager;
|
|
23
14
|
import com.ft.sdk.TraceType;
|
|
24
15
|
import com.ft.sdk.garble.bean.AppState;
|
|
25
16
|
import com.ft.sdk.garble.bean.NetStatusBean;
|
|
26
17
|
import com.ft.sdk.garble.bean.ResourceParams;
|
|
27
18
|
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
19
|
|
|
32
20
|
import org.json.JSONArray;
|
|
33
21
|
import org.json.JSONObject;
|
|
34
22
|
|
|
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
23
|
import java.util.ArrayList;
|
|
41
24
|
import java.util.HashMap;
|
|
42
25
|
import java.util.Iterator;
|
|
@@ -52,9 +35,7 @@ public final class FTCocosBridge {
|
|
|
52
35
|
}
|
|
53
36
|
|
|
54
37
|
public static String invoke(String method, String payload) {
|
|
55
|
-
|
|
56
|
-
if (forwarded != null) return forwarded;
|
|
57
|
-
return invokeLocal(method, payload);
|
|
38
|
+
return FTBridgeTransport.invoke("sdk", method, payload);
|
|
58
39
|
}
|
|
59
40
|
|
|
60
41
|
static String invokeLocal(String method, String payload) {
|
|
@@ -77,63 +58,11 @@ public final class FTCocosBridge {
|
|
|
77
58
|
}
|
|
78
59
|
}
|
|
79
60
|
|
|
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
61
|
private static Object dispatch(String method, JSONObject payload) throws Exception {
|
|
126
62
|
switch (method) {
|
|
127
|
-
case "replay.beginSaveImage":
|
|
128
|
-
return FTCocosReplayImageJobs.begin(payload);
|
|
129
|
-
case "replay.pollSaveImage":
|
|
130
|
-
return FTCocosReplayImageJobs.poll(payload.getString("job"));
|
|
131
63
|
case "hybrid.attach":
|
|
132
64
|
attachHybrid(payload);
|
|
133
65
|
return null;
|
|
134
|
-
case "hybrid.setExternalRecorderActive":
|
|
135
|
-
setExternalRecorderActive(payload.getBoolean("active"));
|
|
136
|
-
return null;
|
|
137
66
|
case "sdk.configure":
|
|
138
67
|
configureSdk(payload);
|
|
139
68
|
return null;
|
|
@@ -196,25 +125,6 @@ public final class FTCocosBridge {
|
|
|
196
125
|
case "trace.getHeaders":
|
|
197
126
|
return FTTraceManager.get().getTraceHeader(
|
|
198
127
|
payload.optString("resourceKey", ""), payload.getString("url"));
|
|
199
|
-
case "replay.configure":
|
|
200
|
-
configureReplay(payload);
|
|
201
|
-
return null;
|
|
202
|
-
case "replay.getContext":
|
|
203
|
-
return SessionReplayManager.get().getCurrentFlutterRumContext();
|
|
204
|
-
case "replay.saveImage":
|
|
205
|
-
return saveReplayImage(payload);
|
|
206
|
-
case "replay.saveImageV2":
|
|
207
|
-
return saveReplayImageV2(payload);
|
|
208
|
-
case "replay.writeSegment":
|
|
209
|
-
SessionReplayManager.get().writeExternalSegment(
|
|
210
|
-
payload.getString("segment"), payload.getString("viewId"));
|
|
211
|
-
return null;
|
|
212
|
-
case "replay.setRecordCount":
|
|
213
|
-
SessionReplayManager.get().setExternalRecordCount(
|
|
214
|
-
payload.getString("viewId"), payload.getLong("count"));
|
|
215
|
-
return null;
|
|
216
|
-
case "replay.stop":
|
|
217
|
-
return null;
|
|
218
128
|
default:
|
|
219
129
|
throw new IllegalArgumentException("Unknown bridge method: " + method);
|
|
220
130
|
}
|
|
@@ -225,7 +135,6 @@ public final class FTCocosBridge {
|
|
|
225
135
|
if (hasText(json, "sdkVersion")) {
|
|
226
136
|
FTSdk.appendGlobalContext(COCOS_SDK_VERSION_KEY, json.getString("sdkVersion"));
|
|
227
137
|
}
|
|
228
|
-
if (json.optBoolean("requiresReplay", false)) hybridRecorderSwitchMethod();
|
|
229
138
|
}
|
|
230
139
|
|
|
231
140
|
private static void ensureNativeHostInitialized() {
|
|
@@ -235,28 +144,6 @@ public final class FTCocosBridge {
|
|
|
235
144
|
}
|
|
236
145
|
}
|
|
237
146
|
|
|
238
|
-
private static Method hybridRecorderSwitchMethod() {
|
|
239
|
-
try {
|
|
240
|
-
return SessionReplayManager.class.getMethod(
|
|
241
|
-
"setExternalRecorderActive", boolean.class);
|
|
242
|
-
} catch (NoSuchMethodException error) {
|
|
243
|
-
throw new IllegalStateException(
|
|
244
|
-
"Android Session Replay does not support Hybrid recorder switching; "
|
|
245
|
-
+ "upgrade the Native SDK");
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
private static void setExternalRecorderActive(boolean active) throws Exception {
|
|
250
|
-
ensureNativeHostInitialized();
|
|
251
|
-
try {
|
|
252
|
-
hybridRecorderSwitchMethod().invoke(SessionReplayManager.get(), active);
|
|
253
|
-
} catch (InvocationTargetException error) {
|
|
254
|
-
Throwable cause = error.getCause();
|
|
255
|
-
if (cause instanceof Exception) throw (Exception) cause;
|
|
256
|
-
throw error;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
147
|
private static void configureSdk(JSONObject json) throws Exception {
|
|
261
148
|
FTSDKConfig config;
|
|
262
149
|
if (hasText(json, "datakitUrl")) {
|
|
@@ -340,20 +227,6 @@ public final class FTCocosBridge {
|
|
|
340
227
|
FTSdk.initTraceWithConfig(config);
|
|
341
228
|
}
|
|
342
229
|
|
|
343
|
-
private static void configureReplay(JSONObject json) throws Exception {
|
|
344
|
-
FTSessionReplayConfig config = new FTSessionReplayConfig();
|
|
345
|
-
if (json.has("sampleRate")) config.setSampleRate((float) json.getDouble("sampleRate"));
|
|
346
|
-
if (json.has("sessionOnErrorSampleRate")) config.setSessionReplayOnErrorSampleRate((float) json.getDouble("sessionOnErrorSampleRate"));
|
|
347
|
-
if ("show".equals(json.optString("touchPrivacy"))) config.setTouchPrivacy(TouchPrivacy.SHOW);
|
|
348
|
-
config.setInternalCallback(new SessionReplayInternalCallback() {
|
|
349
|
-
@Override public Activity getCurrentActivity() { return resolveActivity(); }
|
|
350
|
-
});
|
|
351
|
-
Method externalMode = FTSessionReplayConfig.class.getDeclaredMethod("setExternalRecorderMode", boolean.class);
|
|
352
|
-
externalMode.setAccessible(true);
|
|
353
|
-
externalMode.invoke(config, true);
|
|
354
|
-
FTSdk.initSessionReplayConfig(config);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
230
|
private static void addResource(JSONObject payload) throws Exception {
|
|
358
231
|
JSONObject content = payload.getJSONObject("content");
|
|
359
232
|
ResourceParams params = new ResourceParams();
|
|
@@ -383,58 +256,6 @@ public final class FTCocosBridge {
|
|
|
383
256
|
FTRUMGlobalManager.get().addResource(payload.getString("key"), params, status);
|
|
384
257
|
}
|
|
385
258
|
|
|
386
|
-
private static String saveReplayImage(JSONObject json) throws Exception {
|
|
387
|
-
byte[] bytes = readFile(new File(json.getString("path")));
|
|
388
|
-
return SessionReplayManager.get().saveFlutterImageResource(bytes, json.getInt("width"), json.getInt("height"));
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
private static Object saveReplayImageV2(JSONObject json) throws Exception {
|
|
392
|
-
byte[] bytes = readFile(new File(json.getString("path")));
|
|
393
|
-
Method method;
|
|
394
|
-
try {
|
|
395
|
-
method = SessionReplayManager.class.getMethod(
|
|
396
|
-
"saveExternalImageResourceV2",
|
|
397
|
-
byte[].class,
|
|
398
|
-
int.class,
|
|
399
|
-
int.class,
|
|
400
|
-
float.class,
|
|
401
|
-
int.class);
|
|
402
|
-
} catch (NoSuchMethodException unavailable) {
|
|
403
|
-
throw new IllegalArgumentException("Unknown bridge method: replay.saveImageV2");
|
|
404
|
-
}
|
|
405
|
-
try {
|
|
406
|
-
return method.invoke(
|
|
407
|
-
SessionReplayManager.get(),
|
|
408
|
-
bytes,
|
|
409
|
-
json.getInt("width"),
|
|
410
|
-
json.getInt("height"),
|
|
411
|
-
(float) json.optDouble("quality", 0.45),
|
|
412
|
-
json.optInt("maxFrameBytes", 40 * 1024));
|
|
413
|
-
} catch (InvocationTargetException error) {
|
|
414
|
-
Throwable cause = error.getCause();
|
|
415
|
-
if (cause instanceof Exception) throw (Exception) cause;
|
|
416
|
-
throw error;
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
private static Activity resolveActivity() {
|
|
421
|
-
String[] classNames = {"com.cocos.lib.CocosActivity", "org.cocos2dx.lib.Cocos2dxActivity"};
|
|
422
|
-
String[] methodNames = {"getContext", "getActivity"};
|
|
423
|
-
for (String className : classNames) {
|
|
424
|
-
for (String methodName : methodNames) {
|
|
425
|
-
try {
|
|
426
|
-
Object value = Class.forName(className).getMethod(methodName).invoke(null);
|
|
427
|
-
if (value instanceof Activity) return (Activity) value;
|
|
428
|
-
if (value instanceof Context && ((Context) value).getApplicationContext() instanceof Activity) {
|
|
429
|
-
return (Activity) ((Context) value).getApplicationContext();
|
|
430
|
-
}
|
|
431
|
-
} catch (Throwable ignored) {
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
return null;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
259
|
private static TraceType traceType(String value) {
|
|
439
260
|
if ("zipkinMultiHeader".equals(value)) return TraceType.ZIPKIN_MULTI_HEADER;
|
|
440
261
|
if ("zipkinSingleHeader".equals(value)) return TraceType.ZIPKIN_SINGLE_HEADER;
|
|
@@ -521,16 +342,5 @@ public final class FTCocosBridge {
|
|
|
521
342
|
return json.has(name) ? json.optLong(name, -1) : -1;
|
|
522
343
|
}
|
|
523
344
|
|
|
524
|
-
|
|
525
|
-
FileInputStream input = new FileInputStream(file);
|
|
526
|
-
try {
|
|
527
|
-
ByteArrayOutputStream output = new ByteArrayOutputStream((int) Math.min(file.length(), Integer.MAX_VALUE));
|
|
528
|
-
byte[] buffer = new byte[16 * 1024];
|
|
529
|
-
int read;
|
|
530
|
-
while ((read = input.read(buffer)) >= 0) output.write(buffer, 0, read);
|
|
531
|
-
return output.toByteArray();
|
|
532
|
-
} finally {
|
|
533
|
-
input.close();
|
|
534
|
-
}
|
|
535
|
-
}
|
|
345
|
+
|
|
536
346
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
package com.ft.sdk.cocos;
|
|
2
|
+
|
|
3
|
+
/** SDK-managed endpoint allowlist, regenerated when optional modules change. */
|
|
4
|
+
final class FTCocosBridgeModules {
|
|
5
|
+
static String invokeLocal(String endpoint, String method, String payload) {
|
|
6
|
+
if ("sdk".equals(endpoint)) return FTCocosBridge.invokeLocal(method, payload);
|
|
7
|
+
return "{\"ok\":false,\"error\":\"Native SDK extension is not installed; rerun the integration installer\"}";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -32,7 +32,7 @@ public final class FTCocosBridgeProvider extends ContentProvider {
|
|
|
32
32
|
}
|
|
33
33
|
String payload = extras == null ? null : extras.getString(ARGUMENT_PAYLOAD);
|
|
34
34
|
Bundle result = new Bundle();
|
|
35
|
-
result.putString(RESULT_RESPONSE,
|
|
35
|
+
result.putString(RESULT_RESPONSE, FTCocosBridgeModules.invokeLocal(extras == null ? "sdk" : extras.getString("endpoint", "sdk"), argument, payload));
|
|
36
36
|
return result;
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
#import "FTCocosBridge.h"
|
|
2
|
-
#import "FTCocosReplayImageJobs.h"
|
|
3
2
|
|
|
4
|
-
#import "
|
|
5
|
-
#import "
|
|
3
|
+
#import "FTMobileAgent.h"
|
|
4
|
+
#import "FTSDKConfig.h"
|
|
5
|
+
#import "FTRumConfig.h"
|
|
6
|
+
#import "FTLoggerConfig.h"
|
|
7
|
+
#import "FTMobileConfig.h"
|
|
8
|
+
#import "FTExternalDataManager.h"
|
|
9
|
+
#import "FTResourceMetricsModel.h"
|
|
10
|
+
#import "FTResourceContentModel.h"
|
|
6
11
|
#import <UIKit/UIKit.h>
|
|
7
12
|
#import <objc/message.h>
|
|
8
13
|
|
|
@@ -25,10 +30,6 @@
|
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
+ (id)dispatch:(NSString *)method arguments:(NSDictionary *)arguments {
|
|
28
|
-
if ([method isEqualToString:@"replay.beginSaveImage"]) return [self beginSaveImage:arguments];
|
|
29
|
-
if ([method isEqualToString:@"replay.pollSaveImage"]) {
|
|
30
|
-
return [FTCocosReplayImageJobs poll:[self requiredText:arguments key:@"job"]];
|
|
31
|
-
}
|
|
32
33
|
FTExternalDataManager *rum = [FTExternalDataManager sharedManager];
|
|
33
34
|
if ([method isEqualToString:@"hybrid.attach"]) {
|
|
34
35
|
[self ensureNativeHostInitialized];
|
|
@@ -36,12 +37,6 @@
|
|
|
36
37
|
if (sdkVersion.length > 0) {
|
|
37
38
|
[FTMobileAgent appendGlobalContext:@{ @"sdk_package_cocos": sdkVersion }];
|
|
38
39
|
}
|
|
39
|
-
if ([arguments[@"requiresReplay"] boolValue]) [self ensureHybridRecorderSwitch];
|
|
40
|
-
return nil;
|
|
41
|
-
}
|
|
42
|
-
if ([method isEqualToString:@"hybrid.setExternalRecorderActive"]) {
|
|
43
|
-
[self ensureNativeHostInitialized];
|
|
44
|
-
[self setExternalRecorderActive:[arguments[@"active"] boolValue]];
|
|
45
40
|
return nil;
|
|
46
41
|
}
|
|
47
42
|
if ([method isEqualToString:@"sdk.configure"]) {
|
|
@@ -180,97 +175,10 @@
|
|
|
180
175
|
NSString *key = [self text:arguments[@"resourceKey"]];
|
|
181
176
|
return key.length > 0 ? [rum getTraceHeaderWithKey:key url:url] : [rum getTraceHeaderWithUrl:url];
|
|
182
177
|
}
|
|
183
|
-
if ([method isEqualToString:@"replay.configure"]) {
|
|
184
|
-
FTSessionReplayConfig *config = [[FTSessionReplayConfig alloc] init];
|
|
185
|
-
[self ensureExternalReplayAPIForConfig:config];
|
|
186
|
-
if (arguments[@"sampleRate"]) config.sampleRate = [self percent:arguments[@"sampleRate"]];
|
|
187
|
-
if (arguments[@"sessionOnErrorSampleRate"]) config.sessionReplayOnErrorSampleRate = [self percent:arguments[@"sessionOnErrorSampleRate"]];
|
|
188
|
-
if ([[self text:arguments[@"touchPrivacy"]] isEqualToString:@"show"]) config.touchPrivacy = FTTouchPrivacyLevelShow;
|
|
189
|
-
SEL externalMode = NSSelectorFromString(@"setExternalRecorderMode:");
|
|
190
|
-
((void (*)(id, SEL, BOOL))objc_msgSend)(config, externalMode, YES);
|
|
191
|
-
[[FTRumSessionReplay sharedInstance] startWithSessionReplayConfig:config];
|
|
192
|
-
return nil;
|
|
193
|
-
}
|
|
194
|
-
if ([method isEqualToString:@"replay.getContext"]) {
|
|
195
|
-
return [self invokeReplayObjectSelector:NSSelectorFromString(@"currentExternalRUMContext")];
|
|
196
|
-
}
|
|
197
|
-
if ([method isEqualToString:@"replay.saveImage"]) {
|
|
198
|
-
NSData *rgba = [NSData dataWithContentsOfFile:[self requiredText:arguments key:@"path"]];
|
|
199
|
-
if (!rgba) [self fail:@"Unable to read replay RGBA file"];
|
|
200
|
-
NSData *png = [self pngDataFromRGBA:rgba
|
|
201
|
-
width:[arguments[@"width"] unsignedIntegerValue]
|
|
202
|
-
height:[arguments[@"height"] unsignedIntegerValue]];
|
|
203
|
-
if (!png) [self fail:@"Unable to encode replay image"];
|
|
204
|
-
return [self invokeReplayObjectSelector:NSSelectorFromString(@"saveExternalImageResourceData:mimeType:")
|
|
205
|
-
first:png
|
|
206
|
-
second:@"image/png"];
|
|
207
|
-
}
|
|
208
|
-
if ([method isEqualToString:@"replay.saveImageV2"]) {
|
|
209
|
-
NSUInteger width = [arguments[@"width"] unsignedIntegerValue];
|
|
210
|
-
NSUInteger height = [arguments[@"height"] unsignedIntegerValue];
|
|
211
|
-
NSData *rgba = [NSData dataWithContentsOfFile:[self requiredText:arguments key:@"path"]];
|
|
212
|
-
if (!rgba) [self fail:@"Unable to read replay RGBA file"];
|
|
213
|
-
UIImage *image = [self imageFromRGBA:rgba width:width height:height];
|
|
214
|
-
if (!image) [self fail:@"Unable to decode replay RGBA image"];
|
|
215
|
-
|
|
216
|
-
CGFloat quality = MIN(1.0, MAX(0.1, [arguments[@"quality"] doubleValue] ?: 0.45));
|
|
217
|
-
NSUInteger maxFrameBytes = [arguments[@"maxFrameBytes"] unsignedIntegerValue] ?: 40 * 1024;
|
|
218
|
-
UIImage *encodedImage = [self opaqueImageFromImage:image size:CGSizeMake(width, height)];
|
|
219
|
-
NSData *jpeg = UIImageJPEGRepresentation(encodedImage, quality);
|
|
220
|
-
if (jpeg.length > maxFrameBytes) {
|
|
221
|
-
quality = MAX(0.2, quality * 0.7);
|
|
222
|
-
jpeg = UIImageJPEGRepresentation(encodedImage, quality);
|
|
223
|
-
}
|
|
224
|
-
if (jpeg.length > maxFrameBytes && width > 1 && height > 1) {
|
|
225
|
-
width = MAX(1, (NSUInteger)floor(width * 0.75));
|
|
226
|
-
height = MAX(1, (NSUInteger)floor(height * 0.75));
|
|
227
|
-
encodedImage = [self opaqueImageFromImage:image size:CGSizeMake(width, height)];
|
|
228
|
-
jpeg = UIImageJPEGRepresentation(encodedImage, quality);
|
|
229
|
-
}
|
|
230
|
-
if (!jpeg || jpeg.length > maxFrameBytes) return @{ @"accepted": @NO };
|
|
231
|
-
|
|
232
|
-
NSString *resourceID = [self invokeReplayObjectSelector:NSSelectorFromString(@"saveExternalImageResourceData:mimeType:")
|
|
233
|
-
first:jpeg
|
|
234
|
-
second:@"image/jpeg"];
|
|
235
|
-
if (resourceID.length == 0) [self fail:@"Unable to save replay image resource"];
|
|
236
|
-
return @{
|
|
237
|
-
@"accepted": @YES,
|
|
238
|
-
@"resourceId": resourceID,
|
|
239
|
-
@"byteSize": @(jpeg.length),
|
|
240
|
-
@"width": @(width),
|
|
241
|
-
@"height": @(height),
|
|
242
|
-
@"mimeType": @"image/jpeg",
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
if ([method isEqualToString:@"replay.writeSegment"]) {
|
|
246
|
-
[self invokeReplayVoidSelector:NSSelectorFromString(@"writeExternalSegment:viewID:")
|
|
247
|
-
first:[self requiredText:arguments key:@"segment"]
|
|
248
|
-
second:[self requiredText:arguments key:@"viewId"]];
|
|
249
|
-
return nil;
|
|
250
|
-
}
|
|
251
|
-
if ([method isEqualToString:@"replay.setRecordCount"]) {
|
|
252
|
-
[self invokeReplayCountSelector:NSSelectorFromString(@"setExternalRecordCountForViewID:count:")
|
|
253
|
-
viewID:[self requiredText:arguments key:@"viewId"]
|
|
254
|
-
count:[arguments[@"count"] unsignedIntegerValue]];
|
|
255
|
-
return nil;
|
|
256
|
-
}
|
|
257
|
-
if ([method isEqualToString:@"replay.stop"]) return nil;
|
|
258
178
|
[self fail:[NSString stringWithFormat:@"Unknown bridge method: %@", method]];
|
|
259
179
|
return nil;
|
|
260
180
|
}
|
|
261
181
|
|
|
262
|
-
+ (NSString *)beginSaveImage:(NSDictionary *)arguments {
|
|
263
|
-
NSString *method = [self requiredText:arguments key:@"method"];
|
|
264
|
-
if (![method isEqualToString:@"replay.saveImage"] && ![method isEqualToString:@"replay.saveImageV2"]) {
|
|
265
|
-
[self fail:@"Unsupported asynchronous Replay method"];
|
|
266
|
-
}
|
|
267
|
-
NSDictionary *payload = [[self dictionary:arguments[@"arguments"]] copy];
|
|
268
|
-
if (!payload) [self fail:@"Replay image arguments are required"];
|
|
269
|
-
return [FTCocosReplayImageJobs beginWithWork:^NSString *{
|
|
270
|
-
return [self responseWithValue:[self dispatch:method arguments:payload] error:nil];
|
|
271
|
-
}];
|
|
272
|
-
}
|
|
273
|
-
|
|
274
182
|
+ (void)ensureNativeHostInitialized {
|
|
275
183
|
@try {
|
|
276
184
|
if (![FTMobileAgent sharedInstance]) {
|
|
@@ -281,19 +189,6 @@
|
|
|
281
189
|
}
|
|
282
190
|
}
|
|
283
191
|
|
|
284
|
-
+ (void)ensureHybridRecorderSwitch {
|
|
285
|
-
SEL selector = NSSelectorFromString(@"setExternalRecorderActive:");
|
|
286
|
-
if (![[FTRumSessionReplay sharedInstance] respondsToSelector:selector]) {
|
|
287
|
-
[self fail:@"iOS Session Replay does not support Hybrid recorder switching; upgrade the Native SDK"];
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
+ (void)setExternalRecorderActive:(BOOL)active {
|
|
292
|
-
[self ensureHybridRecorderSwitch];
|
|
293
|
-
SEL selector = NSSelectorFromString(@"setExternalRecorderActive:");
|
|
294
|
-
((void (*)(id, SEL, BOOL))objc_msgSend)([FTRumSessionReplay sharedInstance], selector, active);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
192
|
+ (void)addResource:(NSDictionary *)arguments manager:(FTExternalDataManager *)manager {
|
|
298
193
|
NSDictionary *input = [self dictionary:arguments[@"content"]] ?: @{};
|
|
299
194
|
NSURL *url = [NSURL URLWithString:[self text:input[@"url"]] ?: @""];
|
|
@@ -323,96 +218,6 @@
|
|
|
323
218
|
[manager addResourceWithKey:[self requiredText:arguments key:@"key"] metrics:metrics content:content];
|
|
324
219
|
}
|
|
325
220
|
|
|
326
|
-
+ (id)invokeReplayObjectSelector:(SEL)selector {
|
|
327
|
-
id target = [FTRumSessionReplay sharedInstance];
|
|
328
|
-
if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
|
|
329
|
-
return ((id (*)(id, SEL))objc_msgSend)(target, selector);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
+ (id)invokeReplayObjectSelector:(SEL)selector object:(id)object {
|
|
333
|
-
id target = [FTRumSessionReplay sharedInstance];
|
|
334
|
-
if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
|
|
335
|
-
return ((id (*)(id, SEL, id))objc_msgSend)(target, selector, object);
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
+ (id)invokeReplayObjectSelector:(SEL)selector first:(id)first second:(id)second {
|
|
339
|
-
id target = [FTRumSessionReplay sharedInstance];
|
|
340
|
-
if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
|
|
341
|
-
return ((id (*)(id, SEL, id, id))objc_msgSend)(target, selector, first, second);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
+ (void)invokeReplayVoidSelector:(SEL)selector first:(id)first second:(id)second {
|
|
345
|
-
id target = [FTRumSessionReplay sharedInstance];
|
|
346
|
-
if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
|
|
347
|
-
((void (*)(id, SEL, id, id))objc_msgSend)(target, selector, first, second);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
+ (void)invokeReplayCountSelector:(SEL)selector viewID:(NSString *)viewID count:(NSUInteger)count {
|
|
351
|
-
id target = [FTRumSessionReplay sharedInstance];
|
|
352
|
-
if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
|
|
353
|
-
((void (*)(id, SEL, id, NSUInteger))objc_msgSend)(target, selector, viewID, count);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
+ (void)missingReplayAPI:(SEL)selector {
|
|
357
|
-
[self fail:[NSString stringWithFormat:@"FTMobileSDK lacks external Session Replay API %@; use the Cocos-compatible iOS SDK release", NSStringFromSelector(selector)]];
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
+ (void)ensureExternalReplayAPIForConfig:(FTSessionReplayConfig *)config {
|
|
361
|
-
SEL externalMode = NSSelectorFromString(@"setExternalRecorderMode:");
|
|
362
|
-
if (![config respondsToSelector:externalMode]) [self missingReplayAPI:externalMode];
|
|
363
|
-
|
|
364
|
-
id target = [FTRumSessionReplay sharedInstance];
|
|
365
|
-
NSArray<NSString *> *selectorNames = @[
|
|
366
|
-
@"currentExternalRUMContext",
|
|
367
|
-
@"saveExternalImageResourceData:mimeType:",
|
|
368
|
-
@"writeExternalSegment:viewID:",
|
|
369
|
-
@"setExternalRecordCountForViewID:count:",
|
|
370
|
-
];
|
|
371
|
-
for (NSString *name in selectorNames) {
|
|
372
|
-
SEL selector = NSSelectorFromString(name);
|
|
373
|
-
if (![target respondsToSelector:selector]) [self missingReplayAPI:selector];
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
+ (NSData *)pngDataFromRGBA:(NSData *)rgba width:(NSUInteger)width height:(NSUInteger)height {
|
|
378
|
-
UIImage *image = [self imageFromRGBA:rgba width:width height:height];
|
|
379
|
-
return image ? UIImagePNGRepresentation(image) : nil;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
+ (UIImage *)imageFromRGBA:(NSData *)rgba width:(NSUInteger)width height:(NSUInteger)height {
|
|
383
|
-
if (width == 0 || height == 0 || width > NSUIntegerMax / height / 4 || rgba.length < width * height * 4) {
|
|
384
|
-
return nil;
|
|
385
|
-
}
|
|
386
|
-
NSMutableData *pixels = [rgba mutableCopy];
|
|
387
|
-
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
388
|
-
CGContextRef context = CGBitmapContextCreate(pixels.mutableBytes,
|
|
389
|
-
width,
|
|
390
|
-
height,
|
|
391
|
-
8,
|
|
392
|
-
width * 4,
|
|
393
|
-
colorSpace,
|
|
394
|
-
(CGBitmapInfo)kCGBitmapByteOrderDefault |
|
|
395
|
-
(CGBitmapInfo)kCGImageAlphaPremultipliedLast);
|
|
396
|
-
CGColorSpaceRelease(colorSpace);
|
|
397
|
-
if (!context) return nil;
|
|
398
|
-
CGImageRef imageRef = CGBitmapContextCreateImage(context);
|
|
399
|
-
CGContextRelease(context);
|
|
400
|
-
if (!imageRef) return nil;
|
|
401
|
-
UIImage *image = [UIImage imageWithCGImage:imageRef];
|
|
402
|
-
CGImageRelease(imageRef);
|
|
403
|
-
return image;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
+ (UIImage *)opaqueImageFromImage:(UIImage *)image size:(CGSize)size {
|
|
407
|
-
UIGraphicsBeginImageContextWithOptions(size, YES, 1.0);
|
|
408
|
-
[[UIColor whiteColor] setFill];
|
|
409
|
-
UIRectFill(CGRectMake(0, 0, size.width, size.height));
|
|
410
|
-
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
|
|
411
|
-
UIImage *opaque = UIGraphicsGetImageFromCurrentImageContext();
|
|
412
|
-
UIGraphicsEndImageContext();
|
|
413
|
-
return opaque;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
221
|
+ (NSString *)responseWithValue:(id)value error:(NSString *)error {
|
|
417
222
|
NSMutableDictionary *response = [@{ @"ok": @(error == nil) } mutableCopy];
|
|
418
223
|
if (value) response[@"value"] = value;
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
Pod::Spec.new do |s|
|
|
2
2
|
s.name = 'FTCocosBridge'
|
|
3
|
-
s.version = '0.1.0-alpha.
|
|
3
|
+
s.version = '0.1.0-alpha.7'
|
|
4
4
|
s.summary = 'TrueWatch Mobile SDK native bridge for Cocos Creator.'
|
|
5
5
|
s.homepage = 'https://github.com/TrueWatchTech/datakit-cocos'
|
|
6
6
|
s.license = { :type => 'Apache-2.0' }
|
|
7
7
|
s.author = { 'TrueWatch' => 'support@truewatch.com' }
|
|
8
8
|
s.source = { :path => '.' }
|
|
9
9
|
s.platform = :ios, '12.0'
|
|
10
|
-
s.source_files = 'FTCocosBridge.{h,m}'
|
|
10
|
+
s.source_files = 'FTCocosBridge.{h,m}'
|
|
11
11
|
s.public_header_files = 'FTCocosBridge.h'
|
|
12
12
|
s.requires_arc = true
|
|
13
13
|
# Cocos resolves FTCocosBridge by class name at runtime, so no direct symbol
|
|
14
14
|
# reference would otherwise keep the Objective-C category/class in the app.
|
|
15
15
|
s.user_target_xcconfig = { 'OTHER_LDFLAGS' => '$(inherited) -ObjC' }
|
|
16
|
-
s.dependency 'TrueWatchSDK/Agent', '1.6.8-alpha.
|
|
17
|
-
s.dependency 'TrueWatchSDK/FTSessionReplay', '1.6.8-alpha.2'
|
|
16
|
+
s.dependency 'TrueWatchSDK/Agent', '1.6.8-alpha.5'
|
|
18
17
|
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// swift-tools-version:5.3
|
|
2
|
+
import PackageDescription
|
|
3
|
+
|
|
4
|
+
let package = Package(
|
|
5
|
+
name: "FTCocosBridge",
|
|
6
|
+
platforms: [.iOS(.v12)],
|
|
7
|
+
products: [.library(name: "FTCocosBridge", targets: ["FTCocosBridge"])],
|
|
8
|
+
dependencies: [
|
|
9
|
+
.package(url: "https://github.com/TrueWatchTech/datakit-ios.git", .exact("1.6.8-alpha.5")),
|
|
10
|
+
],
|
|
11
|
+
targets: [
|
|
12
|
+
.target(
|
|
13
|
+
name: "FTCocosBridge",
|
|
14
|
+
dependencies: [
|
|
15
|
+
.product(name: "TrueWatchSDK", package: "datakit-ios"),
|
|
16
|
+
],
|
|
17
|
+
path: ".",
|
|
18
|
+
exclude: ["FTCocosBridge.podspec"],
|
|
19
|
+
sources: ["FTCocosBridge.m"],
|
|
20
|
+
publicHeadersPath: "include",
|
|
21
|
+
cSettings: [.headerSearchPath(".")],
|
|
22
|
+
linkerSettings: [.linkedFramework("UIKit")]
|
|
23
|
+
),
|
|
24
|
+
]
|
|
25
|
+
)
|