mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-09-01 07:04:07 +00:00
fix issue with media shares via intent
This commit is contained in:
parent
280519d63a
commit
0c16188877
5 changed files with 89 additions and 53 deletions
|
|
@ -6,6 +6,7 @@
|
||||||
- Improve: Backup screen clearer and easier to understand
|
- Improve: Backup screen clearer and easier to understand
|
||||||
- Improve: Show username above messages in group chats
|
- Improve: Show username above messages in group chats
|
||||||
- Fix: Background audio correctly pauses and resumes when viewing videos
|
- Fix: Background audio correctly pauses and resumes when viewing videos
|
||||||
|
- Fix: Multiple bug fixes
|
||||||
|
|
||||||
## 0.5.0
|
## 0.5.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/keyvalue.keys.dart';
|
import 'package:twonly/src/constants/keyvalue.keys.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
import 'package:twonly/src/localization/generated/app_localizations.dart';
|
||||||
import 'package:twonly/src/model/json/onboarding_state.model.dart';
|
import 'package:twonly/src/model/json/onboarding_state.model.dart';
|
||||||
import 'package:twonly/src/providers/routing.provider.dart';
|
import 'package:twonly/src/providers/routing.provider.dart';
|
||||||
|
|
@ -180,9 +181,17 @@ class _AppMainWidgetState extends State<AppMainWidget> {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
void handleShareMedia(String path, MediaType type) {
|
||||||
|
HomeViewState.pendingSharedMedia = (path, type);
|
||||||
|
routerProvider.go(Routes.home);
|
||||||
|
HomeViewState.streamHomeViewPageIndex.add(0);
|
||||||
|
HomeViewState.streamSharedMedia.add((path, type));
|
||||||
|
}
|
||||||
|
|
||||||
_intentStreamSub = initIntentStreams(
|
_intentStreamSub = initIntentStreams(
|
||||||
context,
|
context,
|
||||||
handleShareLink,
|
handleShareLink,
|
||||||
|
handleShareMedia,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -227,11 +227,19 @@ Future<void> finishStartedPreprocessing() async {
|
||||||
mediaFile.mediaId,
|
mediaFile.mediaId,
|
||||||
);
|
);
|
||||||
if (messages.isEmpty) {
|
if (messages.isEmpty) {
|
||||||
Log.info(
|
if (mediaFile.createdAt.isBefore(
|
||||||
'Deleted orphaned media file ${mediaFile.mediaId} as no messages reference it.',
|
clock.now().subtract(const Duration(hours: 1)),
|
||||||
);
|
)) {
|
||||||
MediaFileService(mediaFile).fullMediaRemoval();
|
Log.info(
|
||||||
await twonlyDB.mediaFilesDao.deleteMediaFile(mediaFile.mediaId);
|
'Deleted orphaned media file ${mediaFile.mediaId} as no messages reference it.',
|
||||||
|
);
|
||||||
|
MediaFileService(mediaFile).fullMediaRemoval();
|
||||||
|
await twonlyDB.mediaFilesDao.deleteMediaFile(mediaFile.mediaId);
|
||||||
|
} else {
|
||||||
|
Log.info(
|
||||||
|
'Media file ${mediaFile.mediaId} has no messages, but is too new to be deleted by finishStartedPreprocessing. Skipping.',
|
||||||
|
);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -484,12 +492,20 @@ Future<void> _startBackgroundMediaUploadInternal(
|
||||||
mediaService.mediaFile.mediaId,
|
mediaService.mediaFile.mediaId,
|
||||||
);
|
);
|
||||||
if (messages.isEmpty) {
|
if (messages.isEmpty) {
|
||||||
Log.warn(
|
if (mediaService.mediaFile.createdAt.isBefore(
|
||||||
'Media files ${mediaService.mediaFile.mediaId} has no original, temp, or stored path. Removing it from DB as files are not existent.',
|
clock.now().subtract(const Duration(hours: 1)),
|
||||||
);
|
)) {
|
||||||
await twonlyDB.mediaFilesDao.deleteMediaFile(
|
Log.warn(
|
||||||
mediaService.mediaFile.mediaId,
|
'Media files ${mediaService.mediaFile.mediaId} has no original, temp, or stored path. Removing it from DB as files are not existent.',
|
||||||
);
|
);
|
||||||
|
await twonlyDB.mediaFilesDao.deleteMediaFile(
|
||||||
|
mediaService.mediaFile.mediaId,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Log.warn(
|
||||||
|
'Media files ${mediaService.mediaFile.mediaId} has no paths, but is too new to be deleted. Skipping deletion.',
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.warn(
|
Log.warn(
|
||||||
'Media files ${mediaService.mediaFile.mediaId} has no original, temp, or stored path, but messages still reference it. Marking as uploaded to stop retries.',
|
'Media files ${mediaService.mediaFile.mediaId} has no original, temp, or stored path, but messages still reference it. Marking as uploaded to stop retries.',
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -12,7 +10,6 @@ import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
import 'package:twonly/src/database/tables/contacts.table.dart';
|
import 'package:twonly/src/database/tables/contacts.table.dart';
|
||||||
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/services/api/mediafiles/upload.api.dart';
|
|
||||||
import 'package:twonly/src/services/passwordless_recovery.service.dart'
|
import 'package:twonly/src/services/passwordless_recovery.service.dart'
|
||||||
show PasswordlessRecoveryService;
|
show PasswordlessRecoveryService;
|
||||||
import 'package:twonly/src/services/signal/session.signal.dart';
|
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||||
|
|
@ -20,7 +17,6 @@ import 'package:twonly/src/utils/log.dart';
|
||||||
import 'package:twonly/src/utils/misc.dart';
|
import 'package:twonly/src/utils/misc.dart';
|
||||||
import 'package:twonly/src/utils/qr.utils.dart';
|
import 'package:twonly/src/utils/qr.utils.dart';
|
||||||
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
import 'package:twonly/src/visual/components/alert.dialog.dart';
|
||||||
import 'package:twonly/src/visual/views/camera/share_image_editor.view.dart';
|
|
||||||
import 'package:twonly/src/visual/views/contact/add_contact_via_qr_link.view.dart';
|
import 'package:twonly/src/visual/views/contact/add_contact_via_qr_link.view.dart';
|
||||||
import 'package:twonly/src/visual/views/contact/add_new_contact.view.dart';
|
import 'package:twonly/src/visual/views/contact/add_new_contact.view.dart';
|
||||||
|
|
||||||
|
|
@ -149,53 +145,20 @@ Future<void> _pubKeysDoNotMatch(BuildContext context, String username) async {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> handleIntentMediaFile(
|
|
||||||
BuildContext context,
|
|
||||||
String filePath,
|
|
||||||
MediaType type,
|
|
||||||
) async {
|
|
||||||
final file = File(filePath);
|
|
||||||
if (!file.existsSync()) {
|
|
||||||
Log.error('The shared intent file does not exits.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final newMediaService = await initializeMediaUpload(
|
|
||||||
type,
|
|
||||||
userService.currentUser.defaultShowTime,
|
|
||||||
);
|
|
||||||
if (newMediaService == null) {
|
|
||||||
Log.error('Could not create new media file for intent shared file');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.copySync(newMediaService.originalPath.path);
|
|
||||||
if (!context.mounted) return;
|
|
||||||
|
|
||||||
await Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => ShareImageEditorView(
|
|
||||||
mediaFileService: newMediaService,
|
|
||||||
sharedFromGallery: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
StreamSubscription<List<SharedFile>> initIntentStreams(
|
StreamSubscription<List<SharedFile>> initIntentStreams(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
void Function(Uri) onUrlCallBack,
|
void Function(Uri) onUrlCallBack,
|
||||||
|
void Function(String, MediaType) onMediaCallBack,
|
||||||
) {
|
) {
|
||||||
FlutterSharingIntent.instance.getInitialSharing().then((f) {
|
FlutterSharingIntent.instance.getInitialSharing().then((f) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
handleIntentSharedFile(context, f, onUrlCallBack);
|
handleIntentSharedFile(context, f, onUrlCallBack, onMediaCallBack);
|
||||||
});
|
});
|
||||||
|
|
||||||
return FlutterSharingIntent.instance.getMediaStream().listen(
|
return FlutterSharingIntent.instance.getMediaStream().listen(
|
||||||
(f) {
|
(f) {
|
||||||
if (!context.mounted) return;
|
if (!context.mounted) return;
|
||||||
handleIntentSharedFile(context, f, onUrlCallBack);
|
handleIntentSharedFile(context, f, onUrlCallBack, onMediaCallBack);
|
||||||
},
|
},
|
||||||
// ignore: inference_failure_on_untyped_parameter
|
// ignore: inference_failure_on_untyped_parameter
|
||||||
onError: (err) {
|
onError: (err) {
|
||||||
|
|
@ -208,6 +171,7 @@ Future<void> handleIntentSharedFile(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
List<SharedFile> files,
|
List<SharedFile> files,
|
||||||
void Function(Uri) onUrlCallBack,
|
void Function(Uri) onUrlCallBack,
|
||||||
|
void Function(String, MediaType) onMediaCallBack,
|
||||||
) async {
|
) async {
|
||||||
for (final file in files) {
|
for (final file in files) {
|
||||||
if (file.value == null) {
|
if (file.value == null) {
|
||||||
|
|
@ -231,9 +195,9 @@ Future<void> handleIntentSharedFile(
|
||||||
if (file.value!.endsWith('.gif')) {
|
if (file.value!.endsWith('.gif')) {
|
||||||
type = MediaType.gif;
|
type = MediaType.gif;
|
||||||
}
|
}
|
||||||
await handleIntentMediaFile(context, file.value!, type);
|
onMediaCallBack(file.value!, type);
|
||||||
case SharedMediaType.VIDEO:
|
case SharedMediaType.VIDEO:
|
||||||
await handleIntentMediaFile(context, file.value!, MediaType.video);
|
onMediaCallBack(file.value!, MediaType.video);
|
||||||
// ignore: no_default_cases
|
// ignore: no_default_cases
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
@ -9,7 +10,9 @@ import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:twonly/globals.dart';
|
import 'package:twonly/globals.dart';
|
||||||
import 'package:twonly/locator.dart';
|
import 'package:twonly/locator.dart';
|
||||||
import 'package:twonly/src/constants/routes.keys.dart';
|
import 'package:twonly/src/constants/routes.keys.dart';
|
||||||
|
import 'package:twonly/src/database/tables/mediafiles.table.dart';
|
||||||
import 'package:twonly/src/providers/routing.provider.dart';
|
import 'package:twonly/src/providers/routing.provider.dart';
|
||||||
|
import 'package:twonly/src/services/api/mediafiles/upload.api.dart';
|
||||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||||
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
import 'package:twonly/src/services/notifications/setup.notifications.dart';
|
||||||
import 'package:twonly/src/utils/log.dart';
|
import 'package:twonly/src/utils/log.dart';
|
||||||
|
|
@ -47,10 +50,14 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
|
||||||
StreamSubscription<RemoteMessage>? _onMessageOpenedAppSub;
|
StreamSubscription<RemoteMessage>? _onMessageOpenedAppSub;
|
||||||
StreamSubscription<int>? _homeViewPageIndexSub;
|
StreamSubscription<int>? _homeViewPageIndexSub;
|
||||||
StreamSubscription<NotificationResponse>? _selectNotificationSub;
|
StreamSubscription<NotificationResponse>? _selectNotificationSub;
|
||||||
|
StreamSubscription<(String, MediaType)>? _sharedMediaSub;
|
||||||
|
|
||||||
static Uri? pendingSharedLink;
|
static Uri? pendingSharedLink;
|
||||||
|
static (String, MediaType)? pendingSharedMedia;
|
||||||
static final streamHomeViewPageIndex = StreamController<int>.broadcast();
|
static final streamHomeViewPageIndex = StreamController<int>.broadcast();
|
||||||
static final streamSharedLink = StreamController<Uri>.broadcast();
|
static final streamSharedLink = StreamController<Uri>.broadcast();
|
||||||
|
static final streamSharedMedia =
|
||||||
|
StreamController<(String, MediaType)>.broadcast();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -59,6 +66,8 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
|
||||||
var initialPage = widget.initialPage;
|
var initialPage = widget.initialPage;
|
||||||
if (HomeViewState.pendingSharedLink != null) {
|
if (HomeViewState.pendingSharedLink != null) {
|
||||||
initialPage = 1;
|
initialPage = 1;
|
||||||
|
} else if (HomeViewState.pendingSharedMedia != null) {
|
||||||
|
initialPage = 0;
|
||||||
} else if (initialPage == 1 &&
|
} else if (initialPage == 1 &&
|
||||||
!userService.currentUser.startWithCameraOpen) {
|
!userService.currentUser.startWithCameraOpen) {
|
||||||
initialPage = 0;
|
initialPage = 0;
|
||||||
|
|
@ -114,12 +123,48 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_sharedMediaSub = streamSharedMedia.stream.listen((media) async {
|
||||||
|
HomeViewState.pendingSharedMedia = null;
|
||||||
|
final type = media.$2;
|
||||||
|
final filePath = media.$1;
|
||||||
|
|
||||||
|
final newMediaService = await initializeMediaUpload(
|
||||||
|
type,
|
||||||
|
userService.currentUser.defaultShowTime,
|
||||||
|
);
|
||||||
|
if (newMediaService == null) {
|
||||||
|
Log.error('Could not create new media file for intent shared file');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final file = File(filePath);
|
||||||
|
if (!file.existsSync()) {
|
||||||
|
Log.error('The shared intent file does not exist.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
file.copySync(newMediaService.originalPath.path);
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
|
await context.navPush(
|
||||||
|
ShareImageEditorView(
|
||||||
|
mediaFileService: newMediaService,
|
||||||
|
sharedFromGallery: true,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
if (HomeViewState.pendingSharedLink != null) {
|
if (HomeViewState.pendingSharedLink != null) {
|
||||||
final link = HomeViewState.pendingSharedLink!;
|
final link = HomeViewState.pendingSharedLink!;
|
||||||
HomeViewState.pendingSharedLink = null;
|
HomeViewState.pendingSharedLink = null;
|
||||||
_mainCameraController.setSharedLinkForPreview(link);
|
_mainCameraController.setSharedLinkForPreview(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HomeViewState.pendingSharedMedia != null) {
|
||||||
|
final media = HomeViewState.pendingSharedMedia!;
|
||||||
|
HomeViewState.pendingSharedMedia = null;
|
||||||
|
streamSharedMedia.add(media);
|
||||||
|
}
|
||||||
|
|
||||||
if (initialPage == 1) {
|
if (initialPage == 1) {
|
||||||
Permission.camera.isGranted.then((hasPermission) {
|
Permission.camera.isGranted.then((hasPermission) {
|
||||||
if (hasPermission && mounted) {
|
if (hasPermission && mounted) {
|
||||||
|
|
@ -204,6 +249,7 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver {
|
||||||
_mainCameraController.setState = null;
|
_mainCameraController.setState = null;
|
||||||
_mainCameraController.closeCamera();
|
_mainCameraController.closeCamera();
|
||||||
_sharedLinkSub?.cancel();
|
_sharedLinkSub?.cancel();
|
||||||
|
_sharedMediaSub?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue