Skip to content

Commit

Permalink
Write support for calendars
Browse files Browse the repository at this point in the history
* Squash Merge (#72)

* #67 Use int.TryParse instead of Convert.ToInt32

* #59 rename extensions to more accurately represent their purpose

* #61 replace usages of .Count == 0 with !.Any()

* #62 rename cursor to something more meaningful

* #66 Remove not needed Deleted flag from create+update event

Also don't try to set up a null event.

* #68 - Avoid abbreviations where possible

* whoops, missed a change.

* Changes adhere to PR feedback

* PR Feedback - Replace strings with const values for ease of readability and to remove confusion around + numbers

* PR Feedback allow recurrence rules to be removed for existing events.

* #87 ios: delete calendar event instance

* #82 drop down works for setting check boxes, but need to work out other way.

* Undo permission testing code

* Meeting PR Feedback

* Make CalendarList readonly

* Rename TimeSpanExtensions more accurately --> remove unnessacary extension RoundToNearestMinutes

* Started Converting to bit flag

* Update to get checkboxes functioning using bitflag

* Alter according to PR requirements

* CalendarDayOfWeek enum/bitflag working with UI and each platform

* #89 Added Ability to SetEventRecurrenceEndDate

which allows stopping event recurrences going from a specific date (end recurrence rule from date)

* Remove blank line causing error.

* Alter Recurrences TotalOccurrences to be null so that end date dictates the rule changes

* Alter strings to use consts for consistency/readability

* Altered picker to be 4 checkboxes (with custom no longer being an option)

Also merged my two enums into one using condition values to determine results.

* Alter as per PR feedback

* Remove un-necessary using statement

* Altered to PR feedback

* Altered to PR feedback

* Convert RecurrenceDays to readonly property

* Alter ForLoop to use bitflag enum checks rather than relying on int bit logic override

* Remove IsNone, IsWeekday, IsWeekly, IsAllDays --> Converter

* Alter IsDaily || Is Weekly || IsMonthlyOrYearly || IsYearly --> RecurrenceTypeBoolConverter

* Reoganize file and remove redundancies

* missed merge change.

* Resolve conflicts.

* Prevent android from breaking onappearing for calendarpage

Co-authored-by: Nick Randolph <[email protected]>
  • Loading branch information
ScottBTR and nickrandolph committed Sep 28, 2020
1 parent 625ce0f commit 9cf6e78
Show file tree
Hide file tree
Showing 35 changed files with 4,540 additions and 435 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
Expand Down
390 changes: 390 additions & 0 deletions DeviceTests/DeviceTests.Shared/Calendar_Tests.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Samples/Samples.Android/Properties/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
<uses-feature android:name="android.hardware.location.network" android:required="false" />
Expand Down
32 changes: 32 additions & 0 deletions Samples/Samples/Converters/AttendeeRequiredColorConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Samples.Converters
{
public class AttendeeRequiredColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(value is AttendeeType attendeeType))
{
return Color.PaleVioletRed;
}

switch (attendeeType)
{
case AttendeeType.Required:
return Color.LightGoldenrodYellow;
case AttendeeType.Resource:
return Color.PaleGreen;
default:
return Color.LightGray;
}
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
}
33 changes: 33 additions & 0 deletions Samples/Samples/Converters/RecurrenceDayToBoolConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using Samples.ViewModel;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Samples.Converters
{
public class RecurrenceDayToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is CalendarDayOfWeek recurrenceDays) || !(parameter is CalendarDayOfWeek expectedResult))
{
return false;
}

return recurrenceDays == expectedResult;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is bool switchValue) || !(parameter is CalendarDayOfWeek expectedResult) || switchValue == false)
{
return null;
}

return expectedResult;
}
}
}
32 changes: 32 additions & 0 deletions Samples/Samples/Converters/RecurrenceEndTypeToBoolConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Globalization;
using Samples.ViewModel;
using Xamarin.Forms;

namespace Samples.Converters
{
public class RecurrenceEndTypeToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is string type))
{
return false;
}
switch (type)
{
case RecurrenceEndType.AfterOccurences:
return true;
case RecurrenceEndType.UntilEndDate:
case RecurrenceEndType.Indefinitely:
default:
return false;
}
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
86 changes: 86 additions & 0 deletions Samples/Samples/Converters/RecurrenceRuleTextConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Samples.Converters
{
public class RecurrenceRuleTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is RecurrenceRule rule))
return null;

var toReturn = $"Occurs ";

if (rule.Interval > 0)
{
if (rule.Interval == 1)
{
toReturn += $"Every ";
}
else
{
toReturn += $"Every {((int)rule.Interval).ToOrdinal()} ";
}
switch (rule.Frequency)
{
case RecurrenceFrequency.Daily:
toReturn += "Day ";
break;
case RecurrenceFrequency.Weekly:
toReturn += "Week ";
break;
case RecurrenceFrequency.Monthly:
case RecurrenceFrequency.MonthlyOnDay:
toReturn += "Month ";
break;
case RecurrenceFrequency.Yearly:
case RecurrenceFrequency.YearlyOnDay:
toReturn += "Year ";
break;
default:
throw new ArgumentOutOfRangeException();
}
}

