Konuyu Oyla:
  • Derecelendirme: 5/5 - 2 oy
  • 1
  • 2
  • 3
  • 4
  • 5
Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore
#1
Delphi ile Android 11 SDK 30 Kapsamlı Depolama
Scoped Storage : SAF & MediaStore API


Android 10 Gizlilik Değişiklikleri

Android 10 (API seviyesi 29), kullanıcıların gizliliğini daha iyi korumak için bir dizi özellik ve davranış değişikliği sunar. Bu değişiklikler, kullanıcıların verileri ve uygulamalara sağladıkları yetenekler üzerinde sahip oldukları şeffaflığı ve kontrolü genişletir. Bu özellikler, uygulamanızın bağlı olduğu belirli davranışların veya verilerin, platformun eski sürümlerine kıyasla farklı davranabileceği anlamına gelebilir. Uygulamanız, kullanıcı verilerini işlemek için mevcut en iyi uygulamaları izliyorsa, uygulamanız üzerindeki etkiler minimum düzeyde olmalıdır.
 
Uygulama dosyalarına ve medyaya yönelik harici depolama erişimi
Varsayılan olarak, Android 10 ve sonraki sürümleri hedefleyen uygulamalara harici depolamaya veya kapsamlı depolamaya kapsamlı erişim verilir. Bu tür uygulamalar, depolamayla ilgili herhangi bir kullanıcı izni istemeye gerek kalmadan harici bir depolama aygıtında aşağıdaki dosya türlerini görebilir:
·         getExternalFilesDir (TPath.GetPublicPath) kullanılarak erişilen uygulamaya özel dizindeki dosyalar.
·         Uygulamanın medya mağazasında oluşturduğu fotoğraflar, videolar ve ses klipleri.
Kapsamlı depolama ile harici depolama cihazlarına kaydedilmiş dosyaları paylaşma, ulaşma ve değiştirme  hakkında daha fazla bilgi için harici depolama dosyaları yönetme ve medya dosyalarını ulaşma ve değiştirme kılavuzlarına bakın.
https://developer.android.com/about/vers...cy/changes
 
Android 11 (API 30) Gizlilik ve Güvenlik
Android 11'i hedefleyen uygulamaların, yalnızca kendi oluşturdukları harici depolama alanındaki dosyalara ("scoped storage") erişmeye izni vardır. Ayrıca uygulamaya özel bir dizinde bulunan dosyalara ek olarak; ana dizindeki "Müzik", "Resimler" veya "Video" dizinleri erişime dahildir. Diğer herhangi bir dosyaya ancak ve ancak "Depolama Erişim Çerçevesi /  Storage Access Framework" aracılığıyla ve kullanıcı izni yoluyla erişilebilir.
Uygulamada belirlenen konum izni sayesinde üretilen EXIF konum verisinin başarıyla işlenmiş olmasından emin olmak için Android 11, video ve fotoğraf kayıt "niyetlerini / intents"  yalnızca sistem kamera uygulamasına olmak üzere sınırlar.
https://tr.wikipedia.org/wiki/Android_11
Android 11, kullanıcı gizliliğini geliştirmek için aşağıdakiler dahil olmak üzere değişiklikler ve kısıtlamalar getirmiştir:
  • Kapsamlı depolama mecburiyeti : Harici depolama dizinlerine erişim, uygulamaya özel bir dizin ve uygulamanın oluşturduğu belirli ortam türleriyle sınırlıdır.
  • İzinlerin otomatik olarak sıfırlanması : Kullanıcılar bir uygulamayla birkaç aydır etkileşimde bulunmadıysa, sistem uygulamanın hassas izinlerini otomatik olarak sıfırlar.
  • Arka planda konum erişimi : Uygulamalara arka planda konum izni vermek için kullanıcıların sistem ayarlarına yönlendirilmesi gerekir.
  • Paket görünürlüğü : Bir uygulama, cihazda yüklü uygulamaların listesini sorguladığında, döndürülen liste filtrelenir.
https://developer.android.com/about/vers...hanges-all
  • Kapsamlı depolama sınırlaması
  • Arka planda konum erişimi ve resim, video ve ses dosyalarına ulaşma için yeni izinler
Google Play, Tüm dosyalara erişim adlı özel uygulama erişimi de dahil olmak üzere yüksek riskli ya da hassas izinlerin kullanımını kısıtlıyor. Bu, yalnızca Android 11'i hedefleyen (API düzeyi 30) uygulamalar ve Android 11'de eklenen MANAGE_EXTERNAL_STORAGE iznini beyan eden uygulamalar için geçerlidir. Ayrıca bu politika, READ_EXTERNAL_STORAGE izninin kullanımını etkilemez.
Uygulamanız MANAGE_EXTERNAL_STORAGE iznine erişilmesini gerektirmiyorsa uygulamanızı başarılı bir şekilde yayınlayabilmek için bu izni uygulamanızın manifest dosyasından kaldırmanız gerekir. Politikaya uygun alternatiflerin uygulanması hakkında ayrıntılı bilgiyi aşağıda bulabilirsiniz.
Uygulamanız kabul edilebilir kullanım ile ilgili politika gereksinimlerini karşılıyorsa veya istisna olarak tutulmaya uygunsa Play Console'daki Beyan Formu'nu kullanarak bunu ve diğer yüksek riskli izinleri bildirmeniz gerekir.
Politika gereksinimlerini karşılamayan veya Beyan Formu gönderilmeyen uygulamalar Google Play'den kaldırılabilir.
Tüm dosyalara erişim iznine yalnızca uygulamanız, gizliliği daha fazla koruyan en iyi uygulamaları verimli bir şekilde kullanamadığında (ör. Depolama Erişim Çerçevesi veya Media Store API kullanımı) erişmelisiniz.
Buna ek olarak, uygulamanın izin kullanımı, izin verilen kullanımlar kapsamına girmeli ve uygulamanın temelişleviyle doğrudan bağlantılı olmalıdır. Temel işlev, uygulamanın asıl amacı olarak tanımlanır. Bu temel işlev olmadan uygulama "çalışmaz" veya kullanışlı olmaz. Temel işlevin yanı sıra bu temel işlevi oluşturan tüm temel özellikler, uygulamanın açıklamasında belirgin bir şekilde belgelenmeli ve tanıtılmalıdır.
https://support.google.com/googleplay/an...7955?hl=tr

Paylaşılan depolamaya genel bakış

Diğer uygulamalar tarafından erişilebilen veya erişilmesi gereken ve kullanıcı uygulamanızın yüklemesini kaldırsa bile kaydedilen kullanıcı verileri için paylaşılan depolamayı kullanır.
Android, paylaşılabilir veri türlerini depolamak ve bunlara erişmek için aşağıdaki API'ler sağlar:
  • Medya içeriği: Sistem, bu tür dosyalar için standart genel dizinler sağlar, böylece kullanıcının tüm fotoğrafları için ortak bir konumu, tüm müzik ve ses dosyaları için başka bir ortak konumu vb. vardır. Uygulamanız, platformu MediaStore API'sini kullanarak bu içeriğe erişebilir.
  • Belgeler ve diğer dosyalar: Sistem, PDF belgeleri ve EPUB biçimini kullanan kitaplar gibi diğer dosya türlerini içeren özel bir dizine sahiptir. Uygulamanız, platformun Depolama Erişim Çerçevesini (SAF) kullanarak bu dosyalara erişebilir.
  • Veritabanları : Android 11 (API düzeyi 30) ve sonraki sürümlerde sistem, birden fazla uygulamanın kullanabileceği büyük Veritabanlarıni önbelleğe alır. Bu Veritabanları, makine öğrenimi ve medya oynatma gibi kullanma vakalarını destekleyebilir. Uygulamalar, BlobStoreManager API'yi kullanarak bu paylaşılan Veritabanlarıne erişebilir.
Bu API'ler hakkında daha fazla bilgi için aşağıdaki kılavuzlara bakınız: https://developer.android.com/training/d...age/shared
 
 
Kapsamlı Depolama / Scoped Storage

Android 10 ve 11 gizlilik ve güvenlik değişikliklerinden sonra harici depolamaya erişim kapsamlı hale getirilmiş olup, artık dosyalara ulaşmanın 4 ana yolu mevcuttur:
1.   Depolama Erişim Çerçevesi  / Storage Access Framework  (SAF)
2.   MediaStore API
3.   All Files Access API
4.   Paylaşmalı Veritabanları API
 
All Files Access API (ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION izni ve ACTION_MANAGE_STORAGE niyeti) sadece dosya yöneticisi ve antivirus türü uygulamalar için çıkarılmış olup, diğer tüm uygulamalar Google Play Store tarafından reddedilmektedir.
Paylaşmalı Veritabanları / Databases (BlobStoreManager) API de sadece Android 11 SDK 30 ve üstünde çalışmakta olup, Android 10 ve önceki sürümlerde çalışmamaktadır.
Dolayısıyla bu iki API konuya dahil edilmemiştir.

Depolama Erişim Çerçevesi / Storage Access Framework  (SAF)

Android 4.4 (API seviyesi 19), Storage Access Framework (SAF) sınıfını sunmuştur. SAF, kullanıcıların tercih ettikleri tüm belge depolama sağlayıcılarında belgelere, resimlere ve diğer dosyalara göz atmasını ve bunları açmasını kolaylaştırır. Standart, kullanımı kolay bir kullanıcı arayüzü, kullanıcıların dosyalara göz atmasına ve uygulamalar ve sağlayıcılar arasında tutarlı bir şekilde son bilgilere erişmesine olanak tanır.
Bulut veya yerel depolama hizmetleri, DocumentsProvider hizmetlerini kapsayan bir uygulama uygulayarak bu ekosisteme katılabilir. Bir sağlayıcının belgelerine erişmesi gereken istemci uygulamaları, yalnızca birkaç satır kodla SAF ile entegre olabilir.
SAF aşağıdakileri ihtiva eder:
  • Belge sağlayıcı —Bir depolama hizmetinin (Google Drive gibi) yönettiği dosyaları ortaya çıkarmasına izin veren bir içerik sağlayıcı. Bir belge sağlayıcı, sınıfın bir alt DocumentsProvider sınıfı olarak uygulanır. Belge sağlayıcı şeması, geleneksel bir dosya hiyerarşisine dayanır, ancak belge sağlayıcınızın verileri fiziksel olarak nasıl depolayacağı size bağlıdır. Android platformu, İndirilenler, Görüntüler ve Videolar gibi çeşitli yerleşik belge sağlayıcıları içerir.
  • İstemci uygulaması Her bir özel uygulama vakaları olan ACTION_CREATE_DOCUMENT, ACTION_OPEN_DOCUMENT ve ACTION_OPEN_DOCUMENT_TREE niyet eylemlerini (intent action) çağırır ve belge sağlayıcıları tarafından döndürülen dosyaları alır.
  • Seçici —Kullanıcıların, istemci uygulamasının arama kriterlerini karşılayan tüm belge sağlayıcılarından belgelere erişmesine olanak tanıyan bir sistem kullanıcı arabirimi.
SAF'ın sunduğu özelliklerden bazıları şunlardır:
  • Kullanıcıların yalnızca tek bir uygulamadan değil, tüm belge sağlayıcılardan gelen içeriğe göz atmasına olanak tanır.
  • Uygulamanızın, bir belge sağlayıcıya ait belgelere uzun vadeli, kalıcı erişime sahip olmasını mümkün kılar. Bu erişim sayesinde kullanıcılar sağlayıcıya dosya ekleyebilir, düzenleyebilir, kaydedebilir ve silebilir.
  • Yalnızca sürücü takılıyken görünen USB depolama sağlayıcıları gibi birden çok kullanıcı hesabını ve geçici kökleri destekler.
Genel bakış
SAF, DocumentsProvider sınıfın bir alt sınıfı olan bir içerik sağlayıcı etrafında toplanır. Bir belge sağlayıcı içinde veriler, geleneksel bir dosya hiyerarşisi olarak yapılandırılmıştır:

storage_datamodel.png
Şekil 1. Belge sağlayıcı veri modeli. Bir Kök, tek bir Belgeye işaret eder ve ardından tüm ağacın yayılmasını başlatır.
Aşağıdakilere dikkat ediniz:
  • Her belge sağlayıcı, bir belge ağacını keşfetmeye başlama noktaları olan bir veya daha fazla 'kök' bildirir. Her kökün benzersiz bir değeri vardır COLUMN_ROOT_ID ve bu kökün altındaki içeriği temsil eden bir belgeye (bir dizine) işaret eder. Kökler, birden çok hesap, geçici USB depolama aygıtı veya kullanıcı oturum açma/oturum kapatma gibi kullanım durumlarını desteklemek için tasarım gereği dinamiktir.
  • Her kökün altında tek bir belge bulunur. Bu belge 1'den N'ye kadar belgeye işaret eder ve bunların her biri sırayla 1'den N'ye kadar  belgeye işaret edebilir.
  • Her depolama arka ucu, benzersiz bir COLUMN_DOCUMENT_ID. Cihaz yeniden başlatmaları arasında kalıcı URI izinleri için kullanıldığından, belge kimlikleri benzersiz olmalı ve yayınlandıktan sonra değişmemelidir.
  • Belgeler, açılabilir bir dosya (belirli bir MIME türüyle) veya ek belgeler içeren bir dizin ( MIME_TYPE_DIR MIME türüyle) olabilir.
  • Her belge, COLUMN_FLAGS tarafından açıklandığı gibi farklı yeteneklere sahip olabilir. Örneğin FLAG_SUPPORTS_WRITE, FLAG_SUPPORTS_DELETE ve FLAG_SUPPORTS_THUMBNAIL. Aynı COLUMN_DOCUMENT_ID çoklu dizinleri dahil edilebilir.
