एंड्रॉइड 5.0 (लॉलीपॉप) के लिए प्रस्तुत नए एसडी कार्ड एक्सेस एपीआई का उपयोग कैसे करें?


118

पृष्ठभूमि

पर Android 4.4 (किटकैट), गूगल एसडी कार्ड काफी सीमित करने के लिए उपयोग किया है।

एंड्रॉइड लॉलीपॉप (5.0) के रूप में, डेवलपर्स एक नए एपीआई का उपयोग कर सकते हैं जो उपयोगकर्ता को विशिष्ट फ़ोल्डर्स तक पहुंच की पुष्टि करने के लिए कहता है, जैसा कि इस Google-Group पोस्ट पर लिखा गया है ।

समस्या

पोस्ट आपको दो वेबसाइटों पर जाने का निर्देश देती है:

यह एक आंतरिक उदाहरण की तरह दिखता है (शायद एपीआई डेमो पर बाद में दिखाया जाए), लेकिन यह समझना काफी कठिन है कि क्या हो रहा है।

यह नए एपीआई का आधिकारिक दस्तावेज है, लेकिन यह पर्याप्त विवरण नहीं बताता है कि इसका उपयोग कैसे किया जाए।

यहाँ यह आपको बताता है:

यदि आपको वास्तव में दस्तावेजों की एक पूरी उप-सूची तक पूर्ण पहुंच की आवश्यकता है, तो उपयोगकर्ता को एक निर्देशिका चुनने के लिए ACTION_OPEN_DOCUMENT_TREE लॉन्च करके शुरू करें। फिर परिणामी getData () को fromTreeUri (Context, Uri) में से पारित करें ताकि उपयोगकर्ता चयनित पेड़ के साथ काम करना शुरू कर सके।

जब आप DocumentFile इंस्टेंस के ट्री को नेविगेट करते हैं, तो आप हमेशा OpenInputStream (Uri), आदि के साथ उपयोग के लिए उस ऑब्जेक्ट के लिए अंतर्निहित दस्तावेज़ का प्रतिनिधित्व करने के लिए getUri () का उपयोग कर सकते हैं।

KITKAT या पहले से चल रहे उपकरणों पर अपने कोड को सरल बनाने के लिए, आप FFile (फ़ाइल) का उपयोग कर सकते हैं जो एक दस्तावेज़पॉइडर के व्यवहार का अनुकरण करता है।

प्रश्न

मेरे पास नए एपीआई के बारे में कुछ सवाल हैं:

  1. आप वास्तव में इसका उपयोग कैसे करते हैं?
  2. पोस्ट के अनुसार, ओएस याद रखेगा कि एप्लिकेशन को फ़ाइलों / फ़ोल्डरों तक पहुंचने की अनुमति दी गई थी। यदि आप फ़ाइलों / फ़ोल्डरों तक पहुंच सकते हैं, तो आप कैसे जांचेंगे? क्या कोई ऐसा फ़ंक्शन है जो मुझे उन फ़ाइलों / फ़ोल्डरों की सूची लौटाता है जिन्हें मैं एक्सेस कर सकता हूं?
  3. किटकैट पर आप इस समस्या से कैसे निपटेंगे? क्या यह समर्थन पुस्तकालय का एक हिस्सा है?
  4. क्या ओएस पर एक सेटिंग स्क्रीन है जो यह दिखाती है कि किन ऐप्स की पहुंच किस फाइल / फ़ोल्डर तक है?
  5. यदि एक ही डिवाइस पर एक से अधिक उपयोगकर्ताओं के लिए ऐप इंस्टॉल किया जाता है तो क्या होगा?
  6. क्या इस नए एपीआई के बारे में कोई अन्य दस्तावेज / ट्यूटोरियल है?
  7. क्या अनुमति रद्द की जा सकती है? यदि हां, तो क्या कोई आशय है जिसे ऐप में भेजा जा रहा है?
  8. एक चयनित फ़ोल्डर पर पुनरावृत्ति की अनुमति के काम के लिए पूछेंगे?
  9. क्या अनुमति का उपयोग करने से उपयोगकर्ता को उपयोगकर्ता की पसंद से कई चयन का मौका मिल सकता है? या क्या ऐप को विशेष रूप से उस इरादे को बताने की ज़रूरत है जो अनुमति देने के लिए फ़ाइलें / फ़ोल्डर?
  10. क्या नया एपीआई आज़माने के लिए एमुलेटर पर कोई रास्ता है? मेरा मतलब है, इसमें एसडी-कार्ड विभाजन है, लेकिन यह प्राथमिक बाहरी भंडारण के रूप में काम करता है, इसलिए इस तक सभी पहुंच पहले से ही दी गई है (एक साधारण कार्ड का उपयोग करके)।
  11. क्या होता है जब उपयोगकर्ता एसडी कार्ड को दूसरे के साथ बदल देता है?

