mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 13:08:42 +00:00
capture of video does work #25
This commit is contained in:
parent
5e90af79d8
commit
0763fa5d50
2 changed files with 123 additions and 44 deletions
|
|
@ -1,3 +1,4 @@
|
||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -41,10 +42,13 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
bool useHighQuality = false;
|
bool useHighQuality = false;
|
||||||
bool isVideoRecording = false;
|
bool isVideoRecording = false;
|
||||||
bool hasAudioPermission = true;
|
bool hasAudioPermission = true;
|
||||||
|
DateTime? videoRecordingStarted;
|
||||||
|
Timer? videoRecordingTimer;
|
||||||
|
DateTime currentTime = DateTime.now();
|
||||||
final GlobalKey keyTriggerButton = GlobalKey();
|
final GlobalKey keyTriggerButton = GlobalKey();
|
||||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
|
|
||||||
late CameraController controller;
|
CameraController? controller;
|
||||||
ScreenshotController screenshotController = ScreenshotController();
|
ScreenshotController screenshotController = ScreenshotController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -59,7 +63,9 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sharePreviewIsShown) return;
|
if (sharePreviewIsShown) return;
|
||||||
if (controller.value.isInitialized) takePicture();
|
if (controller != null && controller!.value.isInitialized) {
|
||||||
|
takePicture();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
initAsync();
|
initAsync();
|
||||||
|
|
@ -79,8 +85,9 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
void dispose() {
|
void dispose() {
|
||||||
FlutterVolumeController.removeListener();
|
FlutterVolumeController.removeListener();
|
||||||
if (cameraId < gCameras.length) {
|
if (cameraId < gCameras.length) {
|
||||||
controller.dispose();
|
controller?.dispose();
|
||||||
}
|
}
|
||||||
|
videoRecordingTimer?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,11 +99,13 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
openAppSettings();
|
openAppSettings();
|
||||||
} else {
|
} else {
|
||||||
hasAudioPermission = await Permission.microphone.isGranted;
|
hasAudioPermission = await Permission.microphone.isGranted;
|
||||||
setState(() {});
|
if (hasAudioPermission) {
|
||||||
|
selectCamera(cameraId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectCamera(int sCameraId, {bool init = false}) {
|
Future selectCamera(int sCameraId, {bool init = false}) async {
|
||||||
if (sCameraId >= gCameras.length) return;
|
if (sCameraId >= gCameras.length) return;
|
||||||
if (init) {
|
if (init) {
|
||||||
for (; sCameraId < gCameras.length; sCameraId++) {
|
for (; sCameraId < gCameras.length; sCameraId++) {
|
||||||
|
|
@ -111,17 +120,17 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
controller = CameraController(
|
controller = CameraController(
|
||||||
gCameras[sCameraId],
|
gCameras[sCameraId],
|
||||||
ResolutionPreset.high,
|
ResolutionPreset.high,
|
||||||
enableAudio: false,
|
enableAudio: await Permission.microphone.isGranted,
|
||||||
);
|
);
|
||||||
controller.initialize().then((_) async {
|
controller?.initialize().then((_) async {
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await controller.lockCaptureOrientation(DeviceOrientation.portraitUp);
|
await controller?.lockCaptureOrientation(DeviceOrientation.portraitUp);
|
||||||
controller.setFlashMode(isFlashOn ? FlashMode.always : FlashMode.off);
|
controller?.setFlashMode(isFlashOn ? FlashMode.always : FlashMode.off);
|
||||||
|
|
||||||
isZoomAble = await controller.getMinZoomLevel() !=
|
isZoomAble = await controller?.getMinZoomLevel() !=
|
||||||
await controller.getMaxZoomLevel();
|
await controller?.getMaxZoomLevel();
|
||||||
setState(() {
|
setState(() {
|
||||||
cameraLoaded = true;
|
cameraLoaded = true;
|
||||||
});
|
});
|
||||||
|
|
@ -143,9 +152,9 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> updateScaleFactor(double newScale) async {
|
Future<void> updateScaleFactor(double newScale) async {
|
||||||
if (scaleFactor == newScale) return;
|
if (scaleFactor == newScale || controller == null) return;
|
||||||
var minFactor = await controller.getMinZoomLevel();
|
var minFactor = await controller!.getMinZoomLevel();
|
||||||
var maxFactor = await controller.getMaxZoomLevel();
|
var maxFactor = await controller!.getMaxZoomLevel();
|
||||||
if (newScale < minFactor) {
|
if (newScale < minFactor) {
|
||||||
newScale = minFactor;
|
newScale = minFactor;
|
||||||
}
|
}
|
||||||
|
|
@ -153,7 +162,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
newScale = maxFactor;
|
newScale = maxFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
await controller.setZoomLevel(newScale);
|
await controller?.setZoomLevel(newScale);
|
||||||
setState(() {
|
setState(() {
|
||||||
scaleFactor = newScale;
|
scaleFactor = newScale;
|
||||||
});
|
});
|
||||||
|
|
@ -181,7 +190,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future takePicture() async {
|
Future takePicture() async {
|
||||||
if (sharePreviewIsShown) return;
|
if (sharePreviewIsShown || isVideoRecording) return;
|
||||||
late Future<Uint8List?> imageBytes;
|
late Future<Uint8List?> imageBytes;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
|
|
@ -190,12 +199,13 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
|
|
||||||
if (useHighQuality && !isFront) {
|
if (useHighQuality && !isFront) {
|
||||||
if (Platform.isIOS) {
|
if (Platform.isIOS) {
|
||||||
await controller.pausePreview();
|
await controller?.pausePreview();
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Take the picture
|
// Take the picture
|
||||||
final XFile picture = await controller.takePicture();
|
final XFile? picture = await controller?.takePicture();
|
||||||
|
if (picture == null) return;
|
||||||
imageBytes = loadAndDeletePictureFromFile(picture);
|
imageBytes = loadAndDeletePictureFromFile(picture);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_showCameraException(e);
|
_showCameraException(e);
|
||||||
|
|
@ -208,17 +218,17 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
showSelfieFlash = true;
|
showSelfieFlash = true;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
controller.setFlashMode(FlashMode.torch);
|
controller?.setFlashMode(FlashMode.torch);
|
||||||
}
|
}
|
||||||
await Future.delayed(Duration(milliseconds: 1000));
|
await Future.delayed(Duration(milliseconds: 1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
await controller.pausePreview();
|
await controller?.pausePreview();
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
|
|
||||||
controller.setFlashMode(isFlashOn ? FlashMode.always : FlashMode.off);
|
controller?.setFlashMode(isFlashOn ? FlashMode.always : FlashMode.off);
|
||||||
|
imageBytes = screenshotController.capture(
|
||||||
imageBytes = screenshotController.capture(pixelRatio: 1);
|
pixelRatio: MediaQuery.of(context).devicePixelRatio);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (await pushMediaEditor(imageBytes, null)) {
|
if (await pushMediaEditor(imageBytes, null)) {
|
||||||
|
|
@ -262,10 +272,9 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get isFront =>
|
bool get isFront =>
|
||||||
controller.description.lensDirection == CameraLensDirection.front;
|
controller?.description.lensDirection == CameraLensDirection.front;
|
||||||
|
|
||||||
Future onPanUpdate(details) async {
|
Future onPanUpdate(details) async {
|
||||||
print(details);
|
|
||||||
if (isFront) {
|
if (isFront) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -275,7 +284,13 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
|
|
||||||
if (diff > baseDiff) diff = baseDiff;
|
if (diff > baseDiff) diff = baseDiff;
|
||||||
if (diff < -baseDiff) diff = -baseDiff;
|
if (diff < -baseDiff) diff = -baseDiff;
|
||||||
var tmp = (diff / baseDiff * (14 * 2)).toInt() / 4;
|
var tmp = 0.0;
|
||||||
|
if (Platform.isAndroid) {
|
||||||
|
tmp = (diff / baseDiff * (7 * 2)).toInt() / 2;
|
||||||
|
} else {
|
||||||
|
tmp = (diff / baseDiff * (14 * 2)).toInt() / 4;
|
||||||
|
}
|
||||||
|
|
||||||
tmp = baseScaleFactor + tmp;
|
tmp = baseScaleFactor + tmp;
|
||||||
if (tmp < 1) tmp = 1;
|
if (tmp < 1) tmp = 1;
|
||||||
updateScaleFactor(tmp);
|
updateScaleFactor(tmp);
|
||||||
|
|
@ -302,11 +317,24 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future startVideoRecording() async {
|
Future startVideoRecording() async {
|
||||||
if (controller.value.isRecordingVideo) return;
|
if (controller != null && controller!.value.isRecordingVideo) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await controller.startVideoRecording();
|
await controller?.startVideoRecording();
|
||||||
|
videoRecordingTimer = Timer.periodic(Duration(milliseconds: 10), (timer) {
|
||||||
|
setState(() {
|
||||||
|
currentTime = DateTime.now();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (videoRecordingStarted != null &&
|
||||||
|
currentTime.difference(videoRecordingStarted!).inSeconds >= 10) {
|
||||||
|
timer.cancel();
|
||||||
|
videoRecordingTimer = null;
|
||||||
|
stopVideoRecording();
|
||||||
|
}
|
||||||
|
});
|
||||||
setState(() {
|
setState(() {
|
||||||
|
videoRecordingStarted = DateTime.now();
|
||||||
isVideoRecording = true;
|
isVideoRecording = true;
|
||||||
});
|
});
|
||||||
} on CameraException catch (e) {
|
} on CameraException catch (e) {
|
||||||
|
|
@ -316,17 +344,22 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future stopVideoRecording() async {
|
Future stopVideoRecording() async {
|
||||||
if (!controller.value.isRecordingVideo) {
|
if (videoRecordingTimer != null) {
|
||||||
|
videoRecordingTimer?.cancel();
|
||||||
|
videoRecordingTimer = null;
|
||||||
|
}
|
||||||
|
if (controller == null || !controller!.value.isRecordingVideo) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setState(() {
|
setState(() {
|
||||||
|
videoRecordingStarted = null;
|
||||||
isVideoRecording = false;
|
isVideoRecording = false;
|
||||||
sharePreviewIsShown = true;
|
sharePreviewIsShown = true;
|
||||||
});
|
});
|
||||||
XFile? videoPath = await controller.stopVideoRecording();
|
XFile? videoPath = await controller?.stopVideoRecording();
|
||||||
await controller.pausePreview();
|
await controller?.pausePreview();
|
||||||
if (await pushMediaEditor(null, videoPath)) {
|
if (await pushMediaEditor(null, videoPath)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -354,7 +387,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (cameraId >= gCameras.length) {
|
if (cameraId >= gCameras.length || controller == null) {
|
||||||
return Center(
|
return Center(
|
||||||
child: Text("No camera found."),
|
child: Text("No camera found."),
|
||||||
);
|
);
|
||||||
|
|
@ -379,7 +412,6 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
basePanY = details.localPosition.dy;
|
basePanY = details.localPosition.dy;
|
||||||
baseScaleFactor = scaleFactor;
|
baseScaleFactor = scaleFactor;
|
||||||
});
|
});
|
||||||
print("onLongPressDown");
|
|
||||||
// Get the position of the pointer
|
// Get the position of the pointer
|
||||||
RenderBox renderBox =
|
RenderBox renderBox =
|
||||||
keyTriggerButton.currentContext?.findRenderObject() as RenderBox;
|
keyTriggerButton.currentContext?.findRenderObject() as RenderBox;
|
||||||
|
|
@ -405,7 +437,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
children: [
|
children: [
|
||||||
if (!galleryLoadedImageIsShown)
|
if (!galleryLoadedImageIsShown)
|
||||||
CameraPreviewWidget(
|
CameraPreviewWidget(
|
||||||
controller: controller,
|
controller: controller!,
|
||||||
screenshotController: screenshotController,
|
screenshotController: screenshotController,
|
||||||
isFront: isFront,
|
isFront: isFront,
|
||||||
),
|
),
|
||||||
|
|
@ -452,10 +484,10 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
: Colors.white.withAlpha(160),
|
: Colors.white.withAlpha(160),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (isFlashOn) {
|
if (isFlashOn) {
|
||||||
controller.setFlashMode(FlashMode.off);
|
controller?.setFlashMode(FlashMode.off);
|
||||||
isFlashOn = false;
|
isFlashOn = false;
|
||||||
} else {
|
} else {
|
||||||
controller.setFlashMode(FlashMode.always);
|
controller?.setFlashMode(FlashMode.always);
|
||||||
isFlashOn = true;
|
isFlashOn = true;
|
||||||
}
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
|
|
@ -500,7 +532,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
if (controller.value.isInitialized &&
|
if (controller!.value.isInitialized &&
|
||||||
isZoomAble &&
|
isZoomAble &&
|
||||||
!isFront &&
|
!isFront &&
|
||||||
!isVideoRecording)
|
!isVideoRecording)
|
||||||
|
|
@ -510,7 +542,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
key: widget.key,
|
key: widget.key,
|
||||||
scaleFactor: scaleFactor,
|
scaleFactor: scaleFactor,
|
||||||
updateScaleFactor: updateScaleFactor,
|
updateScaleFactor: updateScaleFactor,
|
||||||
controller: controller,
|
controller: controller!,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 30),
|
||||||
|
|
@ -570,6 +602,52 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (videoRecordingStarted != null)
|
||||||
|
Positioned(
|
||||||
|
top: 50,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Center(
|
||||||
|
child: SizedBox(
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Center(
|
||||||
|
child: CircularProgressIndicator(
|
||||||
|
value:
|
||||||
|
(currentTime.difference(videoRecordingStarted!))
|
||||||
|
.inMilliseconds /
|
||||||
|
(10 * 1000),
|
||||||
|
strokeWidth: 4,
|
||||||
|
valueColor:
|
||||||
|
AlwaysStoppedAnimation<Color>(Colors.red),
|
||||||
|
backgroundColor: Colors.grey[300],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Center(
|
||||||
|
child: Text(
|
||||||
|
currentTime
|
||||||
|
.difference(videoRecordingStarted!)
|
||||||
|
.inSeconds
|
||||||
|
.toString(),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 17,
|
||||||
|
shadows: [
|
||||||
|
Shadow(
|
||||||
|
color: const Color.fromARGB(122, 0, 0, 0),
|
||||||
|
blurRadius: 5.0,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
if (!sharePreviewIsShown && widget.sendTo != null)
|
if (!sharePreviewIsShown && widget.sendTo != null)
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 5,
|
left: 5,
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||||
|
import 'package:logging/logging.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/src/views/camera/components/save_to_gallery.dart';
|
import 'package:twonly/src/views/camera/components/save_to_gallery.dart';
|
||||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
||||||
|
|
@ -59,16 +60,12 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
} else if (widget.videFilePath != null) {
|
} else if (widget.videFilePath != null) {
|
||||||
videoController =
|
videoController =
|
||||||
VideoPlayerController.file(File(widget.videFilePath!.path));
|
VideoPlayerController.file(File(widget.videFilePath!.path));
|
||||||
videoController?.addListener(() {
|
|
||||||
setState(() {});
|
|
||||||
});
|
|
||||||
videoController?.setLooping(true);
|
videoController?.setLooping(true);
|
||||||
videoController?.initialize().then((_) {
|
videoController?.initialize().then((_) {
|
||||||
videoController!.play();
|
videoController!.play();
|
||||||
|
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}).catchError((Object error) {
|
}).catchError((Object error) {
|
||||||
print(error);
|
Logger("ui.share_image_editor").shout(error);
|
||||||
});
|
});
|
||||||
videoController?.play();
|
videoController?.play();
|
||||||
print(widget.videFilePath!.path);
|
print(widget.videFilePath!.path);
|
||||||
|
|
@ -260,6 +257,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (wasSend != null && wasSend && context.mounted) {
|
if (wasSend != null && wasSend && context.mounted) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -321,7 +319,10 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
_isRealTwonly,
|
_isRealTwonly,
|
||||||
maxShowTime,
|
maxShowTime,
|
||||||
);
|
);
|
||||||
Navigator.pop(context, true);
|
if (context.mounted) {
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
Navigator.pop(context, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue