react-native-beidou 1.0.6 → 1.0.8

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.
@@ -0,0 +1,488 @@
1
+ package com.fxzs.rnbeidou;
2
+
3
+ import android.content.Context;
4
+ import android.os.Build;
5
+ import android.util.Log;
6
+ import java.io.File;
7
+ import java.io.FileWriter;
8
+ import java.io.IOException;
9
+ import java.io.PrintWriter;
10
+ import java.io.StringWriter;
11
+ import java.text.SimpleDateFormat;
12
+ import java.util.Date;
13
+ import java.util.Locale;
14
+ import java.util.concurrent.ExecutorService;
15
+ import java.util.concurrent.Executors;
16
+ import java.util.Arrays;
17
+ import java.util.Comparator;
18
+ import java.util.concurrent.atomic.AtomicBoolean;
19
+ import javax.crypto.Cipher;
20
+ import javax.crypto.spec.SecretKeySpec;
21
+ import android.util.Base64;
22
+
23
+ /**
24
+ * 日志管理器 - 统一管理SDK日志
25
+ * 功能:
26
+ * 1. Debug版本:正常输出到Logcat
27
+ * 2. Release版本:禁止Logcat输出,写入文件
28
+ * 3. 支持日志文件上传
29
+ * 4. 支持crash日志捕获
30
+ * 5. 支持日志加密
31
+ */
32
+ public class LogManager {
33
+ private static final String TAG = "BeiDouLogManager";
34
+ private static LogManager instance;
35
+ private static final Object lock = new Object();
36
+
37
+ // 日志级别
38
+ public enum LogLevel {
39
+ VERBOSE(2), DEBUG(3), INFO(4), WARN(5), ERROR(6), ASSERT(7);
40
+
41
+ private final int priority;
42
+
43
+ LogLevel(int priority) {
44
+ this.priority = priority;
45
+ }
46
+
47
+ public int getPriority() {
48
+ return priority;
49
+ }
50
+ }
51
+
52
+ // 配置参数
53
+ private boolean isDebugMode;
54
+ private boolean enableFileLog;
55
+ private boolean enableConsoleLog;
56
+ private boolean enableEncryption;
57
+ private String logDir;
58
+ private String currentLogFile;
59
+ private long maxFileSize = 10 * 1024 * 1024; // 10MB
60
+ private int maxFileCount = 5;
61
+ private String encryptionKey;
62
+
63
+ // 异步写入
64
+ private ExecutorService logExecutor;
65
+ private AtomicBoolean isInitialized = new AtomicBoolean(false);
66
+
67
+ // 日期格式化
68
+ private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault());
69
+ private SimpleDateFormat fileNameFormat = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
70
+
71
+ private LogManager() {
72
+ logExecutor = Executors.newSingleThreadExecutor();
73
+ }
74
+
75
+ public static LogManager getInstance() {
76
+ if (instance == null) {
77
+ synchronized (lock) {
78
+ if (instance == null) {
79
+ instance = new LogManager();
80
+ }
81
+ }
82
+ }
83
+ return instance;
84
+ }
85
+
86
+ /**
87
+ * 初始化日志管理器
88
+ *
89
+ * @param context 应用上下文
90
+ * @param isDebug 是否为Debug模式
91
+ * @param enableFileLog 是否启用文件日志
92
+ * @param encryptionKey 加密密钥(可选)
93
+ */
94
+ public void initialize(Context context, boolean isDebug, boolean enableFileLog, String encryptionKey) {
95
+ if (isInitialized.get()) {
96
+ return;
97
+ }
98
+
99
+ this.isDebugMode = isDebug;
100
+ this.enableFileLog = enableFileLog;
101
+ this.enableConsoleLog = isDebug; // Debug模式才输出到控制台
102
+ this.enableEncryption = encryptionKey != null && !encryptionKey.isEmpty();
103
+ this.encryptionKey = encryptionKey;
104
+
105
+ // 设置日志目录
106
+ File filesDir = context.getFilesDir();
107
+ this.logDir = new File(filesDir, "beidou_logs").getAbsolutePath();
108
+
109
+ // 创建日志目录
110
+ File logDirFile = new File(logDir);
111
+ if (!logDirFile.exists()) {
112
+ logDirFile.mkdirs();
113
+ }
114
+
115
+ // 设置当前日志文件
116
+ updateCurrentLogFile();
117
+
118
+ // 清理旧日志文件
119
+ cleanOldLogs();
120
+
121
+ // 设置未捕获异常处理器
122
+ setupUncaughtExceptionHandler();
123
+
124
+ isInitialized.set(true);
125
+
126
+ info(TAG, "LogManager initialized - Debug: " + isDebug + ", FileLog: " + enableFileLog +
127
+ ", Encryption: " + enableEncryption + ", LogDir: " + logDir);
128
+ }
129
+
130
+ /**
131
+ * 更新当前日志文件路径
132
+ */
133
+ private void updateCurrentLogFile() {
134
+ String today = fileNameFormat.format(new Date());
135
+ currentLogFile = logDir + File.separator + "beidou_" + today + ".log";
136
+ }
137
+
138
+ /**
139
+ * 设置未捕获异常处理器
140
+ */
141
+ private void setupUncaughtExceptionHandler() {
142
+ Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
143
+ Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
144
+ @Override
145
+ public void uncaughtException(Thread thread, Throwable throwable) {
146
+ // 记录crash日志
147
+ recordCrash(thread, throwable);
148
+
149
+ // 调用默认处理器
150
+ if (defaultHandler != null) {
151
+ defaultHandler.uncaughtException(thread, throwable);
152
+ }
153
+ }
154
+ });
155
+ }
156
+
157
+ /**
158
+ * 记录crash日志
159
+ */
160
+ private void recordCrash(Thread thread, Throwable throwable) {
161
+ try {
162
+ StringWriter sw = new StringWriter();
163
+ PrintWriter pw = new PrintWriter(sw);
164
+ throwable.printStackTrace(pw);
165
+
166
+ String crashInfo = "=== CRASH LOG ===\n" +
167
+ "Time: " + dateFormat.format(new Date()) + "\n" +
168
+ "Thread: " + thread.getName() + "\n" +
169
+ "Device: " + Build.MANUFACTURER + " " + Build.MODEL + "\n" +
170
+ "Android: " + Build.VERSION.RELEASE + " (API " + Build.VERSION.SDK_INT + ")\n" +
171
+ "Exception: " + throwable.getClass().getName() + "\n" +
172
+ "Message: " + throwable.getMessage() + "\n" +
173
+ "Stack Trace:\n" + sw.toString() + "\n" +
174
+ "=== END CRASH LOG ===\n";
175
+
176
+ // 写入crash文件
177
+ String crashFileName = logDir + File.separator + "crash_" +
178
+ new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault()).format(new Date()) + ".log";
179
+ writeToFile(crashFileName, crashInfo, true);
180
+
181
+ } catch (Exception e) {
182
+ // 记录crash时出错,使用系统日志
183
+ Log.e(TAG, "Failed to record crash", e);
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Verbose日志
189
+ */
190
+ public void v(String tag, String message) {
191
+ log(LogLevel.VERBOSE, tag, message, null);
192
+ }
193
+
194
+ public void v(String tag, String message, Throwable throwable) {
195
+ log(LogLevel.VERBOSE, tag, message, throwable);
196
+ }
197
+
198
+ /**
199
+ * Debug日志
200
+ */
201
+ public void d(String tag, String message) {
202
+ log(LogLevel.DEBUG, tag, message, null);
203
+ }
204
+
205
+ public void d(String tag, String message, Throwable throwable) {
206
+ log(LogLevel.DEBUG, tag, message, throwable);
207
+ }
208
+
209
+ /**
210
+ * Info日志
211
+ */
212
+ public void i(String tag, String message) {
213
+ log(LogLevel.INFO, tag, message, null);
214
+ }
215
+
216
+ public void i(String tag, String message, Throwable throwable) {
217
+ log(LogLevel.INFO, tag, message, throwable);
218
+ }
219
+
220
+ /**
221
+ * Info日志(简化方法)
222
+ */
223
+ public void info(String tag, String message) {
224
+ log(LogLevel.INFO, tag, message, null);
225
+ }
226
+
227
+ /**
228
+ * Warning日志
229
+ */
230
+ public void w(String tag, String message) {
231
+ log(LogLevel.WARN, tag, message, null);
232
+ }
233
+
234
+ public void w(String tag, String message, Throwable throwable) {
235
+ log(LogLevel.WARN, tag, message, throwable);
236
+ }
237
+
238
+ /**
239
+ * Error日志
240
+ */
241
+ public void e(String tag, String message) {
242
+ log(LogLevel.ERROR, tag, message, null);
243
+ }
244
+
245
+ public void e(String tag, String message, Throwable throwable) {
246
+ log(LogLevel.ERROR, tag, message, throwable);
247
+ }
248
+
249
+ /**
250
+ * 核心日志方法
251
+ */
252
+ private void log(LogLevel level, String tag, String message, Throwable throwable) {
253
+ if (!isInitialized.get()) {
254
+ // 未初始化时使用系统日志
255
+ Log.println(level.getPriority(), tag, message);
256
+ return;
257
+ }
258
+
259
+ // 控制台输出(仅Debug模式)
260
+ if (enableConsoleLog) {
261
+ if (throwable != null) {
262
+ Log.println(level.getPriority(), tag, message + "\n" + Log.getStackTraceString(throwable));
263
+ } else {
264
+ Log.println(level.getPriority(), tag, message);
265
+ }
266
+ }
267
+
268
+ // 文件输出
269
+ if (enableFileLog) {
270
+ logExecutor.execute(() -> {
271
+ try {
272
+ writeLogToFile(level, tag, message, throwable);
273
+ } catch (Exception e) {
274
+ // 写入文件失败时使用系统日志
275
+ Log.e(TAG, "Failed to write log to file", e);
276
+ }
277
+ });
278
+ }
279
+ }
280
+
281
+ /**
282
+ * 写入日志到文件
283
+ */
284
+ private void writeLogToFile(LogLevel level, String tag, String message, Throwable throwable) {
285
+ // 检查并轮转日志文件
286
+ checkAndRotateLogFile();
287
+
288
+ // 构建日志内容
289
+ StringBuilder logBuilder = new StringBuilder();
290
+ logBuilder.append(dateFormat.format(new Date()));
291
+ logBuilder.append(" ");
292
+ logBuilder.append(level.name());
293
+ logBuilder.append("/");
294
+ logBuilder.append(tag);
295
+ logBuilder.append(": ");
296
+ logBuilder.append(message);
297
+
298
+ if (throwable != null) {
299
+ StringWriter sw = new StringWriter();
300
+ PrintWriter pw = new PrintWriter(sw);
301
+ throwable.printStackTrace(pw);
302
+ logBuilder.append("\n").append(sw.toString());
303
+ }
304
+
305
+ logBuilder.append("\n");
306
+
307
+ // 写入文件
308
+ writeToFile(currentLogFile, logBuilder.toString(), false);
309
+ }
310
+
311
+ /**
312
+ * 写入内容到文件
313
+ */
314
+ private void writeToFile(String filePath, String content, boolean isNewFile) {
315
+ try {
316
+ // 加密内容(如果启用)
317
+ if (enableEncryption && encryptionKey != null) {
318
+ content = encryptContent(content);
319
+ }
320
+
321
+ FileWriter writer = new FileWriter(filePath, !isNewFile); // append mode
322
+ writer.write(content);
323
+ writer.flush();
324
+ writer.close();
325
+ } catch (IOException e) {
326
+ Log.e(TAG, "Failed to write to file: " + filePath, e);
327
+ }
328
+ }
329
+
330
+ /**
331
+ * 加密内容
332
+ */
333
+ private String encryptContent(String content) {
334
+ try {
335
+ SecretKeySpec keySpec = new SecretKeySpec(encryptionKey.getBytes(), "AES");
336
+ Cipher cipher = Cipher.getInstance("AES");
337
+ cipher.init(Cipher.ENCRYPT_MODE, keySpec);
338
+ byte[] encrypted = cipher.doFinal(content.getBytes());
339
+ return Base64.encodeToString(encrypted, Base64.DEFAULT);
340
+ } catch (Exception e) {
341
+ Log.e(TAG, "Failed to encrypt content", e);
342
+ return content; // 加密失败时返回原内容
343
+ }
344
+ }
345
+
346
+ /**
347
+ * 检查并轮转日志文件
348
+ */
349
+ private void checkAndRotateLogFile() {
350
+ try {
351
+ // 更新当前日志文件路径(处理日期变更)
352
+ updateCurrentLogFile();
353
+
354
+ File currentFile = new File(currentLogFile);
355
+ if (!currentFile.exists()) {
356
+ return;
357
+ }
358
+
359
+ // 检查文件大小
360
+ if (currentFile.length() > maxFileSize) {
361
+ // 重命名当前文件
362
+ String timestamp = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss", Locale.getDefault()).format(new Date());
363
+ String rotatedFileName = logDir + File.separator + "beidou_" + timestamp + ".log";
364
+ File rotatedFile = new File(rotatedFileName);
365
+
366
+ if (currentFile.renameTo(rotatedFile)) {
367
+ Log.d(TAG, "Log file rotated: " + rotatedFileName);
368
+ // 清理旧文件
369
+ cleanOldLogs();
370
+ }
371
+ }
372
+ } catch (Exception e) {
373
+ Log.e(TAG, "Failed to rotate log file", e);
374
+ }
375
+ }
376
+
377
+ /**
378
+ * 清理旧日志文件
379
+ */
380
+ private void cleanOldLogs() {
381
+ try {
382
+ File logDirFile = new File(logDir);
383
+ File[] logFiles = logDirFile.listFiles((dir, name) -> name.endsWith(".log"));
384
+
385
+ if (logFiles != null && logFiles.length > maxFileCount) {
386
+ // 按修改时间排序
387
+ Arrays.sort(logFiles, Comparator.comparingLong(File::lastModified));
388
+
389
+ // 删除最旧的文件
390
+ int filesToDelete = logFiles.length - maxFileCount;
391
+ for (int i = 0; i < filesToDelete; i++) {
392
+ if (logFiles[i].delete()) {
393
+ Log.d(TAG, "Deleted old log file: " + logFiles[i].getName());
394
+ }
395
+ }
396
+ }
397
+ } catch (Exception e) {
398
+ Log.e(TAG, "Failed to clean old logs", e);
399
+ }
400
+ }
401
+
402
+ /**
403
+ * 获取所有日志文件
404
+ */
405
+ public File[] getLogFiles() {
406
+ File logDirFile = new File(logDir);
407
+ return logDirFile.listFiles((dir, name) -> name.endsWith(".log"));
408
+ }
409
+
410
+ /**
411
+ * 清空所有日志文件
412
+ */
413
+ public void clearAllLogs() {
414
+ logExecutor.execute(() -> {
415
+ try {
416
+ File[] logFiles = getLogFiles();
417
+ if (logFiles != null) {
418
+ for (File file : logFiles) {
419
+ if (file.delete()) {
420
+ Log.d(TAG, "Deleted log file: " + file.getName());
421
+ }
422
+ }
423
+ }
424
+ } catch (Exception e) {
425
+ Log.e(TAG, "Failed to clear logs", e);
426
+ }
427
+ });
428
+ }
429
+
430
+ /**
431
+ * 获取日志统计信息
432
+ */
433
+ public String getLogStats() {
434
+ try {
435
+ File[] logFiles = getLogFiles();
436
+ if (logFiles == null) {
437
+ return "No log files found";
438
+ }
439
+
440
+ long totalSize = 0;
441
+ for (File file : logFiles) {
442
+ totalSize += file.length();
443
+ }
444
+
445
+ return String.format(Locale.getDefault(),
446
+ "Log Stats:\n" +
447
+ "Files: %d\n" +
448
+ "Total Size: %.2f MB\n" +
449
+ "Log Dir: %s\n" +
450
+ "Debug Mode: %b\n" +
451
+ "File Log: %b\n" +
452
+ "Encryption: %b",
453
+ logFiles.length,
454
+ totalSize / (1024.0 * 1024.0),
455
+ logDir,
456
+ isDebugMode,
457
+ enableFileLog,
458
+ enableEncryption);
459
+ } catch (Exception e) {
460
+ return "Failed to get log stats: " + e.getMessage();
461
+ }
462
+ }
463
+
464
+ /**
465
+ * 设置日志配置
466
+ */
467
+ public void setLogConfig(boolean enableFileLog, boolean enableConsoleLog, long maxFileSize, int maxFileCount) {
468
+ this.enableFileLog = enableFileLog;
469
+ this.enableConsoleLog = enableConsoleLog;
470
+ this.maxFileSize = maxFileSize;
471
+ this.maxFileCount = maxFileCount;
472
+
473
+ info(TAG, "Log config updated - FileLog: " + enableFileLog +
474
+ ", ConsoleLog: " + enableConsoleLog +
475
+ ", MaxFileSize: " + maxFileSize +
476
+ ", MaxFileCount: " + maxFileCount);
477
+ }
478
+
479
+ /**
480
+ * 销毁日志管理器
481
+ */
482
+ public void destroy() {
483
+ if (logExecutor != null && !logExecutor.isShutdown()) {
484
+ logExecutor.shutdown();
485
+ }
486
+ isInitialized.set(false);
487
+ }
488
+ }
@@ -1,4 +1,4 @@
1
- package com.cmcc_rn_module;
1
+ package com.fxzs.rnbeidou;
2
2
 
