अगर आप ऐसा ऐप्लिकेशन डेवलप कर रहे हैं जो फ़ाइलों के लिए स्टोरेज सेवाएं (जैसे कि क्लाउड सेव सेवा) से जुड़ा होता है, तो आप स्टोरेज ऐक्सेस फ़्रेमवर्क (SAF) का अनुरोध करने के लिए, अपनी पसंद के मुताबिक दस्तावेज़ की सेवा देने वाली कंपनी के बारे में जानकारी लिखें. इस पेज पर, पसंद के मुताबिक दस्तावेज़ की सेवा देने वाली कंपनी बनाने का तरीका बताया गया है.
स्टोरेज ऐक्सेस फ़्रेमवर्क के काम करने के तरीके के बारे में ज़्यादा जानने के लिए, स्टोरेज को ऐक्सेस करने के फ़्रेमवर्क के बारे में खास जानकारी.
मेनिफ़ेस्ट
दस्तावेज़ की सेवा देने वाली कस्टम कंपनी लागू करने के लिए, अपने ऐप्लिकेशन के मेनिफ़ेस्ट:
- एपीआई लेवल 19 या उसके बाद के लेवल का टारगेट.
<provider>
एलिमेंट, जो आपकी कस्टम स्टोरेज के बारे में बताता है कंपनी.-
एट्रिब्यूट
android:name
को आपकेDocumentsProvider
सब-क्लास, जो कि इसकी क्लास का नाम है, इसमें पैकेज का नाम भी शामिल है:com.example.android.storageprovider.MyCloudProvider
. -
एट्रिब्यूट
android:authority
एट्रिब्यूट, आपके पैकेज का नाम क्या है (इस उदाहरण में,com.example.android.storageprovider
) साथ ही, कॉन्टेंट देने वाले का टाइप और (documents
). android:exported
एट्रिब्यूट को"true"
पर सेट किया गया. आपको सेवा देने वाली कंपनी को एक्सपोर्ट करना होगा, ताकि दूसरे ऐप्लिकेशन इसे देख सकें.- एट्रिब्यूट
android:grantUriPermissions
को इस पर सेट किया गया"true"
. इस सेटिंग की मदद से, सिस्टम दूसरे ऐप्लिकेशन को ऐक्सेस दे सकता है के कॉन्टेंट को हटा दिया जाता है. यह जानने के लिए कि ये अन्य ऐप्लिकेशन कैसे अपने प्रोवाइडर के कॉन्टेंट पर अपना ऐक्सेस बनाए रखें, तो परसिस्टेंट अनुमतियां हैं. MANAGE_DOCUMENTS
की अनुमति. डिफ़ॉल्ट रूप से, सेवा देने वाली कोई कंपनी उपलब्ध होती है सभी के लिए उपलब्ध कराया जा सकता है. इस अनुमति को जोड़ने पर, आपको सेवा देने वाली कंपनी को सिस्टम का ऐक्सेस नहीं दिया जा सकता. सुरक्षा के लिहाज़ से, यह पाबंदी ज़रूरी है.- ऐसा इंटेंट फ़िल्टर जिसमें
android.content.action.DOCUMENTS_PROVIDER
कार्रवाई, ताकि आपको सेवा देने वाली कंपनी चुनने पर, जब सिस्टम सेवा देने वाली कंपनियों की खोज करता है, तो यह पिकर में दिखता है.
यहां सैंपल मेनिफ़ेस्ट के कुछ हिस्से दिए गए हैं. इनमें, सेवा देने वाली कंपनी का नाम भी शामिल है:
<manifest... > ... <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19" /> .... <provider android:name="com.example.android.storageprovider.MyCloudProvider" android:authorities="com.example.android.storageprovider.documents" android:grantUriPermissions="true" android:exported="true" android:permission="android.permission.MANAGE_DOCUMENTS"> <intent-filter> <action android:name="android.content.action.DOCUMENTS_PROVIDER" /> </intent-filter> </provider> </application> </manifest>
Android 4.3 और उससे पहले के वर्शन चलाने वाले डिवाइस
कॉन्टेंट बनाने
ACTION_OPEN_DOCUMENT
इंटेंट सिर्फ़ उपलब्ध है
Android 4.4 और उसके बाद वाले वर्शन चला रहे डिवाइसों पर.
अगर आप चाहते हैं कि आपका ऐप्लिकेशन ACTION_GET_CONTENT
का समर्थन करे
Android 4.3 और उससे पहले के वर्शन चलाने वाले डिवाइसों को समायोजित करने के लिए, आपको
इसके बाद, ACTION_GET_CONTENT
इंटेंट फ़िल्टर को बंद कर दें
Android 4.4 या उसके बाद के वर्शन चला रहे डिवाइस के लिए आपका मेनिफ़ेस्ट. ऐप्लिकेशन
दस्तावेज़ देने वाली कंपनी और ACTION_GET_CONTENT
के लिए,
एक-दूसरे के साथ अलग. अगर इन दोनों सुविधाओं को एक साथ इस्तेमाल किया जाता है, तो आपका ऐप्लिकेशन
सिस्टम पिकर यूज़र इंटरफ़ेस (यूआई) में दो बार दिखता है. यह ऐक्सेस करने के दो अलग-अलग तरीके उपलब्ध कराता है
को ऐक्सेस कर सकें. इससे उपयोगकर्ता भ्रमित हो सकते हैं.
को अक्षम करने का
डिवाइसों के लिए ACTION_GET_CONTENT
इंटेंट फ़िल्टर
जो Android 4.4 या इसके बाद वाले वर्शन पर चल रहे हों:
res/values/
से जुड़ी अपनीbool.xml
संसाधन फ़ाइल में, यह जोड़ें यह लाइन:<bool name="atMostJellyBeanMR2">true</bool>
res/values-v19/
से जुड़ी अपनीbool.xml
संसाधन फ़ाइल में, यह जोड़ें यह लाइन:<bool name="atMostJellyBeanMR2">false</bool>
- जोड़ें
गतिविधि
ACTION_GET_CONTENT
इंटेंट को बंद करने के लिए अन्य डोमेन नेम 4.4 (एपीआई लेवल 19) और उसके बाद वाले वर्शन के लिए फ़िल्टर का इस्तेमाल करें. उदाहरण के लिए:<!-- This activity alias is added so that GET_CONTENT intent-filter can be disabled for builds on API level 19 and higher. --> <activity-alias android:name="com.android.example.app.MyPicker" android:targetActivity="com.android.example.app.MyActivity" ... android:enabled="@bool/atMostJellyBeanMR2"> <intent-filter> <action android:name="android.intent.action.GET_CONTENT" /> <category android:name="android.intent.category.OPENABLE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> <data android:mimeType="video/*" /> </intent-filter> </activity-alias>
समझौते
आम तौर पर, जब अपनी पसंद के मुताबिक कॉन्टेंट देने वाला संगठन लिखा जाता है, तो आपका एक काम यह होता है
कॉन्ट्रैक्ट क्लास का इस्तेमाल करना, जैसा कि
कॉन्टेंट देने वाले डेवलपर के लिए गाइड. कॉन्ट्रैक्ट क्लास, public final
क्लास होती है
जिसमें यूआरआई, कॉलम के नाम, एमआईएमई टाइप, और
सेवा देने वाली कंपनी से जुड़ा कोई दूसरा मेटाडेटा. एसएएफ़
आपको ये कॉन्ट्रैक्ट क्लास उपलब्ध कराता है. इसलिए, आपको
मालिकाना हक:
उदाहरण के लिए, यहां कुछ कॉलम दिए गए हैं, जिन्हें वापस लौटाने पर कर्सर घुमाने पर आपसे दस्तावेज़ या रूट के लिए क्वेरी की जाती है:
Kotlin
private val DEFAULT_ROOT_PROJECTION: Array<String> = arrayOf( DocumentsContract.Root.COLUMN_ROOT_ID, DocumentsContract.Root.COLUMN_MIME_TYPES, DocumentsContract.Root.COLUMN_FLAGS, DocumentsContract.Root.COLUMN_ICON, DocumentsContract.Root.COLUMN_TITLE, DocumentsContract.Root.COLUMN_SUMMARY, DocumentsContract.Root.COLUMN_DOCUMENT_ID, DocumentsContract.Root.COLUMN_AVAILABLE_BYTES ) private val DEFAULT_DOCUMENT_PROJECTION: Array<String> = arrayOf( DocumentsContract.Document.COLUMN_DOCUMENT_ID, DocumentsContract.Document.COLUMN_MIME_TYPE, DocumentsContract.Document.COLUMN_DISPLAY_NAME, DocumentsContract.Document.COLUMN_LAST_MODIFIED, DocumentsContract.Document.COLUMN_FLAGS, DocumentsContract.Document.COLUMN_SIZE )
Java
private static final String[] DEFAULT_ROOT_PROJECTION = new String[]{Root.COLUMN_ROOT_ID, Root.COLUMN_MIME_TYPES, Root.COLUMN_FLAGS, Root.COLUMN_ICON, Root.COLUMN_TITLE, Root.COLUMN_SUMMARY, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_AVAILABLE_BYTES,}; private static final String[] DEFAULT_DOCUMENT_PROJECTION = new String[]{Document.COLUMN_DOCUMENT_ID, Document.COLUMN_MIME_TYPE, Document.COLUMN_DISPLAY_NAME, Document.COLUMN_LAST_MODIFIED, Document.COLUMN_FLAGS, Document.COLUMN_SIZE,};
रूट के लिए आपके कर्सर में कुछ ज़रूरी कॉलम शामिल होने चाहिए. ये कॉलम हैं:
दस्तावेज़ों के कर्सर में, यहां दिए गए ज़रूरी कॉलम शामिल होने चाहिए:
COLUMN_DOCUMENT_ID
COLUMN_DISPLAY_NAME
COLUMN_MIME_TYPE
COLUMN_FLAGS
COLUMN_SIZE
COLUMN_LAST_MODIFIED
DocumentsProvider की सब-क्लास बनाएं
कस्टम दस्तावेज़ देने वाली सेवा को लिखने का अगला चरण है,
ऐब्स्ट्रैक्ट क्लास DocumentsProvider
. कम से कम आपको
नीचे दिए गए तरीके लागू करें:
सिर्फ़ इन तरीकों को लागू करना ज़रूरी है. हालांकि,
और भी बहुत सारी चीज़ें आपकी इच्छा हो सकती है. DocumentsProvider
देखें
देखें.
मूल परिभाषित करें
आपके queryRoots()
को लागू करने के लिए एक Cursor
देना होगा, जो सभी
में परिभाषित कॉलम का उपयोग करके आपके दस्तावेज़ कंपनी की रूट डायरेक्ट्री में
DocumentsContract.Root
.
नीचे दिए गए स्निपेट में, projection
पैरामीटर
विशिष्ट फ़ील्ड जिन्हें कॉलर वापस पाना चाहता है. स्निपेट एक नया कर्सर बनाता है
और इसमें एक पंक्ति जोड़ देता है—एक रूट, टॉप लेवल की डायरेक्ट्री, जैसे
डाउनलोड या इमेज. सेवा देने वाली ज़्यादातर कंपनियों के पास सिर्फ़ एक रूट होता है. आपके पास एक से ज़्यादा,
उदाहरण के लिए, कई उपयोगकर्ता खातों के मामले में. इस स्थिति में, बस एक
कर्सर को दूसरी पंक्ति में ले जाएं.
Kotlin
override fun queryRoots(projection: Array<out String>?): Cursor { // Use a MatrixCursor to build a cursor // with either the requested fields, or the default // projection if "projection" is null. val result = MatrixCursor(resolveRootProjection(projection)) // If user is not logged in, return an empty root cursor. This removes our // provider from the list entirely. if (!isUserLoggedIn()) { return result } // It's possible to have multiple roots (e.g. for multiple accounts in the // same app) -- just add multiple cursor rows. result.newRow().apply { add(DocumentsContract.Root.COLUMN_ROOT_ID, ROOT) // You can provide an optional summary, which helps distinguish roots // with the same title. You can also use this field for displaying an // user account name. add(DocumentsContract.Root.COLUMN_SUMMARY, context.getString(R.string.root_summary)) // FLAG_SUPPORTS_CREATE means at least one directory under the root supports // creating documents. FLAG_SUPPORTS_RECENTS means your application's most // recently used documents will show up in the "Recents" category. // FLAG_SUPPORTS_SEARCH allows users to search all documents the application // shares. add( DocumentsContract.Root.COLUMN_FLAGS, DocumentsContract.Root.FLAG_SUPPORTS_CREATE or DocumentsContract.Root.FLAG_SUPPORTS_RECENTS or DocumentsContract.Root.FLAG_SUPPORTS_SEARCH ) // COLUMN_TITLE is the root title (e.g. Gallery, Drive). add(DocumentsContract.Root.COLUMN_TITLE, context.getString(R.string.title)) // This document id cannot change after it's shared. add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, getDocIdForFile(baseDir)) // The child MIME types are used to filter the roots and only present to the // user those roots that contain the desired type somewhere in their file hierarchy. add(DocumentsContract.Root.COLUMN_MIME_TYPES, getChildMimeTypes(baseDir)) add(DocumentsContract.Root.COLUMN_AVAILABLE_BYTES, baseDir.freeSpace) add(DocumentsContract.Root.COLUMN_ICON, R.drawable.ic_launcher) } return result }
Java
@Override public Cursor queryRoots(String[] projection) throws FileNotFoundException { // Use a MatrixCursor to build a cursor // with either the requested fields, or the default // projection if "projection" is null. final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection)); // If user is not logged in, return an empty root cursor. This removes our // provider from the list entirely. if (!isUserLoggedIn()) { return result; } // It's possible to have multiple roots (e.g. for multiple accounts in the // same app) -- just add multiple cursor rows. final MatrixCursor.RowBuilder row = result.newRow(); row.add(Root.COLUMN_ROOT_ID, ROOT); // You can provide an optional summary, which helps distinguish roots // with the same title. You can also use this field for displaying an // user account name. row.add(Root.COLUMN_SUMMARY, getContext().getString(R.string.root_summary)); // FLAG_SUPPORTS_CREATE means at least one directory under the root supports // creating documents. FLAG_SUPPORTS_RECENTS means your application's most // recently used documents will show up in the "Recents" category. // FLAG_SUPPORTS_SEARCH allows users to search all documents the application // shares. row.add(Root.COLUMN_FLAGS, Root.FLAG_SUPPORTS_CREATE | Root.FLAG_SUPPORTS_RECENTS | Root.FLAG_SUPPORTS_SEARCH); // COLUMN_TITLE is the root title (e.g. Gallery, Drive). row.add(Root.COLUMN_TITLE, getContext().getString(R.string.title)); // This document id cannot change after it's shared. row.add(Root.COLUMN_DOCUMENT_ID, getDocIdForFile(baseDir)); // The child MIME types are used to filter the roots and only present to the // user those roots that contain the desired type somewhere in their file hierarchy. row.add(Root.COLUMN_MIME_TYPES, getChildMimeTypes(baseDir)); row.add(Root.COLUMN_AVAILABLE_BYTES, baseDir.getFreeSpace()); row.add(Root.COLUMN_ICON, R.drawable.ic_launcher); return result; }
अगर दस्तावेज़ देने वाली कंपनी, रूट के किसी डाइनैमिक सेट से कनेक्ट होती है, जैसे कि यूएसबी
ऐसा डिवाइस जो डिसकनेक्ट हो सकता है या ऐसा खाता जिससे उपयोगकर्ता साइन आउट कर सकता है—आप
उन बदलावों के साथ सिंक रहने के लिए दस्तावेज़ के यूज़र इंटरफ़ेस (यूआई) को अपडेट कर सकता है. इसके लिए,
ContentResolver.notifyChange()
तरीका, जैसा कि नीचे दिए गए कोड स्निपेट में दिखाया गया है.
Kotlin
val rootsUri: Uri = DocumentsContract.buildRootsUri(BuildConfig.DOCUMENTS_AUTHORITY) context.contentResolver.notifyChange(rootsUri, null)
Java
Uri rootsUri = DocumentsContract.buildRootsUri(BuildConfig.DOCUMENTS_AUTHORITY); context.getContentResolver().notifyChange(rootsUri, null);
सेवा देने वाले संगठन में दस्तावेज़ों की सूची बनाएं
को लागू करना
queryChildDocuments()
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
एक Cursor
देना चाहिए, जो इसमें मौजूद सभी फ़ाइलों पर ले जाता हो
DocumentsContract.Document
.
जब उपयोगकर्ता पिकर यूज़र इंटरफ़ेस (यूआई) में आपका रूट चुनता है, तो यह तरीका कॉल किया जाता है.
यह तरीका, उस दस्तावेज़ आईडी के चाइल्ड एंट्री को हासिल करता है जो इसके बताए गए दस्तावेज़ आईडी के आधार पर तैयार किया गया है
COLUMN_DOCUMENT_ID
.
इसके बाद, उपयोगकर्ता जब भी किसी विज्ञापन को चुनता है, तो सिस्टम इस तरीके को कॉल करता है
सबडायरेक्ट्री की सुविधा का इस्तेमाल करें.
यह स्निपेट अनुरोध किए गए कॉलम के साथ एक नया कर्सर बनाता है, और फिर कर्सर पर पैरंट डायरेक्ट्री में मौजूद हर चाइल्ड के बारे में जानकारी. चाइल्ड कोई इमेज या दूसरी डायरेक्ट्री हो सकती है—कोई भी फ़ाइल:
Kotlin
override fun queryChildDocuments( parentDocumentId: String?, projection: Array<out String>?, sortOrder: String? ): Cursor { return MatrixCursor(resolveDocumentProjection(projection)).apply { val parent: File = getFileForDocId(parentDocumentId) parent.listFiles() .forEach { file -> includeFile(this, null, file) } } }
Java
@Override public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException { final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); final File parent = getFileForDocId(parentDocumentId); for (File file : parent.listFiles()) { // Adds the file's display name, MIME type, size, and so on. includeFile(result, null, file); } return result; }
दस्तावेज़ की जानकारी पाना
को लागू करना
queryDocument()
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
एक Cursor
देना चाहिए, जो बताई गई फ़ाइल पर ले जाता हो,
DocumentsContract.Document
में बताए गए कॉलम का इस्तेमाल करके.
queryDocument()
विधि वही जानकारी वापस भेजती है जो
queryChildDocuments()
,
लेकिन किसी खास फ़ाइल के लिए:
Kotlin
override fun queryDocument(documentId: String?, projection: Array<out String>?): Cursor { // Create a cursor with the requested projection, or the default projection. return MatrixCursor(resolveDocumentProjection(projection)).apply { includeFile(this, documentId, null) } }
Java
@Override public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException { // Create a cursor with the requested projection, or the default projection. final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); includeFile(result, documentId, null); return result; }
दस्तावेज़ देने वाली कंपनी, इसके ज़रिए दस्तावेज़ के लिए थंबनेल भी दे सकती है
को ओवरराइड करना
DocumentsProvider.openDocumentThumbnail()
तरीका जोड़ना और
FLAG_SUPPORTS_THUMBNAIL
फ़्लैग का इस्तेमाल कर सकते हैं.
नीचे दिया गया कोड स्निपेट एक उदाहरण देकर बताया गया है कि
DocumentsProvider.openDocumentThumbnail()
.
Kotlin
override fun openDocumentThumbnail( documentId: String?, sizeHint: Point?, signal: CancellationSignal? ): AssetFileDescriptor { val file = getThumbnailFileForDocId(documentId) val pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) return AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH) }
Java
@Override public AssetFileDescriptor openDocumentThumbnail(String documentId, Point sizeHint, CancellationSignal signal) throws FileNotFoundException { final File file = getThumbnailFileForDocId(documentId); final ParcelFileDescriptor pfd = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); return new AssetFileDescriptor(pfd, 0, AssetFileDescriptor.UNKNOWN_LENGTH); }
चेतावनी:
दस्तावेज़ देने वाली कंपनी को, थंबनेल इमेज दो से ज़्यादा बार नहीं दिखानी चाहिए
sizeHint
पैरामीटर से तय किया गया साइज़.
दस्तावेज़ खोलना
आपको openDocument()
लागू करना होगा, ताकि ParcelFileDescriptor
बताई गई फ़ाइल शामिल है. अन्य ऐप्लिकेशन, लौटाए गए ParcelFileDescriptor
का इस्तेमाल कर सकते हैं
का इस्तेमाल किया जा सकता है. सिस्टम इस तरीके को तब कॉल करता है, जब उपयोगकर्ता कोई फ़ाइल चुनता है,
और क्लाइंट ऐप्लिकेशन कॉल करके उसे ऐक्सेस करने का अनुरोध करता है
openFileDescriptor()
.
उदाहरण के लिए:
Kotlin
override fun openDocument( documentId: String, mode: String, signal: CancellationSignal ): ParcelFileDescriptor { Log.v(TAG, "openDocument, mode: $mode") // It's OK to do network operations in this method to download the document, // as long as you periodically check the CancellationSignal. If you have an // extremely large file to transfer from the network, a better solution may // be pipes or sockets (see ParcelFileDescriptor for helper methods). val file: File = getFileForDocId(documentId) val accessMode: Int = ParcelFileDescriptor.parseMode(mode) val isWrite: Boolean = mode.contains("w") return if (isWrite) { val handler = Handler(context.mainLooper) // Attach a close listener if the document is opened in write mode. try { ParcelFileDescriptor.open(file, accessMode, handler) { // Update the file with the cloud server. The client is done writing. Log.i(TAG, "A file with id $documentId has been closed! Time to update the server.") } } catch (e: IOException) { throw FileNotFoundException( "Failed to open document with id $documentId and mode $mode" ) } } else { ParcelFileDescriptor.open(file, accessMode) } }
Java
@Override public ParcelFileDescriptor openDocument(final String documentId, final String mode, CancellationSignal signal) throws FileNotFoundException { Log.v(TAG, "openDocument, mode: " + mode); // It's OK to do network operations in this method to download the document, // as long as you periodically check the CancellationSignal. If you have an // extremely large file to transfer from the network, a better solution may // be pipes or sockets (see ParcelFileDescriptor for helper methods). final File file = getFileForDocId(documentId); final int accessMode = ParcelFileDescriptor.parseMode(mode); final boolean isWrite = (mode.indexOf('w') != -1); if(isWrite) { // Attach a close listener if the document is opened in write mode. try { Handler handler = new Handler(getContext().getMainLooper()); return ParcelFileDescriptor.open(file, accessMode, handler, new ParcelFileDescriptor.OnCloseListener() { @Override public void onClose(IOException e) { // Update the file with the cloud server. The client is done // writing. Log.i(TAG, "A file with id " + documentId + " has been closed! Time to " + "update the server."); } }); } catch (IOException e) { throw new FileNotFoundException("Failed to open document with id" + documentId + " and mode " + mode); } } else { return ParcelFileDescriptor.open(file, accessMode); } }
अगर आपको दस्तावेज़ देने वाला संगठन, फ़ाइलों को स्ट्रीम करता है या मुश्किल फ़ाइलों को हैंडल करता है
डेटा स्ट्रक्चर के लिए,
createReliablePipe()
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
या
createReliableSocketPair()
तरीके.
इन तरीकों से, आपके कारोबार के हिसाब से
ParcelFileDescriptor
ऑब्जेक्ट, जहां दिया जा सकने वाला आइटम वापस किया जा सकता है
और दूसरे को किसी
ParcelFileDescriptor.AutoCloseOutputStream
या
ParcelFileDescriptor.AutoCloseInputStream
.
हाल ही के दस्तावेज़ों और खोज के लिए मदद चाहिए
आप अपने
queryRecentDocuments()
तरीका और वापस लौट रहे हैं
FLAG_SUPPORTS_RECENTS
,
नीचे दिया गया कोड स्निपेट, एक उदाहरण में बताया गया है कि
queryRecentDocuments()
तरीके.
Kotlin
override fun queryRecentDocuments(rootId: String?, projection: Array<out String>?): Cursor { // This example implementation walks a // local file structure to find the most recently // modified files. Other implementations might // include making a network call to query a // server. // Create a cursor with the requested projection, or the default projection. val result = MatrixCursor(resolveDocumentProjection(projection)) val parent: File = getFileForDocId(rootId) // Create a queue to store the most recent documents, // which orders by last modified. val lastModifiedFiles = PriorityQueue( 5, Comparator<File> { i, j -> Long.compare(i.lastModified(), j.lastModified()) } ) // Iterate through all files and directories // in the file structure under the root. If // the file is more recent than the least // recently modified, add it to the queue, // limiting the number of results. val pending : MutableList<File> = mutableListOf() // Start by adding the parent to the list of files to be processed pending.add(parent) // Do while we still have unexamined files while (pending.isNotEmpty()) { // Take a file from the list of unprocessed files val file: File = pending.removeAt(0) if (file.isDirectory) { // If it's a directory, add all its children to the unprocessed list pending += file.listFiles() } else { // If it's a file, add it to the ordered queue. lastModifiedFiles.add(file) } } // Add the most recent files to the cursor, // not exceeding the max number of results. for (i in 0 until Math.min(MAX_LAST_MODIFIED + 1, lastModifiedFiles.size)) { val file: File = lastModifiedFiles.remove() includeFile(result, null, file) } return result }
Java
@Override public Cursor queryRecentDocuments(String rootId, String[] projection) throws FileNotFoundException { // This example implementation walks a // local file structure to find the most recently // modified files. Other implementations might // include making a network call to query a // server. // Create a cursor with the requested projection, or the default projection. final MatrixCursor result = new MatrixCursor(resolveDocumentProjection(projection)); final File parent = getFileForDocId(rootId); // Create a queue to store the most recent documents, // which orders by last modified. PriorityQueue lastModifiedFiles = new PriorityQueue(5, new Comparator() { public int compare(File i, File j) { return Long.compare(i.lastModified(), j.lastModified()); } }); // Iterate through all files and directories // in the file structure under the root. If // the file is more recent than the least // recently modified, add it to the queue, // limiting the number of results. final LinkedList pending = new LinkedList(); // Start by adding the parent to the list of files to be processed pending.add(parent); // Do while we still have unexamined files while (!pending.isEmpty()) { // Take a file from the list of unprocessed files final File file = pending.removeFirst(); if (file.isDirectory()) { // If it's a directory, add all its children to the unprocessed list Collections.addAll(pending, file.listFiles()); } else { // If it's a file, add it to the ordered queue. lastModifiedFiles.add(file); } } // Add the most recent files to the cursor, // not exceeding the max number of results. for (int i = 0; i < Math.min(MAX_LAST_MODIFIED + 1, lastModifiedFiles.size()); i++) { final File file = lastModifiedFiles.remove(); includeFile(result, null, file); } return result; }
आप डाउनलोड करके ऊपर दिए गए स्निपेट का पूरा कोड डाउनलोड कर सकते हैं: StorageProvider कोड सैंपल.
सहायता दस्तावेज़ बनाना
आपके पास क्लाइंट ऐप्लिकेशन को दस्तावेज़ देने वाली सेवा में फ़ाइलें बनाने की अनुमति देने का विकल्प होता है.
अगर कोई क्लाइंट ऐप्लिकेशन, ACTION_CREATE_DOCUMENT
भेजता है
इंटेंट, तो आपका दस्तावेज़ कंपनी उस क्लाइंट ऐप्लिकेशन को
में नए दस्तावेज़ मौजूद हैं.
दस्तावेज़ बनाने की सुविधा के लिए, आपके रूट में
FLAG_SUPPORTS_CREATE
फ़्लैग.
जिन डायरेक्ट्री में नई फ़ाइलें बनाई जा सकती हैं उनके लिए यह ज़रूरी है कि
FLAG_DIR_SUPPORTS_CREATE
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
फ़्लैग करें.
दस्तावेज़ देने वाली कंपनी को,
createDocument()
तरीका. जब कोई उपयोगकर्ता आपके स्टोर में किसी डायरेक्ट्री को चुनता है
नई फ़ाइल सेव करने के लिए दस्तावेज़ उपलब्ध कराने वाले दस्तावेज़ को सेव करने के लिए, दस्तावेज़ देने वाले को एक कॉल आता है
createDocument()
. कन्वर्ज़न ट्रैकिंग की सुविधा को लागू करने के बारे में जानकारी
createDocument()
तरीका इस्तेमाल करने पर, नया जवाब दिया जाता है
COLUMN_DOCUMENT_ID
फ़ाइल से लिए जाते हैं. इसके बाद, क्लाइंट ऐप्लिकेशन उस आईडी का इस्तेमाल करके, फ़ाइल का हैंडल पा सकता है
और आखिर में,
नई फ़ाइल पर लिखने के लिए openDocument()
.
नीचे दिया गया कोड स्निपेट बताता है कि एक दस्तावेज़ है.
Kotlin
override fun createDocument(documentId: String?, mimeType: String?, displayName: String?): String { val parent: File = getFileForDocId(documentId) val file: File = try { File(parent.path, displayName).apply { createNewFile() setWritable(true) setReadable(true) } } catch (e: IOException) { throw FileNotFoundException( "Failed to create document with name $displayName and documentId $documentId" ) } return getDocIdForFile(file) }
Java
@Override public String createDocument(String documentId, String mimeType, String displayName) throws FileNotFoundException { File parent = getFileForDocId(documentId); File file = new File(parent.getPath(), displayName); try { file.createNewFile(); file.setWritable(true); file.setReadable(true); } catch (IOException e) { throw new FileNotFoundException("Failed to create document with name " + displayName +" and documentId " + documentId); } return getDocIdForFile(file); }
आप डाउनलोड करके ऊपर दिए गए स्निपेट का पूरा कोड डाउनलोड कर सकते हैं: StorageProvider कोड सैंपल.
दस्तावेज़ को मैनेज करने से जुड़ी सुविधाएं
फ़ाइलों को खोलने, बनाने, और देखने के अलावा, आपके दस्तावेज़ की सेवा देने वाली कंपनी
इससे क्लाइंट ऐप्लिकेशन का नाम बदलने, कॉपी करने, उनकी जगह बदलने, और उन्हें मिटाने की सुविधा भी मिल सकती है
फ़ाइलें शामिल हैं. दस्तावेज़ प्रबंधन की सुविधा जोड़ने के लिए
करने के लिए, दस्तावेज़ के
COLUMN_FLAGS
कॉलम
का इस्तेमाल करें. आपको यह भी लागू करना होगा
DocumentsProvider
का संबंधित तरीका
क्लास.
नीचे दी गई टेबल में,
COLUMN_FLAGS
फ़्लैग
और DocumentsProvider
तरीका इस्तेमाल करके
कुछ खास सुविधाएं दिखाने के लिए, प्रोवाइडर को लागू करने की ज़रूरत होगी.
सुविधा | चिह्नित करें | Method |
---|---|---|
फ़ाइल मिटाना |
FLAG_SUPPORTS_DELETE
|
deleteDocument()
|
फ़ाइल का नाम बदलना |
FLAG_SUPPORTS_RENAME
|
renameDocument()
|
दस्तावेज़ देने वाले की प्रॉपर्टी में मौजूद नई पैरंट डायरेक्ट्री में फ़ाइल कॉपी करें |
FLAG_SUPPORTS_COPY
|
copyDocument()
|
दस्तावेज़ देने वाले की भूमिका के तहत, फ़ाइल को एक डायरेक्ट्री से दूसरी डायरेक्ट्री में ले जाना |
FLAG_SUPPORTS_MOVE
|
moveDocument()
|
किसी फ़ाइल को उसकी पैरंट डायरेक्ट्री से हटाना |
FLAG_SUPPORTS_REMOVE
|
removeDocument()
|
वर्चुअल फ़ाइलों और दूसरे फ़ाइल फ़ॉर्मैट के साथ काम करते हैं
वर्चुअल फ़ाइलें, यह Android 7.0 (एपीआई लेवल 24) की एक सुविधा है. यह सुविधा, दस्तावेज़ देने वाली कंपनियों को अनुमति देती है उन फ़ाइलों को देखने का ऐक्सेस दिया जाए जिनके पास डायरेक्ट बाइट कोड प्रज़ेंटेशन. अन्य ऐप्लिकेशन को वर्चुअल फ़ाइलें देखने की अनुमति देने के लिए, आपको दस्तावेज़ की सेवा देने वाली कंपनी को, खोली जा सकने वाली वैकल्पिक फ़ाइल बनानी होगी वर्चुअल फ़ाइलों के लिए उपलब्ध कराई जा सकती है.
उदाहरण के लिए, मान लें कि दस्तावेज़ की सेवा देने वाली किसी कंपनी में एक फ़ाइल है
ऐसा फ़ॉर्मैट जिसे दूसरे ऐप्लिकेशन सीधे नहीं खोल सकते. यह खास तौर पर एक वर्चुअल फ़ाइल होती है.
जब कोई क्लाइंट ऐप्लिकेशन एक ACTION_VIEW
इंटेंट भेजता है
CATEGORY_OPENABLE
कैटगरी के बिना,
तो उपयोगकर्ता दस्तावेज़ देने वाले में इन वर्चुअल फ़ाइलों को चुन सकते हैं
देखने के लिए. इसके बाद, दस्तावेज़ देने वाला व्यक्ति, वर्चुअल फ़ाइल को लौटाता है
जो इमेज जैसे अलग फ़ाइल फ़ॉर्मैट में हों, लेकिन उन्हें आसानी से खोला जा सके.
इसके बाद, क्लाइंट ऐप्लिकेशन उस वर्चुअल फ़ाइल को खोल सकता है जिसे उपयोगकर्ता देख सकता है.
यह पुष्टि करने के लिए कि सेवा देने वाले संगठन में मौजूद कोई दस्तावेज़ वर्चुअल है, आपको
FLAG_VIRTUAL_DOCUMENT
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
फ़्लैग का इस्तेमाल करके फ़ाइल को
queryDocument()
तरीका. यह फ़्लैग क्लाइंट ऐप्लिकेशन को सूचित करता है कि फ़ाइल में कोई डायरेक्ट लिंक नहीं है
बाइट कोड दिखाना और इसे सीधे नहीं खोला जा सकता.
अगर आपने एलान किया है कि आपके दस्तावेज़ देने वाले ऐप्लिकेशन में मौजूद फ़ाइल वर्चुअल है,
तो हमारा सुझाव है कि आप इसे
MIME टाइप, जैसे कि इमेज या PDF. दस्तावेज़ की सेवा देने वाली कंपनी
यह नीति, वैकल्पिक MIME टाइप का एलान करती है
को ओवरराइड करके कोई वर्चुअल फ़ाइल देखने का समर्थन करता है
getDocumentStreamTypes()
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
तरीका. जब क्लाइंट ऐप्लिकेशन
getStreamTypes(android.net.Uri, java.lang.String)
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
तरीका है, तो सिस्टम
getDocumentStreamTypes()
दस्तावेज़ देने वाला है. कॉन्टेंट बनाने
getDocumentStreamTypes()
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
तरीका, वैकल्पिक MIME टाइप का कलेक्शन दिखाता है.
दस्तावेज़ देने वाली कंपनी का नाम, फ़ाइल के लिए काम करता है.
क्लाइंट के तय करने के बाद
दस्तावेज़ देने वाला, दस्तावेज़ को देखी जा सकने वाली फ़ाइल में बना सकता है
फ़ॉर्मैट है, तो क्लाइंट ऐप्लिकेशन
openTypedAssetFileDescriptor()
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
तरीका है, जो दस्तावेज़ कंपनी की
openTypedDocument()
तरीका. दस्तावेज़ देने वाला व्यक्ति, फ़ाइल को क्लाइंट ऐप्लिकेशन में भेजता है
अनुरोध किया गया फ़ाइल फ़ॉर्मैट.
नीचे दिया गया कोड स्निपेट,
getDocumentStreamTypes()
अभी तक किसी भी व्यक्ति ने चेक इन नहीं किया है
और
openTypedDocument()
तरीकों का इस्तेमाल करना होगा.
Kotlin
var SUPPORTED_MIME_TYPES : Array<String> = arrayOf("image/png", "image/jpg") override fun openTypedDocument( documentId: String?, mimeTypeFilter: String, opts: Bundle?, signal: CancellationSignal? ): AssetFileDescriptor? { return try { // Determine which supported MIME type the client app requested. when(mimeTypeFilter) { "image/jpg" -> openJpgDocument(documentId) "image/png", "image/*", "*/*" -> openPngDocument(documentId) else -> throw IllegalArgumentException("Invalid mimeTypeFilter $mimeTypeFilter") } } catch (ex: Exception) { Log.e(TAG, ex.message) null } } override fun getDocumentStreamTypes(documentId: String, mimeTypeFilter: String): Array<String> { return when (mimeTypeFilter) { "*/*", "image/*" -> { // Return all supported MIME types if the client app // passes in '*/*' or 'image/*'. SUPPORTED_MIME_TYPES } else -> { // Filter the list of supported mime types to find a match. SUPPORTED_MIME_TYPES.filter { it == mimeTypeFilter }.toTypedArray() } } }
Java
public static String[] SUPPORTED_MIME_TYPES = {"image/png", "image/jpg"}; @Override public AssetFileDescriptor openTypedDocument(String documentId, String mimeTypeFilter, Bundle opts, CancellationSignal signal) { try { // Determine which supported MIME type the client app requested. if ("image/png".equals(mimeTypeFilter) || "image/*".equals(mimeTypeFilter) || "*/*".equals(mimeTypeFilter)) { // Return the file in the specified format. return openPngDocument(documentId); } else if ("image/jpg".equals(mimeTypeFilter)) { return openJpgDocument(documentId); } else { throw new IllegalArgumentException("Invalid mimeTypeFilter " + mimeTypeFilter); } } catch (Exception ex) { Log.e(TAG, ex.getMessage()); } finally { return null; } } @Override public String[] getDocumentStreamTypes(String documentId, String mimeTypeFilter) { // Return all supported MIME tyupes if the client app // passes in '*/*' or 'image/*'. if ("*/*".equals(mimeTypeFilter) || "image/*".equals(mimeTypeFilter)) { return SUPPORTED_MIME_TYPES; } ArrayList requestedMimeTypes = new ArrayListl&t;g&t;(); // Iterate over the list of supported mime types to find a match. for (int i=0; i l&t; SUPPORTED_MIME_TYPES.length; i++) { if (SUPPORTED_MIME_TYPES[i].equals(mimeTypeFilter)) { requestedMimeTypes.add(SUPPORTED_MIME_TYPES[i]); } } return (String[])requestedMimeTypes.toArray(); }
सुरक्षा
मान लीजिए कि आपको दस्तावेज़ देने वाली कंपनी, पासवर्ड से सुरक्षित क्लाउड स्टोरेज सेवा है
और आपको यह पक्का करना है कि उपयोगकर्ता अपनी फ़ाइलें शेयर करने से पहले लॉग इन कर लें.
अगर उपयोगकर्ता ने लॉग इन नहीं किया है, तो आपके ऐप्लिकेशन को क्या करना चाहिए? इसका हल यह है कि
queryRoots()
को लागू करने में शून्य रूट शामिल हैं. इसका मतलब है कि एक खाली रूट कर्सर:
Kotlin
override fun queryRoots(projection: Array<out String>): Cursor { ... // If user is not logged in, return an empty root cursor. This removes our // provider from the list entirely. if (!isUserLoggedIn()) { return result }
Java
public Cursor queryRoots(String[] projection) throws FileNotFoundException { ... // If user is not logged in, return an empty root cursor. This removes our // provider from the list entirely. if (!isUserLoggedIn()) { return result; }
दूसरा चरण है getContentResolver().notifyChange()
को कॉल करना.
DocumentsContract
याद है? हम इसका इस्तेमाल इसलिए कर रहे हैं,
यह यूआरआई. नीचे दिया गया स्निपेट सिस्टम को आपकी साइट के
उपयोगकर्ता के लॉगिन स्टेटस में बदलाव होने पर, उसे दस्तावेज़ जारी करने वाली कंपनी के तौर पर सेव करता है. अगर उपयोगकर्ता,
लॉग इन किया है, तो queryRoots()
को कॉल करने पर
खाली कर्सर होना चाहिए, जैसा कि ऊपर दिखाया गया है. इससे यह पक्का होता है कि सेवा देने वाली कंपनी के दस्तावेज़
यह तब उपलब्ध होता है, जब उपयोगकर्ता सेवा देने वाली कंपनी में लॉग इन होता है.
Kotlin
private fun onLoginButtonClick() { loginOrLogout() getContentResolver().notifyChange( DocumentsContract.buildRootsUri(AUTHORITY), null ) }
Java
private void onLoginButtonClick() { loginOrLogout(); getContentResolver().notifyChange(DocumentsContract .buildRootsUri(AUTHORITY), null); }
इस पेज से जुड़े सैंपल कोड के लिए, इन्हें देखें:
इस पेज से जुड़े वीडियो के लिए, इन्हें देखें:
- DevBytes: Android 4.4 स्टोरेज ऐक्सेस फ़्रेमवर्क: सेवा देने वाली कंपनी
- स्टोरेज ऐक्सेस फ़्रेमवर्क: DocumentsProvider बनाना
- स्टोरेज के ऐक्सेस फ़्रेमवर्क में मौजूद वर्चुअल फ़ाइलें
इससे जुड़ी अतिरिक्त जानकारी के लिए, इन्हें देखें:
- DocumentsProvider बनाना
- स्टोरेज ऐक्सेस फ़्रेमवर्क का इस्तेमाल करके फ़ाइलें खोलना
- कॉन्टेंट देने वाले के बारे में बुनियादी बातें