// #ifdef APP-PLUS
export function openFileManager() {
return new Promise(async (resolve, reject) => {
try {
await requestPermission();
// 获取应用主Activity实例对象
const main = plus.android.runtimeMainActivity();
// console.log(main);
// 导入Intent类
const Intent = plus.android.importClass('android.content.Intent');
// Intent.ACTION_PICK 图片
// Intent.ACTION_GET_CONTENT 文件 (旧) 用于一次性获取文件的URI,适合于不需要频繁访问文件的场景
// Intent.ACTION_OPEN_DOCUMENT 文件 提供了对文件的长期访问权限,更适合于需要频繁操作文件的应用场景
const intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
// 设置文件类型为任意类型
intent.setType('*/*');
// 多选
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
const REQUEST_CODE = 1;
main.onActivityResult = (requestCode, code, data) => {
// 导入Activity类。
const Activity = plus.android.importClass('android.app.Activity');
plus.android.importClass('android.net.Uri');
// console.log(requestCode, code, Activity.RESULT_OK);
// 选择的文件的路径
let filePathList = [];
// 选择文件
if (code === Activity.RESULT_OK && requestCode === REQUEST_CODE) {
// 导入Uri类。
let Uri = data.getData();
if (!Uri) {
let ClipData = plus.android.importClass('android.content.ClipData');
let clipData = new ClipData();
clipData = data.getClipData();
if (clipData) {
// 返回选择的文件数量。
const count = clipData.getItemCount();
for (let i = 0; i < count; i++) {
const item = clipData.getItemAt(i);
const uri = item.getUri();
const filePath = getFilePath(main, uri);
filePathList.push(filePath);
}
}
} else {
filePathList.push(getFilePath(main, Uri));
}
}
resolve(filePathList);
// if (!filePath.startsWith('file://')) {
// filePath = 'file://' + filePath;
// }
// console.log('filePath => ', filePath);
// 获取文件信息,包括文件大小、文件类型等。
// uni.getFileInfo({
// filePath,
// success: (res) => {
// console.log(res);
// },
// fail: (err) => console.log(err),
// });
};
// 第一个参数是 Intent对象,用于指定要启动的Activity;
// 第二个参数是 请求码(requestCode),用于标识请求来源
main.startActivityForResult(intent, REQUEST_CODE); // 启动一个新的Activity并等待其返回结果
// main.startActivityForResult(Intent.createChooser(intent, 'Select Picture'), 1);
} catch (e) {
reject(e);
}
});
}
/**
* 获取文件路径
* @param {Object} main 应用主Activity实例对象
* @param {Object} Uri 文件路径对象
*/
function getFilePath(main, Uri) {
const Build = plus.android.importClass('android.os.Build');
const DocumentsContract = plus.android.importClass('android.provider.DocumentsContract');
// 是否为文档类型的 URI
const IS_DOUMENT_TYPE = DocumentsContract.isDocumentUri(main, Uri);
const scheme = Uri.getScheme();
// console.log(IS_DOUMENT_TYPE, scheme);
// Build.VERSION.SDK_INT: 当前设备上运行的Android操作系统的API级别
// Build.VERSION_CODES.KITKAT: Android 4.4(KitKat)版本的API级别
// 判断安卓版本是否大于等于4.4(KitKat)
// console.log(Build.VERSION.SDK_INT, Build.VERSION_CODES.KITKAT);
const IS_KITKAT = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
// console.log(IS_KITKAT);
// 设备至少为4.4以上的版本,可以安全地使用某些API
if (IS_KITKAT) {
if (IS_DOUMENT_TYPE) {
// 从 Uri 中提取出文件的 ID
const docId = DocumentsContract.getDocumentId(Uri);
const [type, id] = docId.split(':');
// 获取指定URI的权限部分
const authority = Uri.getAuthority();
// console.log(authority, type, id, docId);
const AUTHORITY_DOCUMENT_TYPE = {
MEDIA: 'com.android.providers.media.documents',
DOWNLOAD: 'com.android.providers.downloads.documents',
EXTERNAL: 'com.android.externalstorage.documents',
};
const authorityFn = {
[AUTHORITY_DOCUMENT_TYPE.DOWNLOAD]: getDownloadDocument,
[AUTHORITY_DOCUMENT_TYPE.MEDIA]: getMediaDocument,
[AUTHORITY_DOCUMENT_TYPE.EXTERNAL]: getExternalDocument,
};
return authorityFn[authority]?.({ main, Uri, docId, type, id });
} else if (scheme === 'content') {
// const authority = Uri.getAuthority();
// console.log(authority);
return getDataColumn(main, Uri);
} else if (scheme === 'file') {
return Uri.getPath();
}
} else {
// 4.4以下
const MediaStore = plus.android.importClass('android.provider.MediaStore');
const columns = [MediaStore.Images.Media.DATA];
const cursor = contentResolver.query(Uri, columns, null, null, null);
if (cursor !== null && cursor.moveToFirst()) {
const column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
const filePath = cursor.getString(column_index);
cursor.close();
return filePath;
}
}
}
/**
* @typedef AuthorityDocumentType 文档类型枚举对象
* @property {Object} main
* @property {Object} Uri 文件路径对象
* @property {String} docId 文件的 ID
* @property {String} type 文件类型,例如 'image'、'video'、'audio'等
* @property {String} id id
*/
/**
* 获取文件路径
* @param {AuthorityDocumentType} options
*/
function getExternalDocument(options) {
const { type, id } = options;
const Environment = plus.android.importClass('android.os.Environment');
let path = '';
if (type === 'primary') {
// 获取外部存储目录的绝对路径。
path = `${Environment.getExternalStorageDirectory()}/${id}`;
} else {
const System = plus.android.importClass('java.lang.System');
path = `${System.getenv('SECONDARY_STORAGE')}/${id}`;
}
return path;
}
/**
* 获取文件路径
* @param {AuthorityDocumentType} options
*/
function getMediaDocument(options) {
const { main, id, type } = options;
const MediaStore = plus.android.importClass('android.provider.MediaStore');
const MEDIA_TYPE = {
image: MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
video: MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
audio: MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
document: MediaStore.Files.getContentUri('external'),
};
const uri = MEDIA_TYPE[type] || null;
return getDataColumn(main, uri, '_id=?', [id]);
}
/**
* 获取文件路径
* @param {AuthorityDocumentType} options
*/
function getDownloadDocument(options) {
const { main, Uri, id } = options;
if (id) {
return id;
}
const ContentUris = plus.android.importClass('android.content.ContentUris');
// 第一个参数 原始的 content URI
// 第二个参数 要附加的 ID
// 返回一个新 URI,其中 ID 已附加到原始 URI。
const originId = Uri.parse('content://downloads/public_downloads');
const newId = ContentUris.parseId(Uri);
const uri = ContentUris.withAppendedId(originId, newId);
return getDataColumn(main, uri);
}
/**
* uri路径转换
* @param {Object} context 应用主Activity实例对象
* @param {Object} uri 文件路径
* @param {String} selection
* @param {Array} selectionArgs 文件的id数组
*/
function getDataColumn(context, uri, selection = null, selectionArgs = null) {
// 导入Uri类。
plus.android.importClass(context.getContentResolver());
const _columns = ['_data']; // 指定要查询的列。
// 查询指定URI的路径。
const Cursor = context.getContentResolver().query(uri, _columns, selection, selectionArgs, null);
// 导入Cursor类。
Cursor && plus.android.importClass(Cursor);
let filePath = '';
// 检查游标是否为空 且 移动到第一行数据。
if (Cursor !== null && Cursor.moveToFirst()) {
// 获取指定列的索引。
const columnIndex = Cursor.getColumnIndexOrThrow(_columns[0]);
// 文件路径
filePath = Cursor.getString(columnIndex);
// 关闭游标。
Cursor.close();
}
return filePath;
}
const PERMISSION_TYPE = {
// 允许
PERMISSION_GRANTED: 0,
// 临时拒绝
PERMISSION_DENIED: 1,
// 永久拒绝
PERMISSION_DENIED_FOREVER: 2,
};
// 申请权限
function requestPermission() {
return new Promise((resolve, reject) => {
plus.android.requestPermissions(
['android.permission.READ_EXTERNAL_STORAGE'],
(e) => {
if (e.deniedAlways.length > 0) {
// 权限被永久拒绝
// 弹出提示框解释为何需要存储权限,引导用户打开设置页面开启
// console.log('Always Denied!!! ' + e.deniedAlways.toString());
reject({
code: PERMISSION_TYPE.PERMISSION_DENIED_FOREVER,
msg: '未授权且不再询问',
});
}
if (e.deniedPresent.length > 0) {
// 权限被临时拒绝
// 弹出提示框解释为何需要存储权限,可再次调用plus.android.requestPermissions申请权限
// console.log('Present Denied!!! ' + e.deniedPresent.toString());
// plus.android.requestPermissions(['android.permission.READ_EXTERNAL_STORAGE']);
reject({
code: PERMISSION_TYPE.PERMISSION_DENIED,
msg: '未授权',
});
}
if (e.granted.length > 0) {
// 权限被允许
// 调用依赖获取存储权限的代码
// console.log('Granted!!! ' + e.granted.toString());
resolve({
code: PERMISSION_TYPE.PERMISSION_GRANTED,
msg: '已授权',
});
}
},
(e) => {
// console.log('Request Permissions error:' + JSON.stringify(e));
reject(e);
}
);
});
}
// #endif