3
3
  /**
4
4
  * Created by shengdi.liu on 2017/9/7.
@@ -1,4 +1,4 @@
1
- package com.cmcc_rn_module.view;
1
+ package com.fxzs.rnbeidou.view;
2
2
 
3
3
  import android.view.View;
4
4
 
@@ -1,4 +1,4 @@
1
- package com.cmcc_rn_module.view;
1
+ package com.fxzs.rnbeidou.view;
2
2
 
3
3
  import android.content.Context;
4
4
  import android.graphics.Canvas;
@@ -274,4 +274,4 @@ public class CompassView extends View implements SensorEventListener {
274
274
  public void onAccuracyChanged(Sensor sensor, int accuracy) {
275
275
  // 可以在这里处理精度变化通知
276
276
  }
277
- }
277
+ }
package/index.ts CHANGED
@@ -124,6 +124,12 @@ interface BeiDouBluetoothModuleType {
124
124
  accuracy: number;
125
125
  timestamp: number;
126
126
  } | null>;
127
+
128
+ // 日志管理相关方法
129
+ configureLogger(enableFileLog: boolean, enableConsoleLog: boolean, maxFileSize: number, maxFileCount: number, encryptionKey: string | null): Promise<boolean>;
130
+ getLogStats(): Promise<string>;
131
+ clearAllLogs(): Promise<boolean>;
132
+ writeLog(level: string, tag: string, message: string): Promise<boolean>;
127
133
  }
128
134
 
129
135
  // ================== 导出的便利方法 ==================
@@ -361,8 +367,8 @@ export const BeiDouModule = {
361
367
  getDevices: getDiscoveredDevicesArray,
362
368
  connect: (deviceNo: string) => BeiDouBluetoothModule.connectToDeviceFromCloudDeviceNO(deviceNo, 0),
363
369
  disconnect: (deviceNo: string) => BeiDouBluetoothModule.disConnectWithDeviceUUID(deviceNo),
364
- writeData: (randomString: string, deviceNo: string, command: number, attrType: number, encrypt: boolean, callback: (result: number) => void) =>
365
- BeiDouBluetoothModule.writeData(randomString, deviceNo, command, attrType, encrypt, callback),
370
+ writeData: (randomString: string, deviceNo: string, command: number, attrType: number, callback: (result: number) => void) =>
371
+ BeiDouBluetoothModule.writeData(randomString, deviceNo, command, attrType, callback),
366
372
  writeInfo: writeInfoToDevice,
367
373
  generateRandomString: () => BeiDouBluetoothModule.generateRandom16BytesString(),
368
374
  setBLEKey: setBeiDouBLEKey,
@@ -457,4 +463,18 @@ export const BeiDouModule = {
457
463
  export default BeiDouModule;
458
464
  export { default as Compass } from './Compass';
459
465
 
466
+ // 导出日志管理功能
467
+ export {
468
+ BeiDouLogManager,
469
+ BeiDouLogger,
470
+ LogConfig,
471
+ LogLevel,
472
+ logVerbose,
473
+ logDebug,
474
+ logInfo,
475
+ logWarn,
476
+ logError,
477
+ setupGlobalLogInterception
478
+ } from './LogManager';
479
+
460
480
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-beidou",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "A React Native module for BeiDou Bluetooth communication.",
5
5
  "main": "index.ts",
6
6
  "types": "index.ts",
@@ -14,7 +14,8 @@
14
14
  "Compass.tsx",
15
15
  "react-native.config.js",
16
16
  "BeiDouAIDLTestPage.tsx",
17
- "TestPage.ts"
17
+ "TestPage.ts",
18
+ "LogManager.ts"
18
19
  ],
19
20
  "keywords": [
20
21
  "react-native",
@@ -3,8 +3,8 @@ module.exports = {
3
3
  platforms: {
4
4
  android: {
5
5
  sourceDir: "./android",
6
- packageImportPath: "import com.cmcc_rn_module.BeiDouBluetoothPackage;",
7
- packageInstance: "new com.cmcc_rn_module.BeiDouBluetoothPackage()"
6
+ packageImportPath: "import com.fxzs.rnbeidou.BeiDouBluetoothPackage;",
7
+ packageInstance: "new com.fxzs.rnbeidou.BeiDouBluetoothPackage()"
8
8
  }
9
9
  }
10
10
  }