Merhabalar,
Bu Unity dersinde, Android ve iOS platformlarda nasıl cihazın galerisine resim/video kaydedebileceğinizi veya galeriden resim/video çekebileceğinizi göstereceğim. Bu iş için kendi yazdığım NativeGallery plugin’ini kullanacağız.
Hazırsanız başlayalım!
Kurulum
Öncelikle şu adresteki unitypackage‘ı Assets-Import Package yoluyla projenize import edin: https://github.com/yasirkula/UnityNativeGallery/releases
Alternatif olarak, asset’i Asset Store’dan da import edebilirsiniz: https://assetstore.unity.com/packages/tools/integration/native-gallery-for-android-ios-112630
Eğer Android’e build alacaksanız Edit-Project Settings-Player‘daki Write Permission‘ı External (SDCard) yapın.
Eğer iOS’a build alacaksanız, Plugins/NativeGallery/Editor/NGPostProcessBuild.cs scriptindeki PHOTO_LIBRARY_USAGE_DESCRIPTION‘ın değerini dilerseniz değiştirin. Buraya girdiğiniz yazı, kullanıcıdan galeriye erişim izni isterken gözükecek.
Fonksiyonlar
A) Resim Kaydetmek
Galeriye resim kaydetmek için 3 farklı fonksiyon mevcut:
NativeGallery.SaveImageToGallery( byte[] mediaBytes, string album, string filename, MediaSaveCallback callback = null )
Eğer resmi bir byte array‘inde tutuyorsanız bu fonksiyonu kullanın. Bir texture‘un byte array’ine EncodeToPNG veya EncodeToJPG fonksiyonlarıyla erişebilirsiniz.
album: Resmin kaydedileceği albümü belirler.
filename: Resim dosyasının ismini belirler. Dosya ismine uzantıyı da eklemeyi unutmayın.
Resim Android’de DCIM/album/filename konumuna yazılırken iOS’ta ise cihazın galeri için rezerve ettiği özel bir konuma yazılır.
callback: Eğer buraya string parametre alan bir fonksiyon verirseniz, resmin kaydedilmesi bittikten sonra bu fonksiyon çağrılır. Eğer resim başarıyla kaydedildiyse bu string’in değeri null olurken bir hata olduysa hata mesajı bu string’de depolanır.
NativeGallery.SaveImageToGallery( string existingMediaPath, string album, string filename, MediaSaveCallback callback = null )
Eğer halihazırda varolan bir resim dosyasını galeriye kaydetmek istiyorsanız bu fonksiyonu kullanın. Bu durumda resim dosyası ilgili albüme kopyalanacaktır.
existingMediaPath: Varolan resim dosyasının konumunu buraya girmelisiniz.
NativeGallery.SaveImageToGallery( Texture2D image, string album, string filename, MediaSaveCallback callback = null )
Bir Texture2D objesini galeriye kaydetmek için bu fonksiyonu kullanabilirsiniz. Eğer filename parametresi .jpg veya .jpeg ile biterse texture JPEG formatında, yoksa PNG formatında kaydedilir.
B) Video Kaydetmek
NativeGallery.SaveVideoToGallery( byte[] mediaBytes, string album, string filename, MediaSaveCallback callback = null )
Eğer video’yu bir byte array‘inde tutuyorsanız bu fonksiyonu kullanın. Mesela projenizde yer alan bir video asset’ini bu fonksiyon ile galeriye kaydetmek isterseniz, videoyu scriptinizdeki bir TextAsset değişkenine değer olarak verebilir ve mediaBytes‘a değer olarak textAssetDegiskeni.bytes verebilirsiniz.
NativeGallery.SaveVideoToGallery( string existingMediaPath, string album, string filename, MediaSaveCallback callback = null )
Eğer halihazırda varolan bir video dosyasını galeriye kaydetmek istiyorsanız bu fonksiyonu kullanın. Bu durumda video dosyası ilgili albüme kopyalanacaktır.
C) Galeriden Resim Çekmek
NativeGallery.GetImageFromGallery( MediaPickCallback callback, string title = "", string mime = "image/*" )
callback: Kullanıcı galeriden bir resim seçince veya işlemi iptal edince bu fonksiyon çağrılır. Buraya vereceğiniz fonksiyon bir string parametre almak zorundadır. Eğer işlem iptal edildiyse bu parametrenin değeri null olurken aksi taktirde seçilen resim dosyasının konumu bu string’de depolanır.
title: Kullanıcının karşısına gelen resim seçme diyaloğunun başlığında yazan yazıyı belirler. Bu parametrenin iOS’ta bir etkisi yoktur.
mime: Galerideki resimleri filtrelemeye yarar. Varsayılan olarak tüm resimler gözükürken, mime’ı örneğin “image/jpeg” yaparsanız sadece JPEG resimler görünür. Bu parametrenin iOS’ta bir etkisi yoktur çünkü iOS’ta seçilen resimler daima PNG formatında olur.
D) Galeriden Video Çekmek
NativeGallery.GetVideoFromGallery( MediaPickCallback callback, string title = "", string mime = "video/*" )
Bu fonksiyon, GetImageFromGallery fonksiyonuna benzer şekilde çalışır. Galeriden bir video seçilince veya işlem iptal edilince callback fonksiyonu çağrılır.
E) Çalışma Zamanı İzinleri (Runtime Permissions) Hakkında
Android 6.0 sürümü itibariyle artık önemli bir Android fonksiyonuna erişmeden önce, çalışma zamanında bu fonksiyona erişim izni istemek zorundayız. NativeGallery fonksiyonları çalışmadan önce otomatik olarak izin isterler ancak dilerseniz kendi başınıza da izinlerin mevcut durumunu sorgulayabilir veya izin isteyebilirsiniz.
NativeGallery.CheckPermission( PermissionType permissionType)
İzinlerin durumunu sorgular ve bir NativeGallery.Permission enum‘u döndürür. Eğer galeriye erişim iznimiz varsa bu enum’un değeri Permission.Granted olurken eğer henüz iznimiz yoksa Permission.ShouldAsk olur. Eğer kullanıcı karşısına gelen izin ekranını “Bir daha sorma” seçili bir şekilde reddederse veya kullanıcının cihazında aktif olan bir ebeveyn kontrol sistemi bu iznin verilmesini engelliyorsa, Permission.Denied döndürülür. Bu durumda kullanıcı izni cihazın ayarlar menüsünden elle vermek zorundadır. Böyle bir durumla baş başa kalırsanız, kullanıcıya ayarlar menüsünden izin vermesini söyleyen bir uyarı penceresi göstermenizi öneririm. permissionType‘ın değeri, GetImage ve GetVideo fonksiyonları için NativeGallery.PermissionType.Read, SaveImage ve SaveVideo fonksiyonları için ise NativeGallery.PermissionType.Write olmalıdır.
NativeGallery.RequestPermission( PermissionType permissionType)
Galeriye erişim izni ister ve sonucu bir NativeGallery.Permission enum’unda döndürür. Resim kaydetmeye/çekmeye yarayan NativeGallery fonksiyonları bu fonksiyonu otomatik olarak çağırıp sonucu döndürürler.
F) Seçilen Resim Dosyasını Texture’a Çevirmek
NativeGallery.LoadImageAtPath( string imagePath, int maxSize = -1, bool markTextureNonReadable = true, bool generateMipmaps = true, bool linearColorSpace = false )
Bu fonksiyon vasıtasıyla bir resim dosyasını hızlıca bir Texture2D objesine çevirebilirsiniz.
imagePath: Texture2D olmasını istediğiniz resim dosyasının konumunu buraya girmelisiniz.
maxSize: Eğer resmin genişliği veya yüksekliği bu değerden büyük olursa, resmin bu boyuta küçültülmüş bir versiyonu döndürülür. Varsayılan olarak bir büyüklük limiti yoktur ancak resim ne kadar büyük olursa hafızada o kadar çok yer kaplayacağı için bu parametreye 512, 1024 vs. gibi işinizi görmeye yetecek bir değer vermeniz şiddetle önerilir.
markTextureNonReadable: Texture2D objesinin RAM’deki kopyasını silerek hafızayı rahatlatır. Bunun dezavantajı, artık resmin piksellerine elle erişemezsiniz; yani eğer GetPixel veya SetPixel fonksiyonlarını kullanıyorsanız bu parametreyi false yapın.
generateMipmaps: Texture’un mipmap’lerinin olup olmayacağını belirler.
linearColorSpace: Texture’un linear renk uzayını mı yoksa gamma renk uzayını mı (varsayılan) kullanacağını belirler.
Örnek Kod
Aşağıdaki örnek kodun 3 işlevi bulunmaktadır:
- ekranın soluna tıklarsanız bir screenshot alınır ve bu screenshot galeriye kaydedilir
- ekranın ortasına tıklarsanız galeriden bir resim seçmeniz istenir ve seçilen resim geçici bir küp objesine texture olarak verilir
- ekranın sağına tıklarsanız galeriden bir video seçmeniz istenir ve seçilen video oynatılır
void Update()
{
if( Input.GetMouseButtonDown( 0 ) )
{
if( Input.mousePosition.x < Screen.width / 3 )
{
// Screenshot al ve galeriye kaydet
StartCoroutine( ScreenshotAlVeKaydet() );
}
else
{
// Eğer halihazırda bir resim/video seçme diyaloğu aktifse,
// o diyalog kapatılana kadar bir şey yapma
if( NativeGallery.IsMediaPickerBusy() )
return;
if( Input.mousePosition.x < Screen.width * 2 / 3 )
{
// Galeriden bir PNG resim çek
// Eğer resmin genişliği veya yüksekliği 512 pikselden büyükse, resmi ufalt
GaleridenResimCek( 512 );
}
else
{
// Galeriden bir video çek
GaleridenVideoCek();
}
}
}
}
private IEnumerator ScreenshotAlVeKaydet()
{
yield return new WaitForEndOfFrame();
Texture2D ss = new Texture2D( Screen.width, Screen.height, TextureFormat.RGB24, false );
ss.ReadPixels( new Rect( 0, 0, Screen.width, Screen.height ), 0, 0 );
ss.Apply();
// Screenshot'ı galeriye kaydet
Debug.Log( "İzin durumu: " + NativeGallery.SaveImageToGallery( ss, "Örnek albüm", "Resim.png" ) );
// Artık screenshot'a ihtiyacımız kalmadı
Destroy( ss );
}
private void GaleridenResimCek( int maksimumBuyukluk )
{
NativeGallery.Permission izin = NativeGallery.GetImageFromGallery( ( konum ) =>
{
Debug.Log( "Seçilen resmin konumu: " + konum );
if( konum != null )
{
// Seçilen resmi bir Texture2D'ye çevir
Texture2D texture = NativeGallery.LoadImageAtPath( konum, maksimumBuyukluk );
if( texture == null )
{
Debug.Log( konum + " konumundaki resimden bir texture oluşturulamadı." );
return;
}
// Texture'u geçici bir küp objesine ver
GameObject kup = GameObject.CreatePrimitive( PrimitiveType.Cube );
kup.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 5f;
kup.transform.forward = -Camera.main.transform.forward;
kup.transform.localScale = new Vector3( 1f, texture.height / (float) texture.width, 1f );
Material material = kup.GetComponent<Renderer>().material;
if( !material.shader.isSupported ) // eğer Standard shader desteklenmiyorsa Diffuse shader'ı kullan
material.shader = Shader.Find( "Legacy Shaders/Diffuse" );
material.mainTexture = texture;
// 5 saniye sonra küp objesini yok et
Destroy( kup, 5f );
// Küp objesi ile birlikte Texture2D objesini de yok et
// Eğer prosedürel bir objeyi (Texture2D) işiniz bitince yok etmezseniz,
// mevcut scene'i değiştirene kadar obje hafızada kalmaya devam eder
Destroy( texture, 5f );
}
}, "Bir resim seçin", "image/png" );
Debug.Log( "İzin durumu: " + izin );
}
private void GaleridenVideoCek()
{
NativeGallery.Permission izin = NativeGallery.GetVideoFromGallery( ( konum ) =>
{
Debug.Log( "Seçilen videonun konumu: " + konum );
if( konum != null )
{
// Seçilen videoyu oynat
Handheld.PlayFullScreenMovie( "file://" + konum );
}
}, "Bir video seçin" );
Debug.Log( "İzin durumu: " + izin );
}
Böylece bu dersin sonuna geldik. Sonraki derslerde görüşmek üzere!