@jincheng_1995/react-native-device-info-plus 1.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/.kiro/steering/rn-plugin-development.md +456 -0
- package/LICENSE +21 -0
- package/PUBLISHING.md +385 -0
- package/README.md +111 -0
- package/android/build.gradle +50 -0
- package/ios/DeviceInfoPlus.h +15 -0
- package/ios/DeviceInfoPlus.mm +283 -0
- package/ios/DeviceInfoPlus.podspec +33 -0
- package/lib/commonjs/NativeDeviceInfoPlus.js +9 -0
- package/lib/commonjs/NativeDeviceInfoPlus.js.map +1 -0
- package/lib/commonjs/index.js +124 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/module/NativeDeviceInfoPlus.js +3 -0
- package/lib/module/NativeDeviceInfoPlus.js.map +1 -0
- package/lib/module/index.js +117 -0
- package/lib/module/index.js.map +1 -0
- package/lib/typescript/NativeDeviceInfoPlus.d.ts +42 -0
- package/lib/typescript/NativeDeviceInfoPlus.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +35 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
---
|
|
2
|
+
inclusion: always
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# React Native 插件开发指南
|
|
6
|
+
|
|
7
|
+
本文档提供 React Native 原生模块/插件开发的完整指南,适用于新架构(Turbo Modules)。
|
|
8
|
+
|
|
9
|
+
## 项目结构
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
react-native-xxx-plugin/
|
|
13
|
+
├── src/ # TypeScript 源代码
|
|
14
|
+
│ ├── index.tsx # 主入口,导出 API
|
|
15
|
+
│ └── NativeXxx.ts # Turbo Module 接口定义
|
|
16
|
+
├── ios/ # iOS 原生实现
|
|
17
|
+
│ ├── Xxx.h # Objective-C 头文件
|
|
18
|
+
│ ├── Xxx.mm # Objective-C++ 实现
|
|
19
|
+
│ └── Xxx.podspec # CocoaPods 配置
|
|
20
|
+
├── android/ # Android 原生实现
|
|
21
|
+
│ ├── build.gradle # Gradle 构建配置
|
|
22
|
+
│ └── src/main/
|
|
23
|
+
│ ├── AndroidManifest.xml # 权限声明
|
|
24
|
+
│ └── java/com/xxx/
|
|
25
|
+
│ ├── XxxModule.kt # 主模块实现
|
|
26
|
+
│ └── XxxPackage.kt # 模块注册
|
|
27
|
+
├── lib/ # 编译输出目录
|
|
28
|
+
├── example/ # 示例应用
|
|
29
|
+
├── package.json # npm 配置
|
|
30
|
+
├── tsconfig.json # TypeScript 配置
|
|
31
|
+
└── tsconfig.build.json # 构建配置
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 开发步骤
|
|
35
|
+
|
|
36
|
+
### 1. TypeScript 接口定义
|
|
37
|
+
|
|
38
|
+
在 `src/NativeXxx.ts` 中定义 Turbo Module 接口:
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import type { TurboModule } from 'react-native';
|
|
42
|
+
import { TurboModuleRegistry } from 'react-native';
|
|
43
|
+
|
|
44
|
+
export interface Spec extends TurboModule {
|
|
45
|
+
// 同步方法
|
|
46
|
+
methodName(param: string): void;
|
|
47
|
+
|
|
48
|
+
// 异步方法(返回 Promise)
|
|
49
|
+
asyncMethod(param: number): Promise<string>;
|
|
50
|
+
|
|
51
|
+
// 复杂类型
|
|
52
|
+
getInfo(): Promise<InfoObject>;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default TurboModuleRegistry.getEnforcing<Spec>('ModuleName');
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 2. TypeScript 实现层
|
|
59
|
+
|
|
60
|
+
在 `src/index.tsx` 中封装原生模块:
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
import { NativeEventEmitter } from 'react-native';
|
|
64
|
+
import NativeXxx from './NativeXxx';
|
|
65
|
+
|
|
66
|
+
class XxxModule {
|
|
67
|
+
async asyncMethod(param: number): Promise<string> {
|
|
68
|
+
return NativeXxx.asyncMethod(param);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
methodName(param: string): void {
|
|
72
|
+
NativeXxx.methodName(param);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default new XxxModule();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3. iOS 原生实现
|
|
80
|
+
|
|
81
|
+
#### 头文件 (Xxx.h)
|
|
82
|
+
|
|
83
|
+
```objective-c
|
|
84
|
+
#import <React/RCTBridgeModule.h>
|
|
85
|
+
#import <React/RCTEventEmitter.h>
|
|
86
|
+
|
|
87
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
88
|
+
#import "RNXxxSpec.h"
|
|
89
|
+
@interface Xxx : RCTEventEmitter <NativeXxxSpec>
|
|
90
|
+
#else
|
|
91
|
+
@interface Xxx : RCTEventEmitter <RCTBridgeModule>
|
|
92
|
+
#endif
|
|
93
|
+
|
|
94
|
+
@end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### 实现文件 (Xxx.mm)
|
|
98
|
+
|
|
99
|
+
```objective-c
|
|
100
|
+
#import "Xxx.h"
|
|
101
|
+
|
|
102
|
+
@implementation Xxx
|
|
103
|
+
|
|
104
|
+
RCT_EXPORT_MODULE()
|
|
105
|
+
|
|
106
|
+
// 同步方法
|
|
107
|
+
RCT_EXPORT_METHOD(methodName:(NSString *)param) {
|
|
108
|
+
// 实现代码
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 异步方法
|
|
112
|
+
RCT_EXPORT_METHOD(asyncMethod:(double)param
|
|
113
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
114
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
115
|
+
// 实现代码
|
|
116
|
+
resolve(@"result");
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 新架构支持
|
|
120
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
121
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
122
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
123
|
+
return std::make_shared<facebook::react::NativeXxxSpecJSI>(params);
|
|
124
|
+
}
|
|
125
|
+
#endif
|
|
126
|
+
|
|
127
|
+
@end
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
#### CocoaPods 配置 (Xxx.podspec)
|
|
131
|
+
|
|
132
|
+
```ruby
|
|
133
|
+
require "json"
|
|
134
|
+
|
|
135
|
+
package = JSON.parse(File.read(File.join(__dir__, "../package.json")))
|
|
136
|
+
|
|
137
|
+
Pod::Spec.new do |s|
|
|
138
|
+
s.name = "Xxx"
|
|
139
|
+
s.version = package["version"]
|
|
140
|
+
s.summary = package["description"]
|
|
141
|
+
s.homepage = package["homepage"]
|
|
142
|
+
s.license = package["license"]
|
|
143
|
+
s.authors = package["author"]
|
|
144
|
+
s.platforms = { :ios => "13.0" }
|
|
145
|
+
s.source = { :git => package["repository"], :tag => "#{s.version}" }
|
|
146
|
+
s.source_files = "*.{h,m,mm}"
|
|
147
|
+
|
|
148
|
+
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
|
|
149
|
+
s.compiler_flags = "-DRCT_NEW_ARCH_ENABLED=1"
|
|
150
|
+
s.pod_target_xcconfig = {
|
|
151
|
+
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
|
152
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
|
153
|
+
}
|
|
154
|
+
s.dependency "React-Codegen"
|
|
155
|
+
s.dependency "RCT-Folly"
|
|
156
|
+
s.dependency "RCTRequired"
|
|
157
|
+
s.dependency "RCTTypeSafety"
|
|
158
|
+
s.dependency "ReactCommon/turbomodule/core"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
s.dependency "React-Core"
|
|
162
|
+
end
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 4. Android 原生实现
|
|
166
|
+
|
|
167
|
+
#### 模块实现 (XxxModule.kt)
|
|
168
|
+
|
|
169
|
+
```kotlin
|
|
170
|
+
package com.xxx
|
|
171
|
+
|
|
172
|
+
import com.facebook.react.bridge.*
|
|
173
|
+
|
|
174
|
+
class XxxModule(reactContext: ReactApplicationContext) :
|
|
175
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
176
|
+
|
|
177
|
+
override fun getName(): String {
|
|
178
|
+
return "Xxx"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
@ReactMethod
|
|
182
|
+
fun methodName(param: String) {
|
|
183
|
+
// 实现代码
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
@ReactMethod
|
|
187
|
+
fun asyncMethod(param: Double, promise: Promise) {
|
|
188
|
+
try {
|
|
189
|
+
val result = "result"
|
|
190
|
+
promise.resolve(result)
|
|
191
|
+
} catch (e: Exception) {
|
|
192
|
+
promise.reject("ERROR", "Error message", e)
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### 包注册 (XxxPackage.kt)
|
|
199
|
+
|
|
200
|
+
```kotlin
|
|
201
|
+
package com.xxx
|
|
202
|
+
|
|
203
|
+
import com.facebook.react.ReactPackage
|
|
204
|
+
import com.facebook.react.bridge.NativeModule
|
|
205
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
206
|
+
import com.facebook.react.uimanager.ViewManager
|
|
207
|
+
|
|
208
|
+
class XxxPackage : ReactPackage {
|
|
209
|
+
override fun createNativeModules(
|
|
210
|
+
reactContext: ReactApplicationContext
|
|
211
|
+
): List<NativeModule> {
|
|
212
|
+
return listOf(XxxModule(reactContext))
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
override fun createViewManagers(
|
|
216
|
+
reactContext: ReactApplicationContext
|
|
217
|
+
): List<ViewManager<*, *>> {
|
|
218
|
+
return emptyList()
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
#### Gradle 配置 (build.gradle)
|
|
224
|
+
|
|
225
|
+
```gradle
|
|
226
|
+
buildscript {
|
|
227
|
+
repositories {
|
|
228
|
+
google()
|
|
229
|
+
mavenCentral()
|
|
230
|
+
}
|
|
231
|
+
dependencies {
|
|
232
|
+
classpath "com.android.tools.build:gradle:7.4.2"
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
apply plugin: "com.android.library"
|
|
237
|
+
|
|
238
|
+
def isNewArchitectureEnabled() {
|
|
239
|
+
return project.hasProperty("newArchEnabled") &&
|
|
240
|
+
project.newArchEnabled == "true"
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
android {
|
|
244
|
+
compileSdkVersion 33
|
|
245
|
+
defaultConfig {
|
|
246
|
+
minSdkVersion 21
|
|
247
|
+
targetSdkVersion 33
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
repositories {
|
|
252
|
+
mavenCentral()
|
|
253
|
+
google()
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
dependencies {
|
|
257
|
+
implementation "com.facebook.react:react-native:+"
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 5. package.json 配置
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"name": "react-native-xxx",
|
|
266
|
+
"version": "1.0.0",
|
|
267
|
+
"description": "描述",
|
|
268
|
+
"main": "lib/commonjs/index",
|
|
269
|
+
"module": "lib/module/index",
|
|
270
|
+
"types": "lib/typescript/index.d.ts",
|
|
271
|
+
"react-native": "src/index",
|
|
272
|
+
"source": "src/index",
|
|
273
|
+
"scripts": {
|
|
274
|
+
"typescript": "tsc --noEmit",
|
|
275
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
276
|
+
"prepare": "bob build"
|
|
277
|
+
},
|
|
278
|
+
"peerDependencies": {
|
|
279
|
+
"react": "*",
|
|
280
|
+
"react-native": "*"
|
|
281
|
+
},
|
|
282
|
+
"devDependencies": {
|
|
283
|
+
"@types/react": "^18.2.0",
|
|
284
|
+
"@types/react-native": "^0.73.0",
|
|
285
|
+
"react": "18.2.0",
|
|
286
|
+
"react-native": "0.73.0",
|
|
287
|
+
"react-native-builder-bob": "^0.23.0",
|
|
288
|
+
"typescript": "^5.3.0"
|
|
289
|
+
},
|
|
290
|
+
"react-native-builder-bob": {
|
|
291
|
+
"source": "src",
|
|
292
|
+
"output": "lib",
|
|
293
|
+
"targets": [
|
|
294
|
+
"commonjs",
|
|
295
|
+
"module",
|
|
296
|
+
["typescript", { "project": "tsconfig.build.json" }]
|
|
297
|
+
]
|
|
298
|
+
},
|
|
299
|
+
"codegenConfig": {
|
|
300
|
+
"name": "RNXxxSpec",
|
|
301
|
+
"type": "modules",
|
|
302
|
+
"jsSrcsDir": "src",
|
|
303
|
+
"android": {
|
|
304
|
+
"javaPackageName": "com.xxx"
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## 常用功能实现
|
|
311
|
+
|
|
312
|
+
### 事件发送(原生 → JS)
|
|
313
|
+
|
|
314
|
+
#### iOS
|
|
315
|
+
```objective-c
|
|
316
|
+
@interface Xxx : RCTEventEmitter
|
|
317
|
+
@end
|
|
318
|
+
|
|
319
|
+
@implementation Xxx
|
|
320
|
+
|
|
321
|
+
- (NSArray<NSString *> *)supportedEvents {
|
|
322
|
+
return @[@"onEvent"];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
- (void)sendEvent {
|
|
326
|
+
[self sendEventWithName:@"onEvent" body:@{@"data": @"value"}];
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
@end
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
#### Android
|
|
333
|
+
```kotlin
|
|
334
|
+
private fun sendEvent(eventName: String, params: WritableMap) {
|
|
335
|
+
reactApplicationContext
|
|
336
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
337
|
+
.emit(eventName, params)
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
#### TypeScript
|
|
342
|
+
```typescript
|
|
343
|
+
const eventEmitter = new NativeEventEmitter(NativeModules.Xxx);
|
|
344
|
+
const subscription = eventEmitter.addListener('onEvent', (data) => {
|
|
345
|
+
console.log(data);
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
// 取消监听
|
|
349
|
+
subscription.remove();
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### 权限处理
|
|
353
|
+
|
|
354
|
+
#### iOS (Info.plist)
|
|
355
|
+
```xml
|
|
356
|
+
<key>NSCameraUsageDescription</key>
|
|
357
|
+
<string>需要访问相机</string>
|
|
358
|
+
<key>NSLocationWhenInUseUsageDescription</key>
|
|
359
|
+
<string>需要访问位置</string>
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### Android (AndroidManifest.xml)
|
|
363
|
+
```xml
|
|
364
|
+
<uses-permission android:name="android.permission.CAMERA" />
|
|
365
|
+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### 线程处理
|
|
369
|
+
|
|
370
|
+
#### iOS - 主线程执行
|
|
371
|
+
```objective-c
|
|
372
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
373
|
+
// UI 操作
|
|
374
|
+
});
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
#### Android - 主线程执行
|
|
378
|
+
```kotlin
|
|
379
|
+
currentActivity?.runOnUiThread {
|
|
380
|
+
// UI 操作
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## 调试技巧
|
|
385
|
+
|
|
386
|
+
### iOS 调试
|
|
387
|
+
```bash
|
|
388
|
+
# 查看日志
|
|
389
|
+
npx react-native log-ios
|
|
390
|
+
|
|
391
|
+
# 清理构建
|
|
392
|
+
cd ios && rm -rf Pods && pod install
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Android 调试
|
|
396
|
+
```bash
|
|
397
|
+
# 查看日志
|
|
398
|
+
npx react-native log-android
|
|
399
|
+
|
|
400
|
+
# 清理构建
|
|
401
|
+
cd android && ./gradlew clean
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### TypeScript 类型检查
|
|
405
|
+
```bash
|
|
406
|
+
npm run typescript
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## 常见问题
|
|
410
|
+
|
|
411
|
+
### 1. 模块未找到
|
|
412
|
+
- iOS: 确保运行了 `pod install`
|
|
413
|
+
- Android: 检查 `MainApplication` 中是否注册了包
|
|
414
|
+
- 重新构建: `npm run prepare`
|
|
415
|
+
|
|
416
|
+
### 2. 类型错误
|
|
417
|
+
- 确保 `tsconfig.json` 中 `types` 包含 `"react-native"`
|
|
418
|
+
- 运行 `npm install` 安装类型定义
|
|
419
|
+
|
|
420
|
+
### 3. 新架构不工作
|
|
421
|
+
- 确保 RN 版本 >= 0.68
|
|
422
|
+
- iOS: 在 Podfile 中设置 `ENV['RCT_NEW_ARCH_ENABLED'] = '1'`
|
|
423
|
+
- Android: 在 gradle.properties 中设置 `newArchEnabled=true`
|
|
424
|
+
|
|
425
|
+
## 发布流程
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
# 1. 构建
|
|
429
|
+
npm run prepare
|
|
430
|
+
|
|
431
|
+
# 2. 测试
|
|
432
|
+
npm run typescript
|
|
433
|
+
npm run lint
|
|
434
|
+
|
|
435
|
+
# 3. 更新版本
|
|
436
|
+
npm version patch # 或 minor, major
|
|
437
|
+
|
|
438
|
+
# 4. 发布
|
|
439
|
+
npm publish
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## 最佳实践
|
|
443
|
+
|
|
444
|
+
1. **类型安全**: 始终使用 TypeScript 定义接口
|
|
445
|
+
2. **错误处理**: Promise 方法要正确处理 reject
|
|
446
|
+
3. **内存管理**: 及时清理监听器和定时器
|
|
447
|
+
4. **权限检查**: 使用敏感功能前检查权限
|
|
448
|
+
5. **文档完善**: 提供清晰的 README 和示例代码
|
|
449
|
+
6. **版本兼容**: 明确支持的 RN 版本范围
|
|
450
|
+
|
|
451
|
+
## 参考资源
|
|
452
|
+
|
|
453
|
+
- [React Native 新架构文档](https://reactnative.dev/docs/the-new-architecture/landing-page)
|
|
454
|
+
- [Turbo Modules 指南](https://reactnative.dev/docs/the-new-architecture/pillars-turbomodules)
|
|
455
|
+
- [iOS 开发文档](https://developer.apple.com/documentation/)
|
|
456
|
+
- [Android 开发文档](https://developer.android.com/docs)
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|