mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 11:18:41 +00:00
mirror front camera for android
This commit is contained in:
parent
06715e8ac9
commit
0d36289169
7 changed files with 111 additions and 71 deletions
|
|
@ -47,6 +47,7 @@ class MediaUploadMetadata {
|
|||
late DateTime messageSendAt;
|
||||
late bool isVideo;
|
||||
late bool videoWithAudio;
|
||||
late bool mirrorVideo;
|
||||
|
||||
MediaUploadMetadata();
|
||||
|
||||
|
|
@ -54,6 +55,7 @@ class MediaUploadMetadata {
|
|||
return {
|
||||
'contactIds': contactIds,
|
||||
'isRealTwonly': isRealTwonly,
|
||||
'mirrorVideo': mirrorVideo,
|
||||
'maxShowTime': maxShowTime,
|
||||
'isVideo': isVideo,
|
||||
'videoWithAudio': videoWithAudio,
|
||||
|
|
@ -67,6 +69,7 @@ class MediaUploadMetadata {
|
|||
state.isRealTwonly = json['isRealTwonly'];
|
||||
state.videoWithAudio = json['videoWithAudio'];
|
||||
state.isVideo = json['isVideo'];
|
||||
state.mirrorVideo = json['mirrorVideo'];
|
||||
state.maxShowTime = json['maxShowTime'];
|
||||
state.maxShowTime = json['maxShowTime'];
|
||||
state.messageSendAt = DateTime.parse(json['messageSendAt']);
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ class MediaMessageContent extends MessageContent {
|
|||
final int maxShowTime;
|
||||
final bool isRealTwonly;
|
||||
final bool isVideo;
|
||||
final bool mirrorVideo;
|
||||
final List<int>? downloadToken;
|
||||
final List<int>? encryptionKey;
|
||||
final List<int>? encryptionMac;
|
||||
|
|
@ -108,6 +109,7 @@ class MediaMessageContent extends MessageContent {
|
|||
required this.maxShowTime,
|
||||
required this.isRealTwonly,
|
||||
required this.isVideo,
|
||||
required this.mirrorVideo,
|
||||
this.downloadToken,
|
||||
this.encryptionKey,
|
||||
this.encryptionMac,
|
||||
|
|
@ -131,6 +133,7 @@ class MediaMessageContent extends MessageContent {
|
|||
maxShowTime: json['maxShowTime'],
|
||||
isRealTwonly: json['isRealTwonly'],
|
||||
isVideo: json['isVideo'] ?? false,
|
||||
mirrorVideo: json['mirrorVideo'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -144,6 +147,7 @@ class MediaMessageContent extends MessageContent {
|
|||
'isRealTwonly': isRealTwonly,
|
||||
'maxShowTime': maxShowTime,
|
||||
'isVideo': isVideo,
|
||||
'mirrorVideo': mirrorVideo,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ import 'package:twonly/src/services/notification_service.dart';
|
|||
import 'package:video_compress/video_compress.dart';
|
||||
|
||||
Future sendMediaFile(
|
||||
List<int> userIds,
|
||||
Uint8List imageBytes,
|
||||
bool isRealTwonly,
|
||||
int maxShowTime,
|
||||
XFile? videoFilePath,
|
||||
bool? enableVideoAudio,
|
||||
) async {
|
||||
List<int> userIds,
|
||||
Uint8List imageBytes,
|
||||
bool isRealTwonly,
|
||||
int maxShowTime,
|
||||
XFile? videoFilePath,
|
||||
bool? enableVideoAudio,
|
||||
bool mirrorVideo) async {
|
||||
MediaUploadMetadata metadata = MediaUploadMetadata();
|
||||
metadata.contactIds = userIds;
|
||||
metadata.isRealTwonly = isRealTwonly;
|
||||
|
|
@ -34,6 +34,7 @@ Future sendMediaFile(
|
|||
metadata.isVideo = videoFilePath != null;
|
||||
metadata.videoWithAudio = enableVideoAudio != null && enableVideoAudio;
|
||||
metadata.maxShowTime = maxShowTime;
|
||||
metadata.mirrorVideo = mirrorVideo;
|
||||
|
||||
int? mediaUploadId = await twonlyDatabase.mediaUploadsDao.insertMediaUpload(
|
||||
MediaUploadsCompanion(
|
||||
|
|
@ -62,62 +63,62 @@ Future retryMediaUpload() async {
|
|||
final lockingHandleMediaFile = Mutex();
|
||||
|
||||
Future handleSingleMediaFile(int mediaUploadId) async {
|
||||
// await lockingHandleMediaFile.protect(() async {
|
||||
MediaUpload? media = await twonlyDatabase.mediaUploadsDao
|
||||
.getMediaUploadById(mediaUploadId)
|
||||
.getSingleOrNull();
|
||||
if (media == null) return;
|
||||
await lockingHandleMediaFile.protect(() async {
|
||||
MediaUpload? media = await twonlyDatabase.mediaUploadsDao
|
||||
.getMediaUploadById(mediaUploadId)
|
||||
.getSingleOrNull();
|
||||
if (media == null) return;
|
||||
|
||||
try {
|
||||
switch (media.state) {
|
||||
case UploadState.pending:
|
||||
await handleAddToMessageDb(media);
|
||||
break;
|
||||
case UploadState.addedToMessagesDb:
|
||||
await handleCompressionState(media);
|
||||
break;
|
||||
case UploadState.isCompressed:
|
||||
await handleEncryptionState(media);
|
||||
break;
|
||||
case UploadState.isEncrypted:
|
||||
if (!await handleGetUploadToken(media)) {
|
||||
return; // recoverable error. try again when connected again to the server...
|
||||
}
|
||||
break;
|
||||
case UploadState.hasUploadToken:
|
||||
if (!await handleUpload(media)) {
|
||||
return; // recoverable error. try again when connected again to the server...
|
||||
}
|
||||
break;
|
||||
case UploadState.isUploaded:
|
||||
if (!await handleNotifyReceiver(media)) {
|
||||
return; // recoverable error. try again when connected again to the server...
|
||||
}
|
||||
break;
|
||||
case UploadState.receiverNotified:
|
||||
return;
|
||||
}
|
||||
|
||||
// this will be called until there is an recoverable error OR
|
||||
// the upload is ready
|
||||
await handleSingleMediaFile(mediaUploadId);
|
||||
} catch (e) {
|
||||
// if the messageIds are already there notify the user about this error...
|
||||
if (media.messageIds != null) {
|
||||
for (int messageId in media.messageIds!) {
|
||||
await twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||
messageId,
|
||||
MessagesCompanion(
|
||||
errorWhileSending: Value(true),
|
||||
),
|
||||
);
|
||||
try {
|
||||
switch (media.state) {
|
||||
case UploadState.pending:
|
||||
await handleAddToMessageDb(media);
|
||||
break;
|
||||
case UploadState.addedToMessagesDb:
|
||||
await handleCompressionState(media);
|
||||
break;
|
||||
case UploadState.isCompressed:
|
||||
await handleEncryptionState(media);
|
||||
break;
|
||||
case UploadState.isEncrypted:
|
||||
if (!await handleGetUploadToken(media)) {
|
||||
return; // recoverable error. try again when connected again to the server...
|
||||
}
|
||||
break;
|
||||
case UploadState.hasUploadToken:
|
||||
if (!await handleUpload(media)) {
|
||||
return; // recoverable error. try again when connected again to the server...
|
||||
}
|
||||
break;
|
||||
case UploadState.isUploaded:
|
||||
if (!await handleNotifyReceiver(media)) {
|
||||
return; // recoverable error. try again when connected again to the server...
|
||||
}
|
||||
break;
|
||||
case UploadState.receiverNotified:
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// if the messageIds are already there notify the user about this error...
|
||||
if (media.messageIds != null) {
|
||||
for (int messageId in media.messageIds!) {
|
||||
await twonlyDatabase.messagesDao.updateMessageByMessageId(
|
||||
messageId,
|
||||
MessagesCompanion(
|
||||
errorWhileSending: Value(true),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
await twonlyDatabase.mediaUploadsDao.deleteMediaUpload(mediaUploadId);
|
||||
Logger("media_send.dart")
|
||||
.shout("Non recoverable error while sending media file: $e");
|
||||
return;
|
||||
}
|
||||
await twonlyDatabase.mediaUploadsDao.deleteMediaUpload(mediaUploadId);
|
||||
Logger("media_send.dart")
|
||||
.shout("Non recoverable error while sending media file: $e");
|
||||
}
|
||||
// });
|
||||
});
|
||||
// this will be called until there is an recoverable error OR
|
||||
// the upload is ready
|
||||
await handleSingleMediaFile(mediaUploadId);
|
||||
}
|
||||
|
||||
Future handleAddToMessageDb(MediaUpload media) async {
|
||||
|
|
@ -137,6 +138,7 @@ Future handleAddToMessageDb(MediaUpload media) async {
|
|||
maxShowTime: media.metadata.maxShowTime,
|
||||
isRealTwonly: media.metadata.isRealTwonly,
|
||||
isVideo: media.metadata.isVideo,
|
||||
mirrorVideo: media.metadata.mirrorVideo,
|
||||
).toJson(),
|
||||
),
|
||||
),
|
||||
|
|
@ -357,7 +359,11 @@ Future<bool> handleUpload(MediaUpload media) async {
|
|||
),
|
||||
);
|
||||
|
||||
await deleteMediaFile(media, "encrypted");
|
||||
try {
|
||||
await deleteMediaFile(media, "encrypted");
|
||||
} catch (e) {
|
||||
Logger("media_send.dart").shout("$e");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -395,6 +401,7 @@ Future<bool> handleNotifyReceiver(MediaUpload media) async {
|
|||
maxShowTime: media.metadata.maxShowTime,
|
||||
isRealTwonly: media.metadata.isRealTwonly,
|
||||
isVideo: media.metadata.isVideo,
|
||||
mirrorVideo: media.metadata.mirrorVideo,
|
||||
encryptionKey: media.encryptionData!.encryptionKey,
|
||||
encryptionMac: media.encryptionData!.encryptionMac,
|
||||
encryptionNonce: media.encryptionData!.encryptionNonce,
|
||||
|
|
|
|||
|
|
@ -232,6 +232,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
|||
videoFilePath: videoFilePath,
|
||||
imageBytes: imageBytes,
|
||||
sendTo: widget.sendTo,
|
||||
mirrorVideo: isFront && Platform.isAndroid,
|
||||
),
|
||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||
return child;
|
||||
|
|
|
|||
|
|
@ -30,11 +30,17 @@ List<Layer> removedLayers = [];
|
|||
const gMediaShowInfinite = 999999;
|
||||
|
||||
class ShareImageEditorView extends StatefulWidget {
|
||||
const ShareImageEditorView(
|
||||
{super.key, this.imageBytes, this.sendTo, this.videoFilePath});
|
||||
const ShareImageEditorView({
|
||||
super.key,
|
||||
this.imageBytes,
|
||||
this.sendTo,
|
||||
this.videoFilePath,
|
||||
required this.mirrorVideo,
|
||||
});
|
||||
final Future<Uint8List?>? imageBytes;
|
||||
final XFile? videoFilePath;
|
||||
final Contact? sendTo;
|
||||
final bool mirrorVideo;
|
||||
@override
|
||||
State<ShareImageEditorView> createState() => _ShareImageEditorView();
|
||||
}
|
||||
|
|
@ -289,6 +295,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
maxShowTime: maxShowTime,
|
||||
preselectedUser: widget.sendTo,
|
||||
videoFilePath: widget.videoFilePath,
|
||||
mirrorVideo: widget.mirrorVideo,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
@ -357,6 +364,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
maxShowTime,
|
||||
widget.videoFilePath,
|
||||
videoWithAudio,
|
||||
widget.mirrorVideo,
|
||||
);
|
||||
if (context.mounted) {
|
||||
// ignore: use_build_context_synchronously
|
||||
|
|
@ -405,7 +413,12 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
|||
child: Stack(
|
||||
children: [
|
||||
if (videoController != null)
|
||||
Positioned.fill(child: VideoPlayer(videoController!)),
|
||||
Positioned.fill(
|
||||
child: Transform.flip(
|
||||
flipX: widget.mirrorVideo,
|
||||
child: VideoPlayer(videoController!),
|
||||
),
|
||||
),
|
||||
Screenshot(
|
||||
controller: screenshotController,
|
||||
child: LayersViewer(
|
||||
|
|
|
|||
|
|
@ -21,12 +21,14 @@ class ShareImageView extends StatefulWidget {
|
|||
{super.key,
|
||||
required this.imageBytesFuture,
|
||||
required this.isRealTwonly,
|
||||
required this.mirrorVideo,
|
||||
required this.maxShowTime,
|
||||
this.preselectedUser,
|
||||
required this.videoFilePath,
|
||||
this.enableVideoAudio});
|
||||
final Future<Uint8List?> imageBytesFuture;
|
||||
final bool isRealTwonly;
|
||||
final bool mirrorVideo;
|
||||
final int maxShowTime;
|
||||
final XFile? videoFilePath;
|
||||
final Contact? preselectedUser;
|
||||
|
|
@ -232,12 +234,14 @@ class _ShareImageView extends State<ShareImageView> {
|
|||
sendingImage = true;
|
||||
});
|
||||
sendMediaFile(
|
||||
_selectedUserIds.toList(),
|
||||
imageBytes!,
|
||||
widget.isRealTwonly,
|
||||
widget.maxShowTime,
|
||||
widget.videoFilePath,
|
||||
widget.enableVideoAudio);
|
||||
_selectedUserIds.toList(),
|
||||
imageBytes!,
|
||||
widget.isRealTwonly,
|
||||
widget.maxShowTime,
|
||||
widget.videoFilePath,
|
||||
widget.enableVideoAudio,
|
||||
widget.mirrorVideo,
|
||||
);
|
||||
if (context.mounted) {
|
||||
if (widget.preselectedUser != null) {
|
||||
Navigator.pop(context, true);
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
int maxShowTime = 999999;
|
||||
double progress = 0;
|
||||
bool isRealTwonly = false;
|
||||
bool mirrorVideo = false;
|
||||
bool isDownloading = false;
|
||||
bool showSendTextMessageInput = false;
|
||||
|
||||
|
|
@ -120,6 +121,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
maxShowTime = 999999;
|
||||
imageSaving = false;
|
||||
imageSaved = false;
|
||||
mirrorVideo = false;
|
||||
progress = 0;
|
||||
isDownloading = false;
|
||||
isRealTwonly = false;
|
||||
|
|
@ -226,6 +228,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
setState(() {
|
||||
maxShowTime = content.maxShowTime;
|
||||
isDownloading = false;
|
||||
mirrorVideo = content.mirrorVideo;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -412,7 +415,12 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
|||
child: Stack(
|
||||
children: [
|
||||
if (videoController != null)
|
||||
Positioned.fill(child: VideoPlayer(videoController!)),
|
||||
Positioned.fill(
|
||||
child: Transform.flip(
|
||||
flipX: mirrorVideo,
|
||||
child: VideoPlayer(videoController!),
|
||||
),
|
||||
),
|
||||
if (imageBytes != null)
|
||||
Positioned.fill(
|
||||
child: Image.memory(
|
||||
|
|
|
|||
Loading…
Reference in a new issue