package com.datarecovery.master.utils.filedetect; import static android.content.Context.POWER_SERVICE; import android.content.Context; import android.os.CancellationSignal; import android.os.PowerManager; import android.text.TextUtils; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import com.datarecovery.master.utils.BrandUtil; import com.datarecovery.master.utils.xfile.XFile; import com.datarecovery.master.utils.xfile.XFileSearch; import org.reactivestreams.Publisher; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import atmob.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import atmob.reactivex.rxjava3.core.BackpressureStrategy; import atmob.reactivex.rxjava3.core.Flowable; import atmob.reactivex.rxjava3.core.FlowableOnSubscribe; import atmob.reactivex.rxjava3.functions.Function; /** * 文件扫描帮助类 */ public class FileScanHelper { private FileScanHelper() { } private static final List wxDirList = new ArrayList<>(); private static final List galleryList = new ArrayList<>(); private static final List qqList = new ArrayList<>(); static { initWxDir(); initGalleryDir(); initQQDir(); } private static void initQQDir() { qqList.add(new String[]{"Android", "data", "com.tencent.mobileqq"}); qqList.add(new String[]{"Android", "data", "com.tencent.tim"}); qqList.add(new String[]{"Pictures", "QQ"}); qqList.add(new String[]{"Download", "QQ"}); qqList.add(new String[]{"Download", "QQMail"}); qqList.add(new String[]{"Download", "QQBrowser"}); } private static void initGalleryDir() { galleryList.add(new String[]{"DCIM"}); galleryList.add(new String[]{"Pictures"}); galleryList.add(new String[]{"Android", "data", "com.android.gallery3d"}); galleryList.add(new String[]{"Android", "data", "com.android.gallery"}); galleryList.addAll(getFactoryGalleryDir()); } private static void initWxDir() { wxDirList.add(new String[]{"Android", "data", "com.tencent.mm"}); wxDirList.add(new String[]{"DCIM", "WeixinWork"}); wxDirList.add(new String[]{"Pictures", "WeiXin"}); wxDirList.add(new String[]{"Download", "WeiXin"}); } /** * 需要能单扫某个文件夹(某个app) */ @IntDef({FileType.IMAGE_GALLERY, FileType.IMAGE_WEIXIN, FileType.IMAGE_QQ}) @Retention(RetentionPolicy.SOURCE) public @interface RefreshScanType { } public static Flowable> refreshImageDetect(@NonNull Context context, @RefreshScanType int scanType) { return Flowable.create((FlowableOnSubscribe) emitter -> { try { CancellationSignal cancellationSignal = XFileSearch.searchSpecifyExternalStorageAsync(context, getImageScanDirectory(scanType), new XFileSearch.FileFilter() { @Override public boolean acceptFile(XFile file) { return isAcceptImageFile(scanType, file); } @Override public boolean acceptDirectory(XFile file) { return isAcceptImageDirectory(scanType, file); } @Override public boolean filterDirectory(XFile file) { return isFilterImageDirectory(scanType, file); } }, new XFileSearch.FileSearchCallback() { @Override public void onStart() { } @Override public void onEachFile(XFile file) { emitter.onNext(file); } @Override public void onFinish() { emitter.onComplete(); } }); emitter.setCancellable(cancellationSignal::cancel); } catch (Exception e) { emitter.onError(e); } }, BackpressureStrategy.BUFFER) .filter(xFile -> xFile.getTag() != null) .flatMap((Function>) xFile -> { int tag = (int) xFile.getTag(); switch (tag) { case ImageType.IMG_MAGIC: case ImageType.IMAGE_SUFFIX: case ImageType.IMG_DATA: return Flowable.just(new DetectFile(xFile, scanType)); case ImageType.XIAOMI_GALLERY_CACHE: return Flowable.just(new DetectFile(xFile, FileType.IMAGE_GALLERY)); case ImageType.WECHAT_CACHE: return ImageCacheUtil.detectWechatCache(context, xFile); case ImageType.GALLERY_CACHE: return ImageCacheUtil.detectGalleryCache(context, xFile); case ImageType.OPPO_GALLERY_CACHE: return ImageCacheUtil.detectOppoGalleryCache(context, xFile); case ImageType.VIVO_GALLERY_CACHE: return ImageCacheUtil.detectVivoGalleryCache(context, xFile); case ImageType.MEIZU_GALLERY_CACHE: return ImageCacheUtil.detectMeizuGalleryCache(context, xFile); case ImageType.HUAWEI_GALLERY_CACHE: return ImageCacheUtil.detectHuaweiGalleryCache(context, xFile); default: return Flowable.empty(); } }) .buffer(200, TimeUnit.MILLISECONDS) .filter(imageFiles -> imageFiles != null && !imageFiles.isEmpty()) .observeOn(AndroidSchedulers.mainThread()) .doOnSubscribe(subscription -> { subscription.request(Long.MAX_VALUE); acquireWakeLock(context); }) .doOnCancel(() -> releaseWakeLock(context)) .doOnTerminate(() -> releaseWakeLock(context)); } private static boolean isAcceptImageFile(int scanType, XFile file) { try { if (file.length() == 0) { return false; } } catch (Exception ignore) { } try { if (file.getMineType() != null && file.getMineType().startsWith("image/")) { file.setTag(ImageType.IMG_DATA); return true; } } catch (Exception ignore) { } try { String name = file.getName(); if (ImageCacheUtil.isImageSuffix(name)) { file.setTag(ImageType.IMAGE_SUFFIX); return true; } } catch (Exception ignore) { } if (scanType == FileType.IMAGE_GALLERY) { try { String path = file.getPath(); int brand = BrandUtil.getBrand(); switch (brand) { case BrandUtil.Oppo: if (ImageCacheUtil.isOppoGalleryCacheFile(path)) { file.setTag(ImageType.OPPO_GALLERY_CACHE); } return true; case BrandUtil.Vivo: if (ImageCacheUtil.isVivoGalleryCacheFile(path)) { file.setTag(ImageType.VIVO_GALLERY_CACHE); } return true; case BrandUtil.XiaoMi: if (ImageCacheUtil.isXiaomiGalleryCacheFile(path)) { file.setTag(ImageType.XIAOMI_GALLERY_CACHE); } return true; case BrandUtil.Meizu: if (ImageCacheUtil.isMeizuGalleryCacheFile(path)) { file.setTag(ImageType.MEIZU_GALLERY_CACHE); } return true; case BrandUtil.HuaWei: if (ImageCacheUtil.isHuaweiGalleryCacheFile(path)) { file.setTag(ImageType.HUAWEI_GALLERY_CACHE); } return true; } } catch (Exception ignore) { } } if (ImageCacheUtil.hasImgMagic(file)) { file.setTag(ImageType.IMG_MAGIC); return true; } return false; } public static boolean isAcceptImageDirectory(int scanType, XFile file) { if (scanType == FileType.IMAGE_GALLERY) { try { String path = file.getPath(); if (isGalleryCacheDirectory(path)) { file.setTag(ImageType.GALLERY_CACHE); return true; } } catch (Exception ignore) { } } return false; } private static boolean isGalleryCacheDirectory(String path) { if (TextUtils.isEmpty(path)) { return false; } return path.contains("com.android.gallery3d%2Fcache") || path.contains("com.android.gallery3d/cache"); } private static boolean isFilterImageDirectory(@RefreshScanType int scanType, @NonNull XFile file) { try { String path = file.getPath(); if (TextUtils.isEmpty(path)) { return true; } if (FileType.IMAGE_GALLERY == scanType) { if (path.endsWith("Pictures/QQ") || path.endsWith("Pictures%2FQQ") || path.endsWith("DCIM/WeixinWork") || path.endsWith("DCIM%2FWeixinWork") || path.endsWith("Pictures/WeiXin") || path.endsWith("Pictures%2FWeiXin") ) { return true; } } } catch (Exception e) { } return false; } private static void releaseWakeLock(Context context) { PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ImageDeepDetector::detect"); if (wakeLock.isHeld()) { wakeLock.release(); } } private static void acquireWakeLock(Context context) { PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE); PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ImageDeepDetector::detect"); wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/); } public static List getGallery() { return galleryList; } public static @NonNull List getFactoryGalleryDir() { List brandGallery = new ArrayList<>(); int brand = BrandUtil.getBrand(); switch (brand) { case BrandUtil.HuaWei: brandGallery.add(new String[]{"Huawei"}); brandGallery.add(new String[]{"Android", "data", "com.huawei.photos"}); break; case BrandUtil.XiaoMi: brandGallery.add(new String[]{"Android", "data", "com.miui.gallery"}); break; case BrandUtil.Vivo: brandGallery.add(new String[]{"Android", "data", "com.vivo.gallery"}); break; case BrandUtil.Oppo: brandGallery.add(new String[]{"Android", "data", "com.oppo.gallery3d"}); break; case BrandUtil.Meizu: brandGallery.add(new String[]{"Android", "data", "com.meizu.media.gallery"}); break; case BrandUtil.Samsung: brandGallery.add(new String[]{"Android", "data", "com.sec.android.gallery3d"}); break; case BrandUtil.Smartisan: brandGallery.add(new String[]{"Android", "data", "com.smartisanos.gallery"}); break; case BrandUtil.Lenovo: brandGallery.add(new String[]{"Android", "data", "com.lenovo.scg"}); break; case BrandUtil.ZTE: brandGallery.add(new String[]{"Android", "data", "com.zte.gallery"}); break; case BrandUtil.OnePlus: brandGallery.add(new String[]{"Android", "data", "com.oneplus.gallery"}); break; case BrandUtil.Google: brandGallery.add(new String[]{"Android", "data", "com.google.android.apps.photos"}); break; case BrandUtil.Sony: brandGallery.add(new String[]{"Android", "data", "com.sonyericsson.album"}); break; case BrandUtil.LG: brandGallery.add(new String[]{"Android", "data", "com.lge.gallery3d"}); break; case BrandUtil.HTC: brandGallery.add(new String[]{"Android", "data", "com.htc.album"}); break; case BrandUtil.ASUS: brandGallery.add(new String[]{"Android", "data", "com.asus.gallery"}); break; case BrandUtil.GIONEE: brandGallery.add(new String[]{"Android", "data", "com.gionee.gallery"}); break; case BrandUtil.MOTOROLA: brandGallery.add(new String[]{"Android", "data", "com.motorola.gallery"}); break; case BrandUtil.NUBIA: brandGallery.add(new String[]{"Android", "data", "com.nubia.gallery"}); break; case BrandUtil.SHARP: brandGallery.add(new String[]{"Android", "data", "com.sharp.gallery"}); break; case BrandUtil.LEPHONE: brandGallery.add(new String[]{"Android", "data", "com.letv.android.gallery3d"}); break; case BrandUtil.HONOR: brandGallery.add(new String[]{"Huawei"}); brandGallery.add(new String[]{"Android", "data", "com.huawei.himovie"}); break; } return brandGallery; } public static List getWeiXin() { return wxDirList; } public static List getQQ() { return qqList; } private static List getImageScanDirectory(@RefreshScanType int scanType) { switch (scanType) { case FileType.IMAGE_GALLERY: return getGallery(); case FileType.IMAGE_WEIXIN: return getWeiXin(); case FileType.IMAGE_QQ: return getQQ(); } return null; } public static List getPreScanImageRelatedDirectory() { List preList = new ArrayList<>(); preList.add(new String[]{"DCIM"}); preList.add(new String[]{"Pictures"}); preList.add(new String[]{"Download"}); preList.add(new String[]{"Android", "data", "com.android.gallery3d"}); preList.add(new String[]{"Android", "data", "com.android.gallery"}); preList.addAll(FileScanHelper.getFactoryGalleryDir()); preList.add(new String[]{"Android", "data", "com.tencent.mm"}); preList.add(new String[]{"Android", "data", "com.tencent.mobileqq"}); return preList; } public static List getPreScanFileRelatedDirectory() { List preList = new ArrayList<>(); //文件保存相关路径 preList.add(new String[]{"Download"}); preList.add(new String[]{"DCIM"}); preList.add(new String[]{"Browser"}); preList.add(new String[]{"Movies"}); preList.add(new String[]{"Music"}); preList.add(new String[]{"QQBrowser"}); preList.add(new String[]{"UCDownloads"}); preList.add(new String[]{"BaiduNetdisk"}); preList.add(new String[]{"Quark"}); preList.add(new String[]{"Android", "data", "com.tencent.mm"}); preList.add(new String[]{"Android", "data", "com.tencent.mobileqq"}); return preList; } public static void searchExternalDirectory(Context context) { } }