Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push a prerelease version #281

Merged
merged 9 commits into from
Mar 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ If you have Proguard enabled, you may need to add the following to your configur

If you want to enable Proguard, please refer to the guide at [Android Developer](https://developer.android.com/studio/build/shrink-code) page

If you consider to publish the app and build a release version you have to enable Proguard. Follow these steps: [here](https://github.com/builttoroam/device_calendar/issues/99#issuecomment-612449677).

**IMPORTANT**: Since version 0.1.0, this version has migrated to use AndroidX instead of the deprecated Android support libraries. When using version 0.10.0 and onwards for this plugin, please ensure your application has been migrated following the guide [here](https://developer.android.com/jetpack/androidx/migrate)

## iOS Integration
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.builttoroam.devicecalendar
package com.builttoroam.devicecalendar

import android.Manifest
import android.annotation.SuppressLint
Expand All @@ -10,6 +10,8 @@ import android.content.pm.PackageManager
import android.database.Cursor
import android.graphics.Color
import android.net.Uri
import android.os.Handler
import android.os.Looper
import android.provider.CalendarContract
import android.provider.CalendarContract.CALLER_IS_SYNCADAPTER
import android.provider.CalendarContract.Events
Expand Down Expand Up @@ -83,6 +85,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
private val CREATE_OR_UPDATE_EVENT_REQUEST_CODE = RETRIEVE_CALENDAR_REQUEST_CODE + 1
private val DELETE_EVENT_REQUEST_CODE = CREATE_OR_UPDATE_EVENT_REQUEST_CODE + 1
private val REQUEST_PERMISSIONS_REQUEST_CODE = DELETE_EVENT_REQUEST_CODE + 1
private val DELETE_CALENDAR_REQUEST_CODE = REQUEST_PERMISSIONS_REQUEST_CODE + 1
private val PART_TEMPLATE = ";%s="
private val BYMONTHDAY_PART = "BYMONTHDAY"
private val BYMONTH_PART = "BYMONTH"
Expand All @@ -93,8 +96,10 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
private var _context: Context? = null
private var _gson: Gson? = null

constructor(activity: Registrar?, context: Context) {
_registrar = activity
private val uiThreadHandler = Handler(Looper.getMainLooper())

constructor(registrar: Registrar?, context: Context) {
_registrar = registrar
_context = context
val gsonBuilder = GsonBuilder()
gsonBuilder.registerTypeAdapter(RecurrenceFrequency::class.java, RecurrenceFrequencySerializer())
Expand Down Expand Up @@ -141,6 +146,9 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
REQUEST_PERMISSIONS_REQUEST_CODE -> {
finishWithSuccess(permissionGranted, cachedValues.pendingChannelResult)
}
DELETE_CALENDAR_REQUEST_CODE -> {
deleteCalendar(cachedValues.calendarId,cachedValues.pendingChannelResult)
}
}

return true
Expand Down Expand Up @@ -236,6 +244,39 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
return null
}

fun deleteCalendar(calendarId: String, pendingChannelResult: MethodChannel.Result, isInternalCall: Boolean = false): Calendar? {
if (isInternalCall || arePermissionsGranted()) {
val calendarIdNumber = calendarId.toLongOrNull()
if (calendarIdNumber == null) {
if (!isInternalCall) {
finishWithError(INVALID_ARGUMENT, CALENDAR_ID_INVALID_ARGUMENT_NOT_A_NUMBER_MESSAGE, pendingChannelResult)
}
return null
}

val contentResolver: ContentResolver? = _context?.contentResolver

val calendar = retrieveCalendar(calendarId,pendingChannelResult,true);
if(calendar != null) {
val calenderUriWithId = ContentUris.withAppendedId(CalendarContract.Calendars.CONTENT_URI, calendarIdNumber)
val deleteSucceeded = contentResolver?.delete(calenderUriWithId, null, null) ?: 0
finishWithSuccess(deleteSucceeded > 0, pendingChannelResult)
}else {
if (!isInternalCall) {
finishWithError(NOT_FOUND, "The calendar with the ID $calendarId could not be found", pendingChannelResult)
}
}
} else {
val parameters = CalendarMethodsParametersCacheModel(
pendingChannelResult = pendingChannelResult,
calendarDelegateMethodCode = DELETE_CALENDAR_REQUEST_CODE,
calendarId = calendarId)
requestPermissions(parameters)
}

return null
}

fun createCalendar(calendarName: String, calendarColor: String?, localAccountName: String, pendingChannelResult: MethodChannel.Result) {
val contentResolver: ContentResolver? = _context?.contentResolver

Expand Down Expand Up @@ -297,7 +338,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
val events: MutableList<Event> = mutableListOf()

val exceptionHandler = CoroutineExceptionHandler { _, exception ->
_registrar!!.activity().runOnUiThread {
uiThreadHandler.post {
finishWithError(GENERIC_ERROR, exception.message, pendingChannelResult)
}
}
Expand All @@ -316,7 +357,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
}.invokeOnCompletion { cause ->
eventsCursor?.close()
if (cause == null) {
_registrar!!.activity().runOnUiThread {
uiThreadHandler.post {
finishWithSuccess(_gson?.toJson(events), pendingChannelResult)
}
}
Expand Down Expand Up @@ -346,7 +387,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
val values = buildEventContentValues(event, calendarId)

val exceptionHandler = CoroutineExceptionHandler { _, exception ->
_registrar!!.activity().runOnUiThread {
uiThreadHandler.post {
finishWithError(GENERIC_ERROR, exception.message, pendingChannelResult)
}
}
Expand Down Expand Up @@ -379,7 +420,7 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
job.invokeOnCompletion {
cause ->
if (cause == null) {
_registrar!!.activity().runOnUiThread {
uiThreadHandler.post {
finishWithSuccess(eventId.toString(), pendingChannelResult)
}
}
Expand Down Expand Up @@ -615,8 +656,8 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {

private fun arePermissionsGranted(): Boolean {
if (atLeastAPI(23)) {
val writeCalendarPermissionGranted = _registrar!!.activity().checkSelfPermission(Manifest.permission.WRITE_CALENDAR) == PackageManager.PERMISSION_GRANTED
val readCalendarPermissionGranted = _registrar!!.activity().checkSelfPermission(Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED
val writeCalendarPermissionGranted = _registrar!!.context().checkSelfPermission(Manifest.permission.WRITE_CALENDAR) == PackageManager.PERMISSION_GRANTED
val readCalendarPermissionGranted = _registrar!!.context().checkSelfPermission(Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_GRANTED
return writeCalendarPermissionGranted && readCalendarPermissionGranted
}

Expand Down Expand Up @@ -926,4 +967,4 @@ class CalendarDelegate : PluginRegistry.RequestPermissionsResultListener {
Events.AVAILABILITY_TENTATIVE -> Availability.TENTATIVE
else -> null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class DeviceCalendarPlugin() : MethodCallHandler {
private val DELETE_EVENT_INSTANCE_METHOD = "deleteEventInstance"
private val CREATE_OR_UPDATE_EVENT_METHOD = "createOrUpdateEvent"
private val CREATE_CALENDAR_METHOD = "createCalendar"
private val DELETE_CALENDAR_METHOD = "deleteCalendar"

// Method arguments
private val CALENDAR_ID_ARGUMENT = "calendarId"
Expand Down Expand Up @@ -130,6 +131,10 @@ class DeviceCalendarPlugin() : MethodCallHandler {

_calendarDelegate.createCalendar(calendarName!!, calendarColor, localAccountName!!, result)
}
DELETE_CALENDAR_METHOD -> {
val calendarId = call.argument<String>(CALENDAR_ID_ARGUMENT)
_calendarDelegate.deleteCalendar(calendarId!!,result)
}
else -> {
result.notImplemented()
}
Expand Down
1 change: 1 addition & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ stages:
displayName: 'Flutter build - Android'
inputs:
target: 'aab'
buildNumber: $(Build.BuildID)
projectDirectory: 'example'

- task: FlutterBuild@0
Expand Down
2 changes: 1 addition & 1 deletion example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

android {
compileSdkVersion 29
ndkVersion '21.3.6528147'
ndkVersion '22.0.7026061'

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
Expand Down
50 changes: 49 additions & 1 deletion example/lib/presentation/pages/calendar_events.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ class _CalendarEventsPageState extends State<CalendarEventsPage> {
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldstate,
appBar: AppBar(title: Text('${_calendar.name} events')),
appBar: AppBar(title: Text('${_calendar.name} events'),actions: [
_getDeleteButton()
],),
body: ((_calendarEvents?.isNotEmpty ?? false) || _isLoading)
? Stack(
children: [
Expand Down Expand Up @@ -137,4 +139,50 @@ class _CalendarEventsPageState extends State<CalendarEventsPage> {
_isLoading = false;
});
}

Widget _getDeleteButton() {
return IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
_showDeleteDialog();
});
}

Future<void> _showDeleteDialog() async {
return showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('Warning'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('This will delete this calendar'),
Text('Are you sure?'),
],
),
),
actions: <Widget>[
TextButton(
child: Text('Delete!'),
onPressed: () async {
var returnValue = await _deviceCalendarPlugin.deleteCalendar(_calendar.id);
print("returnValue: ${returnValue.data}, ${returnValue.errors}");
Navigator.of(context).pop();
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
}


11 changes: 11 additions & 0 deletions example/lib/presentation/pages/calendars.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class _CalendarsPageState extends State<CalendarsPage> {
return Scaffold(
appBar: AppBar(
title: Text('Calendars'),
actions: [
_getRefreshButton()
],
),
body: Column(
children: [
Expand Down Expand Up @@ -136,4 +139,12 @@ class _CalendarsPageState extends State<CalendarsPage> {
print(e);
}
}

Widget _getRefreshButton() {
return IconButton(
icon: Icon(Icons.refresh),
onPressed: () async {
_retrieveCalendars();
});
}
}
29 changes: 29 additions & 0 deletions ios/Classes/SwiftDeviceCalendarPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin {
let retrieveSourcesMethod = "retrieveSources"
let createOrUpdateEventMethod = "createOrUpdateEvent"
let createCalendarMethod = "createCalendar"
let deleteCalendarMethod = "deleteCalendar"
let deleteEventMethod = "deleteEvent"
let deleteEventInstanceMethod = "deleteEventInstance"
let calendarIdArgument = "calendarId"
Expand Down Expand Up @@ -146,6 +147,8 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin {
deleteEvent(call, result)
case createCalendarMethod:
createCalendar(call, result)
case deleteCalendarMethod:
deleteCalendar(call, result)
default:
result(FlutterMethodNotImplemented)
}
Expand Down Expand Up @@ -209,6 +212,32 @@ public class SwiftDeviceCalendarPlugin: NSObject, FlutterPlugin {
}, result: result)
}

private func deleteCalendar(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) {
checkPermissionsThenExecute(permissionsGrantedAction: {
let arguments = call.arguments as! Dictionary<String, AnyObject>
let calendarId = arguments[calendarIdArgument] as! String

let ekCalendar = self.eventStore.calendar(withIdentifier: calendarId)
if ekCalendar == nil {
self.finishWithCalendarNotFoundError(result: result, calendarId: calendarId)
return
}

if !(ekCalendar!.allowsContentModifications) {
self.finishWithCalendarReadOnlyError(result: result, calendarId: calendarId)
return
}

do {
try self.eventStore.removeCalendar(ekCalendar!, commit: true)
result(true)
} catch {
self.eventStore.reset()
result(FlutterError(code: self.genericError, message: error.localizedDescription, details: nil))
}
}, result: result)
}

private func getAccountType(_ sourceType: EKSourceType) -> String {
switch (sourceType) {
case .local:
Expand Down
1 change: 1 addition & 0 deletions lib/src/common/channel_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ class ChannelConstants {
static const String methodNameDeleteEventInstance = 'deleteEventInstance';
static const String methodNameCreateOrUpdateEvent = 'createOrUpdateEvent';
static const String methodNameCreateCalendar = 'createCalendar';
static const String methodNameDeleteCalendar = 'deleteCalendar';

static const String parameterNameCalendarId = 'calendarId';
static const String parameterNameStartDate = 'startDate';
Expand Down
20 changes: 20 additions & 0 deletions lib/src/device_calendar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,26 @@ class DeviceCalendarPlugin {
);
}

/// Deletes a calendar.
/// The `calendarId` parameter is the id of the calendar that plugin will try to delete the event from\///
/// Returns a [Result] indicating if the instance of the calendar has (true) or has not (false) been deleted
Future<Result<bool>> deleteCalendar(
String calendarId,
) async {
return _invokeChannelMethod(
ChannelConstants.methodNameDeleteCalendar,
assertParameters: (result) {
_validateCalendarIdParameter(
result,
calendarId,
);
},
arguments: () => <String, Object>{
ChannelConstants.parameterNameCalendarId: calendarId,
},
);
}

Future<Result<T>> _invokeChannelMethod<T>(
String channelMethodName, {
Function(Result<T>) assertParameters,
Expand Down
2 changes: 0 additions & 2 deletions lib/src/models/event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,6 @@ class Event {
return Reminder.fromJson(decodedReminder);
}).toList();
}

availability = json['availability'];
}

Map<String, dynamic> toJson() {
Expand Down