|
- import 'dart:async';
- import 'dart:io';
-
- import 'package:chat/data/constants.dart';
- import 'package:chat/generated/i18n.dart';
- import 'package:chat/photo/ui/dialog/not_permission_dialog.dart';
- import 'package:chat/utils/screen.dart';
- import 'package:chat/utils/sound_util.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_audio_recorder/flutter_audio_recorder.dart';
- import 'package:oktoast/oktoast.dart';
- import 'package:path_provider/path_provider.dart';
- import 'package:path/path.dart' as p;
- import 'package:permission_handler/permission_handler.dart';
-
- const int soundSizeLimit = 61;
- const double RecorderRadius = 30;
-
- class RecordView extends StatefulWidget {
- final double keyboardHeight;
- final Function sendMsg;
-
- RecordView({this.keyboardHeight, this.sendMsg});
- @override
- _RecordViewState createState() => _RecordViewState();
- }
-
- class _RecordViewState extends State<RecordView> {
- Timer counter;
- int totalCount = 0;
- bool _isRecording = false;
- Timer recordTimer;
- FlutterAudioRecorder recorder;
-
- bool isCancelState = false;
- bool _isCounting = false;
-
- bool hasPermission = false; //录音权限
- bool isTaped = false;
-
- @override
- void initState() {
- super.initState();
- }
-
- void hide() {
- counter.cancel();
- counter = null;
- }
-
- @override
- void deactivate() {
- super.deactivate();
-
- cancelRecorder();
- }
-
- @override
- void dispose() {
- print('record view dispose');
- anchorKey = null;
- counter?.cancel();
- super.dispose();
- }
-
- _timeCounter() {
- if (!_isCounting) {
- return Container(
- height: 40,
- );
- }
- int h = totalCount ~/ 3600;
- int m = (totalCount % 3600) ~/ 60;
- int s = totalCount % 60;
-
- var hStr = h >= 10 ? h.toString() : '0' + h.toString();
- var mStr = m >= 10 ? h.toString() : '0' + m.toString();
- var sStr = s >= 10 ? s.toString() : '0' + s.toString();
- return Container(
- height: 40,
- alignment: Alignment.center,
- child: Text('$hStr:$mStr:$sStr',
- textScaleFactor: 1.0,
- style: TextStyle(color: Colors.black, fontSize: 30)));
- }
-
- _startTimer() {
- totalCount = 0;
- _isCounting = true;
- setState(() {});
- counter?.cancel();
- counter = Timer.periodic(Duration(seconds: 1), (Timer timer) {
- totalCount += 1;
- setState(() {});
- });
- }
-
- _stopTimer() {
- _isCounting = false;
- if (mounted) {
- setState(() {});
- }
-
- counter?.cancel();
- }
-
- askRecordPermission() async {
- final PermissionStatus statusFirst = await PermissionHandler()
- .checkPermissionStatus(PermissionGroup.microphone);
-
- print('录音权限 $statusFirst');
-
- final PermissionStatus storageStatus = await PermissionHandler()
- .checkPermissionStatus(PermissionGroup.storage);
-
- print('storage权限 $storageStatus');
- if (statusFirst == PermissionStatus.granted &&
- storageStatus == PermissionStatus.granted) {
- hasPermission = true;
- } else {
-
- cancelRecorder();
- if(statusFirst == PermissionStatus.unknown || (statusFirst == PermissionStatus.denied && Platform.isAndroid)){
- Map<PermissionGroup, PermissionStatus> permissionRequestResult =
- await PermissionHandler().requestPermissions(
- [PermissionGroup.microphone, PermissionGroup.storage]);
- var status = permissionRequestResult[PermissionGroup.microphone];
- var storageStatus = permissionRequestResult[PermissionGroup.storage];
- if (status == PermissionStatus.granted &&
- storageStatus == PermissionStatus.granted) {
- hasPermission = true;
- }
-
- }else{
-
- var result = await showDialog(
- context: context,
- builder: (ctx) => NotPermissionDialog(I18n.of(context).video_permission),
- );
- if (result == true) {
- PermissionHandler().openAppSettings();
- }
- }
- }
- }
-
- double tempX;
- double tempY;
- GlobalKey anchorKey = GlobalKey();
-
- @override
- Widget build(BuildContext context) {
- var tipStr;
- if (_isRecording) {
- if (isCancelState) {
- tipStr = I18n.of(context).cancel;
- } else {
- tipStr = I18n.of(context).voice_tips2;
- }
- } else {
- tipStr = I18n.of(context).voice_tips;
- }
-
- return InkWell(onTap: (){},child: Container(
- width: double.infinity,
- height: double.infinity,
- padding: EdgeInsets.only(top: 10, bottom: 20),
- color: Colors.white,
- alignment: Alignment.center,
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: <Widget>[
- _timeCounter(),
- Expanded(
- child: GestureDetector(
- child: Container(
- key: anchorKey,
- width: 100,
- height: 100,
- child: Icon(
- IconData(0xe64f, fontFamily: Constants.IconFontFamily),
- color: isTaped ? Colors.white : Color(0xFF018DFE),
- size: 64),
- decoration: BoxDecoration(
- color: isTaped ? Color(0xFF018DFE) : Colors.white,
- shape: BoxShape.circle,
- border: Border.all(color: Color(0xFF60919F)),
- boxShadow: [
- BoxShadow(
- color: Color(0xFFD0D0D0), //阴影颜色
- blurRadius: 5.0, //阴影大小
- ),
- ]),
- ),
- onTap: () {
- //只是短暂按压,取消发送
- print('onTap');
- cancelRecorder();
- },
- onTapUp: (TapUpDetails details) {
- print('onTapUp');
- isTaped = false;
- setState(() {});
- },
- onTapDown: (TapDownDetails details) {
- print('onTapDown');
- isTaped = true;
- setState(() {});
- if (!hasPermission) {
- askRecordPermission();
- }
- RenderBox renderBox =
- anchorKey.currentContext.findRenderObject();
- var offset = renderBox.localToGlobal(Offset(
- renderBox.size.width / 2, renderBox.size.height / 2));
- tempX = offset.dx;
- tempY = offset.dy;
- },
- onLongPressStart: (LongPressStartDetails details) {
- print('onLongPressStart');
- if (hasPermission) {
- if (!_isRecording) {
- startRecorder(context);
- }
- }else
- {
- cancelRecorder();
- }
- },
- onLongPressEnd: (LongPressEndDetails details) {
- print('onLongPressEnd');
- isTaped = false;
- setState(() {});
- if (checkValideArea(details.globalPosition)) {
- print('在范围内');
- stopRecorder();
- } else {
- print('不在范围内');
- cancelRecorder();
- }
- },
- onLongPressMoveUpdate: (LongPressMoveUpdateDetails details) {
- if (!checkValideArea(details.globalPosition)) {
- if (!isCancelState) {
- setState(() {
- isCancelState = true;
- });
- }
- } else {
- if (isCancelState) {
- setState(() {
- isCancelState = false;
- });
- }
- }
- },
- )),
- SizedBox(height: 10),
- Container(
- height: 40,
- alignment: Alignment.center,
- child: Text(tipStr, textScaleFactor: 1.0,),
- )
- ],
- )),);
- }
-
- checkValideArea(Offset position) {
- double offset = RecorderRadius * Screen.scale;
-
- return (position.dx - tempX).abs() <= offset &&
- (position.dy - tempY).abs() <= offset;
- }
-
- void startRecorder(BuildContext context) async {
- if (!hasPermission) {
- return;
- }
-
- SoundUtils().stop();
- try {
- var directory = await getTemporaryDirectory();
- var curTime = DateTime.now().millisecondsSinceEpoch;
- var path = p.join(directory.path, 'record$curTime.wav');
-
- recorder = FlutterAudioRecorder(path,
- audioFormat: AudioFormat.WAV,
- sampleRate: 16000); // or AudioFormat.WAV
- await recorder.initialized;
-
- await recorder.start();
- print('######开始录音');
- _startTimer();
- //60分钟后如果还在录,自动停止
-
- recordTimer = Timer(Duration(seconds: soundSizeLimit), () {
- stopRecorder();
- });
-
- this.setState(() {
- this._isRecording = true;
- });
- } catch (err) {
- print('startRecorder error: $err');
- }
- }
-
- void cancelRecorder() async {
- recordTimer?.cancel();
- isTaped = false;
- await recorder?.stop();
- _isRecording = false;
- _stopTimer();
- }
-
- void stopRecorder() async {
- if (!hasPermission || !_isRecording) {
- return;
- }
-
- try {
- var recording = await recorder.stop();
-
- this._isRecording = false;
-
- var filePath = recording.path;
- var time = recording.duration.inMilliseconds;
- print("Stop recording: $filePath time : $time");
-
- recordTimer?.cancel();
- _stopTimer();
-
- _sendSound(filePath, time);
-
- this.setState(() {});
- } catch (err) {
-
- print('stopRecorder error: $err');
- }
- }
-
- _sendSound(soundPath, int duration) async {
- var fileData = File(soundPath).readAsBytesSync();
-
- print('fileData size ${fileData.length}');
- if (duration < 1000) {
- //小于1秒
- showToast(I18n.of(context).time_little);
- return;
- } else {
-
- print('发送音频文件${DateTime.now()}');
- widget.sendMsg(soundPath,duration);
- }
- }
- }
|