|
|
@@ -1,7 +1,9 @@
|
|
|
package com.baoshi.piece.utils;
|
|
|
|
|
|
+import android.content.ContentUris;
|
|
|
import android.content.ContentValues;
|
|
|
import android.content.Context;
|
|
|
+import android.database.Cursor;
|
|
|
import android.net.Uri;
|
|
|
import android.os.Environment;
|
|
|
import android.provider.MediaStore;
|
|
|
@@ -9,156 +11,201 @@ import android.content.ContentResolver;
|
|
|
import android.os.Build;
|
|
|
import android.util.Log;
|
|
|
|
|
|
-import com.yechaoa.yutils.GsonUtil;
|
|
|
+import com.baoshi.piece.BuildConfig;
|
|
|
|
|
|
+import java.io.BufferedReader;
|
|
|
+import java.io.File;
|
|
|
+import java.io.FileInputStream;
|
|
|
+import java.io.FileOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
import java.io.InputStream;
|
|
|
|
|
|
+import java.io.InputStreamReader;
|
|
|
import java.io.OutputStream;
|
|
|
-
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
|
|
|
|
|
|
public class MacAddressUtils {
|
|
|
- private static final String TAG = "MacAddressManager";
|
|
|
+ private static final String TAG = "MacAddressUtils";
|
|
|
private static final String DIR_NAME = "MyAppData";
|
|
|
private static final String FILE_NAME = "mac_address.txt";
|
|
|
private static final String MIME_TYPE = "text/plain";
|
|
|
+ private static final String RELATIVE_PATH = Environment.DIRECTORY_DOCUMENTS + "/" + DIR_NAME;
|
|
|
+ private static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID;
|
|
|
+ private static final String SAFE_RELATIVE_PATH = "/Android/data/" + PACKAGE_NAME + "/files/" + DIR_NAME;
|
|
|
|
|
|
- // 保存到公共的 Documents/MyAppData 目录
|
|
|
+ // 保存MAC地址(彻底解决文件残留问题)
|
|
|
public static boolean saveMacAddress(Context context, String mac) {
|
|
|
- try {
|
|
|
- deleteMacAddress(context); // 删除已保存的文件(可选)
|
|
|
- ContentResolver resolver = context.getContentResolver();
|
|
|
+ // 1. 彻底清理所有历史文件(包含自动编号变体)
|
|
|
+ deleteAllMacAddressFiles(context);
|
|
|
|
|
|
- // 构建文件元数据
|
|
|
- ContentValues values = new ContentValues();
|
|
|
- values.put(MediaStore.MediaColumns.DISPLAY_NAME, FILE_NAME);
|
|
|
- values.put(MediaStore.MediaColumns.MIME_TYPE, MIME_TYPE);
|
|
|
- values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS + "/" + DIR_NAME);
|
|
|
+ // 2. 使用双重存储方案(优先私有目录,备用MediaStore)
|
|
|
+ if (saveToPrivateStorage(context, mac)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
|
|
|
- // Android 10+ 需要明确指定 IS_PENDING 状态
|
|
|
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
- values.put(MediaStore.MediaColumns.IS_PENDING, 1);
|
|
|
- }
|
|
|
+ return saveToMediaStore(context, mac);
|
|
|
+ }
|
|
|
|
|
|
- // 获取或创建文件 Uri
|
|
|
- Uri fileUri = resolver.insert(MediaStore.Files.getContentUri("external"), values);
|
|
|
- if (fileUri == null) {
|
|
|
- Log.e(TAG, "Failed to create file Uri");
|
|
|
- return false;
|
|
|
- }
|
|
|
+ // 读取MAC地址(精确匹配)
|
|
|
+ public static String readMacAddress(Context context) {
|
|
|
+ // 1. 优先读取私有存储
|
|
|
+ String result = readFromPrivateStorage(context);
|
|
|
+ if (result != null) return result;
|
|
|
|
|
|
- try (OutputStream os = resolver.openOutputStream(fileUri)) {
|
|
|
- if (os == null) {
|
|
|
- Log.e(TAG, "Failed to open output stream");
|
|
|
- return false;
|
|
|
- }
|
|
|
- os.write(mac.getBytes());
|
|
|
+ // 2. 备用方案读取MediaStore
|
|
|
+ return readFromMediaStore(context);
|
|
|
+ }
|
|
|
|
|
|
- // Android 10+ 需要更新 IS_PENDING 状态
|
|
|
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
- values.clear();
|
|
|
- values.put(MediaStore.MediaColumns.IS_PENDING, 0);
|
|
|
- resolver.update(fileUri, values, null, null);
|
|
|
- }
|
|
|
- return true;
|
|
|
+ // 彻底删除所有相关文件(包含自动编号变体)
|
|
|
+ private static void deleteAllMacAddressFiles(Context context) {
|
|
|
+ // 1. 清理私有存储
|
|
|
+ File privateDir = context.getExternalFilesDir(null);
|
|
|
+ if (privateDir != null) {
|
|
|
+ deleteDirectory(privateDir, FILE_NAME);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 清理MediaStore
|
|
|
+ ContentResolver resolver = context.getContentResolver();
|
|
|
+ Uri collection = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
|
|
|
+
|
|
|
+ String selection = MediaStore.MediaColumns.RELATIVE_PATH + " LIKE ? AND " +
|
|
|
+ MediaStore.MediaColumns.DISPLAY_NAME + " LIKE ?";
|
|
|
+ String[] selectionArgs = new String[]{
|
|
|
+ RELATIVE_PATH + "%",
|
|
|
+ FILE_NAME.replace(".txt", "%") + "%"
|
|
|
+ };
|
|
|
+
|
|
|
+ try (Cursor cursor = resolver.query(collection,
|
|
|
+ new String[]{MediaStore.MediaColumns._ID},
|
|
|
+ selection,
|
|
|
+ selectionArgs,
|
|
|
+ null)) {
|
|
|
+ while (cursor.moveToNext()) {
|
|
|
+ long id = cursor.getLong(0);
|
|
|
+ resolver.delete(ContentUris.withAppendedId(collection, id), null, null);
|
|
|
}
|
|
|
- } catch (Exception e) {
|
|
|
- Log.e(TAG, "Save failed: " + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 私有存储方案(卸载自动清理)
|
|
|
+ private static boolean saveToPrivateStorage(Context context, String mac) {
|
|
|
+ File dir = context.getExternalFilesDir(null);
|
|
|
+ if (dir == null) return false;
|
|
|
+
|
|
|
+ File file = new File(dir, FILE_NAME);
|
|
|
+ try (FileOutputStream fos = new FileOutputStream(file)) {
|
|
|
+ fos.write(mac.getBytes(StandardCharsets.UTF_8));
|
|
|
+ return true;
|
|
|
+ } catch (IOException e) {
|
|
|
+ Log.e(TAG, "Private save failed: " + e.getMessage());
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 从公共目录读取
|
|
|
- public static String readMacAddress(Context context) {
|
|
|
- try {
|
|
|
- ContentResolver resolver = context.getContentResolver();
|
|
|
- Uri collection = MediaStore.Files.getContentUri("external");
|
|
|
-
|
|
|
- // 构建查询条件
|
|
|
- String selection = MediaStore.MediaColumns.RELATIVE_PATH + " LIKE ? AND " +
|
|
|
- MediaStore.MediaColumns.DISPLAY_NAME + " = ?";
|
|
|
- String[] selectionArgs = new String[]{
|
|
|
- "%" + DIR_NAME + "%", // 路径包含目录名
|
|
|
- FILE_NAME
|
|
|
- };
|
|
|
-
|
|
|
- // 执行查询
|
|
|
- try (android.database.Cursor cursor = resolver.query(
|
|
|
- collection,
|
|
|
- new String[]{MediaStore.MediaColumns._ID},
|
|
|
- selection,
|
|
|
- selectionArgs,
|
|
|
- null
|
|
|
- )) {
|
|
|
- Log.e(TAG, "cursor count: " + GsonUtil.GsonString(cursor));
|
|
|
- if (cursor == null || !cursor.moveToFirst()) {
|
|
|
- Log.d(TAG, "File not found");
|
|
|
- return null;
|
|
|
- }
|
|
|
+ // 私有存储读取
|
|
|
+ private static String readFromPrivateStorage(Context context) {
|
|
|
+ File dir = context.getExternalFilesDir(null);
|
|
|
+ if (dir == null) return null;
|
|
|
|
|
|
- // 获取文件 Uri
|
|
|
- long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID));
|
|
|
- Uri fileUri = Uri.withAppendedPath(collection, String.valueOf(id));
|
|
|
+ File file = new File(dir, FILE_NAME);
|
|
|
+ if (!file.exists()) return null;
|
|
|
|
|
|
- // 读取文件内容
|
|
|
- try (InputStream is = resolver.openInputStream(fileUri)) {
|
|
|
- if (is == null) {
|
|
|
- Log.e(TAG, "Failed to open input stream");
|
|
|
- return null;
|
|
|
- }
|
|
|
- byte[] buffer = new byte[is.available()];
|
|
|
- is.read(buffer);
|
|
|
- return new String(buffer);
|
|
|
- }
|
|
|
- }
|
|
|
- } catch (Exception e) {
|
|
|
- Log.e(TAG, "Read failed: " + e.getMessage());
|
|
|
+ try (FileInputStream fis = new FileInputStream(file)) {
|
|
|
+ byte[] buffer = new byte[(int) file.length()];
|
|
|
+ fis.read(buffer);
|
|
|
+ return new String(buffer, StandardCharsets.UTF_8);
|
|
|
+ } catch (IOException e) {
|
|
|
+ Log.e(TAG, "Private read failed: " + e.getMessage());
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 删除已保存的文件(可选)
|
|
|
- public static boolean deleteMacAddress(Context context) {
|
|
|
- try {
|
|
|
- ContentResolver resolver = context.getContentResolver();
|
|
|
- Uri fileUri = findFileUri(context);
|
|
|
- if (fileUri != null) {
|
|
|
- return resolver.delete(fileUri, null, null) > 0;
|
|
|
- }
|
|
|
+ // MediaStore方案(兼容旧设备)
|
|
|
+ private static boolean saveToMediaStore(Context context, String mac) {
|
|
|
+ ContentResolver resolver = context.getContentResolver();
|
|
|
+ Uri collection = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
|
|
|
+
|
|
|
+ ContentValues values = new ContentValues();
|
|
|
+ values.put(MediaStore.MediaColumns.DISPLAY_NAME, FILE_NAME);
|
|
|
+ values.put(MediaStore.MediaColumns.MIME_TYPE, MIME_TYPE);
|
|
|
+ values.put(MediaStore.MediaColumns.RELATIVE_PATH, SAFE_RELATIVE_PATH);
|
|
|
+
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ values.put(MediaStore.MediaColumns.IS_PENDING, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ Uri fileUri = resolver.insert(collection, values);
|
|
|
+ if (fileUri == null) {
|
|
|
+ Log.e(TAG, "MediaStore insert failed");
|
|
|
return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ try (OutputStream os = resolver.openOutputStream(fileUri)) {
|
|
|
+ if (os == null) {
|
|
|
+ resolver.delete(fileUri, null, null);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ os.write(mac.getBytes(StandardCharsets.UTF_8));
|
|
|
+
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ ContentValues updateValues = new ContentValues();
|
|
|
+ updateValues.put(MediaStore.MediaColumns.IS_PENDING, 0);
|
|
|
+ resolver.update(fileUri, updateValues, null, null);
|
|
|
+ }
|
|
|
+ return true;
|
|
|
} catch (Exception e) {
|
|
|
- Log.e(TAG, "Delete failed: " + e.getMessage());
|
|
|
+ Log.e(TAG, "MediaStore save failed: " + e.getMessage());
|
|
|
+ resolver.delete(fileUri, null, null);
|
|
|
return false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 辅助方法:查找文件 Uri
|
|
|
- private static Uri findFileUri(Context context) {
|
|
|
+ // MediaStore读取
|
|
|
+ private static String readFromMediaStore(Context context) {
|
|
|
ContentResolver resolver = context.getContentResolver();
|
|
|
- Uri collection = MediaStore.Files.getContentUri("external");
|
|
|
+ Uri collection = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL);
|
|
|
|
|
|
- String[] projection = new String[]{MediaStore.MediaColumns._ID};
|
|
|
- String selection = MediaStore.MediaColumns.RELATIVE_PATH + " LIKE ? AND " +
|
|
|
+ String selection = MediaStore.MediaColumns.RELATIVE_PATH + " = ? AND " +
|
|
|
MediaStore.MediaColumns.DISPLAY_NAME + " = ?";
|
|
|
- String[] selectionArgs = new String[]{
|
|
|
- "%" + DIR_NAME + "%",
|
|
|
- FILE_NAME
|
|
|
- };
|
|
|
+ String[] selectionArgs = new String[]{SAFE_RELATIVE_PATH, FILE_NAME};
|
|
|
|
|
|
- try (android.database.Cursor cursor = resolver.query(
|
|
|
- collection,
|
|
|
- projection,
|
|
|
+ try (Cursor cursor = resolver.query(collection,
|
|
|
+ new String[]{MediaStore.MediaColumns.DATA},
|
|
|
selection,
|
|
|
selectionArgs,
|
|
|
- null
|
|
|
- )) {
|
|
|
- if (cursor != null && cursor.moveToFirst()) {
|
|
|
- long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID));
|
|
|
- return Uri.withAppendedPath(collection, String.valueOf(id));
|
|
|
+ null)) {
|
|
|
+ if (cursor == null || !cursor.moveToFirst()) return null;
|
|
|
+
|
|
|
+ String path = cursor.getString(0);
|
|
|
+ File file = new File(path);
|
|
|
+ if (!file.exists()) return null;
|
|
|
+
|
|
|
+ try (FileInputStream fis = new FileInputStream(file)) {
|
|
|
+ byte[] buffer = new byte[(int) file.length()];
|
|
|
+ fis.read(buffer);
|
|
|
+ return new String(buffer, StandardCharsets.UTF_8);
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
- Log.e(TAG, "Find file failed: " + e.getMessage());
|
|
|
+ Log.e(TAG, "MediaStore read failed: " + e.getMessage());
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 递归删除目录
|
|
|
+ private static void deleteDirectory(File dir, String targetName) {
|
|
|
+ File[] files = dir.listFiles();
|
|
|
+ if (files != null) {
|
|
|
+ for (File file : files) {
|
|
|
+ if (file.isDirectory()) {
|
|
|
+ deleteDirectory(file, targetName);
|
|
|
+ } else if (file.getName().contains(targetName)) {
|
|
|
+ if (file.delete()) {
|
|
|
+ Log.d(TAG, "Deleted file: " + file.getAbsolutePath());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- return null;
|
|
|
}
|
|
|
-}
|
|
|
+}
|