Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

Merge [Readonly] Calendar API #1384

Closed
wants to merge 17 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
<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.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Expand Down
103 changes: 103 additions & 0 deletions DeviceTests/DeviceTests.Shared/Calendar_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Xunit;

namespace DeviceTests
{
// TEST NOTES:
// - a human needs to accept permissions on all systems
// If no calendars are set up none will be returned at this stage
// Same goes for events
public class Calendar_Tests
{
[Fact]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Calendar_List()
{
return Utils.OnMainThread(async () =>
{
var calendarList = await Calendars.GetCalendarsAsync();
Assert.NotNull(calendarList);
});
}

[Fact]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Events_List()
{
return Utils.OnMainThread(async () =>
{
var eventList = await Calendars.GetEventsAsync();
Assert.NotNull(eventList);
});
}

[Theory]
[InlineData("ThisIsAFakeId")]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Events_By_Bad_Calendar_Text_Id(string calendarId)
{
return Utils.OnMainThread(async () =>
{
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => Calendars.GetEventsAsync(calendarId));
});
}

[Theory]
[InlineData("-1")]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Events_By_Bad_Calendar_Id(string calendarId)
{
return Utils.OnMainThread(async () =>
{
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => Calendars.GetEventsAsync(calendarId));
});
}

[Theory]
[InlineData("")]
[InlineData(" ")]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Event_By_Blank_Id(string eventId)
{
return Utils.OnMainThread(async () =>
{
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => Calendars.GetEventAsync(eventId));
});
}

[Theory]
[InlineData(null)]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Event_By_Null_Id(string eventId)
{
return Utils.OnMainThread(async () =>
{
await Assert.ThrowsAsync<ArgumentNullException>(() => Calendars.GetEventAsync(eventId));
});
}

[Theory]
[InlineData("-1")]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Event_By_Bad_Id(string eventId)
{
return Utils.OnMainThread(async () =>
{
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => Calendars.GetEventAsync(eventId));
});
}

[Theory]
[InlineData("ThisIsAFakeId")]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Event_By_Bad_Text_Id(string eventId)
{
return Utils.OnMainThread(async () =>
{
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => Calendars.GetEventAsync(eventId));
});
}
}
}
1 change: 1 addition & 0 deletions DeviceTests/DeviceTests.UWP/Package.appxmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<Capability Name="internetClient" />
<Capability Name="internetClientServer" />
<Capability Name="privateNetworkClientServer" />
<uap:Capability Name="appointments"/>
<DeviceCapability Name="location" />
</Capabilities>
</Package>
2 changes: 2 additions & 0 deletions DeviceTests/DeviceTests.iOS/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,7 @@
<string>1.0.1.0</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Access to your location is required for cool things to happen!</string>
<key>NSCalendarsUsageDescription</key>
<string>Access to view and create calendar events</string>
</dict>
</plist>
1 change: 1 addition & 0 deletions Samples/Samples.Android/Properties/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<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-feature android:name="android.hardware.location" android:required="false" />
Expand Down
6 changes: 5 additions & 1 deletion Samples/Samples.Mac/Entitlements.plist
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.personal-information.calendar</key>
<true/>
</dict>
</plist>
2 changes: 2 additions & 0 deletions Samples/Samples.Mac/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
<string>Assets.xcassets/AppIcon.appiconset</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Access to your location is required for cool things to happen!</string>
<key>NSCalendarsUsageDescription</key>
<string>Calendar Access</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand Down
1 change: 1 addition & 0 deletions Samples/Samples.UWP/Package.appxmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
<Capabilities>
<Capability Name="internetClient" />
<uap:Capability Name="contacts"/>
<uap:Capability Name="appointments"/>
<DeviceCapability Name="location" />
<DeviceCapability Name="microphone"/>
<DeviceCapability Name="webcam"/>
Expand Down
2 changes: 2 additions & 0 deletions Samples/Samples.iOS/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@
<string>1.0</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Access to your location is required for cool things to happen!</string>
<key>NSCalendarsUsageDescription</key>
<string>Access to view and create calendar events</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
Expand Down
12 changes: 10 additions & 2 deletions Samples/Samples/App.xaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converters="clr-namespace:Samples.Converters"
x:Class="Samples.App">

