@luciq/react-native 18.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -0
- package/FONTS_SETUP_GUIDE.md +521 -0
- package/Gemfile +1 -0
- package/Gemfile.lock +11 -0
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/RNLuciq.podspec +21 -0
- package/android/build.gradle +88 -0
- package/android/gradle.properties +4 -0
- package/android/jacoco.gradle +52 -0
- package/android/native.gradle +7 -0
- package/android/proguard-rules.txt +1 -0
- package/android/sourcemaps.gradle +255 -0
- package/android/src/main/AndroidManifest.xml +4 -0
- package/android/src/main/java/ai/luciq/reactlibrary/ArgsRegistry.java +278 -0
- package/android/src/main/java/ai/luciq/reactlibrary/Constants.java +20 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciq.java +328 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqAPMModule.java +392 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqBugReportingModule.java +444 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqCrashReportingModule.java +169 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqFeatureRequestsModule.java +98 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqNetworkLoggerModule.java +195 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqReactnativeModule.java +1611 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqReactnativePackage.java +41 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqRepliesModule.java +298 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqSessionReplayModule.java +213 -0
- package/android/src/main/java/ai/luciq/reactlibrary/RNLuciqSurveysModule.java +237 -0
- package/android/src/main/java/ai/luciq/reactlibrary/utils/ArrayUtil.java +167 -0
- package/android/src/main/java/ai/luciq/reactlibrary/utils/EventEmitterModule.java +35 -0
- package/android/src/main/java/ai/luciq/reactlibrary/utils/LuciqUtil.java +58 -0
- package/android/src/main/java/ai/luciq/reactlibrary/utils/MainThreadHandler.java +13 -0
- package/android/src/main/java/ai/luciq/reactlibrary/utils/MapUtil.java +171 -0
- package/android/src/main/java/ai/luciq/reactlibrary/utils/RNTouchedViewExtractor.java +167 -0
- package/android/src/main/java/ai/luciq/reactlibrary/utils/ReportUtil.java +67 -0
- package/app.plugin.js +1 -0
- package/babel.config.js +3 -0
- package/bin/commands/MigrateCommand.d.ts +6 -0
- package/bin/commands/UploadEasUpdatesSourcemaps.d.ts +2 -0
- package/bin/commands/UploadSoFiles.d.ts +6 -0
- package/bin/commands/UploadSourcemaps.d.ts +2 -0
- package/bin/config/migration-config.json +125 -0
- package/bin/index.d.ts +2 -0
- package/bin/index.js +19179 -0
- package/bin/upload/index.d.ts +4 -0
- package/bin/upload/migrate.d.ts +14 -0
- package/bin/upload/uploadEasUpdatesSourcemaps.d.ts +21 -0
- package/bin/upload/uploadSoFiles.d.ts +21 -0
- package/bin/upload/uploadSourcemaps.d.ts +21 -0
- package/cli/commands/MigrateCommand.ts +32 -0
- package/cli/commands/UploadEasUpdatesSourcemaps.ts +34 -0
- package/cli/commands/UploadSoFiles.ts +38 -0
- package/cli/commands/UploadSourcemaps.ts +40 -0
- package/cli/config/migration-config.json +125 -0
- package/cli/index.ts +21 -0
- package/cli/upload/index.ts +4 -0
- package/cli/upload/migrate.ts +271 -0
- package/cli/upload/uploadEasUpdatesSourcemaps.ts +74 -0
- package/cli/upload/uploadSoFiles.ts +112 -0
- package/cli/upload/uploadSourcemaps.ts +73 -0
- package/dangerfile.ts +44 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +14 -0
- package/dist/models/FeatureFlag.d.ts +11 -0
- package/dist/models/FeatureFlag.js +1 -0
- package/dist/models/LuciqConfig.d.ts +42 -0
- package/dist/models/LuciqConfig.js +1 -0
- package/dist/models/NonFatalOptions.d.ts +15 -0
- package/dist/models/NonFatalOptions.js +1 -0
- package/dist/models/OverAirUpdate.d.ts +12 -0
- package/dist/models/OverAirUpdate.js +1 -0
- package/dist/models/Report.d.ts +70 -0
- package/dist/models/Report.js +109 -0
- package/dist/models/ReproConfig.d.ts +27 -0
- package/dist/models/ReproConfig.js +1 -0
- package/dist/models/SessionMetadata.d.ts +55 -0
- package/dist/models/SessionMetadata.js +1 -0
- package/dist/models/ThemeConfig.d.ts +27 -0
- package/dist/models/ThemeConfig.js +1 -0
- package/dist/models/W3cExternalTraceAttributes.d.ts +22 -0
- package/dist/models/W3cExternalTraceAttributes.js +1 -0
- package/dist/modules/APM.d.ts +77 -0
- package/dist/modules/APM.js +104 -0
- package/dist/modules/BugReporting.d.ts +138 -0
- package/dist/modules/BugReporting.js +202 -0
- package/dist/modules/CrashReporting.d.ts +19 -0
- package/dist/modules/CrashReporting.js +40 -0
- package/dist/modules/FeatureRequests.d.ts +20 -0
- package/dist/modules/FeatureRequests.js +28 -0
- package/dist/modules/Luciq.d.ts +362 -0
- package/dist/modules/Luciq.js +797 -0
- package/dist/modules/NetworkLogger.d.ts +52 -0
- package/dist/modules/NetworkLogger.js +208 -0
- package/dist/modules/Replies.d.ts +78 -0
- package/dist/modules/Replies.js +121 -0
- package/dist/modules/SessionReplay.d.ts +78 -0
- package/dist/modules/SessionReplay.js +98 -0
- package/dist/modules/Surveys.d.ts +75 -0
- package/dist/modules/Surveys.js +101 -0
- package/dist/native/NativeAPM.d.ts +18 -0
- package/dist/native/NativeAPM.js +4 -0
- package/dist/native/NativeBugReporting.d.ts +32 -0
- package/dist/native/NativeBugReporting.js +10 -0
- package/dist/native/NativeConstants.d.ts +182 -0
- package/dist/native/NativeConstants.js +1 -0
- package/dist/native/NativeCrashReporting.d.ts +18 -0
- package/dist/native/NativeCrashReporting.js +2 -0
- package/dist/native/NativeFeatureRequests.d.ts +8 -0
- package/dist/native/NativeFeatureRequests.js +2 -0
- package/dist/native/NativeLuciq.d.ts +86 -0
- package/dist/native/NativeLuciq.js +10 -0
- package/dist/native/NativeNetworkLogger.d.ts +21 -0
- package/dist/native/NativeNetworkLogger.js +14 -0
- package/dist/native/NativePackage.d.ts +21 -0
- package/dist/native/NativePackage.js +2 -0
- package/dist/native/NativeReplies.d.ts +21 -0
- package/dist/native/NativeReplies.js +8 -0
- package/dist/native/NativeSessionReplay.d.ts +16 -0
- package/dist/native/NativeSessionReplay.js +8 -0
- package/dist/native/NativeSurveys.d.ts +22 -0
- package/dist/native/NativeSurveys.js +9 -0
- package/dist/utils/AppStatesHandler.d.ts +3 -0
- package/dist/utils/AppStatesHandler.js +16 -0
- package/dist/utils/Enums.d.ts +244 -0
- package/dist/utils/Enums.js +266 -0
- package/dist/utils/FeatureFlags.d.ts +7 -0
- package/dist/utils/FeatureFlags.js +24 -0
- package/dist/utils/LuciqConstants.d.ts +14 -0
- package/dist/utils/LuciqConstants.js +15 -0
- package/dist/utils/LuciqUtils.d.ts +97 -0
- package/dist/utils/LuciqUtils.js +301 -0
- package/dist/utils/UnhandledRejectionTracking.d.ts +9 -0
- package/dist/utils/UnhandledRejectionTracking.js +99 -0
- package/dist/utils/XhrNetworkInterceptor.d.ts +39 -0
- package/dist/utils/XhrNetworkInterceptor.js +253 -0
- package/dist/utils/config.d.ts +5 -0
- package/dist/utils/config.js +6 -0
- package/dist/utils/logger.d.ts +10 -0
- package/dist/utils/logger.js +39 -0
- package/expo.d.ts +1 -0
- package/expo.js +1 -0
- package/ios/RNLuciq/ArgsRegistry.h +32 -0
- package/ios/RNLuciq/ArgsRegistry.m +276 -0
- package/ios/RNLuciq/LuciqAPMBridge.h +26 -0
- package/ios/RNLuciq/LuciqAPMBridge.m +99 -0
- package/ios/RNLuciq/LuciqBugReportingBridge.h +60 -0
- package/ios/RNLuciq/LuciqBugReportingBridge.m +241 -0
- package/ios/RNLuciq/LuciqCrashReportingBridge.h +18 -0
- package/ios/RNLuciq/LuciqCrashReportingBridge.m +68 -0
- package/ios/RNLuciq/LuciqFeatureRequestsBridge.h +30 -0
- package/ios/RNLuciq/LuciqFeatureRequestsBridge.m +61 -0
- package/ios/RNLuciq/LuciqNetworkLoggerBridge.h +44 -0
- package/ios/RNLuciq/LuciqNetworkLoggerBridge.m +206 -0
- package/ios/RNLuciq/LuciqReactBridge.h +151 -0
- package/ios/RNLuciq/LuciqReactBridge.m +548 -0
- package/ios/RNLuciq/LuciqRepliesBridge.h +40 -0
- package/ios/RNLuciq/LuciqRepliesBridge.m +80 -0
- package/ios/RNLuciq/LuciqSessionReplayBridge.h +32 -0
- package/ios/RNLuciq/LuciqSessionReplayBridge.m +107 -0
- package/ios/RNLuciq/LuciqSurveysBridge.h +46 -0
- package/ios/RNLuciq/LuciqSurveysBridge.m +107 -0
- package/ios/RNLuciq/RCTConvert+LuciqEnums.h +18 -0
- package/ios/RNLuciq/RCTConvert+LuciqEnums.m +127 -0
- package/ios/RNLuciq/RNLuciq.h +35 -0
- package/ios/RNLuciq/RNLuciq.m +107 -0
- package/ios/RNLuciq/Util/LCQAPM+PrivateAPIs.h +15 -0
- package/ios/RNLuciq/Util/LCQCrashReporting+CP.h +13 -0
- package/ios/RNLuciq/Util/LCQNetworkLogger+CP.h +68 -0
- package/ios/RNLuciq/Util/Luciq+CP.h +12 -0
- package/ios/RNLuciq.xcodeproj/project.pbxproj +352 -0
- package/ios/native.rb +12 -0
- package/ios/sourcemaps.sh +120 -0
- package/migrate.js +569 -0
- package/package.json +92 -0
- package/plugin/build/index.js +42078 -0
- package/plugin/src/index.ts +5 -0
- package/plugin/src/pluginProps.ts +6 -0
- package/plugin/src/withLuciq.ts +51 -0
- package/plugin/src/withLuciqAndroid.ts +99 -0
- package/plugin/src/withLuciqIOS.ts +109 -0
- package/plugin/tsconfig.json +7 -0
- package/react-native.config.js +16 -0
- package/scripts/customize-ios-endpoints.sh +28 -0
- package/scripts/dream-11-delete-unused-features.sh +62 -0
- package/scripts/find-token.js +58 -0
- package/scripts/find-token.sh +70 -0
- package/scripts/notify-github.sh +15 -0
- package/scripts/replace.js +58 -0
- package/scripts/snapshot-comment.md +15 -0
- package/scripts/snapshot-version.sh +11 -0
- package/src/index.ts +40 -0
- package/src/models/FeatureFlag.ts +12 -0
- package/src/models/LuciqConfig.ts +48 -0
- package/src/models/NonFatalOptions.ts +16 -0
- package/src/models/OverAirUpdate.ts +14 -0
- package/src/models/Report.ts +124 -0
- package/src/models/ReproConfig.ts +31 -0
- package/src/models/SessionMetadata.ts +57 -0
- package/src/models/ThemeConfig.ts +34 -0
- package/src/models/W3cExternalTraceAttributes.ts +22 -0
- package/src/modules/APM.ts +117 -0
- package/src/modules/BugReporting.ts +254 -0
- package/src/modules/CrashReporting.ts +54 -0
- package/src/modules/FeatureRequests.ts +32 -0
- package/src/modules/Luciq.ts +934 -0
- package/src/modules/NetworkLogger.ts +270 -0
- package/src/modules/Replies.ts +137 -0
- package/src/modules/SessionReplay.ts +111 -0
- package/src/modules/Surveys.ts +118 -0
- package/src/native/NativeAPM.ts +51 -0
- package/src/native/NativeBugReporting.ts +70 -0
- package/src/native/NativeConstants.ts +215 -0
- package/src/native/NativeCrashReporting.ts +29 -0
- package/src/native/NativeFeatureRequests.ts +12 -0
- package/src/native/NativeLuciq.ts +179 -0
- package/src/native/NativeNetworkLogger.ts +42 -0
- package/src/native/NativePackage.ts +25 -0
- package/src/native/NativeReplies.ts +34 -0
- package/src/native/NativeSessionReplay.ts +21 -0
- package/src/native/NativeSurveys.ts +34 -0
- package/src/promise.d.ts +11 -0
- package/src/utils/AppStatesHandler.ts +19 -0
- package/src/utils/Enums.ts +266 -0
- package/src/utils/FeatureFlags.ts +33 -0
- package/src/utils/LuciqConstants.ts +24 -0
- package/src/utils/LuciqUtils.ts +417 -0
- package/src/utils/UnhandledRejectionTracking.ts +118 -0
- package/src/utils/XhrNetworkInterceptor.ts +333 -0
- package/src/utils/config.ts +7 -0
- package/src/utils/logger.ts +54 -0
- package/tsconfig.json +32 -0
- package/tsconfig.test.json +4 -0
- package/tsconfig.upload.json +10 -0
- package/upload/index.d.ts +4 -0
- package/upload/index.js +17314 -0
- package/upload/migrate.d.ts +14 -0
- package/upload/package.json +5 -0
- package/upload/uploadEasUpdatesSourcemaps.d.ts +21 -0
- package/upload/uploadSoFiles.d.ts +21 -0
- package/upload/uploadSourcemaps.d.ts +21 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
package ai.luciq.reactlibrary;
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Arguments;
|
|
4
|
+
import com.facebook.react.bridge.Callback;
|
|
5
|
+
import com.facebook.react.bridge.Promise;
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
7
|
+
import com.facebook.react.bridge.ReactMethod;
|
|
8
|
+
import com.facebook.react.bridge.WritableArray;
|
|
9
|
+
import ai.luciq.library.Feature;
|
|
10
|
+
import ai.luciq.reactlibrary.utils.ArrayUtil;
|
|
11
|
+
import ai.luciq.reactlibrary.utils.EventEmitterModule;
|
|
12
|
+
import ai.luciq.reactlibrary.utils.LuciqUtil;
|
|
13
|
+
import ai.luciq.reactlibrary.utils.MainThreadHandler;
|
|
14
|
+
import ai.luciq.survey.callbacks.*;
|
|
15
|
+
import ai.luciq.survey.Surveys;
|
|
16
|
+
import ai.luciq.survey.Survey;
|
|
17
|
+
|
|
18
|
+
import org.json.JSONArray;
|
|
19
|
+
|
|
20
|
+
import java.util.List;
|
|
21
|
+
|
|
22
|
+
import javax.annotation.Nonnull;
|
|
23
|
+
|
|
24
|
+
public class RNLuciqSurveysModule extends EventEmitterModule {
|
|
25
|
+
|
|
26
|
+
public RNLuciqSurveysModule(ReactApplicationContext reactContext) {
|
|
27
|
+
super(reactContext);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@Nonnull
|
|
31
|
+
@Override
|
|
32
|
+
public String getName() {
|
|
33
|
+
return "LCQSurveys";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@ReactMethod
|
|
37
|
+
public void addListener(String event) {
|
|
38
|
+
super.addListener(event);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@ReactMethod
|
|
42
|
+
public void removeListeners(Integer count) {
|
|
43
|
+
super.removeListeners(count);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns true if the survey with a specific token was answered before.
|
|
48
|
+
* Will return false if the token does not exist or if the survey was not answered before.
|
|
49
|
+
*
|
|
50
|
+
* @param surveyToken the attribute key as string
|
|
51
|
+
* @param promise A promise that gets resolved with the returned value of whether
|
|
52
|
+
* the user has responded to the survey or not.
|
|
53
|
+
* @return the desired value of whether the user has responded to the survey or not.
|
|
54
|
+
*/
|
|
55
|
+
@ReactMethod
|
|
56
|
+
public void hasRespondedToSurvey(final String surveyToken, final Promise promise) {
|
|
57
|
+
MainThreadHandler.runOnMainThread(new Runnable() {
|
|
58
|
+
@Override
|
|
59
|
+
public void run() {
|
|
60
|
+
boolean hasResponded = false;
|
|
61
|
+
try {
|
|
62
|
+
hasResponded = Surveys.hasRespondToSurvey(surveyToken);
|
|
63
|
+
} catch (Exception e) {
|
|
64
|
+
e.printStackTrace();
|
|
65
|
+
}
|
|
66
|
+
promise.resolve(hasResponded);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Shows survey with a specific token.
|
|
73
|
+
* Does nothing if there are no available surveys with that specific token.
|
|
74
|
+
* Answered and cancelled surveys won't show up again.
|
|
75
|
+
*
|
|
76
|
+
* @param surveyToken A String with a survey token.
|
|
77
|
+
*/
|
|
78
|
+
@ReactMethod
|
|
79
|
+
public void showSurvey(final String surveyToken) {
|
|
80
|
+
MainThreadHandler.runOnMainThread(new Runnable() {
|
|
81
|
+
@Override
|
|
82
|
+
public void run() {
|
|
83
|
+
try {
|
|
84
|
+
Surveys.showSurvey(surveyToken);
|
|
85
|
+
} catch (Exception e) {
|
|
86
|
+
e.printStackTrace();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Show any valid survey if exist
|
|
94
|
+
*
|
|
95
|
+
* @return true if a valid survey was shown otherwise false
|
|
96
|
+
*/
|
|
97
|
+
@ReactMethod
|
|
98
|
+
public void showSurveysIfAvailable() {
|
|
99
|
+
MainThreadHandler.runOnMainThread(new Runnable() {
|
|
100
|
+
@Override
|
|
101
|
+
public void run() {
|
|
102
|
+
try {
|
|
103
|
+
Surveys.showSurveyIfAvailable();
|
|
104
|
+
} catch (java.lang.Exception exception) {
|
|
105
|
+
exception.printStackTrace();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Show any valid survey if exist
|
|
113
|
+
*
|
|
114
|
+
* @return true if a valid survey was shown otherwise false
|
|
115
|
+
*/
|
|
116
|
+
@ReactMethod
|
|
117
|
+
public void setEnabled(final boolean isEnabled) {
|
|
118
|
+
MainThreadHandler.runOnMainThread(new Runnable() {
|
|
119
|
+
@Override
|
|
120
|
+
public void run() {
|
|
121
|
+
try {
|
|
122
|
+
if (isEnabled) {
|
|
123
|
+
Surveys.setState(Feature.State.ENABLED);
|
|
124
|
+
} else {
|
|
125
|
+
Surveys.setState(Feature.State.DISABLED);
|
|
126
|
+
}
|
|
127
|
+
} catch (java.lang.Exception exception) {
|
|
128
|
+
exception.printStackTrace();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Sets the runnable that gets executed just before showing any valid survey<br/>
|
|
136
|
+
* WARNING: This runs on your application's main UI thread. Please do not include
|
|
137
|
+
* any blocking operations to avoid ANRs.
|
|
138
|
+
*
|
|
139
|
+
* @param handler to run on the UI thread before showing any valid survey
|
|
140
|
+
*/
|
|
141
|
+
@ReactMethod
|
|
142
|
+
public void setOnShowHandler(final Callback handler) {
|
|
143
|
+
MainThreadHandler.runOnMainThread(new Runnable() {
|
|
144
|
+
@Override
|
|
145
|
+
public void run() {
|
|
146
|
+
Surveys.setOnShowCallback(new OnShowCallback() {
|
|
147
|
+
@Override
|
|
148
|
+
public void onShow() {
|
|
149
|
+
sendEvent(Constants.LCQ_ON_SHOW_SURVEY_HANDLER, null);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Sets the runnable that gets executed just after showing any valid survey<br/>
|
|
158
|
+
* WARNING: This runs on your application's main UI thread. Please do not include
|
|
159
|
+
* any blocking operations to avoid ANRs.
|
|
160
|
+
*
|
|
161
|
+
* @param handler to run on the UI thread after showing any valid survey
|
|
162
|
+
*/
|
|
163
|
+
@ReactMethod
|
|
164
|
+
public void setOnDismissHandler(final Callback handler) {
|
|
165
|
+
MainThreadHandler.runOnMainThread(new Runnable() {
|
|
166
|
+
@Override
|
|
167
|
+
public void run() {
|
|
168
|
+
Surveys.setOnDismissCallback(new OnDismissCallback() {
|
|
169
|
+
@Override
|
|
170
|
+
public void onDismiss() {
|
|
171
|
+
sendEvent(Constants.LCQ_ON_DISMISS_SURVEY_HANDLER, null);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Returns an array containing the available surveys.
|
|
180
|
+
*/
|
|
181
|
+
@ReactMethod
|
|
182
|
+
public void getAvailableSurveys(final Promise promise) {
|
|
183
|
+
MainThreadHandler.runOnMainThread(new Runnable() {
|
|
184
|
+
@Override
|
|
185
|
+
public void run() {
|
|
186
|
+
try {
|
|
187
|
+
List<Survey> availableSurveys = Surveys.getAvailableSurveys();
|
|
188
|
+
JSONArray surveysArray = LuciqUtil.surveyObjectToJson(availableSurveys);
|
|
189
|
+
WritableArray array = ArrayUtil.convertJsonToWritableArray(surveysArray);
|
|
190
|
+
promise.resolve(array);
|
|
191
|
+
} catch (Exception e) {
|
|
192
|
+
e.printStackTrace();
|
|
193
|
+
promise.resolve(Arguments.createArray());
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Set Surveys auto-showing state, default state auto-showing enabled
|
|
201
|
+
*
|
|
202
|
+
* @param autoShowingSurveysEnabled whether Surveys should be auto-showing or not
|
|
203
|
+
*/
|
|
204
|
+
@ReactMethod
|
|
205
|
+
public void setAutoShowingEnabled(final boolean autoShowingSurveysEnabled) {
|
|
206
|
+
MainThreadHandler.runOnMainThread(new Runnable() {
|
|
207
|
+
@Override
|
|
208
|
+
public void run() {
|
|
209
|
+
try {
|
|
210
|
+
Surveys.setAutoShowingEnabled(autoShowingSurveysEnabled);
|
|
211
|
+
} catch (Exception e) {
|
|
212
|
+
e.printStackTrace();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Set Surveys welcome screen enabled, default value is false
|
|
220
|
+
*
|
|
221
|
+
* @param shouldShow shouldShow whether should a welcome screen be shown
|
|
222
|
+
* before taking surveys or not
|
|
223
|
+
*/
|
|
224
|
+
@ReactMethod
|
|
225
|
+
public void setShouldShowWelcomeScreen(final boolean shouldShow) {
|
|
226
|
+
MainThreadHandler.runOnMainThread(new Runnable() {
|
|
227
|
+
@Override
|
|
228
|
+
public void run() {
|
|
229
|
+
try {
|
|
230
|
+
Surveys.setShouldShowWelcomeScreen(shouldShow);
|
|
231
|
+
} catch (java.lang.Exception exception) {
|
|
232
|
+
exception.printStackTrace();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/*
|
|
2
|
+
ArrayUtil exposes a set of helper methods for working with
|
|
3
|
+
ReadableArray (by React Native), Object[], and JSONArray.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
package ai.luciq.reactlibrary.utils;
|
|
7
|
+
|
|
8
|
+
import com.facebook.react.bridge.Arguments;
|
|
9
|
+
import com.facebook.react.bridge.ReadableArray;
|
|
10
|
+
import com.facebook.react.bridge.ReadableType;
|
|
11
|
+
import com.facebook.react.bridge.WritableArray;
|
|
12
|
+
|
|
13
|
+
import java.util.ArrayList;
|
|
14
|
+
import java.util.Map;
|
|
15
|
+
|
|
16
|
+
import org.json.JSONArray;
|
|
17
|
+
import org.json.JSONObject;
|
|
18
|
+
import org.json.JSONException;
|
|
19
|
+
|
|
20
|
+
public class ArrayUtil {
|
|
21
|
+
|
|
22
|
+
public static JSONArray toJSONArray(ReadableArray readableArray) throws JSONException {
|
|
23
|
+
JSONArray jsonArray = new JSONArray();
|
|
24
|
+
|
|
25
|
+
for (int i = 0; i < readableArray.size(); i++) {
|
|
26
|
+
ReadableType type = readableArray.getType(i);
|
|
27
|
+
|
|
28
|
+
switch (type) {
|
|
29
|
+
case Null:
|
|
30
|
+
jsonArray.put(i, null);
|
|
31
|
+
break;
|
|
32
|
+
case Boolean:
|
|
33
|
+
jsonArray.put(i, readableArray.getBoolean(i));
|
|
34
|
+
break;
|
|
35
|
+
case Number:
|
|
36
|
+
jsonArray.put(i, readableArray.getDouble(i));
|
|
37
|
+
break;
|
|
38
|
+
case String:
|
|
39
|
+
jsonArray.put(i, readableArray.getString(i));
|
|
40
|
+
break;
|
|
41
|
+
case Map:
|
|
42
|
+
jsonArray.put(i, MapUtil.toJSONObject(readableArray.getMap(i)));
|
|
43
|
+
break;
|
|
44
|
+
case Array:
|
|
45
|
+
jsonArray.put(i, ArrayUtil.toJSONArray(readableArray.getArray(i)));
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return jsonArray;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public static WritableArray convertJsonToWritableArray(JSONArray jsonArray) throws JSONException {
|
|
54
|
+
WritableArray array = Arguments.createArray();
|
|
55
|
+
|
|
56
|
+
for (int i = 0; i < jsonArray.length(); i++) {
|
|
57
|
+
Object value = jsonArray.get(i);
|
|
58
|
+
if (value instanceof JSONObject) {
|
|
59
|
+
array.pushMap(MapUtil.convertJsonToWritableMap((JSONObject) value));
|
|
60
|
+
} else if (value instanceof JSONArray) {
|
|
61
|
+
array.pushArray(convertJsonToWritableArray((JSONArray) value));
|
|
62
|
+
} else if (value instanceof Boolean) {
|
|
63
|
+
array.pushBoolean((Boolean) value);
|
|
64
|
+
} else if (value instanceof Integer) {
|
|
65
|
+
array.pushInt((Integer) value);
|
|
66
|
+
} else if (value instanceof Double) {
|
|
67
|
+
array.pushDouble((Double) value);
|
|
68
|
+
} else if (value instanceof String) {
|
|
69
|
+
array.pushString((String) value);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return array;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public static Object[] toArray(JSONArray jsonArray) throws JSONException {
|
|
76
|
+
Object[] array = new Object[jsonArray.length()];
|
|
77
|
+
|
|
78
|
+
for (int i = 0; i < jsonArray.length(); i++) {
|
|
79
|
+
Object value = jsonArray.get(i);
|
|
80
|
+
|
|
81
|
+
if (value instanceof JSONObject) {
|
|
82
|
+
value = MapUtil.toMap((JSONObject) value);
|
|
83
|
+
}
|
|
84
|
+
if (value instanceof JSONArray) {
|
|
85
|
+
value = ArrayUtil.toArray((JSONArray) value);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
array[i] = value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return array;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public static Object[] toArray(ReadableArray readableArray) {
|
|
95
|
+
Object[] array = new Object[readableArray.size()];
|
|
96
|
+
|
|
97
|
+
for (int i = 0; i < readableArray.size(); i++) {
|
|
98
|
+
ReadableType type = readableArray.getType(i);
|
|
99
|
+
|
|
100
|
+
switch (type) {
|
|
101
|
+
case Null:
|
|
102
|
+
array[i] = null;
|
|
103
|
+
break;
|
|
104
|
+
case Boolean:
|
|
105
|
+
array[i] = readableArray.getBoolean(i);
|
|
106
|
+
break;
|
|
107
|
+
case Number:
|
|
108
|
+
array[i] = readableArray.getDouble(i);
|
|
109
|
+
break;
|
|
110
|
+
case String:
|
|
111
|
+
array[i] = readableArray.getString(i);
|
|
112
|
+
break;
|
|
113
|
+
case Map:
|
|
114
|
+
array[i] = MapUtil.toMap(readableArray.getMap(i));
|
|
115
|
+
break;
|
|
116
|
+
case Array:
|
|
117
|
+
array[i] = ArrayUtil.toArray(readableArray.getArray(i));
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return array;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public static WritableArray toWritableArray(Object[] array) {
|
|
126
|
+
WritableArray writableArray = Arguments.createArray();
|
|
127
|
+
|
|
128
|
+
for (int i = 0; i < array.length; i++) {
|
|
129
|
+
Object value = array[i];
|
|
130
|
+
|
|
131
|
+
if (value == null) {
|
|
132
|
+
writableArray.pushNull();
|
|
133
|
+
}
|
|
134
|
+
if (value instanceof Boolean) {
|
|
135
|
+
writableArray.pushBoolean((Boolean) value);
|
|
136
|
+
}
|
|
137
|
+
if (value instanceof Double) {
|
|
138
|
+
writableArray.pushDouble((Double) value);
|
|
139
|
+
}
|
|
140
|
+
if (value instanceof Integer) {
|
|
141
|
+
writableArray.pushInt((Integer) value);
|
|
142
|
+
}
|
|
143
|
+
if (value instanceof String) {
|
|
144
|
+
writableArray.pushString((String) value);
|
|
145
|
+
}
|
|
146
|
+
if (value instanceof Map) {
|
|
147
|
+
writableArray.pushMap(MapUtil.toWritableMap((Map<String, Object>) value));
|
|
148
|
+
}
|
|
149
|
+
if (value.getClass().isArray()) {
|
|
150
|
+
writableArray.pushArray(ArrayUtil.toWritableArray((Object[]) value));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return writableArray;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
public static ArrayList<String> parseReadableArrayOfStrings(ReadableArray readableArray) {
|
|
158
|
+
ArrayList<String> array = new ArrayList<>();
|
|
159
|
+
for (int i = 0; i < readableArray.size(); i++) {
|
|
160
|
+
ReadableType type = readableArray.getType(i);
|
|
161
|
+
if (type == ReadableType.String) {
|
|
162
|
+
array.add(readableArray.getString(i));
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return array;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
package ai.luciq.reactlibrary.utils;
|
|
2
|
+
|
|
3
|
+
import androidx.annotation.Nullable;
|
|
4
|
+
import androidx.annotation.VisibleForTesting;
|
|
5
|
+
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext;
|
|
7
|
+
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
|
8
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
9
|
+
import com.facebook.react.bridge.WritableMap;
|
|
10
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
|
11
|
+
|
|
12
|
+
public abstract class EventEmitterModule extends ReactContextBaseJavaModule {
|
|
13
|
+
private int listenerCount = 0;
|
|
14
|
+
|
|
15
|
+
public EventEmitterModule(ReactApplicationContext context) {
|
|
16
|
+
super(context);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@VisibleForTesting
|
|
20
|
+
public void sendEvent(String event, @Nullable ReadableMap params) {
|
|
21
|
+
if (listenerCount > 0) {
|
|
22
|
+
getReactApplicationContext()
|
|
23
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
|
|
24
|
+
.emit(event, params);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected void addListener(String ignoredEvent) {
|
|
29
|
+
listenerCount++;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
protected void removeListeners(Integer count) {
|
|
33
|
+
listenerCount -= count;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package ai.luciq.reactlibrary.utils;
|
|
2
|
+
|
|
3
|
+
import ai.luciq.survey.Survey;
|
|
4
|
+
|
|
5
|
+
import org.json.JSONArray;
|
|
6
|
+
import org.json.JSONException;
|
|
7
|
+
import org.json.JSONObject;
|
|
8
|
+
|
|
9
|
+
import java.lang.reflect.Method;
|
|
10
|
+
import java.util.List;
|
|
11
|
+
|
|
12
|
+
public class LuciqUtil {
|
|
13
|
+
public static Method getMethod(Class clazz, String methodName, Class... parameterType) {
|
|
14
|
+
final Method[] methods = clazz.getDeclaredMethods();
|
|
15
|
+
|
|
16
|
+
for (Method method : methods) {
|
|
17
|
+
if (method.getName().equals(methodName) && method.getParameterTypes().length ==
|
|
18
|
+
parameterType.length) {
|
|
19
|
+
if (parameterType.length == 0) {
|
|
20
|
+
method.setAccessible(true);
|
|
21
|
+
return method;
|
|
22
|
+
}
|
|
23
|
+
for (int i = 0; i < parameterType.length; i++) {
|
|
24
|
+
if (method.getParameterTypes()[i] == parameterType[i]) {
|
|
25
|
+
if (i == method.getParameterTypes().length - 1) {
|
|
26
|
+
method.setAccessible(true);
|
|
27
|
+
return method;
|
|
28
|
+
}
|
|
29
|
+
} else {
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convenience method to convert from a list of Surveys to a JSON array
|
|
40
|
+
*
|
|
41
|
+
* @param list
|
|
42
|
+
* List of Surveys to be converted to JSON array
|
|
43
|
+
*/
|
|
44
|
+
public static JSONArray surveyObjectToJson(List<Survey> list) {
|
|
45
|
+
JSONArray jsonArray = new JSONArray();
|
|
46
|
+
try{
|
|
47
|
+
for (Survey obj : list) {
|
|
48
|
+
JSONObject object = new JSONObject();
|
|
49
|
+
object.put("title", obj.getTitle());
|
|
50
|
+
jsonArray.put(object);
|
|
51
|
+
}
|
|
52
|
+
} catch (JSONException e) {
|
|
53
|
+
e.printStackTrace();
|
|
54
|
+
}
|
|
55
|
+
return jsonArray;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
package ai.luciq.reactlibrary.utils;
|
|
2
|
+
|
|
3
|
+
import android.os.Handler;
|
|
4
|
+
import android.os.Looper;
|
|
5
|
+
|
|
6
|
+
public class MainThreadHandler {
|
|
7
|
+
/**
|
|
8
|
+
* Runs a block of code on the Main Thread
|
|
9
|
+
*/
|
|
10
|
+
public static void runOnMainThread(Runnable runnable) {
|
|
11
|
+
new Handler(Looper.getMainLooper()).post(runnable);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/*
|
|
2
|
+
MapUtil exposes a set of helper methods for working with
|
|
3
|
+
ReadableMap (by React Native), Map<String, Object>, and JSONObject.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
package ai.luciq.reactlibrary.utils;
|
|
7
|
+
|
|
8
|
+
import com.facebook.react.bridge.Arguments;
|
|
9
|
+
import com.facebook.react.bridge.ReadableMap;
|
|
10
|
+
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
|
11
|
+
import com.facebook.react.bridge.ReadableType;
|
|
12
|
+
import com.facebook.react.bridge.WritableMap;
|
|
13
|
+
import com.facebook.react.bridge.WritableNativeMap;
|
|
14
|
+
|
|
15
|
+
import java.util.Map;
|
|
16
|
+
import java.util.HashMap;
|
|
17
|
+
import java.util.Iterator;
|
|
18
|
+
|
|
19
|
+
import org.json.JSONArray;
|
|
20
|
+
import org.json.JSONObject;
|
|
21
|
+
import org.json.JSONException;
|
|
22
|
+
|
|
23
|
+
public class MapUtil {
|
|
24
|
+
|
|
25
|
+
public static JSONObject toJSONObject(ReadableMap readableMap) throws JSONException {
|
|
26
|
+
JSONObject jsonObject = new JSONObject();
|
|
27
|
+
|
|
28
|
+
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
|
|
29
|
+
|
|
30
|
+
while (iterator.hasNextKey()) {
|
|
31
|
+
String key = iterator.nextKey();
|
|
32
|
+
ReadableType type = readableMap.getType(key);
|
|
33
|
+
|
|
34
|
+
switch (type) {
|
|
35
|
+
case Null:
|
|
36
|
+
jsonObject.put(key, null);
|
|
37
|
+
break;
|
|
38
|
+
case Boolean:
|
|
39
|
+
jsonObject.put(key, readableMap.getBoolean(key));
|
|
40
|
+
break;
|
|
41
|
+
case Number:
|
|
42
|
+
jsonObject.put(key, readableMap.getDouble(key));
|
|
43
|
+
break;
|
|
44
|
+
case String:
|
|
45
|
+
jsonObject.put(key, readableMap.getString(key));
|
|
46
|
+
break;
|
|
47
|
+
case Map:
|
|
48
|
+
jsonObject.put(key, MapUtil.toJSONObject(readableMap.getMap(key)));
|
|
49
|
+
break;
|
|
50
|
+
case Array:
|
|
51
|
+
jsonObject.put(key, ArrayUtil.toJSONArray(readableMap.getArray(key)));
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return jsonObject;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public static Map<String, Object> toMap(JSONObject jsonObject) throws JSONException {
|
|
60
|
+
Map<String, Object> map = new HashMap<>();
|
|
61
|
+
Iterator<String> iterator = jsonObject.keys();
|
|
62
|
+
|
|
63
|
+
while (iterator.hasNext()) {
|
|
64
|
+
String key = iterator.next();
|
|
65
|
+
Object value = jsonObject.get(key);
|
|
66
|
+
|
|
67
|
+
if (value instanceof JSONObject) {
|
|
68
|
+
value = MapUtil.toMap((JSONObject) value);
|
|
69
|
+
}
|
|
70
|
+
if (value instanceof JSONArray) {
|
|
71
|
+
value = ArrayUtil.toArray((JSONArray) value);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
map.put(key, value);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return map;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public static Map<String, Object> toMap(ReadableMap readableMap) {
|
|
81
|
+
Map<String, Object> map = new HashMap<>();
|
|
82
|
+
ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
|
|
83
|
+
|
|
84
|
+
while (iterator.hasNextKey()) {
|
|
85
|
+
String key = iterator.nextKey();
|
|
86
|
+
ReadableType type = readableMap.getType(key);
|
|
87
|
+
|
|
88
|
+
switch (type) {
|
|
89
|
+
case Null:
|
|
90
|
+
map.put(key, null);
|
|
91
|
+
break;
|
|
92
|
+
case Boolean:
|
|
93
|
+
map.put(key, readableMap.getBoolean(key));
|
|
94
|
+
break;
|
|
95
|
+
case Number:
|
|
96
|
+
map.put(key, readableMap.getDouble(key));
|
|
97
|
+
break;
|
|
98
|
+
case String:
|
|
99
|
+
map.put(key, readableMap.getString(key));
|
|
100
|
+
break;
|
|
101
|
+
case Map:
|
|
102
|
+
map.put(key, MapUtil.toMap(readableMap.getMap(key)));
|
|
103
|
+
break;
|
|
104
|
+
case Array:
|
|
105
|
+
map.put(key, ArrayUtil.toArray(readableMap.getArray(key)));
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return map;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public static WritableMap toWritableMap(Map<String, Object> map) {
|
|
114
|
+
WritableMap writableMap = Arguments.createMap();
|
|
115
|
+
Iterator iterator = map.entrySet().iterator();
|
|
116
|
+
|
|
117
|
+
while (iterator.hasNext()) {
|
|
118
|
+
Map.Entry pair = (Map.Entry) iterator.next();
|
|
119
|
+
Object value = pair.getValue();
|
|
120
|
+
|
|
121
|
+
if (value == null) {
|
|
122
|
+
writableMap.putNull((String) pair.getKey());
|
|
123
|
+
} else if (value instanceof Boolean) {
|
|
124
|
+
writableMap.putBoolean((String) pair.getKey(), (Boolean) value);
|
|
125
|
+
} else if (value instanceof Double) {
|
|
126
|
+
writableMap.putDouble((String) pair.getKey(), (Double) value);
|
|
127
|
+
} else if (value instanceof Integer) {
|
|
128
|
+
writableMap.putInt((String) pair.getKey(), (Integer) value);
|
|
129
|
+
} else if (value instanceof String) {
|
|
130
|
+
writableMap.putString((String) pair.getKey(), (String) value);
|
|
131
|
+
} else if (value instanceof Map) {
|
|
132
|
+
writableMap.putMap((String) pair.getKey(), MapUtil.toWritableMap((Map<String,
|
|
133
|
+
Object>) value));
|
|
134
|
+
} else if (value.getClass() != null && value.getClass().isArray()) {
|
|
135
|
+
writableMap.putArray((String) pair.getKey(), ArrayUtil.toWritableArray((Object[])
|
|
136
|
+
value));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
iterator.remove();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return writableMap;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
public static WritableMap convertJsonToWritableMap(JSONObject jsonObject) throws JSONException {
|
|
146
|
+
WritableMap map = new WritableNativeMap();
|
|
147
|
+
|
|
148
|
+
Iterator<String> iterator = jsonObject.keys();
|
|
149
|
+
while (iterator.hasNext()) {
|
|
150
|
+
String key = iterator.next();
|
|
151
|
+
Object value = jsonObject.get(key);
|
|
152
|
+
if (value instanceof JSONObject) {
|
|
153
|
+
map.putMap(key, convertJsonToWritableMap((JSONObject) value));
|
|
154
|
+
} else if (value instanceof JSONArray) {
|
|
155
|
+
map.putArray(key, ArrayUtil.convertJsonToWritableArray((JSONArray) value));
|
|
156
|
+
} else if (value instanceof Boolean) {
|
|
157
|
+
map.putBoolean(key, (Boolean) value);
|
|
158
|
+
} else if (value instanceof Integer) {
|
|
159
|
+
map.putInt(key, (Integer) value);
|
|
160
|
+
} else if (value instanceof Double) {
|
|
161
|
+
map.putDouble(key, (Double) value);
|
|
162
|
+
} else if (value instanceof String) {
|
|
163
|
+
map.putString(key, (String) value);
|
|
164
|
+
} else {
|
|
165
|
+
map.putString(key, value.toString());
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return map;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
}
|