لتوفير تجربة مستخدم أكثر إثراءً، تتيح العديد من التطبيقات للمستخدمين المساهمة في المحتوى والوصول إلى الوسائط المتوفّرة على وحدة تخزين خارجية. يقدّم إطار العمل فهرسًا محسّنًا لمجموعات الوسائط، يُعرف باسم متجر الوسائط، يتيح للمستخدمين استرداد ملفات الوسائط هذه وتعديلها بسهولة أكبر. وتظل هذه الملفات على جهاز المستخدم حتى بعد إلغاء تثبيت تطبيقك.
أداة اختيار الصور
كبديل لاستخدام "متجر الوسائط"، تقدّم أداة اختيار الصور في Android طريقة آمنة ومضمّنة للمستخدمين لاختيار ملفات الوسائط بدون الحاجة إلى منح تطبيقك إذن الوصول إلى مكتبة الوسائط بأكملها. لا تتوفّر هذه الميزة إلا على الأجهزة المتوافقة. لمزيد من المعلومات، يُرجى الاطّلاع على دليل أداة اختيار الصور.
"متجر الوسائط"
للتفاعل مع العنصر المجمّع لمتجر الوسائط، استخدِم عنصر
ContentResolver
يتم retrievingه
من سياق تطبيقك:
Kotlin
val projection = arrayOf(media-database-columns-to-retrieve) val selection = sql-where-clause-with-placeholder-variables val selectionArgs = values-of-placeholder-variables val sortOrder = sql-order-by-clause applicationContext.contentResolver.query( MediaStore.media-type.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder )?.use { cursor -> while (cursor.moveToNext()) { // Use an ID column from the projection to get // a URI representing the media item itself. } }
Java
String[] projection = new String[] { media-database-columns-to-retrieve }; String selection = sql-where-clause-with-placeholder-variables; String[] selectionArgs = new String[] { values-of-placeholder-variables }; String sortOrder = sql-order-by-clause; Cursor cursor = getApplicationContext().getContentResolver().query( MediaStore.media-type.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, sortOrder ); while (cursor.moveToNext()) { // Use an ID column from the projection to get // a URI representing the media item itself. }
يفحص النظام تلقائيًا وحدة تخزين خارجية ويضيف ملفات الوسائط إلى المجموعات المحدّدة جيدًا التالية:
- الصور، بما في ذلك الصور الفوتوغرافية ولقطات الشاشة، التي يتم تخزينها في الدليلين
DCIM/
وPictures/
يضيف النظام هذه الملفات إلى جدولMediaStore.Images
. - الفيديوهات، التي يتم تخزينها في الدلائل
DCIM/
وMovies/
وPictures/
يضيف النظام هذه الملفات إلى جدولMediaStore.Video
. - الملفات الصوتية، التي يتم تخزينها في الدلائل
Alarms/
وAudiobooks/
وMusic/
Notifications/
وPodcasts/
وRingtones/
بالإضافة إلى ذلك، يتعرّف النظام على قوائم التشغيل الصوتية في الدليلينMusic/
أوMovies/
بالإضافة إلى التسجيلات الصوتية في الدليلRecordings/
. يضيف النظام هذه الملفات إلى جدولMediaStore.Audio
. لا يتوفّر الدليلRecordings/
على الإصدار 11 من نظام التشغيل Android (المستوى 30 لواجهة برمجة التطبيقات) والإصدارات الأقدم. - الملفات التي تم تنزيلها، والتي يتم تخزينها في الدليل
Download/
على الأجهزة التي تعمل بالإصدار 10 من Android (المستوى 29 لواجهة برمجة التطبيقات) والإصدارات الأحدث، يتم تخزين هذه الملفات في جدولMediaStore.Downloads
. لا يتوفّر هذا الجدول على الإصدار 9 من نظام التشغيل Android (المستوى 28 لواجهة برمجة التطبيقات) والإصدارات الأقدم.
يتضمّن متجر الوسائط أيضًا مجموعة باسم
MediaStore.Files
. تعتمد محتوياته
على ما إذا كان تطبيقك يستخدم مساحة تخزين محدودة النطاق، وهي متاحة للتطبيقات التي تستهدف
Android 10 أو الإصدارات الأحدث.
- في حال تفعيل ميزة "مساحة التخزين المخصّصة"، لا تعرض المجموعة سوى الصور والفيديوهات
والملفات الصوتية التي أنشأها تطبيقك. لا يحتاج معظم المطوّرين إلى استخدام
MediaStore.Files
لعرض ملفات الوسائط من تطبيقات أخرى، ولكن إذا كان لديك متطلبات محدّدة لاستخدام هذا الإذن، يمكنك الإفصاح عن إذنREAD_EXTERNAL_STORAGE
. ومع ذلك، ننصحك باستخدامMediaStore
واجهات برمجة التطبيقات من أجل فتح الملفات التي لم ينشئها تطبيقك. - إذا لم يكن التخزين المحدود النطاق متاحًا أو لم يتم استخدامه، تعرض المجموعة كل أنواع ملفات الوسائط.
طلب الأذونات اللازمة
قبل تنفيذ عمليات على ملفات الوسائط، تأكَّد من أنّ تطبيقك قد أعلن عن الأذونات التي يحتاجها للوصول إلى هذه الملفات. ومع ذلك، يجب عدم تقديم بيان بأذونات لا يحتاج إليها تطبيقك أو لا يستخدمها.
أذونات مساحة التخزين
تعتمد الحاجة إلى أذونات للوصول إلى مساحة التخزين في تطبيقك على ما إذا كان يُدخل تعديلات على ملفاته الخاصة أو الملفات التي أنشأتها تطبيقات أخرى فقط.
الوصول إلى ملفات الوسائط الخاصة بك
على الأجهزة التي تعمل بنظام التشغيل Android 10 أو الإصدارات الأحدث، لا تحتاج إلى
أذونات متعلّقة بمساحة التخزين للوصول إلى ملفات الوسائط التي
يملكها تطبيقك وتعديلها، بما في ذلك الملفات في مجموعة MediaStore.Downloads
. إذا كنت تُطوّر تطبيق كاميرا، على سبيل المثال، لن تحتاج إلى
طلب أذونات متعلّقة بمساحة التخزين للوصول إلى الصور التي يلتقطها، لأنّ
تطبيقك يملك الصور التي تكتبها في "متجر الوسائط".
الوصول إلى ملفات الوسائط في التطبيقات الأخرى
للوصول إلى ملفات الوسائط التي تنشئها التطبيقات الأخرى، عليك الإفصاح عن الأذونات المناسبة المتعلّقة بمساحة التخزين، ويجب أن تكون الملفات مضمّنة في إحدى مجموعات الوسائط التالية:
ما دام الملف مرئيًا من طلبات البحث MediaStore.Images
أو
MediaStore.Video
أو MediaStore.Audio
، يمكن أيضًا عرضه باستخدام طلب البحث
MediaStore.Files
.
يوضِّح مقتطف الرمز التالي كيفية الإفصاح عن أذونات التخزين المناسبة:
<!-- Required only if your app needs to access images or photos that other apps created. --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- Required only if your app needs to access videos that other apps created. --> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- Required only if your app needs to access audio files that other apps created. --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />
أذونات إضافية مطلوبة للتطبيقات التي تعمل على الأجهزة القديمة
إذا كان تطبيقك يُستخدَم على جهاز يعمل بالإصدار 9 من نظام التشغيل Android أو إصدار أقدم، أو إذا كان
تطبيقك قد أوقف مؤقتًا ميزة التخزين المحدود النطاق، عليك
طلب
READ_EXTERNAL_STORAGE
إذن الوصول إلى أي ملف وسائط. إذا كنت تريد تعديل ملفات الوسائط، عليك
طلب
إذن
WRITE_EXTERNAL_STORAGE
هذا أيضًا.
يجب توفُّر إطار عمل الوصول إلى مساحة التخزين للوصول إلى عمليات تنزيل التطبيقات الأخرى.
إذا كان تطبيقك يريد الوصول إلى ملف ضمن مجموعة MediaStore.Downloads
لم ينشئه تطبيقك، يجب استخدام إطار عمل الوصول إلى مساحة التخزين. للتعرّف على
مزيد من المعلومات حول كيفية استخدام هذا الإطار، يُرجى الاطّلاع على مقالة الوصول إلى المستندات والملفات الأخرى من
مساحة التخزين المشتركة.
إذن الوصول إلى الموقع الجغرافي للوسائط
إذا كان تطبيقك يستهدف الإصدار 10 من نظام التشغيل Android (المستوى 29 لواجهة برمجة التطبيقات) أو إصدارًا أحدث وكان يحتاج
إلى استرداد البيانات الوصفية غير المحذوفة من تنسيق EXIF من الصور، عليك الإفصاح عن الإذن
ACCESS_MEDIA_LOCATION
في ملف بيان تطبيقك، ثم طلب هذا الإذن أثناء التشغيل.
التحقّق من توفّر تحديثات لمتجر الوسائط
للوصول إلى ملفات الوسائط بشكل أكثر موثوقية، خاصةً إذا كان تطبيقك يخزّن مؤشرات الموارد المنتظمة أو
البيانات من "متجر الوسائط"، تحقَّق مما إذا كان إصدار "متجر الوسائط" قد تغيّر مقارنةً بآخر مرة تمت فيها مزامنة بيانات الوسائط. لإجراء عملية التحقّق هذه من توافره
التحديثات، يُرجى الاتصال بالرقم getVersion()
.
الإصدار الذي يتم إرجاعه هو سلسلة فريدة تتغيّر كلما حدث تغيير كبير في "متجر الوسائط". إذا كان الإصدار المعروض مختلفًا عن آخر إصدار تمت مزامنته، أعِد فحص ذاكرة التخزين المؤقت للوسائط في تطبيقك وإعادة مزامنتها.
أكمِل عملية التحقّق هذه في وقت بدء تشغيل عملية التطبيق. ولا حاجة إلى التحقّق من الإصدار في كل مرة تطلب فيها معلومات من "متجر الوسائط".
لا تفترض أي تفاصيل تنفيذية بشأن رقم الإصدار.
طلب مجموعة وسائط
للعثور على الوسائط التي تستوفي مجموعة معيّنة من الشروط، مثل مدة تبلغ 5 دقائق أو أكثر، استخدِم عبارة اختيار مشابهة لعبارة SQL مثل العبارة التي تظهر في مقتطف الرمز التالي:
Kotlin
// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your // app didn't create. // Container for information about each video. data class Video(val uri: Uri, val name: String, val duration: Int, val size: Int ) val videoList = mutableListOf<Video>() val collection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaStore.Video.Media.getContentUri( MediaStore.VOLUME_EXTERNAL ) } else { MediaStore.Video.Media.EXTERNAL_CONTENT_URI } val projection = arrayOf( MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.DURATION, MediaStore.Video.Media.SIZE ) // Show only videos that are at least 5 minutes in duration. val selection = "${MediaStore.Video.Media.DURATION} >= ?" val selectionArgs = arrayOf( TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES).toString() ) // Display videos in alphabetical order based on their display name. val sortOrder = "${MediaStore.Video.Media.DISPLAY_NAME} ASC" val query = ContentResolver.query( collection, projection, selection, selectionArgs, sortOrder ) query?.use { cursor -> // Cache column indices. val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID) val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME) val durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION) val sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE) while (cursor.moveToNext()) { // Get values of columns for a given video. val id = cursor.getLong(idColumn) val name = cursor.getString(nameColumn) val duration = cursor.getInt(durationColumn) val size = cursor.getInt(sizeColumn) val contentUri: Uri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id ) // Stores column values and the contentUri in a local object // that represents the media file. videoList += Video(contentUri, name, duration, size) } }
Java
// Need the READ_EXTERNAL_STORAGE permission if accessing video files that your // app didn't create. // Container for information about each video. class Video { private final Uri uri; private final String name; private final int duration; private final int size; public Video(Uri uri, String name, int duration, int size) { this.uri = uri; this.name = name; this.duration = duration; this.size = size; } } List<Video> videoList = new ArrayList<Video>(); Uri collection; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL); } else { collection = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } String[] projection = new String[] { MediaStore.Video.Media._ID, MediaStore.Video.Media.DISPLAY_NAME, MediaStore.Video.Media.DURATION, MediaStore.Video.Media.SIZE }; String selection = MediaStore.Video.Media.DURATION + " >= ?"; String[] selectionArgs = new String[] { String.valueOf(TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES)); }; String sortOrder = MediaStore.Video.Media.DISPLAY_NAME + " ASC"; try (Cursor cursor = getApplicationContext().getContentResolver().query( collection, projection, selection, selectionArgs, sortOrder )) { // Cache column indices. int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID); int nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DISPLAY_NAME); int durationColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATION); int sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.SIZE); while (cursor.moveToNext()) { // Get values of columns for a given video. long id = cursor.getLong(idColumn); String name = cursor.getString(nameColumn); int duration = cursor.getInt(durationColumn); int size = cursor.getInt(sizeColumn); Uri contentUri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id); // Stores column values and the contentUri in a local object // that represents the media file. videoList.add(new Video(contentUri, name, duration, size)); } }
عند إجراء طلب بحث مماثل في تطبيقك، يجب مراعاة ما يلي:
- استخدِم الطريقة
query()
في سلسلة مهام عامل. - يمكنك تخزين مؤشرات الأعمدة مؤقتًا لكي لا تحتاج إلى استدعاء
getColumnIndexOrThrow()
في كل مرة تعالج فيها صفًا من نتيجة طلب البحث. - أضِف المعرّف إلى معرّف الموارد المتسلسل للمحتوى كما هو موضّح في هذا المثال.
- تتطلّب الأجهزة التي تعمل بنظام التشغيل Android 10 والإصدارات الأحدث أسماء عمود
محدّدة في
واجهة برمجة التطبيقات
MediaStore
. إذا كانت مكتبة تابعة داخل تطبيقك تتوقع اسم عمود غير محدّد في واجهة برمجة التطبيقات، مثل"MimeType"
، استخدِمCursorWrapper
لترجمة اسم العمود ديناميكيًا في عملية تطبيقك.
تحميل الصور المصغّرة للملفات
إذا كان تطبيقك يعرض عدة ملفات وسائط ويطلب من المستخدم اختيار أحد هذه الملفات، من الأفضل تحميل إصدارات المعاينة أو الملصقات المصغّرة للملف بدلاً من الملفات نفسها.
لتحميل الصورة المصغّرة لملف وسائط معيّن، استخدِم رمز برمجيًا يتضمن العنصر
loadThumbnail()
وأدخِل حجم الصورة المصغّرة التي تريد تحميلها، كما هو موضّح في المقتطف التالي من الرمز البرمجي:
Kotlin
// Load thumbnail of a specific media item. val thumbnail: Bitmap = applicationContext.contentResolver.loadThumbnail( content-uri, Size(640, 480), null)
Java
// Load thumbnail of a specific media item. Bitmap thumbnail = getApplicationContext().getContentResolver().loadThumbnail( content-uri, new Size(640, 480), null);
فتح ملف وسائط
يعتمد المنطق المحدّد الذي تستخدمه لفتح ملف وسائط على ما إذا كان محتوى الوسائط يتم تمثيله على أفضل وجه كملف وصفي أو بث ملف أو مسار ملف مباشر.
واصِف الملف
لفتح ملف وسائط باستخدام وصف ملف، استخدِم منطقًا مشابهًا لما هو موضّح في مقتطف الرمز البرمجي التالي:
Kotlin
// Open a specific media item using ParcelFileDescriptor. val resolver = applicationContext.contentResolver // "rw" for read-and-write. // "rwt" for truncating or overwriting existing file contents. val readOnlyMode = "r" resolver.openFileDescriptor(content-uri, readOnlyMode).use { pfd -> // Perform operations on "pfd". }
Java
// Open a specific media item using ParcelFileDescriptor. ContentResolver resolver = getApplicationContext() .getContentResolver(); // "rw" for read-and-write. // "rwt" for truncating or overwriting existing file contents. String readOnlyMode = "r"; try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(content-uri, readOnlyMode)) { // Perform operations on "pfd". } catch (IOException e) { e.printStackTrace(); }
الوصول المباشر إلى الملفات
لفتح ملف وسائط باستخدام بث ملف، استخدِم منطقًا مشابهًا لما هو موضّح في مقتطف الرمز البرمجي التالي:
Kotlin
// Open a specific media item using InputStream. val resolver = applicationContext.contentResolver resolver.openInputStream(content-uri).use { stream -> // Perform operations on "stream". }
Java
// Open a specific media item using InputStream. ContentResolver resolver = getApplicationContext() .getContentResolver(); try (InputStream stream = resolver.openInputStream(content-uri)) { // Perform operations on "stream". }
مسارات الملفات المباشرة
لمساعدة تطبيقك على العمل بسلاسة أكبر مع مكتبات الوسائط التابعة لجهات خارجية، يتيح لك الإصدار 11 من Android (المستوى 30 لواجهة برمجة التطبيقات) والإصدارات الأحدث استخدام واجهات برمجة تطبيقات أخرى غير واجهة برمجة التطبيقات
MediaStore
للوصول إلى
ملفات الوسائط من مساحة التخزين المشتركة. يمكنك بدلاً من ذلك الوصول إلى ملفات الوسائط مباشرةً
باستخدام أي من واجهات برمجة التطبيقات التالية:
- واجهة برمجة التطبيقات
File
- المكتبات الأصلية، مثل
fopen()
إذا لم يكن لديك أي أذونات متعلّقة بمساحة التخزين، يمكنك الوصول إلى الملفات في
الدليل الخاص بالتطبيق بالإضافة إلى ملفّات الوسائط
المنسوبة إلى تطبيقك باستخدام واجهة برمجة التطبيقات File
.
إذا حاول تطبيقك الوصول إلى ملف باستخدام واجهة برمجة التطبيقات File
ولم يكن لديه
الأذونات اللازمة، يحدث
FileNotFoundException
.
للوصول إلى الملفات الأخرى في مساحة التخزين المشتركة على جهاز يعمل بنظام التشغيل Android 10 (المستوى 29 من واجهة برمجة التطبيقات)، ننصحك بإيقاف ميزة التخزين المحدود مؤقتًا من خلال ضبط القيمة
requestLegacyExternalStorage
على true
في ملف بيان تطبيقك. للوصول إلى ملفات الوسائط باستخدام methodsملفّات أصلية على نظام التشغيل Android 10، يجب أيضًا طلب إذن
READ_EXTERNAL_STORAGE
.
الاعتبارات الواجب مراعاتها عند الوصول إلى محتوى الوسائط
عند الوصول إلى محتوى الوسائط، يُرجى مراعاة الاعتبارات الموضّحة في المقاطع التالية.
البيانات المؤقتة
إذا كان تطبيقك يخزِّن مؤقتًا عناوين URL أو بيانات من متجر الوسائط، عليك التحقّق بشكل دوري من توفّر تحديثات لمتجر الوسائط. يتيح لك هذا التحقّق إبقاء البيانات المخزّنة مؤقتًا على جانب التطبيق متزامنة مع بيانات مقدّم الخدمة على جانب النظام.
الأداء
عند تنفيذ عمليات قراءة متسلسلة لملفات الوسائط باستخدام مسارات الملفات المباشرة، يكون الأداء مماثلاً لأداء واجهة برمجة التطبيقات
MediaStore
.
ومع ذلك، عند إجراء عمليات قراءة وكتابة عشوائية لملفات الوسائط باستخدام مسارات الملفات المباشرة،
قد تكون العملية بطيئة بمقدار الضعف. في هذه الحالات، ننصح باستخدام MediaStore
API بدلاً من ذلك.
عمود DATA
عند الوصول إلى ملف وسائط حالي، يمكنك استخدام قيمة عمود
DATA
في
منطقك. يرجع ذلك إلى أنّ هذه القيمة لها مسار ملف صالح. ومع ذلك، لا ينبغي
افتراض أنّ الملف متاح دائمًا. يجب أن تكون مستعدًا للتعامل مع أي أخطاء في القراءة/الكتابة
المستندة إلى الملفات.
من ناحية أخرى، لإنشاء ملف وسائط أو تعديله، لا تستخدِم قيمة عمود
DATA
. بدلاً من ذلك، استخدِم قيم عمودَي
DISPLAY_NAME
و
RELATIVE_PATH
.
مجلدات التخزين
يمكن للتطبيقات التي تستهدف الإصدار 10 من نظام التشغيل Android أو الإصدارات الأحدث الوصول إلى الاسم الفريد الذي يخصّصه النظام لكل وحدة تخزين خارجية. يساعدك نظام التسمية هذا في تنظيم المحتوى وفهرسته بكفاءة، كما يمنحك إمكانية التحكّم في مكان تخزين ملفات الوسائط الجديدة.
من المفيد جدًا مراعاة المجلدات التالية:
- يعرض وحدة التخزين
VOLUME_EXTERNAL
جميع مساحات التخزين المشتركة على الجهاز. يمكنك قراءة محتويات هذا المجلد الاصطناعي، ولكن لا يمكنك تعديلها. - يمثّل رمز المساحة التخزينية
VOLUME_EXTERNAL_PRIMARY
مساحة التخزين المشتركة الأساسية على الجهاز. يمكنك قراءة محتويات هذا المجلد وتعديلها.
يمكنك الاطّلاع على مستويات الصوت الأخرى من خلال الاتصال بالرقم التالي:
MediaStore.getExternalVolumeNames()
:
Kotlin
val volumeNames: Set<String> = MediaStore.getExternalVolumeNames(context) val firstVolumeName = volumeNames.iterator().next()
Java
Set<String> volumeNames = MediaStore.getExternalVolumeNames(context); String firstVolumeName = volumeNames.iterator().next();
الموقع الجغرافي الذي تم فيه التقاط الوسائط
تحتوي بعض الصور والفيديوهات على معلومات الموقع الجغرافي في البيانات الوصفية التي تعرض المكان الذي تم فيه التقاط صورة أو تسجيل فيديو.
تعتمد طريقة الوصول إلى معلومات الموقع الجغرافي هذه في تطبيقك على ما إذا كان عليك الوصول إلى معلومات الموقع الجغرافي لصورة أو فيديو.
صور فوتوغرافية
إذا كان تطبيقك يستخدم مساحة تخزين محددة النطاق، يخفي ال system معلومات الموقع الجغرافي تلقائيًا. للوصول إلى هذه المعلومات، عليك إكمال الخطوات التالية:
- اطلب إذن
ACCESS_MEDIA_LOCATION
في ملف بيان تطبيقك. من عنصر
MediaStore
، يمكنك الحصول على البايتات الدقيقة للصورة من خلال استدعاءsetRequireOriginal()
وإدخال معرّف الموارد المنتظم للصورة، كما هو موضّح في مقتطف الرمز التالي:Kotlin
val photoUri: Uri = Uri.withAppendedPath( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getString(idColumnIndex) ) // Get location data using the Exifinterface library. // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted. photoUri = MediaStore.setRequireOriginal(photoUri) contentResolver.openInputStream(photoUri)?.use { stream -> ExifInterface(stream).run { // If lat/long is null, fall back to the coordinates (0, 0). val latLong = latLong ?: doubleArrayOf(0.0, 0.0) } }
Java
Uri photoUri = Uri.withAppendedPath( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getString(idColumnIndex)); final double[] latLong; // Get location data using the Exifinterface library. // Exception occurs if ACCESS_MEDIA_LOCATION permission isn't granted. photoUri = MediaStore.setRequireOriginal(photoUri); InputStream stream = getContentResolver().openInputStream(photoUri); if (stream != null) { ExifInterface exifInterface = new ExifInterface(stream); double[] returnedLatLong = exifInterface.getLatLong(); // If lat/long is null, fall back to the coordinates (0, 0). latLong = returnedLatLong != null ? returnedLatLong : new double[2]; // Don't reuse the stream associated with // the instance of "ExifInterface". stream.close(); } else { // Failed to load the stream, so return the coordinates (0, 0). latLong = new double[2]; }
الفيديوهات
للوصول إلى معلومات الموقع الجغرافي ضمن البيانات الوصفية للفيديو، استخدِم فئة
MediaMetadataRetriever
، كما هو موضّح في مقتطف الرمز التالي. لا يحتاج تطبيقك إلى طلب
أي أذونات إضافية لاستخدام هذه الفئة.
Kotlin
val retriever = MediaMetadataRetriever() val context = applicationContext // Find the videos that are stored on a device by querying the video collection. val query = ContentResolver.query( collection, projection, selection, selectionArgs, sortOrder ) query?.use { cursor -> val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID) while (cursor.moveToNext()) { val id = cursor.getLong(idColumn) val videoUri: Uri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id ) extractVideoLocationInfo(videoUri) } } private fun extractVideoLocationInfo(videoUri: Uri) { try { retriever.setDataSource(context, videoUri) } catch (e: RuntimeException) { Log.e(APP_TAG, "Cannot retrieve video file", e) } // Metadata uses a standardized format. val locationMetadata: String? = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION) }
Java
MediaMetadataRetriever retriever = new MediaMetadataRetriever(); Context context = getApplicationContext(); // Find the videos that are stored on a device by querying the video collection. try (Cursor cursor = context.getContentResolver().query( collection, projection, selection, selectionArgs, sortOrder )) { int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID); while (cursor.moveToNext()) { long id = cursor.getLong(idColumn); Uri videoUri = ContentUris.withAppendedId( MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id); extractVideoLocationInfo(videoUri); } } private void extractVideoLocationInfo(Uri videoUri) { try { retriever.setDataSource(context, videoUri); } catch (RuntimeException e) { Log.e(APP_TAG, "Cannot retrieve video file", e); } // Metadata uses a standardized format. String locationMetadata = retriever.extractMetadata( MediaMetadataRetriever.METADATA_KEY_LOCATION); }
المشاركة
تسمح بعض التطبيقات للمستخدمين بمشاركة ملفات الوسائط مع بعضهم. على سبيل المثال، تتيح تطبيقات الوسائط الاجتماعية للمستخدمين مشاركة الصور والفيديوهات مع الأصدقاء.
لمشاركة ملفات الوسائط، استخدِم معرّف الموارد المنتظم content://
، كما هو مقترَح في دليل
إنشاء مزوّد محتوى.
تحديد مصدر ملفات الوسائط من خلال التطبيق
عند تفعيل ميزة مساحة التخزين ذات النطاق المحدّد لتطبيق يستهدف الإصدار 10 من نظام التشغيل Android أو الإصدارات الأحدث، يربط النظام التطبيق بكل ملف وسائط، ما يحدّد الملفات التي يمكن لتطبيقك الوصول إليها عندما لا يطلب أي أذونات تخزين. يمكن أن يُنسَب كل ملف إلى تطبيق واحد فقط. لذلك، إذا أنشأ تطبيقك ملف وسائط تم تخزينه في مجموعة الوسائط التي تتضمّن الصور أو الفيديوهات أو الملفات الصوتية، يمكن لتطبيقك الوصول إلى الملف.
في حال ألغى المستخدم تثبيت تطبيقك وأعاد تثبيته، يجب أن تطلب منه
READ_EXTERNAL_STORAGE
الوصول إلى الملفات التي أنشأها تطبيقك في الأصل. إنّ طلب الإذن هذا
مطلوب لأنّ النظام يعتبر أنّ الملف ينتمي إلى
الإصدار المثبَّت سابقًا من التطبيق، وليس إلى الإصدار المثبَّت حديثًا.
إضافة عنصر
لإضافة عنصر وسائط إلى مجموعة حالية، استخدِم رمزًا مشابهًا للرمز التالي:
تُستخدَم هذه المقتطفات من الرموز البرمجية للوصول إلى مستوى VOLUME_EXTERNAL_PRIMARY
الصوت
على الأجهزة التي تعمل بنظام التشغيل Android 10 أو إصدار أحدث. يرجع ذلك إلى أنّه على هذه الأجهزة،
لا يمكنك تعديل محتوى أيّ مجلد إلا إذا كان المجلد الأساسي، كما هو описан في قسم أحجام التخزين.
Kotlin
// Add a specific media item. val resolver = applicationContext.contentResolver // Find all audio files on the primary external storage device. val audioCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaStore.Audio.Media.getContentUri( MediaStore.VOLUME_EXTERNAL_PRIMARY ) } else { MediaStore.Audio.Media.EXTERNAL_CONTENT_URI } // Publish a new song. val newSongDetails = ContentValues().apply { put(MediaStore.Audio.Media.DISPLAY_NAME, "My Song.mp3") } // Keep a handle to the new song's URI in case you need to modify it // later. val myFavoriteSongUri = resolver .insert(audioCollection, newSongDetails)
Java
// Add a specific media item. ContentResolver resolver = getApplicationContext() .getContentResolver(); // Find all audio files on the primary external storage device. Uri audioCollection; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { audioCollection = MediaStore.Audio.Media .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); } else { audioCollection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } // Publish a new song. ContentValues newSongDetails = new ContentValues(); newSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME, "My Song.mp3"); // Keep a handle to the new song's URI in case you need to modify it // later. Uri myFavoriteSongUri = resolver .insert(audioCollection, newSongDetails);
تبديل حالة "في انتظار المراجعة" لملفات الوسائط
إذا كان تطبيقك يُجري عمليات قد تستغرق وقتًا طويلاً، مثل الكتابة فيملفّات الوسائط، من المفيد الحصول على إذن وصول حصري إلى الملف أثناء معالجته. على الأجهزة التي تعمل بنظام التشغيل Android 10 أو إصدار أحدث، يمكن لتطبيقك
الحصول على هذا الإذن الحصري من خلال ضبط قيمة العلامة
IS_PENDING
على 1. لا يمكن إلا لتطبيقك عرض الملف إلى أن يغيّر قيمة
IS_PENDING
مرة أخرى إلى 0.
يستند مقتطف الرمز البرمجي التالي إلى مقتطف الرمز البرمجي السابق. يوضّح هذا الاقتباس
كيفية استخدام العلامة IS_PENDING
عند تخزين أغنية طويلة في
المجلد المقابل لمجموعة MediaStore.Audio
:
Kotlin
// Add a media item that other apps don't see until the item is // fully written to the media store. val resolver = applicationContext.contentResolver // Find all audio files on the primary external storage device. val audioCollection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { MediaStore.Audio.Media.getContentUri( MediaStore.VOLUME_EXTERNAL_PRIMARY ) } else { MediaStore.Audio.Media.EXTERNAL_CONTENT_URI } val songDetails = ContentValues().apply { put(MediaStore.Audio.Media.DISPLAY_NAME, "My Workout Playlist.mp3") put(MediaStore.Audio.Media.IS_PENDING, 1) } val songContentUri = resolver.insert(audioCollection, songDetails) // "w" for write. resolver.openFileDescriptor(songContentUri, "w", null).use { pfd -> // Write data into the pending audio file. } // Now that you're finished, release the "pending" status and let other apps // play the audio track. songDetails.clear() songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0) resolver.update(songContentUri, songDetails, null, null)
Java
// Add a media item that other apps don't see until the item is // fully written to the media store. ContentResolver resolver = getApplicationContext() .getContentResolver(); // Find all audio files on the primary external storage device. Uri audioCollection; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { audioCollection = MediaStore.Audio.Media .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY); } else { audioCollection = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } ContentValues songDetails = new ContentValues(); songDetails.put(MediaStore.Audio.Media.DISPLAY_NAME, "My Workout Playlist.mp3"); songDetails.put(MediaStore.Audio.Media.IS_PENDING, 1); Uri songContentUri = resolver .insert(audioCollection, songDetails); // "w" for write. try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(songContentUri, "w", null)) { // Write data into the pending audio file. } // Now that you're finished, release the "pending" status and let other apps // play the audio track. songDetails.clear(); songDetails.put(MediaStore.Audio.Media.IS_PENDING, 0); resolver.update(songContentUri, songDetails, null, null);
تقديم تلميح لموقع الملف
عندما يخزِّن تطبيقك الوسائط على جهاز يعمل بنظام التشغيل Android 10، يتم تلقائيًا
تنظيم الوسائط استنادًا إلى نوعها. على سبيل المثال، يتم تلقائيًا وضع
ملفات الصور الجديدة في الدليل
Environment.DIRECTORY_PICTURES
الذي يتوافق مع مجموعة
MediaStore.Images
.
إذا كان تطبيقك على دراية بموقع معيّن يمكن تخزين الملفات فيه، مثل
ألبوم صور باسم Pictures/MyVacationPictures
، يمكنك ضبط
MediaColumns.RELATIVE_PATH
لإعلام النظام بمكان تخزين الملفات التي تم كتابتها حديثًا.
تعديل عنصر
لتعديل ملف وسائط يملكه تطبيقك، استخدِم رمزًا مشابهًا لما يلي:
Kotlin
// Updates an existing media item. val mediaId = // MediaStore.Audio.Media._ID of item to update. val resolver = applicationContext.contentResolver // When performing a single item update, prefer using the ID. val selection = "${MediaStore.Audio.Media._ID} = ?" // By using selection + args you protect against improper escaping of // values. val selectionArgs = arrayOf(mediaId.toString()) // Update an existing song. val updatedSongDetails = ContentValues().apply { put(MediaStore.Audio.Media.DISPLAY_NAME, "My Favorite Song.mp3") } // Use the individual song's URI to represent the collection that's // updated. val numSongsUpdated = resolver.update( myFavoriteSongUri, updatedSongDetails, selection, selectionArgs)
Java
// Updates an existing media item. long mediaId = // MediaStore.Audio.Media._ID of item to update. ContentResolver resolver = getApplicationContext() .getContentResolver(); // When performing a single item update, prefer using the ID. String selection = MediaStore.Audio.Media._ID + " = ?"; // By using selection + args you protect against improper escaping of // values. Here, "song" is an in-memory object that caches the song's // information. String[] selectionArgs = new String[] { getId().toString() }; // Update an existing song. ContentValues updatedSongDetails = new ContentValues(); updatedSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME, "My Favorite Song.mp3"); // Use the individual song's URI to represent the collection that's // updated. int numSongsUpdated = resolver.update( myFavoriteSongUri, updatedSongDetails, selection, selectionArgs);
إذا لم يكن التخزين المحدود النطاق متاحًا أو غير مفعَّل، تعمل العملية الموضّحة في مقتطف الرمز البرمجي السابق أيضًا على الملفات التي لا يملكها تطبيقك.
تعديل الرمز الأصلي
إذا كنت بحاجة إلى كتابة ملفات وسائط باستخدام مكتبات برمجية أصلية، عليك تمرير وصف الملف المرتبط بالملف من الرمز البرمجي المستنِد إلى Java أو Kotlin إلى الرمز البرمجي الأصلي.
يوضّح مقتطف الرمز التالي كيفية تمرير وصف ملف عنصر الوسائط إلى الرمز الأصلي لتطبيقك:
Kotlin
val contentUri: Uri = ContentUris.withAppendedId( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, cursor.getLong(BaseColumns._ID)) val fileOpenMode = "r" val parcelFd = resolver.openFileDescriptor(contentUri, fileOpenMode) val fd = parcelFd?.detachFd() // Pass the integer value "fd" into your native code. Remember to call // close(2) on the file descriptor when you're done using it.
Java
Uri contentUri = ContentUris.withAppendedId( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, cursor.getLong(Integer.parseInt(BaseColumns._ID))); String fileOpenMode = "r"; ParcelFileDescriptor parcelFd = resolver.openFileDescriptor(contentUri, fileOpenMode); if (parcelFd != null) { int fd = parcelFd.detachFd(); // Pass the integer value "fd" into your native code. Remember to call // close(2) on the file descriptor when you're done using it. }
تعديل ملفات الوسائط في التطبيقات الأخرى
إذا كان تطبيقك يستخدم مساحة تخزين محددة النطاق، لن يتمكّن عادةً من تعديل ملف وسائط ساهم تطبيق آخر في تخزينه في ملف التخزين.
يمكنك الحصول على موافقة المستخدم لتعديل الملف من خلال رصدRecoverableSecurityException
الخطأ الذي تطلقه المنصة. يمكنك بعد ذلك أن تطلب من المستخدم منح تطبيقك إذن
الكتابة في هذا العنصر المحدّد، كما هو موضّح في مقتطف الرمز التالي:
Kotlin
// Apply a grayscale filter to the image at the given content URI. try { // "w" for write. contentResolver.openFileDescriptor(image-content-uri, "w")?.use { setGrayscaleFilter(it) } } catch (securityException: SecurityException) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { val recoverableSecurityException = securityException as? RecoverableSecurityException ?: throw RuntimeException(securityException.message, securityException) val intentSender = recoverableSecurityException.userAction.actionIntent.intentSender intentSender?.let { startIntentSenderForResult(intentSender, image-request-code, null, 0, 0, 0, null) } } else { throw RuntimeException(securityException.message, securityException) } }
Java
try { // "w" for write. ParcelFileDescriptor imageFd = getContentResolver() .openFileDescriptor(image-content-uri, "w"); setGrayscaleFilter(imageFd); } catch (SecurityException securityException) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { RecoverableSecurityException recoverableSecurityException; if (securityException instanceof RecoverableSecurityException) { recoverableSecurityException = (RecoverableSecurityException)securityException; } else { throw new RuntimeException( securityException.getMessage(), securityException); } IntentSender intentSender =recoverableSecurityException.getUserAction() .getActionIntent().getIntentSender(); startIntentSenderForResult(intentSender, image-request-code, null, 0, 0, 0, null); } else { throw new RuntimeException( securityException.getMessage(), securityException); } }
أكمِل هذه العملية كلّما احتاج تطبيقك إلى تعديل ملف وسائط لم ينشئه.
بدلاً من ذلك، إذا كان تطبيقك يعمل بنظام التشغيل Android 11 أو إصدار أحدث، يمكنك
السماح للمستخدمين بمنح تطبيقك إذن الوصول للكتابة إلى مجموعة من ملفات الوسائط. استخدِم الطريقة
createWriteRequest()
، كما هو موضّح في القسم حول كيفية إدارة مجموعاتملفّات الوسائط.
إذا كان تطبيقك يتضمّن حالة استخدام أخرى لا تندرج ضمن نطاق "مساحة التخزين المخصّصة"، يمكنك تقديم طلب لإضافة ميزة وإيقاف "مساحة التخزين المخصّصة" مؤقتًا.
إزالة عنصر
لإزالة عنصر لم يعُد تطبيقك بحاجة إليه من متجر الوسائط، استخدِم منطقًا مشابهًا لما هو موضَّح في المقتطف التالي من الرمز البرمجي:
Kotlin
// Remove a specific media item. val resolver = applicationContext.contentResolver // URI of the image to remove. val imageUri = "..." // WHERE clause. val selection = "..." val selectionArgs = "..." // Perform the actual removal. val numImagesRemoved = resolver.delete( imageUri, selection, selectionArgs)
Java
// Remove a specific media item. ContentResolver resolver = getApplicationContext() getContentResolver(); // URI of the image to remove. Uri imageUri = "..."; // WHERE clause. String selection = "..."; String[] selectionArgs = "..."; // Perform the actual removal. int numImagesRemoved = resolver.delete( imageUri, selection, selectionArgs);
إذا لم يكن التخزين المحدود النطاق متاحًا أو غير مفعَّل، يمكنك استخدام مقتطف الرمز البرمجي السابق
لإزالة الملفات التي تملكها تطبيقات أخرى. في حال تفعيل ميزة "مساحة التخزين المستندة إلى النطاق"،
عليك الحصول على RecoverableSecurityException
لكل ملف يريده
تطبيقك إزالته، كما هو موضّح في القسم حول تعديل ملفّات الوسائط.
إذا كان تطبيقك يعمل بنظام التشغيل Android 11 أو إصدار أحدث، يمكنك السماح للمستخدمين
باختيار مجموعة من ملفات الوسائط لإزالتها. استخدِم createTrashRequest()
الطريقة أو
createDeleteRequest()
الطريقة، كما هو موضّح في القسم حول كيفية إدارة مجموعاتملفّات الوسائط.
إذا كان تطبيقك يتضمّن حالة استخدام أخرى لا تندرج ضمن نطاق "مساحة التخزين المخصّصة"، يمكنك تقديم طلب لإضافة ميزة وإيقاف "مساحة التخزين المخصّصة" مؤقتًا.
رصد التعديلات على ملفات الوسائط
قد يحتاج تطبيقك إلى تحديد مساحات التخزين التي تحتوي على ملفات وسائط أضافتها التطبيقات أو عدّلتها، مقارنةً بنقطة زمنية سابقة. لرصد هذه التغييرات
بأكبر قدر من الموثوقية، عليك تمرير مساحة التخزين التي تهمّك إلى
getGeneration()
.
طالما أنّ إصدار متجر الوسائط لم يتغيّر، تزداد القيمة المعروضة من
هذه الطريقة بشكلٍ منتظم بمرور الوقت.
وعلى وجه الخصوص، يكون العمود getGeneration()
أكثر دقة من الأعمدة التي تتضمّن تواريخ،
مثل
DATE_ADDED
وDATE_MODIFIED
.
ويعود السبب في ذلك إلى أنّ قيم عمود الوسائط هذه يمكن أن تتغيّر عندما يستدعي أحد التطبيقات setLastModified()
أو عندما يغيّر المستخدم ساعة النظام.
إدارة مجموعات ملفات الوسائط
على نظام التشغيل Android 11 والإصدارات الأحدث، يمكنك أن تطلب من المستخدم اختيار مجموعة من ملفات الوسائط، ثم تعديل ملفات الوسائط هذه في عملية واحدة. توفّر هذه الخطوات اتساقًا أفضل على جميع الأجهزة، كما تسهّل على المستخدمين إدارة مجموعات الوسائط الخاصة بهم.
تشمل الطرق التي توفّر وظيفة "التحديث المجمّع" ما يلي:
createWriteRequest()
- اطلب من المستخدم منح تطبيقك إذن الوصول للكتابة إلى المجموعة المحدّدة من ملفات الوسائط.
createFavoriteRequest()
- يُرجى طلب من المستخدم وضع علامة على ملفات الوسائط المحدّدة على أنّها من الوسائط "المفضّلة" على الجهاز. يمكن لأي تطبيق لديه إذن بالوصول إلى هذا الملف للقراءة معرفة أنّ المستخدم وضع علامة "مفضّل" على الملف.
createTrashRequest()
اطلب من المستخدم وضع ملفات الوسائط المحدّدة في المهملات على الجهاز. يتم حذف العناصر من المهملات نهائيًا بعد فترة زمنية محدّدة من النظام.
createDeleteRequest()
اطلب من المستخدم حذف ملفات الوسائط المحدّدة نهائيًا على الفور، بدون وضعها في المهملات مسبقًا.
بعد استدعاء أيّ من هذه الطرق، ينشئ النظام عنصرًا
PendingIntent
. بعد أن يشغِّل تطبيقك
هذا الإجراء، يظهر للمستخدمين مربّع حوار يطلب منهم الموافقة على أن يشغِّل تطبيقك
تحديث ملفات الوسائط المحدّدة أو حذفها.
على سبيل المثال، إليك كيفية صياغة دعوة إلى createWriteRequest()
:
Kotlin
val urisToModify = /* A collection of content URIs to modify. */ val editPendingIntent = MediaStore.createWriteRequest(contentResolver, urisToModify) // Launch a system prompt requesting user permission for the operation. startIntentSenderForResult(editPendingIntent.intentSender, EDIT_REQUEST_CODE, null, 0, 0, 0)
Java
List<Uri> urisToModify = /* A collection of content URIs to modify. */ PendingIntent editPendingIntent = MediaStore.createWriteRequest(contentResolver, urisToModify); // Launch a system prompt requesting user permission for the operation. startIntentSenderForResult(editPendingIntent.getIntentSender(), EDIT_REQUEST_CODE, null, 0, 0, 0);
تقييم ردّ المستخدم إذا قدّم المستخدم موافقته، يمكنك مواصلة العملية المتعلّقة بالوسائط. بخلاف ذلك، اشرح للمستخدم سبب احتياج تطبيقك إلى الإذن:
Kotlin
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { ... when (requestCode) { EDIT_REQUEST_CODE -> if (resultCode == Activity.RESULT_OK) { /* Edit request granted; proceed. */ } else { /* Edit request not granted; explain to the user. */ } } }
Java
@Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { ... if (requestCode == EDIT_REQUEST_CODE) { if (resultCode == Activity.RESULT_OK) { /* Edit request granted; proceed. */ } else { /* Edit request not granted; explain to the user. */ } } }
يمكنك استخدام هذا النمط العام نفسه مع
createFavoriteRequest()
،
createTrashRequest()
،
و
createDeleteRequest()
.
إذن إدارة الوسائط
قد يثق المستخدمون بتطبيق معيّن لإدارة الوسائط، مثل إجراء تعديلات متكرّرة على ملفات الوسائط. إذا كان تطبيقك يستهدف الإصدار Android 11 أو إصدارًا أحدث ولم يكن تطبيق معرض الصور التلقائي على الجهاز، يجب عرض مربّع حوار تأكيد للمستخدم في كل مرة يحاول فيها تطبيقك تعديل ملف أو حذفه.
إذا كان تطبيقك يستهدف الإصدار 12 من نظام التشغيل Android (المستوى 31 لواجهة برمجة التطبيقات) أو إصدارًا أحدث، يمكنك أن تطلب من المستخدمين منح تطبيقك الإذن الخاص لإدارة الوسائط. يتيح هذا الإذن لتطبيقك تنفيذ كلّ ممّا يلي بدون الحاجة إلى طلب المستخدم تنفيذ كلّ عملية ملف:
- تعديل الملفات باستخدام
createWriteRequest()
- نقل الملفات إلى المهملات وخارجها باستخدام رمز
createTrashRequest()
- حذف الملفات باستخدام رمز
createDeleteRequest()
لإجراء ذلك، يُرجى إكمال الخطوات التالية:
يجب الإفصاح عن الإذن
MANAGE_MEDIA
والإذنREAD_EXTERNAL_STORAGE
في ملف بيان تطبيقك.للاتصال بالرقم
createWriteRequest()
بدون عرض مربع حوار confirmation ، يجب أيضًا الإفصاح عن إذنACCESS_MEDIA_LOCATION
.في تطبيقك، اعرض واجهة مستخدم للمستخدم لتوضيح سبب منح إذن الوصول إلى إدارة الوسائط لتطبيقك.
استخدِم الإجراء
ACTION_REQUEST_MANAGE_MEDIA
intent. يؤدي ذلك إلى توجيه المستخدمين إلى شاشة تطبيقات إدارة الوسائط في إعدادات النظام. من هنا، يمكن للمستخدمين منح إذن الوصول إلى التطبيق الخاص.
حالات الاستخدام التي تتطلّب بديلاً لمتجر الوسائط
إذا كان تطبيقك يؤدي أحد الأدوار التالية بشكل أساسي، ننصحك باستخدام بديل لواجهات برمجة تطبيقات MediaStore
.
التعامل مع أنواع أخرى من الملفات
إذا كان تطبيقك يعمل مع المستندات والملفات التي لا تحتوي حصريًا على محتوى إعلام، مثل الملفات التي تستخدم امتداد ملف EPUB أو PDF، استخدِمACTION_OPEN_DOCUMENT
إجراء النية كما هو موضّح في دليل تخزين
المستندات وغيرها من
الملفات والوصول إليها.
مشاركة الملفات في التطبيقات المصاحبة
في الحالات التي تقدّم فيها مجموعة من التطبيقات المصاحبة، مثل تطبيق مراسلة و
تطبيق ملف شخصي، يمكنك إعداد ميزة مشاركة الملفات
باستخدام معرّفات الموارد المنتظمة content://
. ننصحك أيضًا باتّباع سير العمل هذا كأحد أفضل الممارسات المتعلّقة بال security.
مصادر إضافية
لمزيد من المعلومات حول كيفية تخزين الوسائط والوصول إليها، يُرجى الرجوع إلى المراجع التالية:
نماذج
- MediaStore، متاح على GitHub