From 4d2307c98f31e75eec7e3fb904318c5d893a2446 Mon Sep 17 00:00:00 2001
From: saraayy <128136969+saraayy@users.noreply.github.com>
Date: Thu, 12 Mar 2026 17:38:45 +0200
Subject: [PATCH 1/5] Send calendar context when recording starts
---
android/app/src/main/AndroidManifest.xml | 1 +
lib/screens/landing_screen.dart | 26 ++++-
lib/services/calendar_service.dart | 119 +++++++++++++++++++++++
lib/services/websocket_service.dart | 6 ++
pubspec.lock | 24 +++++
pubspec.yaml | 1 +
6 files changed, 173 insertions(+), 4 deletions(-)
create mode 100644 lib/services/calendar_service.dart
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index af13917..445a5d6 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
{
late final Lc3Decoder _decoder;
late final WebsocketService _ws;
late final AudioPipeline _audioPipeline;
+ late final CalendarService _calendarService;
@override
void initState() {
@@ -39,6 +41,7 @@ class _LandingScreenState extends State {
_manager = widget.manager ?? G1Manager();
_decoder = widget.decoder ?? Lc3Decoder();
_ws = widget.ws ?? WebsocketService();
+ _calendarService = CalendarService();
_audioPipeline = widget.audioPipeline ??
AudioPipeline(
_manager,
@@ -106,10 +109,16 @@ class _LandingScreenState extends State {
children: [
Row(
children: [
- IconButton(
- onPressed: () {},
- icon: const Icon(Icons.menu, color: Color(0xFF00239D)),
- ),
+ SizedBox(
+ width: 96,
+ child: Align(
+ alignment: Alignment.centerLeft,
+ child: IconButton(
+ onPressed: () {},
+ icon: const Icon(Icons.menu, color: Color(0xFF00239D)),
+ ),
+ ),
+ ),
const Spacer(),
Image.asset(
'assets/images/Elisa_logo_blue_RGB.png',
@@ -166,6 +175,15 @@ class _LandingScreenState extends State {
manager: _manager,
onRecordToggle: () async {
if (!_manager.transcription.isActive.value) {
+ final granted = await _calendarService.requestPermission();
+ if (granted) {
+ final events = await _calendarService.getUpcomingEvents();
+ final activeEvent = _calendarService.selectActiveContext(events);
+ if (activeEvent != null) {
+ final payload = _calendarService.buildCalendarPayload(activeEvent);
+ _ws.sendCalendarContext(payload);
+ }
+ }
await _startTranscription();
} else {
await _stopTranscription();
diff --git a/lib/services/calendar_service.dart b/lib/services/calendar_service.dart
new file mode 100644
index 0000000..9a3aff7
--- /dev/null
+++ b/lib/services/calendar_service.dart
@@ -0,0 +1,119 @@
+import 'package:device_calendar/device_calendar.dart';
+
+
+
+
+class CalendarService {
+ final DeviceCalendarPlugin _calendarPlugin = DeviceCalendarPlugin();
+
+ //Requests calendar permission
+ Future requestPermission () async {
+ var permissionsGranted = await _calendarPlugin.requestPermissions();
+ if (permissionsGranted.isSuccess && permissionsGranted.data == true) {
+ return true;
+ // Permission granted, you can now access the calendar
+ } else {
+ return false;
+ // Permission denied, handle accordingly
+ }
+ }
+
+ //Searches for upcoming events in the next 7 days
+ Future > getUpcomingEvents() async {
+ var calendarResult = await _calendarPlugin.retrieveCalendars();
+ if (calendarResult.isSuccess && calendarResult.data != null) {
+ List calendars = calendarResult.data!;
+ List events = [];
+
+ DateTime startDate = DateTime.now();
+ DateTime endDate = startDate.add(const Duration(days: 7));
+
+
+ for (var calendar in calendars) {
+ var eventResult = await _calendarPlugin.retrieveEvents(
+ calendar.id!,
+ RetrieveEventsParams(startDate: startDate, endDate: endDate),
+ );
+
+ if (eventResult.isSuccess && eventResult.data != null) {
+ List calendarEvents = eventResult.data!;
+ for (var event in calendarEvents) {
+ if (event.start != null && event.end != null) {
+ events.add(
+ CalendarEventModel(
+ title: event.title ?? 'No Title',
+ description: event.description,
+ start: event.start!,
+ end: event.end!,
+ ),
+ );
+ }
+ }
+ }
+ }
+ events.sort((a, b) => a.start.compareTo(b.start));
+ return events;
+ }
+ return [];
+ }
+
+ //Selects the active or upcoming event
+ CalendarEventModel? selectActiveContext(List events) {
+ DateTime now = DateTime.now();
+ //Event is happening now
+ for (var event in events) {
+ if (event.start.isBefore(now) && event.end.isAfter(now) ) {
+ return event;
+ }
+ }
+ //Upcoming event
+ for (var event in events) {
+ if (event.start.isAfter(now)) {
+ return event;
+ }
+ }
+ //No active or upcoming events
+ return null;
+ }
+
+ //Builds the payload to send to backend
+ Map buildCalendarPayload(CalendarEventModel? event) {
+ if (event == null) {
+ return {
+ "type": "calendar_context",
+ "data": {
+ "title": "General conversation",
+ "description": null,
+ "start": null,
+ "end": null
+ }
+ };
+ }
+ return {
+ "type": "calendar_context",
+ "data": {
+ "title": event.title,
+ "description": event.description,
+ "start": event.start.toIso8601String(),
+ "end": event.end.toIso8601String()
+ }
+ };
+ }
+
+
+}
+
+// Model to represent calendar events in a simplified way for our application
+class CalendarEventModel {
+ final String title;
+ final String? description;
+ final DateTime start;
+ final DateTime end;
+
+ CalendarEventModel({
+ required this.title,
+ required this.description,
+ required this.start,
+ required this.end,
+ });
+}
\ No newline at end of file
diff --git a/lib/services/websocket_service.dart b/lib/services/websocket_service.dart
index a80017e..c9038f4 100644
--- a/lib/services/websocket_service.dart
+++ b/lib/services/websocket_service.dart
@@ -106,6 +106,12 @@ class WebsocketService {
}
}
+ void sendCalendarContext(Map payload) {
+ if (connected.value) {
+ _audioChannel?.sink.add(jsonEncode(payload));
+ }
+ }
+
/// Tell the backend to stop expecting audio data.
Future stopAudioStream() async {
if (connected.value) {
diff --git a/pubspec.lock b/pubspec.lock
index 58e2349..ab477ec 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -89,6 +89,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.11"
+ device_calendar:
+ dependency: "direct main"
+ description:
+ name: device_calendar
+ sha256: "683fb93ec302b6a65c0ce57df40ff9dcc2404f59c67a2f8b93e59318c8a0a225"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.3.3"
even_realities_g1:
dependency: "direct main"
description:
@@ -292,6 +300,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
+ sprintf:
+ dependency: transitive
+ description:
+ name: sprintf
+ sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.0"
stack_trace:
dependency: transitive
description:
@@ -332,6 +348,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.7"
+ timezone:
+ dependency: transitive
+ description:
+ name: timezone
+ sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.9.4"
typed_data:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 930cc3a..3ba24f0 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -33,6 +33,7 @@ dependencies:
cupertino_icons: ^1.0.8
# Local path dependency
+ device_calendar: ^4.3.3
even_realities_g1:
path: packages/even_realities_g1
flutter:
From 6406bf2383f12c12b75caaf78e191ad0f086d276 Mon Sep 17 00:00:00 2001
From: saraayy <128136969+saraayy@users.noreply.github.com>
Date: Thu, 12 Mar 2026 17:49:46 +0200
Subject: [PATCH 2/5] Configure device calendar plugin and Cocoapods
integration for ios
---
ios/Flutter/Debug.xcconfig | 1 +
ios/Flutter/Release.xcconfig | 1 +
ios/Podfile | 43 +++++++
ios/Podfile.lock | 29 +++++
ios/Runner.xcodeproj/project.pbxproj | 121 +++++++++++++++++-
.../contents.xcworkspacedata | 3 +
ios/Runner/Info.plist | 10 +-
7 files changed, 201 insertions(+), 7 deletions(-)
create mode 100644 ios/Podfile
create mode 100644 ios/Podfile.lock
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
index 592ceee..ec97fc6 100644
--- a/ios/Flutter/Debug.xcconfig
+++ b/ios/Flutter/Debug.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
index 592ceee..c4855bf 100644
--- a/ios/Flutter/Release.xcconfig
+++ b/ios/Flutter/Release.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Podfile b/ios/Podfile
new file mode 100644
index 0000000..23eab87
--- /dev/null
+++ b/ios/Podfile
@@ -0,0 +1,43 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '14.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
new file mode 100644
index 0000000..fbeca2d
--- /dev/null
+++ b/ios/Podfile.lock
@@ -0,0 +1,29 @@
+PODS:
+ - device_calendar (0.0.1):
+ - Flutter
+ - Flutter (1.0.0)
+ - flutter_blue_plus_darwin (0.0.2):
+ - Flutter
+ - FlutterMacOS
+
+DEPENDENCIES:
+ - device_calendar (from `.symlinks/plugins/device_calendar/ios`)
+ - Flutter (from `Flutter`)
+ - flutter_blue_plus_darwin (from `.symlinks/plugins/flutter_blue_plus_darwin/darwin`)
+
+EXTERNAL SOURCES:
+ device_calendar:
+ :path: ".symlinks/plugins/device_calendar/ios"
+ Flutter:
+ :path: Flutter
+ flutter_blue_plus_darwin:
+ :path: ".symlinks/plugins/flutter_blue_plus_darwin/darwin"
+
+SPEC CHECKSUMS:
+ device_calendar: b55b2c5406cfba45c95a59f9059156daee1f74ed
+ Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
+ flutter_blue_plus_darwin: 20a08bfeaa0f7804d524858d3d8744bcc1b6dbc3
+
+PODFILE CHECKSUM: 8c4d30c2258325612f2b7276ac7aac1f617fcf63
+
+COCOAPODS: 1.16.2
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 1d1b77b..6413778 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -14,6 +14,8 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ 99F5015DBD6F6F67074A1B9D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9B72C4D29009EF8D1D1EEA7 /* Pods_Runner.framework */; };
+ F8A02A1E8E6684C1D8450DF1 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E9C6A6BD5AE93A3E540561BF /* Pods_RunnerTests.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -45,9 +47,12 @@
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 61CA3FFAF8421699CD486D19 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 807E81FEBDA1B3C207D2F02D /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
+ 96D8DD3C040343A039F310FE /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -55,13 +60,27 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ A66E4728E7B9DA7D3BF9212D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
+ A9B72C4D29009EF8D1D1EEA7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ E805B90C1540E73EE0890817 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
+ E9C6A6BD5AE93A3E540561BF /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ F06CDC837EF05FDD4CA99461 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 7A60E1A3672AB68E0E2A42D1 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F8A02A1E8E6684C1D8450DF1 /* Pods_RunnerTests.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 99F5015DBD6F6F67074A1B9D /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -94,6 +113,8 @@
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
+ F0CFB2D7AEEBFE525929A1A9 /* Pods */,
+ CAAD90FF3B44AD28E7C94A7C /* Frameworks */,
);
sourceTree = "";
};
@@ -121,6 +142,29 @@
path = Runner;
sourceTree = "";
};
+ CAAD90FF3B44AD28E7C94A7C /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ A9B72C4D29009EF8D1D1EEA7 /* Pods_Runner.framework */,
+ E9C6A6BD5AE93A3E540561BF /* Pods_RunnerTests.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ F0CFB2D7AEEBFE525929A1A9 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ F06CDC837EF05FDD4CA99461 /* Pods-Runner.debug.xcconfig */,
+ A66E4728E7B9DA7D3BF9212D /* Pods-Runner.release.xcconfig */,
+ 61CA3FFAF8421699CD486D19 /* Pods-Runner.profile.xcconfig */,
+ 807E81FEBDA1B3C207D2F02D /* Pods-RunnerTests.debug.xcconfig */,
+ E805B90C1540E73EE0890817 /* Pods-RunnerTests.release.xcconfig */,
+ 96D8DD3C040343A039F310FE /* Pods-RunnerTests.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -128,8 +172,10 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
+ FEF6B413DA9FC937656B660D /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
+ 7A60E1A3672AB68E0E2A42D1 /* Frameworks */,
);
buildRules = (
);
@@ -145,12 +191,14 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
+ BEC52B047FD349E6B05F8651 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ 62220B443F82F6667CD7939F /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -238,6 +286,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
+ 62220B443F82F6667CD7939F /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -253,6 +318,50 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
+ BEC52B047FD349E6B05F8651 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ FEF6B413DA9FC937656B660D /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -362,13 +471,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = F2VMBPUNG6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.front;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.front-ai-smarties";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -378,6 +488,7 @@
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = 807E81FEBDA1B3C207D2F02D /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -395,6 +506,7 @@
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = E805B90C1540E73EE0890817 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -410,6 +522,7 @@
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = 96D8DD3C040343A039F310FE /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
@@ -541,13 +654,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = F2VMBPUNG6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.front;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.front-ai-smarties";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -563,13 +677,14 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = F2VMBPUNG6;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.front;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.example.front-ai-smarties";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata
index 1d526a1..21a3cc1 100644
--- a/ios/Runner.xcworkspace/contents.xcworkspacedata
+++ b/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -4,4 +4,7 @@
+
+
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 7788352..010caf8 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -2,6 +2,8 @@
+ CADisableMinimumFrameDurationOnPhone
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -24,6 +26,10 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+ NSCalendarsFullAccessUsageDescription
+ Access most functions for calendar viewing.
+ UIApplicationSupportsIndirectInputEvents
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -41,9 +47,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- CADisableMinimumFrameDurationOnPhone
-
- UIApplicationSupportsIndirectInputEvents
-
From d7cbd6eb4157fc5ac2d3f5eff9b739eea84650ba Mon Sep 17 00:00:00 2001
From: saraayy <128136969+saraayy@users.noreply.github.com>
Date: Thu, 12 Mar 2026 17:51:11 +0200
Subject: [PATCH 3/5] Add CalendarService unit tests
---
test/services/calendar_service_test.dart | 83 ++++++++++++++++++++++++
1 file changed, 83 insertions(+)
create mode 100644 test/services/calendar_service_test.dart
diff --git a/test/services/calendar_service_test.dart b/test/services/calendar_service_test.dart
new file mode 100644
index 0000000..6aec032
--- /dev/null
+++ b/test/services/calendar_service_test.dart
@@ -0,0 +1,83 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:front/services/calendar_service.dart';
+
+void main() {
+ group('CalendarService.selectActiveContext', () {
+ final service = CalendarService();
+
+ test('returns null when event list is empty', () {
+ final result = service.selectActiveContext([]);
+
+ expect(result, isNull);
+ });
+ test('return active event when one is happening now', () {
+ final now = DateTime.now();
+ final events = [CalendarEventModel(
+ title: 'Business meeting',
+ description: 'Discussing quarterly results',
+ start: now.subtract(const Duration(minutes: 30)),
+ end: now.add(const Duration(minutes: 30))
+ ),
+ ];
+ final result = service.selectActiveContext(events);
+
+ expect(result, isNotNull);
+ expect(result!.title, 'Business meeting');
+ });
+ test('return upcoming event when no event is active', () {
+ final now = DateTime.now();
+ final events = [
+ CalendarEventModel(
+ title: 'Project deadline',
+ description: 'Submit final report',
+ start: now.add(const Duration(hours: 1)),
+ end: now.add(const Duration(hours: 2))
+ ),
+ CalendarEventModel(
+ title: 'Later meeting',
+ description: 'Retrospective',
+ start: now.add(const Duration(hours: 3)),
+ end: now.add(const Duration(hours: 4))
+ ),
+ ];
+ final result = service.selectActiveContext(events);
+
+ expect(result, isNotNull);
+ expect(result!.title, 'Project deadline');
+ });
+ });
+
+ group('CalendarService.buildCalendarPayload', () {
+ final service = CalendarService();
+
+ test('Build payload from active event', () {
+ final event = CalendarEventModel(
+ title: 'Business meeting',
+ description: 'Discussing quarterly results',
+ start: DateTime.parse('2025-06-01T10:00:00Z'),
+ end: DateTime.parse('2025-06-01T11:00:00Z')
+ );
+ final payload = service.buildCalendarPayload(event);
+
+ expect(payload['type'], 'calendar_context');
+ expect(payload['data']['title'], 'Business meeting');
+ expect(payload['data']['description'], 'Discussing quarterly results');
+ expect(payload['data']['start'],event.start.toIso8601String());
+ expect(payload['data']['end'], event.end.toIso8601String());
+ });
+ test('Build payload with null description', () {
+ final event = CalendarEventModel(
+ title: 'Quick meeting',
+ description: null,
+ start: DateTime.parse('2025-06-01T12:00:00Z'),
+ end: DateTime.parse('2025-06-01T12:30:00Z')
+ );
+ final payload = service.buildCalendarPayload(event);
+
+ expect(payload['data']['title'], 'Quick meeting');
+ expect(payload['data']['description'], isNull);
+ });
+ });
+}
+
+
From f58f7f6e5874a8373d191b5daa4bd270f6bf1942 Mon Sep 17 00:00:00 2001
From: saraayy <128136969+saraayy@users.noreply.github.com>
Date: Fri, 13 Mar 2026 10:13:33 +0200
Subject: [PATCH 4/5] Apply dart format
---
lib/screens/landing_screen.dart | 16 ++++--
lib/services/calendar_service.dart | 22 +++-----
test/services/calendar_service_test.dart | 72 +++++++++++-------------
3 files changed, 51 insertions(+), 59 deletions(-)
diff --git a/lib/screens/landing_screen.dart b/lib/screens/landing_screen.dart
index 021af72..4435a7b 100644
--- a/lib/screens/landing_screen.dart
+++ b/lib/screens/landing_screen.dart
@@ -109,7 +109,7 @@ class _LandingScreenState extends State {
children: [
Row(
children: [
- SizedBox(
+ SizedBox(
width: 96,
child: Align(
alignment: Alignment.centerLeft,
@@ -118,7 +118,7 @@ class _LandingScreenState extends State {
icon: const Icon(Icons.menu, color: Color(0xFF00239D)),
),
),
- ),
+ ),
const Spacer(),
Image.asset(
'assets/images/Elisa_logo_blue_RGB.png',
@@ -175,12 +175,16 @@ class _LandingScreenState extends State {
manager: _manager,
onRecordToggle: () async {
if (!_manager.transcription.isActive.value) {
- final granted = await _calendarService.requestPermission();
+ final granted =
+ await _calendarService.requestPermission();
if (granted) {
- final events = await _calendarService.getUpcomingEvents();
- final activeEvent = _calendarService.selectActiveContext(events);
+ final events =
+ await _calendarService.getUpcomingEvents();
+ final activeEvent =
+ _calendarService.selectActiveContext(events);
if (activeEvent != null) {
- final payload = _calendarService.buildCalendarPayload(activeEvent);
+ final payload = _calendarService
+ .buildCalendarPayload(activeEvent);
_ws.sendCalendarContext(payload);
}
}
diff --git a/lib/services/calendar_service.dart b/lib/services/calendar_service.dart
index 9a3aff7..2eabc15 100644
--- a/lib/services/calendar_service.dart
+++ b/lib/services/calendar_service.dart
@@ -1,13 +1,10 @@
import 'package:device_calendar/device_calendar.dart';
-
-
-
class CalendarService {
final DeviceCalendarPlugin _calendarPlugin = DeviceCalendarPlugin();
//Requests calendar permission
- Future requestPermission () async {
+ Future requestPermission() async {
var permissionsGranted = await _calendarPlugin.requestPermissions();
if (permissionsGranted.isSuccess && permissionsGranted.data == true) {
return true;
@@ -18,17 +15,16 @@ class CalendarService {
}
}
- //Searches for upcoming events in the next 7 days
- Future > getUpcomingEvents() async {
+ //Searches for upcoming events in the next 7 days
+ Future> getUpcomingEvents() async {
var calendarResult = await _calendarPlugin.retrieveCalendars();
if (calendarResult.isSuccess && calendarResult.data != null) {
List calendars = calendarResult.data!;
List events = [];
-
+
DateTime startDate = DateTime.now();
DateTime endDate = startDate.add(const Duration(days: 7));
-
for (var calendar in calendars) {
var eventResult = await _calendarPlugin.retrieveEvents(
calendar.id!,
@@ -48,13 +44,13 @@ class CalendarService {
),
);
}
- }
+ }
}
}
events.sort((a, b) => a.start.compareTo(b.start));
return events;
}
- return [];
+ return [];
}
//Selects the active or upcoming event
@@ -62,7 +58,7 @@ class CalendarService {
DateTime now = DateTime.now();
//Event is happening now
for (var event in events) {
- if (event.start.isBefore(now) && event.end.isAfter(now) ) {
+ if (event.start.isBefore(now) && event.end.isAfter(now)) {
return event;
}
}
@@ -99,8 +95,6 @@ class CalendarService {
}
};
}
-
-
}
// Model to represent calendar events in a simplified way for our application
@@ -116,4 +110,4 @@ class CalendarEventModel {
required this.start,
required this.end,
});
-}
\ No newline at end of file
+}
diff --git a/test/services/calendar_service_test.dart b/test/services/calendar_service_test.dart
index 6aec032..3b6252f 100644
--- a/test/services/calendar_service_test.dart
+++ b/test/services/calendar_service_test.dart
@@ -12,38 +12,36 @@ void main() {
});
test('return active event when one is happening now', () {
final now = DateTime.now();
- final events = [CalendarEventModel(
- title: 'Business meeting',
- description: 'Discussing quarterly results',
- start: now.subtract(const Duration(minutes: 30)),
- end: now.add(const Duration(minutes: 30))
- ),
- ];
- final result = service.selectActiveContext(events);
+ final events = [
+ CalendarEventModel(
+ title: 'Business meeting',
+ description: 'Discussing quarterly results',
+ start: now.subtract(const Duration(minutes: 30)),
+ end: now.add(const Duration(minutes: 30))),
+ ];
+ final result = service.selectActiveContext(events);
- expect(result, isNotNull);
- expect(result!.title, 'Business meeting');
+ expect(result, isNotNull);
+ expect(result!.title, 'Business meeting');
});
test('return upcoming event when no event is active', () {
final now = DateTime.now();
final events = [
- CalendarEventModel(
- title: 'Project deadline',
- description: 'Submit final report',
- start: now.add(const Duration(hours: 1)),
- end: now.add(const Duration(hours: 2))
- ),
- CalendarEventModel(
- title: 'Later meeting',
- description: 'Retrospective',
- start: now.add(const Duration(hours: 3)),
- end: now.add(const Duration(hours: 4))
- ),
- ];
- final result = service.selectActiveContext(events);
+ CalendarEventModel(
+ title: 'Project deadline',
+ description: 'Submit final report',
+ start: now.add(const Duration(hours: 1)),
+ end: now.add(const Duration(hours: 2))),
+ CalendarEventModel(
+ title: 'Later meeting',
+ description: 'Retrospective',
+ start: now.add(const Duration(hours: 3)),
+ end: now.add(const Duration(hours: 4))),
+ ];
+ final result = service.selectActiveContext(events);
- expect(result, isNotNull);
- expect(result!.title, 'Project deadline');
+ expect(result, isNotNull);
+ expect(result!.title, 'Project deadline');
});
});
@@ -52,26 +50,24 @@ void main() {
test('Build payload from active event', () {
final event = CalendarEventModel(
- title: 'Business meeting',
- description: 'Discussing quarterly results',
- start: DateTime.parse('2025-06-01T10:00:00Z'),
- end: DateTime.parse('2025-06-01T11:00:00Z')
- );
+ title: 'Business meeting',
+ description: 'Discussing quarterly results',
+ start: DateTime.parse('2025-06-01T10:00:00Z'),
+ end: DateTime.parse('2025-06-01T11:00:00Z'));
final payload = service.buildCalendarPayload(event);
expect(payload['type'], 'calendar_context');
expect(payload['data']['title'], 'Business meeting');
expect(payload['data']['description'], 'Discussing quarterly results');
- expect(payload['data']['start'],event.start.toIso8601String());
+ expect(payload['data']['start'], event.start.toIso8601String());
expect(payload['data']['end'], event.end.toIso8601String());
});
test('Build payload with null description', () {
final event = CalendarEventModel(
- title: 'Quick meeting',
- description: null,
- start: DateTime.parse('2025-06-01T12:00:00Z'),
- end: DateTime.parse('2025-06-01T12:30:00Z')
- );
+ title: 'Quick meeting',
+ description: null,
+ start: DateTime.parse('2025-06-01T12:00:00Z'),
+ end: DateTime.parse('2025-06-01T12:30:00Z'));
final payload = service.buildCalendarPayload(event);
expect(payload['data']['title'], 'Quick meeting');
@@ -79,5 +75,3 @@ void main() {
});
});
}
-
-
From 9d66271023f5a9026d69bea75b256560be5506c6 Mon Sep 17 00:00:00 2001
From: saraayy <128136969+saraayy@users.noreply.github.com>
Date: Fri, 13 Mar 2026 11:03:31 +0200
Subject: [PATCH 5/5] Apply dart format
---
lib/screens/landing_screen.dart | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/lib/screens/landing_screen.dart b/lib/screens/landing_screen.dart
index 1b22dff..86b3ded 100644
--- a/lib/screens/landing_screen.dart
+++ b/lib/screens/landing_screen.dart
@@ -283,10 +283,10 @@ class _LandingScreenState extends State {
final granted =
await _calendarService.requestPermission();
if (granted) {
- final events =
- await _calendarService.getUpcomingEvents();
- final activeEvent =
- _calendarService.selectActiveContext(events);
+ final events = await _calendarService
+ .getUpcomingEvents();
+ final activeEvent = _calendarService
+ .selectActiveContext(events);
if (activeEvent != null) {
final payload = _calendarService
.buildCalendarPayload(activeEvent);