FWIW, AndroidPolice के पास आज इसके बारे में थोड़ा लेख था।
फेटेयर

@fattire धन्यवाद। लेकिन वे इस बारे में दिखा रहे हैं कि मैंने पहले से ही क्या पढ़ा है। हालांकि यह अच्छी खबर है।
Android डेवलपर

32
हर बार जब एक नया OS सामने आता है, तो वे हमारे जीवन को अधिक जटिल बना देते हैं ...
फैंटमैक्सएक्स

@Funkystein सच है। काश उन्होंने किटकैट पर ऐसा किया होता। अब 3 प्रकार के व्यवहार को संभालना है।
Android डेवलपर

1
@Funkystein मुझे नहीं पता। मैंने इसे बहुत पहले इस्तेमाल किया था। मुझे लगता है कि ऐसा करना बुरा नहीं है। आपको याद रखना होगा कि वे भी इंसान हैं, इसलिए वे गलतियां कर सकते हैं और समय-समय पर अपने मन को बदल सकते हैं ...
Android डेवलपर

जवाबों:


143

अच्छे सवालों के बहुत सारे, चलो खुदाई में। :)

आप इसका इस्तेमाल कैसे करते हैं?

यहाँ किटकैट में स्टोरेज एक्सेस फ्रेमवर्क के साथ बातचीत करने के लिए एक बढ़िया ट्यूटोरियल है:

https://developer.android.com/guide/topics/providers/document-provider.html#client

लॉलीपॉप में नए एपीआई के साथ बातचीत बहुत समान है। उपयोगकर्ता को निर्देशिका ट्री चुनने के लिए संकेत देने के लिए, आप इस तरह से एक इरादा लॉन्च कर सकते हैं:

    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    startActivityForResult(intent, 42);

फिर आपके onActivityResult () में, आप उपयोगकर्ता द्वारा चुने गए उरई को नए डॉक्यूमेंटफाइल हेल्पर क्लास में पास कर सकते हैं। यहां एक त्वरित उदाहरण है जो चुनी हुई निर्देशिका में फ़ाइलों को सूचीबद्ध करता है, और फिर एक नई फ़ाइल बनाता है:

public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
    if (resultCode == RESULT_OK) {
        Uri treeUri = resultData.getData();
        DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);

        // List all existing files inside picked directory
        for (DocumentFile file : pickedDir.listFiles()) {
            Log.d(TAG, "Found file " + file.getName() + " with size " + file.length());
        }

        // Create a new file and write into it
        DocumentFile newFile = pickedDir.createFile("text/plain", "My Novel");
        OutputStream out = getContentResolver().openOutputStream(newFile.getUri());
        out.write("A long time ago...".getBytes());
        out.close();
    }
}

उरी द्वारा लौटाया गया DocumentFile.getUri()अलग प्लेटफॉर्म एपीआई के साथ उपयोग करने के लिए पर्याप्त लचीला है। उदाहरण के लिए, आप इसे का उपयोग कर साझा कर सकता Intent.setData()साथ Intent.FLAG_GRANT_READ_URI_PERMISSION

आप का उपयोग करना चाहते हैं कि उरी मूल कोड से, आप कॉल कर सकते हैं ContentResolver.openFileDescriptor()और उसके बाद का उपयोग ParcelFileDescriptor.getFd()या detachFd()एक पारंपरिक POSIX फ़ाइल वर्णनकर्ता पूर्णांक प्राप्त करने के लिए।

यदि आप फ़ाइलों / फ़ोल्डरों तक पहुंच सकते हैं, तो आप कैसे जांचेंगे?

