Native Gallery for Android & iOS [Open Source]

Hi there,

I'd like to share a native plugin that helps you interact with Gallery/Photos on Android & iOS. With this plugin, you can:

  • Save your images/videos to the specified album in Gallery/Photos
  • Prompt the user to pick an image or video from Gallery/Photos
  • Prompt the user to pick multiple images or videos from Gallery/Photos (available on Android 18+ and iOS 14+)
  • Prompt the user to pick images and videos simultaneously (available on Android 19+ and all iOS versions)
  • Supports runtime permissions (for Android M+)

Asset Store: https://assetstore.unity.com/packages/tools/integration/native-gallery-for-android-ios-112630
Also available at: https://github.com/yasirkula/UnityNativeGallery
Discord: https://discord.gg/UJJt549AaV
GitHub Sponsors ☕

IMPORTANT: If you are targeting iOS 14 or later, you need to build your app with Xcode 12 or later to avoid any permission issues.

FAQ

  • How can I fetch the path of the saved image or the original path of the picked image on iOS?

You can't. On iOS, these files are stored in an internal directory that we have no access to (I don't think there is even a way to fetch that internal path).

  • Can't access the Gallery, it says "java.lang.ClassNotFoundException: com.yasirkula.unity.NativeGallery" in Logcat

If you are sure that your plugin is up-to-date, then enable Custom Proguard File option from Player Settings and add the following line to that file: -keep class com.yasirkula.unity.* { *; }

  • Android build fails, it says "error: attribute android:requestLegacyExternalStorage not found" in Console

android:requestLegacyExternalStorage attribute in AndroidManifest.xml fixes a rare UnauthorizedAccessException on Android 10 but requires you to update your Android SDK to at least SDK 29. If this isn't possible for you, you should open NativeGallery.aar with WinRAR or 7-Zip and then remove the <application ... /> tag from AndroidManifest.xml.

  • Nothing happens when I try to access the Gallery on Android

Make sure that you've set the Write Permission to External (SDCard) in Player Settings.

  • NativeGallery functions return Permission.Denied even though I've set "Write Permission" to "External (SDCard)"

