阅读 338

Android10+ 文件用其他方式打开或者分享打开添加自己App

萌新处文,水文一篇,大佬轻拍,大佬轻拍,大佬轻拍

需求比较少见,简单来说是可以在app内打开系统文件选择器,取到对应文件并上传(我这边为Excel,比较少)。同时需要支持在其他应用(文件选择器类)中选择发送/分享或者其他应用打开完成后续上传工作。

知识点在于

  1. 添加自己应用在打开/分享列表

  2. 兼容大部分返回的uri


Android用其他方式打开文件


此功能较为简单。

反编译市场上有此功能的软件,查看其对应xml配置可得到。下为对xls和xlsx文件响应。

ContentType 类型可百度找到

我的参考链接为:blog.csdn.net/u013749540/…

<!--分享/发送-->
<intent-filter>
    <action android:name="android.intent.action.SEND"/>
    <action android:name="android.intent.action.SEND_MULTIPLE"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
    <data android:mimeType="application/vnd.ms-excel" />
    <data android:mimeType="text/comma-separated-values" />
</intent-filter>
<!--在其他应用打开-->
<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="file" />
    <data android:scheme="content" />
    <data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
    <data android:mimeType="application/vnd.ms-excel" />
    <data android:mimeType="text/comma-separated-values" />
</intent-filter>复制代码

另外有如声明具体功能的情况,如保存到XX,收藏到XX,可添加

```
<activity-alias
    android:name="com.ucpro.file"
    android:icon="@mipmap/launcher_ic"
    android:label="导入到XX"
    android:resizeableActivity="false"
    android:targetActivity=".ui.activity.HomeActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="file" />
        <data android:scheme="content" />
        <data android:mimeType="*/*" />
        <data android:pathPattern=".*.*" />
    </intent-filter>
```复制代码

兼容大部分返回的Uri的工具类


我目前看到的不同机子不用情况下返回的uri有:

/external/file/11611

/external_files/Download/xxxx

content://com.android.providers.media.documents/document/document:127686

content://com.android.providers.downloads.documents/document/msf:3A127686

/root/storage/emulated/0/android/data/xxxx

.......

另外,Android11 在QQ浏览器或者微信会话使用没法使用第三方打开,其路径为

/QQBrowser/Android/data/com.tencent.mtt/files/.ReaderTemp/thrdcall/contenturi/xxxx

/external/Android/data/com.tencent.mm/MicroMsg/Download/xxxx

Android11储存更新:developer.android.google.cn/about/versi…

所以无法获取到Android/data/下的文件,目前解决方式为引导用户操作????

有大佬能适配这种情况的,求指点下

根据Uri获取真实FilePath参考实现:stackoverflow.com/questions/1…

工具类代码:

public class FilePathUtils {

    /**
     * <p>
     * 获取完整文件名(包含扩展名)
     *
     * @param filePath
     * @return
     */

    public static String getFilenameWithExtension(String filePath) {
        if (filePath == null || filePath.length() == 0) {
            return "";

        }

        int lastIndex = filePath.lastIndexOf(File.separator);

        String filename = filePath.substring(lastIndex + 1);

        return filename;

    }

    /**
     * 判断文件路径的文件名是否存在文件扩展名 eg: /external/images/media/2283
     *
     * @param filePath
     * @return
     */
    public static boolean isFilePathWithExtension(String filePath) {
        String filename = getFilenameWithExtension(filePath);

        return filename.contains(".");

    }

