Skip to content
Permalink
Browse files

feature: Create the ReactiveUI.AndroidX package (#2174)

  • Loading branch information...
glennawatson committed Sep 21, 2019
1 parent ad57904 commit 39564170f936cfb75aff5360cf3fa72b3895df25
@@ -23,6 +23,7 @@ var packageWhitelist = new List<FilePath>
MakeAbsolute(File("./src/ReactiveUI.Fody/ReactiveUI.Fody.csproj")),
MakeAbsolute(File("./src/ReactiveUI.Fody.Helpers/ReactiveUI.Fody.Helpers.csproj")),
MakeAbsolute(File("./src/ReactiveUI.AndroidSupport/ReactiveUI.AndroidSupport.csproj")),
MakeAbsolute(File("./src/ReactiveUI.AndroidX/ReactiveUI.AndroidX.csproj")),
MakeAbsolute(File("./src/ReactiveUI.XamForms/ReactiveUI.XamForms.csproj")),
MakeAbsolute(File("./src/ReactiveUI.Uno/ReactiveUI.Uno.csproj")),
MakeAbsolute(File("./src/ReactiveUI.Blazor/ReactiveUI.Blazor.csproj")),
@@ -41,7 +41,7 @@
</PropertyGroup>

<ItemGroup Condition="$(IsTestProject)">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.console" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
@@ -4,6 +4,7 @@
<PropertyGroup>
<Product>$(AssemblyName) ($(TargetFramework))</Product>
<AndroidUseIntermediateDesignerFile>False</AndroidUseIntermediateDesignerFile>
<EnableVSTestReferences>false</EnableVSTestReferences>
</PropertyGroup>

<PropertyGroup Condition="$(TargetFramework.StartsWith('netstandard'))">
@@ -0,0 +1,54 @@
// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using Android.Views;
using static ReactiveUI.ControlFetcherMixin;
using Fragment = AndroidX.Fragment.App.Fragment;

namespace ReactiveUI.AndroidX
{
/// <summary>
/// ControlFetcherMixin helps you automatically wire-up Activities and
/// Fragments via property names, similar to Butter Knife, as well as allows
/// you to fetch controls manually.
/// </summary>
public static class ControlFetcherMixin
{
/// <summary>
/// Wires a control to a property.
/// This should be called in the Fragment's OnCreateView, with the newly inflated layout.
/// </summary>
/// <param name="fragment">The fragment.</param>
/// <param name="inflatedView">The inflated view.</param>
/// <param name="resolveMembers">The resolve members.</param>
public static void WireUpControls(this Fragment fragment, View inflatedView, ResolveStrategy resolveMembers = ResolveStrategy.Implicit)
{
if (fragment == null)
{
throw new ArgumentNullException(nameof(fragment));
}

var members = fragment.GetWireUpMembers(resolveMembers);

foreach (var member in members)
{
try
{
// Find the android control with the same name from the view
var view = inflatedView.GetControl(fragment.GetType().Assembly, member.GetResourceName());

// Set the activity field's value to the view with that identifier
member.SetValue(fragment, view);
}
catch (Exception ex)
{
throw new
MissingFieldException("Failed to wire up the Property " + member.Name + " to a View in your layout with a corresponding identifier", ex);
}
}
}
}
}
@@ -0,0 +1,221 @@
// Copyright (c) 2019 .NET Foundation and Contributors. All rights reserved.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for full license information.

using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reactive;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using System.Reactive.Threading.Tasks;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Runtime;
using AndroidX.AppCompat.App;

namespace ReactiveUI.AndroidX
{
/// <summary>
/// This is an Activity that is both an Activity and has ReactiveObject powers
/// (i.e. you can call RaiseAndSetIfChanged).
/// </summary>
/// <typeparam name="TViewModel">The view model type.</typeparam>
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "Classes with the same class names within.")]
public class ReactiveAppCompatActivity<TViewModel> : ReactiveAppCompatActivity, IViewFor<TViewModel>, ICanActivate
where TViewModel : class
{
private TViewModel _viewModel;

/// <summary>
/// Initializes a new instance of the <see cref="ReactiveAppCompatActivity{TViewModel}"/> class.
/// </summary>
protected ReactiveAppCompatActivity()
{
}

/// <inheritdoc/>
public TViewModel ViewModel
{
get => _viewModel;
set => this.RaiseAndSetIfChanged(ref _viewModel, value);
}

/// <inheritdoc/>
object IViewFor.ViewModel
{
get => _viewModel;
set => _viewModel = (TViewModel)value;
}
}