Declare the WRITE_EXTERNAL_STORAGE permission manually in your Plugins/Android/AndroidManifest.xml file with the tools:node="replace" attribute as follows: <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="replace"/> (you'll need to add the xmlns:tools="http://schemas.android.com/tools" attribute to the <manifest ...> element)

  • Saving image/video doesn't work properly

Make sure that the filename parameter of the Save function includes the file's extension, as well

Enjoy!

15 Likes

Now live on Asset Store!

1 Like

Hi, I am trying to use your asset but I have difficulties. I can't connect the script "NativeGallery" to any Gameobject. I have the error "the script class can't be abstract". Could you please help me with this?

NativeGallery is a static class, so it can't be attached to any GameObject. Instead, you should simply call its static functions from your scripts, like this:

NativeGallery.SaveImageToGallery( myTexture, "GalleryTest", "My img {0}.png" )

See the online documentation for a more comprehensive example code.

Hi, I am having troubles making the plugin work on iOS using the Unity Cloud service.

Is there any support for it? I have not found any clue in the documentation.

Thanks.

1 Like

Honestly, I haven't thought about Unity Cloud Build support. Let me see how Cloud Build integration for plugins work.

1 Like

I've added a post-processor script to automate the XCode setup. It works fine while building from Editor but I didn't get a chance to test it on Unity Cloud Build. It is currently available at the GitHub repo: https://github.com/yasirkula/UnityNativeGallery

If you test it on UCB, please let me know the result.

Hi there!
First of all, great job on the Plugin!
I am using it to make a memory game where the user can create its own theme by using its photos from the phone gallery. I am having a little bit of trouble trying to understand some things.
- I want to check if the device supports multiple image picker to know if I should call the "GetImage" method or the "GetImages"
- I want to get the images from the gallery and create a path to store all of them, so I can call a method like "Resources.LoadAll("Sprites/Themes/" + path);"

The problem is that I don't know how to treat the path returned by GetImagesFromGallery. I tried to Debug but it didn't show me the path (I used Android Monitor Log, from Android Studio). If you could give me some advice I would be really thankful. Here is my code:

NativeGallery.Permission permission;

if (NativeGallery.CanSelectMultipleFilesFromGallery())
{
permission = NativeGallery.GetImagesFromGallery((paths) =>
{
if (paths != null)
{
Debug.Log("Path multiple Images: " + paths);
}

}, title: "Select multiple images", mime: "image/*");
}
else
{
permission = NativeGallery.GetImageFromGallery((path) =>
{

if (path != null)
{
Debug.Log("Path Single Image: " + path);
Texture2D texture = new Texture2D(2, 2);
texture.LoadImage(File.ReadAllBytes(path));
imageTest.texture = texture; //Raw Image. It worked for a single image
}
}, title: "Select single image", mime: "image/*");
}

You are using NativeGallery.CanSelectMultipleFilesFromGallery() correctly. It returns true only if the device supports multiple image picker.

The only problem with your code is that, as the callback of GetImagesFromGallery returns a string array (rather than a string), you should iterate over the paths variable:

if (paths != null)
{
    foreach( string path in paths )
    {
        Debug.Log("Path multiple Images: " + path);
    }
}

Unfortunately, Resources.LoadAll only works for files that are located inside the Resources folder of your project. So, you will have to create textures from the selected images using a code similar to the one you wrote inside GetImageFromGallery. You don't really have to copy the selected images to a single directory for this. Simply iterate over paths variable and create a texture for each path.

1 Like

Thank you so much for the feedback!

Hi there, I'm using your software to save/load images on mobile devices, and I'm very happy so far, however I'm running into a problem.

The software I'm developing requires me to save the full filepath of the images I'm saving, and have run into a problem doing this on IOS builds using the NativeGallery.

//save image taken from camera
NativeGallery.SaveImageToGallery(toSave, "PassionMaps", m_currentMap + m_imageCount);
//then grab full path so we can save it elsewhere
m_fullImagePath = NativeGallery.GetSavePath("PassionMaps", m_currentMap + m_imageCount + ".png");

As you can see, I've made the GetSavePath a public function from within the NativeGallery.cs script, but due to the way you're handling IOS image loading, using this method doesn't work on my IOS builds. (It adds a "_1" to my images, causing an incorrect filepath, and removing this string still results in an incorrect directory.)

Is there a recommended way that I can get the full filepath of the image on IOS?

Thanks,

Caleb

Plugin saves the image to a temporary location on iOS and deletes this temporary copy after the image is successfully saved to Photos. That's because Photos app internally keeps a separate copy of its images/videos. AFAIK, the full path of images in Photos app is not accessible to developers.

Therefore, on iOS, you may have to write your image to Application.persistentDataPath or another accessible directory and save its full path instead. Then, you can call the SaveImageToGallery function that takes existingMediaPath as parameter to save the image to Photos.

So, on iOS, your code should look similar to this (assuming that toSave is a Texture2D):

string fullPath = Path.Combine( Application.persistentDataPath, m_currentMap + m_imageCount + ".png" );
File.WriteAllBytes( fullPath, toSave.EncodeToPNG() );

NativeGallery.SaveImageToGallery( fullPath, "PassionMaps", Path.GetFileName( fullPath ) );
1 Like

[quote=“yasirkula”, post:12, topic: 693228]

string fullPath = Path.Combine( Application.persistentDataPath, m_currentMap + m_imageCount + ".png" );
File.WriteAllBytes( fullPath, toSave.EncodeToPNG() );

NativeGallery.SaveImageToGallery( fullPath, "PassionMaps", Path.GetFileName( fullPath ) );

[/quote]

Hi there

Thanks for such a swift response! I had to wait a few days for my IOS tester to get back to me, but your solution worked perfectly.

Thanks very much.

1 Like

Hi there!
Does anybody know if it is possible to hide the path where the images are saved so that the user can't access the photos through the android gallery? And how to delete photos from the path only through the application?

I am working on a game in which the images are saved on a path and it is a requisite that the user may only be able to get or delete the photos on this path through the game.

I would really appreciate if somebody could point me the right direction. Thanks!

You can save your photos to Application.persistentDataPath or, preferably, a subfolder of it. These images will not be visible to Gallery/Photos. However, you will not be able to use NativeGallery to save/load these images, which means you will have to create your own UI to save/load these images.

You can use the following functions save/load/delete images:

string imagesFolderPath = Path.Combine( Application.persistentDataPath, "imagesFolderName" );
string imagePath = Path.Combine( imagesFolderPath, "image name.jpeg" );

Save:
Directory.CreateDirectory( imagesFolderPath );
File.WriteAllBytes( imagePath, imageBytes );

Load:
if( File.Exists( imagePath ) )
byte[ ] imageBytes = File.ReadAllBytes( imagePath );

Delete:
if( File.Exists( imagePath ) )
File.Delete( imagePath );

List Files:
if( Directory.Exists( imagesFolderPath ) )
string[ ] files = Directory.GetFiles( imagesFolderPath );

3 Likes

I am finding a plugin which can pick .bmp images from Android Gallery so that I can get the pixel data from it for further usage. Does this plugin support .bmp format? Or only for jpg & png? Thanks!:):)

