dotnet / maui

.NET MAUI is the .NET Multi-platform App UI, a framework for building native device applications spanning mobile, tablet, and desktop.
https://dot.net/maui
MIT License
22.24k stars 1.76k forks source link

Maui media picker on Windows Platform error #7660

Closed TajinderSingh-1 closed 1 year ago

TajinderSingh-1 commented 2 years ago

I have created a very basic MauiApp because I wanted to try the MediaPicker on the Windows Platform.

Thus I copied the code from the official documentation and tried to run my application

However if I add <uap:Capability Name="webcam"/> to the Package.appxmanifest file as suggested in the documentaion, and run the application it gives me the following error:

Error       DEP0700: Registration of the app failed. [0x80080204] error 0xC00CE169: App 
manifest validation error: The app manifest must be valid as per schema: Line 39, Column 
21, Reason: 'webcam' violates enumeration constraint of 'documentsLibrary 
picturesLibrary videosLibrary musicLibrary enterpriseAuthentication 
sharedUserCertificates userAccountInformation removableStorage appointments contacts 
phoneCall blockedChatMessages objects3D voipCall chat'.
The attribute 'Name' with value 'webcam' failed to parse.   MauiApp3            

So in order to solve this problem I tried to change the capability from <uap:Capability Name="webcam"/> to <DeviceCapability Name="webcam"/>.

In this way I can run the application without errors, but photo is always null:

public async void TakePhoto(object sender, EventArgs e)
{
    if (MediaPicker.Default.IsCaptureSupported)
    {
        FileResult photo = await MediaPicker.Default.CapturePhotoAsync();

        if (photo != null)
        {
            // save the file into local storage
            string localFilePath = Path.Combine(FileSystem.CacheDirectory, photo.FileName);

            using Stream sourceStream = await photo.OpenReadAsync();
            using FileStream localFileStream = File.OpenWrite(localFilePath);

            await sourceStream.CopyToAsync(localFileStream);
        }
        else
        {
            // *** IT ALWAYS ENTERS IN THE ELSE CLAUSE ***
            // *** BECAUSE photo IS ALWAYS NULL ***
            CounterBtn.Text = $"Capture is supported but {photo} is null";
        }
    }
}

Note: The function above is called when I click to this button that I've defined in MainPage.xaml file:

        <Button 
            x:Name="ImageBtn"
            Text="Take Photo"
            SemanticProperties.Hint="Take Image"
            Clicked="TakePhoto"
            HorizontalOptions="Center" />

Stackoverflow question: https://stackoverflow.com/questions/72450077/maui-media-picker-on-windows-platform-error

janseris commented 2 years ago

Issues with null always returned:

https://github.com/dotnet/maui/issues/7465 duplicate except IsCaptureSupported https://github.com/dotnet/maui/issues/6660 similar and also IsCaptureSupported was not used

Together with this new issue it seems that it really does nothing on Windows in a full sample with checks and manifest declarations.

GiampaoloGabba commented 2 years ago

Unfortunately this is a old and known bug for WinUI3. https://github.com/microsoft/WindowsAppSDK/issues/1034

As a workaround you can create a custom mediapicker that inherit everything from the MAUI one for Android, iOS and Catalyst.

Them implement a custom mediapicker for Windows, intheriting all the Capture... methods and reimplementing the CapturePhotoAsync using a custom class for camera capture:

public async Task<FileResult> CaptureAsync(MediaPickerOptions options, bool photo)
{
    var captureUi = new CameraCaptureUI(options);

    var file = await captureUi.CaptureFileAsync(photo ? CameraCaptureUIMode.Photo : CameraCaptureUIMode.Video);

    if (file != null)
        return new FileResult(file.Path,file.ContentType);

    return null;
}

This is the class:

using Windows.Foundation.Collections;
using Windows.Media.Capture;
using Windows.Storage;
using Windows.System;
using Microsoft.Maui.Platform;
using WinRT.Interop;

public class CameraCaptureUI
{
    private LauncherOptions _launcherOptions;

    public CameraCaptureUI(MediaPickerOptions options)
    {
        var hndl = WindowStateManager.Default.GetActiveWindow().GetWindowHandle();

        _launcherOptions = new LauncherOptions();
        InitializeWithWindow.Initialize(_launcherOptions, hndl);

        _launcherOptions.TreatAsUntrusted                   = false;
        _launcherOptions.DisplayApplicationPicker           = false;
        _launcherOptions.TargetApplicationPackageFamilyName = "Microsoft.WindowsCamera_8wekyb3d8bbwe";
    }