Kontrol akışı
Yukarıda belirtildiği gibi, belge sağlayıcı veri modeli, geleneksel bir dosya hiyerarşisine dayanmaktadır. Ancak DocumentsProvider API kullanarak erişebildiğiniz sürece verilerinizi fiziksel olarak istediğiniz gibi saklayabilirsiniz. Örneğin, verileriniz için etiket tabanlı bulut depolamayı kullanabilirsiniz.
Şekil 2, bir fotoğraf uygulamasının depolanan verilere erişmek için SAF'ı nasıl kullanabileceğini gösterir:

storage_dataflow.png
Şekil2. Depolama Erişim Çerçevesi akış şeması
Aşağıdakilere dikkat ediniz:
  • SAF'ta sağlayıcılar ve müşteriler doğrudan etkileşime girmez. İstemci, dosyalarla etkileşim (yani, dosyaları okumak, düzenlemek, oluşturmak veya silmek) için izin ister.
  • Bir uygulamanın (bu örnekte, bir fotoğraf uygulaması) ACTION_OPEN_DOCUMENT veya ACTION_CREATE_DOCUMENT niyeti etkileşimi. Niyet /intent şartları daha da hassaslaştırmak için filtreler içerebilir; örneğin, "image” MIME türüne sahip tüm açılabilir dosyaları verir.
  • Niyet harekete geçtiğinde, sistem seçici her kayıtlı sağlayıcıya gider ve kullanıcıya eşleşen içerik köklerini gösterir.
  • Seçici, temeldeki belge sağlayıcıları çok farklı olsa bile, kullanıcılara belgelere erişmek için standart bir arabirim sağlar. Örneğin, şekil 2 bir Google Drive sağlayıcısını, bir USB sağlayıcısını ve bir bulut sağlayıcısını göstermektedir.
Şekil 3, görüntüleri arayan bir kullanıcının İndirilenler klasörünü seçtiği bir seçiciyi göstermektedir. Ayrıca, istemci uygulamasının kullanabileceği tüm kökleri de gösterir.

storage_picker.svg
Şekil 3. (Sistem Dosya) Seçici
Kullanıcı İndirilenler klasörünü seçtikten sonra resimler görüntülenir. Şekil 4, bu işlemin sonucunu göstermektedir. Kullanıcı artık bu görüntülerle sağlayıcının ve istemci uygulamasının desteklediği şekillerde etkileşim kurabilir.

storage_photos.svg
Şekil 4. Sistem seçicide görüntülendiği şekliyle İndirilenler klasöründe saklanan resimler

İstemci uygulaması yazma
Android 4.3 ve önceki sürümlerde, uygulamanızın başka bir uygulamadan dosya almasını istiyorsanız, ACTION_PICK veya ACTION_GET_CONTENT gibi bir niyeti çağırılması gerekir. Kullanıcı daha sonra içinden bir dosya seçeceği tek bir uygulama seçmeli ve seçilen uygulama, kullanıcının mevcut dosyalara göz atması ve aralarından seçim yapması için bir kullanıcı arabirimi sağlamalıdır.
Android 4.4 (API düzeyi 19) ve sonraki sürümlerde, ACTION_OPEN_DOCUMENT kullanıcının diğer uygulamaların kullanıma sunduğu tüm dosyalara göz atmasına olanak tanıyan, sistem kontrollü bir seçici kullanıcı arabirimini görüntüleyen niyeti kullanma ek seçeneğiniz vardır. Bu tek kullanıcı arayüzünden kullanıcı, desteklenen uygulamalardan herhangi birinden bir dosya seçebilir.
Android 5.0 (API düzeyi 21) ve sonraki sürümlerde, kullanıcının bir istemci uygulamasının erişmesi için bir dizin seçmesine olanak tanıyan ACTION_OPEN_DOCUMENT_TREE niyetini de kullanabilirsiniz.
Not: ACTION_OPEN_DOCUMENT niyetinin ACTION_GET_CONTENT‘in yerine geçmesi amaçlanmamıştır. Kullanma Vakası uygulamanızın ihtiyaçlarına bağlıdır:
  • ACTION_GET_CONTENT Uygulamanızın yalnızca verileri okumasını veya içe aktarmasını istiyorsanız kullanın. Bu yaklaşımla uygulama, resim dosyası gibi verilerin bir kopyasını içe aktarır.
  • ACTION_OPEN_DOCUMENT Uygulamanızın bir belge sağlayıcıya ait belgelere uzun süreli, kalıcı erişime sahip olmasını istiyorsanız kullanın. Bir örnek, kullanıcıların bir belge sağlayıcıda depolanan görüntüleri düzenlemesine izin veren bir fotoğraf düzenleme uygulaması olabilir.
Sistem seçici kullanıcı arabirimini kullanarak dosya ve dizinlere göz atmayı nasıl destekleyeceğiniz hakkında daha fazla bilgi için belgelere ve diğer dosyalara nasıl erişileceğine dair kılavuza bakın.
https://developer.android.com/guide/topi...t-provider
 
Paylaşılan depolama alanından belgelere ve diğer dosyalara erişme

Android 4.4 (API düzeyi 19) ve sonraki sürümleri çalıştıran cihazlarda, uygulamanız Depolama Erişim Çerçevesini kullanarak harici depolama birimleri ve bulut tabanlı depolama dahil olmak üzere bir belge sağlayıcıyla etkileşim kurabilir. Bu çerçeve, kullanıcıların bir belge sağlayıcı seçmek için bir sistem seçiciyle etkileşime girmesine ve uygulamanızın oluşturacağı, açacağı veya değiştireceği belirli belgeleri ve diğer dosyaları seçmesine olanak tanır.
Kullanıcı, uygulamanızın erişebileceği dosyaları veya dizinleri seçmeye dahil olduğundan, bu mekanizma herhangi bir sistem izni gerektirmez ve kullanıcı denetimi ve gizliliği artırılır. Ayrıca, uygulamaya özel bir dizinin dışında ve medya deposunun dışında depolanan bu dosyalar, uygulamanızın yüklemesi kaldırıldıktan sonra cihazda kalır.
Çerçeveyi kullanmak aşağıdaki adımları içerir:
  1. Bir uygulama, depolamayla ilgili bir eylem içeren bir niyeti çağırır. Bu eylem, çerçevenin kullanıma sunduğu belirli bir kullanma vakasına karşılık gelir.
  2. Kullanıcı, bir belge sağlayıcısına göz atmasına ve depolamayla ilgili eylemin gerçekleştiği bir konum veya belge seçmesine olanak tanıyan bir Sistem Seçici gösterilir.
  3. Uygulama, kullanıcının seçtiği konumu veya belgeyi temsil eden bir URI'ye okuma ve yazma erişimi kazanır. Bu URI'yi kullanarak uygulama, seçilen konumda işlemler gerçekleştirebilir.
Not: Uygulamanız harici bir depolama birimindeki medya dosyalarına erişiyorsa, bu tür dosyalara erişmek için uygun bir arabirim sağlayan medya deposunu kullanmayı düşünün.
Ancak uygulamanız medya deposunu kullanıyorsa, diğer uygulamaların medya dosyalarına erişmek için READ_EXTERNAL_STORAGE iznini istemeniz gerekir. Android 9 (API düzeyi 28) veya daha eski sürümleri çalıştıran cihazlarda, READ_EXTERNAL_STORAGE uygulamanızın oluşturduğu medya dosyaları da dahil olmak üzere herhangi bir medya dosyasına erişim izni istemesi gerekir.
Bu kılavuz, çerçevenin dosyalar ve diğer belgelerle çalışmak için desteklediği farklı kullanım durumlarını açıklar. Ayrıca, kullanıcı tarafından seçilen konumda işlemlerin nasıl gerçekleştirileceğini de açıklar.

Belgelere ve diğer dosyalara erişmek için vakaları kullanın

Storage Access Framework, dosyalara ve diğer belgelere erişmek için aşağıdaki Kullanma Vakalarını destekler.
Yeni bir dosya oluşturun
ACTION_CREATE_DOCUMENT niyet eylemi, kullanıcıların belirli bir yerde bir dosyayı kaydetmesi için izin verir.
Bir belge veya dosya açın
ACTION_OPEN_DOCUMENT niyet eylemi, kullanıcıların belirli bir belgeyi açması veya dosyayı seçmesi için izin verir.
Bir dizinin içeriğine erişim izni verin
ACTION_OPEN_DOCUMENT_TREE Android 5.0 (API düzeyi 21) ve sonraki sürümlerde bulunan niyet eylemi, kullanıcıların belirli bir dizini seçmesine olanak tanıyarak uygulamanızın bu dizindeki tüm dosyalara ve alt dizinlere erişmesine izin verir.
Aşağıdaki bölümler, her bir kullanma vakasının nasıl yapılandırılacağına ilişkin rehberlik sağlar.

Yeni bir dosya oluşturun

ACTION_CREATE_DOCUMENT Sistem dosyası seçiciyi yüklemek ve kullanıcının bir dosyanın içeriğini yazacağı konumu seçmesine izin vermek için niyet eylemini (intent action) kullanır. Bu işlem, diğer işletim sistemlerinin kullandığı "farklı kaydet" iletişim kutularında kullanılana benzer.
Not: ACTION_CREATE_DOCUMENT mevcut bir dosyanın üzerine yazılamaz. Uygulamanız aynı ada sahip bir dosyayı kaydetmeye çalışırsa, sistem dosya adının sonuna parantez içinde bir sayı ekler.

Uygulamanız yeni dosyayı örneğin confirmation.pdf, zaten o ada sahip dosya bulunan bir dizinde kaydetmeye çalışırsa, sistem yeni dosyayı confirmation(1).pdf adıyla kaydeder.
Niyeti yapılandırırken, dosyanın adını ve MIME türünü belirtin. İsteğe bağlı olarak, EXTRA_INITIAL_URI  ekstra niyeti kullanarak, dosya seçicinin ilk yüklediğinde görüntülemesi gereken dosyanın veya dizinin URI'sini belirtin.
Aşağıdaki kod parçacığı, bir dosya oluşturma niyetinin nasıl oluşturulacağını ve çağrılacağını gösterir:

// PDF belgesi oluşturma için talep kodu.
 const Dosya_Olustur : integer = 11;  //CREATE_FILE = 1

 procedure PdfDosyasiOlustur(seciciBaslangicUri : JNet_Uri);
 var
   Intent : JIntent;
 begin
   Intent := TJIntent.Create;
   Intent.setAction(TJIntent.JavaClass.ACTION_CREATE_DOCUMENT);
   Intent.addCategory(TJIntent.JavaClass.CATEGORY_OPENABLE);
   Intent.setType(StringToJString('application/pdf'));
   Intent.putExtra(TJIntent.JavaClass.EXTRA_TITLE,StringToJString('fatura.pdf'));

   // İsteğe bağlı olarak, uygulamanız dosyayı oluşturduğunda
   // sistem dosya seçici tarafından açılacak dizin için bir URI belirleyin.
   Intent.putExtra(TJDocumentsContract.JavaClass.EXTRA_INITIAL_URI,
     JParcelable(seciciBaslangicUri));

   MainActivity.startActivityForResult(Intent, Dosya_Olustur);
 end;


Bir dosya açın

Uygulamanız, kullanıcıların benzerleriyle paylaşmak veya başka belgelere aktarmak isteyebilecekleri verileri girdiği depolama birimi olarak belgeleri kullanabilir. Birkaç örnek, bir kullanıcının bir üretkenlik belgesi açmasını veya bir EPUB dosyası olarak kaydedilmiş bir kitabı açmasını içerir.
Bu durumlarda, ACTION_OPEN_DOCUMENT sistemin dosya seçici uygulamasını açan niyeti çağırarak kullanıcının açılacak dosyayı seçmesine izin verin. Yalnızca uygulamanızın desteklediği dosya türlerini göstermek için bir MIME türü belirtin. Ayrıca, isteğe bağlı olarak, EXTRA_INITIAL_URI  ekstra niyetini kullanarak dosya seçicinin ilk yüklendiğinde görüntülemesi gereken dosyanın URI'sini belirtebilirsiniz.
Aşağıdaki kod parçacığı, bir PDF belgesi açma niyetinin nasıl oluşturulacağını ve çağrılacağını gösterir:

  // Bir PDF belgesi seçmek için talep kodu.
 // const Pdf_Dosyasi_Sec : integer = 22;  //PICK_PDF_FILE = 2

 procedure PdfDosyasiOlustur(seciciBaslangicUri : JNet_Uri);
 var
   Intent: JIntent;
 begin
   Intent := TJIntent.Create;
   Intent.setAction(TJIntent.JavaClass.ACTION_OPEN_DOCUMENT);
   Intent.addCategory(TJIntent.JavaClass.CATEGORY_OPENABLE);
   Intent.setType(StringToJString('application/pdf'));

   // İsteğe bağlı olarak, sistem dosya seçici yüklendiğinde
   // gösterilecek dosya için bir URI belirleyin.
   Intent.putExtra(TJDocumentsContract.JavaClass.EXTRA_INITIAL_URI,
      JParcelable(seciciBaslangicUri));

   TAndroidHelper.Activity.startActivityForResult(Intent, Pdf_Dosyasi_Sec);
 end;

Bir dizinin içeriğine erişim izni verin

Not: Bu bölümde açıklanan niyet eylemi (intent action), Android 5.0 (API düzeyi 21) ve sonraki sürümlerde mevcuttur.

