Skip to content

Commit

Permalink
#371 Add Android to Caliburn.Micro.Platform
Browse files Browse the repository at this point in the history
  • Loading branch information
nigel-sampson committed Jun 4, 2017
1 parent 85e61d8 commit f73b606
Show file tree
Hide file tree
Showing 7 changed files with 509 additions and 69 deletions.
28 changes: 27 additions & 1 deletion src/Caliburn.Micro.Platform/Caliburn.Micro.Platform.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net45;uap10.0</TargetFrameworks>
<TargetFrameworks>net45;uap10.0;MonoAndroid10</TargetFrameworks>
<AssemblyName>Caliburn.Micro.Platform</AssemblyName>
<RootNamespace>Caliburn.Micro</RootNamespace>
</PropertyGroup>
Expand All @@ -17,6 +17,14 @@
<TargetPlatformMinVersion>10.0.10240.0</TargetPlatformMinVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)' == 'MonoAndroid10'">
<DefineConstants>ANDROID</DefineConstants>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>True</AndroidUseLatestPlatformSdk>
<DevInstrumentationEnabled>True</DevInstrumentationEnabled>
<TargetFrameworkVersion>v6.0</TargetFrameworkVersion>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Platforms\**\*.*" />
<None Include="Platforms\**\*.*" />
Expand All @@ -39,6 +47,24 @@
<PackageReference Include="Microsoft.Xaml.Behaviors.Uwp.Managed" Version="2.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'MonoAndroid10'">
<Compile Remove="*.cs" />
<Compile Include="Platforms\Android\*.cs" />
<Compile Include="ViewModelLocator.cs" />
<Reference Include="Mono.Android" />
<Reference Include="System" />
<Reference Include="System.Collections" />
<Reference Include="System.Threading.Tasks" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime" />
<Reference Include="System.Threading.Tasks" />
<Reference Include="System.IdentityModel" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="MSBuild.Sdk.Extras" Version="1.0.2" />
</ItemGroup>
Expand Down
28 changes: 28 additions & 0 deletions src/Caliburn.Micro.Platform/Platforms/Android/ActivityEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using Android.App;

