readyplayerme / rpm-unity-sdk-core

This Module contains all the core functionality required for using Ready Player Me avatars in Unity, including avatar loading and creation
MIT License
97 stars 33 forks source link

Camera Usage Permission in AndroidManifest for Meta Quest Builds #244

Closed Uralstech closed 7 months ago

Uralstech commented 9 months ago

Describe the bug The usage of WebCamTexture in Runtime\AvatarCreator\Scripts\UI\Elements\PhotoCaptureElement.cs causes Unity to add the camera permission to our app's AndroidManifest.xml. As camera access is not allowed by Meta for their Quest platform, builds of our app cannot be uploaded to the Quest Store or App Lab.

Expected behavior The camera permission should not be added in the AndroidManifest.xml unless the part of the SDK which requires camera access is being used in the app.

Desktop (please complete the following information):

Edit: For those looking for a solution:

pragma warning disable CS1998

namespace ReadyPlayerMe.AvatarCreator { ///

/// A Unity MonoBehaviour class for capturing photos from the device's camera. /// Allows starting and stopping the camera, capturing photos, and handling camera permissions. /// public class PhotoCaptureElement : MonoBehaviour { [Header("Settings")] [SerializeField] private RawImage cameraTextureTarget; [SerializeField] private bool initializeOnEnable = true;

    [Space(5)]
    [Header("Events")]
    public UnityEvent<Texture2D> OnPhotoCaptured;

    // private WebCamTexture cameraTexture;
    private bool isInitialized;

    private int videoRotationAngle;
    private bool videoVerticallyMirrored;

    private CancellationTokenSource ctxSource;

    private void OnEnable()
    {
        if (initializeOnEnable)
        {
            StartCamera();
        }
    }

    private void OnDisable()
    {
        ctxSource?.Cancel();
        StopCamera();
    }

    /// <summary>
    /// Starts the device's camera, requesting camera permissions if needed.
    /// </summary>
    public async void StartCamera()
    {
        await RequestCameraPermission();

        if (!isInitialized)
        {
            InitializeCamera();
        }

        // if (cameraTexture != null && !cameraTexture.isPlaying)
        // {
        //     cameraTexture.Play();
        // 
        //     var currentRotation = cameraTextureTarget.transform.rotation;
        //     cameraTextureTarget.transform.rotation = Quaternion.Euler(currentRotation.eulerAngles.x, currentRotation.eulerAngles.y, cameraTexture.videoRotationAngle);
        // 
        //     if (!cameraTexture.videoVerticallyMirrored)
        //     {
        //         cameraTextureTarget.transform.localScale = new Vector3(-1, 1, 1);
        //     }
        // }
    }

    /// <summary>
    /// Stops the device's camera.
    /// </summary>
    public void StopCamera()
    {
        // if (cameraTexture != null && cameraTexture.isPlaying)
        // {
        //     cameraTexture.Stop();
        // }
    }

    /// <summary>
    /// Takes a photo from the camera and invokes the onPhotoCaptured event with the captured texture.
    /// </summary>
    public void TakePhoto()
    {
        // if (cameraTexture == null || !cameraTexture.isPlaying)
        //     return;
        // 
        // var texture = new Texture2D(cameraTextureTarget.texture.width, cameraTextureTarget.texture.height, TextureFormat.ARGB32, false);
        // texture.SetPixels(cameraTexture.GetPixels());
        // texture.Apply();
        // 
        // OnPhotoCaptured?.Invoke(texture);
    }

    /// <summary>
    /// Requests camera permissions from the user for IOS and Android devices.
    /// </summary>
    private async Task RequestCameraPermission()
    {
        ctxSource?.Cancel();
        ctxSource = new CancellationTokenSource();

if UNITY_ANDROID

        if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
        {
            Permission.RequestUserPermission(Permission.Camera);
        }

        while (!Permission.HasUserAuthorizedPermission(Permission.Camera) && !ctxSource.IsCancellationRequested)
        {
            await Task.Yield();
        }

elif UNITY_IOS

        if (!Application.HasUserAuthorization(UserAuthorization.WebCam))
        {
            var async = Application.RequestUserAuthorization(UserAuthorization.Microphone);
            while (!async.isDone && !ctxSource.IsCancellationRequested)
            {
                await Task.Yield();
            }
        }

endif

    }

    /// <summary>
    /// Finds the device's camera and sets up the camera texture.
    /// </summary>
    private void InitializeCamera()
    {
        // var webCamDevice = GetWebCamDevice();
        // SetupPhotoBoothTexture(webCamDevice?.name);
        // isInitialized = true;
    }

    /// <summary>
    /// Sets up the camera texture with the provided texture name.
    /// </summary>
    /// <param name="textureName">The name for the created WebCamTexture</param>
    private void SetupPhotoBoothTexture(string textureName)
    {
        // var size = cameraTextureTarget.rectTransform.sizeDelta;
        // cameraTexture = new WebCamTexture(textureName, (int) size.x, (int) size.y);
        // cameraTextureTarget.texture = cameraTexture;
    }

    /// <summary>
    /// Tries to find the device's front-facing camera or returns default camera.
    /// </summary>
    /// <returns>Returns the WebCamDevice if found</returns>
    // private static WebCamDevice? GetWebCamDevice()
    // {
    //     var devices = WebCamTexture.devices;
    // 
    //     if (devices.Length == 0)
    //         return null;
    // 
    //     var webCamDevice = devices.FirstOrDefault(device => device.isFrontFacing);
    // 
    //     return webCamDevice.Equals(default(WebCamDevice)) ? devices[0] : webCamDevice;
    // }
}

}

rk132 commented 8 months ago

Thanks for reporting. πŸ‘ We'll resolve it as soon as possible.

Uralstech commented 8 months ago

Thanks for reporting. πŸ‘ We'll resolve it as soon as possible.

Thank you!

HarrisonHough commented 7 months ago

Out of curiosity, did you try removing any references/instances of this script in your game scenes before building? I would think that Unity might be smart enough not to include it in the build if there are no references. In any case we will investigate.

Uralstech commented 7 months ago

Out of curiosity, did you try removing any references/instances of this script in your game scenes before building? I would think that Unity might be smart enough not to include it in the build if there are no references. In any case we will investigate.

Nope, I didn't think of it at the time!

Uralstech commented 7 months ago

I would think that Unity might be smart enough not to include it in the build if there are no references.

Seems not @HarrisonHough. I did these tests:

I checked the built APKs and, in both instances, they contain <uses-permission android:name="android.permission.CAMERA"/>.

It seems Unity just checks all the code that exists and finds references to WebCamTexture. There is one thing I haven't checked though - as described in the issue, I have the RPM sdk in my Assets/ folder. Maybe this could affect the tests I did?

HarrisonHough commented 7 months ago

In the latest release I added an optional define symbol that can be used to disable the use of WebCamTexture. https://github.com/readyplayerme/rpm-unity-sdk-core/pull/259 Just add this define symbol in the player settings for your desired build platform and it should prevent the permission being added RPM_DISABLE_CAMERA_PERMISSION Let me know if it works for you and I will close this issue.

Uralstech commented 1 month ago

In the latest release I added an optional define symbol that can be used to disable the use of WebCamTexture. #259 Just add this define symbol in the player settings for your desired build platform and it should prevent the permission being added RPM_DISABLE_CAMERA_PERMISSION Let me know if it works for you and I will close this issue.

Thanks for this! Can confirm that it works.