Dosya yönetimi ve medya oluşturma uygulamaları, genellikle bir dizin hiyerarşisindeki dosya gruplarını yönetir. Uygulamanızda bu özelliği sağlamak için, Android 11'den (API düzeyi 30) başlayan bazı istisnalar dışında, kullanıcının bir dizin ağacının tamamına erişim vermesine izin veren ACTION_OPEN_DOCUMENT_TREE niyet eylemini kullanın. Uygulamanız daha sonra seçilen dizindeki ve alt dizinlerindeki herhangi bir dosyaya erişebilir. ACTION_OPEN_DOCUMENT_TREE uygulamasını kullanırken, uygulamanız yalnızca kullanıcının seçtiği dizindeki dosyalara erişim kazanır. Kullanıcı tarafından seçilen bu dizinin dışında bulunan diğer uygulamaların dosyalarına erişiminiz yok. Bu kullanıcı kontrollü erişim, kullanıcıların uygulamanızla tam olarak hangi içeriği paylaşacaklarını seçmelerine imkan sağlar.
İsteğe bağlı olarak, EXTRA_INITIAL_URI  ilave niyetini kullanarak dosya seçicinin ilk yüklendiğinde görüntülemesi gereken dizinin URI'sini belirtebilirsiniz.
Aşağıdaki kod parçacığı, bir dizin açma niyetinin nasıl oluşturulacağını ve çağrılacağını gösterir:

  procedure DizinAc(YuklenecekUri : JNet_Uri);
 // Sistem dosya seçici kullanarak bir dizin seçin
 var
   Intent : JIntent;
 begin
   Intent := TJIntent.Create;
   Intent.setAction(TJIntent.JavaClass.ACTION_OPEN_DOCUMENT_TREE);

   // İsteğe bağlı olarak, sistem dosya seçici yüklendiğinde
   // açılacak dizin için bir URI belirleyin.
   Intent.putExtra(TJDocumentsContract.JavaClass.EXTRA_INITIAL_URI, 
       JParcelable(YuklenecekUri));

   Mainactivity.startActivityForResult(Intent, Dizin_Agaci_Ac);
 end;

Dikkat: ACTION_OPEN_DOCUMENT_TREE kullanılarak erişilen dizindeki çok sayıda dosyayı yinelerseniz, uygulamanızın performansı düşebilir.

Erişim kısıtlamaları
Android 11 (API düzeyi 30) ve sonraki sürümlerde, ACTION_OPEN_DOCUMENT_TREE aşağıdaki dizinlere erişim istemek için niyet eylemini kullanamazsınız:
  • Dahili depolama biriminin kök dizini.
  • Kartın öykünmesi veya çıkarılabilir olmasına bakılmaksızın, aygıt üreticisinin güvenilir olduğunu düşündüğü her bir SD kart sürücüsünün kök dizini. Güvenilir sürücü, bir uygulamanın çoğu zaman başarıyla erişebildiği birimdir.
    Download
  • dizini.
Ayrıca, Android 11 (API düzeyi 30) ve sonraki sürümlerde, ACTION_OPEN_DOCUMENT_TREE niyet eylemini kullanıcının aşağıdaki dizinlerden tek tek dosyaları seçmesini istemek için kullanamazsınız:
    Android/data/
  •  dizini ve tüm alt dizinleri.
    Android/obb/
  • dizini ve tüm alt dizinleri.
İşlemleri seçilen yerde gerçekleştirin
Kullanıcı sistemin dosya seçicisini kullanarak bir dosya veya dizin seçtikten sonra, aşağıdaki kodu onActivityResult kullanarak seçilen öğenin URI'sini alabilirsiniz:

procedure TForm1.IletiFaaliyetiYakala(const Sender: TObject; const M: TMessage);
begin
 if M is TMessageResultNotification then
   OnActivityResult(
     TMessageResultNotification(M).RequestCode,
     TMessageResultNotification(M).ResultCode,
     TMessageResultNotification(M).Value);
end;

procedure TForm1.OnActivityResult(RequestCode, ResultCode: Integer;
 Data: JIntent);
var
 Uri: Jnet_Uri;
begin
 if ResultCode = TJActivity.JavaClass.RESULT_OK then
 begin
   // Sonuç verisi kullanıcının seçtiği 
   // belge veya dizin için bir URI içerir.
   Uri := nil;
   if Assigned(Data) then
   begin
     Uri := Data.getData;
     if RequestCode = sizin-talep-kodunuz then
     begin
        // URI’sini kullanarak belge üzerinde işlemler gerçekleştirin.
     end;
   end;
end;

Uygulamanız, seçilen öğenin URI'sine bir referans alarak, öğe üzerinde birkaç işlem gerçekleştirebilir. Örneğin, öğenin meta verilerine erişebilir, öğeyi yerinde düzenleyebilir ve silebilirsiniz.
Aşağıdaki bölümler, kullanıcının seçtiği dosyalar üzerindeki eylemlerin nasıl tamamlanacağını gösterir.

Bir sağlayıcının desteklediği işlemleri belirleyin

Farklı içerik sağlayıcılar, belgeler üzerinde, belgeyi kopyalama veya belgenin küçük resmini görüntüleme gibi farklı işlemlerin gerçekleştirilmesine izin verir. Belirli bir sağlayıcının hangi işlemleri desteklediğini belirlemek için Document.COLUMN_FLAGS değerini tetkik edin. Uygulamanızın kullanıcı arayüzü, yalnızca sağlayıcının desteklediği seçenekleri gösterebilir.

Kalıcı izinler

Uygulamanız okumak veya yazmak için bir dosya açtığında sistem, uygulamanıza bu dosya için kullanıcının cihazı yeniden başlatılana kadar süren bir URI izni verir. Bununla birlikte, uygulamanızın bir resim düzenleme uygulaması olduğunu ve kullanıcıların en son düzenledikleri 5 resme doğrudan uygulamanızdan erişmesini istediğinizi varsayalım. Kullanıcının cihazı yeniden başlatıldıysa, dosyaları bulması için kullanıcıyı sistem seçiciye geri göndermeniz gerekir.
Cihaz yeniden başlatmalarında dosyalara erişimi korumak ve daha iyi bir kullanıcı deneyimi oluşturmak için uygulamanız, aşağıdaki kod parçacığında gösterildiği gibi, sistemin sunduğu kalıcı URI iznini "alabilir":

  // TakeFlags: integer;
 Intent := TJIntent.Create;
 TakeFlags := Intent.getFlags
             and (TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION
             or TJIntent.JavaClass.FLAG_GRANT_WRITE_URI_PERMISSION);
 // En yeni veriyi tetkik et
 TAndroidHelper.Activity.getContentResolver.takePersistableUriPermission
   (Uri, TakeFlags);

Dikkat: takePersistableUriPermission çağırıldıktan sonra bile, ilişkili belge taşınır veya silinirse, uygulamanız URI'ye erişimi korumaz. Bu durumlarda, URI'ye yeniden erişim sağlamak için tekrar izin istemeniz gerekir.

Belge meta verilerini inceleyin

Bir belgenin URI'sine sahip olduğunuzda, onun meta verilerine erişim kazanırsınız. Bu kod parçacığı, URI tarafından belirtilen bir belgenin meta verilerini alır:

  procedure GoruntuMetaVerisiDokumu(uri : JNet_Uri);  (* dumpImageMetaData *)
 // Sorgu, tek bir belgeye uygulandığı için, sadece tek satır döndürür.
 // Alanları filtreleme, sıralama veya seçmeye ihtiyaç yoktur.
 // Çünkü tüm alanları bir belge için istiyoruz.
 var
   displayName, size : JString;
   sizeIndex : integer;
   cursor : JCursor;
 begin
   cursor := TAndroidHelper.Activity.getContentResolver.query(uri,nil,nil,nil,nil,nil);
   try
     if (cursor<>nil) then
       if (cursor.moveToFirst) then
       begin
         displayName := cursor.getString (cursor.getColumnIndex
           (TJOpenableColumns.JavaClass.DISPLAY_NAME));
         Memo1.Lines.Add({TAG.ToString +} 'Görünen Ad: ' + 
            JStringToString (displayName));
         sizeIndex:=cursor.getColumnIndex(TJOpenableColumns.JavaClass.SIZE);
         size := nil;
         if not (cursor.isNull(sizeIndex)) then
           size := cursor.getString(sizeIndex)
         else
           size:=StringToJString ('Bilinmiyor');
         Memo1.Lines.Add({TAG.ToString +} 'Boyut: ' + JStringToString (size));
       end;
   finally
     cursor.close;
   end;
 end;

Belge açın

Bir belgenin URI'sine referans vererek, daha sonraki işlemler için bir belge açabilirsiniz. Bu bölüm, bir bit eşlem ve bir giriş akışı açma örneklerini gösterir.
bit eşlem / bitmap
Aşağıdaki kod parçacığı, Bitmap URI'si verilen bir dosyanın nasıl açılacağını gösterir :

  function UridenBiteslemAl (uri : JNet_Uri): JBitmap;   (* getBitmapFromUri *)
 var
   fileDescriptor : JFileDescriptor;
   parcelFileDescriptor : JParcelFileDescriptor;
   image : JBitmap;
 begin
   Result := nil;
   try
     parcelFileDescriptor := TAndroidHelper.Activity
       .getContentResolver.openFileDescriptor(uri,StringToJString('r'));
     fileDescriptor := parcelFileDescriptor.getFileDescriptor;
     image := TJBitmapFactory.JavaClass.decodeFileDescriptor(fileDescriptor);
     parcelFileDescriptor.close;
     result := image;
   except
     on E: Exception do
       ShowMessage(e.Message);
   end;

Not: Bu işlemi UI iş parçacığında değil arka plan iş parçacığında tamamlamanız gerekir.
Bitmap'i açtıktan sonra, onu bir ImageControl içinde gösterebilirsiniz.
Giriş akışı/ Inputstream
Aşağıdaki kod parçacığı, URI'si verilen bir InputStream nesnesinin nasıl açılacağını gösterir. Bu pasajda, dosyanın satırları bir dizgeye okunuyor:

function TForm1.MetinDosyasiOkuyucu(Uri : JNet_Uri): string;  (* readTextFromUri *)
const
 bufferSize = 4096*2;
var
 inputStream : JInputStream;
 b : TJavaArray<Byte>;
 ms: TMemoryStream;
 sl: TStringList;
 bufflen: Integer;
begin
 result := '';
 try
   inputStream := TAndroidHelper.Context.getContentResolver.openInputStream(Uri);
   ms := TMemoryStream.Create;
   bufflen := inputStream.available;
   b := TJavaArray<Byte>.Create(bufflen);
   inputStream.read(b);
   ms.Write(b.Data^, bufflen);
   ms.position := 0;
   sl := TStringList.Create;
   sl.LoadFromStream(ms);
   result := sl.Text;
   sl.Free;
   b.Free;
   ms.Free;
   inputStream.Close;
 except
   on E: Exception do
     Application.ShowException(E);
 end;
end;

Belge düzenleyin

Bir metin belgesini yerinde düzenlemek için Depolama Erişim Çerçevesini kullanabilirsiniz.
Not: DocumentFile sınıfının canWrite yöntemi uygulamanızın mutlaka bir belgeyi düzenleyebileceğini göstermez. Çünkü Document.COLUMN_FLAGS eğer FLAG_SUPPORTS_DELETE veya FLAG_SUPPORTS_WRITE içeriyorsa true döndürür. Uygulamanızın belirli bir belgeyi düzenleyip düzenleyemeyeceğini belirlemek için doğrudan FLAG_SUPPORTS_WRITE değerini sorgulayın. 
Aşağıdaki kod parçacığı, verilen URI tarafından temsil edilen belgenin içeriğinin üzerine yazar:

  procedure MetinBelgesiDegistir(uri : JNet_Uri);  (* alterDocument *)
 var
   pfd : JParcelFileDescriptor;
   fileOutputStream : JFileOutputStream;
 begin
   try
     pfd := TAndroidHelper.Activity.getContentResolver
       .openFileDescriptor(uri,StringToJString('w'));
     fileOutputStream := TJFileOutputStream.JavaClass.init(pfd.getFileDescriptor);
     fileOutputStream.write(StringToJString('Üzerine yazıldı ' + timetostr(Now)).getBytes);
     fileOutputStream.close;
     pfd.close;
   except
     on E: Exception do
       ShowMessage(e.Message);  
   end;
 end; 

Bir belgeyi silin

Bir belge için URI'niz varsa ve belgenin Document.COLUMN_FLAGS içeriği SUPPORTS_DELETE varsa, belgeyi silebilirsiniz. Örneğin:


TJDocumentsContract.JavaClass.deleteDocument (TAndroidHelper.contentResolver, Uri);
 
Eşdeğer bir medya URI'si alın

getMediaUri yöntemi verilen belgeler sağlayıcısına URI'siyle eşdeğer bir medya deposu URI’si sağlar. 2 URI, aynı temel öğeye atıfta bulunur. Medya deposu URI'sini kullanarak, paylaşılan depodan medya dosyalarına daha kolay erişebilirsiniz.
Not: Bu yöntem herhangi bir yeni izin vermez. Uygulamanızın, belirli bir belge sağlayıcı URI'sine (ör. belgeyi açarak) erişmek için gerekli izinlere zaten sahip olması gerekir.
ExternalStorageProvider URI'leri getMediaUri yöntemini destekler. Android 12 (API düzeyi 31) ve sonraki sürümlerde, MediaDocumentsProvider URI'leri de bu yöntemi destekler.

