-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feature Add AndroidX with new packages (#3889)
<!-- Please be sure to read the [Contribute](https://github.com/reactiveui/reactiveui#contribute) section of the README --> **What kind of change does this PR introduce?** <!-- Bug fix, feature, docs update, ... --> Feature **What is the current behavior?** <!-- You can also link to an open issue here. --> #3851 **What is the new behavior?** <!-- If this is a feature change --> Functionality ported from the Mono Android TFM version that went out of support. Only supports Net 8.0 plus with the latest packages released on 29th August 2024 **What might this PR break?** No support for older targets if upgrading from the legacy package. **Please check if the PR fulfills these requirements** - [ ] Tests for the changes have been added (for bug fixes / features) - [ ] Docs have been added / updated (for bug fixes / features) **Other information**:
- Loading branch information
1 parent
60b9d48
commit de5f3fe
Showing
24 changed files
with
1,343 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright (c) 2024 .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 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) | ||
{ | ||
ArgumentNullException.ThrowIfNull(fragment); | ||
|
||
foreach (var member in fragment.GetWireUpMembers(resolveMembers)) | ||
{ | ||
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); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright (c) 2024 .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. | ||
|
||
global using global::Splat; | ||
global using global::System; | ||
global using global::System.Collections.Generic; | ||
global using global::System.ComponentModel; | ||
global using global::System.Diagnostics.CodeAnalysis; | ||
global using global::System.Linq; | ||
global using global::System.Reactive; | ||
global using global::System.Reactive.Linq; | ||
global using global::System.Reactive.Subjects; | ||
global using global::System.Reactive.Threading.Tasks; | ||
global using global::System.Threading.Tasks; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
// Copyright (c) 2024 .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 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> | ||
public class ReactiveAppCompatActivity : AppCompatActivity, IReactiveObject, IReactiveNotifyPropertyChanged<ReactiveAppCompatActivity>, IHandleObservableErrors | ||
{ | ||
private readonly Subject<Unit> _activated = new(); | ||
private readonly Subject<Unit> _deactivated = new(); | ||
private readonly Subject<(int requestCode, Result result, Intent? intent)> _activityResult = new(); | ||
|
||
/// <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(in IntPtr handle, JniHandleOwnership ownership) | ||
: base(handle, ownership) | ||
{ | ||
} | ||
|
||
/// <inheritdoc/> | ||
public event PropertyChangingEventHandler? PropertyChanging; | ||
|
||
/// <inheritdoc/> | ||
public event PropertyChangedEventHandler? PropertyChanged; | ||
|
||
/// <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) => PropertyChanging?.Invoke(this, args); | ||
|
||
/// <inheritdoc/> | ||
void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args); | ||
|
||
/// <inheritdoc/> | ||
public IDisposable SuppressChangeNotifications() => 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); | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
src/ReactiveUI.AndroidX/ReactiveAppCompatActivity{TViewModel}.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Copyright (c) 2024 .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. | ||
|
||
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> | ||
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
// Copyright (c) 2024 .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. | ||
|
||
namespace ReactiveUI.AndroidX; | ||
|
||
/// <summary> | ||
/// This is a Fragment that is both an Activity and has ReactiveObject powers | ||
/// (i.e. you can call RaiseAndSetIfChanged). | ||
/// </summary> | ||
public class ReactiveDialogFragment : global::AndroidX.Fragment.App.DialogFragment, IReactiveNotifyPropertyChanged<ReactiveDialogFragment>, IReactiveObject, IHandleObservableErrors | ||
{ | ||
private readonly Subject<Unit> _activated = new(); | ||
private readonly Subject<Unit> _deactivated = new(); | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ReactiveDialogFragment"/> class. | ||
/// </summary> | ||
protected ReactiveDialogFragment() | ||
{ | ||
} | ||
|
||
/// <inheritdoc/> | ||
public event PropertyChangingEventHandler? PropertyChanging; | ||
|
||
/// <inheritdoc/> | ||
public event PropertyChangedEventHandler? PropertyChanged; | ||
|
||
/// <inheritdoc/> | ||
public IObservable<Exception> ThrownExceptions => this.GetThrownExceptionsObservable(); | ||
|
||
/// <summary> | ||
/// Gets a observable that signals when the fragment is activated. | ||
/// </summary> | ||
public IObservable<Unit> Activated => _activated.AsObservable(); | ||
|
||
/// <summary> | ||
/// Gets a observable that signals when the fragment is deactivated. | ||
/// </summary> | ||
public IObservable<Unit> Deactivated => _deactivated.AsObservable(); | ||
|
||
/// <inheritdoc /> | ||
public IObservable<IReactivePropertyChangedEventArgs<ReactiveDialogFragment>> Changing => this.GetChangingObservable(); | ||
|
||
/// <inheritdoc/> | ||
public IObservable<IReactivePropertyChangedEventArgs<ReactiveDialogFragment>> Changed => this.GetChangedObservable(); | ||
|
||
/// <inheritdoc/> | ||
void IReactiveObject.RaisePropertyChanging(PropertyChangingEventArgs args) => PropertyChanging?.Invoke(this, args); | ||
|
||
/// <inheritdoc/> | ||
void IReactiveObject.RaisePropertyChanged(PropertyChangedEventArgs args) => PropertyChanged?.Invoke(this, args); | ||
|
||
/// <inheritdoc /> | ||
public IDisposable SuppressChangeNotifications() => IReactiveObjectExtensions.SuppressChangeNotifications(this); | ||
|
||
/// <inheritdoc/> | ||
public override void OnPause() | ||
{ | ||
base.OnPause(); | ||
_deactivated.OnNext(Unit.Default); | ||
} | ||
|
||
/// <inheritdoc/> | ||
public override void OnResume() | ||
{ | ||
base.OnResume(); | ||
_activated.OnNext(Unit.Default); | ||
} | ||
|
||
/// <inheritdoc/> | ||
protected override void Dispose(bool disposing) | ||
{ | ||
if (disposing) | ||
{ | ||
_activated.Dispose(); | ||
_deactivated.Dispose(); | ||
} | ||
|
||
base.Dispose(disposing); | ||
} | ||
} |
Oops, something went wrong.