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

Calendars Read-Only API #1010

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +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.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" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:theme="@style/MainTheme"></application>
</manifest>
100 changes: 100 additions & 0 deletions DeviceTests/DeviceTests.Shared/Calendar_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
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 () =>
{
#if __ANDROID__
await Assert.ThrowsAsync<ArgumentException>(() => Calendars.GetEventsAsync(calendarId));
#else
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => Calendars.GetEventsAsync(calendarId));
#endif
});
}

[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(null)]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Event_By_Blank_Id(string eventId)
{
return Utils.OnMainThread(async () =>
{
await Assert.ThrowsAsync<ArgumentException>(() => Calendars.GetEventByIdAsync(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.GetEventByIdAsync(eventId));
});
}

[Theory]
[InlineData("ThisIsAFakeId")]
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public Task Get_Event_By_Bad_Text_Id(string eventId)
{
return Utils.OnMainThread(async () =>
{
#if __ANDROID__
await Assert.ThrowsAsync<ArgumentException>(() => Calendars.GetEventByIdAsync(eventId));
#else
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(() => Calendars.GetEventByIdAsync(eventId));
#endif
});
}
}
}
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>
4 changes: 3 additions & 1 deletion DeviceTests/DeviceTests.iOS/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
Expand Down 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 @@ -8,6 +8,7 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
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 @@ -24,6 +24,7 @@
</Applications>
<Capabilities>
<Capability Name="internetClient" />
<uap:Capability Name="appointments"/>
<DeviceCapability Name="location" />
</Capabilities>
</Package>
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
2 changes: 1 addition & 1 deletion Samples/Samples.iOS/Samples.iOS.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="Main.cs" />
<Compile Include="AppDelegate.cs" />
<None Include="Entitlements.plist" />
Expand Down
1 change: 1 addition & 0 deletions Samples/Samples/App.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
x:Class="Samples.App">
<Application.Resources>
<ResourceDictionary>
<x:String x:Key="DateDisplayFormatter">{0:dd/MM/yy hh:mm tt}</x:String>
<converters:NegativeConverter x:Key="NegativeConverter" />
<Style TargetType="Page" ApplyToDerivedTypes="True">
<Setter Property="Visual" Value="Material" />
Expand Down
45 changes: 45 additions & 0 deletions Samples/Samples/Behaviors/BehaviorBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// This code is a slightly modified version of the Xamarin-Forms-sample/Behaviors code provided here:
// https://github.com/xamarin/xamarin-forms-samples/tree/master/Behaviors/EventToCommandBehavior/EventToCommandBehavior/Behaviors

using System;
using Xamarin.Forms;

namespace Samples.Behaviors
{
public class BehaviorBase<T> : Behavior<T>
where T : BindableObject
{
public T AssociatedObject { get; private set; }

protected override void OnAttachedTo(T bindable)
{
base.OnAttachedTo(bindable);
AssociatedObject = bindable;

if (bindable.BindingContext != null)
{
BindingContext = bindable.BindingContext;
}

bindable.BindingContextChanged += OnBindingContextChanged;
}

protected override void OnDetachingFrom(T bindable)
{
base.OnDetachingFrom(bindable);
bindable.BindingContextChanged -= OnBindingContextChanged;
AssociatedObject = null;
}

void OnBindingContextChanged(object sender, EventArgs e)
{
OnBindingContextChanged();
}

protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
BindingContext = AssociatedObject.BindingContext;
}
}
}
135 changes: 135 additions & 0 deletions Samples/Samples/Behaviors/EventToCommandBehaviour.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// This code is a slightly modified version of the Xamarin-Forms-sample/Behaviors code provided here:
// https://github.com/xamarin/xamarin-forms-samples/tree/master/Behaviors/EventToCommandBehavior/EventToCommandBehavior/Behaviors

using System;
using System.Reflection;
using System.Windows.Input;
using Xamarin.Forms;

namespace Samples.Behaviors
{
public class EventToCommandBehavior : BehaviorBase<Xamarin.Forms.View>
{
Delegate eventHandler;

public static readonly BindableProperty EventNameProperty = BindableProperty.Create(nameof(EventName), typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
public static readonly BindableProperty CommandProperty = BindableProperty.Create(nameof(Command), typeof(ICommand), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(nameof(CommandParameter), typeof(object), typeof(EventToCommandBehavior), null);
public static readonly BindableProperty InputConverterProperty = BindableProperty.Create(nameof(Converter), typeof(IValueConverter), typeof(EventToCommandBehavior), null);

static void OnEventNameChanged(BindableObject bindable, object oldValue, object newValue)
{
var behavior = (EventToCommandBehavior)bindable;
if (behavior.AssociatedObject == null)
{
return;
}

var oldEventName = (string)oldValue;
var newEventName = (string)newValue;

behavior.DeregisterEvent(oldEventName);
behavior.RegisterEvent(newEventName);
}

public string EventName
{
get => (string)GetValue(EventNameProperty);
set => SetValue(EventNameProperty, value);
}

public ICommand Command
{
get => (ICommand)GetValue(CommandProperty);
set => SetValue(CommandProperty, value);
}

public object CommandParameter
{
get => GetValue(CommandParameterProperty);
set => SetValue(CommandParameterProperty, value);
}

public IValueConverter Converter
{
get => (IValueConverter)GetValue(InputConverterProperty);
set => SetValue(InputConverterProperty, value);
}

protected override void OnAttachedTo(Xamarin.Forms.View bindable)
{
base.OnAttachedTo(bindable);
RegisterEvent(EventName);
}

protected override void OnDetachingFrom(Xamarin.Forms.View bindable)
{
DeregisterEvent(EventName);
base.OnDetachingFrom(bindable);
}

void RegisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}

var eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't register the '{0}' event.", EventName));
}
var methodInfo = typeof(EventToCommandBehavior).GetTypeInfo().GetDeclaredMethod("OnEvent");
eventHandler = methodInfo.CreateDelegate(eventInfo.EventHandlerType, this);
eventInfo.AddEventHandler(AssociatedObject, eventHandler);
}

void DeregisterEvent(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return;
}

if (eventHandler == null)
{
return;
}
var eventInfo = AssociatedObject.GetType().GetRuntimeEvent(name);
if (eventInfo == null)
{
throw new ArgumentException(string.Format("EventToCommandBehavior: Can't de-register the '{0}' event.", EventName));
}
eventInfo.RemoveEventHandler(AssociatedObject, eventHandler);
eventHandler = null;
}

void OnEvent(object sender, object eventArgs)
{
if (Command == null)
{
return;
}

object resolvedParameter;
if (CommandParameter != null)
{
resolvedParameter = CommandParameter;
}
else if (Converter != null)
{
resolvedParameter = Converter.Convert(eventArgs, typeof(object), null, null);
}
else
{
resolvedParameter = eventArgs;
}

if (Command.CanExecute(resolvedParameter))
{
Command.Execute(resolvedParameter);
}
}
}
}
23 changes: 23 additions & 0 deletions Samples/Samples/Converters/CheckBoxEventArgsConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Xamarin.Forms;

namespace Samples.Converters
{
public class CheckBoxEventArgsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var eventArgs = value as CheckedChangedEventArgs;

if (eventArgs == null)
return value;

return eventArgs.Value;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
}
}
Loading