import 'package:http/http.dart' as http; class CalendarEvent { final String? summary; final String? description; final String? location; final DateTime? startTime; final DateTime? endTime; final bool isAllDay; CalendarEvent({ this.summary, this.description, this.location, this.startTime, this.endTime, this.isAllDay = false, }); } class GoogleCalendarService { static const String calendarId = 'b9c267252d5be63d5cdc880be499885a88421eefd0e34e1392890daba0d8471e@group.calendar.google.com'; final http.Client _httpClient = http.Client(); Future> getEvents({ DateTime? startDate, DateTime? endDate, }) async { try { // Use the public iCal feed URL final url = Uri.parse( 'https://calendar.google.com/calendar/ical/$calendarId/public/basic.ics'); final response = await _httpClient.get(url); if (response.statusCode == 200) { final allEvents = _parseICalendar(response.body); // Filter events by date range if provided if (startDate != null || endDate != null) { return allEvents.where((event) { if (event.startTime == null) return false; if (startDate != null && event.startTime!.isBefore(startDate)) { return false; } if (endDate != null && event.startTime!.isAfter(endDate)) { return false; } return true; }).toList(); } return allEvents; } else { throw Exception('Failed to load calendar: ${response.statusCode}'); } } catch (e) { throw Exception('Error fetching events: $e'); } } List _parseICalendar(String icalData) { final events = []; final lines = icalData.split('\n'); String? summary; String? description; String? location; DateTime? startTime; DateTime? endTime; bool isAllDay = false; for (var i = 0; i < lines.length; i++) { var line = lines[i].trim(); // Handle line continuations while (i + 1 < lines.length && lines[i + 1].startsWith(' ')) { i++; line += lines[i].trim(); } if (line.startsWith('BEGIN:VEVENT')) { summary = null; description = null; location = null; startTime = null; endTime = null; isAllDay = false; } else if (line.startsWith('SUMMARY:')) { summary = _unescapeText(line.substring(8)); } else if (line.startsWith('DESCRIPTION:')) { description = _unescapeText(line.substring(12)); } else if (line.startsWith('LOCATION:')) { location = _unescapeText(line.substring(9)); } else if (line.startsWith('DTSTART')) { final dateStr = line.split(':')[1]; if (line.contains('VALUE=DATE')) { isAllDay = true; startTime = _parseDate(dateStr); } else { startTime = _parseDateTime(dateStr); } } else if (line.startsWith('DTEND')) { final dateStr = line.split(':')[1]; if (line.contains('VALUE=DATE')) { endTime = _parseDate(dateStr); } else { endTime = _parseDateTime(dateStr); } } else if (line.startsWith('END:VEVENT')) { events.add(CalendarEvent( summary: summary, description: description, location: location, startTime: startTime, endTime: endTime, isAllDay: isAllDay, )); } } return events; } String _unescapeText(String text) { return text .replaceAll('\\n', '\n') .replaceAll('\\,', ',') .replaceAll('\\;', ';') .replaceAll('\\\\', '\\'); } DateTime? _parseDate(String dateStr) { try { // Format: YYYYMMDD final year = int.parse(dateStr.substring(0, 4)); final month = int.parse(dateStr.substring(4, 6)); final day = int.parse(dateStr.substring(6, 8)); return DateTime(year, month, day); } catch (e) { return null; } } DateTime? _parseDateTime(String dateStr) { try { // Format: YYYYMMDDTHHMMSSZ or YYYYMMDDTHHMMSS final year = int.parse(dateStr.substring(0, 4)); final month = int.parse(dateStr.substring(4, 6)); final day = int.parse(dateStr.substring(6, 8)); final hour = int.parse(dateStr.substring(9, 11)); final minute = int.parse(dateStr.substring(11, 13)); final second = int.parse(dateStr.substring(13, 15)); if (dateStr.endsWith('Z')) { return DateTime.utc(year, month, day, hour, minute, second); } else { return DateTime(year, month, day, hour, minute, second); } } catch (e) { return null; } } void dispose() { _httpClient.close(); } }