تغييرات الخصوصية في Android 10
Android 11 - الخصوصية والأمن
تغييرات السلوك: التطبيقات التي تستهدف Android 11 - الخصوصية
استخدام جميع ملفات الوصول (manage_external_storage) إذن
نظرة عامة على التخزين المشترك
بعد تغييرات Android 10 و 11 الخصوصية والأمان ، يتم الوصول المباشر إلى التخزين عن طريق التطبيقات التي يتم تحديدها إلى داخلي فقط والوصول الآن إلى الملفات الخارجية الممكنة وببساطة بواسطة 4 واجهات برمجة التطبيقات:
جميع الملفات API API (manage_external_storage - action_manage_app_all_files_access_permission إذن و action_manage_storage intent) متاح لتطبيقات مدير الملفات ومكافحة الفيروسات فقط ، يتم رفض جميع تطبيقات الأنواع الأخرى بواسطة Google Play Store Publishing. تقوم قواعد البيانات (BlobStorEmanager) API بتشغيل فقط Android 11 SDK 30 والإصدارات العليا ، وليس Android 10 والإصدارات السابقة. لذلك تم استبعاد كل من واجهات برمجة التطبيقات أدناه.
افتح الملفات باستخدام إطار الوصول إلى التخزين
الوصول إلى المستندات والملفات الأخرى من التخزين المشترك
استخدام حالات الوصول إلى المستندات والملفات الأخرى
يدعم إطار الوصول إلى التخزين حالات الاستخدام التالية للوصول إلى الملفات والمستندات الأخرى.
إنشاء ملف جديد
The ACTION_CREATE_DOCUMENT intent action allows users to save a file in a specific location.
افتح وثيقة أو ملف
The ACTION_OPEN_DOCUMENT intent action allows users to select a specific document or file to open.
منح الوصول إلى محتويات الدليل
The ACTION_OPEN_DOCUMENT_TREE intent action, available on Android 5.0 (API level 21) and higher, allows users to select a specific directory, granting your app access to all of the files and sub-directories within that directory.
توفر الأقسام التالية إرشادات حول كيفية تكوين كل حالة استخدام.
إنشاء ملف جديد
// Request code for creating a PDF document.
const CREATE_FILE : integer = 11 ; // CREATE_FILE = 1
procedure createFile (pickerInitialUri : JNet_Uri); (* PdfDosyasiOlustur *)
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( ' invoice.pdf ' ));
// Optionally, specify a URI for the directory that should be opened in
// the system file picker when your app creates the document.
Intent.putExtra(TJDocumentsContract.JavaClass.EXTRA_INITIAL_URI,JParcelable(pickerInitialUri));
MainActivity.startActivityForResult(Intent, CREATE_FILE);
end ;افتح ملف
// Request code for selecting a PDF document.
// const PICK_PDF_FILE : integer = 22; //PICK_PDF_FILE = 2
procedure openFile (pickerInitialUri : JNet_Uri); (* PdfDosyasiSec *)
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 ' ));
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
Intent.putExtra(TJDocumentsContract.JavaClass.EXTRA_INITIAL_URI,JParcelable(pickerInitialUri));
TAndroidHelper.Activity.startActivityForResult(Intent, PICK_PDF_FILE);
end ;منح الوصول إلى محتويات الدليل
procedure openDirectory (uriToLoad : JNet_Uri); (* DizinAc *)
// Choose a directory using the system's file picker.
var
Intent : JIntent;
begin
Intent := TJIntent.Create;
Intent.setAction(TJIntent.JavaClass.ACTION_OPEN_DOCUMENT_TREE);
// Optionally, specify a URI for the directory that should be opened in
// the system file picker when it loads.
Intent.putExtra(TJDocumentsContract.JavaClass.EXTRA_INITIAL_URI, JParcelable(uriToLoad));
Mainactivity.startActivityForResult(Intent, Open_Doc_Tree);
end ;أداء العمليات على الموقع المختار
procedure TForm1.HandleMessageAction ( const Sender: TObject; const M: TMessage); (* IletiFaaliyetiYakala *)
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
// The result data contains a URI for the document or directory that
// the user selected.
Uri := nil ;
if Assigned(Data) then
begin
Uri := Data.getData;
if RequestCode = your-request-code then
begin
// Perform operations on the document using its URI.
end ;
end ;
end ;من خلال الحصول على إشارة إلى URI للعنصر المحدد ، يمكن للتطبيق الخاص بك إجراء عدة عمليات على العنصر. على سبيل المثال ، يمكنك الوصول إلى بيانات تعريف العنصر ، وتعديل العنصر في مكانه ، وحذف العنصر. توضح الأقسام التالية كيفية إكمال الإجراءات على الملفات التي يحددها المستخدم:
تستمر الأذونات
// TakeFlags: integer;
Intent := TJIntent.Create;
TakeFlags := Intent.getFlags
and (TJIntent.JavaClass.FLAG_GRANT_READ_URI_PERMISSION
or TJIntent.JavaClass.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
TAndroidHelper.Activity.getContentResolver.takePersistableUriPermission
(Uri, TakeFlags);فحص بيانات الوثيقة
procedure dumpImageMetaData (uri : JNet_Uri); (* GoruntuMetaVerisiDokumu *)
// The query, because it only applies to a single document, returns only
// one row. There's no need to filter, sort, or select fields,
// because we want all fields for one document.
var
displayName, size : JString;
sizeIndex : integer;
cursor : JCursor;
begin
cursor := TAndroidHelper.Activity.getContentResolver.query(uri, nil , nil , nil , nil , nil );
try
// moveToFirst() returns false if the cursor has 0 rows. Very handy for
// "if there's anything to look at, look at it" conditionals.
if (cursor<> nil ) then
if (cursor.moveToFirst) then
begin
displayName := cursor.getString (cursor.getColumnIndex (TJOpenableColumns.JavaClass.DISPLAY_NAME));
Memo1.Lines.Add( { TAG.ToString + } ' Display Name: ' + JStringToString (displayName));
sizeIndex:=cursor.getColumnIndex(TJOpenableColumns.JavaClass.SIZE);
size := nil ;
if not (cursor.isNull(sizeIndex)) then
size := cursor.getString(sizeIndex)
else
size:=StringToJString ( ' Unknown ' );
Memo1.Lines.Add( { TAG.ToString + } ' Size: ' + JStringToString (size));
end ;
finally
cursor.close;
end ;
end ;افتح وثيقة - صورة نقطية
function getBitmapFromUri (uri : JNet_Uri): JBitmap; (* UridenBiteslemAl *)
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 ;افتح مستندًا - دفق الإدخال
function TForm1.readTextFromUri (Uri : JNet_Uri): string; (* MetinDosyasiOkuyucu *)
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 ;تحرير وثيقة
procedure alterDocument (uri : JNet_Uri); (* MetinBelgesiDegistir *)
var
pfd : JParcelFileDescriptor;
fileOutputStream : JFileOutputStream;
begin
try
pfd := TAndroidHelper.Activity.getContentResolver
.openFileDescriptor(uri,StringToJString( ' w ' ));
fileOutputStream := TJFileOutputStream.JavaClass.init(pfd.getFileDescriptor);
fileOutputStream.write(StringToJString( ' Overwritten at ' + timetostr(Now)).getBytes);
fileOutputStream.close;
pfd.close;
except
on E: Exception do
ShowMessage(e.Message);
end ;
end ; حذف وثيقة
TJDocumentsContract.JavaClass.deleteDocument (TAndroidHelper.contentResolver, Uri);افتح ملفًا افتراضيًا
function isVirtualFile (Uri : JNet_Uri): boolean; (* SanalDosyami *)
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 ; ملف افتراضي كصورة
function getInputStreamForVirtualFile (Uri : JNet_Uri; mimeTypeFilter : String): JInputStream; (* SanalDosyaIcinGirisAkisiAl *)
var
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
raise Exception.Create( ' File not found! ' );
result := nil ;
exit;
end ;
result := resolver.openTypedAssetFileDescriptor(uri,openableMimeTypes[ 0 ], nil )
.createInputStream;
end ;وظائف مسار RTL القياسية عبر المنصات المستهدفة المدعومة: التخزين الداخلي ، الخارجي الداخلي الخاص ، تخزين خارجي مشترك
تغييرات التخزين في إصدارات Android
| # | شفرة | SDK | نشر | يتغير |
|---|---|---|---|---|
| 1 | - | 1 | 23.09.08 | القصد ، بدأ منتقي النظام (Action_) في الإصدار الأول |
| 4.4 | ك | 19 | 31.10.13 | FileProvider (Content API) ، DocumentScontract ، DocumentProvider Classes |
| 7 | ن | 25 | 04.10.16 | استخدام FileProvider إلزامي (عند استهداف هذا الإصدار) |
| 8 | س | 26 | 21.03.17 | أغسطس 2018 متجر Google Play و. 8.0 متطلبات النشر |
| 10 | س | 29 | 03.09.19 | تخزين النطاق. زادت الخصوصية والأمن |
| 11 | ص | 30 | 09.09.20 | أغسطس 2021 استهداف Android API 30 (متطلبات نشر متجر Google Play) |
مزود المستندات ، Android 4.3 Action_pick ، Action_get_content ؛ Android 4.4 (API 19) Action_open_document ؛ Android 5.0 (API 21) Action_open_document_tree
دعم Android في إصدارات Delphi
Rad Studio Delphi 10+ إصدارات ترافق Android 10+ Java Libraries (Androidapi.jni.graphicscontentviewtext ، Androidapi.ioutils ، androidapi.jni.media ، androidapi.jni.provider ، androidapi.jni.app ، androidapi.helpers ، androidapis ، Androidapi.jni.net ، Androidapi.jni.os ، Androidapi.jni.provider) دعم التخزين المنحدر ، SAF وبعض أوامر MediaStore.
| دلفي | Android |
|---|---|
| XE5 | 2.33 - 4.4 |
| 10 سياتل | 4.03 - 5 |
| 10.1 برلين | 4.03 - 7 |
| 10.2 طوكيو | 4.1 - 8 |
| 10.3 ريو | 5.1 - 10 |
| 10.4 سيدني | 6 - 11 |
| 11 الإسكندرية | 8.1 - 11 |
Rad Studio Delphi 11.0 Alexandria لديها دعم Android 30 API (متطلبات Google Play Store 2021).
الاختلافات في تخزين الملفات والمشاركة في Android:
• لا يوجد فرق في Android 11 للتخزين الداخلي. كما هو الحال في Android 10 وما قبله ، فإن الوصول إلى الملفات غير محدود. مرة أخرى ، لا يلزم إذن.
• مشاركة الملفات هي نفسها ، مرة أخرى Content API (FileProvider) وطريقة الاتصال بـ "intent.setAction (tjintent.javaclass.action_send) ؛" نية تستمر. لتمكين ميزة FileProvider ، يجب إضافة تحديد خيار "مشاركة الملفات الآمنة" في خيارات المشروع-> التطبيق-> قائمة الاستحقاق (المتوفرة في Delphi 10.3 Rio وبعد ذلك) لمواصلة استخدام واجهة برمجة التطبيقات هذه. بالنسبة إلى الإصدارات السابقة لـ Delphi 10.3 ، يجب إضافة FileProvider API يدويًا كما هو موضح في "تخزين الملفات والمشاركة في Android"> "استخدام FileProvider لمشاركة الملفات".
• أوامر نسخ التخزين الخارجية في "تخزين الملفات ومشاركتها في Android" ، يتم تشغيل نموذج المشروع عند استهداف Android 10 SDK 29 ، IE "memo1.lines.savetofile (tpath.combine (tpath.getsharedDownloadspath ، 'memo1external.txt')) ؛". عندما يستهدف Android 11 SDK 30 ، يرفع نفس التطبيق "لا يمكن إنشاء ملف" (تخزين/محاكاة/0/تنزيل/memo1external.txt ". استثناء من الإذن.
• الحالة الحالية قبل Android 10 وبعد Android 11 يتغير تخزين ملفات التخزين الإلزامية:
• تمت إزالة "requestlegacyexternalStorage" من تخزين النطاق والآن يتم رفض التطبيقات التي ترغب في الوصول إليها في GPStore. لنشر تطبيقك ، قم بإجراء التغيير التالي في AndroidManifest.template.xml ::
android:requestLegacyExternalStorage="false">
• يتم رفض جميع ملفات API للوصول إلى الملفات (Action_manage_storage) من قبل Google Play Store ، ولكن إذا تم منح هذا الإذن (ولن يتم نشر التطبيق) ، فيمكنه قراءة الملفات كما كان قبل Android 11. إذا كنت بحاجة إلى ذلك ، فأنت بحاجة إلى إضافة "androidmanifest.template.xml" وطلب "Action_Manage_Alp_Apples_Access_piles_permission".
• قواعد البيانات المشتركة blobstoremanager API تعمل فقط على Android 11 SDK 30 وما فوق ، لا يعمل على Android 10 والإصدارات السابقة.
• تقوم قواعد البيانات المشتركة بتشغيل BlobStoremanager API فقط Android 11 SDK 30 والإصدارات العليا ، ولا تعمل على تشغيل الإصدارات Android 10 والإصدارات السفلية. لا يتم تضمينه نظرًا لأنه لا يدعمه معظم الأجهزة حاليًا.
• الوصول إلى سعة التخزين المشتركة (الخارجية) مع SAF و MediaStore:
• الحصول على URI: فقط الأمثلة الثلاثة الأولى لحالات الاستخدام من رموز التدريب أعلاه تحصل على كل من File URI والإذن للوصول إليها. يتم الوصول إلى الملف الخارجي URIS عن طريق التقاط أفعالهم باستخدام onActivityResult.
• تصف جميع الأمثلة الأخرى كيفية التعامل مع URIS التي تم الحصول عليها من إجراءات الحالات الثلاثة هذه. إذا كنت لا تعرف ملف URI ، فلا يمكنك استخدامه.
• أيضًا ، يمكن استخدام نية Action_Get_Content القديمة للحصول على URI عن طريق استدعاء محدد النظام ، على غرار Action_open_document ؛ لكنه لا يوفر وصول دائم.
• مع mediaStore.files ، يمكن أيضًا استرداد URI للملفات الموجودة في تخزين الوسائط ، ولكن لم يتم تضمينه في محتوى هذا الموضوع نظرًا لبيان "محتوى MediaStore. تعتمد الإملاءات في متجر الوسائط أيضًا على ما إذا كان تطبيقك يستخدم التخزين الواسع المتاح في التطبيقات التي تستهدف Android 10 أو أعلى". على الرغم من أن MediaStore.Downloads و GetMediauri يوفرون أيضًا إمكانية الوصول إلى ملف تخزين خارجية URI ، إلا أنها تدعم فقط SDK 29 وما فوق.
• إذا كنت ترغب في العمل على ملف مع أي رمز دون استخدام حالة ، يمكنك استخدام ملف URI مباشرة (على سبيل المثال: المحتوى: //com.android.providers.downloads.documents/documents/16874) مع "java.lang.securityexception: إذن": القراءة ... تتطلب الحصول على الوصول إلى ACTION_OPEN_DOCITORY
• باختصار ، توجد تطبيقات تسجيل ملفات التخزين الخارجية في تخزين النطاق في عنق الزجاجة URI. علاوة على ذلك ، إذا كنت ترغب في تغطية جميع أجهزة Android في السوق باستخدام الإصدارات القديمة قبل SDK 29 ، فيجب عليك استخدام حالات استخدام SAF ، فلا توجد مخرجات غير ذلك. في مؤتمر Android Developer ، الفيديو ، يزعمون أن الغرض من التحول إلى تخزين النطاق هو إزالة الأذونات غير الضرورية ، ولكن في الواقع ، حدث العكس للمطورين والمستخدمين.
• رموز العينة (كلها على دروس SAF ومعظمها على المنصف) هي SDK المستوى 1-24 ويدعمها إصدارات Delphi 10.x.
• Delphi 11 مطلوب لاستخدام مستويات SDK 29 ve 30 أوامر mediactore من "التنزيلات" ، و "getMediauri" ، وتحميل الصور المصغرة> loadthumbnail ، إضافة عنصر> volume_external_primary ، تبديل المعلقة لملفات الوسائط> is_pending flag ' CreateFavorIteRequest ".
• لمشاركة الملفات من التخزين الخارجي أولاً الحصول على URI of File by SAF PIDE PACER ، ثم مشاركة النوايا المطلوبة. في حالة توفر الملف الخارجي URI ، يمكن استخدامه لفتح ملفات جديدة ، وقراءة ، حفظ ، نسخ.
• العناصر التي انخفضت تخزينها الخارجي:
// ExternalFile := TPath.Combine(TPath.GetSharedDownloadsPath,'delphican.txt');
Memo1.Lines.SaveToFile(ExternalFile);
TFile.Copy(InternalFile, ExternalFile);
DeleteFile(ExternalFile);
Uri := TJnet_Uri.JavaClass.fromFile(TJFile.JavaClass.init(StringToJString(ExternalFile)));
Uri := TJnet_Uri.JavaClass.parse(StringToJString( ' file:/// ' + ExternalFile));
JinputStream1 := TJFileInputStream.JavaClass.init(StringToJString(ExternalFile));
Uri := TJFileProvider.JavaClass.getUriForFile(TAndroidHelper.Context, LAuthority, TJFile.JavaClass.init(StringToJString(ExternalFile)));
Intent := TJFileProvider.JavaClass.getUriForFile(TJnet_Uri.JavaClass.fromFile(TJFile.JavaClass.init(StringToJString(ExternalFile))));
Uri := TAndroidHelper.JFileToJURI (TJFile.JavaClass.init(StringToJString(ExternalFile)));
Uri := TJFileProvider.JavaClass.getUriForFile(Context, LAuthority, AFile);باختصار ، يتم إهلاك جميع الملفات والمحتوى وإجراءات FileProvider والنوايا.
(PS: إذا تم الحصول على إذن للوصول إلى الدليل الذي يوجد فيه الملف بواسطة Action_open_document_tree ، فقد يكون من الممكن الوصول إلى الملفات من خلال هذه الأوامر ، ولكن لا يمكن تجربتها لأن هذه القصد لا تعمل في Delphi 10.))
• لاستخدام SAF Framework SAF ، لا توجد أذونات مطلوبة للحصول عليها من المشروع -> تستخدم الأذونات ، يمكن إغلاق جميعها. لا تحتاج المشروعات إلى أي إذن ، أي "أذونات Service.requestpermissions". جميع الأذونات في عينة المشروع قبالة.
• للوصول إلى الملفات مع SAF ، يكفي ببساطة استدعاء حالات استخدام (Action_create_document ، Action_open_document ، Action_open_document_tree). ومع ذلك ، يفتح كل واحد منهم واجهة منتقي النظام ، على عكس tfile.copy القديم.
• يتم استخدام Action_create_document لحفظ ملف جديد مع واجهة مماثلة لـ "حفظ AS" ، ويتم استخدام Action_open_document لفتح الملفات الحالية وإظهارها وتعديلها. Action_open_document_tree هو للوصول إلى ليس فقط الدلائل ولكن أيضًا جميع الملفات الموجودة تحتها.
• عند استخدام SAF ، فإن الجزء المرشح للقصد في AndroidManifest.template.xml جاهز مع Delphi ، ليست هناك حاجة لإضافة نية أخرى.
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
• تتوفر حالات استخدام SAF على الفور في مشاريع Delphi التي تستهدف SDK 30 ببساطة عن طريق الاتصال بها مع مكتبات Android اللازمة ورموز العينات ، نظرًا لأنها مدمجة مع جميع إصدارات Delphi 10.
• لا يتم تقييد الكتابة إلى الصوت والصورة وملفات الفيديو والتنزيلات أعلاه الإصدار 11 مع MediaStore. أي يمكنك حفظ الصورة دون إذن.
• لا يمكن قراءة مجموعات الوسائط إلا بإذن.
• Access_media_location مطلوب لبيانات موقع الصورة.
• PDF ، نص ، إلخ. الوصول إلى الملفات المتاحة بواسطة "منتقي النظام".
• "منتقي النظام" مطلوب للقراءة والكتابة خارج المجموعة.
• Project -> يستخدم الأذونات -> يجب إيقاف تشغيل خيار Write_External_Storage على SDK 29. read_external_storage مطلوب للقراءة.
• لا تتوفر فئات URI من MediaStore في مكتبات ما قبل Delphi 11 حيث تمت إزالتها للوصول إلى الملفات أعلى من SDK 29. لذلك لم يتم استيراد معظم رموز تعليمية منصف أعلاه من Java إلى Pascal.
• في وقت التشغيل ، اعتمادًا على مستوى SDK للجهاز ، يجب طلب إذن الكتابة (SDK <28) وليس لـ (SDK> = 29).
• AndroidManifest.template.xml التغييرات:
<uses-sdk android:minSdkVersion="%minSdkVersion%" android:targetSdkVersion="30" />
android:requestLegacyExternalStorage="false">
• تتطلب MediaStore إذن القراءة على التخزين الخارجي ، وليس إذن الكتابة. للحصول على التوافق مع الإصدارات قبل Android 10 ، ضمن المشروع -> يستخدم الأذونات ، قم بإزالة خيار Write_External_Storage وأضف ما يلي في البيان:
<%uses-permission%>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
• لم يعد بإمكان نسخ الملفات إجراء أمر واحد (tfile.copy) من التخزين الخارجي. أولاً ، نسمي حالة الاستخدام (Action_open_document أو Action_Create_Document) النية. ثم نتعامل مع رمز الطلب بـ "HandleMessageActivity" و "onActivityResult". أخيرًا ، نكتب إلى الملف الذي فتحناه للتو مع URI والتدفقات (JinputStream ، JFileOutputStream).
procedure TForm1.ButtonFileSharingClick (Sender: TObject);
var
Intent: JIntent;
mime: JMimeTypeMap;
ExtToMime: JString;
ExtFile: string;
File : string;
begin
File := File_name(UriCan);
ExtFile := AnsiLowerCase(StringReplace(TPath.GetExtension( File ),
' . ' , ' ' , []));
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( ' Let '' s share: ' )));
end ; procedure TForm1.ButtonCopyFileFromInternalToExternalClick (Sender: TObject);
(* TFile.Copy(TPath.Combine(TPath.GetDocumentsPath, 'delphican.pdf'),
TPath.Combine(TPath.GetSharedDownloadsPath, 'delphican.pdf')); *)
procedure CreateFilePdf (pickerInitialUri: 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(FileToBeCopied)));
Intent.putExtra(TJDocumentsContract.JavaClass.EXTRA_INITIAL_URI,
JParcelable(pickerInitialUri));
MainActivity.startActivityForResult(Intent,
Copy_File_FromInternal_ToExternal);
end ;
begin
FileToBeCopied := TPath.Combine(TPath.GetDocumentsPath, ' delphican.pdf ' );
CreateFilePdf( nil );
end ;
procedure TForm1.CopyFile_FromInternalToExternal ( File : string);
const
bufferSize = 4096 * 2 ;
var
noOfBytes: Integer;
b: TJavaArray<Byte>;
File_Read: JInputStream;
File_Write: JFileOutputStream;
pfd: JParcelFileDescriptor;
begin
if not FileExists( File ) then
begin
ShowMessage( File + ' not found! ' );
exit;
end ;
try
DosyaOku := TAndroidHelper.Context.getContentResolver.openInputStream
(TJnet_Uri.JavaClass.fromFile(TJFile.JavaClass.init
(StringToJString( File ))));
pfd := TAndroidHelper.Activity.getContentResolver.openFileDescriptor(UriCan,
StringToJString( ' w ' ));
DosyaYaz := TJFileOutputStream.JavaClass.init(pfd.getFileDescriptor);
b := TJavaArray<Byte>.Create(bufferSize);
noOfBytes := File_Read.read(b);
while (noOfBytes > 0 ) do
begin
File_Write.write(b, 0 , noOfBytes);
noOfBytes := File_Read.read(b);
end ;
File_Write.close;
File_Read.close;
except
on E: Exception do
Application.ShowException(E);
end ;
Showmessage( ' File copied from Internal to External : ' + DosyaAdi(UriCan));
end ; procedure TForm1.ButtonFileCopyFromExternalToInternalClick (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,
Copy_File_FromExternal_ToInternal);
end ;
procedure TForm1.FileCopy_FromExternalToInternal ;
const
bufferSize = 4096 * 2 ;
var
noOfBytes: Integer;
b: TJavaArray<Byte>;
File_Read: JInputStream;
File_Write: JFileOutputStream;
File : string;
// pfd : JParcelFileDescriptor;
begin
try
Dosya := TPath.Combine(TPath.GetPublicPath, DosyaAdi(UriCan));
if FileExists( File ) then
begin
ShowMessage( ' " ' + File + ' " zaten mevcut! ' );
exit;
end ;
File_Write := TJFileOutputStream.JavaClass.init(StringToJString( File ));
File_Read := TAndroidHelper.Context.getContentResolver.
openInputStream(UriCan);
b := TJavaArray<Byte>.Create(bufferSize);
noOfBytes := File_Read.read(b);
while (noOfBytes > 0 ) do
begin
File_Write.write(b, 0 , noOfBytes);
noOfBytes := File_Read.read(b);
end ;
File_Write.close;
File_Read.close;
except
on E: Exception do
Application.ShowException(E);
end ;
ShowMessage( ' File copied from External to Internal : ' + File_Name(UriCan));
end ;