Files
2025-09-23 20:03:06 +08:00

391 lines
14 KiB
Java

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();
}
}