Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: calls #855

Draft
wants to merge 30 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
050ec74
feat: v1 of new calls
td-famedly Jan 30, 2024
fa5bbaf
chore: v2 working livekit calls
td-famedly Jan 30, 2024
f314b28
chore: fix calls route
td-famedly Jan 31, 2024
7a8a481
fix: upstream
td-famedly Jan 31, 2024
4166058
chore: use matrix from git
td-famedly Jan 31, 2024
1a1f3af
chore: fix remote user in incoming call
td-famedly Jan 31, 2024
460027d
fix: call redirection if current call is null
td-famedly Jan 31, 2024
62d018d
chore: fix ios builds?
td-famedly Jan 31, 2024
ae27568
chore: make sure web is setup before analyzing
td-famedly Jan 31, 2024
1d9a9c1
chore: skip analyze, fix later
td-famedly Jan 31, 2024
ca525fc
chore: gh pages
td-famedly Jan 31, 2024
afb0f60
chore: bring back analyze
td-famedly Jan 31, 2024
bb7c2f6
chore: fix base href
td-famedly Jan 31, 2024
f33fbc7
chore: fix base href
td-famedly Jan 31, 2024
0c75221
chore: prepare-web.sh
td-famedly Jan 31, 2024
8fc369d
chore: gitignore
td-famedly Jan 31, 2024
1264ab8
chore: workers
td-famedly Jan 31, 2024
50e5096
chore: fix web
td-famedly Jan 31, 2024
60fe9b6
chore: fix path
td-famedly Jan 31, 2024
acaca54
chore: reduce logs
td-famedly Jan 31, 2024
6817cd4
chore: pass e2ee flag to sdk
td-famedly Jan 31, 2024
84f8206
chore: bump sdk
td-famedly Feb 1, 2024
5c5a8f4
chore: bump sdk, dont clear expired events
td-famedly Feb 2, 2024
7fe77ed
fix: ui bug
td-famedly Feb 2, 2024
0b45b69
chore: unique id, why is this broken on macos
td-famedly Feb 3, 2024
c311418
chore: lock file update?
td-famedly Feb 4, 2024
8ec4de9
chore: bump sdk
td-famedly Feb 4, 2024
6e09421
chore: deps
td-famedly Feb 4, 2024
d983708
chore: sdk
td-famedly Feb 4, 2024
ee64dc1
chore: disable cache for build_web
td-famedly Feb 4, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions .github/workflows/integrate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- run: ./scripts/prepare-web.sh
- run: flutter pub get
- name: Check formatting
run: dart format lib/ test/ --set-exit-if-changed
Expand Down Expand Up @@ -41,19 +42,58 @@ jobs:
- run: flutter pub get
- run: flutter build apk --debug

build_debug_web:
build_web:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
cache: false
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install nodejs -y
- name: Remove Emoji Font
run: |
rm -rf fonts/NotoEmoji
yq -i 'del( .flutter.fonts[] | select(.family == "NotoEmoji") )' pubspec.yaml
- run: flutter pub get
- name: Prepare web
run: ./scripts/prepare-web.sh
- run: flutter build web
- name: Build Release Web
run: flutter build web --dart-define=FLUTTER_WEB_CANVASKIT_URL=canvaskit/ --release --source-maps
# - name: Create archive
# run: tar -czf fluffychat-web.tar.gz build/web/
- name: Upload Web Build
uses: actions/upload-artifact@v4
with:
name: web
path: build/web/


deploy_github_pages:
needs: build_web
permissions:
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: web
path: .
- run: ls -la
- uses: actions/configure-pages@v4
- uses: actions/upload-pages-artifact@v2
with:
path: .
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v2


build_debug_linux:
runs-on: ubuntu-latest
Expand Down
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,12 @@ ios/Runner.ipa
/macos/out
.vs
olm

