Skip to content

Commit 83bb7f6

Browse files
authored
Merge pull request #524 from M4dhav/dev
Implement Friends and Direct calling functionality
2 parents 3595bf7 + 330249b commit 83bb7f6

File tree

156 files changed

+16264
-14041
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

156 files changed

+16264
-14041
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ Please go through and strictly follow the [Onboarding Guide](https://github.com/
7878

7979
<div align="center">
8080

81-
| Login Screen (Forest) | Home Screen (Time) | Create Room Screen (Time) |
82-
| :--- | :--- | :--- |
83-
| <img src= "https://github.com/user-attachments/assets/e76147b1-0e51-4852-8198-06bbc975b25c" width="260" height="auto" /> | <img src="https://github.com/user-attachments/assets/ad62eecb-b621-4c31-a01c-001ff5462b28" width="250" height="auto" /> | <img src="https://github.com/user-attachments/assets/31ce6e73-8dca-4e2d-8f48-c22480fa1332" width="250" height="auto" /> |
81+
| Login Screen (Forest) | Home Screen (Time) | Create Room Screen (Time) |
82+
| :----------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- |
83+
| <img src= "https://github.com/user-attachments/assets/e76147b1-0e51-4852-8198-06bbc975b25c" width="260" height="auto" /> | <img src="https://github.com/user-attachments/assets/ad62eecb-b621-4c31-a01c-001ff5462b28" width="250" height="auto" /> | <img src="https://github.com/user-attachments/assets/31ce6e73-8dca-4e2d-8f48-c22480fa1332" width="250" height="auto" /> |
8484

8585
| Room Screen (Cream) | Profile Screen (Amber) | Explore Story (Forest) |
8686
| :---------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------- |

android/app/proguard-rules.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-keep class com.hiennv.flutter_callkit_incoming.** { *; }

android/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626
<!-- MainActivity -->
2727
<activity
2828
android:name=".MainActivity"
29+
2930
android:exported="true"
30-
android:launchMode="singleTop"
31+
android:launchMode="singleInstance"
3132
android:theme="@style/LaunchTheme"
3233
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
3334
android:hardwareAccelerated="true"

lib/controllers/about_app_screen_controller.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,4 @@ community-driven development. Join us in shaping the future of social audio!""";
5555
await Future.delayed(const Duration(seconds: 1));
5656
return false;
5757
}
58-
}
58+
}

lib/controllers/auth_state_controller.dart

Lines changed: 149 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@ import 'package:appwrite/enums.dart';
44
import 'package:appwrite/models.dart';
55
import 'package:firebase_messaging/firebase_messaging.dart';
66
import 'package:flutter/material.dart';
7+
import 'package:flutter_callkit_incoming/entities/call_event.dart';
8+
import 'package:flutter_callkit_incoming/entities/call_kit_params.dart';
9+
import 'package:flutter_callkit_incoming/flutter_callkit_incoming.dart';
710
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
811
import 'package:get/get.dart';
912
import 'package:get_storage/get_storage.dart';
13+
import 'package:resonate/controllers/friend_calling_controller.dart';
14+
import 'package:resonate/controllers/friends_controller.dart';
1015
import 'package:resonate/controllers/upcomming_rooms_controller.dart';
1116
import 'package:resonate/controllers/tabview_controller.dart';
1217
import 'package:resonate/models/follower_user_model.dart';
@@ -30,10 +35,10 @@ class AuthStateController extends GetxController {
3035
Databases? databases,
3136
Client? client,
3237
FirebaseMessaging? messaging,
33-
}) : client = client ?? AppwriteService.getClient(),
34-
account = account ?? AppwriteService.getAccount(),
35-
databases = databases ?? AppwriteService.getDatabases(),
36-
messaging = messaging ?? FirebaseMessaging.instance;
38+
}) : client = client ?? AppwriteService.getClient(),
39+
account = account ?? AppwriteService.getAccount(),
40+
databases = databases ?? AppwriteService.getDatabases(),
41+
messaging = messaging ?? FirebaseMessaging.instance;
3742
late String? uid;
3843
late String? profileImageID;
3944
late String? displayName;
@@ -50,13 +55,17 @@ class AuthStateController extends GetxController {
5055
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
5156
FlutterLocalNotificationsPlugin();
5257
static AndroidNotificationDetails androidNotificationDetails =
53-
AndroidNotificationDetails('your channel id', 'your channel name',
54-
channelDescription: 'your channel description',
55-
importance: Importance.max,
56-
priority: Priority.high,
57-
ticker: 'ticker');
58-
static NotificationDetails notificationDetails =
59-
NotificationDetails(android: androidNotificationDetails);
58+
AndroidNotificationDetails(
59+
'your channel id',
60+
'your channel name',
61+
channelDescription: 'your channel description',
62+
importance: Importance.max,
63+
priority: Priority.high,
64+
ticker: 'ticker',
65+
);
66+
static NotificationDetails notificationDetails = NotificationDetails(
67+
android: androidNotificationDetails,
68+
);
6069

6170
Future<void> initializeLocalNotifications() async {
6271
const AndroidInitializationSettings initializationSettingsAndroid =
@@ -65,19 +74,24 @@ class AuthStateController extends GetxController {
6574
final DarwinInitializationSettings initializationSettingsDarwin =
6675
DarwinInitializationSettings();
6776
InitializationSettings initializationSettings = InitializationSettings(
68-
android: initializationSettingsAndroid,
69-
iOS: initializationSettingsDarwin);
70-
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
71-
onDidReceiveNotificationResponse: onDidReceiveNotificationResponse);
77+
android: initializationSettingsAndroid,
78+
iOS: initializationSettingsDarwin,
79+
);
80+
await flutterLocalNotificationsPlugin.initialize(
81+
initializationSettings,
82+
onDidReceiveNotificationResponse: onDidReceiveNotificationResponse,
83+
);
7284
}
7385

7486
void onDidReceiveNotificationResponse(
75-
NotificationResponse notificationResponse) async {
87+
NotificationResponse notificationResponse,
88+
) async {
7689
String name = notificationResponse.payload!;
7790
UpcomingRoomsController upcomingRoomsController =
7891
Get.find<UpcomingRoomsController>();
79-
int index = upcomingRoomsController.upcomingRooms
80-
.indexWhere((upcomingRoom) => upcomingRoom.name == name);
92+
int index = upcomingRoomsController.upcomingRooms.indexWhere(
93+
(upcomingRoom) => upcomingRoom.name == name,
94+
);
8195

8296
upcomingRoomsController.upcomingRoomScrollController.value =
8397
ScrollController(initialScrollOffset: UiSizes.height_170 * index);
@@ -102,24 +116,39 @@ class AuthStateController extends GetxController {
102116
provisional: false,
103117
sound: true,
104118
);
119+
// Check if can use full screen intent
120+
if (!Get.testMode) {
121+
await FlutterCallkitIncoming.canUseFullScreenIntent();
122+
123+
// Request full intent permission
124+
await FlutterCallkitIncoming.requestFullIntentPermission();
125+
}
105126

106127
await initializeLocalNotifications();
107128

108129
// Listen to notitifcations in foreground
109130
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
131+
log('Got a message whilst in the foreground!');
132+
if (message.data['type'] == 'incoming_call') {
133+
log('saw incoming call');
134+
135+
await FriendCallingController.onCallRecieved(message);
136+
}
110137
if (message.notification != null) {
111138
RegExp exp = RegExp(r'The room (\w+) will Start Soon');
112139
RegExpMatch? matches = exp.firstMatch(message.notification!.body!);
140+
113141
if (matches != null) {
114142
String discussionName = matches.group(1)!;
115143

116144
// send local notification
117145
await flutterLocalNotificationsPlugin.show(
118-
0,
119-
message.notification!.title,
120-
message.notification!.body,
121-
notificationDetails,
122-
payload: discussionName);
146+
0,
147+
message.notification!.title,
148+
message.notification!.body,
149+
notificationDetails,
150+
payload: discussionName,
151+
);
123152
} else {
124153
await flutterLocalNotificationsPlugin.show(
125154
0,
@@ -130,6 +159,27 @@ class AuthStateController extends GetxController {
130159
}
131160
}
132161
});
162+
if (!Get.testMode) {
163+
final friendCallingController = Get.put(
164+
FriendCallingController(),
165+
permanent: true,
166+
);
167+
FlutterCallkitIncoming.onEvent.listen((CallEvent? event) {
168+
if (event!.event == Event.actionCallAccept) {
169+
log(event.body['extra'].toString());
170+
171+
friendCallingController.onAnswerCall(
172+
Map<String, dynamic>.from(event.body['extra']),
173+
);
174+
}
175+
if (event.event == Event.actionCallDecline) {
176+
friendCallingController.onDeclinedCall(
177+
Map<String, dynamic>.from(event.body['extra']),
178+
);
179+
FlutterCallkitIncoming.showMissCallNotification(CallKitParams());
180+
}
181+
});
182+
}
133183
}
134184

135185
Future<bool> get getLoginState async {
@@ -153,24 +203,26 @@ class AuthStateController extends GetxController {
153203
appwriteUser.prefs.data["isUserProfileComplete"] ?? false;
154204
if (isUserProfileComplete == true) {
155205
Document userDataDoc = await databases.getDocument(
156-
databaseId: userDatabaseID,
157-
collectionId: usersCollectionID,
158-
documentId: appwriteUser.$id);
206+
databaseId: userDatabaseID,
207+
collectionId: usersCollectionID,
208+
documentId: appwriteUser.$id,
209+
);
159210
profileImageUrl = userDataDoc.data["profileImageUrl"];
160211
profileImageID = userDataDoc.data["profileImageID"];
161212
userName = userDataDoc.data["username"] ?? "unavailable";
162213
ratingTotal = userDataDoc.data["ratingTotal"].toDouble() ?? 5;
163214
ratingCount = userDataDoc.data["ratingCount"] ?? 1;
164215
followerDocuments =
165216
(userDataDoc.data["followers"] as List<dynamic>?)?.map((e) {
166-
log(e.runtimeType.toString());
167-
return FollowerUserModel.fromJson(e);
168-
}).toList() ??
169-
[];
170-
log("Follower documents: $followerDocuments");
217+
return FollowerUserModel.fromJson(e);
218+
}).toList() ??
219+
[];
171220
}
172221

173222
update();
223+
if (!Get.testMode) {
224+
Get.put(FriendsController(), permanent: true);
225+
}
174226
} catch (e) {
175227
log("Error originating from setUserProfileData$e");
176228
} finally {
@@ -185,10 +237,19 @@ class AuthStateController extends GetxController {
185237
Get.offNamed(AppRoutes.onBoarding);
186238
} else {
187239
Get.offNamed(AppRoutes.tabview);
240+
final activeCalls = await FlutterCallkitIncoming.activeCalls();
241+
if (activeCalls.isNotEmpty) {
242+
final activeCall = activeCalls.last;
243+
await Get.find<FriendCallingController>().onAnswerCall(
244+
Map<String, dynamic>.from(activeCall['extra']),
245+
);
246+
}
188247
}
189248
} catch (e) {
249+
log("Error in isUserLoggedIn$e");
190250
bool? landingScreenShown = GetStorage().read(
191-
"landingScreenShown"); // landingScreenShown is the boolean value that is used to check wether to show the user the onboarding screen or not on the first launch of the app.
251+
"landingScreenShown",
252+
); // landingScreenShown is the boolean value that is used to check wether to show the user the onboarding screen or not on the first launch of the app.
192253
landingScreenShown == null
193254
? Get.offNamed(AppRoutes.landing)
194255
: Get.offNamed(AppRoutes.welcomeScreen);
@@ -205,77 +266,93 @@ class AuthStateController extends GetxController {
205266
final fcmToken = await messaging.getToken();
206267

207268
//subscribed Upcoming Rooms
208-
List<Document> subscribedUpcomingRooms = await databases.listDocuments(
209-
databaseId: upcomingRoomsDatabaseId,
210-
collectionId: subscribedUserCollectionId,
211-
queries: [
212-
Query.equal("userID", [uid])
213-
]).then((value) => value.documents);
269+
List<Document> subscribedUpcomingRooms = await databases
270+
.listDocuments(
271+
databaseId: upcomingRoomsDatabaseId,
272+
collectionId: subscribedUserCollectionId,
273+
queries: [
274+
Query.equal("userID", [uid]),
275+
],
276+
)
277+
.then((value) => value.documents);
214278
for (var subscription in subscribedUpcomingRooms) {
215279
List<dynamic> registrationTokens =
216280
subscription.data['registrationTokens'];
217281
registrationTokens.add(fcmToken!);
218282
databases.updateDocument(
219-
databaseId: upcomingRoomsDatabaseId,
220-
collectionId: subscribedUserCollectionId,
221-
documentId: subscription.$id,
222-
data: {"registrationTokens": registrationTokens});
283+
databaseId: upcomingRoomsDatabaseId,
284+
collectionId: subscribedUserCollectionId,
285+
documentId: subscription.$id,
286+
data: {"registrationTokens": registrationTokens},
287+
);
223288
}
224289

225290
//created Upcoming Rooms
226-
List<Document> createdUpcomingRooms = await databases.listDocuments(
227-
databaseId: upcomingRoomsDatabaseId,
228-
collectionId: upcomingRoomsCollectionId,
229-
queries: [
230-
Query.equal("creatorUid", [uid])
231-
]).then((value) => value.documents);
291+
List<Document> createdUpcomingRooms = await databases
292+
.listDocuments(
293+
databaseId: upcomingRoomsDatabaseId,
294+
collectionId: upcomingRoomsCollectionId,
295+
queries: [
296+
Query.equal("creatorUid", [uid]),
297+
],
298+
)
299+
.then((value) => value.documents);
232300
for (var upcomingRoom in createdUpcomingRooms) {
233301
List<dynamic> creatorFcmTokens = upcomingRoom.data['creator_fcm_tokens'];
234302
creatorFcmTokens.add(fcmToken!);
235303
databases.updateDocument(
236-
databaseId: upcomingRoomsDatabaseId,
237-
collectionId: upcomingRoomsCollectionId,
238-
documentId: upcomingRoom.$id,
239-
data: {"creator_fcm_tokens": creatorFcmTokens});
304+
databaseId: upcomingRoomsDatabaseId,
305+
collectionId: upcomingRoomsCollectionId,
306+
documentId: upcomingRoom.$id,
307+
data: {"creator_fcm_tokens": creatorFcmTokens},
308+
);
240309
}
241310
}
242311

243312
Future<void> removeRegistrationTokenFromSubscribedUpcomingRooms() async {
244313
final fcmToken = await messaging.getToken();
245314

246315
//subscribed Upcoming Rooms
247-
List<Document> subscribedUpcomingRooms = await databases.listDocuments(
248-
databaseId: upcomingRoomsDatabaseId,
249-
collectionId: subscribedUserCollectionId,
250-
queries: [
251-
Query.equal("userID", [uid])
252-
]).then((value) => value.documents);
316+
List<Document> subscribedUpcomingRooms = await databases
317+
.listDocuments(
318+
databaseId: upcomingRoomsDatabaseId,
319+
collectionId: subscribedUserCollectionId,
320+
queries: [
321+
Query.equal("userID", [uid]),
322+
],
323+
)
324+
.then((value) => value.documents);
253325
for (var subscription in subscribedUpcomingRooms) {
254326
List<dynamic> registrationTokens =
255327
subscription.data['registrationTokens'];
256328
registrationTokens.remove(fcmToken!);
257329
databases.updateDocument(
258-
databaseId: upcomingRoomsDatabaseId,
259-
collectionId: subscribedUserCollectionId,
260-
documentId: subscription.$id,
261-
data: {"registrationTokens": registrationTokens});
330+
databaseId: upcomingRoomsDatabaseId,
331+
collectionId: subscribedUserCollectionId,
332+
documentId: subscription.$id,
333+
data: {"registrationTokens": registrationTokens},
334+
);
262335
}
263336

264337
//created Upcoming Rooms
265-
List<Document> createdUpcomingRooms = await databases.listDocuments(
266-
databaseId: upcomingRoomsDatabaseId,
267-
collectionId: upcomingRoomsCollectionId,
268-
queries: [
269-
Query.equal("creatorUid", [uid])
270-
]).then((value) => value.documents);
338+
List<Document> createdUpcomingRooms = await databases
339+
.listDocuments(
340+
databaseId: upcomingRoomsDatabaseId,
341+
collectionId: upcomingRoomsCollectionId,
342+
queries: [
343+
Query.equal("creatorUid", [uid]),
344+
],
345+
)
346+
.then((value) => value.documents);
271347
for (var upcomingRoom in createdUpcomingRooms) {
272348
List<dynamic> creatorFcmTokens = upcomingRoom.data['creator_fcm_tokens'];
273349
creatorFcmTokens.remove(fcmToken!);
274350
databases.updateDocument(
275-
databaseId: upcomingRoomsDatabaseId,
276-
collectionId: upcomingRoomsCollectionId,
277-
documentId: upcomingRoom.$id,
278-
data: {"creator_fcm_tokens": creatorFcmTokens});
351+
databaseId: upcomingRoomsDatabaseId,
352+
collectionId: upcomingRoomsCollectionId,
353+
documentId: upcomingRoom.$id,
354+
data: {"creator_fcm_tokens": creatorFcmTokens},
355+
);
279356
}
280357
}
281358

0 commit comments

Comments
 (0)