zh пре 10 месеци
родитељ
комит
2c58a55de0

+ 1 - 0
app/src/main/AndroidManifest.xml

@@ -15,6 +15,7 @@
         android:name=".base.BaseApplication"
         android:allowBackup="true"
         android:usesCleartextTraffic="true"
+        android:requestLegacyExternalStorage="true"
         android:dataExtractionRules="@xml/data_extraction_rules"
         android:fullBackupContent="@xml/backup_rules"
         android:icon="@mipmap/ic_logo"

+ 46 - 1
app/src/main/java/com/baoshi/piece/db/dao/DeliveryDao.java

@@ -158,7 +158,7 @@ public class DeliveryDao {
     public int updateDeliveryPushFail(String deliveryNo) {
         SQLiteDatabase db = dbHelper.getWritableDatabase();
         ContentValues values = new ContentValues();
-        values.put("isPush", 2); // 标记为已上传
+        values.put("isPush", 2); //
 
         return db.update(
                 "delivery",
@@ -242,6 +242,50 @@ public class DeliveryDao {
     }
 
 
+    // 获取错误记录数
+    public int getErrRecordsCount() {
+        SQLiteDatabase db = dbHelper.getReadableDatabase();
+        Cursor cursor = null;
+        try {
+            cursor = db.rawQuery("SELECT COUNT(*) FROM delivery WHERE isPush = 2", null);
+            if (cursor != null && cursor.moveToFirst()) {
+                return cursor.getInt(0);
+            }
+            return 0;
+        } finally {
+            if (cursor != null) cursor.close();
+            db.close();
+        }
+    }
+
+    // 查询所有上传异常的记录
+    public List<DeliveryRecord> listErrRecords() {
+        List<DeliveryRecord> records = new ArrayList<>();
+        SQLiteDatabase db = dbHelper.getReadableDatabase();
+
+        Cursor cursor = db.query(
+                "delivery",
+                null,
+                "isPush = 2", // 异常的记录
+                null, null, null,
+                "operationTime ASC" // 按时间升序(最早优先)
+        );
+
+        try {
+            if (cursor != null && cursor.moveToFirst()) {
+                do {
+                    DeliveryRecord record = parseRecordFromCursor(cursor);
+                    records.add(record);
+                } while (cursor.moveToNext());
+            }
+            return records;
+        } finally {
+            if (cursor != null) cursor.close();
+            db.close();
+        }
+    }
+
+
     // 获取所有记录(按时间倒序)
     public List<DeliveryRecord> getAllRecords() {
         List<DeliveryRecord> records = new ArrayList<>();
@@ -265,6 +309,7 @@ public class DeliveryDao {
         return records;
     }
 
+
     // 更新机器信息
     public int updateMachine(String deliveryNo, String newMachine) {
         SQLiteDatabase db = dbHelper.getWritableDatabase();

+ 10 - 0
app/src/main/java/com/baoshi/piece/utils/JavaScriptInterface.java

@@ -141,6 +141,16 @@ public class JavaScriptInterface {
         return SpUtil.getString(SAVE_USER_ID);
     }
 
+    @JavascriptInterface
+    public int getErrRecordsCount() {
+        return deliveryDao.getErrRecordsCount();
+    }
+
+    @JavascriptInterface
+    public String listErrRecords() {
+        return GsonUtil.GsonString(deliveryDao.listErrRecords());
+    }
+
     @JavascriptInterface
     public int updateDeliveryPushFail(String deliveryNo) {
         return deliveryDao.updateDeliveryPushFail(deliveryNo);

+ 156 - 109
app/src/main/java/com/baoshi/piece/utils/MacAddressUtils.java

@@ -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;
     }
-}
+}