Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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,12 +5,11 @@
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
xmlns:viewModels="clr-namespace:CommunityToolkit.Maui.Sample.ViewModels.Views"
Title="CameraView"
Unloaded="OnUnloaded"
x:Class="CommunityToolkit.Maui.Sample.Pages.Views.CameraViewPage"
x:TypeArguments="viewModels:CameraViewViewModel"
x:DataType="viewModels:CameraViewViewModel">

<Grid RowDefinitions="200,*,Auto,Auto" ColumnDefinitions="3*,*">
<Grid RowDefinitions="Auto,*,Auto,Auto" ColumnDefinitions="3*,*">
<toolkit:CameraView
x:Name="Camera"
Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Grid.RowSpan="3"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Diagnostics;
using CommunityToolkit.Maui.Core;
using CommunityToolkit.Maui.Sample.ViewModels.Views;

Expand All @@ -7,7 +6,7 @@ namespace CommunityToolkit.Maui.Sample.Pages.Views;
public partial class CameraViewPage : BasePage<CameraViewViewModel>
{
readonly string imagePath;
int pageCount;
bool isInitialized = false;

public CameraViewPage(CameraViewViewModel viewModel, IFileSystem fileSystem) : base(viewModel)
{
Expand All @@ -16,30 +15,27 @@ public CameraViewPage(CameraViewViewModel viewModel, IFileSystem fileSystem) : b
imagePath = Path.Combine(fileSystem.CacheDirectory, "camera-view-image.jpg");

Camera.MediaCaptured += OnMediaCaptured;

Loaded += (s, e) =>
{
pageCount = Navigation.NavigationStack.Count;
};
}

protected override async void OnAppearing()
{
base.OnAppearing();

if (isInitialized)
{
return;
}

var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3));
await BindingContext.RefreshCamerasCommand.ExecuteAsync(cancellationTokenSource.Token);
isInitialized = true;
}

