Browse Source

[New]图片扫描新增华为缓存支持

zhipeng 1 year ago
parent
commit
21641ea98f

+ 147 - 8
app/src/main/java/com/datarecovery/master/utils/ImageDeepDetector.java

@@ -3,6 +3,8 @@ package com.datarecovery.master.utils;
 import static android.content.Context.POWER_SERVICE;
 import static android.content.Context.POWER_SERVICE;
 
 
 import android.content.Context;
 import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.net.Uri;
 import android.os.CancellationSignal;
 import android.os.CancellationSignal;
 import android.os.PowerManager;
 import android.os.PowerManager;
@@ -50,6 +52,7 @@ public class ImageDeepDetector {
     private static final int VIVO_GALLERY_CACHE = 6;
     private static final int VIVO_GALLERY_CACHE = 6;
     private static final int XIAOMI_GALLERY_CACHE = 7;
     private static final int XIAOMI_GALLERY_CACHE = 7;
     private static final int MEIZU_GALLERY_CACHE = 8;
     private static final int MEIZU_GALLERY_CACHE = 8;
+    private static final int HUAWEI_GALLERY_CACHE = 9;
 
 
     public static Flowable<List<ImageFile>> detect(Context context) {
     public static Flowable<List<ImageFile>> detect(Context context) {
         return Flowable.create((FlowableOnSubscribe<XFile>) emitter -> {
         return Flowable.create((FlowableOnSubscribe<XFile>) emitter -> {
@@ -110,6 +113,8 @@ public class ImageDeepDetector {
                             return detectVivoGalleryCache(context, xFile);
                             return detectVivoGalleryCache(context, xFile);
                         case MEIZU_GALLERY_CACHE:
                         case MEIZU_GALLERY_CACHE:
                             return detectMeizuGalleryCache(context, xFile);
                             return detectMeizuGalleryCache(context, xFile);
+                        case HUAWEI_GALLERY_CACHE:
+                            return detectHuaweiGalleryCache(context, xFile);
                         default:
                         default:
                             return Flowable.empty();
                             return Flowable.empty();
                     }
                     }
@@ -151,12 +156,13 @@ public class ImageDeepDetector {
             if (path.contains("com.kuaishou.nebula") && (path.contains("live_gift_store_icon_directory") ||
             if (path.contains("com.kuaishou.nebula") && (path.contains("live_gift_store_icon_directory") ||
                     path.contains("magic_finger_resource") || path.contains("theme_resource") ||
                     path.contains("magic_finger_resource") || path.contains("theme_resource") ||
                     path.contains("magic_emoji_resource") || path.contains(".material_library_resource") ||
                     path.contains("magic_emoji_resource") || path.contains(".material_library_resource") ||
-                    path.contains("sticker_resource")
+                    path.contains("sticker_resource") || path.contains("preload%2Ficon%2Fcommon") ||
+                    path.contains("preload/icon/common") || path.contains(".emoji")
             )) {
             )) {
                 return true;
                 return true;
             }
             }
             if ((path.contains("com.tencent.mobileqq") || path.contains("com.tencent.tim")) && (path.contains("qvideo_newvideo_tips") ||
             if ((path.contains("com.tencent.mobileqq") || path.contains("com.tencent.tim")) && (path.contains("qvideo_newvideo_tips") ||
-                    path.contains("Gameicon"))) {
+                    path.contains("Gameicon") || path.contains("html5") || path.contains(".preloaduni"))) {
                 return true;
                 return true;
             }
             }
             if ((path.contains("com.ss.android.article.video") || path.contains("com.ss.android.ugc.aweme")) && (path.contains("liveroom") ||
             if ((path.contains("com.ss.android.article.video") || path.contains("com.ss.android.ugc.aweme")) && (path.contains("liveroom") ||
@@ -171,7 +177,8 @@ public class ImageDeepDetector {
             if ((path.contains("com.baidu.BaiduMap")) && (path.contains("sticker"))) {
             if ((path.contains("com.baidu.BaiduMap")) && (path.contains("sticker"))) {
                 return true;
                 return true;
             }
             }
-            if ((path.contains("com.eg.android.AlipayGphone")) && (path.contains("Sandbox"))) {
+            if ((path.contains("com.eg.android.AlipayGphone")) && (path.contains("Sandbox") ||
+                    path.contains("emojiFiles"))) {
                 return true;
                 return true;
             }
             }
             if ((path.contains("air.tv.douyu.android")) && (path.contains("skin_download_dir"))) {
             if ((path.contains("air.tv.douyu.android")) && (path.contains("skin_download_dir"))) {
@@ -186,11 +193,23 @@ public class ImageDeepDetector {
             if (path.contains("bddownload/common") || path.contains("bddownload%2Fcommon")) {
             if (path.contains("bddownload/common") || path.contains("bddownload%2Fcommon")) {
                 return true;
                 return true;
             }
             }
+            if (path.contains("Pictures/.gs_fs") || path.contains("Pictures%2F.gs_fs")) {
+                return true;
+            }
+            if (path.contains("__MACOSX")) {
+                return true;
+            }
         } catch (Exception ignore) {
         } catch (Exception ignore) {
         }
         }
         return false;
         return false;
     }
     }
 
 
+    private static Flowable<ImageFile> detectHuaweiGalleryCache(Context context, XFile xFile) {
+        return new HuaweiGalleryCacheDetector(context, xFile)
+                .subscribeOn(Schedulers.io())
+                .onErrorComplete();
+    }
+
     private static Flowable<ImageFile> detectMeizuGalleryCache(Context context, XFile xFile) {
     private static Flowable<ImageFile> detectMeizuGalleryCache(Context context, XFile xFile) {
         return new GenericImgCollectionDetector(context, xFile, ImageFile.CATEGORY_GALLERY)
         return new GenericImgCollectionDetector(context, xFile, ImageFile.CATEGORY_GALLERY)
                 .subscribeOn(Schedulers.io())
                 .subscribeOn(Schedulers.io())
@@ -270,6 +289,10 @@ public class ImageDeepDetector {
                 file.setTag(MEIZU_GALLERY_CACHE);
                 file.setTag(MEIZU_GALLERY_CACHE);
                 return true;
                 return true;
             }
             }
+            if (isHuaweiGalleryCacheFile(path)) {
+                file.setTag(HUAWEI_GALLERY_CACHE);
+                return true;
+            }
         } catch (Exception ignore) {
         } catch (Exception ignore) {
         }
         }
         if (hasImgMagic(file)) {
         if (hasImgMagic(file)) {
@@ -372,6 +395,17 @@ public class ImageDeepDetector {
                 || path.contains("uri_thumbnail_cache");
                 || path.contains("uri_thumbnail_cache");
     }
     }
 
 
+    private static boolean isHuaweiGalleryCacheFile(String path) {
+        if (TextUtils.isEmpty(path)) {
+            return false;
+        }
+        if (!path.contains("com.huawei.photos%2Ffiles%2Fthumbdb") &&
+                !path.contains("com.huawei.photos/files/thumbdb")) {
+            return false;
+        }
+        return path.endsWith("photoshare.db");
+    }
+
     private static boolean isImageSuffix(String name) {
     private static boolean isImageSuffix(String name) {
         if (TextUtils.isEmpty(name)) {
         if (TextUtils.isEmpty(name)) {
             return false;
             return false;
@@ -599,7 +633,7 @@ public class ImageDeepDetector {
                         }
                         }
                         if (i == read - 1 && inputStream.available() == 0) {
                         if (i == read - 1 && inputStream.available() == 0) {
                             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().toString());
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                     subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_WECHAT));
                                     subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_WECHAT));
                                 }
                                 }
@@ -613,7 +647,7 @@ public class ImageDeepDetector {
                             ) {
                             ) {
                                 imageBytes.remove(imageBytes.size() - 1);
                                 imageBytes.remove(imageBytes.size() - 1);
                                 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().toString());
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                     subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_WECHAT));
                                     subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_WECHAT));
                                 }
                                 }
@@ -772,7 +806,7 @@ public class ImageDeepDetector {
                         if (cropData == null) {
                         if (cropData == null) {
                             continue;
                             continue;
                         }
                         }
-                        File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
+                        File cache = new File(detectedCacheDir, UUID.randomUUID().toString());
                         if (cache.createNewFile() && bytes2File(cropData, cache)) {
                         if (cache.createNewFile() && bytes2File(cropData, cache)) {
                             subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_GALLERY));
                             subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_GALLERY));
                         }
                         }
@@ -1118,7 +1152,7 @@ public class ImageDeepDetector {
                         }
                         }
                         if (i == read - 1 && inputStream.available() == 0) {
                         if (i == read - 1 && inputStream.available() == 0) {
                             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().toString());
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                     subscriber.onNext(new ImageFile(new XPathFile(context, cache), category));
                                     subscriber.onNext(new ImageFile(new XPathFile(context, cache), category));
                                 }
                                 }
@@ -1128,7 +1162,7 @@ public class ImageDeepDetector {
                             if (imageBytes.get(imageBytes.size() - 1) == (byte) 0xD9
                             if (imageBytes.get(imageBytes.size() - 1) == (byte) 0xD9
                                     && imageBytes.get(imageBytes.size() - 2) == (byte) 0xFF
                                     && imageBytes.get(imageBytes.size() - 2) == (byte) 0xFF
                             ) {
                             ) {
-                                File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
+                                File cache = new File(detectedCacheDir, UUID.randomUUID().toString());
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                 if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
                                     subscriber.onNext(new ImageFile(new XPathFile(context, cache), category));
                                     subscriber.onNext(new ImageFile(new XPathFile(context, cache), category));
                                 }
                                 }
@@ -1156,4 +1190,109 @@ public class ImageDeepDetector {
             return false;
             return false;
         }
         }
     }
     }
+
+    private static class HuaweiGalleryCacheDetector extends Flowable<ImageFile> {
+
+        private static final String CACHE_DOMAIN = "huawei_gallery_cache_detector";
+        private final Context context;
+        private final XFile dbFile;
+
+        public HuaweiGalleryCacheDetector(Context context, XFile dbFile) {
+            this.context = context;
+            this.dbFile = dbFile;
+        }
+
+        @Override
+        protected void subscribeActual(@NonNull Subscriber<? super ImageFile> subscriber) {
+            long lastModified;
+            try {
+                lastModified = dbFile.lastModified();
+            } catch (Exception e) {
+                subscriber.onError(e);
+                return;
+            }
+
+            if (checkDetectedCache(context, lastModified, subscriber)) {
+                subscriber.onComplete();
+                return;
+            } else {
+                clearDetectedCache(context, CACHE_DOMAIN);
+            }
+
+            File detectedCacheDir = getDetectedCacheDir(context, CACHE_DOMAIN);
+            detectedCacheDir = new File(detectedCacheDir, CryptoUtils.HASH.md5(String.valueOf(lastModified)));
+            if (!detectedCacheDir.exists()) {
+                detectedCacheDir.mkdirs();
+            }
+
+            File dbTempFile;
+            try {
+                dbTempFile = createDbTempFile(detectedCacheDir);
+            } catch (Exception e) {
+                subscriber.onError(e);
+                return;
+            }
+
+            try (SQLiteDatabase sqLiteDatabase = SQLiteDatabase.openDatabase(dbTempFile.getPath(), null, SQLiteDatabase.OPEN_READONLY);
+                 Cursor cursor = sqLiteDatabase.rawQuery("select * from general_kv", null)
+            ) {
+                int vIndex = cursor.getColumnIndex("v");
+                if (vIndex == -1) {
+                    subscriber.onComplete();
+                    return;
+                }
+                while (cursor.moveToNext()) {
+                    byte[] data = cursor.getBlob(vIndex);
+                    if (data == null || data.length == 0) {
+                        continue;
+                    }
+                    File cache = new File(detectedCacheDir, UUID.randomUUID().toString());
+                    if (cache.createNewFile() && bytes2File(data, cache)) {
+                        subscriber.onNext(new ImageFile(new XPathFile(context, cache), ImageFile.CATEGORY_GALLERY));
+                    }
+                }
+                subscriber.onComplete();
+            } catch (Exception e) {
+                subscriber.onError(e);
+            } finally {
+                if (dbTempFile != null && dbTempFile.exists()) {
+                    dbTempFile.delete();
+                }
+            }
+        }
+
+        private File createDbTempFile(File detectedCacheDir) throws Exception {
+            File dbTempFile = new File(detectedCacheDir, "huawei_gallery_cache_detector.db");
+            if (dbTempFile.exists()) {
+                dbTempFile.delete();
+            }
+            try (InputStream inputStream = dbFile.newInputStream();
+                 OutputStream outputStream = new FileOutputStream(dbTempFile)
+            ) {
+                byte[] buffer = new byte[2048];
+                int read;
+                while ((read = inputStream.read(buffer)) != -1) {
+                    outputStream.write(buffer, 0, read);
+                }
+                outputStream.flush();
+                return dbTempFile;
+            }
+        }
+
+        private boolean checkDetectedCache(Context context, long lastModified, Subscriber<? super ImageFile> subscriber) {
+            File detectedCacheDir = getDetectedCacheDir(context, CACHE_DOMAIN);
+            File targetCaches = new File(detectedCacheDir, CryptoUtils.HASH.md5(String.valueOf(lastModified)));
+            File[] files = targetCaches.exists() && targetCaches.isDirectory() ? targetCaches.listFiles() : null;
+            if (files != null && files.length > 0) {
+                for (File file : files) {
+                    if (file.getName().endsWith(".db")) {
+                        continue;
+                    }
+                    subscriber.onNext(new ImageFile(new XPathFile(this.context, file), ImageFile.CATEGORY_GALLERY));
+                }
+                return true;
+            }
+            return false;
+        }
+    }
 }
 }

+ 19 - 0
app/src/main/java/com/datarecovery/master/utils/xfile/StorageVolumeUtil.java

@@ -4,10 +4,12 @@ import android.net.Uri;
 import android.os.Build;
 import android.os.Build;
 import android.os.storage.StorageVolume;
 import android.os.storage.StorageVolume;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract;
+import android.provider.MediaStore;
 
 
 import java.io.File;
 import java.io.File;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Method;
+import java.util.Locale;
 
 
 public class StorageVolumeUtil {
 public class StorageVolumeUtil {
 
 
@@ -48,4 +50,21 @@ public class StorageVolumeUtil {
     public static Uri getTreeUri(StorageVolume storageVolume) {
     public static Uri getTreeUri(StorageVolume storageVolume) {
         return Uri.parse(getRootUri(storageVolume).toString().replace("/root/", "/tree/"));
         return Uri.parse(getRootUri(storageVolume).toString().replace("/root/", "/tree/"));
     }
     }
+
+    public static String getMediaStoreVolumeName(StorageVolume storageVolume) {
+        String volumeName;
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+            volumeName = storageVolume.getMediaStoreVolumeName();
+        } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
+            volumeName = storageVolume.isPrimary() ? MediaStore.VOLUME_EXTERNAL_PRIMARY : getNormalizedUuid(storageVolume);
+        } else {
+            volumeName = storageVolume.isPrimary() ? "external" : getNormalizedUuid(storageVolume);
+        }
+        return volumeName;
+    }
+
+    public static String getNormalizedUuid(StorageVolume storageVolume) {
+        String uuid = storageVolume.getUuid();
+        return uuid == null ? null : uuid.toLowerCase(Locale.US);
+    }
 }
 }