391 lines
14 KiB
Java
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();
|
|
}
|
|
} |