// https://github.com/dotnet/maui/issues/16697
// https://github.com/dotnet/maui/issues/15833
protected override void OnNavigatedFrom(NavigatedFromEventArgs args)
{
base.OnNavigatedFrom(args);

Debug.WriteLine($"< < OnNavigatedFrom {pageCount} {Navigation.NavigationStack.Count}");

if (Navigation.NavigationStack.Count < pageCount)
if (!Shell.Current.Navigation.NavigationStack.Contains(this))
{
Cleanup();
}
Expand All @@ -57,12 +53,6 @@ async void OnImageTapped(object? sender, TappedEventArgs args)
void Cleanup()
{
Camera.MediaCaptured -= OnMediaCaptured;
Camera.Handler?.DisconnectHandler();
}

void OnUnloaded(object? sender, EventArgs e)
{
//Cleanup();
}

void OnMediaCaptured(object? sender, MediaCapturedEventArgs e)
Expand All @@ -75,7 +65,7 @@ void OnMediaCaptured(object? sender, MediaCapturedEventArgs e)
{
// workaround for https://github.com/dotnet/maui/issues/13858
#if ANDROID
image.Source = ImageSource.FromStream(() => File.OpenRead(imagePath));
image.Source = ImageSource.FromStream(() => File.OpenRead(imagePath));
#else
image.Source = ImageSource.FromFile(imagePath);
#endif
Expand Down
26 changes: 5 additions & 21 deletions src/CommunityToolkit.Maui.Camera/CameraManager.android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ protected virtual void Dispose(bool disposing)
previewView?.Dispose();
previewView = null;

processCameraProvider?.UnbindAll();
processCameraProvider?.Dispose();
processCameraProvider = null;

Expand Down Expand Up @@ -158,16 +159,6 @@ protected virtual async partial Task PlatformConnectCamera(CancellationToken tok
{
processCameraProvider = (ProcessCameraProvider)(cameraProviderFuture.Get() ?? throw new CameraException($"Unable to retrieve {nameof(ProcessCameraProvider)}"));

if (cameraProvider.AvailableCameras is null)
{
await cameraProvider.RefreshAvailableCameras(token);

if (cameraProvider.AvailableCameras is null)
{
throw new CameraException("Unable to refresh available cameras");
}
}

await StartUseCase(token);

cameraProviderTCS.SetResult();
Expand Down Expand Up @@ -200,22 +191,14 @@ protected async Task StartUseCase(CancellationToken token)
await StartCameraPreview(token);
}

protected virtual async partial Task PlatformStartCameraPreview(CancellationToken token)
protected virtual partial Task PlatformStartCameraPreview(CancellationToken token)
{
if (previewView is null || processCameraProvider is null || cameraPreview is null || imageCapture is null)
{
return;
return Task.CompletedTask;
}

if (cameraView.SelectedCamera is null)
{
if (cameraProvider.AvailableCameras is null)
{
await cameraProvider.RefreshAvailableCameras(token);
}

cameraView.SelectedCamera = cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");
}
cameraView.SelectedCamera ??= cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");

var cameraSelector = cameraView.SelectedCamera.CameraSelector ?? throw new CameraException($"Unable to retrieve {nameof(CameraSelector)}");

Expand All @@ -231,6 +214,7 @@ protected virtual async partial Task PlatformStartCameraPreview(CancellationToke

IsInitialized = true;
OnLoaded.Invoke();
return Task.CompletedTask;
}

protected virtual partial void PlatformStopCameraPreview()
Expand Down
31 changes: 6 additions & 25 deletions src/CommunityToolkit.Maui.Camera/CameraManager.macios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,18 @@
captureDevice.UnlockForConfiguration();
}

public async partial ValueTask UpdateCaptureResolution(Size resolution, CancellationToken token)
public partial ValueTask UpdateCaptureResolution(Size resolution, CancellationToken token)
{
if (captureDevice is null)
if (captureDevice is null || cameraView.SelectedCamera is null)
{
return;
return ValueTask.CompletedTask;
}

captureDevice.LockForConfiguration(out NSError? error);
if (error is not null)
{
Trace.WriteLine(error);
return;
}

if (cameraView.SelectedCamera is null)
{
await cameraProvider.RefreshAvailableCameras(token);
cameraView.SelectedCamera = cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");
return ValueTask.CompletedTask;
}

var filteredFormatList = cameraView.SelectedCamera.SupportedFormats.Where(f =>
Expand All @@ -116,20 +110,11 @@
}

captureDevice.UnlockForConfiguration();
return ValueTask.CompletedTask;
}

protected virtual async partial Task PlatformConnectCamera(CancellationToken token)
{
if (cameraProvider.AvailableCameras is null)
{
await cameraProvider.RefreshAvailableCameras(token);

if (cameraProvider.AvailableCameras is null)
{
throw new CameraException("Unable to refresh cameras");
}
}

await PlatformStartCameraPreview(token);
}

Expand All @@ -148,11 +133,7 @@
input.Dispose();
}

if (cameraView.SelectedCamera is null)
{
await cameraProvider.RefreshAvailableCameras(token);
cameraView.SelectedCamera = cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");
}
cameraView.SelectedCamera ??= cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");

captureDevice = cameraView.SelectedCamera.CaptureDevice ?? throw new CameraException($"No Camera found");
captureInput = new AVCaptureDeviceInput(captureDevice, out _);
Expand Down Expand Up @@ -203,7 +184,7 @@
var photoOutputConnection = photoOutput.ConnectionFromMediaType(avMediaTypeVideo);
if (photoOutputConnection is not null)
{
photoOutputConnection.VideoOrientation = videoOrientation;

Check warning on line 187 in src/CommunityToolkit.Maui.Camera/CameraManager.macios.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (macos-15)

This call site is reachable on: 'MacCatalyst' 15.0 and later. 'AVCaptureConnection.VideoOrientation' is obsoleted on: 'maccatalyst' 17.0 and later (Use VideoRotationAngle instead.). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422)

Check warning on line 187 in src/CommunityToolkit.Maui.Camera/CameraManager.macios.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (windows-latest)

This call site is reachable on: 'MacCatalyst' 15.0 and later. 'AVCaptureConnection.VideoOrientation' is obsoleted on: 'maccatalyst' 17.0 and later (Use VideoRotationAngle instead.). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422)
}
}

