mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 13:08:42 +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 DateTime messageSendAt;
|
||||||
late bool isVideo;
|
late bool isVideo;
|
||||||
late bool videoWithAudio;
|
late bool videoWithAudio;
|
||||||
|
late bool mirrorVideo;
|
||||||
|
|
||||||
MediaUploadMetadata();
|
MediaUploadMetadata();
|
||||||
|
|
||||||
|
|
@ -54,6 +55,7 @@ class MediaUploadMetadata {
|
||||||
return {
|
return {
|
||||||
'contactIds': contactIds,
|
'contactIds': contactIds,
|
||||||
'isRealTwonly': isRealTwonly,
|
'isRealTwonly': isRealTwonly,
|
||||||
|
'mirrorVideo': mirrorVideo,
|
||||||
'maxShowTime': maxShowTime,
|
'maxShowTime': maxShowTime,
|
||||||
'isVideo': isVideo,
|
'isVideo': isVideo,
|
||||||
'videoWithAudio': videoWithAudio,
|
'videoWithAudio': videoWithAudio,
|
||||||
|
|
@ -67,6 +69,7 @@ class MediaUploadMetadata {
|
||||||
state.isRealTwonly = json['isRealTwonly'];
|
state.isRealTwonly = json['isRealTwonly'];
|
||||||
state.videoWithAudio = json['videoWithAudio'];
|
state.videoWithAudio = json['videoWithAudio'];
|
||||||
state.isVideo = json['isVideo'];
|
state.isVideo = json['isVideo'];
|
||||||
|
state.mirrorVideo = json['mirrorVideo'];
|
||||||
state.maxShowTime = json['maxShowTime'];
|
state.maxShowTime = json['maxShowTime'];
|
||||||
state.maxShowTime = json['maxShowTime'];
|
state.maxShowTime = json['maxShowTime'];
|
||||||
state.messageSendAt = DateTime.parse(json['messageSendAt']);
|
state.messageSendAt = DateTime.parse(json['messageSendAt']);
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ class MediaMessageContent extends MessageContent {
|
||||||
final int maxShowTime;
|
final int maxShowTime;
|
||||||
final bool isRealTwonly;
|
final bool isRealTwonly;
|
||||||
final bool isVideo;
|
final bool isVideo;
|
||||||
|
final bool mirrorVideo;
|
||||||
final List<int>? downloadToken;
|
final List<int>? downloadToken;
|
||||||
final List<int>? encryptionKey;
|
final List<int>? encryptionKey;
|
||||||
final List<int>? encryptionMac;
|
final List<int>? encryptionMac;
|
||||||
|
|
@ -108,6 +109,7 @@ class MediaMessageContent extends MessageContent {
|
||||||
required this.maxShowTime,
|
required this.maxShowTime,
|
||||||
required this.isRealTwonly,
|
required this.isRealTwonly,
|
||||||
required this.isVideo,
|
required this.isVideo,
|
||||||
|
required this.mirrorVideo,
|
||||||
this.downloadToken,
|
this.downloadToken,
|
||||||
this.encryptionKey,
|
this.encryptionKey,
|
||||||
this.encryptionMac,
|
this.encryptionMac,
|
||||||
|
|
@ -131,6 +133,7 @@ class MediaMessageContent extends MessageContent {
|
||||||
maxShowTime: json['maxShowTime'],
|
maxShowTime: json['maxShowTime'],
|
||||||
isRealTwonly: json['isRealTwonly'],
|
isRealTwonly: json['isRealTwonly'],
|
||||||
isVideo: json['isVideo'] ?? false,
|
isVideo: json['isVideo'] ?? false,
|
||||||
|
mirrorVideo: json['mirrorVideo'] ?? false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,6 +147,7 @@ class MediaMessageContent extends MessageContent {
|
||||||
'isRealTwonly': isRealTwonly,
|
'isRealTwonly': isRealTwonly,
|
||||||
'maxShowTime': maxShowTime,
|
'maxShowTime': maxShowTime,
|
||||||
'isVideo': isVideo,
|
'isVideo': isVideo,
|
||||||
|
'mirrorVideo': mirrorVideo,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ Future sendMediaFile(
|
||||||
int maxShowTime,
|
int maxShowTime,
|
||||||
XFile? videoFilePath,
|
XFile? videoFilePath,
|
||||||
bool? enableVideoAudio,
|
bool? enableVideoAudio,
|
||||||
) async {
|
bool mirrorVideo) async {
|
||||||
MediaUploadMetadata metadata = MediaUploadMetadata();
|
MediaUploadMetadata metadata = MediaUploadMetadata();
|
||||||
metadata.contactIds = userIds;
|
metadata.contactIds = userIds;
|
||||||
metadata.isRealTwonly = isRealTwonly;
|
metadata.isRealTwonly = isRealTwonly;
|
||||||
|
|
@ -34,6 +34,7 @@ Future sendMediaFile(
|
||||||
metadata.isVideo = videoFilePath != null;
|
metadata.isVideo = videoFilePath != null;
|
||||||
metadata.videoWithAudio = enableVideoAudio != null && enableVideoAudio;
|
metadata.videoWithAudio = enableVideoAudio != null && enableVideoAudio;
|
||||||
metadata.maxShowTime = maxShowTime;
|
metadata.maxShowTime = maxShowTime;
|
||||||
|
metadata.mirrorVideo = mirrorVideo;
|
||||||
|
|
||||||
int? mediaUploadId = await twonlyDatabase.mediaUploadsDao.insertMediaUpload(
|
int? mediaUploadId = await twonlyDatabase.mediaUploadsDao.insertMediaUpload(
|
||||||
MediaUploadsCompanion(
|
MediaUploadsCompanion(
|
||||||
|
|
@ -62,7 +63,7 @@ Future retryMediaUpload() async {
|
||||||
final lockingHandleMediaFile = Mutex();
|
final lockingHandleMediaFile = Mutex();
|
||||||
|
|
||||||
Future handleSingleMediaFile(int mediaUploadId) async {
|
Future handleSingleMediaFile(int mediaUploadId) async {
|
||||||
// await lockingHandleMediaFile.protect(() async {
|
await lockingHandleMediaFile.protect(() async {
|
||||||
MediaUpload? media = await twonlyDatabase.mediaUploadsDao
|
MediaUpload? media = await twonlyDatabase.mediaUploadsDao
|
||||||
.getMediaUploadById(mediaUploadId)
|
.getMediaUploadById(mediaUploadId)
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
|
|
@ -97,10 +98,6 @@ Future handleSingleMediaFile(int mediaUploadId) async {
|
||||||
case UploadState.receiverNotified:
|
case UploadState.receiverNotified:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this will be called until there is an recoverable error OR
|
|
||||||
// the upload is ready
|
|
||||||
await handleSingleMediaFile(mediaUploadId);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// if the messageIds are already there notify the user about this error...
|
// if the messageIds are already there notify the user about this error...
|
||||||
if (media.messageIds != null) {
|
if (media.messageIds != null) {
|
||||||
|
|
@ -116,8 +113,12 @@ Future handleSingleMediaFile(int mediaUploadId) async {
|
||||||
await twonlyDatabase.mediaUploadsDao.deleteMediaUpload(mediaUploadId);
|
await twonlyDatabase.mediaUploadsDao.deleteMediaUpload(mediaUploadId);
|
||||||
Logger("media_send.dart")
|
Logger("media_send.dart")
|
||||||
.shout("Non recoverable error while sending media file: $e");
|
.shout("Non recoverable error while sending media file: $e");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// });
|
});
|
||||||
|
// this will be called until there is an recoverable error OR
|
||||||
|
// the upload is ready
|
||||||
|
await handleSingleMediaFile(mediaUploadId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future handleAddToMessageDb(MediaUpload media) async {
|
Future handleAddToMessageDb(MediaUpload media) async {
|
||||||
|
|
@ -137,6 +138,7 @@ Future handleAddToMessageDb(MediaUpload media) async {
|
||||||
maxShowTime: media.metadata.maxShowTime,
|
maxShowTime: media.metadata.maxShowTime,
|
||||||
isRealTwonly: media.metadata.isRealTwonly,
|
isRealTwonly: media.metadata.isRealTwonly,
|
||||||
isVideo: media.metadata.isVideo,
|
isVideo: media.metadata.isVideo,
|
||||||
|
mirrorVideo: media.metadata.mirrorVideo,
|
||||||
).toJson(),
|
).toJson(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -357,7 +359,11 @@ Future<bool> handleUpload(MediaUpload media) async {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
await deleteMediaFile(media, "encrypted");
|
await deleteMediaFile(media, "encrypted");
|
||||||
|
} catch (e) {
|
||||||
|
Logger("media_send.dart").shout("$e");
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -395,6 +401,7 @@ Future<bool> handleNotifyReceiver(MediaUpload media) async {
|
||||||
maxShowTime: media.metadata.maxShowTime,
|
maxShowTime: media.metadata.maxShowTime,
|
||||||
isRealTwonly: media.metadata.isRealTwonly,
|
isRealTwonly: media.metadata.isRealTwonly,
|
||||||
isVideo: media.metadata.isVideo,
|
isVideo: media.metadata.isVideo,
|
||||||
|
mirrorVideo: media.metadata.mirrorVideo,
|
||||||
encryptionKey: media.encryptionData!.encryptionKey,
|
encryptionKey: media.encryptionData!.encryptionKey,
|
||||||
encryptionMac: media.encryptionData!.encryptionMac,
|
encryptionMac: media.encryptionData!.encryptionMac,
|
||||||
encryptionNonce: media.encryptionData!.encryptionNonce,
|
encryptionNonce: media.encryptionData!.encryptionNonce,
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,7 @@ class _CameraPreviewViewState extends State<CameraPreviewView> {
|
||||||
videoFilePath: videoFilePath,
|
videoFilePath: videoFilePath,
|
||||||
imageBytes: imageBytes,
|
imageBytes: imageBytes,
|
||||||
sendTo: widget.sendTo,
|
sendTo: widget.sendTo,
|
||||||
|
mirrorVideo: isFront && Platform.isAndroid,
|
||||||
),
|
),
|
||||||
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
transitionsBuilder: (context, animation, secondaryAnimation, child) {
|
||||||
return child;
|
return child;
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,17 @@ List<Layer> removedLayers = [];
|
||||||
const gMediaShowInfinite = 999999;
|
const gMediaShowInfinite = 999999;
|
||||||
|
|
||||||
class ShareImageEditorView extends StatefulWidget {
|
class ShareImageEditorView extends StatefulWidget {
|
||||||
const ShareImageEditorView(
|
const ShareImageEditorView({
|
||||||
{super.key, this.imageBytes, this.sendTo, this.videoFilePath});
|
super.key,
|
||||||
|
this.imageBytes,
|
||||||
|
this.sendTo,
|
||||||
|
this.videoFilePath,
|
||||||
|
required this.mirrorVideo,
|
||||||
|
});
|
||||||
final Future<Uint8List?>? imageBytes;
|
final Future<Uint8List?>? imageBytes;
|
||||||
final XFile? videoFilePath;
|
final XFile? videoFilePath;
|
||||||
final Contact? sendTo;
|
final Contact? sendTo;
|
||||||
|
final bool mirrorVideo;
|
||||||
@override
|
@override
|
||||||
State<ShareImageEditorView> createState() => _ShareImageEditorView();
|
State<ShareImageEditorView> createState() => _ShareImageEditorView();
|
||||||
}
|
}
|
||||||
|
|
@ -289,6 +295,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
maxShowTime: maxShowTime,
|
maxShowTime: maxShowTime,
|
||||||
preselectedUser: widget.sendTo,
|
preselectedUser: widget.sendTo,
|
||||||
videoFilePath: widget.videoFilePath,
|
videoFilePath: widget.videoFilePath,
|
||||||
|
mirrorVideo: widget.mirrorVideo,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -357,6 +364,7 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
maxShowTime,
|
maxShowTime,
|
||||||
widget.videoFilePath,
|
widget.videoFilePath,
|
||||||
videoWithAudio,
|
videoWithAudio,
|
||||||
|
widget.mirrorVideo,
|
||||||
);
|
);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
|
|
@ -405,7 +413,12 @@ class _ShareImageEditorView extends State<ShareImageEditorView> {
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
if (videoController != null)
|
if (videoController != null)
|
||||||
Positioned.fill(child: VideoPlayer(videoController!)),
|
Positioned.fill(
|
||||||
|
child: Transform.flip(
|
||||||
|
flipX: widget.mirrorVideo,
|
||||||
|
child: VideoPlayer(videoController!),
|
||||||
|
),
|
||||||
|
),
|
||||||
Screenshot(
|
Screenshot(
|
||||||
controller: screenshotController,
|
controller: screenshotController,
|
||||||
child: LayersViewer(
|
child: LayersViewer(
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,14 @@ class ShareImageView extends StatefulWidget {
|
||||||
{super.key,
|
{super.key,
|
||||||
required this.imageBytesFuture,
|
required this.imageBytesFuture,
|
||||||
required this.isRealTwonly,
|
required this.isRealTwonly,
|
||||||
|
required this.mirrorVideo,
|
||||||
required this.maxShowTime,
|
required this.maxShowTime,
|
||||||
this.preselectedUser,
|
this.preselectedUser,
|
||||||
required this.videoFilePath,
|
required this.videoFilePath,
|
||||||
this.enableVideoAudio});
|
this.enableVideoAudio});
|
||||||
final Future<Uint8List?> imageBytesFuture;
|
final Future<Uint8List?> imageBytesFuture;
|
||||||
final bool isRealTwonly;
|
final bool isRealTwonly;
|
||||||
|
final bool mirrorVideo;
|
||||||
final int maxShowTime;
|
final int maxShowTime;
|
||||||
final XFile? videoFilePath;
|
final XFile? videoFilePath;
|
||||||
final Contact? preselectedUser;
|
final Contact? preselectedUser;
|
||||||
|
|
@ -237,7 +239,9 @@ class _ShareImageView extends State<ShareImageView> {
|
||||||
widget.isRealTwonly,
|
widget.isRealTwonly,
|
||||||
widget.maxShowTime,
|
widget.maxShowTime,
|
||||||
widget.videoFilePath,
|
widget.videoFilePath,
|
||||||
widget.enableVideoAudio);
|
widget.enableVideoAudio,
|
||||||
|
widget.mirrorVideo,
|
||||||
|
);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
if (widget.preselectedUser != null) {
|
if (widget.preselectedUser != null) {
|
||||||
Navigator.pop(context, true);
|
Navigator.pop(context, true);
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
int maxShowTime = 999999;
|
int maxShowTime = 999999;
|
||||||
double progress = 0;
|
double progress = 0;
|
||||||
bool isRealTwonly = false;
|
bool isRealTwonly = false;
|
||||||
|
bool mirrorVideo = false;
|
||||||
bool isDownloading = false;
|
bool isDownloading = false;
|
||||||
bool showSendTextMessageInput = false;
|
bool showSendTextMessageInput = false;
|
||||||
|
|
||||||
|
|
@ -120,6 +121,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
maxShowTime = 999999;
|
maxShowTime = 999999;
|
||||||
imageSaving = false;
|
imageSaving = false;
|
||||||
imageSaved = false;
|
imageSaved = false;
|
||||||
|
mirrorVideo = false;
|
||||||
progress = 0;
|
progress = 0;
|
||||||
isDownloading = false;
|
isDownloading = false;
|
||||||
isRealTwonly = false;
|
isRealTwonly = false;
|
||||||
|
|
@ -226,6 +228,7 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
setState(() {
|
setState(() {
|
||||||
maxShowTime = content.maxShowTime;
|
maxShowTime = content.maxShowTime;
|
||||||
isDownloading = false;
|
isDownloading = false;
|
||||||
|
mirrorVideo = content.mirrorVideo;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -412,7 +415,12 @@ class _MediaViewerViewState extends State<MediaViewerView> {
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
if (videoController != null)
|
if (videoController != null)
|
||||||
Positioned.fill(child: VideoPlayer(videoController!)),
|
Positioned.fill(
|
||||||
|
child: Transform.flip(
|
||||||
|
flipX: mirrorVideo,
|
||||||
|
child: VideoPlayer(videoController!),
|
||||||
|
),
|
||||||
|
),
|
||||||
if (imageBytes != null)
|
if (imageBytes != null)
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
child: Image.memory(
|
child: Image.memory(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue