Browse Source

[New]图片搜索新增机型适配

zhipeng 1 year ago
parent
commit
4730c28866

+ 145 - 12
app/src/main/java/com/datarecovery/master/utils/ImageDeepDetector.java

@@ -42,6 +42,9 @@ public class ImageDeepDetector {
     private static final int GALLERY_CACHE = 3;
     private static final int GALLERY_CACHE = 3;
     private static final int JPG_MAGIC = 4;
     private static final int JPG_MAGIC = 4;
     private static final int OPPO_GALLERY_CACHE = 5;
     private static final int OPPO_GALLERY_CACHE = 5;
+    private static final int VIVO_GALLERY_CACHE = 6;
+    private static final int XIAOMI_GALLERY_CACHE = 7;
+    private static final int MEIZU_GALLERY_CACHE = 8;
 
 
     public static Flowable<ImageFile> detect(Context context) {
     public static Flowable<ImageFile> detect(Context context) {
         return Flowable.create((FlowableOnSubscribe<XFile>) emitter -> {
         return Flowable.create((FlowableOnSubscribe<XFile>) emitter -> {
@@ -84,21 +87,38 @@ public class ImageDeepDetector {
                     switch (tag) {
                     switch (tag) {
                         case JPG_MAGIC:
                         case JPG_MAGIC:
                         case IMAGE_SUFFIX:
                         case IMAGE_SUFFIX:
-                            return Flowable.just(new ImageFile(xFile));
+                        case XIAOMI_GALLERY_CACHE:
+                            return Flowable.just(new ImageFile(xFile, ImageFile.CATEGORY_GALLERY));
                         case WECHAT_CACHE:
                         case WECHAT_CACHE:
                             return detectWechatCache(context, xFile);
                             return detectWechatCache(context, xFile);
                         case GALLERY_CACHE:
                         case GALLERY_CACHE:
                             return detectGalleryCache(context, xFile);
                             return detectGalleryCache(context, xFile);
                         case OPPO_GALLERY_CACHE:
                         case OPPO_GALLERY_CACHE:
                             return detectOppoGalleryCache(context, xFile);
                             return detectOppoGalleryCache(context, xFile);
+                        case VIVO_GALLERY_CACHE:
+                            return detectVivoGalleryCache(context, xFile);
+                        case MEIZU_GALLERY_CACHE:
+                            return detectMeizuGalleryCache(context, xFile);
                         default:
                         default:
                             return Flowable.empty();
                             return Flowable.empty();
                     }
                     }
                 });
                 });
     }
     }
 
 
-    private static Publisher<ImageFile> detectOppoGalleryCache(Context context, XFile xFile) {
-        return new GenericImgCollectionDetector(context, xFile)
+    private static Flowable<ImageFile> detectMeizuGalleryCache(Context context, XFile xFile) {
+        return new GenericImgCollectionDetector(context, xFile, ImageFile.CATEGORY_GALLERY)
+                .subscribeOn(Schedulers.io())
+                .onErrorComplete();
+    }
+
+    private static Flowable<ImageFile> detectVivoGalleryCache(Context context, XFile xFile) {
+        return new GenericImgCollectionDetector(context, xFile, ImageFile.CATEGORY_GALLERY)
+                .subscribeOn(Schedulers.io())
+                .onErrorComplete();
+    }
+
+    private static Flowable<ImageFile> detectOppoGalleryCache(Context context, XFile xFile) {
+        return new GenericImgCollectionDetector(context, xFile, ImageFile.CATEGORY_GALLERY)
                 .subscribeOn(Schedulers.io())
                 .subscribeOn(Schedulers.io())
                 .onErrorComplete();
                 .onErrorComplete();
     }
     }
@@ -152,6 +172,18 @@ public class ImageDeepDetector {
                 file.setTag(OPPO_GALLERY_CACHE);
                 file.setTag(OPPO_GALLERY_CACHE);
                 return true;
                 return true;
             }
             }
+            if (isVivoGalleryCacheFile(path)) {
+                file.setTag(VIVO_GALLERY_CACHE);
+                return true;
+            }
+            if (isXiaomiGalleryCacheFile(path)) {
+                file.setTag(XIAOMI_GALLERY_CACHE);
+                return true;
+            }
+            if (isMeizuGalleryCacheFile(path)) {
+                file.setTag(MEIZU_GALLERY_CACHE);
+                return true;
+            }
         } catch (Exception ignore) {
         } catch (Exception ignore) {
         }
         }
         if (hasJpgMagic(file)) {
         if (hasJpgMagic(file)) {
@@ -218,6 +250,40 @@ public class ImageDeepDetector {
                 || path.contains("tilecache");
                 || path.contains("tilecache");
     }
     }
 
 
+    private static boolean isVivoGalleryCacheFile(String path) {
+        if (TextUtils.isEmpty(path)) {
+            return false;
+        }
+        if (!path.contains("com.vivo.gallery%2Fcache") &&
+                !path.contains("com.vivo.gallery/cache")) {
+            return false;
+        }
+        return path.contains("imgcache") || path.contains("trackthumbnail_cache");
+    }
+
+    private static boolean isXiaomiGalleryCacheFile(String path) {
+        if (TextUtils.isEmpty(path)) {
+            return false;
+        }
+        if (!path.contains("com.miui.gallery%2Ffiles%2Fgallery_disk_cache") &&
+                !path.contains("com.miui.gallery/files/gallery_disk_cache")) {
+            return false;
+        }
+        return path.contains("full_size") || path.contains("small_size");
+    }
+
+    private static boolean isMeizuGalleryCacheFile(String path) {
+        if (TextUtils.isEmpty(path)) {
+            return false;
+        }
+        if (!path.contains("com.meizu.media.gallery%2Fcache") &&
+                !path.contains("com.meizu.media.gallery/cache")) {
+            return false;
+        }
+        return path.contains("bestPhotoCache") || path.contains("face_thumbnails")
+                || path.contains("uri_thumbnail_cache");
+    }
+
     private static boolean isImageSuffix(String name) {
     private static boolean isImageSuffix(String name) {
         if (TextUtils.isEmpty(name)) {
         if (TextUtils.isEmpty(name)) {
             return false;
             return false;
@@ -290,12 +356,24 @@ public class ImageDeepDetector {
     }
     }
 
 
     public static class ImageFile {
     public static class ImageFile {
+        public static int CATEGORY_UNKNOWN = -1;
+        public static int CATEGORY_OTHER = 0;
+        public static int CATEGORY_QQ = 1;
+        public static int CATEGORY_WECHAT = 2;
+        public static int CATEGORY_GALLERY = 3;
         private final XFile xFile;
         private final XFile xFile;
         private String name;
         private String name;
         private long size;
         private long size;
         private Uri uri;
         private Uri uri;
+        private String path;
+        private int category;
 
 
         public ImageFile(XFile xFile) {
         public ImageFile(XFile xFile) {
+            this(xFile, CATEGORY_UNKNOWN);
+        }
+
+        public ImageFile(XFile xFile, int category) {
+            this.category = category;
             this.xFile = xFile;
             this.xFile = xFile;
             try {
             try {
                 this.name = xFile.getName();
                 this.name = xFile.getName();
@@ -309,6 +387,10 @@ public class ImageDeepDetector {
                 this.uri = xFile.getUri();
                 this.uri = xFile.getUri();
             } catch (Exception ignore) {
             } catch (Exception ignore) {
             }
             }
+            try {
+                this.path = xFile.getPath();
+            } catch (Exception ignore) {
+            }
         }
         }
 
 
         public String getName() {
         public String getName() {
@@ -326,6 +408,55 @@ public class ImageDeepDetector {
         public InputStream newInputStream() throws Exception {
         public InputStream newInputStream() throws Exception {
             return xFile.newInputStream();
             return xFile.newInputStream();
         }
         }
+
+        public boolean delete() throws Exception {
+            return xFile.delete();
+        }
+
+        public int getCategory() {
+            if (category != CATEGORY_UNKNOWN) {
+                return category;
+            }
+            if (isGallery()) {
+                return CATEGORY_GALLERY;
+            } else if (isWechat()) {
+                return CATEGORY_WECHAT;
+            } else if (isQQ()) {
+                return CATEGORY_QQ;
+            } else {
+                return CATEGORY_OTHER;
+            }
+        }
+
+        private boolean isQQ() {
+            if (TextUtils.isEmpty(path)) {
+                return false;
+            }
+            return path.contains("com.tencent.mobileqq") || path.contains("com.tencent.tim");
+        }
+
+        private boolean isWechat() {
+            if (!TextUtils.isEmpty(path) && path.contains("com.tencent.mm")) {
+                return true;
+            }
+            return false;
+        }
+
+        private boolean isGallery() {
+            if (!TextUtils.isEmpty(path) && (
+                    path.contains("com.android.gallery3d") ||
+                            path.contains("com.coloros.gallery3d") ||
+                            path.contains("com.vivo.gallery") ||
+                            path.contains("com.miui.gallery") ||
+                            path.contains("com.meizu.media.gallery") ||
+                            path.contains("com.oppo.gallery3d") ||
+                            path.contains("com.android.gallery") ||
+                            path.contains("DCIM")
+            )) {
+                return true;
+            }
+            return false;
+        }
     }
     }
 
 
     private static class WechatCacheDetector extends Flowable<ImageFile> {
     private static class WechatCacheDetector extends Flowable<ImageFile> {
@@ -382,7 +513,7 @@ public class ImageDeepDetector {
                             if (imageBytes.get(imageBytes.size() - 2) == (byte) 0xFF && imageBytes.get(imageBytes.size() - 1) == (byte) 0xD9) {
                             if (imageBytes.get(imageBytes.size() - 2) == (byte) 0xFF && imageBytes.get(imageBytes.size() - 1) == (byte) 0xD9) {
                                 File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                                 File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
-                                    subscriber.onNext(new ImageFile(new XPathFile(context, cache)));
+                                    subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_WECHAT));
                                 }
                                 }
                             }
                             }
                             imageBytes.clear();
                             imageBytes.clear();
@@ -396,7 +527,7 @@ public class ImageDeepDetector {
                                 imageBytes.remove(imageBytes.size() - 1);
                                 imageBytes.remove(imageBytes.size() - 1);
                                 File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                                 File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
-                                    subscriber.onNext(new ImageFile(new XPathFile(context, cache)));
+                                    subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_WECHAT));
                                 }
                                 }
                                 imageBytes.clear();
                                 imageBytes.clear();
                                 imageBytes.add((byte) 0xFF);
                                 imageBytes.add((byte) 0xFF);
@@ -417,7 +548,7 @@ public class ImageDeepDetector {
             File[] files = targetCaches.exists() && targetCaches.isDirectory() ? targetCaches.listFiles() : null;
             File[] files = targetCaches.exists() && targetCaches.isDirectory() ? targetCaches.listFiles() : null;
             if (files != null && files.length > 0) {
             if (files != null && files.length > 0) {
                 for (File file : files) {
                 for (File file : files) {
-                    subscriber.onNext(new ImageFile(new XPathFile(this.context, file)));
+                    subscriber.onNext(new ImageFile(new XPathFile(this.context, file), ImageFile.CATEGORY_WECHAT));
                 }
                 }
                 return true;
                 return true;
             }
             }
@@ -555,7 +686,7 @@ public class ImageDeepDetector {
                         }
                         }
                         File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                         File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                         if (cache.createNewFile() && bytes2File(cropData, cache)) {
                         if (cache.createNewFile() && bytes2File(cropData, cache)) {
-                            subscriber.onNext(new ImageFile(new XPathFile(context, cache)));
+                            subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_GALLERY));
                         }
                         }
                     } catch (Exception ignore) {
                     } catch (Exception ignore) {
                     }
                     }
@@ -574,7 +705,7 @@ public class ImageDeepDetector {
             File[] files = targetCaches.exists() && targetCaches.isDirectory() ? targetCaches.listFiles() : null;
             File[] files = targetCaches.exists() && targetCaches.isDirectory() ? targetCaches.listFiles() : null;
             if (files != null && files.length > 0) {
             if (files != null && files.length > 0) {
                 for (File file : files) {
                 for (File file : files) {
-                    subscriber.onNext(new ImageFile(new XPathFile(this.context, file)));
+                    subscriber.onNext(new ImageFile(new XPathFile(this.context, file), ImageFile.CATEGORY_GALLERY));
                 }
                 }
                 return true;
                 return true;
             }
             }
@@ -845,13 +976,15 @@ public class ImageDeepDetector {
 
 
     private static class GenericImgCollectionDetector extends Flowable<ImageFile> {
     private static class GenericImgCollectionDetector extends Flowable<ImageFile> {
 
 
+        private final int category;
         private String CACHE_DOMAIN = "generic_img_collection_detector";
         private String CACHE_DOMAIN = "generic_img_collection_detector";
         private final Context context;
         private final Context context;
         private final XFile xFile;
         private final XFile xFile;
 
 
-        public GenericImgCollectionDetector(Context context, XFile xFile) {
+        public GenericImgCollectionDetector(Context context, XFile xFile, int category) {
             this.context = context;
             this.context = context;
             this.xFile = xFile;
             this.xFile = xFile;
+            this.category = category;
         }
         }
 
 
         @Override
         @Override
@@ -899,7 +1032,7 @@ public class ImageDeepDetector {
                             if (imageBytes.get(imageBytes.size() - 2) == (byte) 0xFF && imageBytes.get(imageBytes.size() - 1) == (byte) 0xD9) {
                             if (imageBytes.get(imageBytes.size() - 2) == (byte) 0xFF && imageBytes.get(imageBytes.size() - 1) == (byte) 0xD9) {
                                 File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                                 File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
-                                    subscriber.onNext(new ImageFile(new XPathFile(context, cache)));
+                                    subscriber.onNext(new ImageFile(new XPathFile(context, cache), category));
                                 }
                                 }
                             }
                             }
                             imageBytes.clear();
                             imageBytes.clear();
@@ -909,7 +1042,7 @@ public class ImageDeepDetector {
                             ) {
                             ) {
                                 File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                                 File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
-                                    subscriber.onNext(new ImageFile(new XPathFile(context, cache)));
+                                    subscriber.onNext(new ImageFile(new XPathFile(context, cache), category));
                                 }
                                 }
                                 imageBytes.clear();
                                 imageBytes.clear();
                             }
                             }
@@ -928,7 +1061,7 @@ public class ImageDeepDetector {
             File[] files = targetCaches.exists() && targetCaches.isDirectory() ? targetCaches.listFiles() : null;
             File[] files = targetCaches.exists() && targetCaches.isDirectory() ? targetCaches.listFiles() : null;
             if (files != null && files.length > 0) {
             if (files != null && files.length > 0) {
                 for (File file : files) {
                 for (File file : files) {
-                    subscriber.onNext(new ImageFile(new XPathFile(this.context, file)));
+                    subscriber.onNext(new ImageFile(new XPathFile(this.context, file), category));
                 }
                 }
                 return true;
                 return true;
             }
             }

+ 111 - 6
app/src/main/java/com/datarecovery/master/utils/xfile/XFileSearch.java

@@ -16,7 +16,9 @@ import java.io.File;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashMap;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.List;
+import java.util.Queue;
 import java.util.Stack;
 import java.util.Stack;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.Lock;
@@ -24,6 +26,10 @@ import java.util.concurrent.locks.ReentrantLock;
 
 
 public class XFileSearch {
 public class XFileSearch {
 
 
+    private static final int SEARCH_MODE_DFS = 0;
+    private static final int SEARCH_MODE_BFS = 1;
+    private static final int SEARCH_MODE = SEARCH_MODE_BFS;
+
     /**
     /**
      * 搜索外部存储
      * 搜索外部存储
      * <p>
      * <p>
@@ -92,7 +98,7 @@ public class XFileSearch {
         int currentIndex = 0;
         int currentIndex = 0;
         for (List<XFile> value : splitTask.values()) {
         for (List<XFile> value : splitTask.values()) {
             final int finalCurrentIndex = currentIndex++;
             final int finalCurrentIndex = currentIndex++;
-            ThreadPoolUtil.getInstance().execute(() -> dfsSearchAsync(value, fileFilter, new FileSearchCallback() {
+            ThreadPoolUtil.getInstance().execute(() -> searchInternalAsync(value, fileFilter, new FileSearchCallback() {
                 @Override
                 @Override
                 public void onStart() {
                 public void onStart() {
                 }
                 }
@@ -142,17 +148,17 @@ public class XFileSearch {
                                                       FileSearchCallback callback,
                                                       FileSearchCallback callback,
                                                       CancellationSignal cancellationSignal) {
                                                       CancellationSignal cancellationSignal) {
         List<XFile> startFiles = getExternalStorageFilesOld(context);
         List<XFile> startFiles = getExternalStorageFilesOld(context);
-        dfsSearchAsync(startFiles, fileFilter, callback, cancellationSignal);
+        searchInternalAsync(startFiles, fileFilter, callback, cancellationSignal);
     }
     }
 
 
     private static List<XFile> searchExternalStorageNew(Context context, FileFilter fileFilter) {
     private static List<XFile> searchExternalStorageNew(Context context, FileFilter fileFilter) {
         List<XFile> startFiles = getExternalStorageFilesNew(context);
         List<XFile> startFiles = getExternalStorageFilesNew(context);
-        return dfsSearch(startFiles, fileFilter);
+        return searchInternal(startFiles, fileFilter);
     }
     }
 
 
     private static List<XFile> searchExternalStorageOld(Context context, FileFilter fileFilter) {
     private static List<XFile> searchExternalStorageOld(Context context, FileFilter fileFilter) {
         List<XFile> startFiles = getExternalStorageFilesOld(context);
         List<XFile> startFiles = getExternalStorageFilesOld(context);
-        return dfsSearch(startFiles, fileFilter);
+        return searchInternal(startFiles, fileFilter);
     }
     }
 
 
     public static List<XFile> searchAndroidData(Context context, FileFilter fileFilter) {
     public static List<XFile> searchAndroidData(Context context, FileFilter fileFilter) {
@@ -178,7 +184,7 @@ public class XFileSearch {
                 e.printStackTrace();
                 e.printStackTrace();
             }
             }
         }
         }
-        return dfsSearch(startFiles, fileFilter);
+        return searchInternal(startFiles, fileFilter);
     }
     }
 
 
     private static List<XFile> searchAndroidDataOld(Context context, FileFilter fileFilter) {
     private static List<XFile> searchAndroidDataOld(Context context, FileFilter fileFilter) {
@@ -198,7 +204,7 @@ public class XFileSearch {
                 e.printStackTrace();
                 e.printStackTrace();
             }
             }
         }
         }
-        return dfsSearch(startFiles, fileFilter);
+        return searchInternal(startFiles, fileFilter);
     }
     }
 
 
     @NonNull
     @NonNull
@@ -252,6 +258,19 @@ public class XFileSearch {
         return startFiles;
         return startFiles;
     }
     }
 
 
+    private static List<XFile> searchInternal(List<XFile> startFiles, FileFilter fileFilter) {
+        return SEARCH_MODE == SEARCH_MODE_DFS ? dfsSearch(startFiles, fileFilter) : bfsSearch(startFiles, fileFilter);
+    }
+
+    private static void searchInternalAsync(List<XFile> startFiles, FileFilter fileFilter,
+                                            FileSearchCallback callback, CancellationSignal cancellationSignal) {
+        if (SEARCH_MODE == SEARCH_MODE_DFS) {
+            dfsSearchAsync(startFiles, fileFilter, callback, cancellationSignal);
+        } else {
+            bfsSearchAsync(startFiles, fileFilter, callback, cancellationSignal);
+        }
+    }
+
     /**
     /**
      * 深度优先搜索
      * 深度优先搜索
      */
      */
@@ -340,6 +359,92 @@ public class XFileSearch {
         }
         }
     }
     }
 
 