if (rule.WeekOfMonth != null && (rule.Frequency == RecurrenceFrequency.MonthlyOnDay || rule.Frequency == RecurrenceFrequency.YearlyOnDay))
{
toReturn += $"on the {rule.WeekOfMonth} ";
if (rule.DaysOfTheWeek?.Count > 0)
{
toReturn += $"[";
toReturn = rule.DaysOfTheWeek.Aggregate(toReturn, (current, d) => current + $"{d}, ");
toReturn = toReturn.Substring(0, toReturn.Length - 2) + "] ";
}
if (rule.Frequency == RecurrenceFrequency.YearlyOnDay)
{
toReturn += $"in {rule.MonthOfTheYear.ToString()} ";
}
}
else if (rule.DaysOfTheWeek?.Count > 0)
{
toReturn += $"On: [";
toReturn = rule.DaysOfTheWeek.Aggregate(toReturn, (current, d) => current + $"{d}, ");
toReturn = toReturn.Substring(0, toReturn.Length - 2) + "] ";
}

if (rule.TotalOccurrences > 0)
{
toReturn += $"For the next {rule.TotalOccurrences} occurrences ";
}

if (rule.EndDate.HasValue)
{
toReturn += $"Until {rule.EndDate.Value.DateTime.ToShortDateString()} ";
}

return toReturn;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
}
45 changes: 45 additions & 0 deletions Samples/Samples/Converters/RecurrenceTypeToBoolConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using Samples.ViewModel;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Samples.Converters
{
public class RecurrenceTypeToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is RecurrenceFrequency recurrenceType) || !(parameter is RecurrenceFrequency expectedResult))
{
return false;
}

switch (expectedResult)
{
case RecurrenceFrequency.Monthly:
return recurrenceType == RecurrenceFrequency.MonthlyOnDay ||
recurrenceType == RecurrenceFrequency.Monthly ||
recurrenceType == RecurrenceFrequency.YearlyOnDay ||
recurrenceType == RecurrenceFrequency.Yearly;
case RecurrenceFrequency.Yearly:
return recurrenceType == RecurrenceFrequency.Yearly ||
recurrenceType == RecurrenceFrequency.YearlyOnDay;
default:
return recurrenceType == expectedResult;
}
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is bool switchValue) || !(parameter is RecurrenceFrequency expectedResult) || switchValue == false)
{
return false;
}

return expectedResult;
}
}
}
32 changes: 32 additions & 0 deletions Samples/Samples/Converters/RecurrenceUntilTypeToBoolConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Globalization;
using Samples.ViewModel;
using Xamarin.Forms;

namespace Samples.Converters
{
public class RecurrenceUntilTypeToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is string type))
{
return false;
}
switch (type)
{
case RecurrenceEndType.UntilEndDate:
return true;
case RecurrenceEndType.Indefinitely:
case RecurrenceEndType.AfterOccurences:
default:
return false;
}
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
19 changes: 19 additions & 0 deletions Samples/Samples/Converters/ReminderTextConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System;
using System.Globalization;
using Xamarin.Forms;

namespace Samples.Converters
{
public class ReminderTextConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is int minutes))
return null;

return minutes > 0 ? $"{minutes} minutes prior" : "No Reminders";
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
}
20 changes: 20 additions & 0 deletions Samples/Samples/Converters/StartWidthDisplayConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Globalization;
using Xamarin.Forms;

namespace Samples.Converters
{
public class StartWidthDisplayConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || !(value is bool b))
{
return 1;
}
return b ? 3 : 1;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
}
31 changes: 31 additions & 0 deletions Samples/Samples/View/CalendarAddPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<views:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Samples.View"
xmlns:viewmodels="clr-namespace:Samples.ViewModel"
xmlns:essentials="clr-namespace:Xamarin.Essentials;assembly=Xamarin.Essentials"
xmlns:converters="clr-namespace:Samples.Converters;assembly=Samples"
x:Class="Samples.View.CalendarAddPage"
x:DataType="viewmodels:CalendarAddViewModel"
Title="CalendarAddPage"
Padding="20,20,20,20">
<views:BasePage.Content>
<StackLayout>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Text="Title"
HorizontalTextAlignment="Center"
Grid.Column="0"/>
<Entry Placeholder="Title..."
Text="{Binding CalendarName}"
Grid.Column="1"/>
</Grid>
<Button Text="Add Calendar"
Command="{Binding CreateCalendar}"
IsEnabled="{Binding CanCreateCalendar}"/>
</StackLayout>
</views:BasePage.Content>
</views:BasePage>
10 changes: 10 additions & 0 deletions Samples/Samples/View/CalendarAddPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Samples.View
{
public partial class CalendarAddPage : BasePage
{
public CalendarAddPage()
{
InitializeComponent();
}
}
}
Loading

0 comments on commit 9cf6e78

Please sign in to comment.