Skip to content

Commit 94e2767

Browse files
Add ICameraProvider.AvailableCamerasChanged (#2906)
* Add `ICameraProvider.AvailableCamerasChanged` * Update MockCameraProvider * Add Unit Tests * Add Unit Tests
1 parent 3c77945 commit 94e2767

File tree

6 files changed

+692
-10
lines changed

6 files changed

+692
-10
lines changed

samples/CommunityToolkit.Maui.Sample/ViewModels/Views/CameraView/CameraViewViewModel.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
using CommunityToolkit.Maui.Core;
2-
using CommunityToolkit.Maui.Core.Primitives;
32
using CommunityToolkit.Mvvm.ComponentModel;
43
using CommunityToolkit.Mvvm.Input;
54

65
namespace CommunityToolkit.Maui.Sample.ViewModels.Views;
76

8-
public partial class CameraViewViewModel(ICameraProvider cameraProvider) : BaseViewModel
7+
public partial class CameraViewViewModel : BaseViewModel
98
{
10-
readonly ICameraProvider cameraProvider = cameraProvider;
9+
readonly ICameraProvider cameraProvider;
10+
11+
public CameraViewViewModel(ICameraProvider cameraProvider)
12+
{
13+
this.cameraProvider = cameraProvider;
14+
15+
cameraProvider.AvailableCamerasChanged += HandleAvailableCamerasChanged;
16+
}
1117

1218
public IReadOnlyList<CameraInfo> Cameras => cameraProvider.AvailableCameras ?? [];
1319

@@ -78,4 +84,9 @@ void UpdateResolutionText()
7884
{
7985
ResolutionText = $"Selected Resolution: {SelectedResolution.Width} x {SelectedResolution.Height}";
8086
}
87+
88+
void HandleAvailableCamerasChanged(object? sender, IReadOnlyList<CameraInfo>? e)
89+
{
90+
OnPropertyChanged(nameof(Cameras));
91+
}
8192
}

src/CommunityToolkit.Maui.Camera/CameraInfo.shared.cs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#if IOS || MACCATALYST
44
using AVFoundation;
5+
56
#elif ANDROID
67
using AndroidX.Camera.Core;
78
#elif WINDOWS
@@ -23,18 +24,18 @@ public class CameraInfo(
2324
float maximumZoomFactor,
2425
IEnumerable<Size> supportedResolutions
2526
#if ANDROID
26-
,
27+
,
2728
CameraSelector cameraSelector
2829
#elif IOS || MACCATALYST
29-
,
30+
,
3031
AVCaptureDevice captureDevice,
3132
IEnumerable<AVCaptureDeviceFormat> supportedFormats
3233
#elif WINDOWS
33-
,
34+
,
3435
MediaFrameSourceGroup frameSourceGroup,
3536
IEnumerable<ImageEncodingProperties> imageEncodingProperties
3637
#endif
37-
)
38+
) : IEquatable<CameraInfo>
3839
{
3940
/// <summary>
4041
/// Gets the name of the camera device.
@@ -82,9 +83,29 @@ IEnumerable<ImageEncodingProperties> imageEncodingProperties
8283

8384
#if WINDOWS
8485
internal MediaFrameSourceGroup FrameSourceGroup { get; } = frameSourceGroup;
85-
internal IReadOnlyList<ImageEncodingProperties> ImageEncodingProperties { get; } = [.. imageEncodingProperties];
86+
internal IReadOnlyList<ImageEncodingProperties> ImageEncodingProperties { get; } = [.. imageEncodingProperties];
8687
#endif
8788

89+
/// <inheritdoc cref="Equals(object)"/>
90+
/// <remarks>Equality is determined using <see cref="DeviceId"/></remarks>
91+
public override bool Equals(object? obj) => Equals(obj as CameraInfo);
92+
93+
/// <inheritdoc cref="GetHashCode"/>
94+
/// <remarks>Uses the <see cref="DeviceId"/></remarks>
95+
public override int GetHashCode() => DeviceId.GetHashCode();
96+
97+
/// <inheritdoc cref="IEquatable{T}.Equals(T)"/>
98+
/// <remarks>Equality is determined using <see cref="DeviceId"/></remarks>
99+
public bool Equals(CameraInfo? other)
100+
{
101+
if (other is null)
102+
{
103+
return false;
104+
}
105+
106+
return DeviceId == other.DeviceId;
107+
}
108+
88109
/// <inheritdoc cref="object.ToString" />
89110
public override string ToString()
90111
{

src/CommunityToolkit.Maui.Camera/Interfaces/ICameraProvider.shared.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ namespace CommunityToolkit.Maui.Core;
77
/// </summary>
88
public interface ICameraProvider
99
{
10+
/// <summary>
11+
/// Event fires when the contents <see cref="AvailableCameras"/> has changed
12+
/// </summary>
13+
event EventHandler<IReadOnlyList<CameraInfo>?> AvailableCamerasChanged;
14+
1015
/// <summary>
1116
/// Cameras available on device
1217
/// </summary>

src/CommunityToolkit.Maui.Camera/Providers/CameraProvider.shared.cs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,46 @@
55
/// </summary>
66
partial class CameraProvider : ICameraProvider
77
{
8+
readonly WeakEventManager availableCamerasChangedEventManager = new();
9+
10+
public event EventHandler<IReadOnlyList<CameraInfo>?> AvailableCamerasChanged
11+
{
12+
add => availableCamerasChangedEventManager.AddEventHandler(value);
13+
remove => availableCamerasChangedEventManager.RemoveEventHandler(value);
14+
}
15+
816
/// <inheritdoc/>
9-
public IReadOnlyList<CameraInfo>? AvailableCameras { get; private set; }
17+
public IReadOnlyList<CameraInfo>? AvailableCameras
18+
{
19+
get;
20+
private set
21+
{
22+
if (!AreCameraInfoListsEqual(field, value))
23+
{
24+
field = value;
25+
availableCamerasChangedEventManager.HandleEvent(this, value, nameof(AvailableCamerasChanged));
26+
}
27+
}
28+
}
1029

1130
/// <inheritdoc/>
1231
public partial ValueTask RefreshAvailableCameras(CancellationToken token);
32+
33+
internal static bool AreCameraInfoListsEqual(in IReadOnlyList<CameraInfo>? cameraInfoList1, in IReadOnlyList<CameraInfo>? cameraInfoList2)
34+
{
35+
if (cameraInfoList1 is null && cameraInfoList2 is null)
36+
{
37+
return true;
38+
}
39+
40+
if (cameraInfoList1 is null || cameraInfoList2 is null)
41+
{
42+
return false;
43+
}
44+
45+
var cameraInfosInList1ButNotInList2 = cameraInfoList1.Except(cameraInfoList2).ToList();
46+
var cameraInfosInList2ButNotInList1 = cameraInfoList2.Except(cameraInfoList1).ToList();
47+
48+
return cameraInfosInList1ButNotInList2.Count is 0 && cameraInfosInList2ButNotInList1.Count is 0;
49+
}
1350
}

src/CommunityToolkit.Maui.UnitTests/Mocks/MockCameraProvider.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,20 @@ namespace CommunityToolkit.Maui.UnitTests.Mocks;
55

66
public class MockCameraProvider : ICameraProvider
77
{
8-
public IReadOnlyList<CameraInfo>? AvailableCameras { get; private set; }
8+
public event EventHandler<IReadOnlyList<CameraInfo>?>? AvailableCamerasChanged;
9+
10+
public IReadOnlyList<CameraInfo>? AvailableCameras
11+
{
12+
get;
13+
private set
14+
{
15+
if (!CameraProvider.AreCameraInfoListsEqual(field, value))
16+
{
17+
field = value;
18+
AvailableCamerasChanged?.Invoke(this, value);
19+
}
20+
}
21+
}
922

1023
public ValueTask RefreshAvailableCameras(CancellationToken token)
1124
{

0 commit comments

Comments
 (0)