/// <summary>
/// This is an Activity that is both an Activity and has ReactiveObject powers
/// (i.e. you can call RaiseAndSetIfChanged).
/// </summary>
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:FileMayOnlyContainASingleType", Justification = "Classes with the same class names within.")]
public class ReactiveAppCompatActivity : AppCompatActivity, IReactiveObject, IReactiveNotifyPropertyChanged<ReactiveAppCompatActivity>, IHandleObservableErrors
{
private readonly Subject<Unit> _activated = new Subject<Unit>();
private readonly Subject<Unit> _deactivated = new Subject<Unit>();
private readonly Subject<(int requestCode, Result result, Intent intent)> _activityResult = new Subject<(int requestCode, Result result, Intent intent)>();

/// <summary>
/// Initializes a new instance of the <see cref="ReactiveAppCompatActivity" /> class.
/// </summary>
protected ReactiveAppCompatActivity()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="ReactiveAppCompatActivity" /> class.
/// </summary>
/// <param name="handle">The handle.</param>
/// <param name="ownership">The ownership.</param>
protected ReactiveAppCompatActivity(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}

/// <inheritdoc/>
public event PropertyChangingEventHandler PropertyChanging
{
add => PropertyChangingEventManager.AddHandler(this, value);
remove => PropertyChangingEventManager.RemoveHandler(this, value);
}

/// <inheritdoc/>
public event PropertyChangedEventHandler PropertyChanged
{
add => PropertyChangedEventManager.AddHandler(this, value);
remove => PropertyChangedEventManager.RemoveHandler(this, value);
}

/// <inheritdoc/>
public IObservable<IReactivePropertyChangedEventArgs<ReactiveAppCompatActivity>> Changing => this.GetChangingObservable();

/// <inheritdoc/>
public IObservable<IReactivePropertyChangedEventArgs<ReactiveAppCompatActivity>> Changed => this.GetChangedObservable();

/// <inheritdoc/>
public IObservable<Exception> ThrownExceptions => this.GetThrownExceptionsObservable();

/// <summary>
/// Gets a signal when activated.
/// </summary>
/// <value>
/// The activated.
/// </value>
public IObservable<Unit> Activated => _activated.AsObservable();

/// <summary>
/// Gets a signal when deactivated.
/// </summary>
/// <value>
/// The deactivated.
/// </value>
public IObservable<Unit> Deactivated => _deactivated.AsObservable();

/// <summary>
/// Gets the activity result.
/// </summary>
/// <value>
/// The activity result.
/// </value>
public IObservable<(int requestCode, Result result, Intent intent)> ActivityResult => _activityResult.AsObservable();

/// <inheritdoc/>
void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args)
{
PropertyChangingEventManager.DeliverEvent(this, args);
}

/// <inheritdoc/>
void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args)
{
PropertyChangedEventManager.DeliverEvent(this, args);
}

/// <inheritdoc/>
public IDisposable SuppressChangeNotifications()
{
return IReactiveObjectExtensions.SuppressChangeNotifications(this);
}

/// <summary>
/// Starts the activity for result asynchronously.
/// </summary>
/// <param name="intent">The intent.</param>
/// <param name="requestCode">The request code.</param>
/// <returns>A task with the result and intent.</returns>
public Task<(Result result, Intent intent)> StartActivityForResultAsync(Intent intent, int requestCode)
{
// NB: It's important that we set up the subscription *before* we
// call ActivityForResult
var ret = ActivityResult
.Where(x => x.requestCode == requestCode)
.Select(x => (x.result, x.intent))
.FirstAsync()
.ToTask();

StartActivityForResult(intent, requestCode);
return ret;
}

/// <summary>
/// Starts the activity for result asynchronously.
/// </summary>
/// <param name="type">The type.</param>
/// <param name="requestCode">The request code.</param>
/// <returns>A task with the result and intent.</returns>
public Task<(Result result, Intent intent)> StartActivityForResultAsync(Type type, int requestCode)
{
// NB: It's important that we set up the subscription *before* we
// call ActivityForResult
var ret = ActivityResult
.Where(x => x.requestCode == requestCode)
.Select(x => (x.result, x.intent))
.FirstAsync()
.ToTask();

StartActivityForResult(type, requestCode);
return ret;
}

/// <inheritdoc/>
protected override void OnPause()
{
base.OnPause();
_deactivated.OnNext(Unit.Default);
}

/// <inheritdoc/>
protected override void OnResume()
{
base.OnResume();
_activated.OnNext(Unit.Default);
}

/// <inheritdoc/>
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
_activityResult.OnNext((requestCode, resultCode, data));
}

/// <inheritdoc/>
protected override void Dispose(bool disposing)
{
if (disposing)
{
_activated?.Dispose();
_deactivated?.Dispose();
_activityResult?.Dispose();
}

base.Dispose(disposing);
}
}
}

0 comments on commit 3956417

Please sign in to comment.
You can’t perform that action at this time.