web/*.dart.js*
web/Imaging.js
web/Imaging.wasm
web/olm.js
web/olm.wasm
web/olm_legacy.js
libolm.3.dylib
libcrypto.1.1.dylib
6 changes: 5 additions & 1 deletion android/app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
-keep class net.sqlcipher.** { *; }
-keep class net.sqlcipher.** { *; }

-keep class com.cloudwebrtc.webrtc.** { *; }
-keep class org.webrtc.** { *; }
-keep class com.hiennv.flutter_callkit_incoming.** { *; }
33 changes: 12 additions & 21 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,24 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<!-- calling related permissions -->
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

<uses-permission android:name="android.permission.READ_PHONE_STATE"
android:maxSdkVersion="29" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />

<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />>

<uses-sdk
tools:overrideLibrary="io.wazo.callkeep, net.touchcapture.qr.flutterqr, com.cloudwebrtc.webrtc, org.webrtc, com.it_nomads.fluttersecurestorage, com.pichillilorenzo.flutter_inappwebview, com.example.video_compress, com.otaliastudios.transcoder, com.otaliastudios.opengl, com.kineapps.flutter_file_dialog, com.llfbandit.record, com.pravera.flutter_foreground_task"/>

<application
android:label="FluffyChat"
android:icon="@mipmap/ic_launcher"
Expand Down Expand Up @@ -124,16 +125,6 @@
android:foregroundServiceType="camera|microphone|mediaProjection">
</service>

<service android:name="io.wazo.callkeep.VoiceConnectionService"
android:label="Wazo"
android:foregroundServiceType="camera|microphone|mediaProjection"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>

<receiver android:name="org.unifiedpush.flutter.connector.UnifiedPushReceiver"
tools:replace="android:enabled"
android:enabled="false">
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file removed assets/js/package/.gitkeep
Empty file.
44 changes: 39 additions & 5 deletions assets/l10n/intl_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1960,11 +1960,6 @@
"type": "text",
"placeholders": {}
},
"videoCall": "Video call",
"@videoCall": {
"type": "text",
"placeholders": {}
},
"visibilityOfTheChatHistory": "Visibility of the chat history",
"@visibilityOfTheChatHistory": {
"type": "text",
Expand Down Expand Up @@ -2463,5 +2458,44 @@
"sender": {}
}
},
"videoCall": "Video call",
"audioCall": "Audio call",
"groupCall": "Group call",
"heldTheCall": "held the call",
"startScreenShare": "Start screen sharing",
"stopScreenShare": "Stop screen sharing",
"holdCall": "Hold call",
"unholdCall": "Unhold call",
"flipCamera": "Flip camera",
"audioOutput": "Audio output",
"audioInput": "Audio input",
"hangup": "Hangup",
"joinGroupCall": "Join group call",
"cameraTurnedOff": "Camera turned off",
"youHeldTheCall": "You held the call",
"userHeldTheCall": "{user} held the call",
"@userHeldTheCall": {
"placeholders": {
"user": {}
}
},
"unknownUser": "Unknown user",
"connecting": "Connecting...",
"youAreCalling": "You are calling...",
"ringing": "Ringing...",
"incomingCall": "Incoming call...",
"answering": "Answering...",
"ended": "Ended",
"calling": "Calling",
"startGroupCall": "Start group call",
"startVideoCall": "Start video call",
"startVoiceCall": "Start voice call",
"showInformation": "Show information",
"showMember": "Show member",
"showImages": "Show images",
"activeCall": "Active call",
"call": "Call",
"missedCall": "Missed call",
"callBack": "Call back",
"transparent": "Transparent"
}
2 changes: 1 addition & 1 deletion ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Uncomment this line to define a global platform for your project
platform :ios, '12.0'
platform :ios, '12.1'

# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
Expand Down
4 changes: 4 additions & 0 deletions lib/config/app_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ abstract class AppConfig {
static bool sendPublicReadReceipts = true;
static bool? sendOnEnter;
static bool experimentalVoip = false;
static bool livekitEnabledCalls = true;
// static const livekitServiceUrl = 'https://famedly-livekit-server.teedee.dev';
static const livekitServiceUrl = 'https://livekit-jwt.call.element.dev';
static const enableLivekitE2EE = false;
static const bool hideTypingUsernames = false;
static const bool hideAllStateEvents = false;
static const String inviteLinkPrefix = 'https://matrix.to/#/';
Expand Down
37 changes: 37 additions & 0 deletions lib/config/routes.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

import 'package:go_router/go_router.dart';
import 'package:matrix/matrix.dart';

import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pages/archive/archive.dart';
Expand All @@ -29,6 +31,9 @@ import 'package:fluffychat/pages/settings_notifications/settings_notifications.d
import 'package:fluffychat/pages/settings_password/settings_password.dart';
import 'package:fluffychat/pages/settings_security/settings_security.dart';
import 'package:fluffychat/pages/settings_style/settings_style.dart';
import 'package:fluffychat/pages/voip/calling_page.dart';
import 'package:fluffychat/pages/voip/group_call_onboarding/group_call_onboarding_view.dart';
import 'package:fluffychat/utils/voip/voip_plugin.dart';
import 'package:fluffychat/widgets/layouts/empty_page.dart';
import 'package:fluffychat/widgets/layouts/two_column_layout.dart';
import 'package:fluffychat/widgets/log_view.dart';
Expand Down Expand Up @@ -80,6 +85,38 @@ abstract class AppRoutes {
const LogViewer(),
),
),
GoRoute(
redirect: (context, state) {
if (VoipPlugin.currentCallProxy == null) {
final parts = state.uri.path.split('/');
final redirectPath = '/${parts[1]}/${parts[2]}';
Logs().w(
'[GoRouter] voip currentCallProxy was null, redirecting to $redirectPath',
);
return redirectPath;
}
return null;
},
path: '/rooms/:roomid/call',
pageBuilder: (context, state) => defaultPageBuilder(
context,
VoipPlugin.currentCallProxy == null
? const Center(child: CircularProgressIndicator())
: Calling(
voipPlugin: Matrix.of(context).voipPlugin,
proxy: VoipPlugin.currentCallProxy!,
),
),
),
GoRoute(
path: '/rooms/:roomid/group_call_onboarding',
pageBuilder: (context, state) => defaultPageBuilder(
context,
GroupCallOnboardingView(
roomId: state.pathParameters['roomid']!,
),
),
),
ShellRoute(
pageBuilder: (context, state, child) => defaultPageBuilder(
context,
Expand Down
28 changes: 21 additions & 7 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/utils/client_manager.dart';
import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/voip/voip_plugin.dart';
import 'package:fluffychat/widgets/error_widget.dart';
import 'config/setting_keys.dart';
import 'utils/background_push.dart';
Expand All @@ -24,6 +25,7 @@ void main() async {
Logs().nativeColors = !PlatformInfos.isIOS;
final store = await SharedPreferences.getInstance();
final clients = await ClientManager.getClients(store: store);
final voipPlugins = clients.map((e) => VoipPlugin.clientOnly(e)).toList();

// If the app starts in detached mode, we assume that it is in
// background fetch mode for processing push notifications. This is
Expand All @@ -34,12 +36,12 @@ void main() async {
for (final client in clients) {
client.syncPresence = PresenceType.offline;
}

// In the background fetch mode we do not want to waste ressources with
// starting the Flutter engine but process incoming push notifications.
BackgroundPush.clientOnly(clients.first);
// To start the flutter engine afterwards we add an custom observer.
WidgetsBinding.instance.addObserver(AppStarter(clients, store));
WidgetsBinding.instance
.addObserver(AppStarter(clients, voipPlugins, store));
Logs().i(
'${AppConfig.applicationName} started in background-fetch mode. No GUI will be created unless the app is no longer detached.',
);
Expand All @@ -50,11 +52,15 @@ void main() async {
Logs().i(
'${AppConfig.applicationName} started in foreground mode. Rendering GUI...',
);
await startGui(clients, store);
await startGui(clients, voipPlugins, store);
}

/// Fetch the pincode for the applock and start the flutter engine.
Future<void> startGui(List<Client> clients, SharedPreferences store) async {
Future<void> startGui(
List<Client> clients,
List<VoipPlugin> voipPlugins,
SharedPreferences store,
) async {
// Fetch the pin for the applock if existing for mobile applications.
String? pin;
if (PlatformInfos.isMobile) {
Expand All @@ -72,17 +78,25 @@ Future<void> startGui(List<Client> clients, SharedPreferences store) async {
await firstClient?.accountDataLoading;

ErrorWidget.builder = (details) => FluffyChatErrorWidget(details);
runApp(FluffyChatApp(clients: clients, pincode: pin, store: store));
runApp(
FluffyChatApp(
clients: clients,
voipPlugins: voipPlugins,
pincode: pin,
store: store,
),
);
}

/// Watches the lifecycle changes to start the application when it
/// is no longer detached.
class AppStarter with WidgetsBindingObserver {
final List<Client> clients;
List<VoipPlugin> voipPlugins;
final SharedPreferences store;
bool guiStarted = false;

AppStarter(this.clients, this.store);
AppStarter(this.clients, this.voipPlugins, this.store);

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
Expand All @@ -96,7 +110,7 @@ class AppStarter with WidgetsBindingObserver {
for (final client in clients) {
client.syncPresence = PresenceType.online;
}
startGui(clients, store);
startGui(clients, voipPlugins, store);
// We must make sure that the GUI is only started once.
guiStarted = true;
}
Expand Down
Loading