डिफ़ॉल्ट रूप से, स्टोरेज एक्सेस फ्रेमवर्क के माध्यम से लौटा गया Uris रिबूट के पार बरकरार नहीं है । प्लेटफ़ॉर्म "अनुमति को जारी रखने की क्षमता" प्रदान करता है, लेकिन यदि आप चाहते हैं तो आपको अभी भी अनुमति "लेने" की आवश्यकता है। ऊपर हमारे उदाहरण में, आप कॉल करेंगे:

    getContentResolver().takePersistableUriPermission(treeUri,
            Intent.FLAG_GRANT_READ_URI_PERMISSION |
            Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

आप हमेशा यह पता लगा सकते हैं कि ContentResolver.getPersistedUriPermissions()एपीआई के माध्यम से आपके ऐप की पहुंच कितनी बनी हुई है। यदि आपको अब एक स्थायी उड़ी तक पहुंच की आवश्यकता नहीं है, तो आप इसे जारी कर सकते हैं ContentResolver.releasePersistableUriPermission()

क्या यह किटकैट पर उपलब्ध है?

नहीं, हम मंच के पुराने संस्करणों में नई कार्यक्षमता जोड़ सकते हैं।

क्या मैं देख सकता हूं कि किन ऐप्स की फ़ाइलों / फ़ोल्डरों तक पहुंच है?

वर्तमान में ऐसा कोई UI नहीं है जो इसे दिखाता है, लेकिन आप adb shell dumpsys activity providersआउटपुट के "ग्रेटेड उरी अनुमतियों" अनुभाग में विवरण पा सकते हैं ।

यदि एक ही डिवाइस पर एक से अधिक उपयोगकर्ताओं के लिए ऐप इंस्टॉल किया जाता है तो क्या होगा?

उरी अनुमति अनुदान को अन्य सभी बहु-उपयोगकर्ता प्लेटफ़ॉर्म कार्यक्षमता की तरह, प्रति-उपयोगकर्ता के आधार पर अलग किया जाता है। यही है, दो अलग-अलग उपयोगकर्ताओं के तहत चलने वाले एक ही ऐप में कोई भी ओवररैपिंग या साझा उरी अनुमति अनुदान नहीं है।

क्या अनुमति रद्द की जा सकती है?

बैकिंग डॉक्यूमेंटप्रॉइडर किसी भी समय अनुमति को रद्द कर सकता है, जैसे कि क्लाउड-आधारित डॉक्यूमेंट डिलीट होने पर। इन निरस्त अनुमतियों की खोज करने का सबसे आम तरीका यह है कि जब वे ContentResolver.getPersistedUriPermissions()ऊपर उल्लिखित से गायब हो जाते हैं ।

जब भी ऐप में अनुदान में शामिल ऐप के लिए डेटा साफ़ हो जाता है तो अनुमतियाँ भी रद्द कर दी जाती हैं।

एक चयनित फ़ोल्डर पर पुनरावृत्ति की अनुमति के काम के लिए पूछेंगे?

हाँ, ACTION_OPEN_DOCUMENT_TREEआशय आपको मौजूदा और नई बनाई गई फ़ाइलों और निर्देशिकाओं दोनों के लिए पुनरावर्ती पहुँच प्रदान करता है।

क्या यह कई चयन की अनुमति देता है?

हां, किटकैट के बाद से कई चयन का समर्थन किया गया है, और आप EXTRA_ALLOW_MULTIPLEअपना ACTION_OPEN_DOCUMENTइरादा शुरू करते समय सेटिंग द्वारा अनुमति दे सकते हैं । आप उन फ़ाइलों के प्रकारों का उपयोग Intent.setType()या EXTRA_MIME_TYPESसंकीर्ण कर सकते हैं जिन्हें उठाया जा सकता है:

http://developer.android.com/reference/android/content/Intent.html#ACTION_OPEN_DOCUMENT

क्या नया एपीआई आज़माने के लिए एमुलेटर पर कोई रास्ता है?

हां, प्राथमिक साझा संग्रहण डिवाइस को एम्यूलेटर पर भी बीनने वाले में दिखाई देना चाहिए। आपका ऐप्लिकेशन केवल साझा भंडारण तक पहुँचने के लिए संग्रहण एक्सेस फ़्रेमवर्क का उपयोग करता है, तो आपको अब आवश्यकता READ/WRITE_EXTERNAL_STORAGEअनुमतियों सब पर है और उन्हें हटाने या उपयोग कर सकते हैं android:maxSdkVersionकरने के लिए केवल पुराने मंच संस्करणों पर उन्हें का अनुरोध सुविधा।

जब उपयोगकर्ता एसडी-कार्ड को दूसरे के साथ बदल देता है तो क्या होता है?

जब भौतिक मीडिया शामिल होता है, तो अंतर्निहित मीडिया का UUID (जैसे FAT सीरियल नंबर) हमेशा लौटी हुई उड़ी में जलाया जाता है। सिस्टम इसका उपयोग आपको उस मीडिया से कनेक्ट करने के लिए करता है जिसे उपयोगकर्ता ने मूल रूप से चुना है, भले ही उपयोगकर्ता कई स्लॉट्स के बीच मीडिया को स्वैप करता हो।

यदि उपयोगकर्ता दूसरे कार्ड में स्वैप करता है, तो आपको नए कार्ड तक पहुंच प्राप्त करने के लिए संकेत देना होगा। चूंकि सिस्टम प्रति-UUID आधार पर अनुदान को याद रखता है, इसलिए यदि उपयोगकर्ता बाद में इसे पुन: स्थापित कर लेता है, तो आपको मूल कार्ड तक पहले से दी गई पहुंच जारी रहेगी।

http://en.wikipedia.org/wiki/Volume_serial_number


2
समझा। इसलिए निर्णय यह था कि एक अलग का उपयोग करने के लिए ज्ञात एपीआई (फ़ाइल) में अधिक जोड़ने के बजाय। ठीक है। क्या आप अन्य प्रश्नों के उत्तर दे सकते हैं (पहली टिप्पणी में लिखे गए)? BTW, इस सब का जवाब देने के लिए धन्यवाद।
एंड्रॉइड डेवलपर

7
@JeffSharkey OPEN_DOCUMENT_TREE को आरंभिक स्थान संकेत प्रदान करने का कोई तरीका? उपयोगकर्ताओं को हमेशा इस बात पर नेविगेट करने में सबसे अच्छा नहीं होता है कि एप्लिकेशन को क्या एक्सेस की आवश्यकता है।
जस्टिन

2
क्या कोई तरीका है, फ़ाइल मोड में अंतिम संशोधित टाइमस्टैम्प ( setLastModified () विधि ) कैसे बदलें ? यह अभिलेखागार जैसे अनुप्रयोगों के लिए वास्तव में मौलिक है।
क्वार्क

1
कहते हैं कि आपके पास एक संग्रहीत फ़ोल्डर दस्तावेज़ उरी है और आप डिवाइस को रिबूट करने के बाद किसी बिंदु पर बाद में फ़ाइलों को सूचीबद्ध करना चाहते हैं। DocumentFile.fromTreeUri हमेशा रूट फाइल्स को सूचीबद्ध करता है, कोई फर्क नहीं पड़ता कि Uri आप इसे (यहां तक ​​कि बच्चे को Uri) भी देते हैं, इसलिए आप एक DocumentFile कैसे बनाते हैं, जिस पर आप Listfiles को कॉल कर सकते हैं, जहां DocumentFile रूट या SingleDocument में से किसी एक को भी नहीं दिखा रहा है।
एंडर्स सी

1
@JeffSharkey MediaMuxer में इस URI का उपयोग कैसे किया जा सकता है, क्योंकि यह एक स्ट्रिंग को आउटपुट फ़ाइल पथ के रूप में स्वीकार करता है? MediaMuxer (java.lang.String, int
Petrakeas

45

Github में मेरे एंड्रॉइड प्रोजेक्ट में, नीचे लिंक किया गया है, आप वर्किंग कोड पा सकते हैं जो एंड्रॉइड में extSdCard पर लिखने की अनुमति देता है। यह मानता है कि उपयोगकर्ता पूरे एसडी कार्ड तक पहुंच देता है और फिर आपको इस कार्ड पर हर जगह लिखने देता है। (यदि आप केवल एकल फ़ाइलों तक पहुँच प्राप्त करना चाहते हैं, तो चीजें आसान हो जाती हैं।)

मुख्य कोड स्निपलेट्स

संग्रहण पहुँच ढांचे को ट्रिगर करना:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void triggerStorageAccessFramework() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    startActivityForResult(intent, REQUEST_CODE_STORAGE_ACCESS);
}

स्टोरेज एक्सेस फ्रेमवर्क से प्रतिक्रिया को संभालना:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public final void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) {
    if (requestCode == SettingsFragment.REQUEST_CODE_STORAGE_ACCESS) {
        Uri treeUri = null;
        if (resultCode == Activity.RESULT_OK) {
            // Get Uri from Storage Access Framework.
            treeUri = resultData.getData();

            // Persist URI in shared preference so that you can use it later.
            // Use your own framework here instead of PreferenceUtil.
            PreferenceUtil.setSharedPreferenceUri(R.string.key_internal_uri_extsdcard, treeUri);

            // Persist access permissions.
            final int takeFlags = resultData.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            getActivity().getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
        }
    }
}