Sanal bir dosya açın

Android 7.0 (API düzeyi 25) ve sonraki sürümlerde uygulamanız, Storage Access Framework'ün kullanıma sunduğu sanal dosyaları kullanabilir. Sanal dosyaların ikili gösterimi olmamasına rağmen, uygulamanız içeriklerini farklı bir dosya türüne zorlayarak veya ACTION_VIEW niyet eylemini kullanarak bu dosyaları görüntüleyerek açabilir.
Sanal dosyaları açmak için, istemci uygulamanızın bunları işlemek için özel bir mantık içermesi gerekir. Dosyanın bayt temsilini almak istiyorsanız, örneğin dosyayı önizlemek için, belge sağlayıcısından alternatif bir MIME türü talep etmeniz gerekir.
Not: Bir uygulama, openInputStream yöntemini kullanarak bir sanal dosyayı doğrudan açamayacağından, ACTION_OPEN_DOCUMENT veya ACTION_OPEN_DOCUMENT_TREE eylemi içeren niyeti oluştururken CATEGORY_OPENABLE kategorisini kullanmayın.
Kullanıcı bir seçim yaptıktan sonra, aşağıdaki kod parçacığında gösterildiği gibi, dosyanın sanal olup olmadığını belirlemek için sonuç verilerindeki URI'yi kullanın:

  function SanalDosyami(Uri : JNet_Uri): boolean;  (* isVirtualFile *)
 var
   flags : integer;
   cursor : JCursor;
   s : TJavaObjectArray<JString>;
 begin
   if (not TJDocumentsContract.JavaClass.isDocumentUri(TAndroidHelper.Context,Uri)) then
   begin
     result := false;
     exit;
   end;
   s := TJavaObjectArray<JString>.Create(0);
   s[0] := TJDocumentsContract_Document.JavaClass.COLUMN_FLAGS;
   cursor := TAndroidHelper.Activity.getContentResolver.query(uri,s,nil,nil,nil);
   flags:=0;
   if (cursor.moveToFirst) then
     flags:=cursor.getInt(0);
   cursor.close;
   result := (flags and TJDocumentsContract_Document.JavaClass.FLAG_VIRTUAL_DOCUMENT) <> 0;
 end; 

Belgenin sanal bir dosya olduğunu doğruladıktan sonra, dosyayı "image/png" benzeri MIME türüne çevirebilirsiniz. Aşağıdaki kod parçacığı, bir sanal dosyanın bir görüntü olarak gösterilip gösterilmeyeceğinin nasıl kontrol edileceğini gösterir ve eğer öyleyse, sanal dosyadan bir girdi akışı alır:

  function SanalDosyaIcinGirisAkisiAl(Uri : JNet_Uri; mimeTypeFilter : String): JInputStream;  
 var (* getInputStreamForVirtualFile *)
   openableMimeTypes : TJavaObjectArray<JString>;
   resolver : JContentResolver;
 begin
   resolver := TAndroidHelper.Activity.getContentResolver;
   openableMimeTypes := resolver.getStreamTypes(uri,StringToJString(mimeTypeFilter));
   if ((openableMimeTypes = nil) or (openableMimeTypes.Length < 1)) then
   begin
     Teblig('Dosya bulunamadı!');
     result := nil;
     exit;
   end;
   result := resolver.openTypedAssetFileDescriptor(uri,openableMimeTypes[0],nil)
     .createInputStream;
 end;

https://developer.android.com/training/d...ents-files