    public async Task<StorageFile> CaptureFileAsync(CameraCaptureUIMode mode)
    {
        if (mode != CameraCaptureUIMode.Photo)
        {
            throw new NotImplementedException();
        }

        var currentAppData = ApplicationData.Current;
        var tempLocation = currentAppData.TemporaryFolder;
        var tempFileName = "CCapture.jpg";
        var tempFile = await tempLocation.CreateFileAsync(tempFileName, CreationCollisionOption.GenerateUniqueName);
        var token = Windows.ApplicationModel.DataTransfer.SharedStorageAccessManager.AddFile(tempFile);

        var set = new ValueSet
        {
            { "MediaType", "photo"},
            { "PhotoFileToken", token }
        };

        var uri    = new Uri("microsoft.windows.camera.picker:");
        var result = await Windows.System.Launcher.LaunchUriForResultsAsync(uri, _launcherOptions, set);
        if (result.Status == LaunchUriStatus.Success)
        {
            return tempFile;
        }

        return null;
    }
}

The only downside is that with this class you cannot take videos on windows.

richardrigutins commented 2 years ago

Actually, with a slight modification in the custom CameraCaptureUI class, @GiampaoloGabba's workaround can be used to capture both photos and videos on Windows:

public async Task<StorageFile> CaptureFileAsync(CameraCaptureUIMode mode)
{
    var extension = mode == CameraCaptureUIMode.Photo ? ".jpg" : ".mp4";

    var currentAppData = ApplicationData.Current;
    var tempLocation = currentAppData.LocalCacheFolder;
    var tempFileName = $"CCapture{extension}";
    var tempFile = await tempLocation.CreateFileAsync(tempFileName, CreationCollisionOption.GenerateUniqueName);
    var token = Windows.ApplicationModel.DataTransfer.SharedStorageAccessManager.AddFile(tempFile);

    var set = new ValueSet();
    if (mode == CameraCaptureUIMode.Photo)
    {
        set.Add("MediaType", "photo");
        set.Add("PhotoFileToken", token);
    }
    else
    {
        set.Add("MediaType", "video");
        set.Add("VideoFileToken", token);
    }

    var uri = new Uri("microsoft.windows.camera.picker:");
    var result = await Windows.System.Launcher.LaunchUriForResultsAsync(uri, _launcherOptions, set);
    if (result.Status == LaunchUriStatus.Success && result.Result != null)
    {
        return tempFile;
    }

    return null;
}
GiampaoloGabba commented 2 years ago

@richardrigutins thank you! When i tried the workaround for videos, i forgot to set the "VideoFileToken" ValueSet (i kept using PhotoFileToken) and the app crashed when trying to take videos 😄

I confirm that now videocapturing is owrking

TajinderSingh-1 commented 2 years ago

To me it is showing the type or namespace Windows could not be found error

richardrigutins commented 2 years ago

To me it is showing the type or namespace Windows could not be found error

It's a platform-specific implementation, you need to add it to the Platforms > Windows folder for example (see the documentation).

Here you can find the code for a sample app that uses this workaround: repository.

Shidesu commented 2 years ago

Is there any update on that issue WinUI or MAUI side?

janseris commented 1 year ago

@jsuarezruiz Hi is anything happening with this issue? It's been around for more than 6 months. It exists in WinUI repo for more than 18 months. This comment says it's a unimplemented feature/bug in WinUI but WinUI is a part of MAUI - MAUI depends on WinUI. https://github.com/dotnet/maui/issues/6660#issuecomment-1195227133 Can you add priority for the issue on the WinUI repo? WinUI developers don't seem to do anything about the issue. https://github.com/microsoft/WindowsAppSDK/issues/1034#issuecomment-1348990914

mattleibow commented 1 year ago

Hi folks, just browsing through the older issues and saw this one with some good looking ways to make things work. Not sure if this is all your code or you got it from somewhere else? I want to add some credits here: https://github.com/dotnet/maui/pull/13220

If this is your code, you can go ahead and make a PR as well.

I am having some issues, not sure if you saw these before - on the PR so we don't clog up this issue with dev fixes: #13220