/* * Copyright (C) 2016 AriaLyy(https://github.com/AriaLyy/Aria) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.arialyy.aria.util; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.provider.OpenableColumns; import android.text.TextUtils; /** * 分区存储(Scoped Storage)相关 Uri 工具类,提供文件名解析、可写性探测和权限持久化能力。 */ public class UriFileUtil { private static final String TAG = "UriFileUtil"; /** * 从 Uri 中获取文件名 * * @param context 上下文,可为 null(仅用 lastPathSegment 兜底) * @param uri 文档 Uri * @return 文件名,无法获取时返回空串 */ public static String getFileName(Context context, Uri uri) { if (uri == null) { return ""; } String name = queryDisplayName(context, uri); if (!TextUtils.isEmpty(name)) { return name; } String last = uri.getLastPathSegment(); if (!TextUtils.isEmpty(last)) { int idx = last.lastIndexOf('/'); return idx >= 0 ? last.substring(idx + 1) : last; } return ""; } private static String queryDisplayName(Context context, Uri uri) { if (!"content".equalsIgnoreCase(uri.getScheme()) || context == null) { return null; } Cursor cursor = null; try { cursor = context.getContentResolver() .query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null); if (cursor != null && cursor.moveToFirst()) { int idx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); if (idx >= 0) { return cursor.getString(idx); } } } catch (Exception e) { ALog.w(TAG, "query display name error: " + e.getMessage()); } finally { if (cursor != null) { cursor.close(); } } return null; } /** * 测试 Uri 是否可写(以读写方式打开) */ public static boolean canWrite(Context context, Uri uri) { if (context == null || uri == null) { return false; } ParcelFileDescriptor pfd = null; try { pfd = context.getContentResolver().openFileDescriptor(uri, "rw"); return pfd != null; } catch (Exception e) { ALog.w(TAG, "uri canWrite test failed: " + e.getMessage()); return false; } finally { if (pfd != null) { try { pfd.close(); } catch (Exception ignored) { // ignore } } } } /** * 获取 Uri 对应文件的长度 * * @return 文件长度;无法获取(Uri 不存在、无权限、流式内容等)时返回 -1 */ public static long getFileLength(Context context, Uri uri) { if (context == null || uri == null) { return -1; } ParcelFileDescriptor pfd = null; try { pfd = context.getContentResolver().openFileDescriptor(uri, "r"); return pfd == null ? -1 : pfd.getStatSize(); } catch (Exception e) { ALog.w(TAG, "get uri file length failed: " + e.getMessage()); return -1; } finally { if (pfd != null) { try { pfd.close(); } catch (Exception ignored) { // ignore } } } } /** * 尝试持久化 Uri 权限,以便应用重启后仍能访问(SAF 文档树/文件)。 * 若 Uri 不支持持久化权限则静默忽略。 */ public static void tryTakePersistablePermission(Context context, Uri uri) { if (context == null || uri == null) { return; } try { context.getContentResolver() .takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } catch (SecurityException | IllegalArgumentException ignored) { // 部分 Uri(如 MediaStore 插入项)不支持持久化权限,忽略即可 } } }