MediaStore API
MediaProvider ve uygulamalar arasındaki sözleşme sınıfıdır. Desteklenen URI'ler ve sütunlar için tanımları içerir.
Medya sağlayıcısı, herhangi bir bağlı depolama cihazlarındaki Audio, Video ve Images gibi yaygın medya tipleri için dizinlenmiş bir koleksiyon sağlar. Her koleksiyon, temel alınan içeriğin birincil MIME türüne göre düzenlenir; örneğin, image/* içeriği Images altındaki dizine eklenir. Files kolleksiyonu tüm koleksiyonlar boyunca geniş bir görünüm sağlar ve MIME türüne göre filtre uygulamaz.
createWriteRequest, createDeleteRequest, createTrashRequest, createFavoriteRequest sınıflarını kullanır.
https://developer.android.com/reference/...MediaStore
 
Medya dosyalarına paylaşılan depolama alanından erişin

Daha zenginleştirilmiş bir kullanıcı deneyimi sağlamak için birçok uygulama, kullanıcıların harici bir depolama biriminde bulunan medyaya katkıda bulunmasına ve bu medyaya erişmesine olanak tanır. Çerçeve, bu medya dosyalarının daha kolay alınmasına ve güncellenmesine izin veren, medya deposu adı verilen medya koleksiyonlarına optimize edilmiş bir dizin sağlar. Uygulamanızın yüklemesi kaldırıldıktan sonra bile bu dosyalar kullanıcının cihazında kalır.
Not: Uygulamanız, kullanıcıya yalnızca uygulamanız içinde değer sağlayan medya dosyaları ile çalışıyorsa, bunları harici depolama içindeki uygulamaya özel dizinlerde depolamak en iyisidir.
Medya deposu soyutlamasıyla etkileşim kurmak için, uygulamanızın bağlamından (context) aldığınız bir ContentResolver nesnesini kullanın:
 
  var
   projection, selectionArgs: TJavaObjectArray<JString>;
   selection, sortOrder: JString;
   cursor: JCursor;
 begin
   selection := where-şartı-bulunan-sql-seçme-değişkeni
   projection := alınacak-medya-veritabanı-sütunları //TJavaObjectArray<JString>.Create(3);
   //projection.Items[0] := TJAudio_AlbumColumns.JavaClass.ALBUM;
   //projection.Items[1] := TJAudio_AlbumColumns.JavaClass.ARTIST;
   //projection.Items[2] := StringToJString('_id');
   selectionArgs := seçme-şartı-değişken-değerleri
   sortOrder := order by-sıralama-şartı

   cursor := MainActivity.getContentResolver.query(
     TJAudio_Albums.JavaClass.EXTERNAL_CONTENT_URI,
     projection,
     selection,
     selectionArgs,
     sortOrder);

   while (cursor.moveToNext) do
   begin
     // Projeksiyondan medya kalemini temsil eden bir URI almak için bir ID sütünü kullanın
   end;
   cursor.close;
 end; 

Sistem kendiliğinden harici bir depolama birimini tarar ve medya dosyalarını aşağıdaki iyi tanımlanmış koleksiyonlara ekler:
•        Görüntüler fotoğraf ve ekran görüntüleri de dahil olmak üzere, DCIM/ve Pictures/dizinlerinde saklanır. Sistem bu dosyaları MediaStore.Images tablosuna ekler.
•        Videolar DCIM/, Movies/, ve Pictures/ dizinlerinde saklanır. Sistem bu dosyaları MediaStore.Video tablosuna ekler.
•        Ses dosyaları saklanır, Alarms/, Audiobooks/, Music/, Notifications/, Podcasts/, ve Ringtones/dizinleri. Ayrıca sistem, Music/veya Movies/dizinlerindeki ses çalma listelerini ve dizindeki ses kayıtlarını tanır Recordings/. Sistem bu dosyaları MediaStore.Audio tablosuna ekler. Kayıtlar dizini, Android 11 (API düzeyi 30) ve önceki sürümlerde kullanılamaz.
•       İndirilen dosyalar Download dizininde depolanan Android 10 (API düzeyi 29) ve sonraki sürümleri çalıştıran cihazlarda bu dosyalar MediaStore.Downloads tabloda depolanır. Bu tablo, Android 9 (API düzeyi 28) ve daha düşük sürümlerde mevcut değildir.
Medya deposunda MediaStore.Files içeriği, uygulamanızın Android 10 veya sonraki sürümleri hedefleyen uygulamalarda bulunan kapsamlı depolama kullanıp kullanmadığına bağlıdır:
  • Kapsamlı depolama etkinleştirilirse koleksiyon yalnızca uygulamanızın oluşturduğu fotoğrafları, videoları ve ses dosyalarını gösterir. Çoğu geliştiricinin
    MediaStore.Files diğer uygulamalardan medya dosyalarını görüntülemek için kullanması gerekmez, ancak bunu yapmak için özel bir gereksiniminiz varsa,
    READ_EXTERNAL_STORAGE izin beyan edebilirsiniz. Ancak uygulamanızın oluşturmadığı dosyaları açmak için MediaStore API'lerini kullanmanız önerilir.
  • Kapsamlı depolama yoksa veya kullanılmıyorsa, koleksiyon her tür medya dosyasını gösterir.
Gerekli izinleri isteyin

Medya dosyaları üzerinde işlem yapmadan önce, uygulamanızın bu dosyalara erişmek için ihtiyaç duyduğu izinleri beyan ettiğinden emin olun. Ancak, uygulamanızın ihtiyaç duymadığı veya kullanmadığı izinleri beyan etmemesi gerektiğini unutmayın.

Depolama izni
Uygulamanızdaki medya dosyalarına erişmek için izin modeli, uygulamanızın Android 10 veya sonraki sürümleri hedefleyen uygulamalarda bulunan kapsamlı depolamayı kullanıp kullanmadığına bağlıdır.
Kapsamlı depolama etkinleştirilecek ise
Uygulamanız kapsamlı depolama kullanıyorsa, yalnızca Android 9 (API düzeyi 28) veya daha eski sürümleri çalıştıran cihazlar için depolamayla ilgili izinler istemelidir. Bu şartı, uygulamanızın bildirim dosyasındaki izin bildirimine android:maxSdkVersion özniteliği ekleyerek sağlayabilirsiniz :
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                 android:maxSdkVersion="28" />

Android 10 veya sonraki sürümleri çalıştıran cihazlar için depolamayla ilgili izinleri gereksiz yere istemeyin. Uygulamanız, koleksiyon da dahil olmak üzere iyi tanımlanmış medya koleksiyonlarına MediaStore.Downloads depolamayla ilgili herhangi bir izin istemeden katkıda bulunabilir. Örneğin bir kamera uygulaması geliştiriyorsanız , medya deposuna yazdığınız görüntülerin sahibi uygulamanız olduğundan depolamayla ilgili izinler istemeniz gerekmez.
Diğer uygulamaların oluşturduğu dosyalara erişmek için aşağıdaki koşulların her birinin doğru olması gerekir:
  • Uygulamanıza READ_EXTERNAL_STORAGE izin verilmiş olmalıdır.
  • Dosyalar, aşağıdaki iyi tanımlanmış medya koleksiyonlarından birinde bulunmalıdır:
            MediaStore.Images
            MediaStore.Video
            MediaStore.Audio

Not: Bir dosya olarak MediaStore.Images, MediaStore.Videoya da MediaStore.Audio sorguları  kullanarak görüntülenebilir olduğu gibi, MediaStore.Files sorgusu ile de görüntülenebilir.
Cevapla
#2
.....
Cevapla
#3
Uygulamanız MediaStore.Downloads, uygulamanızın oluşturmadığı koleksiyon içindeki bir dosyaya erişmek istiyorsa, Depolama Erişim Çerçevesini kullanmanız gerekir. Bu çerçevenin nasıl kullanılacağı hakkında daha fazla bilgi edinmek için belgelere ve diğer dosyalara nasıl erişileceğine ilişkin kılavuza bakın.
Kapsamlı depolama devre dışı ise
Uygulamanız Android 9 veya önceki sürümleri çalıştıran bir cihazda kullanılıyorsa veya uygulamanız geçici olarak kapsamlı depolamayı devre dışı bıraktıysa READ_EXTERNAL_STORAGE medya dosyalarına erişim izni istemeniz gerekir. Medya dosyalarını değiştirmek istiyorsanız, WRITE_EXTERNAL_STORAGE izin de istemeniz gerekir.
Not: Uygulamanızın kullanma vakası kapsamlı depolamaya dahil olmuyorsa, bir özellik talebi bildirin ve geçici olarak kapsamlı depolamadan çıkın.
 
Medya konum izni
Uygulamanız Android 10 (API düzeyi 29) veya sonraki bir sürümünü hedefliyorsa, uygulamanızın fotoğraflardan düzenlenmemiş Exif meta verilerini alabilmesi ACCESS_MEDIA_LOCATION için uygulamanızın bildiriminde izni bildirmeniz ve ardından bu izni çalışma zamanında istemeniz gerekir.
Dikkat: Çalışma zamanında ACCESS_MEDIA_LOCATION izin talep ettiğiniz için, uygulamanızın fotoğraflardan redaksiyona tabi tutulmamış Exif meta verilerine erişebileceğinin garantisi yoktur. Uygulamanız, bu bilgilere erişmek için açık kullanıcı onayı gerektirir.
 
Medya depolamadaki değişilikliklerin tetkiki
Medya dosyalarına daha güvenilir bir şekilde erişmek için, özellikle uygulamanız URI'leri veya medya deposundaki verileri önbelleğe alıyorsa, medya deposu sürümünün medya verilerinizi en son senkronize ettiğiniz zamana göre değişip değişmediğini kontrol edin. Güncellemeler için bu kontrolügerçekleştirmek için getVersion çağırın. Döndürülen sürüm, medya deposu önemli ölçüde değiştiğinde değişen benzersiz bir dizedir. Döndürülen sürüm, son eşitlenen sürümden farklıysa, uygulamanızın medya önbelleğini yeniden tarayın ve yeniden eşitleyin.
Bu kontrolü uygulama süreci başlangıç zamanında tamamlayın. Medya deposunu her sorguladığınızda sürümü kontrol etmenize gerek yoktur.
Sürüm numarasıyla ilgili herhangi bir uygulama ayrıntısını varsaymayın.
Not: Medya deposu sürüm numarası, bir uygulamanın medya dosyası eklemesi  gibi uygulama tarafındaki değişikliklerin bir sonucu olarak değişmez. Medya dosyalarındaki güncellemeleri algılamanıza yardımcı olacak ayrı bir yöntem vardır.
 
Medya kolleksiyonu sorgulama
5 dakika veya daha uzun süre gibi belirli bir dizi koşulu karşılayan medyayı bulmak için aşağıdaki kod parçacığında gösterilene benzer bir SQL benzeri seçim ifadesi kullanın:
// Uygulamanızın oluşturmadığı video dosyalarına ulaşmak için READ_EXTERNAL_STORAGE izni gereklidir
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
Uygulamanızda böyle bir sorgu gerçekleştirirken aşağıdakileri aklınızda bulundurunuz:
  • Query yöntemini çalışan bir iş parçacığında çağırın.
  • Sütun dizinlerini önbelleğe alın, böylece  sorgu sonucundan bir satırı her işlediğinizde çağırmanız gerekmez.
  • Kimliği, kod parçacığında gösterildiği gibi içerik URI'sine ekleyin.
  • Android 10 ve sonraki sürümleri çalıştıran cihazlar, MediaStoreAPI'de tanımlanan sütun adlarını gerektirir. Uygulamanızdaki bağımlı bir kitaplık, API'de tanımsız bir sütun adı bekliyorsa, örneğin uygulamanızın işleminde sütun adını dinamik olarak çevirmek için CursorWrapper "MimeType" kullanın.
 
Dosya küçük resimleri (thumbnails) yükleme
 
Uygulamanız birden fazla medya dosyası gösteriyorsa ve kullanıcının bu dosyalardan birini seçmesini isterse, dosyaların kendisi yerine dosyaların önizleme sürümlerini (veya küçük resimlerini) yüklemek daha verimlidir.
Belirli bir medya dosyasının loadThumbnail küçük resmini yüklemek için, aşağıdaki kod parçacığında gösterildiği gibi, yüklemek istediğiniz küçük resmin boyutunu kullanın ve iletin:
// java kodu
 
 
Medya dosyası açma
 
Bir medya dosyasını açmak için kullandığınız belirli mantık, medya içeriğinin en iyi dosya tanımlayıcı, dosya akışı veya doğrudan dosya yolu olarak temsil edilip edilmediğine bağlıdır:
 
Dosya tanımlayıcı / File descriptor
Dosya tanımlayıcı kullanarak bir medya dosyasını açmak için aşağıdaki kod parçacığında gösterilene benzer bir mantık kullanın:

  // ParcelFileDescriptor kullanarak belli bir medya öğesini açın.
 procedure DosyaAc_FileDescriptor(Uri : JNet_Uri);
 var
   pfd : JParcelFileDescriptor;
   readOnlyMode : JString;
   resolver : JContentResolver;
 begin
   resolver := TAndroidHelper.Context.getContentResolver;
   // "r" salt okunur, "rw" oku-yaz
   // "rwt" mevcut dosya muhteviyatını kısaltma ve üzerine yazma.
   readOnlyMode := stringtoJString('r');
   try
     pfd := resolver.openFileDescriptor(Uri, readOnlyMode);
     // "pfd" üzerinde işlemler yapın.
     Memo1.Lines.Add(jstringtostring( pfd.toString));
   except
     on E: Exception do
       Application.ShowException(E);
   end;
 end;
 
Dosya akışı / File stream
Dosya akışı kullanarak bir medya dosyasını açmak için aşağıdaki kod parçacığında gösterilene benzer bir mantık kullanın:

// InputStream kullanarak belli bir medya öğesi açın.
 procedure DosyaAc_FileStream(Uri : JNet_Uri);
 var
   jis: JInputStream;
   resolver : JContentResolver;
 begin
   resolver := TAndroidHelper.Context.getContentResolver;
   try
     jis := resolver.openInputStream(Uri);
     // "jis" üzerinde işlemler yapın.
     Memo1.Lines.Add(jstringtostring( jis.toString));
   except
     on E: Exception do
       Application.ShowException(E);
   end;
 end;
 
Direk dosya dizinleri / paths
Uygulamanızın üçüncü taraf medya kitaplıklarıyla daha sorunsuz çalışmasına yardımcı olmak için Android 11 (API düzeyi 30) ve sonraki sürümleri MediaStore, paylaşılan depolama alanından medya dosyalarına erişmek için API dışındaki API'leri kullanmanıza olanak tanır. Bunun yerine, aşağıdaki API'lerden birini kullanarak medya dosyalarına doğrudan erişebilirsiniz:
•        File API.
•        Yerel kütüphaneler, örneğin fopen.
Eğer herhangi bir depolama ilgili izinleri yoksa, uygulamaya özgü dizine yanı sıra uygulamanıza atfedilir medya dosyaları kendi içinde dosyalara FileAPI kullanarak erişebilir
Uygulamanız FileAPI'yi kullanarak bir dosyaya erişmeye çalışırsa ve gerekli izinlere sahip değilse, bir FileNotFoundException oluşur.
Android'i 10 (API seviyesi 29) çalışan bir cihazda erişim Paylaşılan depolama diğer dosyaları için, uygulamanızın manifest dosyasında geçici kapsamına sahip depolama çıkma amacıyla requestLegacyExternalStorage için true ayarı önerilir. Android 10'da yerel dosya yöntemlerini kullanarak medya dosyalarına erişmek için ayrıca READ_EXTERNAL_STORAGE izin istemeniz gerekir.
 
Medya içeriğine ulaşırken dikkat edilecekler
 
Medya içeriğine erişirken, aşağıdaki bölümlerde ele alınan hususları aklınızda bulundurun.
 
Önbellek verisi / Cached data
Uygulamanız URI'leri veya medya deposundaki verileri önbelleğe alıyorsa, medya deposunda güncelleme olup olmadığını düzenli olarak kontrol edin. Bu kontrol, uygulama tarafında, önbelleğe alınmış verilerinizin sistem tarafında, sağlayıcı verileriyle senkronize kalmasını sağlar.
 
Performans
Doğrudan dosya yollarını kullanarak medya dosyalarının sıralı okumalarını gerçekleştirdiğinizde, performans MediaStore API'nin performansıyla karşılaştırılabilir.
Ancak, doğrudan dosya yollarını kullanarak medya dosyalarının rastgele okuma ve yazma işlemlerini gerçekleştirdiğinizde, işlem iki kata kadar daha yavaş olabilir. Bu durumlarda, bunun yerine MediaStore API'yi kullanmanızı öneririz.
 
Veri sütunu / DATA column
Mevcut bir medya dosyasına eriştiğinizde DATA, mantığınızdaki sütunun değerini kullanabilirsiniz. Bunun nedeni, bu değerin geçerli bir dosya yoluna sahip olmasıdır. Ancak, dosyanın her zaman kullanılabilir olduğunu varsaymayın. Oluşabilecek dosya tabanlı G/Ç hatalarını işlemeye hazır olun.
Öte yandan bir medya dosyası oluşturmak veya güncellemek için DATA sütunun değerini kullanmayın. Bunun yerine DISPLAY_NAMEve RELATIVE_PATH sütunlarının değerlerini kullanın.
 
Depolama sürücüleri
Android 10 veya sonraki sürümleri hedefleyen uygulamalar, sistemin her bir harici depolama birimine atadığı benzersiz ada erişebilir. Bu adlandırma sistemi, içeriği verimli bir şekilde düzenlemenize ve dizine eklemenize yardımcı olur ve yeni medya dosyalarının nerede saklandığını kontrol etmenizi sağlar.
Aşağıdaki hacimlerin akılda tutulması özellikle yararlıdır:
•        VOLUME_EXTERNAL Hacim, cihazdaki tüm paylaşılan depolama hacimlerinin bir görünüm sağlar. Bu sentetik cildin içeriğini okuyabilirsiniz, ancak içeriği değiştiremezsiniz.
•        VOLUME_EXTERNAL_PRIMARY Hacim cihazında, birincil ortak depolama hacmi temsil eder. Bu cildin içeriğini okuyabilir ve değiştirebilirsiniz.
MediaStore.getExternalVolumeNames ile diğer sürücüleri keşfedebilirsiniz:
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
 
Medyanın yakalandığı konum
Bazı fotoğraflar ve videolar, meta verilerinde bir fotoğrafın çekildiği veya bir videonun kaydedildiği yeri gösteren konum bilgileri içerir.
Uygulamanızda bu konum bilgilerine erişmek için fotoğraf konum bilgileri için bir API ve video konum bilgileri için başka bir API kullanın.
Fotoğraflar
Uygulamanız kapsamlı depolama kullanıyorsa sistem varsayılan olarak konum bilgilerini gizler. Bu bilgilere erişmek için aşağıdaki adımları tamamlayın:
  1. Uygulamanızın bildiriminde ACCESS_MEDIA_LOCATION izni isteyin.
  1. MediaStore (SDK seviyesi 29 ve sonrasında geçerli olan) setRequireOriginal nesnesi ile arayarak fotoğrafın tam baytları almak ve aşağıdaki kod parçacığında gösterildiği gibi, fotoğrafın URI’sine geçmek için:
// Java kodu. Delphi 11 & sdk 29 gerekli.
 
Videolar
Bir videonun meta verisindeki konumbilgilerine erişmek MediaMetadataRetriever için aşağıdaki kod parçacığında gösterildiği gibi sınıfı kullanın. Uygulamanızın bu sınıfı kullanmak için herhangi bir ek izin istemesi gerekmez.
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
 
Paylaşma
Bazı uygulamalar, kullanıcıların medya dosyalarını birbirleriyle paylaşmalarına izin verir. Örneğin, sosyal medya uygulamaları, kullanıcılara fotoğrafları ve videoları arkadaşlarıyla paylaşma olanağı verir.
Medya dosyalarını paylaşmak için içerik sağlayıcı oluşturma kılavuzunda önerildiği gibi bir content:// URI kullanınız.
 
Medya dosyalarının uygulama nitelikleri
Kapsamlı depolama Android 10 veya üstü hedeflendiğinde, sistem o bir uygulaması için etkin olup nitelikleri herhangi bir depolama izin istediniz henüz zaman uygulamanızın erişebilmesi dosyaları belirler her medya dosyasına bir uygulamayı. Her dosya yalnızca bir uygulamaya atfedilebilir. Bu nedenle, uygulamanız fotoğraflar, videolar veya ses dosyaları medya koleksiyonunda depolanan bir medya dosyası oluşturursa uygulamanızın dosyaya erişimi olur.
Ancak kullanıcı uygulamanızı kaldırır ve yeniden yüklerse, READ_EXTERNAL_STORAGE uygulamanızın orijinal olarak oluşturduğu dosyalara erişmek için istekte bulunmanız gerekir. Bu izin isteği, sistem dosyanın yeni yüklenen sürümden ziyade uygulamanın önceden yüklenen sürümüyle ilişkilendirildiğini düşündüğü için gereklidir.
 
Öğe ekleme
Mevcut bir koleksiyona medya öğesi eklemek için aşağıdakine benzer bir kod arayın. Bu kod parçacığı, VOLUME_EXTERNAL_PRIMARY Android 10 veya sonraki sürümleri çalıştıran cihazlarda birime erişir. Bunun nedeni, bu cihazlarda, depolama birimleri bölümünde açıklandığı gibi yalnızca birincil birimse bir birimin içeriğini değiştirebilmenizdir.
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
 
Medya dosyaları bekleme durumunu yenileme
Uygulamanız medya dosyalarına yazma gibi potansiyel olarak zaman alıcı işlemler gerçekleştiriyorsa, işlenirken dosyaya özel erişime sahip olmak yararlıdır. Android 10 veya sonraki sürümleri çalıştıran cihaz larda, uygulamanız, IS_PENDING bayrağın değerini 1 olarak ayarlayarak bu özel erişimi elde edebilir.
Aşağıdaki kod parçacığı, önceki kod parçacığını temel alır. Aşağıdaki parça IS_PENDING, MediaStore.Audio koleksiyona karşılık gelen dizinde uzun bir şarkı saklarken bayrağın nasıl kullanılacağını gösterir:
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
 
Dosya konumu için ipucu (hint) verme
Uygulamanız medyayı Android 10 çalıştıran bir cihazda depoladığında, medya varsayılan olarak türüne göre düzenlenir. Örneğin, MediaStore.Images yeni görüntü dosyaları varsayılan olarak koleksiyona Environment.DIRECTORY_PICTURES karşılık gelen dizine yerleştirilir.
Uygulamanız, Pictures/MyVacationPictures adlı bir fotoğraf albümü gibi dosyaların saklanması gereken belirli bir konumun farkındaysa MediaColumns.RELATIVE_PATH, sisteme yeni yazılan dosyaların nerede saklanacağı konusunda bir ipucu sağlayacak şekilde ayarlayabilirsiniz.
Not: Genel amaçlı dosyaları, medya dışı dosyalar da dahil olmak üzere , Documents/klasörde veya klasörde depolamak mümkün olsa da, Download/bu kullanım durumları için Depolama Erişim Çerçevesini (SAF) kullanmak daha iyidir.
 
Bir öğenin güncellenmesi
Uygulamanızın sahip olduğu bir medya dosyasını güncellemek için aşağıdakine benzer bir kod çalıştırın:
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
Kapsamlı depolama kullanılamıyorsa veya etkinleştirilmediyse, önceki kod parçacığında gösterilen işlem, uygulamanızın sahip olmadığı dosyalar için de çalışır.
Not: Bir arama sırasında MediaColumns.RELATIVE_PATH veya MediaColumns.DISPLAY_NAME öğesini update değiştirerek diskteki dosyaları taşıyabilirsiniz.
 
Yerel kodun güncellenmesi
Medya dosyalarını yerel kitaplıkları kullanarak yazmanız gerekiyorsa, Java tabanlı veya Kotlin tabanlı kodunuzdan dosyanın ilişkili dosya tanımlayıcısını yerel kodunuza iletin.
Aşağıdaki kod parçacığı, bir medya nesnesinin dosya tanımlayıcısının uygulamanızın yerel koduna nasıl geçirileceğini gösterir:
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
 
Başka uygulamaların medya dosyalarının güncellenmesi
Uygulamanız kapsamlı depolama kullanıyorsa, normalde farklı bir uygulamanın medya deposuna katkıda bulunduğu bir medya dosyasını güncelleyemez.
Ancak, RecoverableSecurityException platformun attığını yakalayarak dosyayı değiştirmek için kullanıcı onayını almak hala mümkündür. Ardından, aşağıdaki kod parçacığında gösterildiği gibi, kullanıcının uygulamanıza söz konusu öğeye yazma erişimi vermesini isteyebilirsiniz:
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
Uygulamanızın oluşturmadığı bir medya dosyasını değiştirmesi gerektiğinde bu işlemi tamamlayın.
Alternatif olarak, uygulamanız Android 11 veya sonraki sürümlerde çalışıyorsa, kullanıcıların uygulamanıza bir grup medya dosyasına yazma erişimi vermesine izin verebilirsiniz. Medya dosyası gruplarının  createWriteRequest nasıl yönetileceği ile ilgili bölümde açıklandığı gibi yöntemi çağırın.
Uygulamanız kapsamına sahip depolama kapsamında değildir başka kullanma vakasında varsa, bir özellik isteği dosya ve geçici kapsamına sahip depolama çıkma.
 
Bir öğenin kaldırılması
Uygulamanızın medya deposunda artık ihtiyaç duymadığı bir öğeyi kaldırmak için aşağıdaki kod parçacığında gösterilene benzer bir mantık kullanın:
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
Kapsamlı depolama kullanılamıyorsa veya etkin değilse, diğer uygulamaların sahip olduğu dosyaları kaldırmak için önceki kod parçacığını kullanabilirsiniz. Kapsamlı depolama etkinleştirilirse, medya öğelerini güncelleme bölümünde açıklandığı gibi, uygulamanızın kaldırmak istediği her dosya için bir tane RecoverableSecurityException yakalamanız gerekir.
Uygulamanız Android 11 veya sonraki sürümlerde çalışıyorsa, kullanıcıların kaldırılacak bir medya dosyası grubu seçmesine izin verebilirsiniz. Medya dosyası gruplarının nasıl yönetileceği ile ilgili bölümde açıklandığı gibi createTrashRequest veya createDeleteRequest yöntemini çağırın.
Uygulamanız kapsamına sahip depolama kapsamında değildir başka kullanma vakasında varsa, bir özellik isteği dosya ve geçici kapsamına sahip depolama çıkma.
 
Medya dosyalarındaki değişikliklerin tespiti
Uygulamanızın, önceki bir zamana kıyasla, uygulamaların eklediği veya değiştirdiği medya dosyalarını içeren depolama birimlerini tanımlaması gerekebilir. Bu değişiklikleri en güvenilir şekilde algılamak için ilgili depolama hacmini  getGeneration ile kontrol edilir. Media store sürümü değişmediği sürece bu yöntemin dönüş değeri monoton olarak zamanla artar.
Özelli kle DATE_ADDEDDATE_MODIFIED setLastModified ve getGeneration gibi medya sütunlarındaki tarihlerden daha sağlamdır. Bunun nedeni, bir uygulama aradığında veya kullanıcı sistem saatini değiştirdiğinde bu medya sütunu değerlerinin değişebilmesidir.
Dikkat: getGeneration güncellemeler için medya deposu denetimi. Medya deposu sürümü değiştiyse, tam bir senkronizasyon geçişi gerçekleştirin.
 
Medya dosya grupları yönetimi
Android 11 ve sonraki sürümlerde, kullanıcıdan bir grup medya dosyası seçmesini isteyebilir, ardından bu medya dosyalarını tek bir işlemle güncelleyebilirsiniz. Bu yöntemler, cihazlar arasında daha iyi tutarlılık sunar ve yöntemler, kullanıcıların medya koleksiyonlarını yönetmesini kolaylaştırır.
Bu "toplu güncelleme" işlevini sağlayan yöntemler şunları içerir:
createWriteRequest
Kullanıcının, uygulamanıza belirtilen medya dosyaları grubuna yazma erişimi vermesini isteyin.
createFavoriteRequest
Kullanıcının belirtilen medya dosyalarını cihazda "favori" ortamlarından bazıları olarak işaretlemesini isteyin. Bu dosyaya okuma erişimi olan herhangi bir uygulama, kullanıcının dosyayı "favori" olarak işaretlediğini görebilir.
createTrashRequest
Kullanıcıdan, belirtilen medya dosyalarını cihazın çöp kutusuna yerleştirmesini isteyin. Çöp kutusundaki öğeler, sistem tarafından tanımlanan bir süre sonunda kalıcı olarak silinir.
Not: Uygulamanız, cihaz OEM'inin önceden yüklenmiş galeri uygulamasıysa, bir iletişim kutusu göstermeden dosyaları çöp kutusuna yerleştirebilirsiniz. Bunu yapmak IS_TRASHED için doğrudan ayarlayın.
createDeleteRequest
Kullanıcıdan belirtilen medya dosyalarını önceden çöp kutusuna atmadan kalıcı olarak silmesini isteyin.
Bu yöntemlerden herhangi birini çağırdıktan sonra sistem bir PendingIntent nesne oluşturur. Uygulamanız bu niyetini çağırdıktan sonra, kullanıcılar uygulamanızın belirtilen medya dosyalarını güncellemesi veya silmesi için onaylarını isteyen bir iletişim kutusu görür.
Örneğin, createWriteRequest ile bir çağrının nasıl yapılandırılacağı aşağıda açıklanmıştır:
// Java kodu eksik Delphi kütüphaneleri sebebi ile çevirilemedi
Kullanıcının yanıtını değerlendirin. Kullanıcı onay verdiyse, medya işlemine devam edin. Aksi takdirde, kullanıcıya uygulamanızın neden izne ihtiyacı olduğunu açıklayın:
procedure TForm1.OnActivityResult(RequestCode, ResultCode: Integer; ...
Yine aynıgenel tasarım createFavoriteRequest, createTrashRequest ve createDeleteRequest kullanabilir.
 
Medya yönetim izni
Kullanıcılar, medya dosyalarında sık düzenlemeler yapmak gibi medya yönetimini gerçekleştirmek için belirli bir uygulamaya güvenebilir. Uygulamanız Android 11 veya sonraki bir sürümü hedefliyorsa ve cihazın varsayılan galeri uygulaması değilse, uygulamanız bir dosyayı her değiştirmeye veya silmeye çalıştığında kullanıcıya bir onay iletişim kutusu göstermelisiniz.
Uygulamanız Android 12 (API düzeyi 31) veya sonraki bir sürümünü hedefliyorsa, kullanıcılardan Medya yönetimi özel iznine uygulamanıza erişim vermesini isteyebilirsiniz. Bu izin, uygulamanızın her dosya işlemi için kullanıcıdan bilgi istemesine gerek kalmadan aşağıdakilerin her birini yapmasına olanak tanır:
·         createWriteRequest kullanarak dosyaları değiştirin.
·         createTrashRequest kullanarak dosyaları çöp kutusuna ve çöp kutusundan taşıyın.
·         createDeleteRequest kullanarak dosyaları silin.
Bunu yapmak için aşağıdaki adımları tamamlayın:
1.    Uygulamanızın bildirim dosyasındaki MANAGE_MEDIA izni ve READ_EXTERNAL_STORAGE iznini bildirin.  createWriteRequest onay diyaloğu göstermeden aramak için ACCESS_MEDIA_LOCATION iznini de bildirin.
2.    Uygulamanızda, uygulamanıza neden medya yönetimi erişimi vermek isteyebileceklerini açıklamak için kullanıcıya bir kullanıcı arayüzü gösterin.
3.    ACTION_REQUEST_MANAGE_MEDIA intent niyet eylemini çağırın. Bu, kullanıcıları sistem ayarlarında Medya yönetimi uygulamaları ekranına götürür. Buradan, kullanıcılar özel uygulamaya erişim izni verebilir.
 
Medya deposuna alternatif gerektiren olayları kullanın
Uygulamanız öncelikle aşağıdaki rollerden birini gerçekleştiriyorsa MediaStore API'lere alternatif düşünebilirsiniz:
 
Diğer dosya türleri ile çalışın
Uygulamanız, EPUB veya PDF dosya uzantısını kullanan dosyalar gibi yalnızca medya içeriği içermeyen belgeler ve dosyalarla çalışıyorsa, belgelerin ve diğer dosyaların ACTION_OPEN_DOCUMENT ile nasıl depolanacağı ve bunlara nasıl erişileceğiyle ilgili kılavuzda açıklandığı gibi intent eylemini kullanın.
 
Tamamlayıcı uygulamalarda dosya paylaşımı
Bir mesajlaşma uygulaması ve bir profil uygulaması gibi bir tamamlayıcı uygulama paketi sağladığınız durumlarda, content:// URI'leri kullanarak dosya paylaşımını ayarlayın. Ayrıca bu iş akışını en iyi güvenlik uygulaması olarak öneriyoruz.
https://developer.android.com/training/d...ared/media
 

Güncel Delphi - Android Dosya Kayıt Bilgileri
 
Dahili depolama:  TPath.GetHomePath, TPath.GetDocumentsPath, TPath.GetCachePath, TPath.GetLibraryPath
Harici Özel (dahili depolama): TPath.GetPublicPath, TPath.GetTempPath, TPath.GetPicturesPath, TPath.GetCameraPath, TPath.GetMusicPath, TPath.GetMoviesPath, TPath.GetAlarmsPath, TPath.GetRingtonesPath, TPath.GetDownloadsPath
Paylaşılan Harici depolama: TPath.GetSharedDocumentsPath, TPath.GetSharedDownloadsPath, TPath.GetSharedCameraPath, TPath.GetSharedAlarmsPath, TPath.GetSharedPicturesPath, TPath.GetSharedMusicPath, TPath.GetSharedMoviesPath, TPath.GetSharedRingTonesPath

Standart RTL Android Dizin Fonksiyonları
Fonksiyon  *                   Android Hafızası (Java)  Android Klasörü
TPath.GetDocumentsPath GetFilesDir -> Dahili Hafıza /data/data/<application ID>/files
TPath.GetCachePath GetCacheDir -> Dahili Hafıza /data/data/<application ID>/cache
TPath.GetLibraryPath GetLibraryPath /data/app-lib/<application ID>-1
TPath.GetPublicPath GetExternalFilesDir -> Harici /storage/emulated/0/Android/data/<application ID>/files
TPath.GetTempPath GetExternalFilesDir+'/tmp/' /storage/emulated/0/Android/data/<application ID>/files/tmp
TPath.GetPicturesPath GetExternalPicturesDir -> Harici Hafıza /storage/emulated/0/Android/data/<application ID>/files/Pictures
TPath.GetCameraPath GetExternalCameraDir -> Harici Hafıza /storage/emulated/0/Android/data/<application ID>/files/DCIM
TPath.GetMusicPath GetExternalMusicDir -> Harici Hafıza /storage/emulated/0/Android/data/<application ID>/files/Music
TPath.GetMoviesPath GetExternalMoviesDir -> Harici Hafıza /storage/emulated/0/Android/data/<application ID>/files/Movies
TPath.GetAlarmsPath GetExternalAlarmsDir -> Harici Hafıza /storage/emulated/0/Android/data/<application ID>/files/Alarms
TPath.GetRingtonesPath  GetExternalRingtonesDir -> Harici Hafıza /storage/emulated/0/Android/data/<application ID>/files/Ringtones
TPath.GetDownloadsPath GetExternalDownloadsDir -> Harici Hafıza /storage/emulated/0/Android/data/<application ID>/files/Download
TPath.GetSharedDocumentsPath ** GetExternalFilesDir -> Harici Hafıza /storage/emulated/0/Documents
TPath.GetSharedDownloadsPath GetSharedDownloadsDir -> Paylaşılan Harici Hafıza /storage/emulated/0/Download
TPath.GetSharedCameraPath GetSharedCameraDir -> Paylaşılan Harici Hafıza /storage/emulated/0/DCIM
TPath.GetSharedAlarmsPath GetSharedAlarmsDir -> Paylaşılan Harici Hafıza /storage/emulated/0/Alarms
TPath.GetSharedPicturesPath GetSharedPicturesDir -> Paylaşılan Harici Hafıza /storage/emulated/0/Pictures
TPath.GetSharedMusicPath GetSharedMusicDir -> Paylaşılan Harici Hafıza /storage/emulated/0/Music
TPath.GetSharedMoviesPath GetSharedMoviesDir -> Paylaşılan Harici Hafıza /storage/emulated/0/Movies
TPath.GetSharedRingTonesPath GetSharedRingtonesDir -> Paylaşılan Harici Hafıza /storage/emulated/0/Ringtones
* System.SysUtils.pas ile birlikte gelen “GetHomePath” fonksiyonu, “TPath.GetDocumentsPath”ile aynı sonuç olan uygulama klasörünü (GetFilesDir -> Dahili Hafıza) vermektedir.
** TPath.GetSharedDocumentsPath : Cihazda fazladan harici bir depolama yeri daha (mesela SD kart) varsa, bu dizin bu harici depolama içindeki klasör adını vermektedir. Yoksa, cihaz depolaması içindeki herhangi bir yeri (/storage/emulated/0/Documents) vermektedir.
https://docwiki.embarcadero.com/RADStudi..._Platforms

Android Sürümlerinde Dosya Kayıt Depolama Değişiklikleri
┌──┬──┬───┬──────┬──────────────────────────────────────────────┐
| #   | Kod|SDK  |   Yayın     | Değişiklik                                                                                                      |
├──┼──┼───┼──────┼──────────────────────────────────────────────┤
| 1    | -    |   1    | 23.09.08 | Intent, System picker (Action_*) ilk sürüm içinde başladı                              |
| 4.4 | K   |  19   | 31.10.13 | FileProvider (Content API), DocumentsContract, DocumentProvider sınıfları |
| 7    | N   |  25   | 04.10.16 | FileProvider kullanmak mecburi  (Bu sürümü hedefleyince)                           |
| 8    | O   |  26   | 21.03.17 | Ağustos 2018’de Google Play Store’da hedef sürüm 8.0 yayın şartı             |
| 10  | Q   |  29   | 03.09.19 | Kapsamlı depolama (scoped storage). Gizlilik ve güvenlik artırıldı                    |
| 11  | R   |  30   | 09.09.20 | Ağustos 2021’de Android API 30 hedefleme (Google Play Store yayın şartı)  |
└──┴──┴───┴──────┴──────────────────────────────────────────────┘
 
Android 4.3 ve öncesinde, uygulamanızdan başka bir uygulamanın dosyasına ulaşmak için, ACTION_PICK ya da ACTION_GET_CONTENT gibi bir intent çağırmanız ve kullanıcının mevcut bir dosyayı seçmesi için de bir arabirim gerekiyordu.
Android 4.4 (API seviyesi 19) ve üstünde, ilave bir seçenek olarak ACTION_OPEN_DOCUMENT niyetine sahipsiniz ki bu da mevcut tüm dosyaları listeleyen sistem kontrollü seçici arabirimini göstermektedir.
Android 5.0 (API seviyesi 21) ve üstünde, ACTION_OPEN_DOCUMENT_TREE niyeti tüm dizinleri gösteren arabirime ulaşmanızı sağlamaktadır.
https://developer.android.com/guide/topi...t-provider


Delphi sürümlerinde Android Desteği

RAD Studio Delphi’nin Android 10’u destekleyen sürümlerden itibaren Android Java kütüphanelerinde (Androidapi.JNI.GraphicsContentViewText, Androidapi.IOUtils, Androidapi.JNI.Media, Androidapi.JNI.Provider, Androidapi.JNI.App, Androidapi.Helpers, Androidapi.JNI.JavaTypes, Androidapi.JNI.Net, Androidapi.JNI.Os, Androidapi.JNI.Provider) Kapsamlı depolama, SAF ve MediaStore komutları da XE8’den itibaren RAD Studio beraberinde sağlanmakta.

Delphi              Android
───────────
XE5                 2.33 – 4.4
10 Seattle         4.03 – 5
10.1 Berlin       4.03 – 7
10.2 Tokyo         4.1 – 8
10.3 Rio             5.1 – 10
10.4 Sydney          6 – 11
11 Alexandria     8.1 – 11

RAD Studio 11.0 Alexandria ile Android 30 API desteği (Google Play Store’un 2021 şartı) yerleşik olarak gelmektedir.

 
Uygulamada Kapsamlı Depolama

İkaz:  Android cihazlarda Dosya yöneticisi (Dosyalarım vs.) altında depolama dizinlerini Dahili depolama, SD kart, Ağ deplaması olarak gösteriyor. Halbuki bizim yüklediğimiz, cihazda çalışan uygulamalar depolamaları böyle algılamıyor. Uygulamalarımıza göre Dahili Depolama kum havuzu (sandbox) koruması altında olan, Dosya yöneticisi ve diğer uygulamalar tarafından görülemeyen klasörlerdir. Harici Depolama da uygulamamızın klasörleri dışında olan Paylaşılan Harici klasörlerdir. Dolayısıyla aşağıdaki açıklamalarda Dosya yöneticisi ve uygulamalarımız için Dahili ve Harici Depolama kavramlarını karıştırmayıp, uygulama açısından anlaşılmalıdır.

Android’de Dosya Depolama ve Paylaşma konusuna göre farklar:
  • Dahili depolama için Android 11’de de fark yok. Android 10 ve öncesinde olduğu gibi, aynı şekilde dosyalara ulaşmak sınırsız. Yine izin gerekmiyor.
  • Dosya paylaşma aynı şekilde, yani yine Content API (FileProvider) ve “Intent.setAction(TJIntent.JavaClass.ACTION_SEND);” niyeti ile çağırma şekli devam etmekte. FileProvider için Delphi 10.3 Rio’dan itibaren yerleşik gelen Project Options->Application->Entitlement List kısmındaki "Secure File Sharing" seçeneğini işaretlemeli, 10.3 önceki sürümlerinde ise Android’de Dosya Depolama ve Paylaşma > “Dosya paylaşımı için FileProvider kullanma” altında anlatıldığı şekilde ilave edilerek bu API kullanmaya devam edilmelidir.
  • Android’de Dosya Depolama ve Paylaşma bulunan örnek projedeki (Android 10 SDK 29 veya altı hedeflendiğinde) çalışan harici depolama komutları, ör: “Memo1.Lines.SaveToFile(TPath.Combine(TPath.GetSharedDownloadsPath, 'memo1Harici.txt'));” Android 11 SDK 30’u hedeflendiği anda, aynı uygulamada “Cannot create file “(storage/emulated/0/Download/memo1Harici.txt”. Permission denied” hatası veriyor. “DeleteFile” komutu İndirilenler / GetSharedDownloadsPath altındaki dosyaları silmiyor. Bu durum sadece “Download” değil tüm Paylaşılan Harici klasörler için geçerli. Buların sebebi, yukarıdaki konularda bahsedildiği gibi, Android 11 kısıtlamaları.
  • Android 10 öncesi ile Android 11 ile mecburi olan Kapsamlı Depolama sonrasındaki dosya kayıt depolama değişiklikleri ve güncel durum:
  • requestLegacyExternalStorage”  Kapsamlı Depolamadan çıkarılmış olup artık ulaşmak isteyen uygulamalar G.P.Store’da reddediliyor. Uygulamanızı yayınlamak istiyorsanız AndroidManifest.template.xml’de şu değişikliği yapınız:
android:requestLegacyExternalStorage="false">
  • All Files Access API (ACTION_MANAGE_STORAGE), Google Play Store tarafından reddediliyor, ama bu izin varsa (ve uygulama yayınlanmayacaksa) Android 11 öncesinde olduğu gibi dosya okuyup kaydedebiliyor. Buna ihtiyaç duyarsanız “AndroidManifest.template.xml” altına <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> ekleyip, çalışma zamanında da “ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION” izni isteyip ACTION_MANAGE_STORAGE intentini çağırmanız gerekiyor.
  • Paylaşmalı Veritabanları / Databases BlobStoreManager API sadece Android 11 SDK 30 ve üstünde çalışıp, Android 10 ve önceki sürümlerde çalışmamakta, bugün için çok az cihaz desteklediği için incelemeye alınmadı.
  • Paylaşmalı (harici) depolamaya SAF ve MediaStore ile ulaşma:
Şekil 1 Android Depolama sürüm 10 öncesi ve üstü
  • URI Alma: Yukarıdaki eğitim kodlarından sadece ilk üçü olan Kullanma Vakaları örnekleri, hem dosya Uri’lerini hem de bunlara ulaşma izni almaktadır. OnActivityResult ile bunların eylemleri yakalanarak harici dosya URI’lerine ulaşma tanıtılmaktadır.
  • Diğer örneklerin tümü ise bu 3 kullanma vakaları eylemleri ile elde edilen URI’ler ile nasıl işlem yapılacağını anlatmakta. Dosya Uri’sini bilmiyorsanız bunları kullanamıyorsunuz.
  • Ayrıca eski ACTION_GET_CONTENT niyeti de, ACTION_OPEN_DOCUMENT’in benzeri şekilde, Sistem Seçiciyi çağırıp Uri almak için kullanılabilmektedir; fakat kalıcı ulaşma izni sağlamamaktadır.
  • MediaStore.Files ile de medya depolamasındaki dosyaların Uri’si alınabilmektedir, fakat “Medya mağazasında ayrıca MediaStore.Files içeriği, uygulamanızın Android 10 veya sonraki sürümleri hedefleyen uygulamalarda bulunan kapsamlı depolama kullanıp kullanmadığına bağlıdır” ibaresinden dolayı bu konu muhteviyatına alınmadı. MediaStore.Downloads ve getMediaUri sınıfları da yine harici depolama dosya Uri’lerine ulaşmayı sağlasa da yalnızca SDK 29 ve üstü destek vermektedir.
  • Eğer vaka kullanmadan, direk olarak bildiğiniz bir dosya Uri’sini (ör: content://com.android.providers.downloads.documents/documents/16874) herhangi kodla dosya üzerinde işlem yapmak isterseniz, “java.lang.SecurityException: Permission Denial: reading ... requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs” hata iletisi alıp, dosyaya ulaşamıyorsunuz.
  • Özet olarak, Kapsamlı Depolamada harici depolama dosya kayıt uygulamaları, Uri darboğazında. Üstelik SDK 29 öncesi eski sürümleri kullanan piyasadaki Android cihazların tamamını kapsamak istiyorsanız, SAF kullanma vakaları kullanmak zorundasınız, bunların dışında çıkış yolu yok. Android Developer konferans, video, duyurularında Kapsamlı Depolamaya geçme amaçlarının gereksiz izinlerini kaldırmak olduğunu iddia ediyorlar, fakat gerçekte geliştiricileri ve kullanıcılar açısından tam tersi bir durum gerçekleşmiş durumda.
  • Örnek kodlar (SAF eğitim konusundakilerin tümü ve MediaStore konusundakilerin çoğu) SDK seviyesi 1-24 arasında olup, Delphi 10.x sürümleri tarafından desteklenmektedir.
  • SDK seviyeleri 29 ve 30 olan MediaStore’daki “Downloads”, “getMediaUri” sınıfları, Dosya küçük resimleri (thumbnails) yükleme > loadThumbnail, Mevcut bir koleksiyona medya öğesi eklemek / Add an item > VOLUME_EXTERNAL_PRIMARY, Medya dosyaları bekleme durumunu yenileme / Toggle pending status for media files > IS_PENDING flag, Başka uygulamaların medya dosyalarının güncellenmesi / Update other apps' media files > RecoverableSecurityException, - Medya dosya gruplarını yönetin / Manage groups of media files > createWriteRequest createTrashRequest createFavoriteRequest” için Delphi 11 gereklidir.
  • Dosya paylaşma için, harici klasörde ise yine önce önce Uri alıp, dahili klasöre kopyalamalısınız. Kopyalamak için de Akış (JInputStream, JFileOutputStream) komutlarını kullanmanız gerekmektedir. SAF ile alınmış Uri varsa Harici depolamada; yeni dosya açma, okuma, kaydetme, kopyalama amaçları için kullanabilirsiniz. Akışlar Sistem Dosya Seçiciyi açmaz.
  • Harici Depolama İçin Yürürlükten Kalkanlar:
// HariciDosya := TPath.Combine(TPath.GetSharedDownloadsPath,'delphican.txt');
Memo1.Lines.SaveToFile(HariciDosya);
TFile.Copy(DahiliDosya, HariciDosya);
DeleteFile(HariciDosya);
Uri := TJnet_Uri.JavaClass.fromFile(TJFile.JavaClass.init(StringToJString(HariciDosya)));
Uri := TJnet_Uri.JavaClass.parse(StringToJString('file:///' + HariciDosya));
JinputStream1 := TJFileInputStream.JavaClass.init(StringToJString(HariciDosya));
Uri := TJFileProvider.JavaClass.getUriForFile(TAndroidHelper.Context,  
    LAuthority, TJFile.JavaClass.init(StringToJString(HariciDosya)));
Intent := TJFileProvider.JavaClass.getUriForFile(TJnet_Uri.JavaClass.fromFile
   (TJFile.JavaClass.init(StringToJString(HariciDosya))));
Uri := TAndroidHelper.JFileToJURI (TJFile.JavaClass.init(StringToJString(dosya)));
Uri := TJFileProvider.JavaClass.getUriForFile(Context, LAuthority, AFile);
  • Özetle tüm File, Content, FileProvider eylem ve niyetleri ile Harici dizinlere (scoped / depreciated) ulaşılamıyor.
    (Not: ACTION_OPEN_DOCUMENT_TREE ile dosyanın bulunduğu dizine ulaşma izni alınırsa, bu komutlarla ulaşılabilir, fakat bu niyet Delphi 10’da çalışmadığı için denenemedi.)
  • Depolama Erişimi Çerçevesi / Storage Access Framework SAF kullanmak için Project -> Uses Permissions altından herhangi bir İzin gerekmemektedir, hepsi kapatılabilir. Proje içinden de “PermissionsService.RequestPermissions” vs. ile herhangi bir İzin çağrılmadan kullanılabilir. Örnek projedeki tüm izinler kapatılmıştır.
  • SAF ile dosyalara ulaşmak için sadece Kullanma Vakaları (ACTION_CREATE_DOCUMENT, ACTION_OPEN_DOCUMENT, ACTION_OPEN_DOCUMENT_TREE) niyetlerini çağırmak kâfidir. Yalnız bunların herbiri, eski TFile.Copy’den farklı olarak, Sistem Seçici / System Picker arabirimini açmaktadır.
  • ACTION _CREATE_DOCUMENT farklı kaydet benzeri bir arabirim ile yeni dosya kaydetmek için,  ACTION_OPEN_DOCUMENT ise mevcut dosyaları açıp, göstermek, değiştirmek için kullanılır. ACTION_OPEN_DOCUMENT_TREE sadece dizinlere değil altındaki tüm dosyalara da ulaşma amaçlıdır.
  • SAF kullanırken AndroidManifest.template.xml içinde intent-filter kısmı Delphi ile hazır halde geliyor, başka niyet eklemeye lüzum yok.
            <intent-filter>  
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
  • SAF Durum Vakaları; tüm Delphi 10 sürümleri ile yerleşik geldiği için, SDK 30’u hedefleyen Delphi projelerinde sadece gerekli Android kütüphaneleri ve örnek kodlar ile çağırılarak, hemen kullanılabilir durumdadır.
  • Medya deposu / MediaStore ile ses, görüntü, video dosyalarına ve sürüm 11 üstünde İndirilenlere yazma sınırlandırılmamıştır: Ör. Resmi izinsiz olarak kaydedebilirsiniz.
  • Media kolleksiyonları sadece İzin ile okunabilir.
  • Görüntü konum verisi için ACCESS_MEDIA_LOCATION gereklidir.
  • PDF, metin, vb. dosyalara ulaşmak “Sistem Seçici” System Picker ile mümkündür.
  • Kolleksiyon dışında okuma yazma için “Sistem Seçici” gereklidir.
  • Project -> Uses Permissions altında WRITE_EXTERNAL_STORAGE seçeneği SDK 29 üstünde kapatılmalıdır. READ_EXTERNAL_STORAGE okuma için gereklidir.
  • MediaStore Uri sınıfları, SDK 29 üstündeki dosyalara ulaşma amacıyla için çıkarıldığı için, Delphi 11 öncesi kütüphanelerinde mevcut değil. Dolayısıyla yukarıdaki MediaStore eğitim kodlarının çoğu sadece Java’dan Object Pascal’a aktarılamadı.
  • Çalışma zamanında da cihazın SDK seviyesine bakılarak, (SDK<28) yazma izni istenmeli veya (SDK>=29) için istenmemelidir.
  • AndroidManifest.template.xml Değişiklikleri :
<uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="30" />
android:requestLegacyExternalStorage="false">
  • MediaStore’da da harici depolamada okuma izni gerekiyor, yazma izni gerekmiyor. Android 10 öncesi sürümlerle uyumluluk için Project -> Uses Permissions altında WRITE_EXTERNAL_STORAGE seçeneğini kaldırıp, Manifest’te şunu ekleyiniz:   
<%uses-permission%>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
   android:maxSdkVersion="28" />
  • Dosya kopyalama artık harici depolamadan tek komutla (TFile.Copy) gerçekleştirilemiyor. Önce Kullanma Vaka (ACTION_OPEN_DOCUMENT veya ACTION_CREATE_DOCUMENT) niyetini çağırıyoruz.  Sonra “IletiFaaliyetiYakala” ve “OnActivityResult” ile talep kodunu yakalıyoruz. En son Uri ve akışlar (JInputStream, JFileOutputStream) ile yeni açtığımız dosyaya yazıyoruz.
  • Örnek proje içerisindeki bazı kısımlar çalışmayabilir. Delphi ve Android sürümleri ile bildirilirse ileride güncellenebilir. Hepimize faydalı olması dileği ile..
  • Dosya paylaşma
procedure TForm1.ButtonDosyaPaylasinClick(Sender: TObject);
var
  Intent: JIntent;
  mime: JMimeTypeMap;
  ExtToMime: JString;
  ExtFile: string;
  Dosya: string;
begin
  Dosya := DosyaAdi(UriCan);
  ExtFile := AnsiLowerCase(StringReplace(TPath.GetExtension(Dosya),
    '.', '', []));
  mime := TJMimeTypeMap.JavaClass.getSingleton();
  ExtToMime := mime.getMimeTypeFromExtension(StringToJString(ExtFile));
  Intent := TJIntent.Create;
  Intent.setAction(TJIntent.JavaClass.ACTION_SEND);
  Intent.setDataAndType(UriCan, ExtToMime);
  Intent.putExtra(TJIntent.JavaClass.EXTRA_STREAM, JParcelable(UriCan));
  Intent.addFlags(TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION);
  TAndroidHelper.Activity.startActivity(TJIntent.JavaClass.createChooser(Intent,
    StrToJCharSequence('Paylaş Bakalım: ')));
end;
  • Dosya kopyalayın Dahili -> Harici
procedure TForm1.ButtonDosyaKopyalayinDahilidenHariciyeClick(Sender: TObject);
(* TFile.Copy(TPath.Combine(TPath.GetDocumentsPath, 'delphican.pdf'),
  TPath.Combine(TPath.GetSharedDownloadsPath, 'delphican.pdf')); *)
  procedure PdfDosyasiOlustur(seciciBaslangicUri: JNet_Uri);
  var
    Intent: JIntent;
  begin
    Intent := TJIntent.Create;
    Intent.setAction(TJIntent.JavaClass.ACTION_CREATE_DOCUMENT);
    Intent.addCategory(TJIntent.JavaClass.CATEGORY_OPENABLE);
    Intent.setType(StringToJString('application/pdf'));
    Intent.putExtra(TJIntent.JavaClass.EXTRA_TITLE,
      StringToJString(TPath.GetFileName(DosyaKopyalanan)));
    Intent.putExtra(TJDocumentsContract.JavaClass.EXTRA_INITIAL_URI,
      JParcelable(seciciBaslangicUri));
    MainActivity.startActivityForResult(Intent,
      Dosya_Kopyala_Dahiliden_Hariciye);
  end;
begin
  DosyaKopyalanan := TPath.Combine(TPath.GetDocumentsPath, 'delphican.pdf');
  PdfDosyasiOlustur(nil);
end;
 
procedure TForm1.DosyaKopyalaci_DahilidenHariciye(Dosya: string);
const
  bufferSize = 4096 * 2;
var
  noOfBytes: Integer;
  b: TJavaArray<Byte>;
  DosyaOku: JInputStream;
  DosyaYaz: JFileOutputStream;
  pfd: JParcelFileDescriptor;
begin
  if not FileExists(Dosya) then
  begin
    Teblig(Dosya + ' bulunamadı!');
    exit;
  end;
  try
    DosyaOku := TAndroidHelper.Context.getContentResolver.openInputStream
      (TJnet_Uri.JavaClass.fromFile(TJFile.JavaClass.init
      (StringToJString(Dosya))));
    pfd := TAndroidHelper.Activity.getContentResolver.openFileDescriptor(UriCan,
      StringToJString('w'));
    DosyaYaz := TJFileOutputStream.JavaClass.init(pfd.getFileDescriptor);
    b := TJavaArray<Byte>.Create(bufferSize);
    noOfBytes := DosyaOku.read(b);
    while (noOfBytes > 0) do
    begin
      DosyaYaz.write(b, 0, noOfBytes);
      noOfBytes := DosyaOku.read(b);
    end;
    DosyaYaz.close;
    DosyaOku.close;
  except
    on E: Exception do
      Application.ShowException(E);
  end;
end;
  • Dosya kopyalayın Harici -> Dahili
procedure TForm1.ButtonDosyaKopyalayinHaricidenDahiliyeClick(Sender: TObject);
(* TFile.Copy(TPath.Combine(TPath.GetSharedDownloadsPath, 'delphican.pdf'),
  TPath.Combine(TPath.GetPublicPath, 'delphican.pdf')); *)
var
  Intent: JIntent;
begin
  Intent := TJIntent.Create;
  Intent.setAction(TJIntent.JavaClass.ACTION_OPEN_DOCUMENT);
  Intent.addCategory(TJIntent.JavaClass.CATEGORY_OPENABLE);
  Intent.setType(StringToJString('*/*'));
  TAndroidHelper.Activity.startActivityForResult(Intent,
    Dosya_Kopyala_Hariciden_Dahiliye);
