mirror of
https://github.com/BlueBubblesApp/bluebubbles-app.git
synced 2025-05-21 15:16:23 +08:00
237 lines
8.4 KiB
Dart
237 lines
8.4 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:bluebubbles/app/layouts/settings/pages/scheduling/scheduled_messages_panel.dart';
|
|
import 'package:bluebubbles/app/layouts/settings/pages/server/server_management_panel.dart';
|
|
import 'package:bluebubbles/app/wrappers/theme_switcher.dart';
|
|
import 'package:bluebubbles/helpers/backend/startup_tasks.dart';
|
|
import 'package:bluebubbles/helpers/ui/facetime_helpers.dart';
|
|
|
|
import 'package:bluebubbles/utils/logger/logger.dart';
|
|
import 'package:bluebubbles/helpers/helpers.dart';
|
|
import 'package:bluebubbles/app/layouts/chat_creator/chat_creator.dart';
|
|
import 'package:bluebubbles/app/layouts/conversation_view/pages/conversation_view.dart';
|
|
import 'package:bluebubbles/database/models.dart';
|
|
import 'package:bluebubbles/services/services.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart' hide Intent;
|
|
import 'package:get/get.dart';
|
|
import 'package:path/path.dart';
|
|
import 'package:receive_intent/receive_intent.dart';
|
|
import 'package:tuple/tuple.dart';
|
|
import 'package:universal_io/io.dart';
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
|
|
IntentsService intents = Get.isRegistered<IntentsService>() ? Get.find<IntentsService>() : Get.put(IntentsService());
|
|
|
|
class IntentsService extends GetxService {
|
|
late final StreamSubscription sub;
|
|
|
|
Future<void> init() async {
|
|
if (kIsWeb || kIsDesktop) return;
|
|
|
|
final intent = await ReceiveIntent.getInitialIntent();
|
|
handleIntent(intent);
|
|
|
|
sub = ReceiveIntent.receivedIntentStream.listen((Intent? intent) {
|
|
handleIntent(intent);
|
|
}, onError: (err) {
|
|
Logger.error("Failed to get intent!", error: err);
|
|
});
|
|
}
|
|
|
|
@override
|
|
void onClose() async {
|
|
await sub.cancel();
|
|
super.onClose();
|
|
}
|
|
|
|
void handleIntent(Intent? intent) async {
|
|
if (intent == null) return;
|
|
|
|
switch (intent.action) {
|
|
case "android.intent.action.SEND":
|
|
case "android.intent.action.SEND_MULTIPLE":
|
|
final id = intent.extra?["android.intent.extra.shortcut.ID"];
|
|
final text = intent.extra?["android.intent.extra.TEXT"];
|
|
final files = <PlatformFile>[];
|
|
if (intent.extra?["android.intent.extra.STREAM"] != null) {
|
|
final data = intent.extra!["android.intent.extra.STREAM"];
|
|
if (data is List) {
|
|
for (String? s in data) {
|
|
if (s == null) continue;
|
|
final path = await mcs.invokeMethod("get-content-uri-path", {"uri": s});
|
|
final bytes = await File(path).readAsBytes();
|
|
files.add(PlatformFile(
|
|
path: path,
|
|
name: basename(path),
|
|
bytes: bytes,
|
|
size: bytes.length,
|
|
));
|
|
}
|
|
} else if (data != null) {
|
|
final path = await mcs.invokeMethod("get-content-uri-path", {"uri": data});
|
|
final bytes = await File(path).readAsBytes();
|
|
files.add(PlatformFile(
|
|
path: path,
|
|
name: basename(path),
|
|
bytes: bytes,
|
|
size: bytes.length,
|
|
));
|
|
}
|
|
}
|
|
await openChat(id, text: text, attachments: files);
|
|
return;
|
|
default:
|
|
if (intent.data?.startsWith("imessage://") ?? false) {
|
|
final uri = Uri.tryParse(intent.data!.replaceFirst("imessage://", "imessage:").replaceFirst("&body=", "?body="));
|
|
if (uri != null) {
|
|
final address = uri.path;
|
|
final handle = Handle.findOne(addressAndService: Tuple2(address, "iMessage"));
|
|
ns.pushAndRemoveUntil(
|
|
Get.context!,
|
|
ChatCreator(
|
|
initialSelected: [SelectedContact(displayName: handle?.displayName ?? address, address: address)],
|
|
initialText: uri.queryParameters['body'],
|
|
),
|
|
(route) => route.isFirst,
|
|
);
|
|
}
|
|
} else if (intent.extra?["chatGuid"] != null) {
|
|
final guid = intent.extra!["chatGuid"]!;
|
|
final bubble = intent.extra!["bubble"] == true;
|
|
ls.isBubble = bubble;
|
|
await openChat(guid);
|
|
} else if (intent.extra?["callUuid"] != null) {
|
|
await StartupTasks.waitForUI();
|
|
if (intent.extra?["answer"] == true) {
|
|
await answerFaceTime(intent.extra?["callUuid"]!);
|
|
} else {
|
|
await showFaceTimeOverlay(intent.extra?["callUuid"], intent.extra?["caller"], null, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> answerFaceTime(String callUuid) async {
|
|
if (Get.context != null) {
|
|
showDialog(
|
|
context: Get.context!,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
backgroundColor: context.theme.colorScheme.properSurface,
|
|
title: Text(
|
|
"Generating link for call...",
|
|
style: context.theme.textTheme.titleLarge,
|
|
),
|
|
content: Container(
|
|
height: 70,
|
|
child: Center(
|
|
child: CircularProgressIndicator(
|
|
backgroundColor: context.theme.colorScheme.properSurface,
|
|
valueColor: AlwaysStoppedAnimation<Color>(context.theme.colorScheme.primary),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
);
|
|
hideFaceTimeOverlay(callUuid);
|
|
}
|
|
|
|
String? link;
|
|
try {
|
|
final call = await http.answerFaceTime(callUuid);
|
|
link = call.data?["data"]?["link"];
|
|
} catch (_) {}
|
|
if (Get.context != null) {
|
|
Navigator.of(Get.context!).pop();
|
|
}
|
|
if (link == null) {
|
|
return showSnackbar("Failed to answer FaceTime", "Unable to generate FaceTime link!");
|
|
}
|
|
|
|
if (!kIsWeb) {
|
|
await launchUrl(Uri.parse(link), mode: LaunchMode.externalApplication);
|
|
} else if (kIsWeb) {
|
|
// TODO: Implement web FaceTime
|
|
}
|
|
}
|
|
|
|
Future<void> openChat(String? guid, {String? text, List<PlatformFile> attachments = const []}) async {
|
|
Logger.info("Handling open chat intent with guid: $guid", tag: "IntentsService");
|
|
|
|
if (guid == null) {
|
|
Logger.debug("Opening new chat creator..", tag: "IntentsService");
|
|
await StartupTasks.waitForUI();
|
|
ns.pushAndRemoveUntil(
|
|
Get.context!,
|
|
ChatCreator(
|
|
initialAttachments: attachments,
|
|
initialText: text,
|
|
),
|
|
(route) => route.isFirst,
|
|
);
|
|
} else if (guid == "-1") {
|
|
Logger.debug("Popping all routes...", tag: "IntentsService");
|
|
if (cm.activeChat != null) {
|
|
Navigator.of(Get.context!).popUntil((route) => route.isFirst);
|
|
}
|
|
} else if (guid == "-2") {
|
|
Logger.debug("Opening server management panel...", tag: "IntentsService");
|
|
Navigator.of(Get.context!).push(
|
|
ThemeSwitcher.buildPageRoute(
|
|
builder: (BuildContext context) {
|
|
return ServerManagementPanel();
|
|
},
|
|
),
|
|
);
|
|
} else if (guid.contains("scheduled")) {
|
|
Logger.debug("Opening scheduled messages panel...", tag: "IntentsService");
|
|
Navigator.of(Get.context!).push(
|
|
ThemeSwitcher.buildPageRoute(
|
|
builder: (BuildContext context) {
|
|
return ScheduledMessagesPanel();
|
|
},
|
|
),
|
|
);
|
|
} else {
|
|
Logger.debug("Opening existing chat (Attachments: ${attachments.length}; Text: ${text?.shorten(10) ?? 'N/A'})", tag: "IntentsService");
|
|
final chat = Chat.findOne(guid: guid);
|
|
if (chat == null) {
|
|
Logger.debug("Chat not found with guid: $guid", tag: "IntentsService");
|
|
return;
|
|
}
|
|
|
|
bool chatIsOpen = cm.activeChat?.chat.guid == guid;
|
|
Logger.debug("Chat is active: $chatIsOpen", tag: "IntentsService");
|
|
|
|
setPickedAttachments() {
|
|
if (attachments.isNotEmpty) {
|
|
cvc(chat).pickedAttachments.value = attachments;
|
|
}
|
|
|
|
if (text != null && text.isNotEmpty) {
|
|
cvc(chat).textController.text = text;
|
|
}
|
|
}
|
|
|
|
if (!chatIsOpen) {
|
|
Logger.debug("Navigating to conversation view...", tag: "IntentsService");
|
|
await StartupTasks.waitForUI();
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
await ns.pushAndRemoveUntil(
|
|
Get.context!,
|
|
ConversationView(
|
|
chat: chat,
|
|
onInit: () => setPickedAttachments(),
|
|
),
|
|
(route) => route.isFirst,
|
|
);
|
|
} else {
|
|
Logger.debug("Chat is already open, not navigating", tag: "IntentsService");
|
|
setPickedAttachments();
|
|
}
|
|
}
|
|
}
|
|
}
|