The Android file manager selects a file and obtains the file path URI to file

Time:2021-12-9

Record a murder caused by file upload.

Solve the problem of QQ browser com.tencent.mtt.fileprovider.

Test demo


Update list

date Modification content
July 2, 2019 Update problems encountered

Previous description:

Using the system file manager, select the specified file type to upload.

Basic knowledge
  • MIME
  • Call up file manager
  • Specify browse location (path to URI)
  • Set multiple file types
  • Uri to path
Stepping pit
  • Com.tencent.mtt.fileprovider questions

1. MIME

Mime (Multipurpose Internet mail extensions) is an Internet standard that describes message content types.

final String DOC = "application/msword";
final String XLS = "application/vnd.ms-excel";
final String PPT = "application/vnd.ms-powerpoint";
final String DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
final String XLSX = "application/x-excel";
final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
final String PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
final String PDF = "application/pdf";
final String MP4 = "video/mp4";
final String M3U8 = "application/x-mpegURL";

More file types, baidu

2. Activate the file manager

  1. All types of files

    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    //Any type of file
    intent.setType("*/*");
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    startActivityForResult(intent,1);
    
    //-------Common types
        //Picture
    //intent.setType(“image/*”);
        //Audio
    //intent.setType(“audio/*”);
        //Video
    //intent.setType(“video/*”); 
    //intent.setType(“video/*;image/*”);
  2. Photo album of the system

     Intent intent= new Intent(Intent.ACTION_PICK, null);
     intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
     startActivityForResult(intent, REQUEST_CODE_FILE); 
    
    

3. Specify browse location (path to URI)

Jumping to the specified path involves converting the path to a URI, taking into account the differences between Android versions

/**
 * file --> uri
 * @param context
 * @param file
 *
 * @return
 */
public static Uri getUriFromFile(Context context, File file) {
    if (context == null || file == null) {
        throw new NullPointerException();
    }
    Uri uri;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        uri = FileProvider.getUriForFile(context.getApplicationContext(), BuildConfig.APPLICATION_ID + ".fileprovider", file);
    } else {
        uri = Uri.fromFile(file);
    }
    return uri;
}

Because the 7.0 upgrade still needs to beAndroidManifest.xmlConfigure fileprovider in

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

${applicationId}.fileproviderThis configuration should be kept in mind. In case of a big pit in the later stage, it depends on this value.

xml/file_pathsThe documents are as follows:

Reference CSDN

<!-- The physical path is equivalent to context. Getfilesdir() + / path / - >
<files-path name="name" path="path" />

 <!-- The physical path is equivalent to context. Getcachedir() + / path / - >
<cache-path name="name" path="path" /> 
 <!--  The physical path is equivalent to environment. Getexternalstoragedirectory() + / path / - >
<external-path name="name" path="path" />
 <!--  The physical path is equivalent to context. Getexternalfilesdir (string) + / path / - >
<external-files-path name="name" path="path" />
 <!--  The physical path is equivalent to context. Getexternalcachedir() + / path / - >
<external-cache-path name="name" path="path" />

Convert file path to(Test with wechat download directory)After the URI is, it is set to intent.

/**
 *Load the file selector according to the type
 */
private void chooseFileWithPath() {
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);

    //Jump to the specified path. If the path does not exist, switch to sdcard
    //Read permission required
    String path = getSDPath();
    if (!TextUtils.isEmpty(path)) {
        //Use wechat to download directory test
        path = path + File.separator + "tencent/MicroMsg/Download";
        File file = new File(path);
        if (file.exists()) {
            //Main code
            intent.setDataAndType(FileUtil.getUriFromFile(this, new File(path)), "*/*");
        } else {
            intent.setType("*/*");
        }
    } else {
        intent.setType("*/*");
    }
    startActivityForResult(intent, REQUEST_CODE_FILE);
}

Main effective code:

Intent intent = new Intent(action,uri);
intent.setType("*/*");
startActivityForResult(intent, REQUEST_CODE_FILE);

perhaps

Intent intent = new Intent(action);
intent.setDataAndType(uri, "*/*");
startActivityForResult(intent, REQUEST_CODE_FILE);

Special attention:
When the specified directory is called, there is no other attention in the native manager, but if the user uses a third-party manager, such as QQ browser, the return operation cannot be performed when entering the specified directory.
For example:
If the path is set to yes/sdcard/tencent/MicroMsg/Download, before enteringdownloadDirectory. If there are no files in this directory, you want to return to its upper directoryMicroMsg, No.

4. Set multiple file types

adoptintent.setType()The file type set by the method can only take effect once, so if you want to select onlydoc、excel、pdt、pptWait for office documents, filter out other files, and you can’t use themintent .setType()Way, but useIntent.EXTRA_MIME_TYPESTo pass the value.