end;
 
procedure TForm1.DosyaKopyalaci_HaricidenDahiliye;
const
  bufferSize = 4096 * 2;
var
  noOfBytes: Integer;
  b: TJavaArray<Byte>;
  DosyaOku: JInputStream;
  DosyaYaz: JFileOutputStream;
  Dosya: string;
begin
  try
    Dosya := TPath.Combine(TPath.GetPublicPath, DosyaAdi(UriCan));
    if FileExists(Dosya) then
    begin
      Teblig('"' + Dosya + '" zaten mevcut!');
      exit;
    end;
    DosyaYaz := TJFileOutputStream.JavaClass.init(StringToJString(Dosya));
    DosyaOku := TAndroidHelper.Context.getContentResolver.
      openInputStream(UriCan);
    b := TJavaArray<Byte>.Create(bufferSize);
    noOfBytes := DosyaOku.read(b);
    while (noOfBytes > 0) do
    begin
      DosyaYaz.write(b, 0, noOfBytes);
      noOfBytes := DosyaOku.read(b);
    end;
    DosyaYaz.close;
    DosyaOku.close;
  except
    on E: Exception do
      Application.ShowException(E);
  end;
end;


Ek Dosyalar Resimler
       

.zip   addp_SAF.zip (Dosya Boyutu: 106,36 KB / İndirme Sayısı: 37)
Cevapla
#4
Hocam mükemmel bir makale olmuş.. Elinize sağlık.
// Bilgi paylaştıkça çoğalır.. 