+    /**
+     * 广度优先搜索
+     */
+    private static List<XFile> bfsSearch(List<XFile> startFiles, FileFilter fileFilter) {
+        Queue<XFile> fileQueue = new LinkedList<>(startFiles);
+        ArrayList<XFile> result = new ArrayList<>();
+        while (!fileQueue.isEmpty()) {
+            XFile file = fileQueue.poll();
+            if (file == null) {
+                continue;
+            }
+            try {
+                XFile[] files = file.listFiles();
+                if (files == null) {
+                    continue;
+                }
+                for (XFile childFile : files) {
+                    if (childFile == null) {
+                        continue;
+                    }
+                    if (childFile.isDirectory()) {
+                        if (fileFilter == null || !fileFilter.acceptDirectory(childFile)) {
+                            fileQueue.offer(childFile);
+                        } else {
+                            result.add(childFile);
+                        }
+                    } else if (fileFilter == null || fileFilter.acceptFile(childFile)) {
+                        result.add(childFile);
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * 广度优先搜索
+     */
+    private static void bfsSearchAsync(List<XFile> startFiles, FileFilter fileFilter,
+                                       FileSearchCallback callback, CancellationSignal cancellationSignal) {
+        Queue<XFile> fileQueue = new LinkedList<>(startFiles);
+        if (callback != null) {
+            callback.onStart();
+        }
+        while (!fileQueue.isEmpty()) {
+            if (cancellationSignal.isCanceled()) {
+                break;
+            }
+            XFile file = fileQueue.poll();
+            if (file == null) {
+                continue;
+            }
+            try {
+                XFile[] files = file.listFiles();
+                if (files == null) {
+                    continue;
+                }
+                for (XFile childFile : files) {
+                    if (childFile == null) {
+                        continue;
+                    }
+                    if (childFile.isDirectory()) {
+                        if (fileFilter == null || !fileFilter.acceptDirectory(childFile)) {
+                            fileQueue.offer(childFile);
+                        } else {
+                            if (callback != null) {
+                                callback.onEachFile(childFile);
+                            }
+                        }
+                    } else if (fileFilter == null || fileFilter.acceptFile(childFile)) {
+                        if (callback != null) {
+                            callback.onEachFile(childFile);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        if (callback != null) {
+            callback.onFinish();
+        }
+    }
+
     public interface FileFilter {
     public interface FileFilter {
         boolean acceptFile(XFile file);
         boolean acceptFile(XFile file);