Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,20 @@ public CameraViewPage(CameraViewViewModel viewModel, IFileSystem fileSystem, IFi
protected override async void OnAppearing()
{
base.OnAppearing();
var cameraRequest = await Permissions.RequestAsync<Permissions.Camera>();
var microphoneRequest = await Permissions.RequestAsync<Permissions.Microphone>();
if (cameraRequest is not PermissionStatus.Granted)
{
Trace.TraceInformation("Camera permission is not granted.");
return;
}

if (microphoneRequest is not PermissionStatus.Granted)
{
Trace.TraceInformation("Microphone permission is not granted.");
return;
}

var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(3));
await BindingContext.RefreshCamerasCommand.ExecuteAsync(cancellationTokenSource.Token);
}
Expand Down Expand Up @@ -124,6 +137,12 @@ async void SaveVideo(object? sender, EventArgs e)
}
else
{
var status = await Permissions.RequestAsync<Permissions.StorageWrite>();
if (status is not PermissionStatus.Granted)
{
throw new PermissionException("Storage permission is not granted.");
}

await fileSaver.SaveAsync("recording.mp4", videoRecordingStream);
await videoRecordingStream.DisposeAsync();
videoRecordingStream = Stream.Null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public partial class FileSaverViewModel(IFileSaver fileSaver) : BaseViewModel
[RelayCommand]
async Task SaveFile(CancellationToken cancellationToken)
{
await RequestPermissions();
using var stream = new MemoryStream(Encoding.Default.GetBytes("Hello from the Community Toolkit!"));
try
{
Expand All @@ -32,6 +33,7 @@ async Task SaveFile(CancellationToken cancellationToken)
[RelayCommand]
async Task SaveFileStatic(CancellationToken cancellationToken)
{
await RequestPermissions();
using var stream = new MemoryStream(Encoding.Default.GetBytes("Hello from the Community Toolkit!"));
var fileSaveResult = await FileSaver.SaveAsync("DCIM", "test.txt", stream, cancellationToken);
if (fileSaveResult.IsSuccessful)
Expand All @@ -47,6 +49,7 @@ async Task SaveFileStatic(CancellationToken cancellationToken)
[RelayCommand]
async Task SaveFileInstance(CancellationToken cancellationToken)
{
await RequestPermissions();
using var client = new HttpClient();

const string communityToolkitNuGetUrl = "https://www.nuget.org/api/v2/package/CommunityToolkit.Maui/5.0.0";
Expand All @@ -67,4 +70,10 @@ async Task SaveFileInstance(CancellationToken cancellationToken)
await Toast.Make($"File is not saved, {ex.Message}").Show(cancellationToken);
}
}

async Task RequestPermissions()
{
await Permissions.RequestAsync<Permissions.StorageRead>();
await Permissions.RequestAsync<Permissions.StorageWrite>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public FolderPickerViewModel(IFolderPicker folderPicker)
[RelayCommand]
async Task PickFolder(CancellationToken cancellationToken)
{
await RequestPermissions();
var folderPickerResult = await folderPicker.PickAsync(cancellationToken);
if (folderPickerResult.IsSuccessful)
{
Expand All @@ -31,6 +32,7 @@ async Task PickFolder(CancellationToken cancellationToken)
[RelayCommand]
async Task PickFolderStatic(CancellationToken cancellationToken)
{
await RequestPermissions();
var folderResult = await FolderPicker.PickAsync("DCIM", cancellationToken);
if (folderResult.IsSuccessful)
{
Expand All @@ -46,6 +48,7 @@ async Task PickFolderStatic(CancellationToken cancellationToken)
[RelayCommand]
async Task PickFolderInstance(CancellationToken cancellationToken)
{
await RequestPermissions();
var folderPickerInstance = new FolderPickerImplementation();
try
{
Expand All @@ -62,4 +65,10 @@ async Task PickFolderInstance(CancellationToken cancellationToken)
await Toast.Make($"Folder is not picked, {e.Message}").Show(cancellationToken);
}
}

async Task RequestPermissions()
{
await Permissions.RequestAsync<Permissions.StorageRead>();
await Permissions.RequestAsync<Permissions.StorageWrite>();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public OfflineSpeechToTextViewModel()
[RelayCommand]
async Task StartListen()
{
var isGranted = await speechToText.RequestPermissions(CancellationToken.None);
var isGranted = await RequestPermissions();
if (!isGranted)
{
await Toast.Make("Permission not granted").Show(CancellationToken.None);
Expand Down Expand Up @@ -74,4 +74,11 @@ void HandleSpeechToTextStateChanged(object? sender, SpeechToTextStateChangedEven
{
OnPropertyChanged(nameof(State));
}

async Task<bool> RequestPermissions()
{
var microphoneGranted = await Permissions.RequestAsync<Permissions.Microphone>();
var recognitionGranted = await SpeechToText.RequestPermissions(CancellationToken.None);
return microphoneGranted == PermissionStatus.Granted && recognitionGranted;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ async Task StartListen()
CanStartListenExecute = false;
CanStopListenExecute = true;

var isGranted = await speechToText.RequestPermissions(CancellationToken.None);
var isGranted = await RequestPermissions();
if (!isGranted)
{
await Toast.Make("Permission not granted").Show(CancellationToken.None);
Expand Down Expand Up @@ -151,4 +151,11 @@ public async ValueTask DisposeAsync()
{
await speechToText.DisposeAsync();
}

async Task<bool> RequestPermissions()
{
var microphoneGranted = await Permissions.RequestAsync<Permissions.Microphone>();
var recognitionGranted = await SpeechToText.RequestPermissions(CancellationToken.None);
return microphoneGranted == PermissionStatus.Granted && recognitionGranted;
}
}
23 changes: 0 additions & 23 deletions src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,15 @@
/// <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 18 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 18 in src/CommunityToolkit.Maui.Camera/CameraManager.shared.cs

View workflow job for this annotation

GitHub Actions / Run Benchmarks (macos-15)

Parameter 'mauiContext' is unread.

Check warning on line 18 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 18 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 18 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 18 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 18 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 18 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 18 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 18 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.
ICameraView cameraView,

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

View workflow job for this annotation

GitHub Actions / Run Benchmarks (windows-latest)

Parameter 'cameraView' is unread.

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

View workflow job for this annotation

GitHub Actions / Run Benchmarks (macos-15)

Parameter 'cameraView' is unread.

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

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

Parameter 'cameraView' is unread.

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

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

Parameter 'cameraView' is unread.

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

View workflow job for this annotation

GitHub Actions / Build Library (macos-15)

Parameter 'cameraView' is unread.

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

View workflow job for this annotation

GitHub Actions / Build Library (macos-15)

Parameter 'cameraView' is unread.
ICameraProvider cameraProvider,

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

View workflow job for this annotation

GitHub Actions / Run Benchmarks (windows-latest)

Parameter 'cameraProvider' is unread.

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

View workflow job for this annotation

GitHub Actions / Run Benchmarks (macos-15)

Parameter 'cameraProvider' is unread.

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

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

Parameter 'cameraProvider' is unread.

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

View workflow job for this annotation

GitHub Actions / Build Library (windows-latest)

Parameter 'cameraProvider' is unread.

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

View workflow job for this annotation

GitHub Actions / Build Library (macos-15)

Parameter 'cameraProvider' is unread.

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

View workflow job for this annotation

GitHub Actions / Build Library (macos-15)

Parameter 'cameraProvider' is unread.
Action onLoaded) : IDisposable
{
internal Action OnLoaded { get; } = onLoaded;

internal bool IsInitialized { get; private set; }

/// <summary>
/// Whether the user has granted the required permissions through the use of the <see cref="Permissions"/> API.
/// </summary>
/// <returns>Returns <c>true</c> if permission has been granted, <c>false</c> otherwise.</returns>
public async Task<bool> ArePermissionsGranted()
{
var cameraRequest = await Permissions.RequestAsync<Permissions.Camera>();
var microphoneRequest = await Permissions.RequestAsync<Permissions.Microphone>();
if (cameraRequest is not PermissionStatus.Granted)
{
Trace.TraceInformation("Camera permission is not granted.");
return false;
}

if (microphoneRequest is not PermissionStatus.Granted)
{
Trace.TraceInformation("Microphone permission is not granted.");
return false;
}

return true;
}

/// <summary>
/// Connects to the camera.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ protected override async void ConnectHandler(NativePlatformCameraPreviewView pla
{
base.ConnectHandler(platformView);

await CameraManager.ArePermissionsGranted();
await CameraManager.ConnectCamera(CancellationToken.None);
await cameraProvider.RefreshAvailableCameras(CancellationToken.None);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,7 @@ static async Task<string> InternalSaveAsync(string initialPath, string fileName,
}

AndroidUri? filePath = null;

if (!OperatingSystem.IsAndroidVersionAtLeast(33))
{
var status = await Permissions.RequestAsync<Permissions.StorageWrite>().WaitAsync(cancellationToken).ConfigureAwait(false);
if (status is not PermissionStatus.Granted)
{
throw new PermissionException("Storage permission is not granted.");
}
}


if (Android.OS.Environment.ExternalStorageDirectory is not null)
{
initialPath = initialPath.Replace(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, string.Empty, StringComparison.InvariantCulture);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@ public sealed partial class FileSaverImplementation : IFileSaver
{
async Task<string> InternalSaveAsync(string initialPath, string fileName, Stream stream, IProgress<double>? progress, CancellationToken cancellationToken)
{
var status = await Permissions.RequestAsync<Permissions.StorageRead>().WaitAsync(cancellationToken);
if (status is not PermissionStatus.Granted)
{
throw new PermissionException("Storage permission is not granted.");
}

using var dialog = new FileFolderDialog(true, initialPath, fileName: fileName);
var path = await dialog.Open().WaitAsync(cancellationToken).ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,6 @@ static async Task<Folder> InternalPickAsync(string initialPath, CancellationToke

Folder? folder = null;

if (!OperatingSystem.IsAndroidVersionAtLeast(33))
{
var statusRead = await Permissions.RequestAsync<Permissions.StorageRead>().WaitAsync(cancellationToken).ConfigureAwait(false);
if (statusRead is not PermissionStatus.Granted)
{
throw new PermissionException("Storage permission is not granted.");
}
}

if (Android.OS.Environment.ExternalStorageDirectory is not null)
{
initialPath = initialPath.Replace(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, string.Empty, StringComparison.InvariantCulture);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ public sealed partial class FolderPickerImplementation : IFolderPicker
{
async Task<Folder> InternalPickAsync(string initialPath, CancellationToken cancellationToken)
{
var status = await Permissions.RequestAsync<Permissions.StorageRead>().WaitAsync(cancellationToken);
if (status is not PermissionStatus.Granted)
{
throw new PermissionException("Storage permission is not granted.");
}

using var dialog = new FileFolderDialog(false, initialPath);
var path = await dialog.Open().WaitAsync(cancellationToken).ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,6 @@ public event EventHandler<SpeechToTextStateChangedEventArgs> StateChanged
public async Task StartListenAsync(SpeechToTextOptions options, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();

var isPermissionGranted = await IsSpeechPermissionAuthorized(cancellationToken).ConfigureAwait(false);
if (!isPermissionGranted)
{
throw new PermissionException($"{nameof(Permissions)}.{nameof(Permissions.Microphone)} Not Granted");
}

await InternalStartListening(options, cancellationToken);
}

Expand Down Expand Up @@ -75,11 +68,5 @@ public async Task<bool> RequestPermissions(CancellationToken cancellationToken =
var status = await Permissions.RequestAsync<Permissions.Microphone>().WaitAsync(cancellationToken).ConfigureAwait(false);
return status is PermissionStatus.Granted;
}

static async Task<bool> IsSpeechPermissionAuthorized(CancellationToken cancellationToken)
{
var status = await Permissions.CheckStatusAsync<Permissions.Microphone>().WaitAsync(cancellationToken).ConfigureAwait(false);
return status is PermissionStatus.Granted;
}
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,6 @@ public Task<bool> RequestPermissions(CancellationToken cancellationToken = defau
return taskResult.Task.WaitAsync(cancellationToken);
}

static Task<bool> IsSpeechPermissionAuthorized(CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
return Task.FromResult(SFSpeechRecognizer.AuthorizationStatus is SFSpeechRecognizerAuthorizationStatus.Authorized);
}

static void InitializeAvAudioSession(out AVAudioSession sharedAvAudioSession)
{
sharedAvAudioSession = AVAudioSession.SharedInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ public async Task StartListenAsync(SpeechToTextOptions options, CancellationToke
{
cancellationToken.ThrowIfCancellationRequested();

var isPermissionGranted = await IsSpeechPermissionAuthorized(cancellationToken).ConfigureAwait(false);
if (!isPermissionGranted)
{
throw new PermissionException($"{nameof(Permissions)}.{nameof(Permissions.Microphone)} Not Granted");
}

await InternalStartListeningAsync(options, cancellationToken).ConfigureAwait(false);
}

Expand Down Expand Up @@ -70,11 +64,5 @@ public async Task<bool> RequestPermissions(CancellationToken cancellationToken =
var status = await Permissions.RequestAsync<Permissions.Microphone>().WaitAsync(cancellationToken).ConfigureAwait(false);
return status is PermissionStatus.Granted;
}

static async Task<bool> IsSpeechPermissionAuthorized(CancellationToken cancellationToken)
{
var status = await Permissions.CheckStatusAsync<Permissions.Microphone>().WaitAsync(cancellationToken).ConfigureAwait(false);
return status is PermissionStatus.Granted;
}
#endif
}
Loading