Skip to content

Commit 2f9ddec

Browse files
committed
feat: calling is added via lan
1 parent bab3f27 commit 2f9ddec

25 files changed

Lines changed: 3784 additions & 1885 deletions

.vscode/launch.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": [
7+
{
8+
"name": "airchat",
9+
"request": "launch",
10+
"type": "dart"
11+
},
12+
{
13+
"name": "airchat (profile mode)",
14+
"request": "launch",
15+
"type": "dart",
16+
"flutterMode": "profile"
17+
},
18+
{
19+
"name": "airchat (release mode)",
20+
"request": "launch",
21+
"type": "dart",
22+
"flutterMode": "release"
23+
}
24+
]
25+
}

android/app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
1818
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
1919
<uses-permission android:name="android.permission.RECORD_AUDIO" />
20+
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
2021

2122
<application
2223
android:icon="@mipmap/launcher_icon"
23-
android:label="AirChat">
24+
android:label="AirChat"
25+
android:usesCleartextTraffic="true">
2426
<activity
2527
android:name=".MainActivity"
2628
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<network-security-config>
3+
<domain-config cleartextTrafficPermitted="true">
4+
<domain includeSubdomains="false">127.0.0.1</domain>
5+
</domain-config>
6+
</network-security-config>

assets/ringtone/calling.mp3

282 KB
Binary file not shown.

assets/ringtone/incoming.mp3

58.1 KB
Binary file not shown.

lib/main.dart

Lines changed: 72 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
1+
import 'package:airchat/services/connection_service.dart';
2+
import 'package:airchat/widgets/call_overlay_widget.dart';
23
import 'package:flutter/material.dart';
34
import 'package:hive_flutter/hive_flutter.dart';
5+
import 'package:just_audio_media_kit/just_audio_media_kit.dart';
46
import 'package:media_kit/media_kit.dart';
57
import 'models/chat_user.dart';
68
import 'models/chat_message.dart';
@@ -10,10 +12,15 @@ import 'ui/settings_screen.dart';
1012
import 'package:uuid/uuid.dart';
1113
import 'package:provider/provider.dart';
1214
import 'providers/connection_state_provider.dart';
15+
import 'providers/call_state_provider.dart';
16+
import 'services/global_banner_controller.dart';
17+
18+
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
1319

1420
void main() async {
1521
WidgetsFlutterBinding.ensureInitialized();
1622
MediaKit.ensureInitialized();
23+
JustAudioMediaKit.ensureInitialized();
1724
await Hive.initFlutter();
1825
Hive.registerAdapter(ChatUserAdapter());
1926
Hive.registerAdapter(ChatMessageAdapter());
@@ -22,36 +29,81 @@ void main() async {
2229
var settingsBox = await Hive.openBox('settings');
2330
if (settingsBox.get('userId') == null) {
2431
settingsBox.put('userId', const Uuid().v4());
25-
}
32+
}
2633
runApp(
27-
ChangeNotifierProvider(
28-
create: (_) => ConnectionStateProvider(),
34+
MultiProvider(
35+
providers: [
36+
ChangeNotifierProvider(create: (_) => ConnectionStateProvider()),
37+
ChangeNotifierProvider(create: (_) => CallStateProvider()),
38+
ChangeNotifierProvider(create: (_) => GlobalBannerController()),
39+
],
2940
child: const AirChatApp(),
3041
),
3142
);
43+
44+
// Listen for call events and update CallStateProvider
45+
WidgetsBinding.instance.addPostFrameCallback((_) {
46+
ConnectionService.listenForCallEvents();
47+
final context = navigatorKey.currentContext;
48+
if (context != null) {
49+
final callProvider =
50+
Provider.of<CallStateProvider>(context, listen: false);
51+
ConnectionService.callEventsStream.listen((event) async {
52+
final type = event['type'];
53+
final from = event['from'];
54+
final id = event['id'];
55+
if (type == 'call_invite') {
56+
callProvider.receiveIncomingCall(from, id);
57+
} else if (type == 'call_accept') {
58+
callProvider.acceptCall();
59+
} else if (type == 'call_reject' || type == 'call_end') {
60+
await ConnectionService.updateCallDuration(
61+
callProvider.currentCallUserId!,
62+
callProvider.callId!,
63+
callProvider.callState == CallState.inCall
64+
? callProvider.formattedCallDuration
65+
: null,
66+
callProvider.callDirection!);
67+
callProvider.endCall();
68+
}
69+
});
70+
}
71+
});
3272
}
3373

3474
class AirChatApp extends StatelessWidget {
3575
const AirChatApp({super.key});
3676

37-
3877
@override
3978
Widget build(BuildContext context) {
4079
return MaterialApp(
41-
title: 'AirChat',
42-
color: Colors.white,
43-
theme: AirChatTheme.lightTheme,
44-
debugShowCheckedModeBanner: false,
45-
initialRoute: '/',
46-
routes: {
47-
'/': (context) => const HomeScreen(),
48-
'/home': (context) => const HomeScreen(),
49-
'/settings': (context) => const SettingsScreen(),
50-
},
51-
);
52-
53-
54-
55-
80+
title: 'AirChat',
81+
color: Colors.white,
82+
theme: AirChatTheme.lightTheme,
83+
debugShowCheckedModeBanner: false,
84+
navigatorKey: navigatorKey,
85+
initialRoute: '/',
86+
routes: {
87+
'/': (context) => const HomeScreen(),
88+
'/home': (context) => const HomeScreen(),
89+
'/settings': (context) => const SettingsScreen(),
90+
},
91+
builder: (context, child) {
92+
return Consumer2<GlobalBannerController, CallStateProvider>(
93+
builder: (context, bannerController, callProvider, _) {
94+
return Column(
95+
mainAxisSize: MainAxisSize.max,
96+
children: [
97+
if (!callProvider.isCallingScreenOpen &&
98+
callProvider.callState != CallState.idle &&
99+
callProvider.callState != CallState.ended)
100+
CallOverlayWidget(navigatorKey: navigatorKey),
101+
Expanded(child: child ?? SizedBox.shrink()),
102+
],
103+
);
104+
},
105+
);
106+
},
107+
);
56108
}
57109
}

lib/models/chat_message.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class ChatMessage extends HiveObject {
2121
bool isRead;
2222

2323
@HiveField(5)
24-
String type; // 'text', 'image', 'file'
24+
String type; // 'text', 'image', 'file', 'call'
2525

2626
@HiveField(6)
2727
String? fileName;
@@ -41,6 +41,9 @@ class ChatMessage extends HiveObject {
4141
@HiveField(11)
4242
int? status; // 1: success, 2: failure, 3: in_progress , 4: canceled
4343

44+
@HiveField(12)
45+
String? duration;
46+
4447
ChatMessage({
4548
required this.id,
4649
required this.senderId,
@@ -53,6 +56,7 @@ class ChatMessage extends HiveObject {
5356
this.filePath,
5457
this.mimeType,
5558
this.transferProgress,
56-
this.status
59+
this.status,
60+
this.duration
5761
});
5862
}

lib/models/chat_message.g.dart

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)