    /**
     * 判断文件路径的文件名是否存在文件扩展名 eg: /external/images/media/2283
     *
     * @param context
     * @param uri
     * @return
     */
    @RequiresApi(api = Build.VERSION_CODES.Q)
    public static String getPathFromUri(final Context context, final Uri uri) {

        final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

        // DocumentProvider
        if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
            // ExternalStorageProvider
            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];
                }

                // TODO handle non-primary volumes
            }
            // DownloadsProvider
            else if (isDownloadsDocument(uri)) {
                final String id = DocumentsContract.getDocumentId(uri);
                /* 文件浏览器的下载返回的uri为
                 *  content://com.android.providers.downloads.documents/document/msf:3A127686
                 *  Android10以上,getDataColumn中的uri为 MediaStore.Downloads.EXTERNAL_CONTENT_URI
                 */
                if (id.startsWith("msf:")) {
                    final String[] split = id.split(":");
                    final String selection = "_id=?";
                    final String[] selectionArgs = new String[]{split[1]};
                    return getDataColumn(context, MediaStore.Downloads.EXTERNAL_CONTENT_URI, selection, selectionArgs);

                } else {
                    final Uri contentUri = ContentUris.withAppendedId(
                            Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

                    return getDataColumn(context, contentUri, null, null);
                }
            }
            // MediaProvider
            else if (isMediaDocument(uri)) {

                final String docId = DocumentsContract.getDocumentId(uri);
                final String[] split = docId.split(":");
                final String type = split[0];
                Uri contentUri = null;
                /*
                 * 由于是文档类型,所以记得加上document判断,具体的MediaStore项我没有找到,有大佬知道的话,求指点下
                 * */
                if ("image".equals(type)) {
                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
                } else if ("video".equals(type)) {
                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
                } else if ("audio".equals(type)) {
                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
                } else if ("document".equals(type)) {
                    contentUri = MediaStore.Downloads.EXTERNAL_CONTENT_URI;
                }
                final String selection = "_id=?";
                final String[] selectionArgs = new String[]{
                        split[1]
                };
                return getDataColumn(context, contentUri, selection, selectionArgs);
            }
        }
        // MediaStore (and general)
        else if ("content".equalsIgnoreCase(uri.getScheme())) {

            // Return the remote address
            if (isGooglePhotosUri(uri)) {
                return uri.getLastPathSegment();
            }

            return getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }

    public static String getDataColumn(Context context, Uri uri, String selection,
                                       String[] selectionArgs) {
        Cursor cursor = null;
        final String column = "_data";
        final String[] projection = {
                column
        };

        try {
            cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                    null);
            if (cursor != null && cursor.moveToFirst()) {
                final int index = cursor.getColumnIndexOrThrow(column);
                return cursor.getString(index);
            }
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
        return null;
    }


    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is ExternalStorageProvider.
     */
    public static boolean isExternalStorageDocument(Uri uri) {
        return "com.android.externalstorage.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is DownloadsProvider.
     */
    public static boolean isDownloadsDocument(Uri uri) {
        return "com.android.providers.downloads.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is MediaProvider.
     */
    public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    /**
     * @param uri The Uri to check.
     * @return Whether the Uri authority is Google Photos.
     */
    public static boolean isGooglePhotosUri(Uri uri) {
        return "com.google.android.apps.photos.content".equals(uri.getAuthority());
    }
}复制代码

工具类的使用:

文件浏览器回调

 startActivityForResult(intent, (resultCode, data) -> {
        if (resultCode == Activity.RESULT_OK && data != null) {
            Uri uri = data.getData();
            if (uri != null) {
                File file;
                /*
                *判断有无文件拓展名,有则直接调用 http://blankj.com 大神的工具箱,无则表示为content形式调用自己的工具类
                * */
                if (FilePathUtils.isFilePathWithExtension(uri.getPath())) {
                    file = UriUtils.uri2File(uri);
                } else {
                    file = FileUtils.getFileByPath(FilePathUtils.getPathFromUri(getContext(), uri));
                }
                uploadFile(file);
            }
        }
    });
}复制代码

发送/分享,打开也差不多就不反复贴了。

@SuppressLint("ObsoleteSdkInt")
@RequiresApi(api = Build.VERSION_CODES.Q)
private void getShareFromOthers() {
    //分享/发送
    Intent shareIntent = getIntent();
    String action = shareIntent.getAction();
    String type = shareIntent.getType();
    if (Intent.ACTION_SEND.equals(action) && type != null) {
        if ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet".equals(type) || "application/vnd.ms-excel".equals(type)) {
            Uri uri2 = shareIntent.getParcelableExtra(Intent.EXTRA_STREAM);
            if (uri2 != null) {
                String host = uri2.getHost();
                String dataString = shareIntent.getDataString();
                String path = uri2.getPath();
                String path1 = uri2.getEncodedPath();
                /* 华为文件管理器根目录会以/root/storage/emulated/0/方式传,手动剔除/root*/
                if (path.startsWith(getString(R.string.file_root))){
                    path=path.substring(5);
                }
                if (excelShareInput) {
                    File file;
                    /* 判断是否文件是否有后缀*/
                    if(FilePathUtils.isFilePathWithExtension(path)){
                        /* 小米Android11返回为/external_files/xxx,将其替换*/
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                            path = path.replace(getString(R.string.file_storage), PathUtils.getExternalStoragePath());
                            file = FileUtils.getFileByPath(path);
                            Timber.d("file = %s", file);
                        } else {
                            file = FileUtils.getFileByPath(path);
                        }
                    }else {
                        /* 由content获取真实路径*/
                        file =FileUtils.getFileByPath(FilePathUtils.getPathFromUri(getContext(),uri2));
                    }
               
                }
            }
        }
    }复制代码

写到这里。文件用其他方式打开或者分享打开添加自己App和App内找对文件上传算是完事了。当然后面如果有更多的适配方案还会继续补充。????


作者:无常锅
链接:https://juejin.cn/post/7020371661396377637

文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