diff --git a/.gitignore b/.gitignore index dc649c50..8afe5ddc 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,6 @@ pubspec.lock # If you don't generate documentation locally you can remove this line. doc/api/ device_calendar.code-workspace -device_calendar/example/.flutter-plugins-dependencies -device_calendar/example/ios/Flutter/flutter_export_environment.sh -device_calendar/example/ios/Flutter/Flutter.podspec +example/.flutter-plugins-dependencies +example/ios/Flutter/flutter_export_environment.sh +example/ios/Flutter/Flutter.podspec diff --git a/device_calendar/CHANGELOG.md b/CHANGELOG.md similarity index 79% rename from device_calendar/CHANGELOG.md rename to CHANGELOG.md index b7a2d22a..4d78a113 100644 --- a/device_calendar/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,28 @@ # Changelog -## 3.1.0 6th March 2020 - Bug fixes and new features +## 3.2.0 -* Boolean variable `isDefault` added for issue [145](https://github.com/builttoroam/flutter_plugins/issues/145) (**NOTE**: This is not supported Android API 16 or lower, `isDefault` will always be false) -* Events with 'null' title now defaults to 'New Event', issue [126](https://github.com/builttoroam/flutter_plugins/issues/126) -* Updated property summaries for issues [121](https://github.com/builttoroam/flutter_plugins/issues/121) and [122](https://github.com/builttoroam/flutter_plugins/issues/122) -* Updated example documentation for issue [119](https://github.com/builttoroam/flutter_plugins/issues/119) +* Added time zone support +* Project hierarchy update and clean up + +## 3.1.0 25th March 2020 - Bug fixes and new features + +* Boolean variable `isDefault` added for issue [145](https://github.com/builttoroam/device_calendar/issues/145) (**NOTE**: This is not supported Android API 16 or lower, `isDefault` will always be false) +* Events with 'null' title now defaults to 'New Event', issue [126](https://github.com/builttoroam/device_calendar/issues/126) +* Updated property summaries for issues [121](https://github.com/builttoroam/device_calendar/issues/121) and [122](https://github.com/builttoroam/device_calendar/issues/122) +* Updated example documentation for issue [119](https://github.com/builttoroam/device_calendar/issues/119) * Read-only calendars cannot be edited or deleted for the example app * Added `DayOfWeekGroup` enum and an extension `getDays` to get corresponding dates of the enum values * Added to retrieve colour for calendars. Thanks to [nadavfima](https://github.com/nadavfima) for the contribution and PR to add colour support for both Android and iOS * Added compatibility with a new Flutter plugin for Android. Thanks to the PR submitted by [RohitKumarMishra](https://github.com/RohitKumarMishra) -* [Android] Fixed all day timezone issue [164](https://github.com/builttoroam/flutter_plugins/issues/164) -* Added support for deleting individual or multiple instances of a recurring event for issue [108](https://github.com/builttoroam/flutter_plugins/issues/108) -* Ability to add local calendars with a desired colour for issue [115](https://github.com/builttoroam/flutter_plugins/issues/115) -* Returns account name and type for each calendars for issue [179](https://github.com/builttoroam/flutter_plugins/issues/179) +* [Android] Fixed all day timezone issue [164](https://github.com/builttoroam/device_calendar/issues/164) +* Added support for deleting individual or multiple instances of a recurring event for issue [108](https://github.com/builttoroam/device_calendar/issues/108) +* Ability to add local calendars with a desired colour for issue [115](https://github.com/builttoroam/device_calendar/issues/115) +* Returns account name and type for each calendars for issue [179](https://github.com/builttoroam/device_calendar/issues/179) ## 3.0.0+3 3rd February 2020 -* Fixed all day conditional check for issue [162](https://github.com/builttoroam/flutter_plugins/issues/162) +* Fixed all day conditional check for issue [162](https://github.com/builttoroam/device_calendar/issues/162) ## 3.0.0+2 30th January 2020 @@ -25,7 +30,7 @@ ## 3.0.0+1 28th January 2020 -* Updated `event.url` property in `createOrUpdateEvent` method to be null-aware for issue [152](https://github.com/builttoroam/flutter_plugins/issues/152) +* Updated `event.url` property in `createOrUpdateEvent` method to be null-aware for issue [152](https://github.com/builttoroam/device_calendar/issues/152) ## 3.0.0 21st January 2020 @@ -50,7 +55,7 @@ ## 1.0.0+3 9th January 2020 * Flutter upgrade to 1.12.13 -* Added an URL input for calendar events for issue [132](https://github.com/builttoroam/flutter_plugins/issues/132) +* Added an URL input for calendar events for issue [132](https://github.com/builttoroam/device_calendar/issues/132) ## 1.0.0+2 30th August 2019 @@ -62,11 +67,11 @@ ## 1.0.0 28th August 2019 -* **BREAKING CHANGE** `retrieveCalendars` and `retrieveEvents` now return lists that cannot be modified (`UnmodifiableListView`) to address part of issue [113](https://github.com/builttoroam/flutter_plugins/issues/113) +* **BREAKING CHANGE** `retrieveCalendars` and `retrieveEvents` now return lists that cannot be modified (`UnmodifiableListView`) to address part of issue [113](https://github.com/builttoroam/device_calendar/issues/113) * Support for more advanced recurrence rules -* Update README to include information about using ProGuard for issue [99](https://github.com/builttoroam/flutter_plugins/issues/99) -* Made event title optional to fix issue [72](https://github.com/builttoroam/flutter_plugins/issues/72) -* Return information about the organiser of the event as per issue [73](https://github.com/builttoroam/flutter_plugins/issues/73) +* Update README to include information about using ProGuard for issue [99](https://github.com/builttoroam/device_calendar/issues/99) +* Made event title optional to fix issue [72](https://github.com/builttoroam/device_calendar/issues/72) +* Return information about the organiser of the event as per issue [73](https://github.com/builttoroam/device_calendar/issues/73) * Return attendance status of attendees and if they're required for an event. These are details are different across iOS and Android and so are returned within platform-specific objects * Ability to modify attendees for an event * Ability to create reminders for events expressed in minutes before the event starts @@ -81,7 +86,7 @@ ## 0.2.1 5th August 2019 -* [Android] Fixes issue [101](https://github.com/builttoroam/flutter_plugins/issues/101) where plugin results in a crash with headless execution +* [Android] Fixes issue [101](https://github.com/builttoroam/device_calendar/issues/101) where plugin results in a crash with headless execution ## 0.2.0 30th July 2019 @@ -91,7 +96,7 @@ ## 0.1.3 5th July 2019 -* [iOS] Fixes issue [94](https://github.com/builttoroam/flutter_plugins/issues/94) that occurred on 32-bit iOS devices around date of events. Thanks to the PR submitted by [duzenko](https://github.com/duzenko) +* [iOS] Fixes issue [94](https://github.com/builttoroam/device_calendar/issues/94) that occurred on 32-bit iOS devices around date of events. Thanks to the PR submitted by [duzenko](https://github.com/duzenko) ## 0.1.2+2 28th May 2019 @@ -104,11 +109,11 @@ ## 0.1.2 - 16th May 2019 -* [Android] An updated fix to address issue [79](https://github.com/builttoroam/flutter_plugins/issues/79), thanks to the PR submitted by [Gerry High](https://github.com/gerryhigh) +* [Android] An updated fix to address issue [79](https://github.com/builttoroam/device_calendar/issues/79), thanks to the PR submitted by [Gerry High](https://github.com/gerryhigh) ## 0.1.1 - 1st March 2019 -* Fixed issue [79](https://github.com/builttoroam/flutter_plugins/issues/79) where on Android, the plugin was indicating that it was handling permissions that it shouldn't have +* Fixed issue [79](https://github.com/builttoroam/device_calendar/issues/79) where on Android, the plugin was indicating that it was handling permissions that it shouldn't have ## 0.1.0 - 26th February 2019 @@ -122,7 +127,7 @@ ## 0.0.7 - 16th November 2018 -* Fixes issue [##67](https://github.com/builttoroam/flutter_plugins/issues/67) and [##68](https://github.com/builttoroam/flutter_plugins/issues/68). Thanks to PR submitted by huzhiren. +* Fixes issue [##67](https://github.com/builttoroam/device_calendar/issues/67) and [##68](https://github.com/builttoroam/device_calendar/issues/68). Thanks to PR submitted by huzhiren. ## 0.0.6 - 18th June 2018 diff --git a/device_calendar/LICENSE b/LICENSE similarity index 100% rename from device_calendar/LICENSE rename to LICENSE diff --git a/device_calendar/README.md b/README.md similarity index 82% rename from device_calendar/README.md rename to README.md index 6c50ab06..c89bac21 100644 --- a/device_calendar/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Device Calendar Plugin -[![pub package](https://img.shields.io/pub/v/device_calendar.svg)](https://pub.dartlang.org/packages/device_calendar) [![Build Status](https://dev.azure.com/builttoroam/Flutter%20Plugins/_apis/build/status/Device%20Calendar)](https://dev.azure.com/builttoroam/Flutter%20Plugins/_build/latest?definitionId=106) +[![pub package](https://img.shields.io/pub/v/device_calendar.svg)](https://pub.dartlang.org/packages/device_calendar) [![Build Status](https://dev.azure.com/builttoroam/Flutter%20Plugins/_apis/build/status/Device%20Calendar)](https://dev.azure.com/builttoroam/Flutter%20Plugins/_build/latest?definitionId=111) A cross platform plugin for modifying calendars on the user's device. @@ -16,6 +16,10 @@ A cross platform plugin for modifying calendars on the user's device. * **NOTE**: Deleting multiple instances in **Android** takes time to update, you'll see the changes after a few seconds * Ability to add, modify or remove attendees and receive if an attendee is an organiser for an event * Ability to setup reminders for an event +* Ability to specify a time zone for event start and end date + * **NOTE**: Due to a limitation of iOS API, single time zone property is used for iOS (`event.startTimeZone`) + * **NOTE**: For the time zone list, please refer to the `TZ database name` column on [Wikipedia](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) + * **NOTE**: If the time zone values are null or invalid, it will be defaulted to the device's current time zone. ## Android Integration @@ -26,7 +30,7 @@ The following will need to be added to the manifest file for your application to ``` -If you have Proguard enabled, you may need to add the following to your configuration (thanks to [Britannio Jarrett](https://github.com/britannio) who posted about it [here](https://github.com/builttoroam/flutter_plugins/issues/99)) +If you have Proguard enabled, you may need to add the following to your configuration (thanks to [Britannio Jarrett](https://github.com/britannio) who posted about it [here](https://github.com/builttoroam/device_calendar/issues/99)) ``` -keep class com.builttoroam.devicecalendar.** { *; } diff --git a/device_calendar/analysis_options.yaml b/analysis_options.yaml similarity index 100% rename from device_calendar/analysis_options.yaml rename to analysis_options.yaml diff --git a/device_calendar/android/.gitignore b/android/.gitignore similarity index 100% rename from device_calendar/android/.gitignore rename to android/.gitignore diff --git a/device_calendar/android/build.gradle b/android/build.gradle similarity index 93% rename from device_calendar/android/build.gradle rename to android/build.gradle index d258f881..f58ff0b2 100644 --- a/device_calendar/android/build.gradle +++ b/android/build.gradle @@ -44,4 +44,5 @@ dependencies { implementation 'com.google.code.gson:gson:2.8.5' api 'androidx.appcompat:appcompat:1.0.2' implementation 'org.dmfs:lib-recur:0.11.2' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5' } diff --git a/device_calendar/android/gradle.properties b/android/gradle.properties similarity index 100% rename from device_calendar/android/gradle.properties rename to android/gradle.properties diff --git a/device_calendar/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from device_calendar/android/gradle/wrapper/gradle-wrapper.jar rename to android/gradle/wrapper/gradle-wrapper.jar diff --git a/device_calendar/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from device_calendar/android/gradle/wrapper/gradle-wrapper.properties rename to android/gradle/wrapper/gradle-wrapper.properties diff --git a/device_calendar/android/gradlew b/android/gradlew similarity index 100% rename from device_calendar/android/gradlew rename to android/gradlew diff --git a/device_calendar/android/gradlew.bat b/android/gradlew.bat similarity index 100% rename from device_calendar/android/gradlew.bat rename to android/gradlew.bat diff --git a/device_calendar/android/settings.gradle b/android/settings.gradle similarity index 100% rename from device_calendar/android/settings.gradle rename to android/settings.gradle diff --git a/device_calendar/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml similarity index 100% rename from device_calendar/android/src/main/AndroidManifest.xml rename to android/src/main/AndroidManifest.xml diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt similarity index 89% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt index 59de2f69..328060e5 100644 --- a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/CalendarDelegate.kt @@ -1,8 +1,8 @@ package com.builttoroam.devicecalendar import android.Manifest +import android.graphics.Color; import android.annotation.SuppressLint -import android.app.Activity import android.content.ContentResolver import android.content.ContentUris import android.content.ContentValues @@ -14,7 +14,7 @@ import android.provider.CalendarContract import android.provider.CalendarContract.CALLER_IS_SYNCADAPTER import android.provider.CalendarContract.Events import android.text.format.DateUtils -import io.flutter.plugin.common.PluginRegistry.Registrar +import com.builttoroam.devicecalendar.common.Constants import com.builttoroam.devicecalendar.common.Constants.Companion.ATTENDEE_EMAIL_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.ATTENDEE_NAME_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.ATTENDEE_PROJECTION @@ -23,29 +23,32 @@ import com.builttoroam.devicecalendar.common.Constants.Companion.ATTENDEE_STATUS import com.builttoroam.devicecalendar.common.Constants.Companion.ATTENDEE_TYPE_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION_ACCESS_LEVEL_INDEX -import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION_COLOR_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION_ACCOUNT_NAME_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION_ACCOUNT_TYPE_INDEX +import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION_COLOR_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION_DISPLAY_NAME_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION_ID_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION_IS_PRIMARY_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.CALENDAR_PROJECTION_OLDER_API +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_BEGIN_INDEX +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_END_INDEX +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_ID_INDEX +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_LAST_DATE_INDEX +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_RRULE_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_ALL_DAY_INDEX +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_AVAILABILITY_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_BEGIN_INDEX +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_CUSTOM_APP_URI_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_DESCRIPTION_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_END_INDEX +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_END_TIMEZONE_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_EVENT_LOCATION_INDEX -import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_CUSTOM_APP_URI_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_ID_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_RECURRING_RULE_INDEX +import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_START_TIMEZONE_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_PROJECTION_TITLE_INDEX -import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION -import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_ID_INDEX -import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_RRULE_INDEX -import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_LAST_DATE_INDEX -import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_BEGIN_INDEX -import com.builttoroam.devicecalendar.common.Constants.Companion.EVENT_INSTANCE_DELETION_END_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.REMINDER_MINUTES_INDEX import com.builttoroam.devicecalendar.common.Constants.Companion.REMINDER_PROJECTION import com.builttoroam.devicecalendar.common.DayOfWeek @@ -57,6 +60,7 @@ import com.builttoroam.devicecalendar.common.ErrorCodes.Companion.NOT_FOUND import com.builttoroam.devicecalendar.common.ErrorMessages import com.builttoroam.devicecalendar.common.ErrorMessages.Companion.CALENDAR_ID_INVALID_ARGUMENT_NOT_A_NUMBER_MESSAGE import com.builttoroam.devicecalendar.common.ErrorMessages.Companion.CREATE_EVENT_ARGUMENTS_NOT_VALID_MESSAGE +import com.builttoroam.devicecalendar.common.ErrorMessages.Companion.EVENT_ID_CANNOT_BE_NULL_ON_DELETION_MESSAGE import com.builttoroam.devicecalendar.common.ErrorMessages.Companion.NOT_AUTHORIZED_MESSAGE import com.builttoroam.devicecalendar.common.RecurrenceFrequency import com.builttoroam.devicecalendar.models.* @@ -65,12 +69,16 @@ import com.google.gson.Gson import com.google.gson.GsonBuilder import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.PluginRegistry +import io.flutter.plugin.common.PluginRegistry.Registrar +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import org.dmfs.rfc5545.DateTime import org.dmfs.rfc5545.Weekday import org.dmfs.rfc5545.recur.Freq import java.text.SimpleDateFormat import java.util.* -import com.builttoroam.devicecalendar.models.CalendarMethodsParametersCacheModel class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { private val RETRIEVE_CALENDARS_REQUEST_CODE = 0 @@ -139,8 +147,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { } return true - } - finally { + } finally { _cachedParametersMap.remove(cachedValues.calendarDelegateMethodCode) } } @@ -199,7 +206,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { val contentResolver: ContentResolver? = _context?.contentResolver val uri: Uri = CalendarContract.Calendars.CONTENT_URI - + val cursor: Cursor? = if (atLeastAPI(17)) { contentResolver?.query(ContentUris.withAppendedId(uri, calendarIdNumber), CALENDAR_PROJECTION, null, null, null) } else { @@ -247,7 +254,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { values.put(CalendarContract.Calendars.ACCOUNT_NAME, localAccountName) values.put(CalendarContract.Calendars.ACCOUNT_TYPE, CalendarContract.ACCOUNT_TYPE_LOCAL) values.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_OWNER) - values.put(CalendarContract.Calendars.CALENDAR_COLOR, calendarColor ?: "0xFFFF0000") // Red colour as a default + values.put(CalendarContract.Calendars.CALENDAR_COLOR, Color.parseColor((calendarColor?:"0xFFFF0000").replace("0x","#"))) // Red colour as a default values.put(CalendarContract.Calendars.OWNER_ACCOUNT, localAccountName) values.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, java.util.Calendar.getInstance().timeZone.id) @@ -258,7 +265,6 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { finishWithSuccess(calendarId.toString(), pendingChannelResult) } - @SuppressLint("MissingPermission") fun retrieveEvents(calendarId: String, startDate: Long?, endDate: Long?, eventIds: List, pendingChannelResult: MethodChannel.Result) { if (startDate == null && endDate == null && eventIds.isEmpty()) { finishWithError(INVALID_ARGUMENT, ErrorMessages.RETRIEVE_EVENTS_ARGUMENTS_NOT_VALID_MESSAGE, pendingChannelResult) @@ -280,40 +286,44 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { val eventsUri = eventsUriBuilder.build() val eventsCalendarQuery = "(${Events.CALENDAR_ID} = $calendarId)" val eventsNotDeletedQuery = "(${Events.DELETED} != 1)" - val eventsIdsQueryElements = eventIds.map { "(${CalendarContract.Instances.EVENT_ID} = $it)" } - val eventsIdsQuery = eventsIdsQueryElements.joinToString(" OR ") + val eventsIdsQuery ="(${CalendarContract.Instances.EVENT_ID} IN (${eventIds.joinToString()}))" var eventsSelectionQuery = "$eventsCalendarQuery AND $eventsNotDeletedQuery" - if (eventsIdsQuery.isNotEmpty()) { + if (eventIds.isNotEmpty()) { eventsSelectionQuery += " AND ($eventsIdsQuery)" } - val eventsSortOrder = Events.DTSTART + " ASC" + val eventsSortOrder = Events.DTSTART + " DESC" + val eventsCursor = contentResolver?.query(eventsUri, EVENT_PROJECTION, eventsSelectionQuery, null, eventsSortOrder) val events: MutableList = mutableListOf() - try { - if (eventsCursor?.moveToFirst() == true) { - do { - val event = parseEvent(calendarId, eventsCursor) ?: continue - events.add(event) - - } while (eventsCursor.moveToNext()) - - for (event in events) { - val attendees = retrieveAttendees(event.eventId!!, contentResolver) - event.organizer = attendees.firstOrNull { it.isOrganizer != null && it.isOrganizer } - event.attendees = attendees - event.reminders = retrieveReminders(event.eventId!!, contentResolver) - } + val exceptionHandler = CoroutineExceptionHandler { _, exception -> + _registrar!!.activity().runOnUiThread { + finishWithError(GENERIC_ERROR, exception.message, pendingChannelResult) } - } catch (e: Exception) { - finishWithError(GENERIC_ERROR, e.message, pendingChannelResult) - } finally { - eventsCursor?.close() } - finishWithSuccess(_gson?.toJson(events), pendingChannelResult) + GlobalScope.launch(Dispatchers.IO + exceptionHandler) { + while (eventsCursor?.moveToNext() == true) { + val event = parseEvent(calendarId, eventsCursor) ?: continue + events.add(event) + } + for (event in events) { + val attendees = retrieveAttendees(event.eventId!!, contentResolver) + event.organizer = attendees.firstOrNull { it.isOrganizer != null && it.isOrganizer } + event.attendees = attendees + event.reminders = retrieveReminders(event.eventId!!, contentResolver) + } + }.invokeOnCompletion { + cause -> + eventsCursor?.close() + if (cause == null) { + _registrar!!.activity().runOnUiThread { + finishWithSuccess(_gson?.toJson(events), pendingChannelResult) + } + } + } } else { val parameters = CalendarMethodsParametersCacheModel(pendingChannelResult, RETRIEVE_EVENTS_REQUEST_CODE, calendarId, startDate, endDate) requestPermissions(parameters) @@ -417,20 +427,18 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { calendar.set(java.util.Calendar.MILLISECOND, 0) // All day events must have UTC timezone - val utcTimeZone = TimeZone.getTimeZone("UTC") + val utcTimeZone = TimeZone.getTimeZone("UTC") calendar.timeZone = utcTimeZone values.put(Events.DTSTART, calendar.timeInMillis) values.put(Events.DTEND, calendar.timeInMillis) values.put(Events.EVENT_TIMEZONE, utcTimeZone.id) - } - else { + } else { values.put(Events.DTSTART, event.start!!) - values.put(Events.DTEND, event.end!!) + values.put(Events.EVENT_TIMEZONE, getTimeZone(event.startTimeZone).id) - // MK using current device time zone - val currentTimeZone: TimeZone = java.util.Calendar.getInstance().timeZone - values.put(Events.EVENT_TIMEZONE, currentTimeZone.id) + values.put(Events.DTEND, event.end!!) + values.put(Events.EVENT_END_TIMEZONE, getTimeZone(event.endTimeZone).id) } values.put(Events.TITLE, event.title) values.put(Events.DESCRIPTION, event.description) @@ -438,6 +446,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { values.put(Events.CUSTOM_APP_URI, event.url) values.put(Events.CALENDAR_ID, calendarId) values.put(Events.DURATION, duration) + values.put(Events.AVAILABILITY, getAvailability(event.availability)) if (event.recurrenceRule != null) { val recurrenceRuleParams = buildRecurrenceRuleParams(event.recurrenceRule!!) @@ -446,6 +455,24 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { return values } + private fun getTimeZone(timeZoneString: String?): TimeZone { + val deviceTimeZone: TimeZone = java.util.Calendar.getInstance().timeZone + var timeZone = TimeZone.getTimeZone(timeZoneString ?: deviceTimeZone.id) + + // Invalid time zone names defaults to GMT so update that to be device's time zone + if (timeZone.id == "GMT" && timeZoneString != "GMT") { + timeZone = TimeZone.getTimeZone(deviceTimeZone.id) + } + + return timeZone + } + + private fun getAvailability(availability: String?): Int? = when (availability) { + Constants.AVAILABILITY_BUSY -> Events.AVAILABILITY_BUSY + Constants.AVAILABILITY_FREE -> Events.AVAILABILITY_FREE + else -> null + } + @SuppressLint("MissingPermission") private fun insertAttendees(attendees: List, eventId: Long?, contentResolver: ContentResolver?) { if (attendees.isEmpty()) { @@ -495,7 +522,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { val eventIdNumber = eventId.toLongOrNull() if (eventIdNumber == null) { - finishWithError(INVALID_ARGUMENT, CALENDAR_ID_INVALID_ARGUMENT_NOT_A_NUMBER_MESSAGE, pendingChannelResult) + finishWithError(INVALID_ARGUMENT, EVENT_ID_CANNOT_BE_NULL_ON_DELETION_MESSAGE, pendingChannelResult) return } @@ -504,8 +531,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { val eventsUriWithId = ContentUris.withAppendedId(Events.CONTENT_URI, eventIdNumber) val deleteSucceeded = contentResolver?.delete(eventsUriWithId, null, null) ?: 0 finishWithSuccess(deleteSucceeded > 0, pendingChannelResult) - } - else { + } else { if (!followingInstances!!) { // Only this instance val exceptionUriWithId = ContentUris.withAppendedId(Events.CONTENT_EXCEPTION_URI, eventIdNumber) val values = ContentValues() @@ -523,8 +549,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { val deleteSucceeded = contentResolver?.insert(exceptionUriWithId, values) instanceCursor.close() finishWithSuccess(deleteSucceeded != null, pendingChannelResult) - } - else { // This and following instances + } else { // This and following instances val eventsUriWithId = ContentUris.withAppendedId(Events.CONTENT_URI, eventIdNumber) val values = ContentValues() val instanceCursor = CalendarContract.Instances.query(contentResolver, EVENT_INSTANCE_DELETION, startDate!!, endDate!!) @@ -544,8 +569,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { } } cursor.close() - } - else { // Indefinite and specified date rule + } else { // Indefinite and specified date rule val cursor = CalendarContract.Instances.query(contentResolver, EVENT_INSTANCE_DELETION, startDate - DateUtils.YEAR_IN_MILLIS, startDate - 1) var lastRecurrenceDate: Long? = null @@ -557,8 +581,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { if (lastRecurrenceDate != null) { newRule.until = DateTime(lastRecurrenceDate) - } - else { + } else { newRule.until = DateTime(startDate - 1) } cursor.close() @@ -617,8 +640,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { if (atLeastAPI(17)) { val isPrimary = cursor.getString(CALENDAR_PROJECTION_IS_PRIMARY_INDEX) calendar.isDefault = isPrimary == "1" - } - else { + } else { calendar.isDefault = false } return calendar @@ -637,7 +659,10 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { val recurringRule = cursor.getString(EVENT_PROJECTION_RECURRING_RULE_INDEX) val allDay = cursor.getInt(EVENT_PROJECTION_ALL_DAY_INDEX) > 0 val location = cursor.getString(EVENT_PROJECTION_EVENT_LOCATION_INDEX) - var url = cursor.getString(EVENT_PROJECTION_CUSTOM_APP_URI_INDEX) + val url = cursor.getString(EVENT_PROJECTION_CUSTOM_APP_URI_INDEX) + val startTimeZone = cursor.getString(EVENT_PROJECTION_START_TIMEZONE_INDEX) + val endTimeZone = cursor.getString(EVENT_PROJECTION_END_TIMEZONE_INDEX) + val availability = parseAvailability(cursor.getInt(EVENT_PROJECTION_AVAILABILITY_INDEX)) val event = Event() event.title = title ?: "New Event" @@ -650,6 +675,10 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { event.location = location event.url = url event.recurrenceRule = parseRecurrenceRuleString(recurringRule) + event.startTimeZone = startTimeZone + event.endTimeZone = endTimeZone + event.availability = availability + return event } @@ -767,7 +796,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { } @SuppressLint("MissingPermission") - private fun retrieveReminders(eventId: String, contentResolver: ContentResolver?) : MutableList { + private fun retrieveReminders(eventId: String, contentResolver: ContentResolver?): MutableList { val reminders: MutableList = mutableListOf() val remindersQuery = "(${CalendarContract.Reminders.EVENT_ID} = ${eventId})" val remindersCursor = contentResolver?.query(CalendarContract.Reminders.CONTENT_URI, REMINDER_PROJECTION, remindersQuery, null, null) @@ -829,7 +858,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { } if (recurrenceRule.recurrenceFrequency == RecurrenceFrequency.WEEKLY || - recurrenceRule.weekOfMonth != null && (recurrenceRule.recurrenceFrequency == RecurrenceFrequency.MONTHLY || recurrenceRule.recurrenceFrequency == RecurrenceFrequency.YEARLY)) { + recurrenceRule.weekOfMonth != null && (recurrenceRule.recurrenceFrequency == RecurrenceFrequency.MONTHLY || recurrenceRule.recurrenceFrequency == RecurrenceFrequency.YEARLY)) { rr.byDayPart = buildByDayPart(recurrenceRule) } @@ -868,7 +897,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { it.ordinal == dayOfWeek.ordinal } }?.map { - org.dmfs.rfc5545.recur.RecurrenceRule.WeekdayNum(recurrenceRule.weekOfMonth?: 0, it) + org.dmfs.rfc5545.recur.RecurrenceRule.WeekdayNum(recurrenceRule.weekOfMonth ?: 0, it) } } @@ -879,4 +908,12 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener { return this } + + private fun parseAvailability(availability: Int): String? = when (availability) { + Events.AVAILABILITY_BUSY -> Constants.AVAILABILITY_BUSY + Events.AVAILABILITY_FREE -> Constants.AVAILABILITY_FREE + else -> null + } + + } \ No newline at end of file diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/DayOfWeekSerializer.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/DayOfWeekSerializer.kt similarity index 100% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/DayOfWeekSerializer.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/DayOfWeekSerializer.kt diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt similarity index 95% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt index ef63e228..50c1bd60 100644 --- a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/DeviceCalendarPlugin.kt @@ -1,6 +1,5 @@ package com.builttoroam.devicecalendar -import android.app.Activity import android.content.Context import com.builttoroam.devicecalendar.common.DayOfWeek import com.builttoroam.devicecalendar.common.RecurrenceFrequency @@ -13,7 +12,6 @@ import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugin.common.PluginRegistry.Registrar -import org.json.JSONArray const val CHANNEL_NAME = "plugins.builttoroam.com/device_calendar" @@ -42,6 +40,8 @@ class DeviceCalendarPlugin() : MethodCallHandler { private val EVENT_ALL_DAY_ARGUMENT = "eventAllDay" private val EVENT_START_DATE_ARGUMENT = "eventStartDate" private val EVENT_END_DATE_ARGUMENT = "eventEndDate" + private val EVENT_START_TIMEZONE_ARGUMENT = "eventStartTimeZone" + private val EVENT_END_TIMEZONE_ARGUMENT = "eventEndTimeZone" private val RECURRENCE_RULE_ARGUMENT = "recurrenceRule" private val RECURRENCE_FREQUENCY_ARGUMENT = "recurrenceFrequency" private val TOTAL_OCCURRENCES_ARGUMENT = "totalOccurrences" @@ -59,6 +59,8 @@ class DeviceCalendarPlugin() : MethodCallHandler { private val FOLLOWING_INSTANCES = "followingInstances" private val CALENDAR_COLOR_ARGUMENT = "calendarColor" private val LOCAL_ACCOUNT_NAME_ARGUMENT = "localAccountName" + private val EVENT_AVAILABILITY_ARGUMENT = "availability" + private lateinit var _registrar: Registrar private lateinit var _calendarDelegate: CalendarDelegate @@ -145,8 +147,11 @@ class DeviceCalendarPlugin() : MethodCallHandler { event.allDay = call.argument(EVENT_ALL_DAY_ARGUMENT) ?: false event.start = call.argument(EVENT_START_DATE_ARGUMENT)!! event.end = call.argument(EVENT_END_DATE_ARGUMENT)!! + event.startTimeZone = call.argument(EVENT_START_TIMEZONE_ARGUMENT) + event.endTimeZone = call.argument(EVENT_END_TIMEZONE_ARGUMENT) event.location = call.argument(EVENT_LOCATION_ARGUMENT) event.url = call.argument(EVENT_URL_ARGUMENT) + event.availability = call.argument(EVENT_AVAILABILITY_ARGUMENT) if (call.hasArgument(RECURRENCE_RULE_ARGUMENT) && call.argument>(RECURRENCE_RULE_ARGUMENT) != null) { val recurrenceRule = parseRecurrenceRuleArgs(call) diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/RecurrenceFrequencySerializer.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/RecurrenceFrequencySerializer.kt similarity index 100% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/RecurrenceFrequencySerializer.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/RecurrenceFrequencySerializer.kt diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt similarity index 90% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt index 4a09d862..6fce84b6 100644 --- a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/Constants.kt @@ -46,6 +46,9 @@ class Constants { const val EVENT_PROJECTION_ALL_DAY_INDEX: Int = 8 const val EVENT_PROJECTION_EVENT_LOCATION_INDEX: Int = 9 const val EVENT_PROJECTION_CUSTOM_APP_URI_INDEX: Int = 10 + const val EVENT_PROJECTION_START_TIMEZONE_INDEX: Int = 11 + const val EVENT_PROJECTION_END_TIMEZONE_INDEX: Int = 12 + const val EVENT_PROJECTION_AVAILABILITY_INDEX: Int = 13 val EVENT_PROJECTION: Array = arrayOf( CalendarContract.Instances.EVENT_ID, @@ -58,7 +61,10 @@ class Constants { CalendarContract.Events.RRULE, CalendarContract.Events.ALL_DAY, CalendarContract.Events.EVENT_LOCATION, - CalendarContract.Events.CUSTOM_APP_URI + CalendarContract.Events.CUSTOM_APP_URI, + CalendarContract.Events.EVENT_TIMEZONE, + CalendarContract.Events.EVENT_END_TIMEZONE, + CalendarContract.Events.AVAILABILITY ) const val EVENT_INSTANCE_DELETION_ID_INDEX: Int = 0 @@ -98,5 +104,8 @@ class Constants { CalendarContract.Reminders.EVENT_ID, CalendarContract.Reminders.MINUTES ) + + const val AVAILABILITY_BUSY : String = "BUSY" + const val AVAILABILITY_FREE : String = "FREE" } } \ No newline at end of file diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/DayOfWeek.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/DayOfWeek.kt similarity index 100% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/DayOfWeek.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/common/DayOfWeek.kt diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorCodes.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorCodes.kt similarity index 100% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorCodes.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorCodes.kt diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorMessages.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorMessages.kt similarity index 79% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorMessages.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorMessages.kt index c31d1fff..e8a8c82c 100644 --- a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorMessages.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/ErrorMessages.kt @@ -3,9 +3,9 @@ package com.builttoroam.devicecalendar.common class ErrorMessages { companion object { const val CALENDAR_ID_INVALID_ARGUMENT_NOT_A_NUMBER_MESSAGE: String = "Calendar ID is not a number" - const val RETRIEVE_EVENTS_ARGUMENTS_NOT_VALID_MESSAGE: String = "Provided arguments (i.e. start, end and event ids) are null or emtpy" + const val EVENT_ID_CANNOT_BE_NULL_ON_DELETION_MESSAGE: String = "Event ID cannot be null on deletion" + const val RETRIEVE_EVENTS_ARGUMENTS_NOT_VALID_MESSAGE: String = "Provided arguments (i.e. start, end and event ids) are null or empty" const val CREATE_EVENT_ARGUMENTS_NOT_VALID_MESSAGE: String = "Some of the event arguments are not valid" - const val DELETING_RECURRING_EVENT_NOT_SUPPORTED_MESSAGE: String = "Currently, deleting of recurring events is not supported" const val NOT_AUTHORIZED_MESSAGE: String = "The user has not allowed this application to modify their calendar(s)" } } diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/RecurrenceFrequency.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/common/RecurrenceFrequency.kt similarity index 100% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/common/RecurrenceFrequency.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/common/RecurrenceFrequency.kt diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Attendee.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Attendee.kt similarity index 97% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Attendee.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/models/Attendee.kt index 70014d4d..ea22491f 100644 --- a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Attendee.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Attendee.kt @@ -1,4 +1,3 @@ package com.builttoroam.devicecalendar.models -class Attendee(val emailAddress: String, val name: String?, val role: Int, val attendanceStatus: Int?, val isOrganizer: Boolean?) { -} \ No newline at end of file +class Attendee(val emailAddress: String, val name: String?, val role: Int, val attendanceStatus: Int?, val isOrganizer: Boolean?) \ No newline at end of file diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Calendar.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Calendar.kt similarity index 88% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Calendar.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/models/Calendar.kt index 735ea904..281e4534 100644 --- a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Calendar.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Calendar.kt @@ -1,7 +1,5 @@ package com.builttoroam.devicecalendar.models -import android.graphics.Color - class Calendar(val id: String, val name: String, val color : Int, val accountName: String, val accountType: String) { var isReadOnly: Boolean = false var isDefault: Boolean = false diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/CalendarMethodsParametersCacheModel.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/CalendarMethodsParametersCacheModel.kt similarity index 100% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/CalendarMethodsParametersCacheModel.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/models/CalendarMethodsParametersCacheModel.kt diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt similarity index 83% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt index f85a5809..2329ef1b 100644 --- a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Event.kt @@ -7,6 +7,8 @@ class Event { var description: String? = null var start: Long? = null var end: Long? = null + var startTimeZone: String? = null + var endTimeZone: String? = null var allDay: Boolean = false var location: String? = null var url: String? = null @@ -14,4 +16,5 @@ class Event { var recurrenceRule: RecurrenceRule? = null var organizer: Attendee? = null var reminders: MutableList = mutableListOf() + var availability: String? = null } \ No newline at end of file diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/RecurrenceRule.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/RecurrenceRule.kt similarity index 100% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/RecurrenceRule.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/models/RecurrenceRule.kt diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Reminder.kt b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Reminder.kt similarity index 56% rename from device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Reminder.kt rename to android/src/main/kotlin/com/builttoroam/devicecalendar/models/Reminder.kt index 2ae22f85..c9695796 100644 --- a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Reminder.kt +++ b/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Reminder.kt @@ -1,4 +1,3 @@ package com.builttoroam.devicecalendar.models -class Reminder(val minutes: Int) { -} \ No newline at end of file +class Reminder(val minutes: Int) \ No newline at end of file diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c3fc51d2..7b548a5a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -31,13 +31,13 @@ stages: displayName: 'Flutter build - Android' inputs: target: 'aab' - projectDirectory: 'device_calendar/example' + projectDirectory: 'example' - task: FlutterBuild@0 displayName: 'Flutter build - iOS' inputs: target: 'ios' - projectDirectory: 'device_calendar/example' + projectDirectory: 'example' iosCodesign: false # -----------CD Pre-release ----------- @@ -60,10 +60,10 @@ stages: inputs: targetType: 'inline' script: | - [string] $version = select-string -path "$(Build.SourcesDirectory)/device_calendar/pubspec.yaml" -pattern "$(versionNumberRegex)" | %{ $_.Matches[0].Value } + [string] $version = select-string -path "$(Build.SourcesDirectory)/pubspec.yaml" -pattern "$(versionNumberRegex)" | %{ $_.Matches[0].Value } Write-Host "##vso[task.setvariable variable=currentVersion]$version" - [string] $pubspecContent = Get-Content -Path "$(Build.SourcesDirectory)/device_calendar/pubspec.yaml" -Raw + [string] $pubspecContent = Get-Content -Path "$(Build.SourcesDirectory)/pubspec.yaml" -Raw Write-Host " (i) Current version: $version" Write-Host " (i) Original pubspec.yaml content: $pubspecContent" @@ -74,14 +74,14 @@ stages: Write-Host " (i) Updated pubspec.yaml content: $newPubspecContent" - $newPubspecContent | Set-Content -Path "$(Build.SourcesDirectory)/device_calendar/pubspec.yaml" + $newPubspecContent | Set-Content -Path "$(Build.SourcesDirectory)/pubspec.yaml" - task: PowerShell@2 displayName: 'Pre-release versioning - CHANGELOG.md' inputs: targetType: 'inline' script: | - [string] $changelogContent = Get-Content -Path "$(Build.SourcesDirectory)/device_calendar/CHANGELOG.md" -Raw + [string] $changelogContent = Get-Content -Path "$(Build.SourcesDirectory)/CHANGELOG.md" -Raw Write-Host " (i) Current version: $env.currentVersion" Write-Host " (i) Original CHANGELOG.md content: $changelogContent" @@ -90,7 +90,7 @@ stages: Write-Host " (i) Updated CHANGELOG.md content: $newChangelogContent" - $newChangelogContent | Set-Content -Path "$(Build.SourcesDirectory)/device_calendar/CHANGELOG.md" + $newChangelogContent | Set-Content -Path "$(Build.SourcesDirectory)/CHANGELOG.md" - task: PowerShell@2 displayName: 'Create publisher credentials file' @@ -112,13 +112,13 @@ stages: - task: CmdLine@2 displayName: 'Dry run publish' inputs: - workingDirectory: '$(Build.SourcesDirectory)/device_calendar' + workingDirectory: '$(Build.SourcesDirectory)' script: '$(flutterExecPath) pub publish --dry-run' - task: CmdLine@2 displayName: 'Publish' inputs: - workingDirectory: '$(Build.SourcesDirectory)/device_calendar' + workingDirectory: '$(Build.SourcesDirectory)' script: '$(flutterExecPath) pub publish --force' # ----------- CD Production ----------- @@ -141,7 +141,7 @@ stages: inputs: targetType: 'inline' script: | - [string] $version = select-string -path "$(Build.SourcesDirectory)/device_calendar/pubspec.yaml" -pattern "$(versionNumberRegex)" | %{ $_.Matches[0].Value } + [string] $version = select-string -path "$(Build.SourcesDirectory)/pubspec.yaml" -pattern "$(versionNumberRegex)" | %{ $_.Matches[0].Value } Write-Host "##vso[task.setvariable variable=currentVersion]$version" @@ -150,7 +150,7 @@ stages: inputs: targetType: 'inline' script: | - [string] $summary = select-string -path "$(Build.SourcesDirectory)/device_calendar/CHANGELOG.md" -pattern "($([regex]::escape($env:currentVersion))).*" | %{ $_.Matches[0].Value } + [string] $summary = select-string -path "$(Build.SourcesDirectory)/CHANGELOG.md" -pattern "($([regex]::escape($env:currentVersion))).*" | %{ $_.Matches[0].Value } Write-Host "##vso[task.setvariable variable=versionSummary]$summary" @@ -174,13 +174,13 @@ stages: - task: CmdLine@2 displayName: 'Dry run publish' inputs: - workingDirectory: '$(Build.SourcesDirectory)/device_calendar' + workingDirectory: '$(Build.SourcesDirectory)' script: '$(flutterExecPath) pub publish --dry-run' - task: CmdLine@2 displayName: 'Publish' inputs: - workingDirectory: '$(Build.SourcesDirectory)/device_calendar' + workingDirectory: '$(Build.SourcesDirectory)' script: '$(flutterExecPath) pub publish --force' - task: GitHubReleasePublish@1 @@ -188,7 +188,7 @@ stages: inputs: githubEndpoint: 'GitHub Tagging' manuallySetRepository: false - githubRepository: 'builttoroam/flutter_plugins' + githubRepository: 'builttoroam/device_calendar' githubTag: 'v$(currentVersion)' githubReleaseTitle: '$(versionSummary)' githubReleaseDraft: false diff --git a/device_calendar/device_calendar.iml b/device_calendar.iml similarity index 100% rename from device_calendar/device_calendar.iml rename to device_calendar.iml diff --git a/device_calendar/.gitignore b/device_calendar/.gitignore deleted file mode 100644 index d969c0aa..00000000 --- a/device_calendar/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# See https://www.dartlang.org/tools/private-files.html - -# Files and directories created by pub -.idea -.DS_Store -.packages -.pub/ -.vscode/ -build/ -# If you're building an application, you may want to check-in your pubspec.lock -pubspec.lock - -# Directory created by dartdoc -# If you don't generate documentation locally you can remove this line. -doc/api/ - -ios/.generated/ \ No newline at end of file diff --git a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Account.kt b/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Account.kt deleted file mode 100644 index b06713d4..00000000 --- a/device_calendar/android/src/main/kotlin/com/builttoroam/devicecalendar/models/Account.kt +++ /dev/null @@ -1,4 +0,0 @@ -package com.builttoroam.devicecalendar.models - -class Account(val name: String, val type: String) { -} \ No newline at end of file diff --git a/device_calendar/example/lib/presentation/recurring_event_dialog.dart b/device_calendar/example/lib/presentation/recurring_event_dialog.dart deleted file mode 100644 index d994075b..00000000 --- a/device_calendar/example/lib/presentation/recurring_event_dialog.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:device_calendar/device_calendar.dart'; - -class RecurringEventDialog extends StatefulWidget { - final DeviceCalendarPlugin _deviceCalendarPlugin; - final Event _calendarEvent; - - final VoidCallback _onLoadingStarted; - final Function(bool) _onDeleteFinished; - - RecurringEventDialog( - this._deviceCalendarPlugin, - this._calendarEvent, - this._onLoadingStarted, - this._onDeleteFinished, - {Key key}) : super(key: key); - - _RecurringEventDialogState createState() => - _RecurringEventDialogState(_deviceCalendarPlugin, _calendarEvent, onLoadingStarted: _onLoadingStarted, onDeleteFinished: _onDeleteFinished); -} - -class _RecurringEventDialogState extends State { - DeviceCalendarPlugin _deviceCalendarPlugin; - Event _calendarEvent; - VoidCallback _onLoadingStarted; - Function(bool) _onDeleteFinished; - - _RecurringEventDialogState(DeviceCalendarPlugin deviceCalendarPlugin, Event calendarEvent, {VoidCallback onLoadingStarted, Function(bool) onDeleteFinished}) { - _deviceCalendarPlugin = deviceCalendarPlugin; - _calendarEvent = calendarEvent; - _onLoadingStarted = onLoadingStarted; - _onDeleteFinished = onDeleteFinished; - } - - @override - Widget build(BuildContext context) { - return SimpleDialog( - title: Text('Are you sure you want to delete this event?'), - children: [ - SimpleDialogOption( - child: Text('This instance only'), - onPressed: () async { - Navigator.of(context).pop(true); - _onLoadingStarted(); - final deleteResult = await _deviceCalendarPlugin.deleteEventInstance( - _calendarEvent.calendarId, - _calendarEvent.eventId, - _calendarEvent.start.millisecondsSinceEpoch, - _calendarEvent.end.millisecondsSinceEpoch, - false); - _onDeleteFinished(deleteResult.isSuccess && deleteResult.data); - }, - ), - SimpleDialogOption( - child: Text('This and following instances'), - onPressed: () async { - Navigator.of(context).pop(true); - _onLoadingStarted(); - final deleteResult = await _deviceCalendarPlugin.deleteEventInstance( - _calendarEvent.calendarId, - _calendarEvent.eventId, - _calendarEvent.start.millisecondsSinceEpoch, - _calendarEvent.end.millisecondsSinceEpoch, - true); - _onDeleteFinished(deleteResult.isSuccess && deleteResult.data); - }, - ), - SimpleDialogOption( - child: Text('All instances'), - onPressed: () async { - Navigator.of(context).pop(true); - _onLoadingStarted(); - final deleteResult = await _deviceCalendarPlugin.deleteEvent(_calendarEvent.calendarId, _calendarEvent.eventId); - _onDeleteFinished(deleteResult.isSuccess && deleteResult.data); - }, - ), - SimpleDialogOption( - child: Text('Cancel'), - onPressed: () { Navigator.of(context).pop(false); }, - ) - ], - ); - } -} diff --git a/device_calendar/lib/src/common/calendar_enums.dart b/device_calendar/lib/src/common/calendar_enums.dart deleted file mode 100644 index a7d4b16a..00000000 --- a/device_calendar/lib/src/common/calendar_enums.dart +++ /dev/null @@ -1,195 +0,0 @@ -enum DayOfWeek { - Monday, - Tuesday, - Wednesday, - Thursday, - Friday, - Saturday, - Sunday -} - -enum DayOfWeekGroup { - None, - Weekday, - Weekend, - AllDays -} - -enum MonthOfYear { - January, - Feburary, - March, - April, - May, - June, - July, - August, - September, - October, - November, - December -} - -enum WeekNumber { - First, - Second, - Third, - Fourth, - Last -} - -enum AttendeeRole { - None, - Required, - Optional, - Resource -} - -// Adding extensions for enum values as Flutter does not support it yet -// TODO: Replace this to enum values when introduced. Ref: https://github.com/dart-lang/language/issues/158 -extension DayOfWeekExtension on DayOfWeek { - static int _value(DayOfWeek val) { - switch (val) { - case DayOfWeek.Monday: return 1; - case DayOfWeek.Tuesday: return 2; - case DayOfWeek.Wednesday: return 3; - case DayOfWeek.Thursday: return 4; - case DayOfWeek.Friday: return 5; - case DayOfWeek.Saturday: return 6; - case DayOfWeek.Sunday: return 0; - default: return 1; - } - } - - String _enumToString(DayOfWeek enumValue) { - return enumValue.toString().split('.').last; - } - - int get value => _value(this); - String get enumToString => _enumToString(this); -} - -extension DaysOfWeekGroupExtension on DayOfWeekGroup { - static List _getDays(DayOfWeekGroup val) { - switch (val) { - case DayOfWeekGroup.Weekday: - return [DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday]; - case DayOfWeekGroup.Weekend: - return [DayOfWeek.Saturday, DayOfWeek.Sunday]; - case DayOfWeekGroup.AllDays: - return [DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday, DayOfWeek.Sunday]; - default: - return []; - } - } - - String _enumToString(DayOfWeekGroup enumValue) { - return enumValue.toString().split('.').last; - } - - List get getDays => _getDays(this); - String get enumToString => _enumToString(this); -} - -extension MonthOfYearExtension on MonthOfYear { - static int _value(MonthOfYear val) { - switch (val) { - case MonthOfYear.January: return 1; - case MonthOfYear.Feburary: return 2; - case MonthOfYear.March: return 3; - case MonthOfYear.April: return 4; - case MonthOfYear.May: return 5; - case MonthOfYear.June: return 6; - case MonthOfYear.July: return 7; - case MonthOfYear.August: return 8; - case MonthOfYear.September: return 9; - case MonthOfYear.October: return 10; - case MonthOfYear.November: return 11; - case MonthOfYear.December: return 12; - default: return 1; - } - } - - String _enumToString(MonthOfYear enumValue) { - return enumValue.toString().split('.').last; - } - - int get value => _value(this); - String get enumToString => _enumToString(this); -} - -extension WeekNumberExtension on WeekNumber { - static int _value(WeekNumber val) { - switch (val) { - case WeekNumber.First: return 1; - case WeekNumber.Second: return 2; - case WeekNumber.Third: return 3; - case WeekNumber.Fourth: return 4; - case WeekNumber.Last: return -1; - default: return 1; - } - } - - String _enumToString(WeekNumber enumValue) { - return enumValue.toString().split('.').last; - } - - int get value => _value(this); - String get enumToString => _enumToString(this); -} - -extension IntExtensions on int { - static DayOfWeek _getDayOfWeekEnumValue(int val) { - switch (val) { - case 1: return DayOfWeek.Monday; - case 2: return DayOfWeek.Tuesday; - case 3: return DayOfWeek.Wednesday; - case 4: return DayOfWeek.Thursday; - case 5: return DayOfWeek.Friday; - case 6: return DayOfWeek.Saturday; - case 0: return DayOfWeek.Sunday; - default: return DayOfWeek.Monday; - } - } - - static MonthOfYear _getMonthOfYearEnumValue(int val) { - switch (val) { - case 1: return MonthOfYear.January; - case 2: return MonthOfYear.Feburary; - case 3: return MonthOfYear.March; - case 4: return MonthOfYear.April; - case 5: return MonthOfYear.May; - case 6: return MonthOfYear.June; - case 7: return MonthOfYear.July; - case 8: return MonthOfYear.August; - case 9: return MonthOfYear.September; - case 10: return MonthOfYear.October; - case 11: return MonthOfYear.November; - case 12: return MonthOfYear.December; - default: return MonthOfYear.January; - } - } - - static WeekNumber _getWeekNumberEnumValue(int val) { - switch (val) { - case 1: return WeekNumber.First; - case 2: return WeekNumber.Second; - case 3: return WeekNumber.Third; - case 4: return WeekNumber.Fourth; - case -1: return WeekNumber.Last; - default: return WeekNumber.First; - } - } - - DayOfWeek get getDayOfWeekEnumValue => _getDayOfWeekEnumValue(this); - MonthOfYear get getMonthOfYearEnumValue => _getMonthOfYearEnumValue(this); - WeekNumber get getWeekNumberEnumValue => _getWeekNumberEnumValue(this); -} - -extension RoleExtensions on AttendeeRole { - String _enumToString(AttendeeRole enumValue) { - return enumValue.toString().split('.').last; - } - - String get enumToString => _enumToString(this); -} \ No newline at end of file diff --git a/device_calendar/device_calendar_android.iml b/device_calendar_android.iml similarity index 100% rename from device_calendar/device_calendar_android.iml rename to device_calendar_android.iml diff --git a/device_calendar/example/.gitignore b/example/.gitignore similarity index 100% rename from device_calendar/example/.gitignore rename to example/.gitignore diff --git a/device_calendar/example/.metadata b/example/.metadata similarity index 100% rename from device_calendar/example/.metadata rename to example/.metadata diff --git a/device_calendar/example/README.md b/example/README.md similarity index 93% rename from device_calendar/example/README.md rename to example/README.md index 6ce2dfc5..3bf319dd 100644 --- a/device_calendar/example/README.md +++ b/example/README.md @@ -1,6 +1,6 @@ # Examples -Most of the APIs are covered in [calendar_event.dart](https://github.com/builttoroam/flutter_plugins/blob/master/device_calendar/example/lib/presentation/pages/calendar_event.dart) or [calendar_events.dart](https://github.com/builttoroam/flutter_plugins/blob/master/device_calendar/example/lib/presentation/pages/calendar_events.dart) files in the example app. +Most of the APIs are covered in [calendar_event.dart](https://github.com/builttoroam/device_calendar/blob/master/example/lib/presentation/pages/calendar_event.dart) or [calendar_events.dart](https://github.com/builttoroam/device_calendar/blob/master/example/lib/presentation/pages/calendar_events.dart) files in the example app. You'll be able to get a reference of how the APIs are used. For a full API reference, the documentation can be found at [pub.dev](https://pub.dev/documentation/device_calendar/latest/device_calendar/device_calendar-library.html). diff --git a/device_calendar/example/analysis_options.yaml b/example/analysis_options.yaml similarity index 100% rename from device_calendar/example/analysis_options.yaml rename to example/analysis_options.yaml diff --git a/device_calendar/example/android/.gitignore b/example/android/.gitignore similarity index 100% rename from device_calendar/example/android/.gitignore rename to example/android/.gitignore diff --git a/example/android/.idea/caches/build_file_checksums.ser b/example/android/.idea/caches/build_file_checksums.ser new file mode 100644 index 00000000..1efc677b Binary files /dev/null and b/example/android/.idea/caches/build_file_checksums.ser differ diff --git a/example/android/.idea/codeStyles/Project.xml b/example/android/.idea/codeStyles/Project.xml new file mode 100644 index 00000000..681f41ae --- /dev/null +++ b/example/android/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/example/android/.idea/gradle.xml b/example/android/.idea/gradle.xml new file mode 100644 index 00000000..7f7a0d67 --- /dev/null +++ b/example/android/.idea/gradle.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/example/android/.idea/misc.xml b/example/android/.idea/misc.xml new file mode 100644 index 00000000..7bfef59d --- /dev/null +++ b/example/android/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/example/android/.idea/modules.xml b/example/android/.idea/modules.xml new file mode 100644 index 00000000..c34eb2fe --- /dev/null +++ b/example/android/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/example/android/.idea/runConfigurations.xml b/example/android/.idea/runConfigurations.xml new file mode 100644 index 00000000..7f68460d --- /dev/null +++ b/example/android/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/example/android/.idea/vcs.xml b/example/android/.idea/vcs.xml new file mode 100644 index 00000000..c2365ab1 --- /dev/null +++ b/example/android/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/device_calendar/example/android/app/build.gradle b/example/android/app/build.gradle similarity index 100% rename from device_calendar/example/android/app/build.gradle rename to example/android/app/build.gradle diff --git a/device_calendar/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml similarity index 100% rename from device_calendar/example/android/app/src/main/AndroidManifest.xml rename to example/android/app/src/main/AndroidManifest.xml diff --git a/device_calendar/example/android/app/src/main/kotlin/com/builttoroam/devicecalendarexample/MainActivity.kt b/example/android/app/src/main/kotlin/com/builttoroam/devicecalendarexample/MainActivity.kt similarity index 88% rename from device_calendar/example/android/app/src/main/kotlin/com/builttoroam/devicecalendarexample/MainActivity.kt rename to example/android/app/src/main/kotlin/com/builttoroam/devicecalendarexample/MainActivity.kt index 350fa42a..df80d509 100644 --- a/device_calendar/example/android/app/src/main/kotlin/com/builttoroam/devicecalendarexample/MainActivity.kt +++ b/example/android/app/src/main/kotlin/com/builttoroam/devicecalendarexample/MainActivity.kt @@ -5,7 +5,7 @@ import android.os.Bundle import io.flutter.app.FlutterActivity import io.flutter.plugins.GeneratedPluginRegistrant -class MainActivity(): FlutterActivity() { +class MainActivity : FlutterActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) GeneratedPluginRegistrant.registerWith(this) diff --git a/device_calendar/example/android/app/src/main/res/drawable/launch_background.xml b/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from device_calendar/example/android/app/src/main/res/drawable/launch_background.xml rename to example/android/app/src/main/res/drawable/launch_background.xml diff --git a/device_calendar/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from device_calendar/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/device_calendar/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from device_calendar/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/device_calendar/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from device_calendar/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/device_calendar/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from device_calendar/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/device_calendar/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from device_calendar/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/device_calendar/example/android/app/src/main/res/values/styles.xml b/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from device_calendar/example/android/app/src/main/res/values/styles.xml rename to example/android/app/src/main/res/values/styles.xml diff --git a/device_calendar/example/android/build.gradle b/example/android/build.gradle similarity index 100% rename from device_calendar/example/android/build.gradle rename to example/android/build.gradle diff --git a/device_calendar/example/android/gradle.properties b/example/android/gradle.properties similarity index 100% rename from device_calendar/example/android/gradle.properties rename to example/android/gradle.properties diff --git a/device_calendar/example/android/gradle/wrapper/gradle-wrapper.jar b/example/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from device_calendar/example/android/gradle/wrapper/gradle-wrapper.jar rename to example/android/gradle/wrapper/gradle-wrapper.jar diff --git a/device_calendar/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from device_calendar/example/android/gradle/wrapper/gradle-wrapper.properties rename to example/android/gradle/wrapper/gradle-wrapper.properties diff --git a/device_calendar/example/android/gradlew b/example/android/gradlew similarity index 100% rename from device_calendar/example/android/gradlew rename to example/android/gradlew diff --git a/device_calendar/example/android/gradlew.bat b/example/android/gradlew.bat similarity index 100% rename from device_calendar/example/android/gradlew.bat rename to example/android/gradlew.bat diff --git a/device_calendar/example/android/settings.gradle b/example/android/settings.gradle similarity index 100% rename from device_calendar/example/android/settings.gradle rename to example/android/settings.gradle diff --git a/device_calendar/example/device_calendar_example.iml b/example/device_calendar_example.iml similarity index 100% rename from device_calendar/example/device_calendar_example.iml rename to example/device_calendar_example.iml diff --git a/device_calendar/example/device_calendar_example_android.iml b/example/device_calendar_example_android.iml similarity index 100% rename from device_calendar/example/device_calendar_example_android.iml rename to example/device_calendar_example_android.iml diff --git a/device_calendar/example/ios/.gitignore b/example/ios/.gitignore similarity index 100% rename from device_calendar/example/ios/.gitignore rename to example/ios/.gitignore diff --git a/device_calendar/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from device_calendar/example/ios/Flutter/AppFrameworkInfo.plist rename to example/ios/Flutter/AppFrameworkInfo.plist diff --git a/device_calendar/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from device_calendar/example/ios/Flutter/Debug.xcconfig rename to example/ios/Flutter/Debug.xcconfig diff --git a/device_calendar/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig similarity index 100% rename from device_calendar/example/ios/Flutter/Release.xcconfig rename to example/ios/Flutter/Release.xcconfig diff --git a/device_calendar/example/ios/Podfile b/example/ios/Podfile similarity index 100% rename from device_calendar/example/ios/Podfile rename to example/ios/Podfile diff --git a/device_calendar/example/ios/Podfile.lock b/example/ios/Podfile.lock similarity index 100% rename from device_calendar/example/ios/Podfile.lock rename to example/ios/Podfile.lock diff --git a/device_calendar/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj similarity index 100% rename from device_calendar/example/ios/Runner.xcodeproj/project.pbxproj rename to example/ios/Runner.xcodeproj/project.pbxproj diff --git a/device_calendar/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from device_calendar/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/device_calendar/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from device_calendar/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/device_calendar/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from device_calendar/example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/device_calendar/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from device_calendar/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/device_calendar/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from device_calendar/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/device_calendar/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift similarity index 100% rename from device_calendar/example/ios/Runner/AppDelegate.swift rename to example/ios/Runner/AppDelegate.swift diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png rename to example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from device_calendar/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/device_calendar/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from device_calendar/example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/device_calendar/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from device_calendar/example/ios/Runner/Base.lproj/Main.storyboard rename to example/ios/Runner/Base.lproj/Main.storyboard diff --git a/device_calendar/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist similarity index 100% rename from device_calendar/example/ios/Runner/Info.plist rename to example/ios/Runner/Info.plist diff --git a/device_calendar/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from device_calendar/example/ios/Runner/Runner-Bridging-Header.h rename to example/ios/Runner/Runner-Bridging-Header.h diff --git a/device_calendar/example/lib/common/app_routes.dart b/example/lib/common/app_routes.dart similarity index 100% rename from device_calendar/example/lib/common/app_routes.dart rename to example/lib/common/app_routes.dart diff --git a/device_calendar/example/lib/main.dart b/example/lib/main.dart similarity index 100% rename from device_calendar/example/lib/main.dart rename to example/lib/main.dart diff --git a/device_calendar/example/lib/presentation/date_time_picker.dart b/example/lib/presentation/date_time_picker.dart similarity index 98% rename from device_calendar/example/lib/presentation/date_time_picker.dart rename to example/lib/presentation/date_time_picker.dart index 283463d9..cf04f993 100644 --- a/device_calendar/example/lib/presentation/date_time_picker.dart +++ b/example/lib/presentation/date_time_picker.dart @@ -57,7 +57,7 @@ class DateTimePicker extends StatelessWidget { }, ), ), - if (enableTime) ... [ + if (enableTime) ...[ const SizedBox(width: 12.0), Expanded( flex: 3, diff --git a/device_calendar/example/lib/presentation/event_item.dart b/example/lib/presentation/event_item.dart similarity index 81% rename from device_calendar/example/lib/presentation/event_item.dart rename to example/lib/presentation/event_item.dart index 9b8ad524..621dac2c 100644 --- a/device_calendar/example/lib/presentation/event_item.dart +++ b/example/lib/presentation/event_item.dart @@ -15,8 +15,13 @@ class EventItem extends StatelessWidget { final double _eventFieldNameWidth = 75.0; - EventItem(this._calendarEvent, this._deviceCalendarPlugin, - this._onLoadingStarted, this._onDeleteFinished, this._onTapped, this._isReadOnly); + EventItem( + this._calendarEvent, + this._deviceCalendarPlugin, + this._onLoadingStarted, + this._onDeleteFinished, + this._onTapped, + this._isReadOnly); @override Widget build(BuildContext context) { @@ -156,6 +161,26 @@ class EventItem extends StatelessWidget { ], ), ), + SizedBox( + height: 10.0, + ), + Align( + alignment: Alignment.topLeft, + child: Row( + children: [ + Container( + width: _eventFieldNameWidth, + child: Text('Availability'), + ), + Expanded( + child: Text( + _calendarEvent?.availability ?? '', + overflow: TextOverflow.ellipsis, + ), + ) + ], + ), + ), ], ), ), @@ -176,7 +201,8 @@ class EventItem extends StatelessWidget { builder: (BuildContext context) { if (_calendarEvent.recurrenceRule == null) { return AlertDialog( - title: Text('Are you sure you want to delete this event?'), + title: Text( + 'Are you sure you want to delete this event?'), actions: [ FlatButton( onPressed: () { @@ -188,29 +214,30 @@ class EventItem extends StatelessWidget { onPressed: () async { Navigator.of(context).pop(); _onLoadingStarted(); - final deleteResult = await _deviceCalendarPlugin.deleteEvent(_calendarEvent.calendarId, _calendarEvent.eventId); - _onDeleteFinished(deleteResult.isSuccess && deleteResult.data); + final deleteResult = + await _deviceCalendarPlugin.deleteEvent( + _calendarEvent.calendarId, + _calendarEvent.eventId); + _onDeleteFinished(deleteResult.isSuccess && + deleteResult.data); }, child: Text('Delete'), ), ], ); - } - else { + } else { return RecurringEventDialog( - _deviceCalendarPlugin, - _calendarEvent, - _onLoadingStarted, - _onDeleteFinished - ); + _deviceCalendarPlugin, + _calendarEvent, + _onLoadingStarted, + _onDeleteFinished); } - } + }, ); }, icon: Icon(Icons.delete), ), - ] - else ... [ + ] else ...[ IconButton( onPressed: () { _onTapped(_calendarEvent); diff --git a/device_calendar/example/lib/presentation/input_dropdown.dart b/example/lib/presentation/input_dropdown.dart similarity index 100% rename from device_calendar/example/lib/presentation/input_dropdown.dart rename to example/lib/presentation/input_dropdown.dart diff --git a/device_calendar/example/lib/presentation/pages/calendar_add.dart b/example/lib/presentation/pages/calendar_add.dart similarity index 87% rename from device_calendar/example/lib/presentation/pages/calendar_add.dart rename to example/lib/presentation/pages/calendar_add.dart index 96deb056..2d304ca2 100644 --- a/device_calendar/example/lib/presentation/pages/calendar_add.dart +++ b/example/lib/presentation/pages/calendar_add.dart @@ -131,18 +131,28 @@ enum ColorChoice { extension ColorChoiceExtension on ColorChoice { static Color _value(ColorChoice val) { switch (val) { - case ColorChoice.Red: return Colors.red; - case ColorChoice.Orange: return Colors.orange; - case ColorChoice.Yellow: return Colors.yellow; - case ColorChoice.Green: return Colors.green; - case ColorChoice.Blue: return Colors.blue; - case ColorChoice.Purple: return Colors.purple; - case ColorChoice.Brown: return Colors.brown; - case ColorChoice.Black: return Colors.black; - case ColorChoice.White: return Colors.white; - default: return Colors.red; + case ColorChoice.Red: + return Colors.red; + case ColorChoice.Orange: + return Colors.orange; + case ColorChoice.Yellow: + return Colors.yellow; + case ColorChoice.Green: + return Colors.green; + case ColorChoice.Blue: + return Colors.blue; + case ColorChoice.Purple: + return Colors.purple; + case ColorChoice.Brown: + return Colors.brown; + case ColorChoice.Black: + return Colors.black; + case ColorChoice.White: + return Colors.white; + default: + return Colors.red; } } Color get value => _value(this); -} \ No newline at end of file +} diff --git a/device_calendar/example/lib/presentation/pages/calendar_event.dart b/example/lib/presentation/pages/calendar_event.dart similarity index 68% rename from device_calendar/example/lib/presentation/pages/calendar_event.dart rename to example/lib/presentation/pages/calendar_event.dart index 73c5caff..66d1d701 100644 --- a/device_calendar/example/lib/presentation/pages/calendar_event.dart +++ b/example/lib/presentation/pages/calendar_event.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:device_calendar/device_calendar.dart'; import 'package:flutter/services.dart'; import 'event_attendee.dart'; @@ -58,8 +60,9 @@ class _CalendarEventPageState extends State { List _attendees = List(); List _reminders = List(); - - _CalendarEventPageState(this._calendar, this._event, this._recurringEventDialog) { + + _CalendarEventPageState( + this._calendar, this._event, this._recurringEventDialog) { _deviceCalendarPlugin = DeviceCalendarPlugin(); _recurringEventDialog = this._recurringEventDialog; @@ -76,8 +79,7 @@ class _CalendarEventPageState extends State { _dayOfMonth = 1; _monthOfYear = MonthOfYear.January; _weekOfMonth = WeekNumber.First; - } - else { + } else { _startDate = _event.start; _endDate = _event.end; _isRecurringEvent = _event.recurrenceRule != null; @@ -108,9 +110,10 @@ class _CalendarEventPageState extends State { _daysOfWeek = _event.recurrenceRule.daysOfWeek ?? List(); _monthOfYear = _event.recurrenceRule.monthOfYear ?? MonthOfYear.January; _weekOfMonth = _event.recurrenceRule.weekOfMonth ?? WeekNumber.First; - _selectedDayOfWeek = _daysOfWeek.isNotEmpty ? _daysOfWeek.first : DayOfWeek.Monday; + _selectedDayOfWeek = + _daysOfWeek.isNotEmpty ? _daysOfWeek.first : DayOfWeek.Monday; _dayOfMonth = _event.recurrenceRule.dayOfMonth ?? 1; - + if (_daysOfWeek.isNotEmpty) { _updateDaysOfWeekGroup(); } @@ -140,7 +143,9 @@ class _CalendarEventPageState extends State { appBar: AppBar( title: Text(_event.eventId?.isEmpty ?? true ? 'Create event' - : _calendar.isReadOnly ? 'View event ${_event.title}' : 'Edit event ${_event.title}'), + : _calendar.isReadOnly + ? 'View event ${_event.title}' + : 'Edit event ${_event.title}'), ), body: SingleChildScrollView( child: AbsorbPointer( @@ -183,7 +188,8 @@ class _CalendarEventPageState extends State { child: TextFormField( initialValue: _event.location, decoration: const InputDecoration( - labelText: 'Location', hintText: 'Sydney, Australia'), + labelText: 'Location', + hintText: 'Sydney, Australia'), onSaved: (String value) { _event.location = value; }, @@ -194,19 +200,38 @@ class _CalendarEventPageState extends State { child: TextFormField( initialValue: _event.url?.data?.contentText ?? '', decoration: const InputDecoration( - labelText: 'URL', - hintText: 'https://google.com'), + labelText: 'URL', hintText: 'https://google.com'), onSaved: (String value) { var uri = Uri.dataFromString(value); _event.url = uri; }, ), ), - SwitchListTile( - value: _event.allDay, - onChanged: (value) => setState(() => _event.allDay = value), - title: Text('All Day'), + ListTile( + leading: Text('Availability', style: TextStyle(fontSize: 16),), + trailing: DropdownButton( + value: _event.availability ?? 'BUSY', + onChanged: (String newValue) { + setState(() { + _event.availability = newValue; + }); + }, + items: ['BUSY', 'FREE'] + .map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }) + .toList(), ), + ), + SwitchListTile( + value: _event.allDay, + onChanged: (value) => + setState(() => _event.allDay = value), + title: Text('All Day'), + ), Padding( padding: const EdgeInsets.all(10.0), child: DateTimePicker( @@ -217,19 +242,35 @@ class _CalendarEventPageState extends State { selectDate: (DateTime date) { setState(() { _startDate = date; - _event.start = _combineDateWithTime(_startDate, _startTime); + _event.start = + _combineDateWithTime(_startDate, _startTime); }); }, selectTime: (TimeOfDay time) { - setState(() { + setState( + () { _startTime = time; - _event.start = _combineDateWithTime(_startDate, _startTime); + _event.start = + _combineDateWithTime(_startDate, _startTime); }, ); }, ), ), - if (!_event.allDay) ... [ + if (!_event.allDay) ...[ + if (Platform.isAndroid) + Padding( + padding: const EdgeInsets.all(10.0), + child: TextFormField( + initialValue: _event.startTimeZone, + decoration: const InputDecoration( + labelText: 'Start date time zone', + hintText: 'Australia/Sydney'), + onSaved: (String value) { + _event.startTimeZone = value; + }, + ), + ), Padding( padding: const EdgeInsets.all(10.0), child: DateTimePicker( @@ -256,12 +297,29 @@ class _CalendarEventPageState extends State { }, ), ), + Padding( + padding: const EdgeInsets.all(10.0), + child: TextFormField( + initialValue: Platform.isAndroid + ? _event.endTimeZone + : _event.startTimeZone, + decoration: InputDecoration( + labelText: Platform.isAndroid + ? 'End date time zone' + : 'Start and end time zone', + hintText: 'Australia/Sydney'), + onSaved: (String value) => Platform.isAndroid + ? _event.endTimeZone = value + : _event.startTimeZone = value, + ), + ), ], GestureDetector( onTap: () async { Attendee result = await Navigator.push( context, - MaterialPageRoute( builder: (context) => EventAttendeePage())); + MaterialPageRoute( + builder: (context) => EventAttendeePage())); if (result == null) return; _attendees.add(result); }, @@ -272,7 +330,12 @@ class _CalendarEventPageState extends State { child: Wrap( crossAxisAlignment: WrapCrossAlignment.center, spacing: 10.0, - children: [ Icon(Icons.people), Text(!_calendar.isReadOnly ? 'Add Attendees' : 'Attendees') ], + children: [ + Icon(Icons.people), + Text(!_calendar.isReadOnly + ? 'Add Attendees' + : 'Attendees') + ], ), ), ), @@ -283,35 +346,51 @@ class _CalendarEventPageState extends State { itemCount: _attendees.length, itemBuilder: (context, index) { return Container( - color: _attendees[index].isOrganiser ? Colors.greenAccent[100] : Colors.transparent, + color: _attendees[index].isOrganiser + ? Colors.greenAccent[100] + : Colors.transparent, child: ListTile( title: GestureDetector( - child: Text('${_attendees[index].emailAddress}'), - onTap: () async { - Attendee result = await Navigator.push( - context, - MaterialPageRoute( builder: (context) => EventAttendeePage(attendee: _attendees[index]))); - if (result == null) return; - _attendees[index] = result; - } - ), - trailing: Row ( + child: + Text('${_attendees[index].emailAddress}'), + onTap: () async { + Attendee result = await Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + EventAttendeePage( + attendee: + _attendees[index]))); + if (result == null) return; + _attendees[index] = result; + }), + trailing: Row( mainAxisSize: MainAxisSize.min, children: [ Container( margin: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(3.0), - decoration: BoxDecoration(border: Border.all(color: Colors.blueAccent)), - child: Text('${_attendees[index].role.enumToString}'), + decoration: BoxDecoration( + border: + Border.all(color: Colors.blueAccent)), + child: Text( + '${_attendees[index].role.enumToString}'), ), IconButton( padding: const EdgeInsets.all(0), - onPressed: () { setState(() { _attendees.removeAt(index); }); }, - icon: Icon(Icons.remove_circle, color: Colors.redAccent,), + onPressed: () { + setState(() { + _attendees.removeAt(index); + }); + }, + icon: Icon( + Icons.remove_circle, + color: Colors.redAccent, + ), ) - ] - ) - ) + ], + ), + ), ); }, ), @@ -336,7 +415,10 @@ class _CalendarEventPageState extends State { spacing: 10.0, children: [ Icon(Icons.alarm), - if (_reminders.isEmpty) Text(!_calendar.isReadOnly ? 'Add reminders' : 'Reminders'), + if (_reminders.isEmpty) + Text(!_calendar.isReadOnly + ? 'Add reminders' + : 'Reminders'), for (var reminder in _reminders) Text('${reminder.minutes} minutes before; ') ], @@ -367,7 +449,8 @@ class _CalendarEventPageState extends State { items: RecurrenceFrequency.values .map((frequency) => DropdownMenuItem( value: frequency, - child: _recurrenceFrequencyToText(frequency), + child: + _recurrenceFrequencyToText(frequency), )) .toList(), ), @@ -380,10 +463,11 @@ class _CalendarEventPageState extends State { Flexible( child: TextFormField( initialValue: _interval?.toString() ?? '1', - decoration: const InputDecoration(hintText: '1'), + decoration: + const InputDecoration(hintText: '1'), keyboardType: TextInputType.number, inputFormatters: [ - WhitelistingTextInputFormatter.digitsOnly, + WhitelistingTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(2) ], validator: _validateInterval, @@ -393,17 +477,20 @@ class _CalendarEventPageState extends State { }, ), ), - _recurrenceFrequencyToIntervalText(_recurrenceFrequency), + _recurrenceFrequencyToIntervalText( + _recurrenceFrequency), ], ), ), - if (_recurrenceFrequency == RecurrenceFrequency.Weekly) ... [ + if (_recurrenceFrequency == + RecurrenceFrequency.Weekly) ...[ Column( - children: [ + children: [ ...DayOfWeek.values.map((day) { return CheckboxListTile( title: Text(day.enumToString), - value: _daysOfWeek?.any((dow) => dow == day) ?? false, + value: _daysOfWeek?.any((dow) => dow == day) ?? + false, onChanged: (selected) { setState(() { if (selected) { @@ -419,29 +506,33 @@ class _CalendarEventPageState extends State { Divider(color: Colors.black), ...DayOfWeekGroup.values.map((group) { return RadioListTile( - title: Text(group.enumToString), - value: group, - groupValue: _dayOfWeekGroup, - onChanged: (selected) { - setState(() { - _dayOfWeekGroup = selected; - _updateDaysOfWeek(); - }); - }, - controlAffinity: ListTileControlAffinity.trailing - ); + title: Text(group.enumToString), + value: group, + groupValue: _dayOfWeekGroup, + onChanged: (selected) { + setState(() { + _dayOfWeekGroup = selected; + _updateDaysOfWeek(); + }); + }, + controlAffinity: + ListTileControlAffinity.trailing); }), ], ) ], - if (_recurrenceFrequency == RecurrenceFrequency.Monthly || _recurrenceFrequency == RecurrenceFrequency.Yearly) ...[ + if (_recurrenceFrequency == RecurrenceFrequency.Monthly || + _recurrenceFrequency == + RecurrenceFrequency.Yearly) ...[ SwitchListTile( value: _isByDayOfMonth, - onChanged: (value) => setState(() => _isByDayOfMonth = value), + onChanged: (value) => + setState(() => _isByDayOfMonth = value), title: Text('By day of the month'), ) ], - if (_recurrenceFrequency == RecurrenceFrequency.Yearly && _isByDayOfMonth) ...[ + if (_recurrenceFrequency == RecurrenceFrequency.Yearly && + _isByDayOfMonth) ...[ ListTile( leading: Text('Month of the year'), trailing: DropdownButton( @@ -453,36 +544,50 @@ class _CalendarEventPageState extends State { }, value: _monthOfYear, items: MonthOfYear.values - .map((month) => DropdownMenuItem( - value: month, - child: Text(month.enumToString), - )).toList(), + .map((month) => DropdownMenuItem( + value: month, + child: Text(month.enumToString), + )) + .toList(), ), ), ], - if (_isByDayOfMonth && (_recurrenceFrequency == RecurrenceFrequency.Monthly || _recurrenceFrequency == RecurrenceFrequency.Yearly)) ...[ + if (_isByDayOfMonth && + (_recurrenceFrequency == + RecurrenceFrequency.Monthly || + _recurrenceFrequency == + RecurrenceFrequency.Yearly)) ...[ ListTile( leading: Text('Day of the month'), trailing: DropdownButton( onChanged: (value) { - setState(() { _dayOfMonth = value; }); + setState(() { + _dayOfMonth = value; + }); }, value: _dayOfMonth, items: _validDaysOfMonth - .map((day) => DropdownMenuItem( - value: day, - child: Text(day.toString()), - )).toList(), + .map((day) => DropdownMenuItem( + value: day, + child: Text(day.toString()), + )) + .toList(), ), ), ], - if (!_isByDayOfMonth && (_recurrenceFrequency == RecurrenceFrequency.Monthly || _recurrenceFrequency == RecurrenceFrequency.Yearly)) ...[ + if (!_isByDayOfMonth && + (_recurrenceFrequency == + RecurrenceFrequency.Monthly || + _recurrenceFrequency == + RecurrenceFrequency.Yearly)) ...[ Padding( padding: const EdgeInsets.fromLTRB(15, 10, 15, 10), child: Align( - alignment: Alignment.centerLeft, - child: Text(_recurrenceFrequencyToText(_recurrenceFrequency).data + ' on the ') - ), + alignment: Alignment.centerLeft, + child: Text(_recurrenceFrequencyToText( + _recurrenceFrequency) + .data + + ' on the ')), ), Padding( padding: const EdgeInsets.fromLTRB(15, 0, 15, 10), @@ -491,42 +596,55 @@ class _CalendarEventPageState extends State { children: [ Flexible( child: DropdownButton( - onChanged: (value) { - setState(() { _weekOfMonth = value; }); + onChanged: (value) { + setState(() { + _weekOfMonth = value; + }); }, value: _weekOfMonth ?? WeekNumber.First, items: WeekNumber.values - .map((weekNum) => DropdownMenuItem( - value: weekNum, - child: Text(weekNum.enumToString), - )).toList(), + .map((weekNum) => DropdownMenuItem( + value: weekNum, + child: Text(weekNum.enumToString), + )) + .toList(), ), ), Flexible( child: DropdownButton( - onChanged: (value) { setState(() { _selectedDayOfWeek = value; }); }, - value: DayOfWeek.values[_selectedDayOfWeek.index], + onChanged: (value) { + setState(() { + _selectedDayOfWeek = value; + }); + }, + value: DayOfWeek + .values[_selectedDayOfWeek.index], items: DayOfWeek.values - .map((day) => DropdownMenuItem( - value: day, - child: Text(day.enumToString), - )).toList(), + .map((day) => DropdownMenuItem( + value: day, + child: Text(day.enumToString), + )) + .toList(), ), ), - if (_recurrenceFrequency == RecurrenceFrequency.Yearly) ... [ + if (_recurrenceFrequency == + RecurrenceFrequency.Yearly) ...[ Text('of'), Flexible( child: DropdownButton( onChanged: (value) { - setState(() { _monthOfYear = value; }); + setState(() { + _monthOfYear = value; + }); }, value: _monthOfYear, items: MonthOfYear.values - .map((month) => DropdownMenuItem( - value: month, - child: Text(month.enumToString), - )).toList(), - ), + .map((month) => DropdownMenuItem( + value: month, + child: Text(month.enumToString), + )) + .toList(), + ), ), ] ], @@ -545,12 +663,14 @@ class _CalendarEventPageState extends State { items: RecurrenceRuleEndType.values .map((frequency) => DropdownMenuItem( value: frequency, - child: _recurrenceRuleEndTypeToText(frequency), + child: + _recurrenceRuleEndTypeToText(frequency), )) .toList(), ), ), - if (_recurrenceRuleEndType == RecurrenceRuleEndType.MaxOccurrences) + if (_recurrenceRuleEndType == + RecurrenceRuleEndType.MaxOccurrences) Padding( padding: const EdgeInsets.fromLTRB(15, 0, 15, 10), child: Row( @@ -558,11 +678,13 @@ class _CalendarEventPageState extends State { Text('For the next '), Flexible( child: TextFormField( - initialValue: _totalOccurrences?.toString() ?? '1', - decoration: const InputDecoration(hintText: '1'), + initialValue: + _totalOccurrences?.toString() ?? '1', + decoration: + const InputDecoration(hintText: '1'), keyboardType: TextInputType.number, inputFormatters: [ - WhitelistingTextInputFormatter.digitsOnly, + WhitelistingTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(3), ], validator: _validateTotalOccurrences, @@ -576,7 +698,8 @@ class _CalendarEventPageState extends State { ], ), ), - if (_recurrenceRuleEndType == RecurrenceRuleEndType.SpecifiedEndDate) + if (_recurrenceRuleEndType == + RecurrenceRuleEndType.SpecifiedEndDate) Padding( padding: const EdgeInsets.all(10.0), child: DateTimePicker( @@ -594,7 +717,8 @@ class _CalendarEventPageState extends State { ], ), ), - if (!_calendar.isReadOnly && (_event.eventId?.isNotEmpty ?? false)) ...[ + if (!_calendar.isReadOnly && + (_event.eventId?.isNotEmpty ?? false)) ...[ RaisedButton( key: Key('deleteEventButton'), textColor: Colors.white, @@ -603,16 +727,15 @@ class _CalendarEventPageState extends State { onPressed: () async { var result = true; if (!_isRecurringEvent) { - await _deviceCalendarPlugin.deleteEvent(_calendar.id, _event.eventId); - } - else { + await _deviceCalendarPlugin.deleteEvent( + _calendar.id, _event.eventId); + } else { result = await showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return _recurringEventDialog; - } - ); + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return _recurringEventDialog; + }); } if (result) { @@ -637,19 +760,23 @@ class _CalendarEventPageState extends State { } else { form.save(); if (_isRecurringEvent) { - if (!_isByDayOfMonth && (_recurrenceFrequency == RecurrenceFrequency.Monthly || _recurrenceFrequency == RecurrenceFrequency.Yearly)) { + if (!_isByDayOfMonth && + (_recurrenceFrequency == RecurrenceFrequency.Monthly || + _recurrenceFrequency == RecurrenceFrequency.Yearly)) { // Setting day of the week parameters for WeekNumber to avoid clashing with the weekly recurrence values _daysOfWeek.clear(); _daysOfWeek.add(_selectedDayOfWeek); - } - else { + } else { _weekOfMonth = null; } _event.recurrenceRule = RecurrenceRule(_recurrenceFrequency, interval: _interval, totalOccurrences: _totalOccurrences, - endDate: _recurrenceRuleEndType == RecurrenceRuleEndType.SpecifiedEndDate ? _recurrenceEndDate : null, + endDate: _recurrenceRuleEndType == + RecurrenceRuleEndType.SpecifiedEndDate + ? _recurrenceEndDate + : null, daysOfWeek: _daysOfWeek, dayOfMonth: _dayOfMonth, monthOfYear: _monthOfYear, @@ -668,7 +795,7 @@ class _CalendarEventPageState extends State { }, child: Icon(Icons.check), ), - ) + ), ); } @@ -687,7 +814,8 @@ class _CalendarEventPageState extends State { } } - Text _recurrenceFrequencyToIntervalText(RecurrenceFrequency recurrenceFrequency) { + Text _recurrenceFrequencyToIntervalText( + RecurrenceFrequency recurrenceFrequency) { switch (recurrenceFrequency) { case RecurrenceFrequency.Daily: return Text(' Day(s)'); @@ -723,8 +851,8 @@ class _CalendarEventPageState extends State { // Year frequency: Get total days of the selected month if (frequency == RecurrenceFrequency.Yearly) { totalDays = DateTime(DateTime.now().year, _monthOfYear.value + 1, 0).day; - } - else { // Otherwise, get total days of the current month + } else { + // Otherwise, get total days of the current month var now = DateTime.now(); totalDays = DateTime(now.year, now.month + 1, 0).day; } @@ -758,15 +886,18 @@ class _CalendarEventPageState extends State { _dayOfWeekGroup = DayOfWeekGroup.None; } // If _daysOfWeek contains Monday to Friday - else if (deepEquality(_daysOfWeek, DayOfWeekGroup.Weekday.getDays) && _dayOfWeekGroup != DayOfWeekGroup.Weekday) { + else if (deepEquality(_daysOfWeek, DayOfWeekGroup.Weekday.getDays) && + _dayOfWeekGroup != DayOfWeekGroup.Weekday) { _dayOfWeekGroup = DayOfWeekGroup.Weekday; } // If _daysOfWeek contains Saturday and Sunday - else if (deepEquality(_daysOfWeek, DayOfWeekGroup.Weekend.getDays) && _dayOfWeekGroup != DayOfWeekGroup.Weekend) { + else if (deepEquality(_daysOfWeek, DayOfWeekGroup.Weekend.getDays) && + _dayOfWeekGroup != DayOfWeekGroup.Weekend) { _dayOfWeekGroup = DayOfWeekGroup.Weekend; } // If _daysOfWeek contains all days - else if (deepEquality(_daysOfWeek, DayOfWeekGroup.AllDays.getDays) && _dayOfWeekGroup != DayOfWeekGroup.AllDays) { + else if (deepEquality(_daysOfWeek, DayOfWeekGroup.AllDays.getDays) && + _dayOfWeekGroup != DayOfWeekGroup.AllDays) { _dayOfWeekGroup = DayOfWeekGroup.AllDays; } // Otherwise null diff --git a/device_calendar/example/lib/presentation/pages/calendar_events.dart b/example/lib/presentation/pages/calendar_events.dart similarity index 70% rename from device_calendar/example/lib/presentation/pages/calendar_events.dart rename to example/lib/presentation/pages/calendar_events.dart index 3a52a75d..239e96ea 100644 --- a/device_calendar/example/lib/presentation/pages/calendar_events.dart +++ b/example/lib/presentation/pages/calendar_events.dart @@ -39,32 +39,31 @@ class _CalendarEventsPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - key: _scaffoldstate, - appBar: AppBar(title: Text('${_calendar.name} events')), - body: (_calendarEvents?.isNotEmpty ?? false) - ? Stack( - children: [ - ListView.builder( - itemCount: _calendarEvents?.length ?? 0, - itemBuilder: (BuildContext context, int index) { - return EventItem( - _calendarEvents[index], - _deviceCalendarPlugin, - _onLoading, - _onDeletedFinished, - _onTapped, - _calendar.isReadOnly); - }, - ), - if (_isLoading) - Center( - child: CircularProgressIndicator(), - ) - ], - ) - : Center(child: Text('No events found')), - floatingActionButton: _getAddEventButton(context) - ); + key: _scaffoldstate, + appBar: AppBar(title: Text('${_calendar.name} events')), + body: ((_calendarEvents?.isNotEmpty ?? false) || _isLoading) + ? Stack( + children: [ + ListView.builder( + itemCount: _calendarEvents?.length ?? 0, + itemBuilder: (BuildContext context, int index) { + return EventItem( + _calendarEvents[index], + _deviceCalendarPlugin, + _onLoading, + _onDeletedFinished, + _onTapped, + _calendar.isReadOnly); + }, + ), + if (_isLoading) + Center( + child: CircularProgressIndicator(), + ) + ], + ) + : Center(child: Text('No events found')), + floatingActionButton: _getAddEventButton(context)); } Widget _getAddEventButton(BuildContext context) { @@ -82,8 +81,7 @@ class _CalendarEventsPageState extends State { }, child: Icon(Icons.add), ); - } - else { + } else { return null; } } @@ -98,7 +96,7 @@ class _CalendarEventsPageState extends State { if (deleteSucceeded) { await _retrieveCalendarEvents(); } else { - _scaffoldstate.currentState.showSnackBar(SnackBar( + _scaffoldstate.currentState.showSnackBar(SnackBar( content: Text('Oops, we ran into an issue deleting the event'), backgroundColor: Colors.red, duration: Duration(seconds: 5), @@ -112,13 +110,16 @@ class _CalendarEventsPageState extends State { Future _onTapped(Event event) async { final refreshEvents = await Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) { - return CalendarEventPage(_calendar, event, + return CalendarEventPage( + _calendar, + event, RecurringEventDialog( _deviceCalendarPlugin, event, _onLoading, _onDeletedFinished, - ),); + ), + ); })); if (refreshEvents != null && refreshEvents) { await _retrieveCalendarEvents(); diff --git a/device_calendar/example/lib/presentation/pages/calendars.dart b/example/lib/presentation/pages/calendars.dart similarity index 94% rename from device_calendar/example/lib/presentation/pages/calendars.dart rename to example/lib/presentation/pages/calendars.dart index 807da87a..3d52aa2a 100644 --- a/device_calendar/example/lib/presentation/pages/calendars.dart +++ b/example/lib/presentation/pages/calendars.dart @@ -79,15 +79,15 @@ class _CalendarsPageState extends State { width: 15, height: 15, decoration: BoxDecoration( - shape: BoxShape.circle, - color: Color(_calendars[index].color) - ), + shape: BoxShape.circle, + color: Color(_calendars[index].color)), ), SizedBox(width: 10), Container( margin: const EdgeInsets.fromLTRB(0, 0, 5.0, 0), padding: const EdgeInsets.all(3.0), - decoration: BoxDecoration(border: Border.all(color: Colors.blueAccent)), + decoration: BoxDecoration( + border: Border.all(color: Colors.blueAccent)), child: Text('Default'), ), Icon(_calendars[index].isReadOnly @@ -108,7 +108,7 @@ class _CalendarsPageState extends State { MaterialPageRoute(builder: (BuildContext context) { return CalendarAddPage(); })); - + if (createCalendar == true) { _retrieveCalendars(); } diff --git a/device_calendar/example/lib/presentation/pages/event_attendee.dart b/example/lib/presentation/pages/event_attendee.dart similarity index 60% rename from device_calendar/example/lib/presentation/pages/event_attendee.dart rename to example/lib/presentation/pages/event_attendee.dart index 06b07c9e..7f208e88 100644 --- a/device_calendar/example/lib/presentation/pages/event_attendee.dart +++ b/example/lib/presentation/pages/event_attendee.dart @@ -6,8 +6,7 @@ class EventAttendeePage extends StatefulWidget { const EventAttendeePage({Key key, this.attendee}) : super(key: key); @override - _EventAttendeePageState createState() => - _EventAttendeePageState(attendee); + _EventAttendeePageState createState() => _EventAttendeePageState(attendee); } class _EventAttendeePageState extends State { @@ -37,7 +36,9 @@ class _EventAttendeePageState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text(_attendee != null ? 'Edit attendee ${_attendee.name}' : 'Add an Attendee'), + title: Text(_attendee != null + ? 'Edit attendee ${_attendee.name}' + : 'Add an Attendee'), ), body: Column( children: [ @@ -48,39 +49,51 @@ class _EventAttendeePageState extends State { Padding( padding: const EdgeInsets.all(10.0), child: TextFormField( - controller: _nameController, - validator: (value) { - if (value == null || value.isEmpty) return 'Please enter a name'; - return null; - }, - decoration: const InputDecoration(labelText: 'Name'), - ), + controller: _nameController, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter a name'; + } + return null; + }, + decoration: const InputDecoration(labelText: 'Name'), + ), ), Padding( padding: const EdgeInsets.all(10.0), child: TextFormField( - controller: _emailAddressController, - validator: (value) { - if (value == null || value.isEmpty || !value.contains('@')) return 'Please enter a valid email address'; - return null; - }, - decoration: const InputDecoration(labelText: 'Email Address'), - ), + controller: _emailAddressController, + validator: (value) { + if (value == null || + value.isEmpty || + !value.contains('@')) { + return 'Please enter a valid email address'; + } + return null; + }, + decoration: + const InputDecoration(labelText: 'Email Address'), + ), ), ListTile( leading: Text('Role'), trailing: DropdownButton( - onChanged: (value) { setState(() { _role = value; }); }, + onChanged: (value) { + setState(() { + _role = value; + }); + }, value: _role, items: AttendeeRole.values - .map((role) => DropdownMenuItem( - value: role, - child: Text(role.enumToString), - )) - .toList(), + .map((role) => DropdownMenuItem( + value: role, + child: Text(role.enumToString), + )) + .toList(), ), ), - ]) + ], + ), ), RaisedButton( child: Text(_attendee != null ? 'Update' : 'Add'), diff --git a/device_calendar/example/lib/presentation/pages/event_reminders.dart b/example/lib/presentation/pages/event_reminders.dart similarity index 100% rename from device_calendar/example/lib/presentation/pages/event_reminders.dart rename to example/lib/presentation/pages/event_reminders.dart diff --git a/example/lib/presentation/recurring_event_dialog.dart b/example/lib/presentation/recurring_event_dialog.dart new file mode 100644 index 00000000..d77bddef --- /dev/null +++ b/example/lib/presentation/recurring_event_dialog.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:device_calendar/device_calendar.dart'; + +class RecurringEventDialog extends StatefulWidget { + final DeviceCalendarPlugin _deviceCalendarPlugin; + final Event _calendarEvent; + + final VoidCallback _onLoadingStarted; + final Function(bool) _onDeleteFinished; + + RecurringEventDialog(this._deviceCalendarPlugin, this._calendarEvent, + this._onLoadingStarted, this._onDeleteFinished, + {Key key}) + : super(key: key); + + _RecurringEventDialogState createState() => + _RecurringEventDialogState(_deviceCalendarPlugin, _calendarEvent, + onLoadingStarted: _onLoadingStarted, + onDeleteFinished: _onDeleteFinished); +} + +class _RecurringEventDialogState extends State { + DeviceCalendarPlugin _deviceCalendarPlugin; + Event _calendarEvent; + VoidCallback _onLoadingStarted; + Function(bool) _onDeleteFinished; + + _RecurringEventDialogState( + DeviceCalendarPlugin deviceCalendarPlugin, Event calendarEvent, + {VoidCallback onLoadingStarted, Function(bool) onDeleteFinished}) { + _deviceCalendarPlugin = deviceCalendarPlugin; + _calendarEvent = calendarEvent; + _onLoadingStarted = onLoadingStarted; + _onDeleteFinished = onDeleteFinished; + } + + @override + Widget build(BuildContext context) { + return SimpleDialog( + title: Text('Are you sure you want to delete this event?'), + children: [ + SimpleDialogOption( + child: Text('This instance only'), + onPressed: () async { + Navigator.of(context).pop(true); + _onLoadingStarted(); + final deleteResult = + await _deviceCalendarPlugin.deleteEventInstance( + _calendarEvent.calendarId, + _calendarEvent.eventId, + _calendarEvent.start.millisecondsSinceEpoch, + _calendarEvent.end.millisecondsSinceEpoch, + false); + _onDeleteFinished(deleteResult.isSuccess && deleteResult.data); + }, + ), + SimpleDialogOption( + child: Text('This and following instances'), + onPressed: () async { + Navigator.of(context).pop(true); + _onLoadingStarted(); + final deleteResult = + await _deviceCalendarPlugin.deleteEventInstance( + _calendarEvent.calendarId, + _calendarEvent.eventId, + _calendarEvent.start.millisecondsSinceEpoch, + _calendarEvent.end.millisecondsSinceEpoch, + true); + _onDeleteFinished(deleteResult.isSuccess && deleteResult.data); + }, + ), + SimpleDialogOption( + child: Text('All instances'), + onPressed: () async { + Navigator.of(context).pop(true); + _onLoadingStarted(); + final deleteResult = await _deviceCalendarPlugin.deleteEvent( + _calendarEvent.calendarId, _calendarEvent.eventId); + _onDeleteFinished(deleteResult.isSuccess && deleteResult.data); + }, + ), + SimpleDialogOption( + child: Text('Cancel'), + onPressed: () { + Navigator.of(context).pop(false); + }, + ) + ], + ); + } +} diff --git a/device_calendar/example/pubspec.yaml b/example/pubspec.yaml similarity index 100% rename from device_calendar/example/pubspec.yaml rename to example/pubspec.yaml diff --git a/device_calendar/example/test_driver/app.dart b/example/test_driver/app.dart similarity index 100% rename from device_calendar/example/test_driver/app.dart rename to example/test_driver/app.dart diff --git a/device_calendar/example/test_driver/app_test.dart b/example/test_driver/app_test.dart similarity index 100% rename from device_calendar/example/test_driver/app_test.dart rename to example/test_driver/app_test.dart diff --git a/device_calendar/ios/.gitignore b/ios/.gitignore similarity index 100% rename from device_calendar/ios/.gitignore rename to ios/.gitignore diff --git a/device_calendar/ios/Assets/.gitkeep b/ios/Assets/.gitkeep similarity index 100% rename from device_calendar/ios/Assets/.gitkeep rename to ios/Assets/.gitkeep diff --git a/device_calendar/ios/Classes/DeviceCalendarPlugin.h b/ios/Classes/DeviceCalendarPlugin.h similarity index 100% rename from device_calendar/ios/Classes/DeviceCalendarPlugin.h rename to ios/Classes/DeviceCalendarPlugin.h diff --git a/device_calendar/ios/Classes/DeviceCalendarPlugin.m b/ios/Classes/DeviceCalendarPlugin.m similarity index 100% rename from device_calendar/ios/Classes/DeviceCalendarPlugin.m rename to ios/Classes/DeviceCalendarPlugin.m diff --git a/device_calendar/ios/Classes/SwiftDeviceCalendarPlugin.swift b/ios/Classes/SwiftDeviceCalendarPlugin.swift similarity index 94% rename from device_calendar/ios/Classes/SwiftDeviceCalendarPlugin.swift rename to ios/Classes/SwiftDeviceCalendarPlugin.swift index d802ea4f..4238aa34 100644 --- a/device_calendar/ios/Classes/SwiftDeviceCalendarPlugin.swift +++ b/ios/Classes/SwiftDeviceCalendarPlugin.swift @@ -30,6 +30,7 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { let description: String? let start: Int64 let end: Int64 + let startTimeZone: String? let allDay: Bool let attendees: [Attendee] let location: String? @@ -37,6 +38,7 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { let recurrenceRule: RecurrenceRule? let organizer: Attendee? let reminders: [Reminder] + let availability: Availability? } struct RecurrenceRule: Codable { @@ -61,6 +63,10 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { let minutes: Int } + enum Availability: String, Codable { + case BUSY, FREE + } + static let channelName = "plugins.builttoroam.com/device_calendar" let notFoundErrorCode = "404" let notAllowed = "405" @@ -90,6 +96,7 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { let eventAllDayArgument = "eventAllDay" let eventStartDateArgument = "eventStartDate" let eventEndDateArgument = "eventEndDate" + let eventStartTimeZoneArgument = "eventStartTimeZone" let eventLocationArgument = "eventLocation" let eventURLArgument = "eventURL" let attendeesArgument = "attendees" @@ -109,6 +116,7 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { let followingInstancesArgument = "followingInstances" let calendarNameArgument = "calendarName" let calendarColorArgument = "calendarColor" + let availabilityArgument = "availability" let validFrequencyTypes = [EKRecurrenceFrequency.daily, EKRecurrenceFrequency.weekly, EKRecurrenceFrequency.monthly, EKRecurrenceFrequency.yearly] public static func register(with registrar: FlutterPluginRegistrar) { @@ -294,13 +302,15 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { description: ekEvent.notes, start: Int64(ekEvent.startDate.millisecondsSinceEpoch), end: Int64(ekEvent.endDate.millisecondsSinceEpoch), + startTimeZone: ekEvent.timeZone?.identifier, allDay: ekEvent.isAllDay, attendees: attendees, location: ekEvent.location, url: ekEvent.url?.absoluteString, recurrenceRule: recurrenceRule, organizer: convertEkParticipantToAttendee(ekParticipant: ekEvent.organizer), - reminders: reminders + reminders: reminders, + availability: convertEkEventAvailabilityToString(ekEventAvailability: ekEvent.availability) ) return event } @@ -314,6 +324,17 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { return attendee } + private func convertEkEventAvailabilityToString(ekEventAvailability: EKEventAvailability?) -> Availability? { + switch ekEventAvailability { + case .busy: + return Availability.BUSY + case .free: + return Availability.FREE + default: + return nil + } + } + private func parseEKRecurrenceRules(_ ekEvent: EKEvent) -> RecurrenceRule? { var recurrenceRule: RecurrenceRule? if ekEvent.hasRecurrenceRules { @@ -521,6 +542,19 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { return reminders } + private func setAvailability(_ arguments: [String : AnyObject]) -> EKEventAvailability { + guard let availabilityValue = arguments[availabilityArgument] as? String else { return .busy } + + switch availabilityValue { + case Availability.BUSY.rawValue: + return .busy + case Availability.FREE.rawValue: + return .free + default: + return .busy + } + } + private func createOrUpdateEvent(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { checkPermissionsThenExecute(permissionsGrantedAction: { let arguments = call.arguments as! Dictionary @@ -531,6 +565,7 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { let endDateDateMillisecondsSinceEpoch = arguments[eventEndDateArgument] as! NSNumber let startDate = Date (timeIntervalSince1970: startDateMillisecondsSinceEpoch.doubleValue / 1000.0) let endDate = Date (timeIntervalSince1970: endDateDateMillisecondsSinceEpoch.doubleValue / 1000.0) + let startTimeZoneString = arguments[eventStartTimeZoneArgument] as? String let title = arguments[self.eventTitleArgument] as! String let description = arguments[self.eventDescriptionArgument] as? String let location = arguments[self.eventLocationArgument] as? String @@ -562,7 +597,12 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { ekEvent!.isAllDay = isAllDay ekEvent!.startDate = startDate if (isAllDay) { ekEvent!.endDate = startDate } - else { ekEvent!.endDate = endDate } + else { + ekEvent!.endDate = endDate + + let timeZone = TimeZone(identifier: startTimeZoneString ?? TimeZone.current.identifier) ?? .current + ekEvent!.timeZone = timeZone + } ekEvent!.calendar = ekCalendar! ekEvent!.location = location @@ -574,10 +614,11 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { else { ekEvent!.url = nil } - + ekEvent!.recurrenceRules = createEKRecurrenceRules(arguments) setAttendees(arguments, ekEvent) ekEvent!.alarms = createReminders(arguments) + ekEvent!.availability = setAvailability(arguments) do { try self.eventStore.save(ekEvent!, span: .futureEvents) @@ -732,8 +773,14 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin { } } -extension UIColor { +extension Date { + func convert(from initTimeZone: TimeZone, to targetTimeZone: TimeZone) -> Date { + let delta = TimeInterval(initTimeZone.secondsFromGMT() - targetTimeZone.secondsFromGMT()) + return addingTimeInterval(delta) + } +} +extension UIColor { func rgb() -> Int? { var fRed : CGFloat = 0 var fGreen : CGFloat = 0 diff --git a/device_calendar/ios/device_calendar.podspec b/ios/device_calendar.podspec similarity index 100% rename from device_calendar/ios/device_calendar.podspec rename to ios/device_calendar.podspec diff --git a/device_calendar/lib/device_calendar.dart b/lib/device_calendar.dart similarity index 100% rename from device_calendar/lib/device_calendar.dart rename to lib/device_calendar.dart diff --git a/lib/src/common/calendar_enums.dart b/lib/src/common/calendar_enums.dart new file mode 100644 index 00000000..313b9373 --- /dev/null +++ b/lib/src/common/calendar_enums.dart @@ -0,0 +1,247 @@ +enum DayOfWeek { + Monday, + Tuesday, + Wednesday, + Thursday, + Friday, + Saturday, + Sunday +} + +enum DayOfWeekGroup { None, Weekday, Weekend, AllDays } + +enum MonthOfYear { + January, + Feburary, + March, + April, + May, + June, + July, + August, + September, + October, + November, + December +} + +enum WeekNumber { First, Second, Third, Fourth, Last } + +enum AttendeeRole { None, Required, Optional, Resource } + +// Adding extensions for enum values as Flutter does not support it yet +// TODO: Replace this to enum values when introduced. Ref: https://github.com/dart-lang/language/issues/158 +extension DayOfWeekExtension on DayOfWeek { + static int _value(DayOfWeek val) { + switch (val) { + case DayOfWeek.Monday: + return 1; + case DayOfWeek.Tuesday: + return 2; + case DayOfWeek.Wednesday: + return 3; + case DayOfWeek.Thursday: + return 4; + case DayOfWeek.Friday: + return 5; + case DayOfWeek.Saturday: + return 6; + case DayOfWeek.Sunday: + return 0; + default: + return 1; + } + } + + String _enumToString(DayOfWeek enumValue) { + return enumValue.toString().split('.').last; + } + + int get value => _value(this); + String get enumToString => _enumToString(this); +} + +extension DaysOfWeekGroupExtension on DayOfWeekGroup { + static List _getDays(DayOfWeekGroup val) { + switch (val) { + case DayOfWeekGroup.Weekday: + return [ + DayOfWeek.Monday, + DayOfWeek.Tuesday, + DayOfWeek.Wednesday, + DayOfWeek.Thursday, + DayOfWeek.Friday + ]; + case DayOfWeekGroup.Weekend: + return [DayOfWeek.Saturday, DayOfWeek.Sunday]; + case DayOfWeekGroup.AllDays: + return [ + DayOfWeek.Monday, + DayOfWeek.Tuesday, + DayOfWeek.Wednesday, + DayOfWeek.Thursday, + DayOfWeek.Friday, + DayOfWeek.Saturday, + DayOfWeek.Sunday + ]; + default: + return []; + } + } + + String _enumToString(DayOfWeekGroup enumValue) { + return enumValue.toString().split('.').last; + } + + List get getDays => _getDays(this); + String get enumToString => _enumToString(this); +} + +extension MonthOfYearExtension on MonthOfYear { + static int _value(MonthOfYear val) { + switch (val) { + case MonthOfYear.January: + return 1; + case MonthOfYear.Feburary: + return 2; + case MonthOfYear.March: + return 3; + case MonthOfYear.April: + return 4; + case MonthOfYear.May: + return 5; + case MonthOfYear.June: + return 6; + case MonthOfYear.July: + return 7; + case MonthOfYear.August: + return 8; + case MonthOfYear.September: + return 9; + case MonthOfYear.October: + return 10; + case MonthOfYear.November: + return 11; + case MonthOfYear.December: + return 12; + default: + return 1; + } + } + + String _enumToString(MonthOfYear enumValue) { + return enumValue.toString().split('.').last; + } + + int get value => _value(this); + String get enumToString => _enumToString(this); +} + +extension WeekNumberExtension on WeekNumber { + static int _value(WeekNumber val) { + switch (val) { + case WeekNumber.First: + return 1; + case WeekNumber.Second: + return 2; + case WeekNumber.Third: + return 3; + case WeekNumber.Fourth: + return 4; + case WeekNumber.Last: + return -1; + default: + return 1; + } + } + + String _enumToString(WeekNumber enumValue) { + return enumValue.toString().split('.').last; + } + + int get value => _value(this); + String get enumToString => _enumToString(this); +} + +extension IntExtensions on int { + static DayOfWeek _getDayOfWeekEnumValue(int val) { + switch (val) { + case 1: + return DayOfWeek.Monday; + case 2: + return DayOfWeek.Tuesday; + case 3: + return DayOfWeek.Wednesday; + case 4: + return DayOfWeek.Thursday; + case 5: + return DayOfWeek.Friday; + case 6: + return DayOfWeek.Saturday; + case 0: + return DayOfWeek.Sunday; + default: + return DayOfWeek.Monday; + } + } + + static MonthOfYear _getMonthOfYearEnumValue(int val) { + switch (val) { + case 1: + return MonthOfYear.January; + case 2: + return MonthOfYear.Feburary; + case 3: + return MonthOfYear.March; + case 4: + return MonthOfYear.April; + case 5: + return MonthOfYear.May; + case 6: + return MonthOfYear.June; + case 7: + return MonthOfYear.July; + case 8: + return MonthOfYear.August; + case 9: + return MonthOfYear.September; + case 10: + return MonthOfYear.October; + case 11: + return MonthOfYear.November; + case 12: + return MonthOfYear.December; + default: + return MonthOfYear.January; + } + } + + static WeekNumber _getWeekNumberEnumValue(int val) { + switch (val) { + case 1: + return WeekNumber.First; + case 2: + return WeekNumber.Second; + case 3: + return WeekNumber.Third; + case 4: + return WeekNumber.Fourth; + case -1: + return WeekNumber.Last; + default: + return WeekNumber.First; + } + } + + DayOfWeek get getDayOfWeekEnumValue => _getDayOfWeekEnumValue(this); + MonthOfYear get getMonthOfYearEnumValue => _getMonthOfYearEnumValue(this); + WeekNumber get getWeekNumberEnumValue => _getWeekNumberEnumValue(this); +} + +extension RoleExtensions on AttendeeRole { + String _enumToString(AttendeeRole enumValue) { + return enumValue.toString().split('.').last; + } + + String get enumToString => _enumToString(this); +} diff --git a/device_calendar/lib/src/common/error_codes.dart b/lib/src/common/error_codes.dart similarity index 100% rename from device_calendar/lib/src/common/error_codes.dart rename to lib/src/common/error_codes.dart diff --git a/device_calendar/lib/src/common/error_messages.dart b/lib/src/common/error_messages.dart similarity index 100% rename from device_calendar/lib/src/common/error_messages.dart rename to lib/src/common/error_messages.dart diff --git a/device_calendar/lib/src/common/recurrence_frequency.dart b/lib/src/common/recurrence_frequency.dart similarity index 100% rename from device_calendar/lib/src/common/recurrence_frequency.dart rename to lib/src/common/recurrence_frequency.dart diff --git a/device_calendar/lib/src/device_calendar.dart b/lib/src/device_calendar.dart similarity index 82% rename from device_calendar/lib/src/device_calendar.dart rename to lib/src/device_calendar.dart index 497b8dd7..26e282bb 100644 --- a/device_calendar/lib/src/device_calendar.dart +++ b/lib/src/device_calendar.dart @@ -167,7 +167,8 @@ class DeviceCalendarPlugin { /// The `deleteFollowingInstances` parameter will also delete the following instances if set to true /// /// Returns a [Result] indicating if the instance of the event has (true) or has not (false) been deleted from the calendar - Future> deleteEventInstance(String calendarId, String eventId, int startDate, int endDate, bool deleteFollowingInstances) async { + Future> deleteEventInstance(String calendarId, String eventId, + int startDate, int endDate, bool deleteFollowingInstances) async { final res = Result(); if ((calendarId?.isEmpty ?? true) || (eventId?.isEmpty ?? true)) { @@ -177,14 +178,14 @@ class DeviceCalendarPlugin { } try { - res.data = await channel.invokeMethod('deleteEventInstance', - { - 'calendarId': calendarId, - 'eventId': eventId, - 'eventStartDate': startDate, - 'eventEndDate': endDate, - 'followingInstances': deleteFollowingInstances - }); + res.data = + await channel.invokeMethod('deleteEventInstance', { + 'calendarId': calendarId, + 'eventId': eventId, + 'eventStartDate': startDate, + 'eventEndDate': endDate, + 'followingInstances': deleteFollowingInstances + }); } catch (e) { _parsePlatformExceptionAndUpdateResult(e, res); } @@ -204,36 +205,31 @@ class DeviceCalendarPlugin { // Setting time to 0 for all day events if (event.allDay == true) { - event.start = DateTime(event.start.year, event.start.month, event.start.day, 0, 0, 0); - event.end = DateTime(event.end.year, event.end.month, event.end.day, 0, 0, 0); + event.start = DateTime( + event.start.year, event.start.month, event.start.day, 0, 0, 0); + event.end = + DateTime(event.end.year, event.end.month, event.end.day, 0, 0, 0); } - if (event.allDay == true && (event?.calendarId?.isEmpty ?? true) || event.start == null || event.end == null) { - result.errorMessages.add('[${ErrorCodes.invalidArguments}] ${ErrorMessages.createOrUpdateEventInvalidArgumentsMessageAllDay}'); + if (event.allDay == true && (event?.calendarId?.isEmpty ?? true) || + event.start == null || + event.end == null) { + result.errorMessages.add( + '[${ErrorCodes.invalidArguments}] ${ErrorMessages.createOrUpdateEventInvalidArgumentsMessageAllDay}'); return result; - } - else if (event.allDay != true && ((event?.calendarId?.isEmpty ?? true) || event.start == null || event.end == null || event.start.isAfter(event.end))) { - result.errorMessages.add('[${ErrorCodes.invalidArguments}] ${ErrorMessages.createOrUpdateEventInvalidArgumentsMessage}'); + } else if (event.allDay != true && + ((event?.calendarId?.isEmpty ?? true) || + event.start == null || + event.end == null || + event.start.isAfter(event.end))) { + result.errorMessages.add( + '[${ErrorCodes.invalidArguments}] ${ErrorMessages.createOrUpdateEventInvalidArgumentsMessage}'); return result; } try { result.data = - await channel.invokeMethod('createOrUpdateEvent', { - 'calendarId': event.calendarId, - 'eventId': event.eventId, - 'eventTitle': event.title, - 'eventDescription': event.description, - 'eventLocation': event.location, - 'eventAllDay': event.allDay, - 'eventStartDate': event.start.millisecondsSinceEpoch, - 'eventEndDate': event.end.millisecondsSinceEpoch, - 'eventLocation': event.location, - 'eventURL': event.url?.data?.contentText, - 'recurrenceRule': event.recurrenceRule?.toJson(), - 'attendees': event.attendees?.map((a) => a.toJson())?.toList(), - 'reminders': event.reminders?.map((r) => r.toJson())?.toList() - }); + await channel.invokeMethod('createOrUpdateEvent', event.toJson()); } catch (e) { _parsePlatformExceptionAndUpdateResult(e, result); } @@ -244,7 +240,7 @@ class DeviceCalendarPlugin { /// Creates a new local calendar for the current device. /// /// The `calendarName` parameter is the name of the new calendar\ - /// The `calendarColor` parameter is the color of the calendar. If null, + /// The `calendarColor` parameter is the color of the calendar. If null, /// a default color (red) will be used\ /// The `localAccountName` parameter is the name of the local account: /// - [Android] Required. If `localAccountName` parameter is null or empty, it will default to 'Device Calendar'. @@ -253,23 +249,26 @@ class DeviceCalendarPlugin { /// - [iOS] Not used. A local account will be picked up automatically, if not found, an error will be thrown. /// /// Returns a [Result] with the newly created [Calendar.id] - Future> createCalendar(String calendarName, {Color calendarColor, String localAccountName}) async { + Future> createCalendar(String calendarName, + {Color calendarColor, String localAccountName}) async { final result = Result(); if (calendarName?.isNotEmpty == true) { calendarColor ??= Colors.red; - try { + try { result.data = - await channel.invokeMethod('createCalendar', { - 'calendarName': calendarName, - 'calendarColor': '0x${calendarColor.value.toRadixString(16)}', - 'localAccountName': localAccountName?.isEmpty ?? true ? 'Device Calendar' : localAccountName }); + await channel.invokeMethod('createCalendar', { + 'calendarName': calendarName, + 'calendarColor': '0x${calendarColor.value.toRadixString(16)}', + 'localAccountName': localAccountName?.isEmpty ?? true + ? 'Device Calendar' + : localAccountName + }); } catch (e) { _parsePlatformExceptionAndUpdateResult(e, result); } - } - else { + } else { result.errorMessages.add('Calendar name must not be null or empty'); } diff --git a/device_calendar/lib/src/models/attendee.dart b/lib/src/models/attendee.dart similarity index 83% rename from device_calendar/lib/src/models/attendee.dart rename to lib/src/models/attendee.dart index ec7ae572..37a29a3d 100644 --- a/device_calendar/lib/src/models/attendee.dart +++ b/lib/src/models/attendee.dart @@ -27,13 +27,13 @@ class Attendee { /// When reading details for an existing event, this will only be populated on Android devices. AndroidAttendeeDetails androidAttendeeDetails; - Attendee({ - this.name, - this.emailAddress, - this.role, - this.isOrganiser = false, - this.iosAttendeeDetails, - this.androidAttendeeDetails}); + Attendee( + {this.name, + this.emailAddress, + this.role, + this.isOrganiser = false, + this.iosAttendeeDetails, + this.androidAttendeeDetails}); Attendee.fromJson(Map json) { if (json == null) { @@ -45,7 +45,8 @@ class Attendee { role = AttendeeRole.values[json['role'] ?? 0]; if (Platform.isAndroid) { - isOrganiser = json['isOrganizer']; // Getting and setting an organiser for Android + isOrganiser = + json['isOrganizer']; // Getting and setting an organiser for Android androidAttendeeDetails = AndroidAttendeeDetails.fromJson(json); } @@ -55,7 +56,11 @@ class Attendee { } Map toJson() { - final data = { 'name': name, 'emailAddress': emailAddress, 'role': role?.index }; + final data = { + 'name': name, + 'emailAddress': emailAddress, + 'role': role?.index + }; if (Platform.isIOS && iosAttendeeDetails != null) { data.addEntries(iosAttendeeDetails.toJson().entries); diff --git a/device_calendar/lib/src/models/calendar.dart b/lib/src/models/calendar.dart similarity index 87% rename from device_calendar/lib/src/models/calendar.dart rename to lib/src/models/calendar.dart index 45866dda..c9bddb93 100644 --- a/device_calendar/lib/src/models/calendar.dart +++ b/lib/src/models/calendar.dart @@ -1,4 +1,3 @@ - /// A calendar on the user's device class Calendar { /// Read-only. The unique identifier for this calendar @@ -22,7 +21,14 @@ class Calendar { // Read-only. Account type associated with the calendar String accountType; - Calendar({this.id, this.name, this.isReadOnly, this.isDefault, this.color, this.accountName, this.accountType}); + Calendar( + {this.id, + this.name, + this.isReadOnly, + this.isDefault, + this.color, + this.accountName, + this.accountType}); Calendar.fromJson(Map json) { id = json['id']; diff --git a/device_calendar/lib/src/models/event.dart b/lib/src/models/event.dart similarity index 64% rename from device_calendar/lib/src/models/event.dart rename to lib/src/models/event.dart index 0338a66b..58d5d044 100644 --- a/device_calendar/lib/src/models/event.dart +++ b/lib/src/models/event.dart @@ -8,7 +8,7 @@ class Event { /// Read-only. The unique identifier for this event. This is auto-generated when a new event is created String eventId; - /// The identifier of the calendar that this event is associated with + /// Read-only. The identifier of the calendar that this event is associated with String calendarId; /// The title of this event @@ -23,6 +23,14 @@ class Event { /// Indicates when the event ends DateTime end; + /// Time zone of the event start date\ + /// **Note**: In iOS this will set time zones for both start and end date + String startTimeZone; + + /// Time zone of the event end date\ + /// **Note**: Not used in iOS, only single time zone is used. Please use `startTimeZone` + String endTimeZone; + /// Indicates if this is an all-day event bool allDay; @@ -38,16 +46,24 @@ class Event { /// The recurrence rule for this event RecurrenceRule recurrenceRule; + /// A list of reminders (by minutes) for this event List reminders; + /// If this event counts as busy time or is still free time + String availability; + Event(this.calendarId, {this.eventId, this.title, this.start, this.end, + this.startTimeZone, + this.endTimeZone, this.description, this.attendees, this.recurrenceRule, + this.reminders, + this.availability, this.allDay = false}); Event.fromJson(Map json) { @@ -67,14 +83,15 @@ class Event { if (endMillisecondsSinceEpoch != null) { end = DateTime.fromMillisecondsSinceEpoch(endMillisecondsSinceEpoch); } + startTimeZone = json['startTimeZone']; + endTimeZone = json['endTimeZone']; allDay = json['allDay']; location = json['location']; var foundUrl = json['url']?.toString(); if (foundUrl?.isEmpty ?? true) { url = null; - } - else { + } else { url = Uri.dataFromString(foundUrl); } @@ -86,11 +103,16 @@ class Event { if (json['recurrenceRule'] != null) { recurrenceRule = RecurrenceRule.fromJson(json['recurrenceRule']); } - if (json['organizer'] != null) { // Getting and setting an organiser for iOS + if (json['organizer'] != null) { + // Getting and setting an organiser for iOS var organiser = Attendee.fromJson(json['organizer']); - var attendee = attendees.firstWhere((at) => at.name == organiser.name && at.emailAddress == organiser.emailAddress, orElse: () => null); - if (attendee != null) { + var attendee = attendees.firstWhere( + (at) => + at.name == organiser.name && + at.emailAddress == organiser.emailAddress, + orElse: () => null); + if (attendee != null) { attendee.isOrganiser = true; } } @@ -99,19 +121,25 @@ class Event { return Reminder.fromJson(decodedReminder); }).toList(); } + + availability = json['availability']; + } - // TODO: look at using this method - /* Map toJson() { - final Map data = Map(); - data['eventId'] = eventId; + Map toJson() { + final data = {}; + data['calendarId'] = calendarId; - data['title'] = title; - data['description'] = description; - data['start'] = start.millisecondsSinceEpoch; - data['end'] = end.millisecondsSinceEpoch; - data['allDay'] = allDay; - data['location'] = location; + data['eventId'] = eventId; + data['eventTitle'] = title; + data['eventDescription'] = description; + data['eventStartDate'] = start.millisecondsSinceEpoch; + data['eventEndDate'] = end.millisecondsSinceEpoch; + data['eventStartTimeZone'] = startTimeZone; + data['eventEndTimeZone'] = endTimeZone; + data['eventAllDay'] = allDay; + data['eventLocation'] = location; + data['eventURL'] = url?.data?.contentText; if (attendees != null) { data['attendees'] = attendees.map((a) => a.toJson()).toList(); } @@ -121,6 +149,8 @@ class Event { if (reminders != null) { data['reminders'] = reminders.map((r) => r.toJson()).toList(); } + data['availability'] = availability; + return data; - }*/ + } } diff --git a/device_calendar/lib/src/models/platform_specifics/android/attendance_status.dart b/lib/src/models/platform_specifics/android/attendance_status.dart similarity index 100% rename from device_calendar/lib/src/models/platform_specifics/android/attendance_status.dart rename to lib/src/models/platform_specifics/android/attendance_status.dart diff --git a/device_calendar/lib/src/models/platform_specifics/android/attendee_details.dart b/lib/src/models/platform_specifics/android/attendee_details.dart similarity index 85% rename from device_calendar/lib/src/models/platform_specifics/android/attendee_details.dart rename to lib/src/models/platform_specifics/android/attendee_details.dart index 6ba78df7..61ab585e 100644 --- a/device_calendar/lib/src/models/platform_specifics/android/attendee_details.dart +++ b/lib/src/models/platform_specifics/android/attendee_details.dart @@ -23,11 +23,12 @@ class AndroidAttendeeDetails { role = AttendeeRole.values[json['role']]; if (json['attendanceStatus'] != null && json['attendanceStatus'] is int) { - _attendanceStatus = AndroidAttendanceStatus.values[json['attendanceStatus']]; + _attendanceStatus = + AndroidAttendanceStatus.values[json['attendanceStatus']]; } } Map toJson() { - return { 'role': role?.index }; + return {'role': role?.index}; } } diff --git a/device_calendar/lib/src/models/platform_specifics/ios/attendance_status.dart b/lib/src/models/platform_specifics/ios/attendance_status.dart similarity index 100% rename from device_calendar/lib/src/models/platform_specifics/ios/attendance_status.dart rename to lib/src/models/platform_specifics/ios/attendance_status.dart diff --git a/device_calendar/lib/src/models/platform_specifics/ios/attendee_details.dart b/lib/src/models/platform_specifics/ios/attendee_details.dart similarity index 93% rename from device_calendar/lib/src/models/platform_specifics/ios/attendee_details.dart rename to lib/src/models/platform_specifics/ios/attendee_details.dart index b3675770..a8f72381 100644 --- a/device_calendar/lib/src/models/platform_specifics/ios/attendee_details.dart +++ b/lib/src/models/platform_specifics/ios/attendee_details.dart @@ -4,7 +4,7 @@ import 'attendance_status.dart'; class IosAttendeeDetails { IosAttendanceStatus _attendanceStatus; - + /// An attendee role: None, Optional, Required or Resource AttendeeRole role; @@ -26,6 +26,6 @@ class IosAttendeeDetails { } Map toJson() { - return { 'role': role?.index }; + return {'role': role?.index}; } } diff --git a/device_calendar/lib/src/models/recurrence_rule.dart b/lib/src/models/recurrence_rule.dart similarity index 77% rename from device_calendar/lib/src/models/recurrence_rule.dart rename to lib/src/models/recurrence_rule.dart index 747b0a42..99c6e9fa 100644 --- a/device_calendar/lib/src/models/recurrence_rule.dart +++ b/lib/src/models/recurrence_rule.dart @@ -36,15 +36,16 @@ class RecurrenceRule { final String _monthOfYearKey = 'monthOfYear'; final String _weekOfMonthKey = 'weekOfMonth'; - RecurrenceRule(this.recurrenceFrequency, { - this.totalOccurrences, - this.interval, - this.endDate, - this.daysOfWeek, - this.dayOfMonth, - this.monthOfYear, - this.weekOfMonth}) - : assert(!(endDate != null && totalOccurrences != null), 'Cannot specify both an end date and total occurrences for a recurring event'); + RecurrenceRule(this.recurrenceFrequency, + {this.totalOccurrences, + this.interval, + this.endDate, + this.daysOfWeek, + this.dayOfMonth, + this.monthOfYear, + this.weekOfMonth}) + : assert(!(endDate != null && totalOccurrences != null), + 'Cannot specify both an end date and total occurrences for a recurring event'); RecurrenceRule.fromJson(Map json) { if (json == null) { @@ -52,7 +53,8 @@ class RecurrenceRule { } int recurrenceFrequencyIndex = json[_recurrenceFrequencyKey]; - if (recurrenceFrequencyIndex == null && recurrenceFrequencyIndex >= RecurrenceFrequency.values.length) { + if (recurrenceFrequencyIndex == null && + recurrenceFrequencyIndex >= RecurrenceFrequency.values.length) { throw ArgumentError(ErrorMessages.invalidRecurrencyFrequency); } recurrenceFrequency = RecurrenceFrequency.values[recurrenceFrequencyIndex]; @@ -63,7 +65,8 @@ class RecurrenceRule { int endDateMillisecondsSinceEpoch = json[_endDateKey]; if (endDateMillisecondsSinceEpoch != null) { - endDate = DateTime.fromMillisecondsSinceEpoch(endDateMillisecondsSinceEpoch); + endDate = + DateTime.fromMillisecondsSinceEpoch(endDateMillisecondsSinceEpoch); } List daysOfWeekValues = json[_daysOfWeekKey]; @@ -75,8 +78,10 @@ class RecurrenceRule { } dayOfMonth = json[_dayOfMonthKey]; - monthOfYear = convertDynamicToInt(json[_monthOfYearKey])?.getMonthOfYearEnumValue; - weekOfMonth = convertDynamicToInt(json[_weekOfMonthKey])?.getWeekNumberEnumValue; + monthOfYear = + convertDynamicToInt(json[_monthOfYearKey])?.getMonthOfYearEnumValue; + weekOfMonth = + convertDynamicToInt(json[_weekOfMonthKey])?.getWeekNumberEnumValue; } int convertDynamicToInt(dynamic value) { @@ -105,22 +110,23 @@ class RecurrenceRule { data[_daysOfWeekKey] = daysOfWeek.map((d) => d.value).toList(); } - if (monthOfYear != null && recurrenceFrequency == RecurrenceFrequency.Yearly) { + if (monthOfYear != null && + recurrenceFrequency == RecurrenceFrequency.Yearly) { data[_monthOfYearKey] = monthOfYear.value; } - if (recurrenceFrequency == RecurrenceFrequency.Monthly || recurrenceFrequency == RecurrenceFrequency.Yearly) { + if (recurrenceFrequency == RecurrenceFrequency.Monthly || + recurrenceFrequency == RecurrenceFrequency.Yearly) { if (weekOfMonth != null) { data[_weekOfMonthKey] = weekOfMonth.value; - } - else { + } else { // Days of the month should not be added to the recurrence parameter when WeekOfMonth is used if (dayOfMonth != null) { data[_dayOfMonthKey] = dayOfMonth; } } } - + return data; } } diff --git a/device_calendar/lib/src/models/reminder.dart b/lib/src/models/reminder.dart similarity index 100% rename from device_calendar/lib/src/models/reminder.dart rename to lib/src/models/reminder.dart diff --git a/device_calendar/lib/src/models/result.dart b/lib/src/models/result.dart similarity index 100% rename from device_calendar/lib/src/models/result.dart rename to lib/src/models/result.dart diff --git a/device_calendar/lib/src/models/retrieve_events_params.dart b/lib/src/models/retrieve_events_params.dart similarity index 100% rename from device_calendar/lib/src/models/retrieve_events_params.dart rename to lib/src/models/retrieve_events_params.dart diff --git a/device_calendar/pubspec.yaml b/pubspec.yaml similarity index 87% rename from device_calendar/pubspec.yaml rename to pubspec.yaml index 1d529d14..c1f35d96 100644 --- a/device_calendar/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: device_calendar description: A cross platform plugin for modifying calendars on the user's device. -version: 3.1.0 -homepage: https://github.com/builttoroam/flutter_plugins/tree/master/device_calendar +version: 3.2.0 +homepage: https://github.com/builttoroam/device_calendar/tree/master dependencies: flutter: diff --git a/device_calendar/test/device_calendar_test.dart b/test/device_calendar_test.dart similarity index 100% rename from device_calendar/test/device_calendar_test.dart rename to test/device_calendar_test.dart diff --git a/device_calendar/test_driver/app.dart b/test_driver/app.dart similarity index 100% rename from device_calendar/test_driver/app.dart rename to test_driver/app.dart