Cevapla
#5
Teşekkürler elinize sağlık.
Cevapla
#6
Hocam ellerinize sağlık çok yararlı bir doküman olmuş.
Cevapla
#7
Github örnek proje bağlantısı https://github.com/emozgun/delphi-android-SAF
Cevapla
#8
güzel bir çalışma olmuş, bende cameradan resim çekme için ihtiyaca oldu, delphi demo olan sistem ile devam etmiştim. yeni sisteme için iyi bir kayak sağlamışsınız
Cevapla
#9
var
Intent: JIntent;
begin
Intent := TJIntent.Create;
Intent.setAction(TJIntent.JavaClass.ACTION_OPEN_DOCUMENT);  // ACTION_GET_CONTENT);
Intent.addCategory(TJIntent.JavaClass.CATEGORY_OPENABLE);
Intent.setType(StringToJString('*/*'));
TAndroidHelper.Activity.startActivityForResult(Intent, 77);
End;



procedure OnActivityResult(RequestCode, ResultCode: Integer;
 Data: JIntent);
var
 Uri: JNet_Uri;
 Ad: string;
 AItem: TSkinListViewItem;
 UriString: JString;
begin

 if ResultCode = TJActivity.JavaClass.RESULT_OK then
 begin
 Uri := nil;

 ad:='';
   if Assigned(Data) then
   begin
     if (Data = nil) then
     begin
       exit;
     end;
     Uri := Data.getData;
     Ad := jstringtostring(Uri.getPath);// gerçek dosya adı
     if RequestCode = 77 then
     begin
        // kayıt       
     end;
   end;
 end;