Thank you so much, yasirkula!

[quote=“garethlamyh”, post:16, topic: 693228]
I am finding a plugin which can pick .bmp images from Android Gallery so that I can get the pixel data from it for further usage. Does this plugin support .bmp format? Or only for jpg & png? Thanks!:):slight_smile:
[/quote]

Although haven’t tested it myself, the NativeGallery.LoadImageAtPath function should support bmp, gif, jpeg, png and webp formats. Here is an example code:

NativeGallery.GetImageFromGallery( ( path ) =>
{
    Debug.Log( "Image path: " + path );
    if( path != null )
    {
        // Create Texture from selected image
        Texture2D texture = NativeGallery.LoadImageAtPath( path, 1024 ); // image will be downscaled if its width or height is larger than 1024px
        if( texture == null )
        {
            Debug.Log( "Couldn't load texture from " + path );
            return;
        }
     
        // Use 'texture' here
        // ...
    }
}
1 Like

yasirkula, thanks for writing such a great plugin. I can really import a bmp image from android gallery and set it as the texture2D of a raw image. However, when I tried to use getpixel() functions to read pixel data from the texture, the texture turns white. And I do not understand what’s wrong…

here’s my code:
[quote=“yasirkula”, post:18, topic: 693228]
Although haven’t tested it myself, the NativeGallery.LoadImageAtPath function should support bmp, gif, jpeg, png and webp formats. This function is currently available on GitHub repository. Asset Store update is under review.

Here is an example code:

    private void pickBMP()
    {
        NativeGallery.Permission permission = NativeGallery.GetImageFromGallery((BMPPath) =>
       {
           Debug.Log("Image path: " + BMPPath);
           if (BMPPath != null)
           {
               Texture2D texture = NativeGallery.LoadImageAtPath(BMPPath, 1024);
               if (texture == null)
               {
                   Debug.Log("Couldn't load texture from " + BMPPath);
                   return;
               }

               Texture2D newTex = texture;
               RenderTexture tmp = RenderTexture.GetTemporary(newTex.width, newTex.height, 0, RenderTextureFormat.Default, RenderTextureReadWrite.Linear);
               UnityEngine.Graphics.Blit(newTex, tmp);
               RenderTexture previous = RenderTexture.active;
               RenderTexture.active = tmp;
               Texture2D tempImage = new Texture2D(newTex.width, newTex.height);
               tempImage.ReadPixels(new Rect(0, 0, tmp.width, tmp.height), 0, 0);
               tempImage.Apply();
               RenderTexture.active = previous;
               RenderTexture.ReleaseTemporary(tmp);
               UnityEngine.Color C = tempImage.GetPixel(500, 500);
               widthAndheightInfo.text = "Tempimage Width: " + tempImage.width + ", Height: " + tempImage.height + ", R: " + (C.r * 255f).ToString() + ", G: " + (C.g * 255f).ToString() + ", G: " + (C.b * 255f).ToString();
           }
       }, "Select a BMP image", "image/bmp", 1024);
    }

[/quote]

NativeGallery.LoadImageAtPath has an optional parameter called markTextureNonReadable, which is set to true by default. Can you try loading the image with it set to false, instead:

Texture2D texture = NativeGallery.LoadImageAtPath(BMPPath, 1024, false);

1 Like