Expand Down Expand Up @@ -269,8 +250,8 @@
{
IEnumerable<UIScene> scenes = UIApplication.SharedApplication.ConnectedScenes;
var interfaceOrientation = scenes.FirstOrDefault() is UIWindowScene windowScene
? windowScene.InterfaceOrientation

Check warning on line 253 in src/CommunityToolkit.Maui.Camera/CameraManager.macios.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (macos-15)

This call site is reachable on: 'MacCatalyst' 15.0 and later. 'UIWindowScene.InterfaceOrientation' is obsoleted on: 'maccatalyst' 26.0 and later (Use 'EffectiveGeometry.InterfaceOrientation' instead.). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422)

Check warning on line 253 in src/CommunityToolkit.Maui.Camera/CameraManager.macios.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (windows-latest)

This call site is reachable on: 'MacCatalyst' 15.0 and later. 'UIWindowScene.InterfaceOrientation' is obsoleted on: 'maccatalyst' 26.0 and later (Use 'EffectiveGeometry.InterfaceOrientation' instead.). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422)
: UIApplication.SharedApplication.StatusBarOrientation;

Check warning on line 254 in src/CommunityToolkit.Maui.Camera/CameraManager.macios.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (macos-15)

This call site is reachable on: 'MacCatalyst' 15.0 and later. 'UIApplication.StatusBarOrientation' is obsoleted on: 'maccatalyst' 9.0 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422)

Check warning on line 254 in src/CommunityToolkit.Maui.Camera/CameraManager.macios.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (windows-latest)

This call site is reachable on: 'MacCatalyst' 15.0 and later. 'UIApplication.StatusBarOrientation' is obsoleted on: 'maccatalyst' 9.0 and later. (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422)

