Android10+ 文件用其他方式打开或者分享打开添加自己App
萌新处文,水文一篇,大佬轻拍,大佬轻拍,大佬轻拍
需求比较少见,简单来说是可以在app内打开系统文件选择器,取到对应文件并上传(我这边为Excel,比较少)。同时需要支持在其他应用(文件选择器类)中选择发送/分享或者其他应用打开完成后续上传工作。
知识点在于
添加自己应用在打开/分享列表
兼容大部分返回的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