Unity Android Galeri Etkileşimi (Resim/Video Kaydetmek veya Yüklemek)

Yayınlandı: 04 Haziran 2018 yasirkula tarafından Oyun Tasarımı, UNITY 3D içinde

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()

İ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.

  • NativeGallery.RequestPermission()

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!

yorum
  1. Mahmutcan ZAMAN dedi ki:

    Merhaba hocam dediğiniz işlemleri yaptım resimi sprite a atadım. Bu resmi resources kısmına kaydetmek istiyorum.(Oyun dosyalarında kayıtlı kalmasını istiyorum sürekli kullanabilmek için) nasıl project kısmına kayıt alabilirim aldığımız resimi?

    • yasirkula dedi ki:

      Bunu sadece editörde yapmak istiyorsanız mümkün ama anladığım kadarıyla build aldığınız oyunda yapmak istiyorsunuz, oyunu build aldıktan sonra Resources klasörünü değiştiremezsiniz. Dosyayı sadece Application.persistentDataPath veya Application.temporaryCachePath klasörlerine kaydedebilirsiniz.

  2. Ahmet Gül dedi ki:

    Hocam çok rahatsız ettim ama gelen fotoğrafı kodlarla otomatik 16 parçaya slice etme şansım var mı

  3. Mahmutcan zaman dedi ki:

    Hocam texture olarak resmi galeriden aldığımız da unity içerisinde bir image içine tam oturacak şekilde nasıl tanımlayabilirim.Yada gelen resmi hiyerarşiye nasıl kaydedebilirim.

    • yasirkula dedi ki:

      Resmi Hierarchy’e kaydetmeyi anlayamadım ama resmi Image’ın sprite’ına atayabilmek için önce LoadImageAtPath ile Texture oluşturmalı, sonra Sprite.Create ile o Texture’dan Sprite oluşturmalısınız: imageObj.sprite = Sprite.Create(texture, new Rect(0f, 0f, texture.width, texture.height), new Vector2(0.5f, 0.5f), 100.0f);

      • Mahmutcan zaman dedi ki:

        Hocam kusura bakmayın yeni başladım. Rahatsız ediyorum sürekli. The name ‘sprite’ does not exist in the current context diye bir hata aldım, dediğinizi yaparak. Bu konuda ki gördüğüm tek kaynak da sizsiniz bu konuda elinize sağlık.

      • yasirkula dedi ki:

        public Image imageObj; değişkeni oluşturup Inspector’dan değer vermeniz lazım.

  4. Ahmet Gül dedi ki:

    Hocam galeriden aldığım fotoğrafı kırpmak ve çözünürlüğünü değiştirmek istiyorum. Örneğin 1920*1080 bir fotoğrafı seçtikten sonra kare şekilde kırpıp sonradan çözünürlüğü 1000*1000 şeklinde sabit ayarlamak istiyorum. Mümkün müdür ?

    • yasirkula dedi ki:

      Resmi oyuncu elle istediği yerden mi kırpacak yoksa kod ile resmin orta kısmı otomatik mi kırpılacak?

      • Ahmet Gül dedi ki:

        Oyuncu 1000*1000 piksel
        bir kare alanın içinde sürükleyerek kırpacak. En son kendi istediği yeri seçmiş olacak ama çözünürlük 1000*1000 olacak

      • yasirkula dedi ki:

        Şu plugin ile kırpma işlemini yapabilirsiniz: https://github.com/yasirkula/UnityImageCropper. Şu örnek koddaki croppedImageResizePolicy içerisinde width ve height değerlerini 1000 yaparsanız, oyuncu ne kadar büyük veya ne kadar küçük bir alan seçmiş olursa olsun en son Texture 1000×1000 çözünürlüğünde olur. selectionMinAspectRatio ve selectionMaxAspectRatio’ya değer olarak 1f verirseniz, oyuncunun seçimi daima kare şeklinde olur.

  5. Ali dedi ki:

    Merhaba
    ResimSec.cs(56,49): error CS1501: No overload for method ‘GetImageFromGallery’ takes 4 arguments

    eklentiyi projeye aktadım fakat bu şekilde bir hata alıyorum. Nasıl çözülebilir ?

    Unity 2019.3.15f1

    • Ali dedi ki:

      Hocam githubda eklentiyi yeniledim bu sefer bu şekilde bir konsol hatası alıyorum..

      Copying assembly from ‘Temp/com.unity.multiplayer-hlapi.Runtime.dll’ to ‘Library/ScriptAssemblies/com.unity.multiplayer-hlapi.Runtime.dll’ failed

    • yasirkula dedi ki:

      O fonksiyonun dokümantasyonu eski kalmış, şimdi güncelledim. Aldığınız diğer hata NativeGallery ile alakalı değil; eğer konsolu Clear yapınca hata gidiyorsa, hatayı yoksayabilirsiniz. Aksi taktirde bir şekilde Unity’nin HLAPI multiplayer package’ı sıkıntı çıkarıyordur. Multiplayer oyun yapmıyorsanız Package Manager’dan HLAPI/Multiplayer’ı çıkarabilirsiniz.

  6. Furkan Çalışkan dedi ki:

    Hocam merhabalar,
    Kullanıcı, resim seçme menüsüne girdiğinde ana sunucuyla olan bağlantısı direk kesiliyor.
    Uygulamayı arkaplana alınca da direkt timeout hatası veriyor.
    Bunu nasıl engelleyebilirim?

    • Furkan Çalışkan dedi ki:

      Sorunu çözdüm. Aynı sorunu yaşayan arkadaşlar için yazıyorum. Telefonunuz güç tasarruf modunda ise yaptığınız uygulamayı arkaplana alır almaz görevi sonlandırıyor. Bunun için telefon ayarlarından güç tasarrufunu kapatabilir veya uygulama profiline girip yaptığınız uygulamaya özel güç tasarrufunu kapatabilirsiniz. Kod ile izin alınarak yapılabilse güzel olurdu, bilen arkadaşlar varsa yazabilirlerse memnun olurum.

  7. Saffet dedi ki:

    merhabalar. emeğiniz için çok teşekkürler..
    acemi olduğum için tam çözemedim. bu dersin finalinde bu linkteki uygulamaya benzer bir sonuç
    alabilir miyim.. yani istediğim sadede uygulamayı kullanan bilgisayarından yada android’ inden
    materyal üzerine dokusunu kendi atabilsin
    Link

  8. Faruk dedi ki:

    hocam firebase ile ilgili bir eğitim yazarmısınız

    • yasirkula dedi ki:

      Şu anda Firebase veya Google Play Services gibi servisler için ders planlamıyorum. Halihazırda yazmayı planladığım başka dersler var ama onlara bile zaman bulamıyorum şu anda.

  9. Merhabalar,
    Öncelikle paylaşımınız için çok teşekkür ederim ancak ufak bir sorum olacak:
    Kullanıcının seçtiği resmi firebase stroge’ye upload etmek istiyorum. Teoride her şey düzgün ancak android cihazımda deneyince upload işlemi işe yaramıyor.
    Android’de /storage/emulated/0/… şekilde bir konum alıyorum, konum alma işlemi çalışıyor ancak firebaseye bu konumdaki dosyayı yüklemesini söyleyince işe yaramıyor. Aynı şeyi bilgisayarda C:/user/Downloads/… tarzı bir konum ile deniyorum ve bilgisayarda sıkıntısız çalışıyor. Yine de Android’de hatayı ve sebebini çözemedim.
    Bu konuda yardımcı olabilir misiniz? Şimdiden teşekkürler

  10. Furkan Çalışkan dedi ki:

    Selamlar hocam, kullanıcıdan resim yükletip profil resmi olarak veritabanına kaydetmek istiyorum. Fakat yazdığınız konum değişkeninden ve callback fonksiyonundan pek bir şey anlayamadım.

    public string deneme;

    NativeGallery.GetImageFromGallery(deneme, “Profil Resmi”, “image/*”, 512);

    deneme kısmı hata veriyor.

    Kullanıcının, eklediğim butona bastığında galerisinin açılmasını ve oradan resim seçmesini nasıl yapabilirim?

    • yasirkula dedi ki:

      Örnek koddaki GaleridenResimCek fonksiyonunu inceleyebilirsiniz. Oradaki “// Texture’u geçici bir küp objesine ver” comment’inden sonrasını, resmi veritabanına kaydetme kodunuzla değiştirebilirsiniz.

      • Furkan Çalışkan dedi ki:

        GaleridenResimCek fonksiyonundaki konum değişkeninin türü nedir?

      • Furkan Çalışkan dedi ki:

        Yazdığınız kodu kopyala yapıştır yapınca düzeldi hocam. Sanırım ileri düzey bir kod olduğundan anlamakta zorlandım 🙂 Teşekkür ederim.

  11. enes beser dedi ki:

    çok saolun hocam peki 10 tane resim seçtirmek istesem max galeriden bunları 2d imageler içerisine atmak istiyorum nasıl olur texture ı image e dönüştürmeyi birtaz araştırdım kafam karıştı yardımcı olursanız sevinirim 2 konuda da

    • enes beser dedi ki:

      ios ve android için

    • yasirkula dedi ki:

      Texture oluşturmak için “Seçilen Resim Dosyasını Texture’a Çevirmek” kısmındaki fonksiyonu kullanabilirsiniz. Birden çok resim seçtirmek için ekranda bir + tuşunuz olabilir ve kullanıcı buna her bastığında yeniden NativeGallery.GetImageFromGallery fonksiyonunu çağırırsınız.

  12. Erol ŞIVKIN dedi ki:

    Çok teşekkürler hocam sağolun. Keşke UDEMY’de kapsamlı bir eğitim hazırlasanız.

  13. Erol Şıvkın dedi ki:

    Hocam çok yararlı bir ders olmuş. Teşekkürler. Ben basit bir puzzle yaptım da android için. Galeriden resim seçtirdim sizin metodunuzla. Seçtiğim resmi sahneye alabilir miyim? Sahnede hazır bir resim var ve ben sprite editor ile 6 ya böldüm. Kodlar şöyle;

    // Use this for initialization

    [SerializeField]
    private Transform[] pictures;

    [SerializeField]
    private GameObject winText;

    public static bool youWin;

    private void Start()
    {

    winText.SetActive(false);
    youWin = false;
    }

    private void Update()
    {

    if (pictures[0].rotation.z == 0 &&
    pictures[1].rotation.z == 0 &&
    pictures[2].rotation.z == 0 &&
    pictures[3].rotation.z == 0 &&
    pictures[4].rotation.z == 0 &&
    pictures[5].rotation.z == 0)

    {
    winText.SetActive(true);
    youWin = true;
    }
    }

    public void Kapat()
    {

    Application.Quit();

    }
    public void GaleridenResimCek()
    {
    // ÖRNEK KOD
    }

    İyi çalışmalar.

    • yasirkula dedi ki:

      Resmi kaç satır kaç sütun olacak şekilde böldünüz (örneğin 2×3)? Ayrıca, rotation.z yerine eulerAngles.z yapmanız gerekebilir.

      • Erol ŞIVKIN dedi ki:

        Hocam 6 hücre yani 2 satır 3 sütun olacak şekilde.

      • yasirkula dedi ki:

        Öncelikle projenize şu plugin’i import edin: https://github.com/yasirkula/UnityTextureOps/raw/master/TextureOps.unitypackage

        Ardından NativeGallery’den elde ettiğiniz resmi bir Texture’a çevirip bu Texture’u şu fonksiyon vasıtasıyla 6 parçaya bölebilirsiniz:

        public Sprite[] ResmiParcalaraBol( Texture2D resim )
        {
        	Texture2D[] parcalar = TextureOps.Slice( resim, resim.width / 3, resim.height / 2, TextureFormat.RGB24 ); // 2 satır 3 sütun
        	
        	Sprite[] sonuc = new Texture2D[parcalar.Length];
        	for( int i = 0; i < parcalar.Length; i++ )
        		sonuc[i] = Sprite.Create( parcalar[i], new Rect( 0.0f, 0.0f, parcalar[i].width, parcalar[i].height ), new Vector2( 0.5f, 0.5f ), 100.0f );
        	
        	return sonuc;
        }
        

Saffet için bir cevap yazın Cevabı iptal et

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google fotoğrafı

Google hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.