From d650f91256c317648b8ae8f5b0b66edab40a4e28 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Sun, 14 Jul 2024 20:57:07 +0200 Subject: [PATCH 01/20] building fixes --- example/android/app/build.gradle | 4 ++-- example/android/build.gradle | 2 +- example/lib/presentation/date_time_picker.dart | 2 +- example/lib/presentation/pages/calendars.dart | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 776dc817..7f0cf3be 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -16,7 +16,7 @@ apply plugin: 'kotlin-android' apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" android { - compileSdkVersion 32 + compileSdkVersion 34 ndkVersion '22.1.7171670' sourceSets { @@ -30,7 +30,7 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.builttoroam.devicecalendarexample" - minSdkVersion 19 + minSdkVersion flutter.minSdkVersion targetSdkVersion 31 versionCode 1 versionName "1.0" diff --git a/example/android/build.gradle b/example/android/build.gradle index d3f65307..8bbe685b 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.0' + ext.kotlin_version = '1.8.22' repositories { google() mavenCentral() diff --git a/example/lib/presentation/date_time_picker.dart b/example/lib/presentation/date_time_picker.dart index dc11e8d9..449b82e8 100644 --- a/example/lib/presentation/date_time_picker.dart +++ b/example/lib/presentation/date_time_picker.dart @@ -45,7 +45,7 @@ class DateTimePicker extends StatelessWidget { @override Widget build(BuildContext context) { - final valueStyle = Theme.of(context).textTheme.headline6; + final valueStyle = Theme.of(context).textTheme.titleLarge; return Row( crossAxisAlignment: CrossAxisAlignment.end, children: [ diff --git a/example/lib/presentation/pages/calendars.dart b/example/lib/presentation/pages/calendars.dart index 71c47ea5..389d9b02 100644 --- a/example/lib/presentation/pages/calendars.dart +++ b/example/lib/presentation/pages/calendars.dart @@ -46,7 +46,7 @@ class _CalendarsPageState extends State { padding: const EdgeInsets.all(10.0), child: Text( 'WARNING: some aspects of saving events are hardcoded in this example app. As such we recommend you do not modify existing events as this may result in loss of information', - style: Theme.of(context).textTheme.headline6, + style: Theme.of(context).textTheme.titleLarge, ), ), Expanded( @@ -77,7 +77,7 @@ class _CalendarsPageState extends State { Text( "${_calendars[index].id}: ${_calendars[index].name!}", style: - Theme.of(context).textTheme.subtitle1, + Theme.of(context).textTheme.titleSmall, ), Text( "Account: ${_calendars[index].accountName!}"), From 634fd1b1673f36258bd07df903a9f551665caa83 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Sun, 14 Jul 2024 20:57:44 +0200 Subject: [PATCH 02/20] added event color for android --- android/build.gradle | 2 +- .../devicecalendar/CalendarDelegate.kt | 53 +++++++++++ .../devicecalendar/DeviceCalendarPlugin.kt | 9 ++ .../devicecalendar/common/Constants.kt | 4 +- .../devicecalendar/models/Event.kt | 2 + .../presentation/pages/calendar_event.dart | 95 ++++++++++++++----- .../presentation/pages/calendar_events.dart | 7 ++ lib/device_calendar.dart | 1 + lib/src/common/channel_constants.dart | 2 + lib/src/device_calendar.dart | 16 ++++ lib/src/models/event.dart | 20 +++- lib/src/models/event_color.dart | 9 ++ 12 files changed, 194 insertions(+), 26 deletions(-) create mode 100644 lib/src/models/event_color.dart diff --git a/android/build.gradle b/android/build.gradle index 1f5ff500..34fcf1ae 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 33 + compileSdkVersion 34 sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt index 1cd3f98a..888c2bff 100644 --- a/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt @@ -39,6 +39,8 @@ import com.builttoroam.devicecalendar.common.ErrorCodes.Companion as EC import com.builttoroam.devicecalendar.common.ErrorMessages.Companion as EM import org.dmfs.rfc5545.recur.Freq as RruleFreq import org.dmfs.rfc5545.recur.RecurrenceRule as Rrule +import android.provider.CalendarContract.Colors +import androidx.collection.SparseArrayCompat private const val RETRIEVE_CALENDARS_REQUEST_CODE = 0 private const val RETRIEVE_EVENTS_REQUEST_CODE = RETRIEVE_CALENDARS_REQUEST_CODE + 1 @@ -625,6 +627,7 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : values.put(Events.DTEND, end) values.put(Events.EVENT_END_TIMEZONE, endTimeZone) values.put(Events.DURATION, duration) + values.put(Events.EVENT_COLOR_KEY, event.eventColorKey) return values } @@ -938,6 +941,7 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : val endTimeZone = cursor.getString(Cst.EVENT_PROJECTION_END_TIMEZONE_INDEX) val availability = parseAvailability(cursor.getInt(Cst.EVENT_PROJECTION_AVAILABILITY_INDEX)) val eventStatus = parseEventStatus(cursor.getInt(Cst.EVENT_PROJECTION_STATUS_INDEX)) + val eventColor = cursor.getInt(Cst.EVENT_PROJECTION_EVENT_COLOR_INDEX) val event = Event() event.eventTitle = title ?: "New Event" event.eventId = eventId.toString() @@ -953,6 +957,7 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : event.eventEndTimeZone = endTimeZone event.availability = availability event.eventStatus = eventStatus + event.eventColor = if (eventColor == 0) null else eventColor return event } @@ -1125,6 +1130,54 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : return reminders } + /** + * load available event colors for the given account name + * unable to find official documentation, so logic is based on https://android.googlesource.com/platform/packages/apps/Calendar.git/+/refs/heads/pie-release/src/com/android/calendar/EventInfoFragment.java + **/ + fun retrieveEventColors(accountName: String): List> { + val contentResolver: ContentResolver? = _context?.contentResolver + val uri: Uri = Colors.CONTENT_URI + val colors = mutableListOf() + val displayColorKeyMap = SparseArrayCompat() + + val projection = arrayOf( + Colors.COLOR, + Colors.COLOR_KEY, + ) + + // load only event colors for the given account name + val selection = "${Colors.COLOR_TYPE} = ? AND ${Colors.ACCOUNT_NAME} = ?" + val selectionArgs = arrayOf(Colors.TYPE_EVENT.toString(), accountName) + + val cursor: Cursor? = contentResolver?.query(uri, projection, selection, selectionArgs, null) + cursor?.use { + while (it.moveToNext()) { + val color = it.getInt(it.getColumnIndexOrThrow(Colors.COLOR)) + val colorKey = it.getInt(it.getColumnIndexOrThrow(Colors.COLOR_KEY)) + displayColorKeyMap.put(color, colorKey); + colors.add(color) + } + cursor.close(); + // sort colors by colorValue, since they are loaded unordered + colors.sortWith(HsvColorComparator()) + } + return colors.map { Pair(it, displayColorKeyMap[it]!! ) }.toList() + } + + /** + * Compares colors based on their hue values in the HSV color space. + * https://android.googlesource.com/platform/prebuilts/fullsdk/sources/+/refs/heads/androidx-compose-integration-release/android-34/com/android/colorpicker/HsvColorComparator.java + */ + private class HsvColorComparator : Comparator { + override fun compare(color1: Int, color2: Int): Int { + val hsv1 = FloatArray(3) + val hsv2 = FloatArray(3) + Color.colorToHSV(color1, hsv1) + Color.colorToHSV(color2, hsv2) + return hsv1[0].compareTo(hsv2[0]) + } + } + @Synchronized private fun generateUniqueRequestCodeAndCacheParameters(parameters: CalendarMethodsParametersCacheModel): Int { // TODO we can ran out of Int's at some point so this probably should re-use some of the freed ones diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt index c1f14533..2af342bd 100644 --- a/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt @@ -25,10 +25,12 @@ private const val DELETE_EVENT_INSTANCE_METHOD = "deleteEventInstance" private const val CREATE_OR_UPDATE_EVENT_METHOD = "createOrUpdateEvent" private const val CREATE_CALENDAR_METHOD = "createCalendar" private const val DELETE_CALENDAR_METHOD = "deleteCalendar" +private const val RETRIEVE_EVENT_COLORS_METHOD = "retrieveEventColors" // Method arguments private const val CALENDAR_ID_ARGUMENT = "calendarId" private const val CALENDAR_NAME_ARGUMENT = "calendarName" +private const val CALENDAR_ACCOUNT_NAME_ARGUMENT = "accountName" private const val START_DATE_ARGUMENT = "startDate" private const val END_DATE_ARGUMENT = "endDate" private const val EVENT_IDS_ARGUMENT = "eventIds" @@ -66,6 +68,7 @@ private const val LOCAL_ACCOUNT_NAME_ARGUMENT = "localAccountName" private const val EVENT_AVAILABILITY_ARGUMENT = "availability" private const val ATTENDANCE_STATUS_ARGUMENT = "attendanceStatus" private const val EVENT_STATUS_ARGUMENT = "eventStatus" +private const val EVENT_COLOR_KEY_ARGUMENT = "eventColorKey" class DeviceCalendarPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { @@ -171,6 +174,11 @@ class DeviceCalendarPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { val calendarId = call.argument(CALENDAR_ID_ARGUMENT) _calendarDelegate.deleteCalendar(calendarId!!, result) } + RETRIEVE_EVENT_COLORS_METHOD -> { + val accountName = call.argument(CALENDAR_ACCOUNT_NAME_ARGUMENT) + val colors = _calendarDelegate.retrieveEventColors(accountName!!) + result.success(colors.map { listOf(it.first, it.second) }) + } else -> { result.notImplemented() } @@ -192,6 +200,7 @@ class DeviceCalendarPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { event.eventURL = call.argument(EVENT_URL_ARGUMENT) event.availability = parseAvailability(call.argument(EVENT_AVAILABILITY_ARGUMENT)) event.eventStatus = parseEventStatus(call.argument(EVENT_STATUS_ARGUMENT)) + event.eventColorKey = call.argument(EVENT_COLOR_KEY_ARGUMENT) if (call.hasArgument(RECURRENCE_RULE_ARGUMENT) && call.argument>( RECURRENCE_RULE_ARGUMENT diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt index 9d136ed5..fc49f227 100644 --- a/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt @@ -50,6 +50,7 @@ class Constants { const val EVENT_PROJECTION_END_TIMEZONE_INDEX: Int = 12 const val EVENT_PROJECTION_AVAILABILITY_INDEX: Int = 13 const val EVENT_PROJECTION_STATUS_INDEX: Int = 14 + const val EVENT_PROJECTION_EVENT_COLOR_INDEX: Int = 15 val EVENT_PROJECTION: Array = arrayOf( CalendarContract.Instances.EVENT_ID, @@ -66,7 +67,8 @@ class Constants { CalendarContract.Events.EVENT_TIMEZONE, CalendarContract.Events.EVENT_END_TIMEZONE, CalendarContract.Events.AVAILABILITY, - CalendarContract.Events.STATUS + CalendarContract.Events.STATUS, + CalendarContract.Events.EVENT_COLOR ) const val EVENT_INSTANCE_DELETION_ID_INDEX: Int = 0 diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt index 456e549f..dc988fbb 100644 --- a/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt @@ -18,4 +18,6 @@ class Event { var reminders: MutableList = mutableListOf() var availability: Availability? = null var eventStatus: EventStatus? = null + var eventColor: Int? = null + var eventColorKey: Int? = null } \ No newline at end of file diff --git a/example/lib/presentation/pages/calendar_event.dart b/example/lib/presentation/pages/calendar_event.dart index 72c4cb5b..47bd5a5c 100644 --- a/example/lib/presentation/pages/calendar_event.dart +++ b/example/lib/presentation/pages/calendar_event.dart @@ -18,14 +18,15 @@ class CalendarEventPage extends StatefulWidget { final Calendar _calendar; final Event? _event; final RecurringEventDialog? _recurringEventDialog; + final List? _eventColors; const CalendarEventPage(this._calendar, - [this._event, this._recurringEventDialog, Key? key]) + [this._event, this._recurringEventDialog, this._eventColors, Key? key]) : super(key: key); @override _CalendarEventPageState createState() { - return _CalendarEventPageState(_calendar, _event, _recurringEventDialog); + return _CalendarEventPageState(_calendar, _event, _recurringEventDialog, _eventColors); } } @@ -61,10 +62,11 @@ class _CalendarEventPageState extends State { EventStatus? _eventStatus; List? _attendees; List? _reminders; + List? _eventColors; String _timezone = 'Etc/UTC'; _CalendarEventPageState( - this._calendar, this._event, this._recurringEventDialog) { + this._calendar, this._event, this._recurringEventDialog, this._eventColors) { getCurentLocation(); } @@ -283,6 +285,30 @@ class _CalendarEventPageState extends State { }).toList(), ), ), + if (_eventColors?.isNotEmpty ?? false) + ListTile( + leading: const Text( + 'EventColor', + style: TextStyle(fontSize: 16), + ), + trailing: widget._event?.color == null ? const Text("not set") : Container( + width: 30, + height: 30, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Color(widget._event?.color ?? 0), + )), + onTap: () async { + final colors = _eventColors; + if (colors != null) { + final newColor = await selectColorDialog(colors); + if (newColor != null) { + setState(() { + _event?.updateEventColor(newColor); + }); + }} + }, + ), SwitchListTile( value: _event?.allDay ?? false, onChanged: (value) => @@ -674,11 +700,11 @@ class _CalendarEventPageState extends State { setState(() { if (value) { _rrule = _rrule?.copyWith( - byMonthDays: {1}, byWeekDays: {}); + byMonthDays: [1], byWeekDays: []); } else { _rrule = _rrule?.copyWith( - byMonthDays: {}, - byWeekDays: {ByWeekDayEntry(1, 1)}); + byMonthDays: [], + byWeekDays: [ByWeekDayEntry(1, 1)]); } }); }, @@ -694,7 +720,7 @@ class _CalendarEventPageState extends State { if (value != null) { setState(() { _rrule = _rrule - ?.copyWith(byMonths: {value.index + 1}); + ?.copyWith(byMonths: [value.index + 1]); _getValidDaysOfMonth(_rrule?.frequency); }); } @@ -722,7 +748,7 @@ class _CalendarEventPageState extends State { if (value != null) { setState(() { _rrule = - _rrule?.copyWith(byMonthDays: {value}); + _rrule?.copyWith(byMonthDays: [value]); }); } }, @@ -766,10 +792,10 @@ class _CalendarEventPageState extends State { _rrule?.byWeekDays.first.day ?? 1; setState(() { _rrule = _rrule?.copyWith( - byWeekDays: { + byWeekDays: [ ByWeekDayEntry( weekDay, value.index + 1) - }); + ]); }); } }, @@ -795,10 +821,10 @@ class _CalendarEventPageState extends State { 1; setState(() { _rrule = _rrule?.copyWith( - byWeekDays: { + byWeekDays: [ ByWeekDayEntry( value.index + 1, weekNo) - }); + ]); }); } }, @@ -825,7 +851,7 @@ class _CalendarEventPageState extends State { if (value != null) { setState(() { _rrule = _rrule?.copyWith( - byMonths: {value.index + 1}); + byMonths: [value.index + 1]); }); } }, @@ -1068,22 +1094,22 @@ class _CalendarEventPageState extends State { void _updateDaysOfWeek() { switch (_dayOfWeekGroup) { case DayOfWeekGroup.Weekday: - _rrule = _rrule?.copyWith(byWeekDays: { + _rrule = _rrule?.copyWith(byWeekDays: [ ByWeekDayEntry(1), ByWeekDayEntry(2), ByWeekDayEntry(3), ByWeekDayEntry(4), ByWeekDayEntry(5), - }); + ]); break; case DayOfWeekGroup.Weekend: - _rrule = _rrule?.copyWith(byWeekDays: { + _rrule = _rrule?.copyWith(byWeekDays: [ ByWeekDayEntry(6), ByWeekDayEntry(7), - }); + ]); break; case DayOfWeekGroup.AllDays: - _rrule = _rrule?.copyWith(byWeekDays: { + _rrule = _rrule?.copyWith(byWeekDays: [ ByWeekDayEntry(1), ByWeekDayEntry(2), ByWeekDayEntry(3), @@ -1091,7 +1117,7 @@ class _CalendarEventPageState extends State { ByWeekDayEntry(5), ByWeekDayEntry(6), ByWeekDayEntry(7), - }); + ]); break; case DayOfWeekGroup.None: default: @@ -1138,7 +1164,7 @@ class _CalendarEventPageState extends State { } } - int _weekNumFromWeekDayOccurence(Set weekdays) { + int _weekNumFromWeekDayOccurence(List weekdays) { final weekNum = weekdays.first.occurrence; if (weekNum != null) { return weekNum - 1; @@ -1168,7 +1194,7 @@ class _CalendarEventPageState extends State { } if (!hasByWeekDays && !hasByMonthDays) { _rrule = rrule - .copyWith(frequency: freq, byWeekDays: {ByWeekDayEntry(1, 1)}); + .copyWith(frequency: freq, byWeekDays: [ByWeekDayEntry(1, 1)]); } else { _rrule = rrule.copyWith(frequency: freq); } @@ -1177,8 +1203,8 @@ class _CalendarEventPageState extends State { if (!hasByWeekDays || !hasByMonths) { _rrule = rrule.copyWith( frequency: freq, - byWeekDays: {ByWeekDayEntry(1, 1)}, - byMonths: {1}); + byWeekDays: [ByWeekDayEntry(1, 1)], + byMonths: [1]); } else { _rrule = rrule.copyWith(frequency: freq); } @@ -1258,4 +1284,27 @@ class _CalendarEventPageState extends State { void showInSnackBar(BuildContext context, String value) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(value))); } + + Future selectColorDialog(List colors) async { + return await showDialog( + context: context, + builder: (BuildContext context) { + return SimpleDialog( + title: const Text('Select Event color'), + children: colors.map((color) => + SimpleDialogOption( + onPressed: () { Navigator.pop(context, color); }, + child: Container( + width: 48, + height: 48, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Color(color.color)), + ), + ) + ).toList() + ); + } + ); + } } diff --git a/example/lib/presentation/pages/calendar_events.dart b/example/lib/presentation/pages/calendar_events.dart index a8d4b2b2..781b39b4 100644 --- a/example/lib/presentation/pages/calendar_events.dart +++ b/example/lib/presentation/pages/calendar_events.dart @@ -24,6 +24,7 @@ class _CalendarEventsPageState extends State { late DeviceCalendarPlugin _deviceCalendarPlugin; List _calendarEvents = []; + List? _eventColors; bool _isLoading = true; _CalendarEventsPageState(this._calendar) { @@ -33,6 +34,7 @@ class _CalendarEventsPageState extends State { @override void initState() { super.initState(); + _retrieveEventColors(); _retrieveCalendarEvents(); } @@ -123,6 +125,7 @@ class _CalendarEventsPageState extends State { _onLoading, _onDeletedFinished, ), + _eventColors ); })); if (refreshEvents != null && refreshEvents) { @@ -142,6 +145,10 @@ class _CalendarEventsPageState extends State { }); } + void _retrieveEventColors() async { + _eventColors = await _deviceCalendarPlugin.retrieveEventColors(_calendar); + } + Widget _getDeleteButton() { return IconButton( icon: const Icon(Icons.delete), diff --git a/lib/device_calendar.dart b/lib/device_calendar.dart index 3566d5df..b3a7c906 100644 --- a/lib/device_calendar.dart +++ b/lib/device_calendar.dart @@ -6,6 +6,7 @@ export 'src/models/calendar.dart'; export 'src/models/result.dart'; export 'src/models/reminder.dart'; export 'src/models/event.dart'; +export 'src/models/event_color.dart'; export 'src/models/retrieve_events_params.dart'; export 'package:rrule/rrule.dart'; export 'package:rrule/src/frequency.dart'; diff --git a/lib/src/common/channel_constants.dart b/lib/src/common/channel_constants.dart index 2eef3d2d..4c1890d5 100644 --- a/lib/src/common/channel_constants.dart +++ b/lib/src/common/channel_constants.dart @@ -11,6 +11,7 @@ class ChannelConstants { static const String methodNameCreateCalendar = 'createCalendar'; static const String methodNameDeleteCalendar = 'deleteCalendar'; static const String methodNameShowiOSEventModal = 'showiOSEventModal'; + static const String methodNameRetrieveEventColors = 'retrieveEventColors'; static const String parameterNameCalendarId = 'calendarId'; static const String parameterNameStartDate = 'startDate'; @@ -23,4 +24,5 @@ class ChannelConstants { static const String parameterNameCalendarName = 'calendarName'; static const String parameterNameCalendarColor = 'calendarColor'; static const String parameterNameLocalAccountName = 'localAccountName'; + static const String parameterAccountName = "accountName"; } diff --git a/lib/src/device_calendar.dart b/lib/src/device_calendar.dart index 4c1d12f1..b48d8971 100644 --- a/lib/src/device_calendar.dart +++ b/lib/src/device_calendar.dart @@ -2,6 +2,7 @@ import 'dart:collection'; import 'dart:convert'; import 'dart:io'; +import 'package:device_calendar/src/models/event_color.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:timezone/data/latest.dart' as tz; @@ -342,6 +343,21 @@ class DeviceCalendarPlugin { ); } + Future?> retrieveEventColors(Calendar calendar) async { + if (!Platform.isAndroid) { + return null; + } + final accountName = calendar.accountName; + if (accountName == null) { + return []; + } + final dynamic colors = await _invokeChannelMethod(ChannelConstants.methodNameRetrieveEventColors, + arguments: () => { + ChannelConstants.parameterAccountName: accountName, + },); + return (colors.data as List).cast().map((color) => EventColor(color[0], color[1])).toList(); + } + Future> _invokeChannelMethod( String channelMethodName, { Function(Result)? assertParameters, diff --git a/lib/src/models/event.dart b/lib/src/models/event.dart index 94ef6217..12e359d8 100644 --- a/lib/src/models/event.dart +++ b/lib/src/models/event.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:collection/collection.dart'; +import 'event_color.dart'; import '../../device_calendar.dart'; import '../common/error_messages.dart'; @@ -49,6 +50,15 @@ class Event { /// Indicates if this event is of confirmed, canceled, tentative or none status EventStatus? status; + /// Read-only. Color of the event + int? get color=> _color; + + /// The color of this event + int? _color; + + /// The color key of this event. This is needed to change the event color + int? _colorKey; + ///Note for development: /// ///JSON field names are coded in dart, swift and kotlin to facilitate data exchange. @@ -110,6 +120,7 @@ class Event { calendarId = json['calendarId']; title = json['eventTitle']; description = json['eventDescription']; + _color = json['eventColor']; startTimestamp = json['eventStartDate']; startLocationName = json['eventStartTimeZone']; @@ -237,6 +248,8 @@ class Event { data['eventURL'] = url?.data?.contentText; data['availability'] = availability.enumToString; data['eventStatus'] = status?.enumToString; + data['eventColor'] = color; + data['eventColorKey'] = _colorKey; if (attendees != null) { data['attendees'] = attendees?.map((a) => a?.toJson()).toList(); @@ -310,4 +323,9 @@ class Event { return false; } } -} + + void updateEventColor(EventColor eventColor) { + _color = eventColor.color; + _colorKey = eventColor.colorKey; + } +} \ No newline at end of file diff --git a/lib/src/models/event_color.dart b/lib/src/models/event_color.dart new file mode 100644 index 00000000..0851bf23 --- /dev/null +++ b/lib/src/models/event_color.dart @@ -0,0 +1,9 @@ +class EventColor { + final int color; + final int colorKey; + + EventColor(this.color, this.colorKey); + + @override + String toString() => 'EventColor(color: $color, colorKey: $colorKey)'; +} \ No newline at end of file From 7dce8d1a88eedfccd4f927dab3486596170bc894 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Sun, 14 Jul 2024 21:08:54 +0200 Subject: [PATCH 03/20] added removing of event color --- .../presentation/pages/calendar_event.dart | 22 +++++++++++++------ lib/src/models/event.dart | 6 ++--- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/example/lib/presentation/pages/calendar_event.dart b/example/lib/presentation/pages/calendar_event.dart index 47bd5a5c..82af5695 100644 --- a/example/lib/presentation/pages/calendar_event.dart +++ b/example/lib/presentation/pages/calendar_event.dart @@ -302,11 +302,10 @@ class _CalendarEventPageState extends State { final colors = _eventColors; if (colors != null) { final newColor = await selectColorDialog(colors); - if (newColor != null) { - setState(() { - _event?.updateEventColor(newColor); - }); - }} + setState(() { + _event?.updateEventColor(newColor); + }); + } }, ), SwitchListTile( @@ -1288,10 +1287,19 @@ class _CalendarEventPageState extends State { Future selectColorDialog(List colors) async { return await showDialog( context: context, + barrierDismissible: false, builder: (BuildContext context) { return SimpleDialog( title: const Text('Select Event color'), - children: colors.map((color) => + children: [ + SimpleDialogOption( + onPressed: () { Navigator.pop(context, null); }, + child: const Padding( + padding: EdgeInsets.all(16.0), + child: Text('Reset', textAlign: TextAlign.center,), + ), + ), + ...colors.map((color) => SimpleDialogOption( onPressed: () { Navigator.pop(context, color); }, child: Container( @@ -1302,7 +1310,7 @@ class _CalendarEventPageState extends State { color: Color(color.color)), ), ) - ).toList() + )] ); } ); diff --git a/lib/src/models/event.dart b/lib/src/models/event.dart index 12e359d8..2c20e48f 100644 --- a/lib/src/models/event.dart +++ b/lib/src/models/event.dart @@ -324,8 +324,8 @@ class Event { } } - void updateEventColor(EventColor eventColor) { - _color = eventColor.color; - _colorKey = eventColor.colorKey; + void updateEventColor(EventColor? eventColor) { + _color = eventColor?.color; + _colorKey = eventColor?.colorKey; } } \ No newline at end of file From a669955e3792581aa94322d4d3bf71a872684069 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Mon, 22 Jul 2024 23:39:15 +0200 Subject: [PATCH 04/20] added updating of calendar color for android and ios --- .../devicecalendar/CalendarDelegate.kt | 23 ++++- .../devicecalendar/DeviceCalendarPlugin.kt | 24 ++++- .../presentation/pages/calendar_event.dart | 43 ++------- example/lib/presentation/pages/calendars.dart | 87 +++++++++++++++---- .../pages/color_picker_dialog.dart | 28 ++++++ ios/Classes/SwiftDeviceCalendarPlugin.swift | 30 +++++++ lib/device_calendar.dart | 1 + lib/src/common/channel_constants.dart | 3 + lib/src/device_calendar.dart | 76 +++++++++++++--- 9 files changed, 245 insertions(+), 70 deletions(-) create mode 100644 example/lib/presentation/pages/color_picker_dialog.dart diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt index 888c2bff..165c720a 100644 --- a/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt @@ -1134,7 +1134,7 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : * load available event colors for the given account name * unable to find official documentation, so logic is based on https://android.googlesource.com/platform/packages/apps/Calendar.git/+/refs/heads/pie-release/src/com/android/calendar/EventInfoFragment.java **/ - fun retrieveEventColors(accountName: String): List> { + private fun retrieveColors(accountName: String, colorType: Int): List> { val contentResolver: ContentResolver? = _context?.contentResolver val uri: Uri = Colors.CONTENT_URI val colors = mutableListOf() @@ -1147,7 +1147,8 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : // load only event colors for the given account name val selection = "${Colors.COLOR_TYPE} = ? AND ${Colors.ACCOUNT_NAME} = ?" - val selectionArgs = arrayOf(Colors.TYPE_EVENT.toString(), accountName) + val selectionArgs = arrayOf(colorType.toString(), accountName) + val cursor: Cursor? = contentResolver?.query(uri, projection, selection, selectionArgs, null) cursor?.use { @@ -1164,6 +1165,24 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : return colors.map { Pair(it, displayColorKeyMap[it]!! ) }.toList() } + fun retrieveEventColors(accountName: String): List> { + return retrieveColors(accountName, Colors.TYPE_EVENT) + } + fun retrieveCalendarColors(accountName: String): List> { + return retrieveColors(accountName, Colors.TYPE_CALENDAR) + } + + fun updateCalendarColor(calendarId: Long, newColorKey: Int?, newColor: Int?): Boolean { + val contentResolver: ContentResolver? = _context?.contentResolver + val uri: Uri = ContentUris.withAppendedId(CalendarContract.Calendars.CONTENT_URI, calendarId) + val values = ContentValues().apply { + put(CalendarContract.Calendars.CALENDAR_COLOR_KEY, newColorKey) + put(CalendarContract.Calendars.CALENDAR_COLOR, newColor) + } + val rows = contentResolver?.update(uri, values, null, null) + return (rows ?: 0) > 0 + } + /** * Compares colors based on their hue values in the HSV color space. * https://android.googlesource.com/platform/prebuilts/fullsdk/sources/+/refs/heads/androidx-compose-integration-release/android-34/com/android/colorpicker/HsvColorComparator.java diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt index 2af342bd..bde9ce5a 100644 --- a/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt @@ -26,6 +26,8 @@ private const val CREATE_OR_UPDATE_EVENT_METHOD = "createOrUpdateEvent" private const val CREATE_CALENDAR_METHOD = "createCalendar" private const val DELETE_CALENDAR_METHOD = "deleteCalendar" private const val RETRIEVE_EVENT_COLORS_METHOD = "retrieveEventColors" +private const val RETRIEVE_CALENDAR_COLORS_METHOD = "retrieveCalendarColors" +private const val UPDATE_CALENDAR_COLOR = "updateCalendarColor" // Method arguments private const val CALENDAR_ID_ARGUMENT = "calendarId" @@ -69,6 +71,7 @@ private const val EVENT_AVAILABILITY_ARGUMENT = "availability" private const val ATTENDANCE_STATUS_ARGUMENT = "attendanceStatus" private const val EVENT_STATUS_ARGUMENT = "eventStatus" private const val EVENT_COLOR_KEY_ARGUMENT = "eventColorKey" +private const val CALENDAR_COLOR_KEY_ARGUMENT = "calendarColorKey" class DeviceCalendarPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { @@ -176,9 +179,28 @@ class DeviceCalendarPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } RETRIEVE_EVENT_COLORS_METHOD -> { val accountName = call.argument(CALENDAR_ACCOUNT_NAME_ARGUMENT) - val colors = _calendarDelegate.retrieveEventColors(accountName!!) + val colors = _calendarDelegate.retrieveEventColors(accountName!!, ) result.success(colors.map { listOf(it.first, it.second) }) } + RETRIEVE_CALENDAR_COLORS_METHOD -> { + val accountName = call.argument(CALENDAR_ACCOUNT_NAME_ARGUMENT) + if (accountName == null) { + return []; + } + val colors = _calendarDelegate.retrieveCalendarColors(accountName) + result.success(colors.map { listOf(it.first, it.second) }) + } + UPDATE_CALENDAR_COLOR -> { + val calendarId = call.argument(CALENDAR_ID_ARGUMENT)?.toLong() + if (calendarId == null) { + result.success(false) + return + } + val newColorKey = (call.argument(CALENDAR_COLOR_KEY_ARGUMENT))?.toInt() + val newColor = (call.argument(CALENDAR_COLOR_ARGUMENT))?.toInt() + val success = _calendarDelegate.updateCalendarColor(calendarId, newColorKey, newColor) + result.success(success) + } else -> { result.notImplemented() } diff --git a/example/lib/presentation/pages/calendar_event.dart b/example/lib/presentation/pages/calendar_event.dart index 82af5695..523024e7 100644 --- a/example/lib/presentation/pages/calendar_event.dart +++ b/example/lib/presentation/pages/calendar_event.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:device_calendar/device_calendar.dart'; +import 'package:device_calendar_example/presentation/pages/color_picker_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_native_timezone/flutter_native_timezone.dart'; @@ -299,13 +300,13 @@ class _CalendarEventPageState extends State { color: Color(widget._event?.color ?? 0), )), onTap: () async { - final colors = _eventColors; - if (colors != null) { - final newColor = await selectColorDialog(colors); + if (_eventColors != null) { + final colors = _eventColors?.map((eventColor) => Color(eventColor.color)).toList(); + final newColor = await ColorPickerDialog.selectColorDialog(colors ?? [], context); setState(() { - _event?.updateEventColor(newColor); + _event?.updateEventColor(_eventColors?.firstWhereOrNull((eventColor) => eventColor.color == newColor?.value)); }); - } + } }, ), SwitchListTile( @@ -1283,36 +1284,4 @@ class _CalendarEventPageState extends State { void showInSnackBar(BuildContext context, String value) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(value))); } - - Future selectColorDialog(List colors) async { - return await showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return SimpleDialog( - title: const Text('Select Event color'), - children: [ - SimpleDialogOption( - onPressed: () { Navigator.pop(context, null); }, - child: const Padding( - padding: EdgeInsets.all(16.0), - child: Text('Reset', textAlign: TextAlign.center,), - ), - ), - ...colors.map((color) => - SimpleDialogOption( - onPressed: () { Navigator.pop(context, color); }, - child: Container( - width: 48, - height: 48, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Color(color.color)), - ), - ) - )] - ); - } - ); - } } diff --git a/example/lib/presentation/pages/calendars.dart b/example/lib/presentation/pages/calendars.dart index 389d9b02..3c8bf1f5 100644 --- a/example/lib/presentation/pages/calendars.dart +++ b/example/lib/presentation/pages/calendars.dart @@ -1,7 +1,11 @@ +import 'dart:io'; + import 'package:device_calendar/device_calendar.dart'; import 'package:device_calendar_example/presentation/pages/calendar_add.dart'; +import 'package:device_calendar_example/presentation/pages/color_picker_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:collection/collection.dart'; import 'calendar_events.dart'; @@ -17,6 +21,7 @@ class CalendarsPage extends StatefulWidget { class _CalendarsPageState extends State { late DeviceCalendarPlugin _deviceCalendarPlugin; List _calendars = []; + List get _writableCalendars => _calendars.where((c) => c.isReadOnly == false).toList(); @@ -46,7 +51,10 @@ class _CalendarsPageState extends State { padding: const EdgeInsets.all(10.0), child: Text( 'WARNING: some aspects of saving events are hardcoded in this example app. As such we recommend you do not modify existing events as this may result in loss of information', - style: Theme.of(context).textTheme.titleLarge, + style: Theme + .of(context) + .textTheme + .titleLarge, ), ), Expanded( @@ -55,15 +63,13 @@ class _CalendarsPageState extends State { itemCount: _calendars.length, itemBuilder: (BuildContext context, int index) { return GestureDetector( - key: Key(_calendars[index].isReadOnly == true - ? 'readOnlyCalendar${_readOnlyCalendars.indexWhere((c) => c.id == _calendars[index].id)}' - : 'writableCalendar${_writableCalendars.indexWhere((c) => c.id == _calendars[index].id)}'), + key: ValueKey(_calendars[index].color), onTap: () async { await Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { - return CalendarEventsPage(_calendars[index], - key: const Key('calendarEventsPage')); - })); + return CalendarEventsPage(_calendars[index], + key: const Key('calendarEventsPage')); + })); }, child: Padding( padding: const EdgeInsets.all(10.0), @@ -75,21 +81,64 @@ class _CalendarsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "${_calendars[index].id}: ${_calendars[index].name!}", + "${_calendars[index] + .id}: ${_calendars[index].name!}", style: - Theme.of(context).textTheme.titleSmall, + Theme + .of(context) + .textTheme + .titleSmall, ), Text( - "Account: ${_calendars[index].accountName!}"), + "Account: ${_calendars[index] + .accountName!}"), Text( "type: ${_calendars[index].accountType}"), ])), - Container( - width: 15, - height: 15, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Color(_calendars[index].color!)), + GestureDetector( + onTap: () async { + final calendar = _calendars[index]; + final googleCalendarColors = await _deviceCalendarPlugin + .retrieveCalendarColors(_calendars[index]); + final colors = googleCalendarColors.isNotEmpty + ? googleCalendarColors.map((calendarColor) => + Color(calendarColor.color)).toList() + : [ + Colors.red, + Colors.green, + Colors.blue, + Colors.yellow, + Colors.orange, + Colors.purple, + Colors.cyan, + Colors.pink, + Colors.brown, + Colors.grey, + ]; + final color = await ColorPickerDialog + .selectColorDialog(colors, context); + if (color != null) { + final success = await _deviceCalendarPlugin + .updateCalendarColor(calendar, + calendarColor: googleCalendarColors + .firstWhereOrNull((calendarColor) => + calendarColor.color == color.value), + color: color); + if (success) { + _retrieveCalendars(); + } + } + }, + child: Container( + key: ValueKey(_calendars[index].color), + margin: const EdgeInsets.symmetric( + horizontal: 5, vertical: 10), + width: 20, + height: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Color(_calendars[index].color!)), + ), ), const SizedBox(width: 10), if (_calendars[index].isDefault!) @@ -116,8 +165,8 @@ class _CalendarsPageState extends State { onPressed: () async { final createCalendar = await Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { - return const CalendarAddPage(); - })); + return const CalendarAddPage(); + })); if (createCalendar == true) { _retrieveCalendars(); @@ -158,4 +207,4 @@ class _CalendarsPageState extends State { _retrieveCalendars(); }); } -} +} \ No newline at end of file diff --git a/example/lib/presentation/pages/color_picker_dialog.dart b/example/lib/presentation/pages/color_picker_dialog.dart new file mode 100644 index 00000000..04d7fc7d --- /dev/null +++ b/example/lib/presentation/pages/color_picker_dialog.dart @@ -0,0 +1,28 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class ColorPickerDialog { + static Future selectColorDialog(List colors, BuildContext context) async { + return await showDialog( + context: context, + builder: (BuildContext context) { + return SimpleDialog( + title: const Text('Select color'), + children: [ + ...colors.map((color) => + SimpleDialogOption( + onPressed: () { Navigator.pop(context, color); }, + child: Container( + width: 48, + height: 48, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color), + ), + ) + )] + ); + } + ); + } +} \ No newline at end of file diff --git a/ios/Classes/SwiftDeviceCalendarPlugin.swift b/ios/Classes/SwiftDeviceCalendarPlugin.swift index f37d1a5a..f1d519a7 100644 --- a/ios/Classes/SwiftDeviceCalendarPlugin.swift +++ b/ios/Classes/SwiftDeviceCalendarPlugin.swift @@ -114,6 +114,7 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin, EKEventViewDele let deleteEventMethod = "deleteEvent" let deleteEventInstanceMethod = "deleteEventInstance" let showEventModalMethod = "showiOSEventModal" + let updateCalendarColor = "updateCalendarColor" let calendarIdArgument = "calendarId" let startDateArgument = "startDate" let endDateArgument = "endDate" @@ -185,6 +186,8 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin, EKEventViewDele case showEventModalMethod: self.flutterResult = result showEventModal(call, result) + case updateCalendarColor: + updateCalendarColor(call, result) default: result(FlutterMethodNotImplemented) } @@ -245,6 +248,33 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin, EKEventViewDele } } + + + private func createCalendar(_ call: FlutterMethodCall, _ result: FlutterResult) { + let arguments = call.arguments as! Dictionary + let arguments = call.arguments as! Dictionary + let calendarId = arguments[calendarIdArgument] as! String + let color = arguments[calendarColorArgument] as! Int + + guard let calendar = eventStore.calendar(withIdentifier: calendarIdentifier) else { + print("Calendar not found") + result(false) + return + } + + // Update the calendar color + calendar.cgColor = UIColorFromRGB(color ?? 0)?.cgColor + + // Save the changes + do { + try eventStore.saveCalendar(calendar, commit: true) + result(false) + } catch { + result(FlutterError(code: self.genericError, message: error.localizedDescription, details: nil)) + } + } + } + private func retrieveCalendars(_ result: @escaping FlutterResult) { checkPermissionsThenExecute(permissionsGrantedAction: { let ekCalendars = self.eventStore.calendars(for: .event) diff --git a/lib/device_calendar.dart b/lib/device_calendar.dart index b3a7c906..ab9f78d6 100644 --- a/lib/device_calendar.dart +++ b/lib/device_calendar.dart @@ -7,6 +7,7 @@ export 'src/models/result.dart'; export 'src/models/reminder.dart'; export 'src/models/event.dart'; export 'src/models/event_color.dart'; +export 'src/models/calendar_color.dart'; export 'src/models/retrieve_events_params.dart'; export 'package:rrule/rrule.dart'; export 'package:rrule/src/frequency.dart'; diff --git a/lib/src/common/channel_constants.dart b/lib/src/common/channel_constants.dart index 4c1890d5..b56f8adf 100644 --- a/lib/src/common/channel_constants.dart +++ b/lib/src/common/channel_constants.dart @@ -12,6 +12,8 @@ class ChannelConstants { static const String methodNameDeleteCalendar = 'deleteCalendar'; static const String methodNameShowiOSEventModal = 'showiOSEventModal'; static const String methodNameRetrieveEventColors = 'retrieveEventColors'; + static const String methodNameRetrieveCalendarColors = 'retrieveCalendarColors'; + static const String methodNameUpdateCalendarColor = 'updateCalendarColor'; static const String parameterNameCalendarId = 'calendarId'; static const String parameterNameStartDate = 'startDate'; @@ -23,6 +25,7 @@ class ChannelConstants { static const String parameterNameFollowingInstances = 'followingInstances'; static const String parameterNameCalendarName = 'calendarName'; static const String parameterNameCalendarColor = 'calendarColor'; + static const String parameterNameCalendarColorKey = 'calendarColorKey'; static const String parameterNameLocalAccountName = 'localAccountName'; static const String parameterAccountName = "accountName"; } diff --git a/lib/src/device_calendar.dart b/lib/src/device_calendar.dart index b48d8971..6fc778e9 100644 --- a/lib/src/device_calendar.dart +++ b/lib/src/device_calendar.dart @@ -2,19 +2,14 @@ import 'dart:collection'; import 'dart:convert'; import 'dart:io'; -import 'package:device_calendar/src/models/event_color.dart'; +import 'package:device_calendar/device_calendar.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:timezone/data/latest.dart' as tz; -import 'package:timezone/timezone.dart'; import 'common/channel_constants.dart'; import 'common/error_codes.dart'; import 'common/error_messages.dart'; -import 'models/calendar.dart'; -import 'models/event.dart'; -import 'models/result.dart'; -import 'models/retrieve_events_params.dart'; /// Provides functionality for working with device calendar(s) class DeviceCalendarPlugin { @@ -343,7 +338,7 @@ class DeviceCalendarPlugin { ); } - Future?> retrieveEventColors(Calendar calendar) async { + Future?> retrieveEventColors(Calendar calendar) async { if (!Platform.isAndroid) { return null; } @@ -351,11 +346,70 @@ class DeviceCalendarPlugin { if (accountName == null) { return []; } - final dynamic colors = await _invokeChannelMethod(ChannelConstants.methodNameRetrieveEventColors, + final dynamic colors = await _invokeChannelMethod( + ChannelConstants.methodNameRetrieveEventColors, arguments: () => { - ChannelConstants.parameterAccountName: accountName, - },); - return (colors.data as List).cast().map((color) => EventColor(color[0], color[1])).toList(); + ChannelConstants.parameterAccountName: accountName, + }, + ); + return (colors.data as List) + .cast() + .map((color) => EventColor(color[0], color[1])) + .toList(); + } + + /// Retrieves available colors for Google Calendars. + /// + /// For non-Google calendars, an empty list is returned. Use the `color` parameter in [updateCalendarColor] for these. + /// + /// [calendar] The calendar to retrieve colors for. + /// + /// Returns a List with available colors for Google Calendars or an empty list for others. + Future> retrieveCalendarColors(Calendar calendar) async { + if (!Platform.isAndroid) { + return []; + } + final accountName = calendar.accountName; + if (accountName == null) { + return []; + } + final dynamic colors = await _invokeChannelMethod( + ChannelConstants.methodNameRetrieveCalendarColors, + arguments: () => { + ChannelConstants.parameterAccountName: accountName, + }, + ); + return (colors.data as List) + .cast() + .map((color) => CalendarColor(color[0], color[1])) + .toList(); + } + + /// Updates the color of a calendar using Google Calendar colors or platform-specific colors. + /// [calendar] The calendar to update. Must have a non-null `id`. + /// [calendarColor] Required for Google Calendars where [retrieveCalendarColors] is not empty. + /// [color] Required for locale or iOS Calendars where [retrieveCalendarColors] is empty. + /// + /// Returns `true` if the update was successful, otherwise `false`. + Future updateCalendarColor(Calendar calendar, + {CalendarColor? calendarColor, Color? color}) async { + final calendarId = calendar.id; + if (calendarId == null || color == null && calendarColor == null) { + return false; + } + final result = await _invokeChannelMethod( + ChannelConstants.methodNameUpdateCalendarColor, + arguments: () => { + ChannelConstants.parameterNameCalendarId: Platform.isAndroid ? int.tryParse(calendarId) : calendarId, + ChannelConstants.parameterNameCalendarColorKey: calendarColor?.colorKey, + ChannelConstants.parameterNameCalendarColor: color?.value, + }, + ); + final success = (result.data as bool?) ?? false; + if (success) { + calendar.color = color?.value ?? calendarColor?.color; + } + return success; } Future> _invokeChannelMethod( From f44704179a52358cee7fd315e602a0e477d76e19 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Tue, 23 Jul 2024 00:10:02 +0200 Subject: [PATCH 05/20] fixes for iOS updateCalendarcolor --- ios/Classes/SwiftDeviceCalendarPlugin.swift | 25 ++++++++++++--------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/ios/Classes/SwiftDeviceCalendarPlugin.swift b/ios/Classes/SwiftDeviceCalendarPlugin.swift index f1d519a7..323c8f5a 100644 --- a/ios/Classes/SwiftDeviceCalendarPlugin.swift +++ b/ios/Classes/SwiftDeviceCalendarPlugin.swift @@ -248,31 +248,36 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin, EKEventViewDele } } - - - private func createCalendar(_ call: FlutterMethodCall, _ result: FlutterResult) { - let arguments = call.arguments as! Dictionary + private func updateCalendarColor(_ call: FlutterMethodCall, _ result: FlutterResult) { let arguments = call.arguments as! Dictionary let calendarId = arguments[calendarIdArgument] as! String let color = arguments[calendarColorArgument] as! Int - - guard let calendar = eventStore.calendar(withIdentifier: calendarIdentifier) else { + + guard let calendar = eventStore.calendar(withIdentifier: calendarId) else { print("Calendar not found") result(false) return } - + // Update the calendar color - calendar.cgColor = UIColorFromRGB(color ?? 0)?.cgColor - + calendar.cgColor = UIColorFromRGB(color).cgColor + // Save the changes do { try eventStore.saveCalendar(calendar, commit: true) - result(false) + result(true) // Assuming the operation was successful, return true } catch { result(FlutterError(code: self.genericError, message: error.localizedDescription, details: nil)) } } + + func UIColorFromRGB(_ rgbValue: Int) -> UIColor { + return UIColor( + red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, + green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(rgbValue & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) } private func retrieveCalendars(_ result: @escaping FlutterResult) { From c333f314b77353b923e45628d42272dd4db6c7df Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Tue, 23 Jul 2024 14:38:53 +0200 Subject: [PATCH 06/20] small fix --- .../com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt index bde9ce5a..a5d7df80 100644 --- a/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt @@ -179,13 +179,18 @@ class DeviceCalendarPlugin : FlutterPlugin, MethodCallHandler, ActivityAware { } RETRIEVE_EVENT_COLORS_METHOD -> { val accountName = call.argument(CALENDAR_ACCOUNT_NAME_ARGUMENT) + if (accountName == null) { + result.success(intArrayOf()) + return; + } val colors = _calendarDelegate.retrieveEventColors(accountName!!, ) result.success(colors.map { listOf(it.first, it.second) }) } RETRIEVE_CALENDAR_COLORS_METHOD -> { val accountName = call.argument(CALENDAR_ACCOUNT_NAME_ARGUMENT) if (accountName == null) { - return []; + result.success(intArrayOf()) + return; } val colors = _calendarDelegate.retrieveCalendarColors(accountName) result.success(colors.map { listOf(it.first, it.second) }) From cd0c652380fcc5767ca5c5acdfe371479ed3b464 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Tue, 23 Jul 2024 21:56:10 +0200 Subject: [PATCH 07/20] added calendar color file --- lib/src/models/calendar_color.dart | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 lib/src/models/calendar_color.dart diff --git a/lib/src/models/calendar_color.dart b/lib/src/models/calendar_color.dart new file mode 100644 index 00000000..e63e0be1 --- /dev/null +++ b/lib/src/models/calendar_color.dart @@ -0,0 +1,9 @@ +class CalendarColor { + final int color; + final int colorKey; + + CalendarColor(this.color, this.colorKey); + + @override + String toString() => 'CalendarColor(color: $color, colorKey: $colorKey)'; +} \ No newline at end of file From 75ee949f416aaaa9fafdfd976b66d069a8828311 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Mon, 12 Aug 2024 12:09:10 +0200 Subject: [PATCH 08/20] made colorKey accessible in event --- lib/src/models/event.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/src/models/event.dart b/lib/src/models/event.dart index 2c20e48f..1c5fe91f 100644 --- a/lib/src/models/event.dart +++ b/lib/src/models/event.dart @@ -53,10 +53,13 @@ class Event { /// Read-only. Color of the event int? get color=> _color; - /// The color of this event + /// Read-only. Color of the event + int? get colorKey=> _colorKey; + + /// Only updatable for Android calendars where [DeviceCalendarPlugin.retrieveEventColors] returns an empty list. int? _color; - /// The color key of this event. This is needed to change the event color + /// Only updatable for colors of [DeviceCalendarPlugin.retrieveEventColors]. int? _colorKey; ///Note for development: From 72b58f27bf0af6975166381efa467ef8a50ad412 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Mon, 12 Aug 2024 14:20:55 +0200 Subject: [PATCH 09/20] set eventcolor, loaded eventColorKey for event --- .../kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt | 3 +++ .../kotlin/com/builttoroam/devicecalendar/common/Constants.kt | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt index 165c720a..dd73716f 100644 --- a/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt @@ -628,6 +628,7 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : values.put(Events.EVENT_END_TIMEZONE, endTimeZone) values.put(Events.DURATION, duration) values.put(Events.EVENT_COLOR_KEY, event.eventColorKey) + values.put(Events.EVENT_COLOR, event.eventColor) return values } @@ -942,6 +943,7 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : val availability = parseAvailability(cursor.getInt(Cst.EVENT_PROJECTION_AVAILABILITY_INDEX)) val eventStatus = parseEventStatus(cursor.getInt(Cst.EVENT_PROJECTION_STATUS_INDEX)) val eventColor = cursor.getInt(Cst.EVENT_PROJECTION_EVENT_COLOR_INDEX) + val eventColorKey = cursor.getInt(Cst.EVENT_PROJECTION_EVENT_COLOR_KEY_INDEX) val event = Event() event.eventTitle = title ?: "New Event" event.eventId = eventId.toString() @@ -958,6 +960,7 @@ class CalendarDelegate(binding: ActivityPluginBinding?, context: Context) : event.availability = availability event.eventStatus = eventStatus event.eventColor = if (eventColor == 0) null else eventColor + event.eventColorKey = if (eventColorKey == 0) null else eventColorKey return event } diff --git a/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt index fc49f227..f02eebd2 100644 --- a/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt @@ -51,6 +51,7 @@ class Constants { const val EVENT_PROJECTION_AVAILABILITY_INDEX: Int = 13 const val EVENT_PROJECTION_STATUS_INDEX: Int = 14 const val EVENT_PROJECTION_EVENT_COLOR_INDEX: Int = 15 + const val EVENT_PROJECTION_EVENT_COLOR_KEY_INDEX: Int = 16 val EVENT_PROJECTION: Array = arrayOf( CalendarContract.Instances.EVENT_ID, @@ -68,7 +69,8 @@ class Constants { CalendarContract.Events.EVENT_END_TIMEZONE, CalendarContract.Events.AVAILABILITY, CalendarContract.Events.STATUS, - CalendarContract.Events.EVENT_COLOR + CalendarContract.Events.EVENT_COLOR, + CalendarContract.Events.EVENT_COLOR_KEY ) const val EVENT_INSTANCE_DELETION_ID_INDEX: Int = 0 From e6e88e867444f568071900671355be10dd67e732 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Mon, 12 Aug 2024 18:12:35 +0200 Subject: [PATCH 10/20] mvoed color picker file --- .../lib/presentation/color_picker_dialog.dart | 28 +++++++++++++++++++ example/lib/presentation/pages/calendars.dart | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 example/lib/presentation/color_picker_dialog.dart diff --git a/example/lib/presentation/color_picker_dialog.dart b/example/lib/presentation/color_picker_dialog.dart new file mode 100644 index 00000000..04d7fc7d --- /dev/null +++ b/example/lib/presentation/color_picker_dialog.dart @@ -0,0 +1,28 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class ColorPickerDialog { + static Future selectColorDialog(List colors, BuildContext context) async { + return await showDialog( + context: context, + builder: (BuildContext context) { + return SimpleDialog( + title: const Text('Select color'), + children: [ + ...colors.map((color) => + SimpleDialogOption( + onPressed: () { Navigator.pop(context, color); }, + child: Container( + width: 48, + height: 48, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: color), + ), + ) + )] + ); + } + ); + } +} \ No newline at end of file diff --git a/example/lib/presentation/pages/calendars.dart b/example/lib/presentation/pages/calendars.dart index 3c8bf1f5..5dc921bc 100644 --- a/example/lib/presentation/pages/calendars.dart +++ b/example/lib/presentation/pages/calendars.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:device_calendar/device_calendar.dart'; import 'package:device_calendar_example/presentation/pages/calendar_add.dart'; -import 'package:device_calendar_example/presentation/pages/color_picker_dialog.dart'; +import 'package:device_calendar_example/presentation/color_picker_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:collection/collection.dart'; From 35c49e62dbcc1a89d46966029c3e62c47cd5cbe4 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Mon, 12 Aug 2024 18:12:50 +0200 Subject: [PATCH 11/20] removed old color picker --- .../pages/color_picker_dialog.dart | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 example/lib/presentation/pages/color_picker_dialog.dart diff --git a/example/lib/presentation/pages/color_picker_dialog.dart b/example/lib/presentation/pages/color_picker_dialog.dart deleted file mode 100644 index 04d7fc7d..00000000 --- a/example/lib/presentation/pages/color_picker_dialog.dart +++ /dev/null @@ -1,28 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class ColorPickerDialog { - static Future selectColorDialog(List colors, BuildContext context) async { - return await showDialog( - context: context, - builder: (BuildContext context) { - return SimpleDialog( - title: const Text('Select color'), - children: [ - ...colors.map((color) => - SimpleDialogOption( - onPressed: () { Navigator.pop(context, color); }, - child: Container( - width: 48, - height: 48, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: color), - ), - ) - )] - ); - } - ); - } -} \ No newline at end of file From 992240b2228f437b82688c257df3724a840f5564 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Wed, 4 Sep 2024 22:22:14 +0200 Subject: [PATCH 12/20] exposed color and colorKey --- lib/src/models/event.dart | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/src/models/event.dart b/lib/src/models/event.dart index 1c5fe91f..8b170910 100644 --- a/lib/src/models/event.dart +++ b/lib/src/models/event.dart @@ -51,16 +51,13 @@ class Event { EventStatus? status; /// Read-only. Color of the event - int? get color=> _color; - /// Read-only. Color of the event - int? get colorKey=> _colorKey; /// Only updatable for Android calendars where [DeviceCalendarPlugin.retrieveEventColors] returns an empty list. - int? _color; + int? color; /// Only updatable for colors of [DeviceCalendarPlugin.retrieveEventColors]. - int? _colorKey; + int? colorKey; ///Note for development: /// @@ -123,7 +120,7 @@ class Event { calendarId = json['calendarId']; title = json['eventTitle']; description = json['eventDescription']; - _color = json['eventColor']; + color = json['eventColor']; startTimestamp = json['eventStartDate']; startLocationName = json['eventStartTimeZone']; @@ -252,7 +249,7 @@ class Event { data['availability'] = availability.enumToString; data['eventStatus'] = status?.enumToString; data['eventColor'] = color; - data['eventColorKey'] = _colorKey; + data['eventColorKey'] = colorKey; if (attendees != null) { data['attendees'] = attendees?.map((a) => a?.toJson()).toList(); @@ -328,7 +325,7 @@ class Event { } void updateEventColor(EventColor? eventColor) { - _color = eventColor?.color; - _colorKey = eventColor?.colorKey; + color = eventColor?.color; + colorKey = eventColor?.colorKey; } } \ No newline at end of file From 44c248c9f083fa91cf2341549107fbce7570a0d7 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Wed, 11 Sep 2024 13:25:11 +0200 Subject: [PATCH 13/20] fixed color picker import --- example/lib/presentation/pages/calendar_event.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/lib/presentation/pages/calendar_event.dart b/example/lib/presentation/pages/calendar_event.dart index 523024e7..a447d33c 100644 --- a/example/lib/presentation/pages/calendar_event.dart +++ b/example/lib/presentation/pages/calendar_event.dart @@ -2,12 +2,12 @@ import 'dart:io'; import 'package:collection/collection.dart'; import 'package:device_calendar/device_calendar.dart'; -import 'package:device_calendar_example/presentation/pages/color_picker_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_native_timezone/flutter_native_timezone.dart'; import 'package:intl/intl.dart'; +import '../color_picker_dialog.dart'; import '../date_time_picker.dart'; import '../recurring_event_dialog.dart'; import 'event_attendee.dart'; From ba0910b43a1271fe17ce118d6e8f92ed599047ea Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Wed, 11 Sep 2024 17:05:56 +0200 Subject: [PATCH 14/20] fixes for setting event color --- example/lib/presentation/pages/calendar_event.dart | 2 +- example/lib/presentation/pages/calendar_events.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/example/lib/presentation/pages/calendar_event.dart b/example/lib/presentation/pages/calendar_event.dart index a447d33c..a4bc270b 100644 --- a/example/lib/presentation/pages/calendar_event.dart +++ b/example/lib/presentation/pages/calendar_event.dart @@ -304,7 +304,7 @@ class _CalendarEventPageState extends State { final colors = _eventColors?.map((eventColor) => Color(eventColor.color)).toList(); final newColor = await ColorPickerDialog.selectColorDialog(colors ?? [], context); setState(() { - _event?.updateEventColor(_eventColors?.firstWhereOrNull((eventColor) => eventColor.color == newColor?.value)); + _event?.updateEventColor(_eventColors?.firstWhereOrNull((eventColor) => Color(eventColor.color).value == newColor?.value)); }); } }, diff --git a/example/lib/presentation/pages/calendar_events.dart b/example/lib/presentation/pages/calendar_events.dart index 781b39b4..6b2e8384 100644 --- a/example/lib/presentation/pages/calendar_events.dart +++ b/example/lib/presentation/pages/calendar_events.dart @@ -79,7 +79,7 @@ class _CalendarEventsPageState extends State { onPressed: () async { final refreshEvents = await Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { - return CalendarEventPage(_calendar); + return CalendarEventPage(_calendar, null, null, _eventColors); })); if (refreshEvents == true) { await _retrieveCalendarEvents(); From c702f17b663c963e6682d5d99246fe72fc82dad2 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Wed, 11 Sep 2024 17:18:44 +0200 Subject: [PATCH 15/20] clean up --- example/lib/presentation/pages/calendars.dart | 4 +++- lib/src/models/event.dart | 7 ++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/example/lib/presentation/pages/calendars.dart b/example/lib/presentation/pages/calendars.dart index 5dc921bc..bc434173 100644 --- a/example/lib/presentation/pages/calendars.dart +++ b/example/lib/presentation/pages/calendars.dart @@ -63,7 +63,9 @@ class _CalendarsPageState extends State { itemCount: _calendars.length, itemBuilder: (BuildContext context, int index) { return GestureDetector( - key: ValueKey(_calendars[index].color), + key: Key(_calendars[index].isReadOnly == true + ? 'readOnlyCalendar${_readOnlyCalendars.indexWhere((c) => c.id == _calendars[index].id)} color:${_calendars[index].color}' + : 'writableCalendar${_writableCalendars.indexWhere((c) => c.id == _calendars[index].id)} color:${_calendars[index].color}'), onTap: () async { await Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { diff --git a/lib/src/models/event.dart b/lib/src/models/event.dart index 8b170910..d106eac1 100644 --- a/lib/src/models/event.dart +++ b/lib/src/models/event.dart @@ -50,13 +50,10 @@ class Event { /// Indicates if this event is of confirmed, canceled, tentative or none status EventStatus? status; - /// Read-only. Color of the event - - - /// Only updatable for Android calendars where [DeviceCalendarPlugin.retrieveEventColors] returns an empty list. + /// Read-only. Android exclusive. Updatable only using [Event.updateEventColor] with color from [DeviceCalendarPlugin.retrieveEventColors] int? color; - /// Only updatable for colors of [DeviceCalendarPlugin.retrieveEventColors]. + /// Read-only. Android exclusive. Updatable only using [Event.updateEventColor] with color from [DeviceCalendarPlugin.retrieveEventColors] int? colorKey; ///Note for development: From 2ee14a1035cc2e03a3478aa7f4e067fa531b54b5 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Wed, 11 Sep 2024 17:26:30 +0200 Subject: [PATCH 16/20] added serilization test for eventColor --- lib/src/models/event.dart | 1 + test/device_calendar_test.dart | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/src/models/event.dart b/lib/src/models/event.dart index d106eac1..eda68ffa 100644 --- a/lib/src/models/event.dart +++ b/lib/src/models/event.dart @@ -118,6 +118,7 @@ class Event { title = json['eventTitle']; description = json['eventDescription']; color = json['eventColor']; + colorKey = json['eventColorKey']; startTimestamp = json['eventStartDate']; startLocationName = json['eventStartTimeZone']; diff --git a/test/device_calendar_test.dart b/test/device_calendar_test.dart index 132aad61..a2f1f1ca 100644 --- a/test/device_calendar_test.dart +++ b/test/device_calendar_test.dart @@ -216,7 +216,9 @@ void main() { recurrenceRule: recurrence, reminders: [reminder], availability: Availability.Busy, - status: EventStatus.Confirmed); + status: EventStatus.Confirmed, + ); + event.updateEventColor(EventColor(0xffff00ff, 1)); final stringEvent = event.toJson(); expect(stringEvent, isNotNull); @@ -241,5 +243,7 @@ void main() { expect(newEvent.reminders?.length, equals(1)); expect(newEvent.availability, equals(event.availability)); expect(newEvent.status, equals(event.status)); + expect(newEvent.color, equals(event.color)); + expect(newEvent.colorKey, equals(event.colorKey)); }); } From b41223cf6737c179bc2ea39bc47a01dbbe419e54 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Wed, 11 Sep 2024 17:56:16 +0200 Subject: [PATCH 17/20] tiny formatting adjustment --- test/device_calendar_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/device_calendar_test.dart b/test/device_calendar_test.dart index a2f1f1ca..0d91e738 100644 --- a/test/device_calendar_test.dart +++ b/test/device_calendar_test.dart @@ -216,8 +216,7 @@ void main() { recurrenceRule: recurrence, reminders: [reminder], availability: Availability.Busy, - status: EventStatus.Confirmed, - ); + status: EventStatus.Confirmed); event.updateEventColor(EventColor(0xffff00ff, 1)); final stringEvent = event.toJson(); From 787696271ec29fc41a300bc0f5a88da00682be79 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Wed, 11 Sep 2024 21:02:45 +0200 Subject: [PATCH 18/20] set example kotlin version to 1.8..22 --- android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/build.gradle b/android/build.gradle index 34fcf1ae..73ccfc61 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,7 +2,7 @@ group 'com.builttoroam.devicecalendar' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.6.0' + ext.kotlin_version = '1.8.22' repositories { google() mavenCentral() From 755ddfa99832cf9bc722df3f472ec2438490c54a Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Fri, 13 Sep 2024 21:13:51 +0200 Subject: [PATCH 19/20] flutter_native_timezone made release builds fail, migrated to flutter_timezone --- example/lib/presentation/event_item.dart | 4 ++-- example/lib/presentation/pages/calendar_event.dart | 4 ++-- example/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/example/lib/presentation/event_item.dart b/example/lib/presentation/event_item.dart index f91bb7de..b18d299d 100644 --- a/example/lib/presentation/event_item.dart +++ b/example/lib/presentation/event_item.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:device_calendar/device_calendar.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_native_timezone/flutter_native_timezone.dart'; +import 'package:flutter_timezone/flutter_timezone.dart'; import 'package:intl/intl.dart'; import 'recurring_event_dialog.dart'; @@ -313,7 +313,7 @@ class _EventItemState extends State { void setCurentLocation() async { String? timezone; try { - timezone = await FlutterNativeTimezone.getLocalTimezone(); + timezone = await FlutterTimezone.getLocalTimezone(); } catch (e) { debugPrint('Could not get the local timezone'); } diff --git a/example/lib/presentation/pages/calendar_event.dart b/example/lib/presentation/pages/calendar_event.dart index a4bc270b..83ad1a23 100644 --- a/example/lib/presentation/pages/calendar_event.dart +++ b/example/lib/presentation/pages/calendar_event.dart @@ -4,7 +4,7 @@ import 'package:collection/collection.dart'; import 'package:device_calendar/device_calendar.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_native_timezone/flutter_native_timezone.dart'; +import 'package:flutter_timezone/flutter_timezone.dart'; import 'package:intl/intl.dart'; import '../color_picker_dialog.dart'; @@ -73,7 +73,7 @@ class _CalendarEventPageState extends State { void getCurentLocation() async { try { - _timezone = await FlutterNativeTimezone.getLocalTimezone(); + _timezone = await FlutterTimezone.getLocalTimezone(); } catch (e) { debugPrint('Could not get the local timezone'); } diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b32d2fb0..9ffb81f4 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter intl: ^0.17.0 uuid: ^3.0.6 - flutter_native_timezone: ^2.0.0 + flutter_timezone: ^3.0.1 device_calendar: path: ../ From 7791f596754213df3fa871a0c0316e139c78c305 Mon Sep 17 00:00:00 2001 From: Christopher Wolf <> Date: Wed, 25 Sep 2024 20:51:04 +0200 Subject: [PATCH 20/20] increased rrule version from 0.2.10 to 0.2.15 --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index 7c99a9c5..101681fb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ dependencies: sdk: flutter collection: ^1.16.0 timezone: ^0.9.0 - rrule: ^0.2.10 + rrule: ^0.2.15 dev_dependencies: flutter_test: