Browse Source

Merge branch 'new_dev' of https://github.com/deepcloud2048/deep_voice into new_dev

weicu
liwei1dao 1 year ago
parent
commit
638ad7cabd
  1. 40
      lib/core/utils/ftpconnect/src/ftp_entry.dart
  2. 96
      lib/modules/meeting/controllers/meeting_connect_controller.dart
  3. 26
      lib/modules/meeting/controllers/meeting_controller.dart
  4. 36
      lib/modules/meeting/views/meeting_connect_view.dart
  5. 3
      lib/modules/meeting/views/meeting_view.dart

40
lib/core/utils/ftpconnect/src/ftp_entry.dart

@ -27,11 +27,8 @@ class FTPEntry {
r"(.+)$" //file/dir name[8]
);
static final regexpLISTSiiServers = RegExp(r"^(.{8}\s+.{7})\s+" //date[1]
r"(.{0,5})\s+" //type file or dir [2]
r"(\d{0,24})\s+" //size [3]
r"(.+)$" //file/ dir name [4]
);
static final regexpLISTSiiServers = RegExp(
r"^(\d{2}-\d{2}-\d{4}\s+\d{2}:\d{2}[AP]M)\s+(\<DIR\>)?\s*(\d*)\s+(.+)$");
// Hide constructor
FTPEntry._(
@ -218,35 +215,22 @@ class FTPEntry {
int _uid = -1;
Iterable<Match> matches = regexpLISTSiiServers.allMatches(responseLine);
for (Match match in matches) {
//date
String date =
match.group(1)!.split(" ").fold('', (previousValue, element) {
//keep only one space and add fullyear if only last 2 digits in year
if (element.isEmpty) return previousValue;
if (previousValue.isEmpty) {
return element.length <= 8
? element.substring(0, 6) +
DateTime.now().year.toString().substring(0, 2) +
element.substring(6, 8)
: element;
}
return '$previousValue $element';
});
// 日期,合并多余空格
String date = match.group(1)!.replaceAll(RegExp(r'\s+'), ' ');
_modifyTime = DateFormat('MM-dd-yyyy hh:mma').parse(date);
//type
if (match.group(2)!.trim().isEmpty) {
_type = FTPEntryType.FILE;
} else if (match.group(2)!.toLowerCase().contains("dir")) {
// 类型
if ((match.group(2) ?? '').trim().isNotEmpty) {
_type = FTPEntryType.DIR;
} else {
_type = FTPEntryType.LINK;
_type = FTPEntryType.FILE;
}
//size
_size = int.tryParse(match.group(3)!) ?? 0;
//file/dir name
_name = match.group(4)!;
// 大小
_size = int.tryParse(match.group(3) ?? '') ?? 0;
// 文件/目录名
_name = match.group(4)!.trim();
}
return FTPEntry._(_name, _modifyTime, _persmission, _type, _size, _unique,
_group, _gid, _mode, _owner, _uid, {});

96
lib/modules/meeting/controllers/meeting_connect_controller.dart

@ -1,13 +1,19 @@
import 'dart:async';
import 'dart:io';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:path_provider/path_provider.dart';
import 'package:wifi_iot/wifi_iot.dart';
import '../../../core/utils/ftpconnect/ftpconnect.dart';
import 'meeting_controller.dart';
class MeetingConnectController extends GetxController {
final _meetingController = Get.find<MeetingController>();
RxString stateStr = '搜索中...'.obs;
RxBool isRescan = false.obs;
RxBool isImportedFiles = false.obs;
RxString importedFilesStr = ''.obs;
late StreamSubscription<List<ScanResult>> _scanResultsSubscription;
@ -16,8 +22,8 @@ class MeetingConnectController extends GetxController {
bool isScanFailed = false;
late BluetoothCharacteristic? _writeCharacteristic;
late StreamSubscription<List<int>>? _valueReceivedSubscription;
BluetoothCharacteristic? _writeCharacteristic;
StreamSubscription<List<int>>? _valueReceivedSubscription;
String _wifiSSID = '';
String _wifiPassword = '';
@ -26,6 +32,13 @@ class MeetingConnectController extends GetxController {
String _ftpAccount = '';
String _ftpPassword = '';
FTPConnect? _ftpConnect;
RxBool isDownload = false.obs;
RxDouble downloadProgress = 0.0.obs;
RxInt totalReceived = 0.obs;
RxInt fileSize = 0.obs;
@override
void onInit() {
super.onInit();
@ -56,6 +69,7 @@ class MeetingConnectController extends GetxController {
_scanResultsSubscription.cancel();
_isScanningSubscription.cancel();
_valueReceivedSubscription?.cancel();
_ftpConnect?.disconnect();
_stopScan();
_disconnect();
super.onClose();
@ -86,6 +100,7 @@ class MeetingConnectController extends GetxController {
}
Future<void> _disconnect() async {
await WiFiForIoTPlugin.forceWifiUsage(false);
await WiFiForIoTPlugin.disconnect();
await _device?.disconnect();
}
@ -131,26 +146,25 @@ class MeetingConnectController extends GetxController {
}
void _listenData(List<int> value) {
print('----$value--------');
int command = value[2];
switch (command) {
case 0x0d: // 是否有导入文件
if (value[3] == 0x01) {
isImportedFiles.value = true;
importedFilesStr.value = '检查到有新文件,是否导入';
stateStr.value = '正在开启Wi-Fi...';
importedFilesStr.value = '检查到有新文件,正在准备导入';
sendData([0x5a, 0x02, 0x87, 0x01]);
} else {
isImportedFiles.value = false;
importedFilesStr.value = '没有新文件需要导入';
}
break;
case 0x87: // Wi-Fi 开启/关闭
if (value[3] == 0x01 && value[4] == 0x01) {
_getWifi();
sendData([0x5a, 0x01, 0x0b]);
}
break;
case 0x0b: // WiFi 热点 SSID
_wifiSSID = String.fromCharCodes(value.sublist(3, value.length - 2));
_connectWifi();
sendData([0x5a, 0x01, 0x0c]);
break;
case 0x0c: // WiFi 热点密码
_wifiPassword = _decodePassword(value);
@ -176,11 +190,6 @@ class MeetingConnectController extends GetxController {
}
}
void _getWifi() async {
await sendData([0x5a, 0x01, 0x0b]);
await sendData([0x5a, 0x01, 0x0c]);
}
void _connectWifi() async {
if (_wifiSSID.isNotEmpty && _wifiPassword.isNotEmpty) {
stateStr.value = '正在连接 Wi-Fi...';
@ -189,16 +198,71 @@ class MeetingConnectController extends GetxController {
password: _wifiPassword,
security: NetworkSecurity.WPA,
);
await WiFiForIoTPlugin.forceWifiUsage(true);
stateStr.value = 'Wi-Fi 连接成功';
sendData([0x5a, 0x01, 0x03]);
}
}
void _connectFtp() {
void _connectFtp() async {
if (_ftpAddress.isNotEmpty &&
_ftpPort.isNotEmpty &&
_ftpAccount.isNotEmpty &&
_ftpPassword.isNotEmpty) {}
_ftpPassword.isNotEmpty) {
_ftpConnect = FTPConnect(
_ftpAddress,
port: int.tryParse(_ftpPort) ?? 21,
user: _ftpAccount,
pass: _ftpPassword,
);
await _ftpConnect?.connect();
_ftpConnect?.listCommand = ListCommand.LIST;
List<FTPEntry>? listDirectory = await _ftpConnect?.listDirectoryContent();
if (listDirectory != null && listDirectory.isNotEmpty) {
stateStr.value = '正在下载文件...';
isDownload.value = true;
int totalSize = listDirectory.fold(
0,
(sum, entry) => sum + (entry.size ?? 0),
);
for (var i = 0; i < listDirectory.length; i++) {
importedFilesStr.value =
'共${listDirectory.length}个文件,正在下载第${i + 1}个文件';
int previousSize = i > 0 ? listDirectory[i - 1].size ?? 0 : 0;
FTPEntry element = listDirectory[i];
await _downloadFile(totalSize, previousSize, element);
await _ftpConnect?.deleteFile(element.name);
}
stateStr.value = '下载完成';
importedFilesStr.value = '下载完成,共${listDirectory.length}个文件,2秒后自动返回';
_disconnect();
Future.delayed(const Duration(seconds: 2), () {
Get.back();
});
}
}
}
Future<void> _downloadFile(
int totalSize, int previousSize, FTPEntry entry) async {
final directory = await getApplicationDocumentsDirectory();
final directoryDir = Directory('${directory.path}/RecordingPen');
if (!await directoryDir.exists()) {
await directoryDir.create(recursive: true);
}
final String filePath = '${directory.path}/RecordingPen/${entry.name}';
final File localFile = File(filePath);
await _ftpConnect?.downloadFileWithRetry(
entry.name,
localFile,
pRetryCount: 3,
onProgress: (double progress, int received, int size) {
downloadProgress.value = (previousSize + received) / totalSize;
totalReceived.value = received;
fileSize.value = size;
},
);
await _meetingController.insertMeetingAudio(entry.name, filePath, '');
}
String _decodePassword(List<int> data) {

26
lib/modules/meeting/controllers/meeting_controller.dart

@ -190,13 +190,11 @@ class MeetingController extends GetxController {
int addUp = 0;
final appDir = await appDocDir;
// 定义源目录列表
final sourceDirs = [
Directory("${appDir.path}/MeetingAudio/"),
Directory("${appDir.path}/DestAudio/"),
];
// 创建目标目录(如果不存在)
final destDir = Directory("${appDir.path}/LocalAudio/");
if (!await destDir.exists()) {
await destDir.create(recursive: true);
@ -212,8 +210,6 @@ class MeetingController extends GetxController {
}
}
// 此时 allFiles 包含 MeetingAudio 和 DestAudio 目录下所有的文件
for (final fileEntity in allFiles) {
try {
final file = File(fileEntity.path);
@ -334,9 +330,21 @@ class MeetingController extends GetxController {
}
}
await _audioPlayer.setAudioSource(AudioSource.file(filePath!));
var audiourl =
await UploadOss.upload(filepath: filePath, rootDir: 'ExternalAudio');
final meetingId = await insertMeetingAudio(fileName, filePath, audiourl);
if (isParamProvided) {
Get.toNamed(Routes.meetingDetails, arguments: {
'id': meetingId,
});
}
EasyLoading.dismiss();
}
Future insertMeetingAudio(
String fileName, String filePath, String audiourl) async {
await _audioPlayer.setAudioSource(AudioSource.file(filePath));
int seconds = _audioPlayer.duration?.inSeconds ?? 0;
int creationTime = DateTime.now().millisecondsSinceEpoch;
Map<String, Object?> data = {
@ -357,13 +365,7 @@ class MeetingController extends GetxController {
});
addQuantity++;
}
if (isParamProvided) {
Get.toNamed(Routes.meetingDetails, arguments: {
'id': meetingId,
});
}
EasyLoading.dismiss();
return meetingId;
}
Future<String> _saveFile(PlatformFile file) async {

36
lib/modules/meeting/views/meeting_connect_view.dart

@ -76,19 +76,29 @@ class MeetingConnectView extends GetView<MeetingConnectController> {
),
),
),
Obx(
() => controller.isImportedFiles.value
? Padding(
padding: EdgeInsets.symmetric(vertical: 10.w),
child: _button('导入文件', Icons.file_download, onTap: () {
controller.stateStr.value = '正在开启Wi-Fi...';
controller.importedFilesStr.value = '';
controller.isImportedFiles.value = false;
controller.sendData([0x5a, 0x02, 0x87, 0x01]);
}),
)
: const SizedBox(),
),
Obx(() => controller.isDownload.value
? Column(
children: [
Container(
width: 300.w,
height: 3.w,
margin: EdgeInsets.symmetric(vertical: 10.w),
child: LinearProgressIndicator(
value: controller.downloadProgress.value,
valueColor:
const AlwaysStoppedAnimation<Color>(Colors.red),
),
),
Text(
'${controller.totalReceived.value} / ${controller.fileSize.value} bytes',
style: TextStyle(
fontSize: 14.sp,
color: isDarkMode ? Colors.white70 : Colors.black54,
),
),
],
)
: const SizedBox()),
],
),
);

3
lib/modules/meeting/views/meeting_view.dart

@ -423,6 +423,7 @@ class MeetingView extends GetView<MeetingController> {
PreferredSizeWidget _buildAppBar(BuildContext context, bool isDarkMode) {
return AppBar(
backgroundColor: isDarkMode ? Colors.black : Colors.white,
surfaceTintColor: Colors.transparent,
elevation: 0.5,
leading: IconButton(
icon: Icon(
@ -459,7 +460,7 @@ class MeetingView extends GetView<MeetingController> {
() => ListView.builder(
controller: controller.listController,
itemCount: controller.dataList.length,
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 5.w),
padding: EdgeInsets.fromLTRB(12.w, 5.w, 12.w, 90.w),
itemBuilder: (BuildContext context, int index) {
MeetingModel data = MeetingModel.fromJson(controller.dataList[index]);
return _item(context, isDarkMode, data);

Loading…
Cancel
Save