From fae5ca3d25f6a0207cfc1e0afa02ded7feaacbb2 Mon Sep 17 00:00:00 2001 From: otsmr Date: Thu, 21 May 2026 16:12:19 +0200 Subject: [PATCH] Fix: Issues with the camera initialization --- CHANGELOG.md | 3 +- fastlane/Appfile | 2 + fastlane/Fastfile | 15 +++ fastlane/README.md | 32 +++++ fastlane/report.xml | 18 +++ lib/globals.dart | 1 + lib/src/visual/helpers/screenshot.helper.dart | 25 +++- .../views/camera/add_new_shortcut.view.dart | 1 + .../camera_preview_controller_view.dart | 100 ++++++++------- .../main_camera_controller.dart | 12 +- .../zoom_selector.dart | 119 ++++++------------ .../share_image_contact_selection.view.dart | 1 + .../best_friends_selector.dart | 2 +- .../views/camera/share_image_editor.view.dart | 34 +++-- .../visual/views/chats/chat_list.view.dart | 1 + .../group_create_select_group_name.view.dart | 1 + .../group_create_select_members.view.dart | 1 + lib/src/visual/views/home.view.dart | 18 +-- .../select_additional_users.view.dart | 1 + .../views/shared/select_contacts.view.dart | 1 + pubspec.yaml | 2 +- 21 files changed, 228 insertions(+), 162 deletions(-) create mode 100644 fastlane/Appfile create mode 100644 fastlane/Fastfile create mode 100644 fastlane/README.md create mode 100644 fastlane/report.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 580246a1..b4d58459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## 0.2.17 +## 0.2.18 - New: Adds an "Ask a Friend" button to new contact suggestions. - New: Adds security profiles. @@ -10,6 +10,7 @@ - Fix: Issue with receiving messages when user closed app while decrypting - Fix: Background message fetching reliability. - Fix: Issue with focus changing when taking a picture +- Fix: Issues with the camera initialization ## 0.2.16 diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 00000000..0df77ab7 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,2 @@ +json_key_file(ENV["GOOGLE_PLAY_JSON_KEY_PATH"] || "../../local_data/accesskeys/upload_track_releases_google_play.json") +package_name("eu.twonly") # Your application ID diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 00000000..966178d2 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,15 @@ +default_platform(:android) + +platform :android do + desc "Submit a new App Bundle to the Google Play Internal Track" + lane :internal do + # This lane assumes that `flutter build appbundle` has already been run from the flutter root. + upload_to_play_store( + track: 'internal', + aab: 'build/app/outputs/bundle/release/app-release.aab', + skip_upload_metadata: true, + skip_upload_images: true, + skip_upload_screenshots: true + ) + end +end diff --git a/fastlane/README.md b/fastlane/README.md new file mode 100644 index 00000000..54009708 --- /dev/null +++ b/fastlane/README.md @@ -0,0 +1,32 @@ +fastlane documentation +---- + +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +```sh +xcode-select --install +``` + +For _fastlane_ installation instructions, see [Installing _fastlane_](https://docs.fastlane.tools/#installing-fastlane) + +# Available Actions + +## Android + +### android internal + +```sh +[bundle exec] fastlane android internal +``` + +Submit a new App Bundle to the Google Play Internal Track + +---- + +This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. + +More information about _fastlane_ can be found on [fastlane.tools](https://fastlane.tools). + +The documentation of _fastlane_ can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/fastlane/report.xml b/fastlane/report.xml new file mode 100644 index 00000000..388ef818 --- /dev/null +++ b/fastlane/report.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/lib/globals.dart b/lib/globals.dart index 90bc7deb..35de1911 100644 --- a/lib/globals.dart +++ b/lib/globals.dart @@ -33,4 +33,5 @@ class AppState { static bool allowErrorTrackingViaSentry = false; static bool gotMessageFromServer = false; static int latestAppVersionId = 116; + static bool hasCameraPermissions = false; } diff --git a/lib/src/visual/helpers/screenshot.helper.dart b/lib/src/visual/helpers/screenshot.helper.dart index 02c906d9..8605a38e 100644 --- a/lib/src/visual/helpers/screenshot.helper.dart +++ b/lib/src/visual/helpers/screenshot.helper.dart @@ -46,24 +46,43 @@ class ScreenshotController { } late GlobalKey _containerKey; - Future capture({double? pixelRatio}) async { + Future capture({ + double? pixelRatio, + int retries = 20, + }) async { try { final findRenderObject = _containerKey.currentContext?.findRenderObject(); if (findRenderObject == null) { return null; } final boundary = findRenderObject as RenderRepaintBoundary; + final context = _containerKey.currentContext; var tmpPixelRatio = pixelRatio; if (tmpPixelRatio == null) { if (context != null && context.mounted) { - tmpPixelRatio = - tmpPixelRatio ?? MediaQuery.of(context).devicePixelRatio; + tmpPixelRatio = tmpPixelRatio ?? MediaQuery.of(context).devicePixelRatio; } } final image = await boundary.toImage(pixelRatio: tmpPixelRatio ?? 1); return ScreenshotImageHelper(image: image); } catch (e) { + if (retries > 0) { + final completer = Completer(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + final result = await capture( + pixelRatio: pixelRatio, + retries: retries - 1, + ); + completer.complete(result); + }); + Timer(const Duration(milliseconds: 50), () { + if (!completer.isCompleted) { + WidgetsBinding.instance.scheduleFrame(); + } + }); + return completer.future; + } Log.error(e); } return null; diff --git a/lib/src/visual/views/camera/add_new_shortcut.view.dart b/lib/src/visual/views/camera/add_new_shortcut.view.dart index 11a611e4..41291ace 100644 --- a/lib/src/visual/views/camera/add_new_shortcut.view.dart +++ b/lib/src/visual/views/camera/add_new_shortcut.view.dart @@ -203,6 +203,7 @@ class _StartNewChatView extends State { const SizedBox(width: 8), ], ), + floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation, floatingActionButton: FilledButton.icon( onPressed: (_selectedGroups.isEmpty || shortcutEmoji == null) ? null diff --git a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart index 2ccec5ac..1a3e52a3 100644 --- a/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart +++ b/lib/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart @@ -48,7 +48,7 @@ class SelectedCameraDetails { bool cameraLoaded = false; } -class CameraPreviewControllerView extends StatelessWidget { +class CameraPreviewControllerView extends StatefulWidget { const CameraPreviewControllerView({ required this.mainController, required this.isVisible, @@ -62,23 +62,52 @@ class CameraPreviewControllerView extends StatelessWidget { final bool isVisible; final bool hideControllers; + @override + State createState() => _CameraPreviewControllerViewState(); +} + +class _CameraPreviewControllerViewState extends State { + Future? _permissionsFuture; + + @override + void initState() { + super.initState(); + if (!AppState.hasCameraPermissions) { + _permissionsFuture = checkPermissions().then((hasPermission) { + if (hasPermission) { + AppState.hasCameraPermissions = true; + } + return hasPermission; + }); + } + } + @override Widget build(BuildContext context) { - return FutureBuilder( - future: checkPermissions(), + if (AppState.hasCameraPermissions) { + return CameraPreviewView( + sendToGroup: widget.sendToGroup, + mainCameraController: widget.mainController, + isVisible: widget.isVisible, + hideControllers: widget.hideControllers, + ); + } + + return FutureBuilder( + future: _permissionsFuture, builder: (context, snap) { if (snap.hasData) { if (snap.data!) { return CameraPreviewView( - sendToGroup: sendToGroup, - mainCameraController: mainController, - isVisible: isVisible, - hideControllers: hideControllers, + sendToGroup: widget.sendToGroup, + mainCameraController: widget.mainController, + isVisible: widget.isVisible, + hideControllers: widget.hideControllers, ); } else { return PermissionHandlerView( onSuccess: () { - mainController.selectCamera(0, true); + widget.mainController.selectCamera(0, true); }, ); } @@ -210,8 +239,7 @@ class _CameraPreviewViewState extends State { Future initAsync() async { _hasAudioPermission = await Permission.microphone.isGranted; - if (!_hasAudioPermission && - !userService.currentUser.requestedAudioPermission) { + if (!_hasAudioPermission && !userService.currentUser.requestedAudioPermission) { await UserService.update((u) => u.requestedAudioPermission = true); await requestMicrophonePermission(); } @@ -232,8 +260,7 @@ class _CameraPreviewViewState extends State { } Future updateScaleFactor(double newScale) async { - if (mc.selectedCameraDetails.scaleFactor == newScale || - mc.cameraController == null) { + if (mc.selectedCameraDetails.scaleFactor == newScale || mc.cameraController == null) { return; } await mc.cameraController?.setZoomLevel( @@ -316,9 +343,7 @@ class _CameraPreviewViewState extends State { bool sharedFromGallery = false, MediaType? mediaType, }) async { - final type = - mediaType ?? - ((videoFilePath != null) ? MediaType.video : MediaType.image); + final type = mediaType ?? ((videoFilePath != null) ? MediaType.video : MediaType.image); final mediaFileService = await initializeMediaUpload( type, userService.currentUser.defaultShowTime, @@ -359,10 +384,9 @@ class _CameraPreviewViewState extends State { mainCameraController: mc, previewLink: mc.sharedLinkForPreview, ), - transitionsBuilder: - (context, animation, secondaryAnimation, child) { - return child; - }, + transitionsBuilder: (context, animation, secondaryAnimation, child) { + return child; + }, transitionDuration: Duration.zero, reverseTransitionDuration: Duration.zero, ), @@ -392,16 +416,13 @@ class _CameraPreviewViewState extends State { return false; } - bool get isFront => - mc.cameraController?.description.lensDirection == - CameraLensDirection.front; + bool get isFront => mc.cameraController?.description.lensDirection == CameraLensDirection.front; Future onPanUpdate(dynamic details) async { if (details == null) { return; } - if (mc.cameraController == null || - !mc.cameraController!.value.isInitialized) { + if (mc.cameraController == null || !mc.cameraController!.value.isInitialized) { return; } @@ -530,8 +551,7 @@ class _CameraPreviewViewState extends State { } Future startVideoRecording() async { - if (mc.cameraController != null && - mc.cameraController!.value.isRecordingVideo) { + if (mc.cameraController != null && mc.cameraController!.value.isRecordingVideo) { return; } setState(() { @@ -551,8 +571,7 @@ class _CameraPreviewViewState extends State { _currentTime = clock.now(); }); if (_videoRecordingStarted != null && - _currentTime.difference(_videoRecordingStarted!).inSeconds >= - maxVideoRecordingTime) { + _currentTime.difference(_videoRecordingStarted!).inSeconds >= maxVideoRecordingTime) { timer.cancel(); _videoRecordingTimer = null; stopVideoRecording(); @@ -589,8 +608,7 @@ class _CameraPreviewViewState extends State { _videoRecordingLocked = false; }); - if (mc.cameraController == null || - !mc.cameraController!.value.isRecordingVideo) { + if (mc.cameraController == null || !mc.cameraController!.value.isRecordingVideo) { return; } @@ -618,8 +636,7 @@ class _CameraPreviewViewState extends State { @override Widget build(BuildContext context) { - if (mc.selectedCameraDetails.cameraId >= AppEnvironment.cameras.length || - mc.cameraController == null) { + if (mc.selectedCameraDetails.cameraId >= AppEnvironment.cameras.length || mc.cameraController == null) { return Container(); } return StreamBuilder( @@ -643,9 +660,7 @@ class _CameraPreviewViewState extends State { _baseScaleFactor = mc.selectedCameraDetails.scaleFactor; }); // Get the position of the pointer - final renderBox = - keyTriggerButton.currentContext!.findRenderObject()! - as RenderBox; + final renderBox = keyTriggerButton.currentContext!.findRenderObject()! as RenderBox; final localPosition = renderBox.globalToLocal( details.globalPosition, ); @@ -681,24 +696,18 @@ class _CameraPreviewViewState extends State { ), ), ), - if (!mc.isSharePreviewIsShown && - widget.sendToGroup != null && - !mc.isVideoRecording) + if (!mc.isSharePreviewIsShown && widget.sendToGroup != null && !mc.isVideoRecording) ShowTitleText( title: widget.sendToGroup!.groupName, desc: context.lang.cameraPreviewSendTo, ), - if (!mc.isSharePreviewIsShown && - mc.sharedLinkForPreview != null && - !mc.isVideoRecording) + if (!mc.isSharePreviewIsShown && mc.sharedLinkForPreview != null && !mc.isVideoRecording) ShowTitleText( title: mc.sharedLinkForPreview?.host ?? '', desc: 'Link', isLink: true, ), - if (!mc.isSharePreviewIsShown && - !mc.isVideoRecording && - !widget.hideControllers) + if (!mc.isSharePreviewIsShown && !mc.isVideoRecording && !widget.hideControllers) CameraTopActions( selectedCameraDetails: mc.selectedCameraDetails, hasAudioPermission: _hasAudioPermission, @@ -742,8 +751,7 @@ class _CameraPreviewViewState extends State { videoRecordingStarted: _videoRecordingStarted, maxVideoRecordingTime: maxVideoRecordingTime, ), - if (!mc.isSharePreviewIsShown && widget.sendToGroup != null || - widget.hideControllers) + if (!mc.isSharePreviewIsShown && widget.sendToGroup != null || widget.hideControllers) Positioned( left: 5, top: 10, diff --git a/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart b/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart index 3cf2c903..1a2cae41 100644 --- a/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart +++ b/lib/src/visual/views/camera/camera_preview_components/main_camera_controller.dart @@ -87,8 +87,10 @@ class MainCameraController { Future? _initializeFuture; Future? _pendingDisposal; + int _cameraSessionId = 0; Future closeCamera() async { + _cameraSessionId++; contactsVerified = {}; scannedNewProfiles = {}; scannedUrl = null; @@ -119,11 +121,14 @@ class MainCameraController { } Future selectCamera(int sCameraId, bool init) async { - await _pendingDisposal; initCameraStarted = true; + final sessionId = ++_cameraSessionId; + await _pendingDisposal; + if (sessionId != _cameraSessionId) return; if (AppEnvironment.cameras.isEmpty) { AppEnvironment.cameras = await availableCameras(); + if (sessionId != _cameraSessionId) return; } var cameraId = sCameraId; @@ -145,10 +150,13 @@ class MainCameraController { selectedCameraDetails.isZoomAble = false; if (cameraController == null) { + final hasMic = await Permission.microphone.isGranted; + if (sessionId != _cameraSessionId) return; + cameraController = CameraController( AppEnvironment.cameras[cameraId], ResolutionPreset.high, - enableAudio: await Permission.microphone.isGranted, + enableAudio: hasMic, imageFormatGroup: Platform.isAndroid ? ImageFormatGroup.nv21 : ImageFormatGroup.bgra8888, ); try { diff --git a/lib/src/visual/views/camera/camera_preview_components/zoom_selector.dart b/lib/src/visual/views/camera/camera_preview_components/zoom_selector.dart index b0a22bf5..dc40aec9 100644 --- a/lib/src/visual/views/camera/camera_preview_components/zoom_selector.dart +++ b/lib/src/visual/views/camera/camera_preview_components/zoom_selector.dart @@ -8,7 +8,15 @@ import 'package:flutter/material.dart'; import 'package:twonly/globals.dart'; import 'package:twonly/src/visual/views/camera/camera_preview_components/camera_preview_controller_view.dart'; -class CameraZoomButtons extends StatefulWidget { +String beautifulZoomScale(double scale) { + var tmp = scale.toStringAsFixed(1); + if (tmp[0] == '0') { + tmp = tmp.substring(1, tmp.length); + } + return tmp; +} + +class CameraZoomButtons extends StatelessWidget { const CameraZoomButtons({ required this.controller, required this.updateScaleFactor, @@ -25,32 +33,10 @@ class CameraZoomButtons extends StatefulWidget { final Future Function(int sCameraId, bool init) selectCamera; @override - State createState() => _CameraZoomButtonsState(); -} - -String beautifulZoomScale(double scale) { - var tmp = scale.toStringAsFixed(1); - if (tmp[0] == '0') { - tmp = tmp.substring(1, tmp.length); - } - return tmp; -} - -class _CameraZoomButtonsState extends State { - bool showWideAngleZoom = false; - bool showWideAngleZoomIOS = false; - bool _isDisposed = false; - int? _wideCameraIndex; - - @override - void initState() { - super.initState(); - unawaited(initAsync()); - } - - Future initAsync() async { - showWideAngleZoom = (await widget.controller.getMinZoomLevel()) < 1; + Widget build(BuildContext context) { + final showWideAngleZoom = selectedCameraDetails.minAvailableZoom < 1; + int? wideCameraIndex; var index = AppEnvironment.cameras.indexWhere( (t) => t.lensType == CameraLensType.ultraWide, ); @@ -60,33 +46,13 @@ class _CameraZoomButtonsState extends State { ); } if (index != -1) { - _wideCameraIndex = index; + wideCameraIndex = index; } - final isFront = - widget.controller.description.lensDirection == - CameraLensDirection.front; + final isFront = controller.description.lensDirection == CameraLensDirection.front; - if (!showWideAngleZoom && - Platform.isIOS && - _wideCameraIndex != null && - !isFront) { - showWideAngleZoomIOS = true; - } else { - showWideAngleZoomIOS = false; - } - if (_isDisposed) return; - setState(() {}); - } + final showWideAngleZoomIOS = !showWideAngleZoom && Platform.isIOS && wideCameraIndex != null && !isFront; - @override - void dispose() { - _isDisposed = true; // Set the flag to true when disposing - super.dispose(); - } - - @override - Widget build(BuildContext context) { final zoomButtonStyle = TextButton.styleFrom( padding: EdgeInsets.zero, foregroundColor: Colors.white, @@ -97,24 +63,21 @@ class _CameraZoomButtonsState extends State { const zoomTextStyle = TextStyle(fontSize: 13); final isSmallerFocused = - widget.scaleFactor < 1 || - (showWideAngleZoomIOS && - widget.selectedCameraDetails.cameraId == _wideCameraIndex); + scaleFactor < 1 || (showWideAngleZoomIOS && selectedCameraDetails.cameraId == wideCameraIndex); final isMiddleFocused = - widget.scaleFactor >= 1 && - widget.scaleFactor < 2 && - !(showWideAngleZoomIOS && - widget.selectedCameraDetails.cameraId == _wideCameraIndex); + scaleFactor >= 1 && + scaleFactor < 2 && + !(showWideAngleZoomIOS && selectedCameraDetails.cameraId == wideCameraIndex); final maxLevel = max( - min(widget.selectedCameraDetails.maxAvailableZoom, 2), - widget.scaleFactor, + min(selectedCameraDetails.maxAvailableZoom, 2), + scaleFactor, ); final minLevel = beautifulZoomScale( - widget.selectedCameraDetails.minAvailableZoom, + selectedCameraDetails.minAvailableZoom, ); - final currentLevel = beautifulZoomScale(widget.scaleFactor); + final currentLevel = beautifulZoomScale(scaleFactor); return Center( child: ClipRRect( borderRadius: BorderRadius.circular(40), @@ -132,20 +95,18 @@ class _CameraZoomButtonsState extends State { ), onPressed: () async { if (showWideAngleZoomIOS) { - if (_wideCameraIndex != null) { - await widget.selectCamera(_wideCameraIndex!, true); + if (wideCameraIndex != null) { + await selectCamera(wideCameraIndex, true); } } else { - final level = await widget.controller.getMinZoomLevel(); - widget.updateScaleFactor(level); + final level = await controller.getMinZoomLevel(); + updateScaleFactor(level); } }, child: showWideAngleZoomIOS ? const Text('0.5') : Text( - widget.scaleFactor < 1 - ? '${currentLevel}x' - : '${minLevel}x', + scaleFactor < 1 ? '${currentLevel}x' : '${minLevel}x', style: zoomTextStyle, ), ), @@ -156,39 +117,33 @@ class _CameraZoomButtonsState extends State { ), ), onPressed: () async { - if (showWideAngleZoomIOS && - widget.selectedCameraDetails.cameraId == - _wideCameraIndex) { - await widget.selectCamera(0, true); + if (showWideAngleZoomIOS && selectedCameraDetails.cameraId == wideCameraIndex) { + await selectCamera(0, true); } else { - widget.updateScaleFactor(1.0); + updateScaleFactor(1.0); } }, child: Text( - isMiddleFocused - ? '${beautifulZoomScale(widget.scaleFactor)}x' - : '1.0x', + isMiddleFocused ? '${beautifulZoomScale(scaleFactor)}x' : '1.0x', style: zoomTextStyle, ), ), TextButton( style: zoomButtonStyle.copyWith( foregroundColor: WidgetStateProperty.all( - (widget.scaleFactor >= 2) ? Colors.yellow : Colors.white, + (scaleFactor >= 2) ? Colors.yellow : Colors.white, ), ), onPressed: () async { final level = min( - await widget.controller.getMaxZoomLevel(), + await controller.getMaxZoomLevel(), 2, ).toDouble(); - if (showWideAngleZoomIOS && - widget.selectedCameraDetails.cameraId == - _wideCameraIndex) { - await widget.selectCamera(0, true); + if (showWideAngleZoomIOS && selectedCameraDetails.cameraId == wideCameraIndex) { + await selectCamera(0, true); } - widget.updateScaleFactor(level); + updateScaleFactor(level); }, child: Text( '${beautifulZoomScale(maxLevel.toDouble())}x', diff --git a/lib/src/visual/views/camera/share_image_contact_selection.view.dart b/lib/src/visual/views/camera/share_image_contact_selection.view.dart index c968a3c0..4a6f7a40 100644 --- a/lib/src/visual/views/camera/share_image_contact_selection.view.dart +++ b/lib/src/visual/views/camera/share_image_contact_selection.view.dart @@ -254,6 +254,7 @@ class _ShareImageView extends State { ), ), ), + floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation, floatingActionButton: _allGroups.isEmpty ? null : SizedBox( diff --git a/lib/src/visual/views/camera/share_image_contact_selection_components/best_friends_selector.dart b/lib/src/visual/views/camera/share_image_contact_selection_components/best_friends_selector.dart index 78c87516..91f8a70b 100644 --- a/lib/src/visual/views/camera/share_image_contact_selection_components/best_friends_selector.dart +++ b/lib/src/visual/views/camera/share_image_contact_selection_components/best_friends_selector.dart @@ -156,7 +156,7 @@ class UserCheckbox extends StatelessWidget { Row( children: [ Text( - substringBy(group.groupName, 12), + substringBy(group.groupName, 11), overflow: TextOverflow.ellipsis, ), ], diff --git a/lib/src/visual/views/camera/share_image_editor.view.dart b/lib/src/visual/views/camera/share_image_editor.view.dart index d2b29bc7..d09ad5e5 100644 --- a/lib/src/visual/views/camera/share_image_editor.view.dart +++ b/lib/src/visual/views/camera/share_image_editor.view.dart @@ -214,8 +214,7 @@ class _ShareImageEditorView extends State { List get actionsAtTheRight { if (layers.isNotEmpty && - (layers.first.isEditing || - (layers.last.isEditing && layers.last.hasCustomActionButtons))) { + (layers.first.isEditing || (layers.last.isEditing && layers.last.hasCustomActionButtons))) { return []; } return [ @@ -291,13 +290,9 @@ class _ShareImageEditorView extends State { if (media.type == MediaType.video) ...[ const SizedBox(height: 8), ActionButton( - (mediaService.removeAudio) - ? Icons.volume_off_rounded - : Icons.volume_up_rounded, + (mediaService.removeAudio) ? Icons.volume_off_rounded : Icons.volume_up_rounded, tooltipText: 'Enable Audio in Video', - color: (mediaService.removeAudio) - ? Colors.white.withAlpha(160) - : Colors.white, + color: (mediaService.removeAudio) ? Colors.white.withAlpha(160) : Colors.white, onPressed: () async { await mediaService.toggleRemoveAudio(); if (mediaService.removeAudio) { @@ -335,9 +330,7 @@ class _ShareImageEditorView extends State { ActionButton( FontAwesomeIcons.shieldHeart, tooltipText: context.lang.protectAsARealTwonly, - color: media.requiresAuthentication - ? Theme.of(context).colorScheme.primary - : Colors.white, + color: media.requiresAuthentication ? Theme.of(context).colorScheme.primary : Colors.white, onPressed: () async { await mediaService.setRequiresAuth(!media.requiresAuthentication); selectedGroupIds = HashSet(); @@ -383,8 +376,7 @@ class _ShareImageEditorView extends State { List get actionsAtTheTop { if (layers.isNotEmpty && - (layers.first.isEditing || - (layers.last.isEditing && layers.last.hasCustomActionButtons))) { + (layers.first.isEditing || (layers.last.isEditing && layers.last.hasCustomActionButtons))) { return []; } return [ @@ -474,6 +466,14 @@ class _ShareImageEditorView extends State { return (layers.first as BackgroundLayerData).image.image; } } + if (layers.length == 2) { + final filterLayer = layers[1]; + if (layers.first is BackgroundLayerData && filterLayer is FilterLayerData) { + if (filterLayer.page == 1) { + return (layers.first as BackgroundLayerData).image.image; + } + } + } for (final x in layers) { x.showCustomButtons = false; @@ -513,15 +513,15 @@ class _ShareImageEditorView extends State { } } ScreenshotImageHelper? image; - var bytes = await widget.screenshotImage?.getBytes(); if (media.type == MediaType.gif) { + final bytes = await widget.screenshotImage?.getBytes(); if (bytes != null) { mediaService.originalPath.writeAsBytesSync(bytes.toList()); } } else { image = await getEditedImageBytes(); if (image == null) return null; - bytes = await image.getBytes(); + final bytes = await image.getBytes(); if (bytes == null) { Log.error('imageBytes are empty'); return null; @@ -657,9 +657,7 @@ class _ShareImageEditorView extends State { await askToCloseThenClose(); }, child: Scaffold( - backgroundColor: widget.sharedFromGallery - ? null - : Colors.white.withAlpha(0), + backgroundColor: widget.sharedFromGallery ? null : Colors.white.withAlpha(0), resizeToAvoidBottomInset: false, body: Stack( fit: StackFit.expand, diff --git a/lib/src/visual/views/chats/chat_list.view.dart b/lib/src/visual/views/chats/chat_list.view.dart index d45536cb..4feb177f 100644 --- a/lib/src/visual/views/chats/chat_list.view.dart +++ b/lib/src/visual/views/chats/chat_list.view.dart @@ -283,6 +283,7 @@ class _ChatListViewState extends State { ], ), ), + floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation, floatingActionButton: !_hasContacts ? null : Padding( diff --git a/lib/src/visual/views/groups/group_create_select_group_name.view.dart b/lib/src/visual/views/groups/group_create_select_group_name.view.dart index 974e6fc8..1bfc7132 100644 --- a/lib/src/visual/views/groups/group_create_select_group_name.view.dart +++ b/lib/src/visual/views/groups/group_create_select_group_name.view.dart @@ -58,6 +58,7 @@ class _GroupCreateSelectGroupNameViewState appBar: AppBar( title: Text(context.lang.selectGroupName), ), + floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation, floatingActionButton: FilledButton.icon( onPressed: (textFieldGroupName.text.isEmpty || _isLoading) ? null diff --git a/lib/src/visual/views/groups/group_create_select_members.view.dart b/lib/src/visual/views/groups/group_create_select_members.view.dart index 5c6a0d0d..42930b06 100644 --- a/lib/src/visual/views/groups/group_create_select_members.view.dart +++ b/lib/src/visual/views/groups/group_create_select_members.view.dart @@ -129,6 +129,7 @@ class _StartNewChatView extends State { : context.lang.addMember, ), ), + floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation, floatingActionButton: FilledButton.icon( onPressed: selectedUsers.isEmpty ? null : submitChanges, label: Text( diff --git a/lib/src/visual/views/home.view.dart b/lib/src/visual/views/home.view.dart index b44b87f6..791106e2 100644 --- a/lib/src/visual/views/home.view.dart +++ b/lib/src/visual/views/home.view.dart @@ -267,15 +267,17 @@ class HomeViewState extends State { ), ), ), - if (_offsetRatio == 0) - Positioned.fill( - child: GestureDetector( - behavior: HitTestBehavior.translucent, - onDoubleTap: _mainCameraController.onDoubleTap, - onTapDown: _mainCameraController.onTapDown, - ), - ), + Positioned.fill( + child: _offsetRatio == 0 + ? GestureDetector( + behavior: HitTestBehavior.translucent, + onDoubleTap: _mainCameraController.onDoubleTap, + onTapDown: _mainCameraController.onTapDown, + ) + : const SizedBox.shrink(), + ), Positioned( + key: const ValueKey('camera_controls'), left: 0, top: 0, right: 0, diff --git a/lib/src/visual/views/settings/subscription/select_additional_users.view.dart b/lib/src/visual/views/settings/subscription/select_additional_users.view.dart index 21252dcd..051d4982 100644 --- a/lib/src/visual/views/settings/subscription/select_additional_users.view.dart +++ b/lib/src/visual/views/settings/subscription/select_additional_users.view.dart @@ -99,6 +99,7 @@ class _SelectAdditionalUsers extends State { appBar: AppBar( title: Text(context.lang.additionalUserSelectTitle), ), + floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation, floatingActionButton: FilledButton.icon( onPressed: selectedUsers.isEmpty ? null diff --git a/lib/src/visual/views/shared/select_contacts.view.dart b/lib/src/visual/views/shared/select_contacts.view.dart index 53684c33..80e87310 100644 --- a/lib/src/visual/views/shared/select_contacts.view.dart +++ b/lib/src/visual/views/shared/select_contacts.view.dart @@ -112,6 +112,7 @@ class _SelectAdditionalUsers extends State { appBar: AppBar( title: Text(widget.text.title), ), + floatingActionButtonAnimator: FloatingActionButtonAnimator.noAnimation, floatingActionButton: FilledButton.icon( onPressed: selectedUsers.isEmpty ? null diff --git a/pubspec.yaml b/pubspec.yaml index 2c843a3d..63b56ded 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,7 +3,7 @@ description: "twonly, a privacy-friendly way to connect with friends through sec publish_to: 'none' -version: 0.2.16+125 +version: 0.2.17+126 environment: sdk: ^3.11.0