- Calls keep going with the app in the background: a foreground service (CallService.kt, driven from lib/matrix/call_service.dart) declares the microphone, the camera while it's on and the screen while it's shared. Its notification (CallStyle) shows the call's length, opens the call and hangs up. Swiping the app away ends the call. - Screen sharing on Android: consent, then media projection on the service, then LiveKit publishes a screen-share track. A shared screen replaces the sharer's camera on their tile, shown whole and unmirrored; stopping it from Android's controls stops the share. - Read receipts per thread (MSC3771): an open thread sends receipts with thread_id, "Read" under replies comes from receipts in the thread, and threads with unread replies get a dot on "N replies" and in the list. Not tested on a device yet. README updated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
62 lines
1.8 KiB
Dart
62 lines
1.8 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/services.dart';
|
|
import 'package:matrix/matrix.dart';
|
|
|
|
/// The Android foreground service that keeps a call going with the app in
|
|
/// the background (android/.../CallService.kt): without it Android cuts a
|
|
/// backgrounded app off from the microphone and camera, and screen sharing
|
|
/// can't start at all. Its notification shows the call, opens it and hangs
|
|
/// up. Nothing on other platforms.
|
|
class CallService {
|
|
static const _channel = MethodChannel('warp/call_service');
|
|
|
|
/// Hang up from the notification, and tapping it.
|
|
static void listen({
|
|
required void Function() onHangUp,
|
|
required void Function() onOpen,
|
|
}) {
|
|
if (!Platform.isAndroid) return;
|
|
_channel.setMethodCallHandler((call) async {
|
|
switch (call.method) {
|
|
case 'hangUp':
|
|
onHangUp();
|
|
case 'openCall':
|
|
onOpen();
|
|
}
|
|
});
|
|
}
|
|
|
|
/// Starts the service, or changes what it declares: the camera while it
|
|
/// is on, the screen while it is shared. False if Android refused.
|
|
static Future<bool> update({
|
|
required String title,
|
|
required DateTime startedAt,
|
|
required bool camera,
|
|
required bool screen,
|
|
}) async {
|
|
if (!Platform.isAndroid) return true;
|
|
try {
|
|
return await _channel.invokeMethod<bool>('update', {
|
|
'title': title,
|
|
'startedAt': startedAt.millisecondsSinceEpoch,
|
|
'camera': camera,
|
|
'screen': screen,
|
|
}) ??
|
|
false;
|
|
} catch (e) {
|
|
Logs().w('Could not start the call service', e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static Future<void> stop() async {
|
|
if (!Platform.isAndroid) return;
|
|
try {
|
|
await _channel.invokeMethod<void>('stop');
|
|
} catch (e) {
|
|
Logs().w('Could not stop the call service', e);
|
|
}
|
|
}
|
|
}
|