स्टोरेज एक्सेस फ्रेमवर्क (संग्रहीत URL का उपयोग करना, यह मानते हुए कि यह बाहरी एसडी कार्ड का रूट फ़ोल्डर का URL है) के माध्यम से किसी फ़ाइल के लिए एक आउटपुटस्ट्रीम प्राप्त करना

DocumentFile targetDocument = getDocumentFile(file, false);
OutputStream outStream = Application.getAppContext().
    getContentResolver().openOutputStream(targetDocument.getUri());

यह निम्न सहायक विधियों का उपयोग करता है:

public static DocumentFile getDocumentFile(final File file, final boolean isDirectory) {
    String baseFolder = getExtSdCardFolder(file);

    if (baseFolder == null) {
        return null;
    }

    String relativePath = null;
    try {
        String fullPath = file.getCanonicalPath();
        relativePath = fullPath.substring(baseFolder.length() + 1);
    }
    catch (IOException e) {
        return null;
    }

    Uri treeUri = PreferenceUtil.getSharedPreferenceUri(R.string.key_internal_uri_extsdcard);

    if (treeUri == null) {
        return null;
    }

    // start with root of SD card and then parse through document tree.
    DocumentFile document = DocumentFile.fromTreeUri(Application.getAppContext(), treeUri);

    String[] parts = relativePath.split("\\/");
    for (int i = 0; i < parts.length; i++) {
        DocumentFile nextDocument = document.findFile(parts[i]);

        if (nextDocument == null) {
            if ((i < parts.length - 1) || isDirectory) {
                nextDocument = document.createDirectory(parts[i]);
            }
            else {
                nextDocument = document.createFile("image", parts[i]);
            }
        }
        document = nextDocument;
    }

    return document;
}

