添加复制的资源
This commit is contained in:
@@ -0,0 +1,391 @@
|
||||
package com.khronos.vulkan_samples;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class AssetsCopyUtil {
|
||||
private static final String TAG = "AssetsCopyUtil";
|
||||
private static final String PREF_NAME = "assets_copy_pref";
|
||||
private static final String PREF_KEY_COPIED_FILES = "copied_files";
|
||||
|
||||
/**
|
||||
* 复制 assets 到应用专属外部存储的 files 目录(递归复制子文件夹)
|
||||
*/
|
||||
public static void copyAssetsToAppFiles(Context context, String assetsPath) {
|
||||
try {
|
||||
AssetManager assetManager = context.getAssets();
|
||||
|
||||
// 获取应用专属外部存储的 files 目录
|
||||
File externalFilesDir = context.getExternalFilesDir(null);
|
||||
if (externalFilesDir == null) {
|
||||
Log.e(TAG, "External files directory is not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// 递归复制
|
||||
copyAssetsRecursive(context, assetManager, assetsPath, externalFilesDir.getAbsolutePath());
|
||||
|
||||
Log.i(TAG, "Assets copy completed for: " + assetsPath);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error copying assets from: " + assetsPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归复制 assets 中的文件和文件夹
|
||||
*/
|
||||
private static void copyAssetsRecursive(Context context, AssetManager assetManager,
|
||||
String assetsPath, String targetBasePath) {
|
||||
try {
|
||||
// 获取 assets 路径下的所有项目
|
||||
String[] items = assetManager.list(assetsPath);
|
||||
if (items == null || items.length == 0) {
|
||||
Log.w(TAG, "No items found in assets path: " + assetsPath);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建目标目录
|
||||
File targetDir = new File(targetBasePath, assetsPath);
|
||||
if (!targetDir.exists() && !targetDir.mkdirs()) {
|
||||
Log.e(TAG, "Failed to create target directory: " + targetDir.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取已复制的文件列表
|
||||
Set<String> copiedFiles = getCopiedFiles(context);
|
||||
boolean hasNewFiles = false;
|
||||
|
||||
for (String item : items) {
|
||||
String assetItemPath = assetsPath.isEmpty() ? item : assetsPath + "/" + item;
|
||||
String fileKey = assetItemPath;
|
||||
|
||||
// 检查是否已经复制过
|
||||
if (copiedFiles.contains(fileKey)) {
|
||||
Log.d(TAG, "Item already copied: " + assetItemPath);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// 判断是文件还是目录的正确方法
|
||||
boolean isFile = isAssetFile(assetManager, assetsPath, item);
|
||||
|
||||
if (isFile) {
|
||||
// 复制文件
|
||||
if (copySingleFile(assetManager, assetItemPath, targetDir, item)) {
|
||||
copiedFiles.add(fileKey);
|
||||
hasNewFiles = true;
|
||||
Log.d(TAG, "Successfully copied file: " + assetItemPath);
|
||||
}
|
||||
} else {
|
||||
// 递归复制子目录
|
||||
copyAssetsRecursive(context, assetManager, assetItemPath, targetBasePath);
|
||||
// 将目录也标记为已处理
|
||||
copiedFiles.add(fileKey);
|
||||
hasNewFiles = true;
|
||||
Log.d(TAG, "Processed directory: " + assetItemPath);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error processing: " + assetItemPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有新文件被复制,更新记录
|
||||
if (hasNewFiles) {
|
||||
saveCopiedFiles(context, copiedFiles);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error in recursive copy for path: " + assetsPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 正确判断 assets 中的项目是文件还是目录
|
||||
*/
|
||||
private static boolean isAssetFile(AssetManager assetManager, String basePath, String item) {
|
||||
try {
|
||||
// 方法1:尝试列出子项,如果能列出且不为空,说明是目录
|
||||
String testPath = basePath.isEmpty() ? item : basePath + "/" + item;
|
||||
String[] subItems = assetManager.list(testPath);
|
||||
|
||||
// 如果子项为空,说明是文件或者是空目录
|
||||
if (subItems == null) {
|
||||
return true; // 应该是文件
|
||||
}
|
||||
|
||||
// 如果有子项,说明是目录
|
||||
if (subItems.length > 0) {
|
||||
return false; // 是目录
|
||||
}
|
||||
|
||||
// 如果子项为空数组,需要进一步判断
|
||||
// 尝试打开它,如果能打开说明是文件,不能打开说明是空目录
|
||||
try {
|
||||
InputStream test = assetManager.open(testPath);
|
||||
test.close();
|
||||
return true; // 能打开,是文件
|
||||
} catch (Exception e) {
|
||||
return false; // 不能打开,是空目录
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error checking asset type: " + item, e);
|
||||
return true; // 默认当作文件处理
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更可靠的递归复制方法
|
||||
*/
|
||||
public static void copyAssetsToAppFilesSimple(Context context, String assetsPath) {
|
||||
try {
|
||||
AssetManager assetManager = context.getAssets();
|
||||
File externalFilesDir = context.getExternalFilesDir(null);
|
||||
if (externalFilesDir == null) {
|
||||
Log.e(TAG, "External storage not available");
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建目标目录
|
||||
File targetBaseDir = new File(externalFilesDir, assetsPath);
|
||||
if (!targetBaseDir.exists() && !targetBaseDir.mkdirs()) {
|
||||
Log.e(TAG, "Failed to create base directory");
|
||||
return;
|
||||
}
|
||||
|
||||
copyAssetsRecursiveSimple(assetManager, assetsPath, targetBaseDir.getAbsolutePath());
|
||||
Log.i(TAG, "Simple copy completed for: " + assetsPath);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error in simple copy", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyAssetsRecursiveSimple(AssetManager assetManager, String assetsPath, String targetPath) {
|
||||
try {
|
||||
String[] items = assetManager.list(assetsPath);
|
||||
if (items == null || items.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String item : items) {
|
||||
String fullAssetPath = assetsPath.isEmpty() ? item : assetsPath + "/" + item;
|
||||
|
||||
// 判断是文件还是目录
|
||||
boolean isFile = isAssetFileSimple(assetManager, fullAssetPath);
|
||||
|
||||
if (isFile) {
|
||||
// 复制文件
|
||||
copySingleFileSimple(assetManager, fullAssetPath, targetPath, item);
|
||||
Log.d(TAG, "Copied file: " + fullAssetPath + " to " + targetPath);
|
||||
} else {
|
||||
// 创建子目录并递归复制
|
||||
File subDir = new File(targetPath, item);
|
||||
if (!subDir.exists()) {
|
||||
subDir.mkdirs();
|
||||
}
|
||||
copyAssetsRecursiveSimple(assetManager, fullAssetPath, subDir.getAbsolutePath());
|
||||
Log.d(TAG, "Processed directory: " + fullAssetPath);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error in simple recursive copy: " + assetsPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isAssetFileSimple(AssetManager assetManager, String path) {
|
||||
try {
|
||||
String[] subItems = assetManager.list(path);
|
||||
if (subItems == null) return true; // 应该是文件
|
||||
|
||||
if (subItems.length > 0) return false; // 有子项,是目录
|
||||
|
||||
// 空数组,尝试打开判断
|
||||
try {
|
||||
InputStream test = assetManager.open(path);
|
||||
test.close();
|
||||
return true; // 能打开,是文件
|
||||
} catch (Exception e) {
|
||||
return false; // 不能打开,是空目录
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return true; // 出错时默认当作文件
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制单个文件
|
||||
*/
|
||||
private static boolean copySingleFile(AssetManager assetManager, String assetFilePath, File targetDir, String filename) {
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
|
||||
try {
|
||||
File outFile = new File(targetDir, filename);
|
||||
in = assetManager.open(assetFilePath);
|
||||
out = new FileOutputStream(outFile);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
Log.d(TAG, "File copied: " + outFile.getAbsolutePath());
|
||||
return true;
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Failed to copy file: " + assetFilePath, e);
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (in != null) in.close();
|
||||
if (out != null) {
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error closing streams", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void copySingleFileSimple(AssetManager assetManager, String assetFilePath,
|
||||
String targetPath, String filename) {
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
|
||||
try {
|
||||
File outFile = new File(targetPath, filename);
|
||||
in = assetManager.open(assetFilePath);
|
||||
out = new FileOutputStream(outFile);
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error copying: " + assetFilePath, e);
|
||||
} finally {
|
||||
try {
|
||||
if (in != null) in.close();
|
||||
if (out != null) {
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error closing streams", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调试方法:打印 assets 结构
|
||||
*/
|
||||
public static void printAssetsStructure(Context context, String path) {
|
||||
try {
|
||||
Log.d(TAG, "=== Assets Structure for: " + path + " ===");
|
||||
AssetManager am = context.getAssets();
|
||||
printAssetsRecursive(am, path, 0);
|
||||
Log.d(TAG, "=== End of Assets Structure ===");
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error printing assets structure", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void printAssetsRecursive(AssetManager am, String path, int depth) {
|
||||
try {
|
||||
String indent = "";
|
||||
for (int i = 0; i < depth; i++) indent += " ";
|
||||
|
||||
String[] items = am.list(path);
|
||||
if (items != null) {
|
||||
for (String item : items) {
|
||||
String fullPath = path.isEmpty() ? item : path + "/" + item;
|
||||
boolean isFile = isAssetFileSimple(am, fullPath);
|
||||
|
||||
if (isFile) {
|
||||
Log.d(TAG, indent + "📄 " + item + " (file)");
|
||||
} else {
|
||||
Log.d(TAG, indent + "📁 " + item + " (dir)");
|
||||
printAssetsRecursive(am, fullPath, depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error in recursive print", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已复制的文件列表
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Set<String> getCopiedFiles(Context context) {
|
||||
return context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.getStringSet(PREF_KEY_COPIED_FILES, new HashSet<String>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存已复制的文件列表
|
||||
*/
|
||||
private static void saveCopiedFiles(Context context, Set<String> copiedFiles) {
|
||||
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.putStringSet(PREF_KEY_COPIED_FILES, new HashSet<>(copiedFiles))
|
||||
.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空复制记录
|
||||
*/
|
||||
public static void clearCopyRecords(Context context) {
|
||||
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.remove(PREF_KEY_COPIED_FILES)
|
||||
.apply();
|
||||
Log.d(TAG, "Copy records cleared");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查外部存储是否可用
|
||||
*/
|
||||
public static boolean isExternalStorageAvailable() {
|
||||
String state = Environment.getExternalStorageState();
|
||||
return Environment.MEDIA_MOUNTED.equals(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取目标文件的完整路径
|
||||
*/
|
||||
public static String getFilePath(Context context, String relativePath) {
|
||||
File externalFilesDir = context.getExternalFilesDir(null);
|
||||
if (externalFilesDir == null) return null;
|
||||
|
||||
File targetFile = new File(externalFilesDir, relativePath);
|
||||
return targetFile.getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
*/
|
||||
public static boolean fileExists(Context context, String relativePath) {
|
||||
File externalFilesDir = context.getExternalFilesDir(null);
|
||||
if (externalFilesDir == null) return false;
|
||||
|
||||
File targetFile = new File(externalFilesDir, relativePath);
|
||||
return targetFile.exists();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user