return interfaceOrientation switch
{
Expand Down Expand Up @@ -346,7 +327,7 @@
{
if (PreviewLayer.Connection is not null)
{
PreviewLayer.Connection.VideoOrientation = videoOrientation;

Check warning on line 330 in src/CommunityToolkit.Maui.Camera/CameraManager.macios.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (macos-15)

This call site is reachable on: 'MacCatalyst' 15.0 and later. 'AVCaptureConnection.VideoOrientation' is obsoleted on: 'maccatalyst' 17.0 and later (Use VideoRotationAngle instead.). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422)

Check warning on line 330 in src/CommunityToolkit.Maui.Camera/CameraManager.macios.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (windows-latest)

This call site is reachable on: 'MacCatalyst' 15.0 and later. 'AVCaptureConnection.VideoOrientation' is obsoleted on: 'maccatalyst' 17.0 and later (Use VideoRotationAngle instead.). (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1422)
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
/// <exception cref="NullReferenceException">Thrown when no <see cref="CameraProvider"/> can be resolved.</exception>
/// <exception cref="InvalidOperationException">Thrown when there are no cameras available.</exception>
partial class CameraManager(
IMauiContext mauiContext,

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (macos-15)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (macos-15)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Library (macos-15)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (macos-15)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Build Sample App using Latest .NET SDK (windows-latest)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Run Benchmarks (windows-latest)

Parameter 'mauiContext' is unread.

Check warning on line 16 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Run Benchmarks (macos-15)

Parameter 'mauiContext' is unread.
ICameraView cameraView,
ICameraProvider cameraProvider,
Action onLoaded) : IDisposable
Expand All @@ -33,7 +33,15 @@
/// Connects to the camera.
/// </summary>
/// <returns>A <see cref="ValueTask"/> that can be awaited.</returns>
public Task ConnectCamera(CancellationToken token) => PlatformConnectCamera(token);
public async Task ConnectCamera(CancellationToken token)
{
if (cameraProvider.AvailableCameras is null)
{
await cameraProvider.RefreshAvailableCameras(token);
}
cameraView.SelectedCamera ??= cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");
Copy link

Copilot AI Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] This line combines null-conditional assignment, null-conditional access, null-coalescing, and exception throwing in a single statement, making it difficult to read and debug. Consider breaking this into multiple lines for better clarity.

Suggested change
cameraView.SelectedCamera ??= cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");
var availableCameras = cameraProvider.AvailableCameras;
if (availableCameras is null || !availableCameras.Any())
{
throw new CameraException("No camera available on device");
}
if (cameraView.SelectedCamera is null)
{
cameraView.SelectedCamera = availableCameras.First();
}

Copilot uses AI. Check for mistakes.
await PlatformConnectCamera(token);
}

/// <summary>
/// Disconnects from the camera.
Expand Down
33 changes: 7 additions & 26 deletions src/CommunityToolkit.Maui.Camera/CameraManager.windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,16 +119,6 @@ protected virtual void Dispose(bool disposing)

protected virtual async partial Task PlatformConnectCamera(CancellationToken token)
{
if (cameraProvider.AvailableCameras is null)
{
await cameraProvider.RefreshAvailableCameras(token);

if (cameraProvider.AvailableCameras is null)
{
throw new CameraException("Unable to refresh cameras");
}
}

await StartCameraPreview(token);
}

Expand All @@ -139,13 +129,9 @@ protected virtual async partial Task PlatformStartCameraPreview(CancellationToke
return;
}

mediaCapture = new MediaCapture();
cameraView.SelectedCamera ??= cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");

if (cameraView.SelectedCamera is null)
{
await cameraProvider.RefreshAvailableCameras(token);
cameraView.SelectedCamera = cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");
}
mediaCapture = new MediaCapture();

await mediaCapture.InitializeCameraForCameraView(cameraView.SelectedCamera.DeviceId, token);

Expand Down Expand Up @@ -180,22 +166,17 @@ protected virtual partial void PlatformStopCameraPreview()

protected async Task PlatformUpdateResolution(Size resolution, CancellationToken token)
{
if (!IsInitialized || mediaCapture is null)
if (!IsInitialized || mediaCapture is null || cameraView.SelectedCamera is null)
{
return;
}

if (cameraView.SelectedCamera is null)
{
await cameraProvider.RefreshAvailableCameras(token);
cameraView.SelectedCamera = cameraProvider.AvailableCameras?.FirstOrDefault() ?? throw new CameraException("No camera available on device");
}

var filteredPropertiesList = cameraView.SelectedCamera.ImageEncodingProperties.Where(p => p.Width <= resolution.Width && p.Height <= resolution.Height).ToList();

filteredPropertiesList = filteredPropertiesList.Count is not 0
? filteredPropertiesList
: [.. cameraView.SelectedCamera.ImageEncodingProperties.OrderByDescending(p => p.Width * p.Height)];
if (filteredPropertiesList.Count is 0)
{
filteredPropertiesList = [.. cameraView.SelectedCamera.ImageEncodingProperties.OrderByDescending(p => p.Width * p.Height)];
}

if (filteredPropertiesList.Count is not 0)
Comment on lines +176 to 181
Copy link

Copilot AI Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The conditional pattern Count is 0 is inconsistent with the pattern used elsewhere in the codebase. Consider using Count == 0 or Count is not 0 consistently throughout the file for better readability.

Suggested change
if (filteredPropertiesList.Count is 0)
{
filteredPropertiesList = [.. cameraView.SelectedCamera.ImageEncodingProperties.OrderByDescending(p => p.Width * p.Height)];
}
if (filteredPropertiesList.Count is not 0)
if (filteredPropertiesList.Count == 0)
{
filteredPropertiesList = [.. cameraView.SelectedCamera.ImageEncodingProperties.OrderByDescending(p => p.Width * p.Height)];
}
if (filteredPropertiesList.Count != 0)

Copilot uses AI. Check for mistakes.
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ protected override async void ConnectHandler(NativePlatformCameraPreviewView pla

await CameraManager.ArePermissionsGranted();
await CameraManager.ConnectCamera(CancellationToken.None);
await cameraProvider.RefreshAvailableCameras(CancellationToken.None);
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ partial class CameraProvider
{
readonly Context context = Android.App.Application.Context;

public async partial ValueTask RefreshAvailableCameras(CancellationToken token)
private async partial ValueTask PlatformRefreshAvailableCameras(CancellationToken token)
{
var cameraProviderFuture = ProcessCameraProvider.GetInstance(context);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ partial class CameraProvider
{
static readonly AVCaptureDeviceType[] captureDevices = InitializeCaptureDevices();

public partial ValueTask RefreshAvailableCameras(CancellationToken token)
private partial ValueTask PlatformRefreshAvailableCameras(CancellationToken token)
{
var discoverySession = AVCaptureDeviceDiscoverySession.Create(captureDevices, AVMediaTypes.Video, AVCaptureDevicePosition.Unspecified);
var availableCameras = new List<CameraInfo>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ namespace CommunityToolkit.Maui.Core;

partial class CameraProvider
{
public partial ValueTask RefreshAvailableCameras(CancellationToken token) => throw new NotSupportedException();
private partial ValueTask PlatformRefreshAvailableCameras(CancellationToken token) => throw new NotSupportedException();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
partial class CameraProvider : ICameraProvider
{
readonly WeakEventManager availableCamerasChangedEventManager = new();

Task? refreshAvailableCamerasTask;

public event EventHandler<IReadOnlyList<CameraInfo>?> AvailableCamerasChanged
{
add => availableCamerasChangedEventManager.AddEventHandler(value);
Expand All @@ -22,13 +23,23 @@ private set
if (!AreCameraInfoListsEqual(field, value))
{
field = value;
availableCamerasChangedEventManager.HandleEvent(this, value, nameof(AvailableCamerasChanged));
availableCamerasChangedEventManager.HandleEvent(this, value, nameof(AvailableCamerasChanged));
}
}
}

private partial ValueTask PlatformRefreshAvailableCameras(CancellationToken token);

/// <inheritdoc/>
public partial ValueTask RefreshAvailableCameras(CancellationToken token);
public async ValueTask RefreshAvailableCameras(CancellationToken token)
{
if (refreshAvailableCamerasTask is null || refreshAvailableCamerasTask.IsCompleted)
{
refreshAvailableCamerasTask = PlatformRefreshAvailableCameras(token).AsTask();
Copy link

Copilot AI Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CancellationToken from the current call is used to create the task, but subsequent callers with different tokens will await a task that may not respect their cancellation requirements. Consider how to handle cancellation tokens properly across multiple concurrent calls.

Copilot uses AI. Check for mistakes.
}

await refreshAvailableCamerasTask;
Comment on lines +36 to +41
Copy link

Copilot AI Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current implementation has a race condition where multiple threads could simultaneously check refreshAvailableCamerasTask and both create new tasks. Consider using a lock or Interlocked.CompareExchange to ensure thread-safe task creation.

Copilot uses AI. Check for mistakes.
}

internal static bool AreCameraInfoListsEqual(in IReadOnlyList<CameraInfo>? cameraInfoList1, in IReadOnlyList<CameraInfo>? cameraInfoList2)
{
Expand All @@ -47,4 +58,4 @@ internal static bool AreCameraInfoListsEqual(in IReadOnlyList<CameraInfo>? camer

return cameraInfosInList1ButNotInList2.Count is 0 && cameraInfosInList2ButNotInList1.Count is 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

partial class CameraProvider
{
public partial ValueTask RefreshAvailableCameras(CancellationToken token) => throw new NotSupportedException();
private partial ValueTask PlatformRefreshAvailableCameras(CancellationToken token) => throw new NotSupportedException();
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace CommunityToolkit.Maui.Core;

partial class CameraProvider
{
public async partial ValueTask RefreshAvailableCameras(CancellationToken token)
private async partial ValueTask PlatformRefreshAvailableCameras(CancellationToken token)
{
var deviceInfoCollection = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture).AsTask(token);
var mediaFrameSourceGroup = await MediaFrameSourceGroup.FindAllAsync().AsTask(token);
Expand Down Expand Up @@ -68,4 +68,4 @@ public async partial ValueTask RefreshAvailableCameras(CancellationToken token)
AvailableCameras = availableCameras;
}

}
}
Loading
Loading