public static String getExtSdCardFolder(final File file) {
    String[] extSdPaths = getExtSdCardPaths();
    try {
        for (int i = 0; i < extSdPaths.length; i++) {
            if (file.getCanonicalPath().startsWith(extSdPaths[i])) {
                return extSdPaths[i];
            }
        }
    }
    catch (IOException e) {
        return null;
    }
    return null;
}

/**
 * Get a list of external SD card paths. (Kitkat or higher.)
 *
 * @return A list of external SD card paths.
 */
@TargetApi(Build.VERSION_CODES.KITKAT)
private static String[] getExtSdCardPaths() {
    List<String> paths = new ArrayList<>();
    for (File file : Application.getAppContext().getExternalFilesDirs("external")) {
        if (file != null && !file.equals(Application.getAppContext().getExternalFilesDir("external"))) {
            int index = file.getAbsolutePath().lastIndexOf("/Android/data");
            if (index < 0) {
                Log.w(Application.TAG, "Unexpected external file dir: " + file.getAbsolutePath());
            }
            else {
                String path = file.getAbsolutePath().substring(0, index);
                try {
                    path = new File(path).getCanonicalPath();
                }
                catch (IOException e) {
                    // Keep non-canonical path.
                }
                paths.add(path);
            }
        }
    }
    return paths.toArray(new String[paths.size()]);
}

 /**
 * Retrieve the application context.
 *
 * @return The (statically stored) application context
 */
public static Context getAppContext() {
    return Application.mApplication.getApplicationContext();
}

पूर्ण कोड का संदर्भ

https://github.com/jeisfeld/Augendiagnose/blob/master/AugendiagnoseIdea/augendiagnoseLib/src/main/java/de/jeisfeld/augendiagnoselib/fragments/SettingsFragment.java#L521

तथा