<Application.Resources>
<ResourceDictionary>

<x:String x:Key="TimeSpanDisplayFormatter">{0:h\:mm}</x:String>
<x:String x:Key="DateDisplayFormatter">{0:dd/MM/yy hh:mm tt}</x:String>

<converters:NegativeConverter x:Key="NegativeConverter" />
<converters:CalendarEventDateConverter x:Key="CalendarEventDateConverter" />

<Style TargetType="Page" ApplyToDerivedTypes="True">
<Setter Property="Visual" Value="Material" />
</Style>

</ResourceDictionary>
</Application.Resources>

</Application>
12 changes: 8 additions & 4 deletions Samples/Samples/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public App()

MainPage = new NavigationPage(new HomePage());

AppActions.OnAppAction += AppActions_OnAppAction;
if (Device.RuntimePlatform != Device.macOS)
AppActions.OnAppAction += AppActions_OnAppAction;
}

protected override async void OnStart()
Expand All @@ -48,9 +49,12 @@ protected override async void OnStart()
typeof(Distribute));
}

await AppActions.SetAsync(
new AppAction("app_info", "App Info", icon: "app_info_action_icon"),
new AppAction("battery_info", "Battery Info"));
if (Device.RuntimePlatform != Device.macOS)
{
await AppActions.SetAsync(
new AppAction("app_info", "App Info", icon: "app_info_action_icon"),
new AppAction("battery_info", "Battery Info"));
}
}

void AppActions_OnAppAction(object sender, AppActionEventArgs e)
Expand Down
27 changes: 27 additions & 0 deletions Samples/Samples/Converters/CalendarEventDateConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Globalization;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Samples.Converters
{
public class CalendarEventDateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var e = value as CalendarEvent;
if (e == null)
return "<unknown>";

if (e.AllDay)
return "All day";

var format = Application.Current.Resources["DateDisplayFormatter"] as string;

return $"{string.Format(format, e.StartDate)} to {string.Format(format, e.EndDate)}";
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
throw new NotImplementedException();
}
}
48 changes: 48 additions & 0 deletions Samples/Samples/View/CalendarEventPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<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.CalendarEventPage"
x:DataType="essentials:CalendarEvent"
Title="{Binding Title}">

<ScrollView>
<StackLayout Padding="12" Spacing="6">

<Label Text="{Binding Title}" FontSize="Title" HorizontalTextAlignment="Center" />

<Grid ColumnDefinitions="Auto,*" RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto,Auto"
ColumnSpacing="6" RowSpacing="6">
<Label Grid.Column="0" Grid.Row="0" Text="When: " FontAttributes="Bold" />
<Label Grid.Column="1" Grid.Row="0" Text="{Binding ., Converter={StaticResource CalendarEventDateConverter}}" />

<Label Grid.Column="0" Grid.Row="1" Text="Duration: " FontAttributes="Bold" />
<Label Grid.Column="1" Grid.Row="1" Text="{Binding Duration}" />

<Label Grid.Column="0" Grid.Row="2" Text="Location: " FontAttributes="Bold" />
<Label Grid.Column="1" Grid.Row="2" Text="{Binding Location}" />

<Label Grid.Column="0" Grid.Row="3" Text="Attendees: " FontAttributes="Bold" />
<ScrollView Grid.Column="1" Grid.Row="3">
<StackLayout Spacing="3" BindableLayout.ItemsSource="{Binding Attendees}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="essentials:CalendarEventAttendee">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" />
<Label Text="{Binding Email}" TextColor="DarkGray"/>
</StackLayout>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</ScrollView>

<Label Grid.Column="0" Grid.Row="4" Text="Description: " FontAttributes="Bold" />
<Label Grid.Column="1" Grid.Row="4" Text="{Binding Description}" />
</Grid>

</StackLayout>
</ScrollView>

</views:BasePage>
15 changes: 15 additions & 0 deletions Samples/Samples/View/CalendarEventPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System;
using System.Collections.ObjectModel;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Samples.View
{
public partial class CalendarEventPage : BasePage
{
public CalendarEventPage()
{
InitializeComponent();
}
}
}
Loading