final String DOC = "application/msword";
final String XLS = "application/vnd.ms-excel";
final String PPT = "application/vnd.ms-powerpoint";
final String DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
final String XLSX = "application/x-excel";
final String XLSX = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
final String PPTX = "application/vnd.openxmlformats-officedocument.presentationml.presentation";
final String PDF = "application/pdf";

/**
 *Multiple file types
*/
private void chooseMoreTypes() {
    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    String[] mimeTypes = {DOC, DOCX, PDF, PPT, PPTX, XLS, XLS1, XLSX};
    intent.setType("application/*");

    intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
    startActivityForResult(intent, REQUEST_CODE_FILE);
}

The above is the preparatory work. Call up the file manager to select files. For an Android Developer, the above steps are only a small step. Don’t forgetAdaptation

The next step is the long march.

5. URI to path

In this step, the main solution is to convert the returned URI to file, and big pits often appear in this step.

You can find the code from file to file on the Internet, but 8 of the 10 contents from Baidu are the same, and the remaining 2 cannot be read.

However, almost all of the eight articles are the same and do not solve a crucial problemQQ file manager

Maybe you can’t use search engines.

Upper code

/**
 *It is designed for Android 4.4 to obtain the absolute path of the file from URI. The previous method is not easy to use
 */
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {

        //Some third-party file browsers will enter this method, such as es
        //QQ file manager is not in this column

        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];
            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {
        ...
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
        ...
        }
    } else if ("content".equalsIgnoreCase(uri.getScheme())) {// MediaStore (and general)
        return getDataColumn(context, uri, null, null);
    } else if ("file".equalsIgnoreCase(uri.getScheme())) {// File
        ...
    }
    return null;
}

It is found that the mobile phone will have a third-party file manager, such as es, QQ, etcAt present, only these two kinds of apps are exposed, and other apps are not excluded, I believe most of them are good comrades, but not limited to goose factory bosses.

The file selected through QQ browser will be reportedDoes not exist_ Data fieldThis error.

WTF

Stupid.jpg

In the returned URIuri.getAuthority()The value of is not set in manifest.xml itself${applicationId}.fileproviderButcom.tencent.mtt.fileprovider

At this time, the articles searched in front almost didn’t solve this problem.

In view of the limited level, he was trapped for half a day.

Through analysisuri.getPath();Value of.

content://com.tencent.mtt.fileprovider/QQBrowser/demo.mp4

Write down the following code. If you have a better solution, please let me know.

//Judge QQ file manager
if (isQQMediaDocument(uri)) {
    String path = uri.getPath();
    File fileDir = Environment.getExternalStorageDirectory();
    File file = new File(fileDir, path.substring("/QQBrowser".length(), path.length()));
    return file.exists() ? file.toString() : null;
}

Models passing the test: Meizu 15, Samsung 9300, MI6, oppor9, Huawei mate9

The missing code shall be supplemented by itself,
Main class codeFileUtil


Other issues

1. File type setting

Product requirements:

╔══════════════════════════════
║
║ uploading files and videos
║
╚══════════════════════════════

Requirements: 1. Click video to select MP4, 2. Click file to select PDF, word and other office files.

There are two types of intent settings:

  • intent.setType("*/*")
    In this case, the intention is to select an office document. adoptIntent.EXTRA_MIME_TYPESTo limit the file type, but in this case, a third-party file manager will appear, which will not take effect in some cases. All files can be selected.

What I do is, by observing the MIME type, what I set isapplication/*The file management icon of the third party is hidden, and files can only be selected through the file management of the system.

The Android file manager selects a file and obtains the file path URI to file
The Android file manager selects a file and obtains the file path URI to file
  • intent.setType("video/mp4);
  1. This will display the third-party file manager, but will filter out other files, only those of video type. If there is avi type, you need toonActivityResultDetermine the file suffix in.
  2. The file manager of the system will take effect and can only be selectedIntent.EXTRA_MIME_TYPESSet the type of.

2. The problem of returning URI

  • Select a file from the file manager and the URI returned iscontent://com.android.externalstorage.documents/document/primary/update/A5679B1.mp4
  • Select a file from “video” and the returned URI iscontent://com.android.providers.media.documents/document/video:5188

Problems encountered:

Determine whether the file format is the type I set. Ifintent.setType("video/*");However, if you only want files in “MP4” format, thenonActivityResultJudge by the returned data in the previous stageuri.getLastPathsegment(), judge the suffix of the file, but later the test encountered the second case. Select the file from the “video”. At this time, the returned URI does not comply with the rules, so laziness is not allowed. You can only judge the suffix by converting the name of the source file.

The Android file manager selects a file and obtains the file path URI to file