@react-native-ohos/react-native-text-input-mask 3.1.6-rc.1 → 3.1.6-rc.4

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 (40) hide show
  1. package/LICENSE +21 -21
  2. package/README.OpenSource +10 -10
  3. package/README.md +15 -134
  4. package/dist/tsconfig.tsbuildinfo +1 -1
  5. package/harmony/text_input_mask/BuildProfile.ets +16 -16
  6. package/harmony/text_input_mask/Index.ets +7 -7
  7. package/harmony/text_input_mask/build-profile.json5 +31 -31
  8. package/harmony/text_input_mask/hvigorfile.ts +6 -6
  9. package/harmony/text_input_mask/obfuscation-rules.txt +22 -22
  10. package/harmony/text_input_mask/oh-package.json5 +11 -11
  11. package/harmony/text_input_mask/src/main/cpp/CMakeLists.txt +9 -9
  12. package/harmony/text_input_mask/src/main/cpp/RNTextInputMask.cpp +421 -415
  13. package/harmony/text_input_mask/src/main/cpp/RNTextInputMask.h +93 -93
  14. package/harmony/text_input_mask/src/main/cpp/RNTextInputMaskPackage.h +31 -31
  15. package/harmony/text_input_mask/src/main/cpp/common/Compiler.h +174 -174
  16. package/harmony/text_input_mask/src/main/cpp/common/FormatError.h +22 -22
  17. package/harmony/text_input_mask/src/main/cpp/common/FormatSanitizer.h +230 -230
  18. package/harmony/text_input_mask/src/main/cpp/common/Mask.h +380 -377
  19. package/harmony/text_input_mask/src/main/cpp/common/RTLMask.h +78 -78
  20. package/harmony/text_input_mask/src/main/cpp/common/model/AffinityCalculationStrategy.h +57 -57
  21. package/harmony/text_input_mask/src/main/cpp/common/model/CaretString.h +75 -75
  22. package/harmony/text_input_mask/src/main/cpp/common/model/CaretStringIterator.h +58 -58
  23. package/harmony/text_input_mask/src/main/cpp/common/model/Next.h +24 -24
  24. package/harmony/text_input_mask/src/main/cpp/common/model/Notation.h +29 -24
  25. package/harmony/text_input_mask/src/main/cpp/common/model/RTLCaretStringIterator.h +22 -22
  26. package/harmony/text_input_mask/src/main/cpp/common/model/State.h +302 -302
  27. package/harmony/text_input_mask/src/main/cpp/common/model/common.h +94 -94
  28. package/harmony/text_input_mask/src/main/ets/RNTextInputMaskPackage.ts +28 -28
  29. package/harmony/text_input_mask/src/main/ets/RNTextInputMaskTurboModle.ts +32 -32
  30. package/harmony/text_input_mask/src/main/module.json5 +11 -11
  31. package/harmony/text_input_mask/src/main/resources/base/element/string.json +8 -8
  32. package/harmony/text_input_mask/src/main/resources/en_US/element/string.json +8 -8
  33. package/harmony/text_input_mask/src/main/resources/zh_CN/element/string.json +8 -8
  34. package/harmony/text_input_mask/ts.ts +8 -8
  35. package/harmony/text_input_mask.har +0 -0
  36. package/index.tsx +258 -258
  37. package/package.json +48 -50
  38. package/src/RNNativeTextInputMask.ts +142 -142
  39. package/src/index.harmony.ts +147 -147
  40. package/src/index.ts +36 -36