end;


Android de seçtiğim dosyanın gerçek yolunu nasıl alabilirim. Uri.getPath ile "/document/primaryBig Grinocuments/XXXXXX.pdf" diye bir yol çıkıyor sonra bu yolu kullanamıyorum.

Ben : /storage/emulated/0/Documents/xxxx.pdf olarak istiyorum.
Cevapla
#10
Merhaba @elixir84
DosyaAdi fonksiyonunu değiştirin. Dosyanın gerçek yolunu "Documents/xxxx.pdf" şeklinde alacaksınız.

function TForm1.DosyaAdi(Uri: JNet_Uri): string;
var
 C: JCursor;
 I: integer;
begin
 C := TAndroidHelper.Activity.getContentResolver.query(Uri, nil,
   StringToJString(''), nil, StringToJString(''));
 C.moveToFirst;
 Result := '';
 for I := 0 to C.getColumnCount - 1 do
 begin
   if JStringToString(C.getColumnName(I)) = 'document_id' then
   begin
     Result  := JStringToString(C.getString(I));
     Break;
   end;
 end;
 Result := StringReplace(Result, 'primary:', '', [rfReplaceAll]);
end;
Cevapla


Konu ile Alakalı Benzer Konular
Konular Yazar Yorumlar Okunma Son Yorum
  Android Proje Donma/ Takılma Problemleri kajmerantime 15 460 4 saat önce
Son Yorum: mcuyan
  Fmx Android - Termal Yazıcı Resim Yazdırma hi_selamlar 8 574 17-10-2024, Saat: 18:21
Son Yorum: esistem
  Delphi FMX Android admar2010 6 309 26-08-2024, Saat: 14:25
Son Yorum: RAD Coder
  Rad Studio 12 ile android sdk zombi 2 215 20-08-2024, Saat: 21:00
Son Yorum: zombi
  Android 33 api sdk güncelleme [ÇÖZÜLDÜ] codder71 15 1.745 14-07-2024, Saat: 09:11
Son Yorum: RAD Coder



Konuyu Okuyanlar: 1 Ziyaretçi