![]() |
|
Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - Baskı Önizleme +- Delphi Can (https://www.delphican.com) +-- Forum: Delphi (https://www.delphican.com/forumdisplay.php?fid=3) +--- Forum: Mobil Platform - FireMonkey (FMX) (https://www.delphican.com/forumdisplay.php?fid=7) +--- Konu Başlığı: Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore (/showthread.php?tid=6470) Sayfalar:
1
2
|
Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - emozgun - 08-12-2021 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/versions/10/privacy/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:
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/android-developer/answer/10467955?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:
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:
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: ![]() Ş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:
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: ![]() Şekil2. Depolama Erişim Çerçevesi akış şeması Aşağıdakilere dikkat ediniz:
Ş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. Ş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:
https://developer.android.com/guide/topics/providers/document-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:
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:
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/data-storage/shared/documents-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/android/provider/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:
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:
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. Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - kullaniciD - 08-12-2021 ..... Cvp: Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - emozgun - 08-12-2021 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:
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:
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/data-storage/shared/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/RADStudio/Alexandria/en/Standard_RTL_Path_Functions_across_the_Supported_Target_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/topics/providers/document-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:
android:requestLegacyExternalStorage="false">
// 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);
<intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter>
<uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="30" /> android:requestLegacyExternalStorage="false">
<%uses-permission%> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
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;
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;
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;
Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - mcuyan - 09-12-2021 Hocam mükemmel bir makale olmuş.. Elinize sağlık. Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - frmman - 09-12-2021 Teşekkürler elinize sağlık. Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - codder71 - 09-12-2021 Hocam ellerinize sağlık çok yararlı bir doküman olmuş. Cvp: Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - emozgun - 16-12-2021 Github örnek proje bağlantısı https://github.com/emozgun/delphi-android-SAF Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - nguzeller - 16-12-2021 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 Cvp: Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - elixir84 - 25-01-2024
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/primary ocuments/XXXXXX.pdf" diye bir yol çıkıyor sonra bu yolu kullanamıyorum.Ben : /storage/emulated/0/Documents/xxxx.pdf olarak istiyorum. Cvp: Android’de Dosya Depolama ve Paylaşma-2: And 11 SDK 30 Scoped Storage SAF MediaStore - emozgun - 09-03-2024 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;
|