namespace Caliburn.Micro
{
/// <summary>
/// Arguments for activity events
/// </summary>
public class ActivityEventArgs : EventArgs
{
private readonly Activity activity;

/// <summary>
/// Creates a new ActivityEventArgs.
/// </summary>
/// <param name="activity">The activity this event corresponds to.</param>
public ActivityEventArgs(Activity activity) {
this.activity = activity;
}

/// <summary>
/// The activity this event corresponds to.
/// </summary>
public Activity Activity {
get { return activity; }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
using System;
using Android.App;
using Android.OS;

namespace Caliburn.Micro
{
/// <summary>
/// Handles callbacks for the activity lifecycle and exposes them as events
/// </summary>
public class ActivityLifecycleCallbackHandler : Java.Lang.Object, Application.IActivityLifecycleCallbacks {

/// <summary>
/// Invoked when an activity is created
/// </summary>
public event EventHandler<ActivityEventArgs> ActivityCreated = delegate { };

/// <summary>
/// Invoked when an acitivty is destroyed
/// </summary>
public event EventHandler<ActivityEventArgs> ActivityDestoryed = delegate { };

/// <summary>
/// Invoked when an acitivty is paused
/// </summary>
public event EventHandler<ActivityEventArgs> ActivityPaused = delegate { };

/// <summary>
/// Invoked when an acitivty is resumed
/// </summary>
public event EventHandler<ActivityEventArgs> ActivityResumed = delegate { };

/// <summary>
/// Invoked when an acitities instance state is saved
/// </summary>
public event EventHandler<ActivityEventArgs> ActivitySaveInstanceState = delegate { };

/// <summary>
/// Invoked when an activity is started
/// </summary>
public event EventHandler<ActivityEventArgs> ActivityStarted = delegate { };

/// <summary>
/// Invoked when an activity is stopped
/// </summary>
public event EventHandler<ActivityEventArgs> ActivityStopped = delegate { };

/// <summary>
/// Invokes the ActivityCreated event
/// </summary>
/// <param name="activity">The activity</param>
/// <param name="savedInstanceState">The saved instance state</param>
public void OnActivityCreated(Activity activity, Bundle savedInstanceState)
{
ActivityCreated(this, new ActivityEventArgs(activity));
}

/// <summary>
/// Invokes the ActivityDestroyed event
/// </summary>
/// <param name="activity">The activity</param>
public void OnActivityDestroyed(Activity activity)
{
ActivityDestoryed(this, new ActivityEventArgs(activity));
}

/// <summary>
/// Invokes the ActivityPaused event
/// </summary>
/// <param name="activity">The activity</param>
public void OnActivityPaused(Activity activity)
{
ActivityPaused(this, new ActivityEventArgs(activity));
}

/// <summary>
/// Invokes the ActivityResumed event
/// </summary>
/// <param name="activity">The activity</param>
public void OnActivityResumed(Activity activity)
{
ActivityResumed(this, new ActivityEventArgs(activity));
}

/// <summary>
/// Invokes the ActivitySaveInstanceState event
/// </summary>
/// <param name="activity">The activity</param>
/// <param name="outState">The output state</param>
public void OnActivitySaveInstanceState(Activity activity, Bundle outState)
{
ActivitySaveInstanceState(this, new ActivityEventArgs(activity));
}

/// <summary>
/// Invokes the ActivityStarted event
/// </summary>
/// <param name="activity">The activity</param>
public void OnActivityStarted(Activity activity)
{
ActivityStarted(this, new ActivityEventArgs(activity));
}

/// <summary>
/// Invokes the ActivityStopped event
/// </summary>
/// <param name="activity">The activity</param>
public void OnActivityStopped(Activity activity)
{
ActivityStopped(this, new ActivityEventArgs(activity));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Android.App;

namespace Caliburn.Micro
{
/// <summary>
/// A <see cref="IPlatformProvider"/> implementation for the Xamarin Android platfrom.
/// </summary>
public class AndroidPlatformProvider : IPlatformProvider
{
private readonly ActivityLifecycleCallbackHandler lifecycleHandler = new ActivityLifecycleCallbackHandler();

/// <summary>
/// Creates an instance of <see cref="AndroidPlatformProvider"/>.
/// </summary>
/// <param name="application">The Android Application</param>
public AndroidPlatformProvider(Application application) {
application.RegisterActivityLifecycleCallbacks(lifecycleHandler);
}


private bool CheckAccess() {
return SynchronizationContext.Current != null;
}

/// <summary>
/// Indicates whether or not the framework is in design-time mode.
/// </summary>
public bool InDesignMode {
get { return false; }
}

/// <summary>
/// Executes the action on the UI thread asynchronously.
/// </summary>
/// <param name="action">The action to execute.</param>
public void BeginOnUIThread(Action action) {

Application.SynchronizationContext.Post(s => action(), null);
}

/// <summary>
/// Executes the action on the UI thread asynchronously.
/// </summary>
/// <param name = "action">The action to execute.</param>
public Task OnUIThreadAsync(Action action) {

var completionSource = new TaskCompletionSource<bool>();

Application.SynchronizationContext.Post(s => {
try {
action();
completionSource.SetResult(true);
}
catch (TaskCanceledException) {
completionSource.SetCanceled();
}
catch (Exception ex) {
completionSource.SetException(ex);
}
}, null);


return completionSource.Task;
}

/// <summary>
/// Executes the action on the UI thread.
/// </summary>
/// <param name = "action">The action to execute.</param>
public void OnUIThread(Action action) {

if (CheckAccess())
action();
else
OnUIThreadAsync(action).Wait();
}

/// <summary>
/// Used to retrieve the root, non-framework-created view.
/// </summary>
/// <param name="view">The view to search.</param>
/// <returns>The root element that was not created by the framework.</returns>
/// <remarks>In certain instances the services create UI elements.
/// For example, if you ask the window manager to show a UserControl as a dialog, it creates a window to host the UserControl in.
/// The WindowManager marks that element as a framework-created element so that it can determine what it created vs. what was intended by the developer.
/// Calling GetFirstNonGeneratedView allows the framework to discover what the original element was.
/// </remarks>
public object GetFirstNonGeneratedView(object view) {
return view;
}

/// <summary>
/// Executes the handler the fist time the view is loaded.
/// </summary>
/// <param name="view">The view.</param>
/// <param name="handler">The handler.</param>
public void ExecuteOnFirstLoad(object view, Action<object> handler) {

var activity = view as Activity;

if (activity != null) {

EventHandler<ActivityEventArgs> created = null;

created = (s, e) => {
if (e.Activity != activity)
return;
lifecycleHandler.ActivityCreated -= created;
handler(view);
};

lifecycleHandler.ActivityCreated += created;
}

}

/// <summary>
/// Executes the handler the next time the view's LayoutUpdated event fires.
/// </summary>
/// <param name="view">The view.</param>
/// <param name="handler">The handler.</param>
public void ExecuteOnLayoutUpdated(object view, Action<object> handler) {
var activity = view as Activity;

if (activity != null)
{
EventHandler<ActivityEventArgs> resumed = null;

resumed = (s, e) =>
{
if (e.Activity != activity)
return;
lifecycleHandler.ActivityResumed -= resumed;
handler(view);
};

lifecycleHandler.ActivityResumed += resumed;
}
}

/// <summary>
/// Get the close action for the specified view model.
/// </summary>
/// <param name="viewModel">The view model to close.</param>
/// <param name="views">The associated views.</param>
/// <param name="dialogResult">The dialog result.</param>
/// <returns>An <see cref="Action"/> to close the view model.</returns>
public Action GetViewCloseAction(object viewModel, ICollection<object> views, bool? dialogResult) {

var child = viewModel as IChild;

if (child != null) {
var conductor = child.Parent as IConductor;

if (conductor != null) {
return () => conductor.CloseItem(viewModel);
}
}

return () => LogManager.GetLog(typeof(Screen)).Info("TryClose requires a parent IConductor or a view with a Close method or IsOpen property.");
}
}
}
Loading

0 comments on commit f73b606

Please sign in to comment.