https://github.com/jeisfeld/Augendiagnose/blob/master/AugendiagnoseIdea/augendiagnoseLib/src/main/java/de/jeisfeld/augendiagnoselib/util/imagefile/FileUtil.java


1
क्या आप कृपया इसे एक न्यूनतम परियोजना में रख सकते हैं, जो केवल एसडी कार्ड को संभालती है? इसके अलावा, क्या आप जानते हैं कि अगर सभी बाहरी स्टोरेज उपलब्ध हैं, तो मैं कैसे देख सकता हूं कि वे भी सुलभ हैं, ताकि मैं उनकी अनुमति के लिए कुछ न करूं?
Android डेवलपर

1
काश मैं इसे और बढ़ा पाता। मैं इस समाधान के लिए आधा था और दस्तावेज़ नेविगेशन को इतना भयानक पाया कि मुझे लगा कि मैं इसे गलत कर रहा हूं। इस पर कुछ पुष्टि करने के लिए अच्छा है। धन्यवाद Google ... कुछ भी नहीं के लिए।
एंथनी

1
हां, बाहरी एसडी पर लिखने के लिए आप सामान्य फ़ाइल दृष्टिकोण का उपयोग नहीं कर सकते। दूसरी ओर, पुराने एंड्रॉइड संस्करणों के लिए और प्राथमिक एसडी के लिए, फ़ाइल अभी भी सबसे कुशल दृष्टिकोण है। इसलिए आपको फ़ाइल एक्सेस के लिए एक कस्टम यूटिलिटी क्लास का उपयोग करना चाहिए।
जोर्ज इस्फ़िल्ड

15
@ JörgEisfeld: मेरे पास एक ऐप है जो File254 बार उपयोग करता है। आप कल्पना कर सकते हैं कि फिक्सिंग ?? Android पिछड़े अनुकूलता की कुल कमी के साथ देवताओं के लिए एक बुरा सपना बन रहा है। मुझे अभी भी कोई ऐसी जगह नहीं मिली जहां वे समझाते हों कि Google ने बाह्य भंडारण के संबंध में यह सभी मूर्खतापूर्ण फैसले क्यों लिए। कुछ दावे "सुरक्षा" करते हैं, लेकिन निश्चित रूप से बकवास है क्योंकि कोई भी ऐप आंतरिक भंडारण को गड़बड़ कर सकता है। मेरा अनुमान है कि हमें उनकी क्लाउड सेवाओं का उपयोग करने के लिए मजबूर करने का प्रयास करना है। शुक्र है, रूटिंग समस्याओं को हल करती है ... कम से कम एंड्रॉइड के लिए <6 ....
लुइस ए। फ्लोरिट

1
ठीक है। जादुई रूप से, मैंने अपना फ़ोन पुनः आरंभ करने के बाद, यह काम करता है :)
sancho21

0

यह सिर्फ एक पूरक उत्तर है।

एक नई फ़ाइल बनाने के बाद, आपको इसके स्थान को अपने डेटाबेस में सहेजना होगा और कल इसे पढ़ना होगा। आप इस विधि का उपयोग करके इसे पुनः प्राप्त कर सकते हैं:

/**
 * Get {@link DocumentFile} object from SD card.
 * @param directory SD card ID followed by directory name, for example {@code 6881-2249:Download/Archive},
 *                 where ID for SD card is {@code 6881-2249}
 * @param fileName for example {@code intel_haxm.zip}
 * @return <code>null</code> if does not exist
 */
public static DocumentFile getExternalFile(Context context, String directory, String fileName){
    Uri uri = Uri.parse("content://com.android.externalstorage.documents/tree/" + directory);
    DocumentFile parent = DocumentFile.fromTreeUri(context, uri);
    return parent != null ? parent.findFile(fileName) : null;
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == SettingsFragment.REQUEST_CODE_STORAGE_ACCESS && resultCode == RESULT_OK) {
        int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
        getContentResolver().takePersistableUriPermission(data.getData(), takeFlags);
        String sdcard = data.getDataString().replace("content://com.android.externalstorage.documents/tree/", "");
        try {
            sdcard = URLDecoder.decode(sdcard, "ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // for example, sdcardId results "6312-2234"
        String sdcardId = sdcard.substring(0, sdcard.indexOf(':'));
        // save to preferences if you want to use it later
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
        preferences.edit().putString("sdcard", sdcardId).apply();
    }
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.