@@ -1,415 +1,421 @@
1
- /*
2
- * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
3
- * Use of this source code is governed by a MIT license that can be
4
- * found in the LICENSE file.
5
- */
6
-
7
- #include "RNTextInputMask.h"
8
- #include "RNOH/arkui/TextInputNode.h"
9
- #include "RNOH/ComponentInstance.h"
10
- #include "RNOH/RNInstanceCAPI.h"
11
- #include "RNOHCorePackage/ComponentInstances/TextInputComponentInstance.h"
12
- #include "common/model/AffinityCalculationStrategy.h"
13
-
14
- #include <cstdint>
15
- #include <iostream>
16
- #include <jsi/jsi.h>
17
- #include <string>
18
- #include <thread>
19
-
20
- using namespace facebook;
21
- using namespace rnoh;
22
- using namespace react;
23
- using namespace TinpMask;
24
- static constexpr int AVOIDENCE = 1;
25
- std::unordered_map<std::string, std::shared_ptr<RTLMask>> TinpMask::RTLMask::cache;
26
- std::unordered_map<std::string, std::shared_ptr<Mask>> TinpMask::Mask::MaskFactory::maskCache;
27
- void maybeThrow(int32_t status) {
28
- DLOG(INFO) << "=====text change maybeThrow status: " << status;
29
- if (status != 0) {
30
- auto message = std::string("ArkUINode operation failed with status: ") + std::to_string(status);
31
- LOG(ERROR) << message;
32
- throw std::runtime_error(std::move(message));
33
- }
34
- }
35
-
36
- void myEventReceiver(ArkUI_NodeEvent *event) {
37
- int32_t eventId = OH_ArkUI_NodeEvent_GetTargetId(event);
38
- ArkUI_NodeHandle textNode = OH_ArkUI_NodeEvent_GetNodeHandle(event);
39
- void *data = NativeNodeApi::getInstance()->getUserData(textNode);
40
- auto item = NativeNodeApi::getInstance()->getAttribute(textNode, NODE_TEXT_INPUT_TEXT);
41
- std::string content = item->string;
42
- UserData *userData = reinterpret_cast<UserData *>(data);
43
- auto self = userData->instance;
44
- if (self == nullptr) { return; };
45
- bool isDelete = userData->lastInputText.size() > content.size();
46
- bool useAutocomplete = !isDelete ? userData->maskOptions.autocomplete.value() : false;
47
- bool useAutoskip = isDelete ? userData->maskOptions.autoskip.value() : false;
48
-
49
- // onChange 事件
50
- if (eventId == 110) {
51
- std::shared_ptr<CaretString::CaretGravity> caretGravity =
52
- isDelete ? std::make_shared<CaretString::CaretGravity>(CaretString::Backward(useAutoskip))
53
- : std::make_shared<CaretString::CaretGravity>(CaretString::Forward(useAutocomplete));
54
- CaretString text(content, content.length(), caretGravity);
55
- try {
56
- auto maskObj = self->pickMask(text, userData->maskOptions, userData->primaryFormat);
57
- auto result = maskObj->apply(text);
58
- std::string resultString = result.formattedText.string;
59
- DLOG(INFO) << "mask result complete: " << result.complete;
60
- userData->lastInputText = resultString;
61
- std::string finalString = isDelete ? content : resultString;
62
- ArkUI_AttributeItem item{.string = finalString.c_str()};
63
- userData->lastInputText = finalString;
64
- maybeThrow(NativeNodeApi::getInstance()->setAttribute(userData->data, NODE_TEXT_INPUT_TEXT, &item));
65
- } catch (FormatError e) {
66
- DLOG(ERROR) << " mask complier error " << e.what();
67
- }
68
- }
69
- // onFocus 事件
70
- if (eventId == 111) {
71
- if (userData->maskOptions.autocomplete.value()) {
72
- std::string text = "";
73
- text += content;
74
- try {
75
- CaretString string(text, text.length(),
76
- std::make_shared<CaretString::Forward>(userData->maskOptions.autocomplete.value()));
77
- auto maskObj = self->pickMask(string, userData->maskOptions, userData->primaryFormat);
78
- std::string resultString = maskObj->apply(string).formattedText.string;
79
- ArkUI_AttributeItem item{.string = resultString.c_str()};
80
- maybeThrow(NativeNodeApi::getInstance()->setAttribute(userData->data, NODE_TEXT_INPUT_TEXT, &item));
81
- } catch (FormatError e) {
82
- DLOG(ERROR) << " mask complier error " << e.what();
83
- }
84
- }
85
- }
86
- }
87
-
88
- // 计算亲和度
89
- int calculateAffinity(Mask mask, const CaretString &text, std::string affinityCalculationStrategy) {
90
- AffinityCalculationStrategy strategy;
91
- if (affinityCalculationStrategy == "WHOLE_STRING") {
92
- strategy = AffinityCalculationStrategy::WHOLE_STRING;
93
- } else if (affinityCalculationStrategy == "PREFIX") {
94
- strategy = AffinityCalculationStrategy::PREFIX;
95
- } else if (affinityCalculationStrategy == "CAPACITY") {
96
- strategy = AffinityCalculationStrategy::CAPACITY;
97
- } else {
98
- strategy = AffinityCalculationStrategy::EXTRACTED_VALUE_CAPACITY;
99
- }
100
- int affinity = AffinityCalculator::calculateAffinityOfMask(strategy, mask, text);
101
-
102
- return affinity;
103
- }
104
- // 获取或创建 Mask
105
- std::shared_ptr<Mask> maskGetOrCreate(const std::string &format, const std::vector<Notation> &customNotations,
106
- bool rightToLeft) {
107
- if (rightToLeft) {
108
- return RTLMask::getOrCreate(format, customNotations);
109
- } else {
110
- return Mask::MaskFactory::getOrCreate(format, customNotations);
111
- }
112
- }
113
-
114
- std::shared_ptr<Mask> RNTextInputMask::pickMask(const CaretString &text, MaskOptions maskOptions,
115
- std::string primaryMask) {
116
- // 如果 affineFormats 为空,直接返回 primaryMask
117
- if (maskOptions.affineFormats->size() <= 0) {
118
- auto mask = maskGetOrCreate(primaryMask, maskOptions.customNotations.value(), maskOptions.rightToLeft.value());
119
- int affinity = calculateAffinity(*mask, text, maskOptions.affinityCalculationStrategy.value());
120
- DLOG(INFO) << " ======= pickMask calculateAffinity value: " << affinity
121
- << "\n affinityCalculationStrategy: " << maskOptions.affinityCalculationStrategy.value();
122
- return mask;
123
- }
124
- // 定义 MaskAffinity 结构体,用于存储 Mask 和相应的亲和度
125
- struct MaskAffinity {
126
- Mask mask; // Mask 对象
127
- int affinity; // 亲和度
128
- MaskAffinity(const Mask &m, int a) : mask(m), affinity(a) {}
129
- };
130
-
131
- // 计算 primaryMask 的亲和度
132
- int primaryAffinity = calculateAffinity(primaryMask, text, maskOptions.affinityCalculationStrategy.value());
133
- DLOG(INFO) << " ======= pickMask calculateAffinity value: " << primaryAffinity << " \n mask: " << primaryMask
134
- << "\n affinityCalculationStrategy: " << maskOptions.affinityCalculationStrategy.value();
135
- // 存储所有 mask 和亲和度的列表
136
- std::vector<MaskAffinity> masksAndAffinities;
137
-
138
- // 遍历 affineFormats 以获取每个格式的 Mask 和亲和度
139
- for (const auto &format : *maskOptions.affineFormats) {
140
- std::shared_ptr<Mask> mask =
141
- maskGetOrCreate(format, maskOptions.customNotations.value(), maskOptions.rightToLeft.value());
142
- int affinity = calculateAffinity(*mask, text, maskOptions.affinityCalculationStrategy.value());
143
- DLOG(INFO) << " ======= pickMask calculateAffinity value: " << affinity << "\n affineFormat: " << format
144
- << "\n affinityCalculationStrategy: " << maskOptions.affinityCalculationStrategy.value();
145
- masksAndAffinities.emplace_back(*mask, affinity);
146
- }
147
-
148
- // 按亲和度降序排序
149
- std::sort(masksAndAffinities.begin(), masksAndAffinities.end(),
150
- [](const MaskAffinity &a, const MaskAffinity &b) { return a.affinity > b.affinity; });
151
-
152
- // 寻找插入位置
153
- int insertIndex = -1;
154
- for (size_t index = 0; index < masksAndAffinities.size(); ++index) {
155
- if (primaryAffinity >= masksAndAffinities[index].affinity) {
156
- insertIndex = static_cast<int>(index);
157
- break;
158
- }
159
- }
160
-
161
- // 插入 primaryMask
162
- if (insertIndex >= 0) {
163
- masksAndAffinities.insert(masksAndAffinities.begin() + insertIndex, MaskAffinity(primaryMask, primaryAffinity));
164
- } else {
165
- masksAndAffinities.emplace_back(primaryMask, primaryAffinity);
166
- }
167
-
168
- // 返回亲和度最高的 Mask
169
- return std::make_shared<Mask>(masksAndAffinities.front().mask);
170
- }
171
-
172
- void RNTextInputMask::setMask(int reactNode, std::string primaryFormat, MaskOptions maskOptions) {
173
- auto task = [this, reactNode, primaryFormat, maskOptions] {
174
- auto weakInstance = m_ctx.instance;
175
- auto instance = weakInstance.lock();
176
- auto instanceCAPI = std::dynamic_pointer_cast<RNInstanceCAPI>(instance);
177
- if (!instanceCAPI) {
178
- return;
179
- }
180
- auto componentInstance = instanceCAPI->findComponentInstanceByTag(reactNode);
181
- if (!componentInstance) {
182
- return;
183
- }
184
- auto input = std::dynamic_pointer_cast<TextInputComponentInstance>(componentInstance);
185
- if (!input) {
186
- throw std::runtime_error("find ComponentInstance failed,check the reactNode is Valid ");
187
- }
188
- ArkUINode &node = input->getLocalRootArkUINode();
189
- TextInputNode *textInputNode = dynamic_cast<TextInputNode *>(&node);
190
- UserData *userData = new UserData({.data = textInputNode->getArkUINodeHandle(),
191
- .maskOptions = maskOptions,
192
- .primaryFormat = primaryFormat,
193
- .node = reactNode,
194
- .instance = this});
195
- NativeNodeApi::getInstance()->registerNodeEvent(textInputNode->getArkUINodeHandle(), NODE_TEXT_INPUT_ON_CHANGE,
196
- 110, textInputNode);
197
- NativeNodeApi::getInstance()->registerNodeEvent(textInputNode->getArkUINodeHandle(), NODE_ON_FOCUS, 111,
198
- textInputNode);
199
- this->m_userDatas.insert(userData);
200
- NativeNodeApi::getInstance()->setUserData(textInputNode->getArkUINodeHandle(), userData);
201
- NativeNodeApi::getInstance()->addNodeEventReceiver(textInputNode->getArkUINodeHandle(), myEventReceiver);
202
- };
203
- this->m_ctx.taskExecutor->runTask(TaskThread::MAIN, std::move(task));
204
- }
205
-
206
- std::string getString(std::string maskValue, std::string value, bool autocomplete, bool isMask) {
207
-
208
- auto maskObj = Mask::MaskFactory::getOrCreate(maskValue, {});
209
- CaretString text(value, value.length(), std::make_shared<CaretString::Forward>(autocomplete));
210
- auto r = maskObj->apply(text);
211
- std::string result;
212
- if (isMask) {
213
- result = r.formattedText.string;
214
- } else {
215
- result = r.extractedValue;
216
- }
217
- return result;
218
- }
219
-
220
- std::string getString(std::string maskValue, std::string value, bool autocomplete, bool isMask, bool rightToLeft) {
221
- std::shared_ptr<Mask> maskObj;
222
- if (rightToLeft) {
223
- maskObj = RTLMask::getOrCreate(maskValue, {});
224
- } else {
225
- maskObj = Mask::MaskFactory::getOrCreate(maskValue, {});
226
- }
227
- CaretString text(value, value.length(), std::make_shared<CaretString::Forward>(autocomplete));
228
- auto r = maskObj->apply(text);
229
- std::string result;
230
- if (isMask) {
231
- result = r.formattedText.string;
232
- } else {
233
- result = r.extractedValue;
234
- }
235
- return result;
236
- }
237
-
238
- static jsi::Value __hostFunction_RNTextInputMask_unmask(jsi::Runtime &rt, react::TurboModule &turboModule,
239
- const jsi::Value *args, size_t count) {
240
- std::string maskValue = args[0].getString(rt).utf8(rt);
241
- std::string value = args[1].getString(rt).utf8(rt);
242
- bool autocomplete = args[2].getBool();
243
- return createPromiseAsJSIValue(
244
- rt, [maskValue, value, autocomplete](jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
245
- try {
246
- auto start = std::chrono::high_resolution_clock::now();
247
- std::string result = getString(maskValue, value, autocomplete, 0);
248
- promise->resolve(jsi::String::createFromUtf8(rt2, result));
249
- // 获取结束时间点
250
- auto end = std::chrono::high_resolution_clock::now();
251
- // 计算延迟
252
- std::chrono::duration<double, std::milli> latency = end - start;
253
- DLOG(INFO) << "=======unmask 响应时长: " << latency.count() << " 毫秒" << std::endl;
254
- } catch (FormatError e) {
255
- promise->reject(e.what());
256
- }
257
- });
258
- }
259
-
260
- static jsi::Value __hostFunction_RNTextInputMask_unmaskWithRightToLeft(jsi::Runtime &rt, react::TurboModule &turboModule,
261
- const jsi::Value *args, size_t count) {
262
- std::string maskValue = args[0].getString(rt).utf8(rt);
263
- std::string value = args[1].getString(rt).utf8(rt);
264
- bool autocomplete = args[2].getBool();
265
- bool rightToLeft = args[3].getBool();
266
- return createPromiseAsJSIValue(
267
- rt, [maskValue, value, autocomplete, rightToLeft](jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
268
- try {
269
- auto start = std::chrono::high_resolution_clock::now();
270
- std::string result = getString(maskValue, value, autocomplete, 0, rightToLeft);
271
- promise->resolve(jsi::String::createFromUtf8(rt2, result));
272
- // 获取结束时间点
273
- auto end = std::chrono::high_resolution_clock::now();
274
- // 计算延迟
275
- std::chrono::duration<double, std::milli> latency = end - start;
276
- DLOG(INFO) << "=======unmask 响应时长: " << latency.count() << " 毫秒" << std::endl;
277
- } catch (FormatError e) {
278
- promise->reject(e.what());
279
- }
280
- });
281
- }
282
-
283
- static jsi::Value __hostFunction_RNTextInputMask_mask(jsi::Runtime &rt, react::TurboModule &turboModule,
284
- const jsi::Value *args, size_t count) {
285
- std::string maskValue = args[0].getString(rt).utf8(rt);
286
- std::string value = args[1].getString(rt).utf8(rt);
287
- bool autocomplete = args[2].getBool();
288
- return createPromiseAsJSIValue(
289
- rt, [maskValue, value, autocomplete](jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
290
- try {
291
- auto start = std::chrono::high_resolution_clock::now();
292
- std::string result = getString(maskValue, value, autocomplete, 1);
293
- promise->resolve(jsi::String::createFromUtf8(rt2, result));
294
- // 获取结束时间点
295
- auto end = std::chrono::high_resolution_clock::now();
296
- // 计算延迟
297
- std::chrono::duration<double, std::milli> latency = end - start;
298
- DLOG(INFO) << "=======mask 响应时长: " << latency.count() << " 毫秒" << std::endl;
299
- } catch (FormatError e) {
300
- promise->reject(e.what());
301
- }
302
- });
303
- }
304
-
305
- static jsi::Value __hostFunction_RNTextInputMask_setMask(jsi::Runtime &rt, react::TurboModule &turboModule,
306
- const jsi::Value *args, size_t count) {
307
-
308
- auto turbo = static_cast<RNTextInputMask *>(&turboModule);
309
- if (turbo->grt == nullptr) {
310
- turbo->grt = &rt;
311
- }
312
- int reactNode = args[0].getNumber();
313
- std::string primaryFormat = args[1].getString(rt).utf8(rt);
314
- jsi::Object obj = args[2].asObject(rt);
315
- std::vector<std::string> affineFormatsValues; // 用于存储数组的值
316
- if (obj.hasProperty(rt, "affineFormats") && !obj.getProperty(rt, "affineFormats").isUndefined()) {
317
- jsi::Object affineFormats = obj.getProperty(rt, "affineFormats").asObject(rt);
318
- // 确保它是一个数组
319
- if (affineFormats.isArray(rt)) {
320
- // 获取数组的长度
321
- jsi::Array arrayAffineFormats = affineFormats.asArray(rt);
322
- size_t length = arrayAffineFormats.size(rt);
323
- // 遍历数组
324
- for (size_t i = 0; i < length; ++i) {
325
- // 获取数组元素
326
- jsi::Value value = arrayAffineFormats.getValueAtIndex(rt, i);
327
- std::cout << "===affineFormats index" + std::to_string(i) << "--" << value.getString(rt).utf8(rt)
328
- << std::endl;
329
- affineFormatsValues.push_back(value.getString(rt).utf8(rt));
330
- }
331
- } else {
332
- std::cerr << "====The property 'myArray' is not an array." << std::endl;
333
- }
334
- }
335
- std::vector<Notation> customNotationsValues;
336
- if (obj.hasProperty(rt, "customNotations") && !obj.getProperty(rt, "customNotations").isUndefined()) {
337
- jsi::Object customNotations = obj.getProperty(rt, "customNotations").asObject(rt);
338
- // 确保它是一个数组
339
- if (customNotations.isArray(rt)) {
340
- // 获取数组的长度
341
- jsi::Array arrayCustomNotations = customNotations.asArray(rt);
342
- size_t length = arrayCustomNotations.size(rt);
343
- // 遍历数组
344
- for (size_t i = 0; i < length; ++i) {
345
- // 获取数组元素
346
- jsi::Object value = arrayCustomNotations.getValueAtIndex(rt, i).asObject(rt);
347
- std::string character;
348
- if (value.hasProperty(rt, "character")) {
349
- character = value.getProperty(rt, "character").getString(rt).utf8(rt);
350
- }
351
- std::string characterSet;
352
- if (value.hasProperty(rt, "character")) {
353
- characterSet = value.getProperty(rt, "characterSet").getString(rt).utf8(rt);
354
- }
355
- bool isOptional;
356
- if (value.hasProperty(rt, "isOptional")) {
357
- isOptional = value.getProperty(rt, "isOptional").getBool();
358
- }
359
- Notation notation(character[0], characterSet, isOptional);
360
- customNotationsValues.push_back(notation);
361
- }
362
- } else {
363
- std::cerr << "====The property 'myArray' is not an array." << std::endl;
364
- }
365
- }
366
-
367
-
368
- std::string affinityCalculationStrategy;
369
- if (obj.hasProperty(rt, "affinityCalculationStrategy") &&
370
- !obj.getProperty(rt, "affinityCalculationStrategy").isUndefined()) {
371
- affinityCalculationStrategy = obj.getProperty(rt, "affinityCalculationStrategy").asString(rt).utf8(rt);
372
- }
373
-
374
- bool autocomplete = true;
375
- if (obj.hasProperty(rt, "autocomplete") && !obj.getProperty(rt, "autocomplete").isUndefined()) {
376
- autocomplete = obj.getProperty(rt, "autocomplete").asBool();
377
- }
378
-
379
- bool autoskip = false;
380
- if (obj.hasProperty(rt, "autoskip") && !obj.getProperty(rt, "autoskip").isUndefined()) {
381
- autoskip = obj.getProperty(rt, "autoskip").asBool();
382
- }
383
-
384
- bool rightToLeft = false;
385
- if (obj.hasProperty(rt, "rightToLeft") && !obj.getProperty(rt, "rightToLeft").isUndefined()) {
386
- rightToLeft = obj.getProperty(rt, "rightToLeft").asBool();
387
- }
388
-
389
- auto maskOptions = new MaskOptions(affineFormatsValues, customNotationsValues, affinityCalculationStrategy,
390
- autocomplete, autoskip, rightToLeft);
391
- static_cast<RNTextInputMask *>(&turboModule)->setMask(reactNode, primaryFormat, *maskOptions);
392
- return jsi::Value::undefined();
393
- }
394
-
395
- RNTextInputMask::RNTextInputMask(const ArkTSTurboModule::Context ctx, const std::string name)
396
- : ArkTSTurboModule(ctx, name) {
397
- methodMap_["setMask"] = MethodMetadata{3, __hostFunction_RNTextInputMask_setMask};
398
- methodMap_["mask"] = MethodMetadata{3, __hostFunction_RNTextInputMask_mask};
399
- methodMap_["unmask"] = MethodMetadata{3, __hostFunction_RNTextInputMask_unmask};
400
- methodMap_["unmaskWithRightToLeft"] = MethodMetadata{4, __hostFunction_RNTextInputMask_unmaskWithRightToLeft};
401
- }
402
-
403
- RNTextInputMask::~RNTextInputMask() {
404
- for (auto userData : m_userDatas) {
405
- if (userData != nullptr) {
406
- NativeNodeApi::getInstance()->unregisterNodeEvent(userData->data, NODE_TEXT_INPUT_ON_CHANGE);
407
- NativeNodeApi::getInstance()->unregisterNodeEvent(userData->data, NODE_ON_FOCUS);
408
- NativeNodeApi::getInstance()->removeNodeEventReceiver(userData->data, myEventReceiver);
409
- delete userData;
410
- userData = nullptr;
411
- }
412
- }
413
- }
414
-
415
- // namespace rnoh
1
+ /*
2
+ * Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved
3
+ * Use of this source code is governed by a MIT license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+
7
+ #include "RNTextInputMask.h"
8
+ #include "RNOH/arkui/TextInputNode.h"
9
+ #include "RNOH/ComponentInstance.h"
10
+ #include "RNOH/RNInstanceCAPI.h"
11
+ #include "RNOHCorePackage/ComponentInstances/TextInputComponentInstance.h"
12
+ #include "common/model/AffinityCalculationStrategy.h"
13
+
14
+ #include <cstdint>
15
+ #include <iostream>
16
+ #include <jsi/jsi.h>
17
+ #include <string>
18
+ #include <thread>
19
+
20
+ using namespace facebook;
21
+ using namespace rnoh;
22
+ using namespace react;
23
+ using namespace TinpMask;
24
+ static constexpr int AVOIDENCE = 1;
25
+ std::unordered_map<std::string, std::shared_ptr<RTLMask>> TinpMask::RTLMask::cache;
26
+ std::unordered_map<std::string, std::shared_ptr<Mask>> TinpMask::Mask::MaskFactory::maskCache;
27
+ void maybeThrow(int32_t status) {
28
+ DLOG(INFO) << "=====text change maybeThrow status: " << status;
29
+ if (status != 0) {
30
+ auto message = std::string("ArkUINode operation failed with status: ") + std::to_string(status);
31
+ LOG(ERROR) << message;
32
+ throw std::runtime_error(std::move(message));
33
+ }
34
+ }
35
+
36
+ void myEventReceiver(ArkUI_NodeEvent *event) {
37
+ int32_t eventId = OH_ArkUI_NodeEvent_GetTargetId(event);
38
+ ArkUI_NodeHandle textNode = OH_ArkUI_NodeEvent_GetNodeHandle(event);
39
+ void *data = NativeNodeApi::getInstance()->getUserData(textNode);
40
+ auto item = NativeNodeApi::getInstance()->getAttribute(textNode, NODE_TEXT_INPUT_TEXT);
41
+ std::string content = item->string;
42
+ UserData *userData = reinterpret_cast<UserData *>(data);
43
+ auto self = userData->instance;
44
+ if (self == nullptr) { return; };
45
+ bool isDelete = userData->lastInputText.size() >= content.size();
46
+ bool useAutocomplete = !isDelete ? userData->maskOptions.autocomplete.value() : false;
47
+ bool useAutoskip = isDelete ? userData->maskOptions.autoskip.value() : false;
48
+
49
+ // onChange 事件
50
+ if (eventId == userData->node) {
51
+ std::shared_ptr<CaretString::CaretGravity> caretGravity = nullptr;
52
+ if (isDelete) {
53
+ caretGravity = std::make_shared<CaretString::Backward>(useAutoskip);
54
+ } else {
55
+ caretGravity = std::make_shared<CaretString::Forward>(useAutocomplete);
56
+ }
57
+ CaretString text(content, content.length(), caretGravity);
58
+ try {
59
+ auto maskObj = self->pickMask(text, userData->maskOptions, userData->primaryFormat);
60
+ auto result = maskObj->apply(text);
61
+ std::string resultString = result.formattedText.string;
62
+ DLOG(INFO) << "mask result complete: " << result.complete;
63
+ userData->lastInputText = resultString;
64
+ if (isDelete) {
65
+ content = resultString;
66
+ }
67
+ std::string finalString = isDelete ? content : resultString;
68
+ ArkUI_AttributeItem item{.string = finalString.c_str()};
69
+ userData->lastInputText = finalString;
70
+ maybeThrow(NativeNodeApi::getInstance()->setAttribute(userData->data, NODE_TEXT_INPUT_TEXT, &item));
71
+ } catch (FormatError e) {
72
+ DLOG(ERROR) << " mask complier error " << e.what();
73
+ }
74
+ }
75
+ // onFocus 事件
76
+ if (eventId == 111) {
77
+ if (userData->maskOptions.autocomplete.value()) {
78
+ std::string text = "";
79
+ text += content;
80
+ try {
81
+ CaretString string(text, text.length(),
82
+ std::make_shared<CaretString::Forward>(userData->maskOptions.autocomplete.value()));
83
+ auto maskObj = self->pickMask(string, userData->maskOptions, userData->primaryFormat);
84
+ std::string resultString = maskObj->apply(string).formattedText.string;
85
+ ArkUI_AttributeItem item{.string = resultString.c_str()};
86
+ maybeThrow(NativeNodeApi::getInstance()->setAttribute(userData->data, NODE_TEXT_INPUT_TEXT, &item));
87
+ } catch (FormatError e) {
88
+ DLOG(ERROR) << " mask complier error " << e.what();
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ // 计算亲和度
95
+ int calculateAffinity(Mask mask, const CaretString &text, std::string affinityCalculationStrategy) {
96
+ AffinityCalculationStrategy strategy;
97
+ if (affinityCalculationStrategy == "WHOLE_STRING") {
98
+ strategy = AffinityCalculationStrategy::WHOLE_STRING;
99
+ } else if (affinityCalculationStrategy == "PREFIX") {
100
+ strategy = AffinityCalculationStrategy::PREFIX;
101
+ } else if (affinityCalculationStrategy == "CAPACITY") {
102
+ strategy = AffinityCalculationStrategy::CAPACITY;
103
+ } else {
104
+ strategy = AffinityCalculationStrategy::EXTRACTED_VALUE_CAPACITY;
105
+ }
106
+ int affinity = AffinityCalculator::calculateAffinityOfMask(strategy, mask, text);
107
+
108
+ return affinity;
109
+ }
110
+ // 获取或创建 Mask
111
+ std::shared_ptr<Mask> maskGetOrCreate(const std::string &format, const std::vector<Notation> &customNotations,
112
+ bool rightToLeft) {
113
+ if (rightToLeft) {
114
+ return RTLMask::getOrCreate(format, customNotations);
115
+ } else {
116
+ return Mask::MaskFactory::getOrCreate(format, customNotations);
117
+ }
118
+ }
119
+
120
+ std::shared_ptr<Mask> RNTextInputMask::pickMask(const CaretString &text, MaskOptions maskOptions,
121
+ std::string primaryMask) {
122
+ // 如果 affineFormats 为空,直接返回 primaryMask
123
+ if (maskOptions.affineFormats->size() <= 0) {
124
+ auto mask = maskGetOrCreate(primaryMask, maskOptions.customNotations.value(), maskOptions.rightToLeft.value());
125
+ int affinity = calculateAffinity(*mask, text, maskOptions.affinityCalculationStrategy.value());
126
+ DLOG(INFO) << " ======= pickMask calculateAffinity value: " << affinity
127
+ << "\n affinityCalculationStrategy: " << maskOptions.affinityCalculationStrategy.value();
128
+ return mask;
129
+ }
130
+ // 定义 MaskAffinity 结构体,用于存储 Mask 和相应的亲和度
131
+ struct MaskAffinity {
132
+ Mask mask; // Mask 对象
133
+ int affinity; // 亲和度
134
+ MaskAffinity(const Mask &m, int a) : mask(m), affinity(a) {}
135
+ };
136
+
137
+ // 计算 primaryMask 的亲和度
138
+ int primaryAffinity = calculateAffinity(primaryMask, text, maskOptions.affinityCalculationStrategy.value());
139
+ DLOG(INFO) << " ======= pickMask calculateAffinity value: " << primaryAffinity << " \n mask: " << primaryMask
140
+ << "\n affinityCalculationStrategy: " << maskOptions.affinityCalculationStrategy.value();
141
+ // 存储所有 mask 和亲和度的列表
142
+ std::vector<MaskAffinity> masksAndAffinities;
143
+
144
+ // 遍历 affineFormats 以获取每个格式的 Mask 和亲和度
145
+ for (const auto &format : *maskOptions.affineFormats) {
146
+ std::shared_ptr<Mask> mask =
147
+ maskGetOrCreate(format, maskOptions.customNotations.value(), maskOptions.rightToLeft.value());
148
+ int affinity = calculateAffinity(*mask, text, maskOptions.affinityCalculationStrategy.value());
149
+ DLOG(INFO) << " ======= pickMask calculateAffinity value: " << affinity << "\n affineFormat: " << format
150
+ << "\n affinityCalculationStrategy: " << maskOptions.affinityCalculationStrategy.value();
151
+ masksAndAffinities.emplace_back(*mask, affinity);
152
+ }
153
+
154
+ // 按亲和度降序排序
155
+ std::sort(masksAndAffinities.begin(), masksAndAffinities.end(),
156
+ [](const MaskAffinity &a, const MaskAffinity &b) { return a.affinity > b.affinity; });
157
+
158
+ // 寻找插入位置
159
+ int insertIndex = -1;
160
+ for (size_t index = 0; index < masksAndAffinities.size(); ++index) {
161
+ if (primaryAffinity >= masksAndAffinities[index].affinity) {
162
+ insertIndex = static_cast<int>(index);
163
+ break;
164
+ }
165
+ }
166
+
167
+ // 插入 primaryMask
168
+ if (insertIndex >= 0) {
169
+ masksAndAffinities.insert(masksAndAffinities.begin() + insertIndex, MaskAffinity(primaryMask, primaryAffinity));
170
+ } else {
171
+ masksAndAffinities.emplace_back(primaryMask, primaryAffinity);
172
+ }
173
+
174
+ // 返回亲和度最高的 Mask
175
+ return std::make_shared<Mask>(masksAndAffinities.front().mask);
176
+ }
177
+
178
+ void RNTextInputMask::setMask(int reactNode, std::string primaryFormat, MaskOptions maskOptions) {
179
+ auto task = [this, reactNode, primaryFormat, maskOptions] {
180
+ auto weakInstance = m_ctx.instance;
181
+ auto instance = weakInstance.lock();
182
+ auto instanceCAPI = std::dynamic_pointer_cast<RNInstanceCAPI>(instance);
183
+ if (!instanceCAPI) {
184
+ return;
185
+ }
186
+ auto componentInstance = instanceCAPI->findComponentInstanceByTag(reactNode);
187
+ if (!componentInstance) {
188
+ return;
189
+ }
190
+ auto input = std::dynamic_pointer_cast<TextInputComponentInstance>(componentInstance);
191
+ if (!input) {
192
+ throw std::runtime_error("find ComponentInstance failed,check the reactNode is Valid ");
193
+ }
194
+ ArkUINode &node = input->getLocalRootArkUINode();
195
+ TextInputNode *textInputNode = dynamic_cast<TextInputNode *>(&node);
196
+ UserData *userData = new UserData({.data = textInputNode->getArkUINodeHandle(),
197
+ .maskOptions = maskOptions,
198
+ .primaryFormat = primaryFormat,
199
+ .node = reactNode,
200
+ .instance = this});
201
+ NativeNodeApi::getInstance()->registerNodeEvent(textInputNode->getArkUINodeHandle(), NODE_TEXT_INPUT_ON_CHANGE,
202
+ reactNode, textInputNode);
203
+ NativeNodeApi::getInstance()->registerNodeEvent(textInputNode->getArkUINodeHandle(), NODE_ON_FOCUS, 111,
204
+ textInputNode);
205
+ this->m_userDatas.insert(userData);
206
+ NativeNodeApi::getInstance()->setUserData(textInputNode->getArkUINodeHandle(), userData);
207
+ NativeNodeApi::getInstance()->addNodeEventReceiver(textInputNode->getArkUINodeHandle(), myEventReceiver);
208
+ };
209
+ this->m_ctx.taskExecutor->runTask(TaskThread::MAIN, std::move(task));
210
+ }
211
+
212
+ std::string getString(std::string maskValue, std::string value, bool autocomplete, bool isMask) {
213
+
214
+ auto maskObj = Mask::MaskFactory::getOrCreate(maskValue, {});
215
+ CaretString text(value, value.length(), std::make_shared<CaretString::Forward>(autocomplete));
216
+ auto r = maskObj->apply(text);
217
+ std::string result;
218
+ if (isMask) {
219
+ result = r.formattedText.string;
220
+ } else {
221
+ result = r.extractedValue;
222
+ }
223
+ return result;
224
+ }
225
+
226
+ std::string getString(std::string maskValue, std::string value, bool autocomplete, bool isMask, bool rightToLeft) {
227
+ std::shared_ptr<Mask> maskObj;
228
+ if (rightToLeft) {
229
+ maskObj = RTLMask::getOrCreate(maskValue, {});
230
+ } else {
231
+ maskObj = Mask::MaskFactory::getOrCreate(maskValue, {});
232
+ }
233
+ CaretString text(value, value.length(), std::make_shared<CaretString::Forward>(autocomplete));
234
+ auto r = maskObj->apply(text);
235
+ std::string result;
236
+ if (isMask) {
237
+ result = r.formattedText.string;
238
+ } else {
239
+ result = r.extractedValue;
240
+ }
241
+ return result;
242
+ }
243
+
244
+ static jsi::Value __hostFunction_RNTextInputMask_unmask(jsi::Runtime &rt, react::TurboModule &turboModule,
245
+ const jsi::Value *args, size_t count) {
246
+ std::string maskValue = args[0].getString(rt).utf8(rt);
247
+ std::string value = args[1].getString(rt).utf8(rt);
248
+ bool autocomplete = args[2].getBool();
249
+ return createPromiseAsJSIValue(
250
+ rt, [maskValue, value, autocomplete](jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
251
+ try {
252
+ auto start = std::chrono::high_resolution_clock::now();
253
+ std::string result = getString(maskValue, value, autocomplete, 0);
254
+ promise->resolve(jsi::String::createFromUtf8(rt2, result));
255
+ // 获取结束时间点
256
+ auto end = std::chrono::high_resolution_clock::now();
257
+ // 计算延迟
258
+ std::chrono::duration<double, std::milli> latency = end - start;
259
+ DLOG(INFO) << "=======unmask 响应时长: " << latency.count() << " 毫秒" << std::endl;
260
+ } catch (FormatError e) {
261
+ promise->reject(e.what());
262
+ }
263
+ });
264
+ }
265
+
266
+ static jsi::Value __hostFunction_RNTextInputMask_unmaskWithRightToLeft(jsi::Runtime &rt, react::TurboModule &turboModule,
267
+ const jsi::Value *args, size_t count) {
268
+ std::string maskValue = args[0].getString(rt).utf8(rt);
269
+ std::string value = args[1].getString(rt).utf8(rt);
270
+ bool autocomplete = args[2].getBool();
271
+ bool rightToLeft = args[3].getBool();
272
+ return createPromiseAsJSIValue(
273
+ rt, [maskValue, value, autocomplete, rightToLeft](jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
274
+ try {
275
+ auto start = std::chrono::high_resolution_clock::now();
276
+ std::string result = getString(maskValue, value, autocomplete, 0, rightToLeft);
277
+ promise->resolve(jsi::String::createFromUtf8(rt2, result));
278
+ // 获取结束时间点
279
+ auto end = std::chrono::high_resolution_clock::now();
280
+ // 计算延迟
281
+ std::chrono::duration<double, std::milli> latency = end - start;
282
+ DLOG(INFO) << "=======unmask 响应时长: " << latency.count() << " 毫秒" << std::endl;
283
+ } catch (FormatError e) {
284
+ promise->reject(e.what());
285
+ }
286
+ });
287
+ }
288
+
289
+ static jsi::Value __hostFunction_RNTextInputMask_mask(jsi::Runtime &rt, react::TurboModule &turboModule,
290
+ const jsi::Value *args, size_t count) {
291
+ std::string maskValue = args[0].getString(rt).utf8(rt);
292
+ std::string value = args[1].getString(rt).utf8(rt);
293
+ bool autocomplete = args[2].getBool();
294
+ return createPromiseAsJSIValue(
295
+ rt, [maskValue, value, autocomplete](jsi::Runtime &rt2, std::shared_ptr<facebook::react::Promise> promise) {
296
+ try {
297
+ auto start = std::chrono::high_resolution_clock::now();
298
+ std::string result = getString(maskValue, value, autocomplete, 1);
299
+ promise->resolve(jsi::String::createFromUtf8(rt2, result));
300
+ // 获取结束时间点
301
+ auto end = std::chrono::high_resolution_clock::now();
302
+ // 计算延迟
303
+ std::chrono::duration<double, std::milli> latency = end - start;
304
+ DLOG(INFO) << "=======mask 响应时长: " << latency.count() << " 毫秒" << std::endl;
305
+ } catch (FormatError e) {
306
+ promise->reject(e.what());
307
+ }
308
+ });
309
+ }
310
+
311
+ static jsi::Value __hostFunction_RNTextInputMask_setMask(jsi::Runtime &rt, react::TurboModule &turboModule,
312
+ const jsi::Value *args, size_t count) {
313
+
314
+ auto turbo = static_cast<RNTextInputMask *>(&turboModule);
315
+ if (turbo->grt == nullptr) {
316
+ turbo->grt = &rt;
317
+ }
318
+ int reactNode = args[0].getNumber();
319
+ std::string primaryFormat = args[1].getString(rt).utf8(rt);
320
+ jsi::Object obj = args[2].asObject(rt);
321
+ std::vector<std::string> affineFormatsValues; // 用于存储数组的值
322
+ if (obj.hasProperty(rt, "affineFormats") && !obj.getProperty(rt, "affineFormats").isUndefined()) {
323
+ jsi::Object affineFormats = obj.getProperty(rt, "affineFormats").asObject(rt);
324
+ // 确保它是一个数组
325
+ if (affineFormats.isArray(rt)) {
326
+ // 获取数组的长度
327
+ jsi::Array arrayAffineFormats = affineFormats.asArray(rt);
328
+ size_t length = arrayAffineFormats.size(rt);
329
+ // 遍历数组
330
+ for (size_t i = 0; i < length; ++i) {
331
+ // 获取数组元素
332
+ jsi::Value value = arrayAffineFormats.getValueAtIndex(rt, i);
333
+ std::cout << "===affineFormats index" + std::to_string(i) << "--" << value.getString(rt).utf8(rt)
334
+ << std::endl;
335
+ affineFormatsValues.push_back(value.getString(rt).utf8(rt));
336
+ }
337
+ } else {
338
+ std::cerr << "====The property 'myArray' is not an array." << std::endl;
339
+ }
340
+ }
341
+ std::vector<Notation> customNotationsValues;
342
+ if (obj.hasProperty(rt, "customNotations") && !obj.getProperty(rt, "customNotations").isUndefined()) {
343
+ jsi::Object customNotations = obj.getProperty(rt, "customNotations").asObject(rt);
344
+ // 确保它是一个数组
345
+ if (customNotations.isArray(rt)) {
346
+ // 获取数组的长度
347
+ jsi::Array arrayCustomNotations = customNotations.asArray(rt);
348
+ size_t length = arrayCustomNotations.size(rt);
349
+ // 遍历数组
350
+ for (size_t i = 0; i < length; ++i) {
351
+ // 获取数组元素
352
+ jsi::Object value = arrayCustomNotations.getValueAtIndex(rt, i).asObject(rt);
353
+ std::string character;
354
+ if (value.hasProperty(rt, "character")) {
355
+ character = value.getProperty(rt, "character").getString(rt).utf8(rt);
356
+ }
357
+ std::string characterSet;
358
+ if (value.hasProperty(rt, "character")) {
359
+ characterSet = value.getProperty(rt, "characterSet").getString(rt).utf8(rt);
360
+ }
361
+ bool isOptional;
362
+ if (value.hasProperty(rt, "isOptional")) {
363
+ isOptional = value.getProperty(rt, "isOptional").getBool();
364
+ }
365
+ Notation notation(character[0], characterSet, isOptional);
366
+ customNotationsValues.push_back(notation);
367
+ }
368
+ } else {
369
+ std::cerr << "====The property 'myArray' is not an array." << std::endl;
370
+ }
371
+ }
372
+
373
+
374
+ std::string affinityCalculationStrategy;
375
+ if (obj.hasProperty(rt, "affinityCalculationStrategy") &&
376
+ !obj.getProperty(rt, "affinityCalculationStrategy").isUndefined()) {
377
+ affinityCalculationStrategy = obj.getProperty(rt, "affinityCalculationStrategy").asString(rt).utf8(rt);
378
+ }
379
+
380
+ bool autocomplete = true;
381
+ if (obj.hasProperty(rt, "autocomplete") && !obj.getProperty(rt, "autocomplete").isUndefined()) {
382
+ autocomplete = obj.getProperty(rt, "autocomplete").asBool();
383
+ }
384
+
385
+ bool autoskip = false;
386
+ if (obj.hasProperty(rt, "autoskip") && !obj.getProperty(rt, "autoskip").isUndefined()) {
387
+ autoskip = obj.getProperty(rt, "autoskip").asBool();
388
+ }
389
+
390
+ bool rightToLeft = false;
391
+ if (obj.hasProperty(rt, "rightToLeft") && !obj.getProperty(rt, "rightToLeft").isUndefined()) {
392
+ rightToLeft = obj.getProperty(rt, "rightToLeft").asBool();
393
+ }
394
+
395
+ auto maskOptions = new MaskOptions(affineFormatsValues, customNotationsValues, affinityCalculationStrategy,
396
+ autocomplete, autoskip, rightToLeft);
397
+ static_cast<RNTextInputMask *>(&turboModule)->setMask(reactNode, primaryFormat, *maskOptions);
398
+ return jsi::Value::undefined();
399
+ }
400
+
401
+ RNTextInputMask::RNTextInputMask(const ArkTSTurboModule::Context ctx, const std::string name)
402
+ : ArkTSTurboModule(ctx, name) {
403
+ methodMap_["setMask"] = MethodMetadata{3, __hostFunction_RNTextInputMask_setMask};
404
+ methodMap_["mask"] = MethodMetadata{3, __hostFunction_RNTextInputMask_mask};
405
+ methodMap_["unmask"] = MethodMetadata{3, __hostFunction_RNTextInputMask_unmask};
406
+ methodMap_["unmaskWithRightToLeft"] = MethodMetadata{4, __hostFunction_RNTextInputMask_unmaskWithRightToLeft};
407
+ }
408
+
409
+ RNTextInputMask::~RNTextInputMask() {
410
+ for (auto userData : m_userDatas) {
411
+ if (userData != nullptr) {
412
+ NativeNodeApi::getInstance()->unregisterNodeEvent(userData->data, NODE_TEXT_INPUT_ON_CHANGE);
413
+ NativeNodeApi::getInstance()->unregisterNodeEvent(userData->data, NODE_ON_FOCUS);
414
+ NativeNodeApi::getInstance()->removeNodeEventReceiver(userData->data, myEventReceiver);
415
+ delete userData;
416
+ userData = nullptr;
417
+ }
418
+ }
419
+ }
420
+
421
+ // namespace rnoh