FileScanHelper.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417
  1. package com.datarecovery.master.utils.filedetect;
  2. import static android.content.Context.POWER_SERVICE;
  3. import android.content.Context;
  4. import android.os.CancellationSignal;
  5. import android.os.PowerManager;
  6. import android.text.TextUtils;
  7. import androidx.annotation.IntDef;
  8. import androidx.annotation.NonNull;
  9. import com.datarecovery.master.utils.BrandUtil;
  10. import com.datarecovery.master.utils.xfile.XFile;
  11. import com.datarecovery.master.utils.xfile.XFileSearch;
  12. import org.reactivestreams.Publisher;
  13. import java.lang.annotation.Retention;
  14. import java.lang.annotation.RetentionPolicy;
  15. import java.util.ArrayList;
  16. import java.util.List;
  17. import java.util.concurrent.TimeUnit;
  18. import atmob.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
  19. import atmob.reactivex.rxjava3.core.BackpressureStrategy;
  20. import atmob.reactivex.rxjava3.core.Flowable;
  21. import atmob.reactivex.rxjava3.core.FlowableOnSubscribe;
  22. import atmob.reactivex.rxjava3.functions.Function;
  23. /**
  24. * 文件扫描帮助类
  25. */
  26. public class FileScanHelper {
  27. private FileScanHelper() {
  28. }
  29. private static final List<String[]> wxDirList = new ArrayList<>();
  30. private static final List<String[]> galleryList = new ArrayList<>();
  31. private static final List<String[]> qqList = new ArrayList<>();
  32. static {
  33. initWxDir();
  34. initGalleryDir();
  35. initQQDir();
  36. }
  37. private static void initQQDir() {
  38. qqList.add(new String[]{"Android", "data", "com.tencent.mobileqq"});
  39. qqList.add(new String[]{"Android", "data", "com.tencent.tim"});
  40. qqList.add(new String[]{"Pictures", "QQ"});
  41. qqList.add(new String[]{"Download", "QQ"});
  42. qqList.add(new String[]{"Download", "QQMail"});
  43. qqList.add(new String[]{"Download", "QQBrowser"});
  44. }
  45. private static void initGalleryDir() {
  46. galleryList.add(new String[]{"DCIM"});
  47. galleryList.add(new String[]{"Pictures"});
  48. galleryList.add(new String[]{"Android", "data", "com.android.gallery3d"});
  49. galleryList.add(new String[]{"Android", "data", "com.android.gallery"});
  50. galleryList.addAll(getFactoryGalleryDir());
  51. }
  52. private static void initWxDir() {
  53. wxDirList.add(new String[]{"Android", "data", "com.tencent.mm"});
  54. wxDirList.add(new String[]{"DCIM", "WeixinWork"});
  55. wxDirList.add(new String[]{"Pictures", "WeiXin"});
  56. wxDirList.add(new String[]{"Download", "WeiXin"});
  57. }
  58. /**
  59. * 需要能单扫某个文件夹(某个app)
  60. */
  61. @IntDef({FileType.IMAGE_GALLERY, FileType.IMAGE_WEIXIN, FileType.IMAGE_QQ})
  62. @Retention(RetentionPolicy.SOURCE)
  63. public @interface RefreshScanType {
  64. }
  65. public static Flowable<List<DetectFile>> refreshImageDetect(@NonNull Context context, @RefreshScanType int scanType) {
  66. return Flowable.create((FlowableOnSubscribe<XFile>) emitter -> {
  67. try {
  68. CancellationSignal cancellationSignal = XFileSearch.searchSpecifyExternalStorageAsync(context, getImageScanDirectory(scanType), new XFileSearch.FileFilter() {
  69. @Override
  70. public boolean acceptFile(XFile file) {
  71. return isAcceptImageFile(scanType, file);
  72. }
  73. @Override
  74. public boolean acceptDirectory(XFile file) {
  75. return isAcceptImageDirectory(scanType, file);
  76. }
  77. @Override
  78. public boolean filterDirectory(XFile file) {
  79. return isFilterImageDirectory(scanType, file);
  80. }
  81. }, new XFileSearch.FileSearchCallback() {
  82. @Override
  83. public void onStart() {
  84. }
  85. @Override
  86. public void onEachFile(XFile file) {
  87. emitter.onNext(file);
  88. }
  89. @Override
  90. public void onFinish() {
  91. emitter.onComplete();
  92. }
  93. });
  94. emitter.setCancellable(cancellationSignal::cancel);
  95. } catch (Exception e) {
  96. emitter.onError(e);
  97. }
  98. }, BackpressureStrategy.BUFFER)
  99. .filter(xFile -> xFile.getTag() != null)
  100. .flatMap((Function<XFile, Publisher<DetectFile>>) xFile -> {
  101. int tag = (int) xFile.getTag();
  102. switch (tag) {
  103. case ImageType.IMG_MAGIC:
  104. case ImageType.IMAGE_SUFFIX:
  105. case ImageType.IMG_DATA:
  106. return Flowable.just(new DetectFile(xFile, scanType));
  107. case ImageType.XIAOMI_GALLERY_CACHE:
  108. return Flowable.just(new DetectFile(xFile, FileType.IMAGE_GALLERY));
  109. case ImageType.WECHAT_CACHE:
  110. return ImageCacheUtil.detectWechatCache(context, xFile);
  111. case ImageType.GALLERY_CACHE:
  112. return ImageCacheUtil.detectGalleryCache(context, xFile);
  113. case ImageType.OPPO_GALLERY_CACHE:
  114. return ImageCacheUtil.detectOppoGalleryCache(context, xFile);
  115. case ImageType.VIVO_GALLERY_CACHE:
  116. return ImageCacheUtil.detectVivoGalleryCache(context, xFile);
  117. case ImageType.MEIZU_GALLERY_CACHE:
  118. return ImageCacheUtil.detectMeizuGalleryCache(context, xFile);
  119. case ImageType.HUAWEI_GALLERY_CACHE:
  120. return ImageCacheUtil.detectHuaweiGalleryCache(context, xFile);
  121. default:
  122. return Flowable.empty();
  123. }
  124. })
  125. .buffer(200, TimeUnit.MILLISECONDS)
  126. .filter(imageFiles -> imageFiles != null && !imageFiles.isEmpty())
  127. .observeOn(AndroidSchedulers.mainThread())
  128. .doOnSubscribe(subscription -> {
  129. subscription.request(Long.MAX_VALUE);
  130. acquireWakeLock(context);
  131. })
  132. .doOnCancel(() -> releaseWakeLock(context))
  133. .doOnTerminate(() -> releaseWakeLock(context));
  134. }
  135. private static boolean isAcceptImageFile(int scanType, XFile file) {
  136. try {
  137. if (file.length() == 0) {
  138. return false;
  139. }
  140. } catch (Exception ignore) {
  141. }
  142. try {
  143. if (file.getMineType() != null && file.getMineType().startsWith("image/")) {
  144. file.setTag(ImageType.IMG_DATA);
  145. return true;
  146. }
  147. } catch (Exception ignore) {
  148. }
  149. try {
  150. String name = file.getName();
  151. if (ImageCacheUtil.isImageSuffix(name)) {
  152. file.setTag(ImageType.IMAGE_SUFFIX);
  153. return true;
  154. }
  155. } catch (Exception ignore) {
  156. }
  157. if (scanType == FileType.IMAGE_GALLERY) {
  158. try {
  159. String path = file.getPath();
  160. int brand = BrandUtil.getBrand();
  161. switch (brand) {
  162. case BrandUtil.Oppo:
  163. if (ImageCacheUtil.isOppoGalleryCacheFile(path)) {
  164. file.setTag(ImageType.OPPO_GALLERY_CACHE);
  165. }
  166. return true;
  167. case BrandUtil.Vivo:
  168. if (ImageCacheUtil.isVivoGalleryCacheFile(path)) {
  169. file.setTag(ImageType.VIVO_GALLERY_CACHE);
  170. }
  171. return true;
  172. case BrandUtil.XiaoMi:
  173. if (ImageCacheUtil.isXiaomiGalleryCacheFile(path)) {
  174. file.setTag(ImageType.XIAOMI_GALLERY_CACHE);
  175. }
  176. return true;
  177. case BrandUtil.Meizu:
  178. if (ImageCacheUtil.isMeizuGalleryCacheFile(path)) {
  179. file.setTag(ImageType.MEIZU_GALLERY_CACHE);
  180. }
  181. return true;
  182. case BrandUtil.HuaWei:
  183. if (ImageCacheUtil.isHuaweiGalleryCacheFile(path)) {
  184. file.setTag(ImageType.HUAWEI_GALLERY_CACHE);
  185. }
  186. return true;
  187. }
  188. } catch (Exception ignore) {
  189. }
  190. }
  191. if (ImageCacheUtil.hasImgMagic(file)) {
  192. file.setTag(ImageType.IMG_MAGIC);
  193. return true;
  194. }
  195. return false;
  196. }
  197. public static boolean isAcceptImageDirectory(int scanType, XFile file) {
  198. if (scanType == FileType.IMAGE_GALLERY) {
  199. try {
  200. String path = file.getPath();
  201. if (isGalleryCacheDirectory(path)) {
  202. file.setTag(ImageType.GALLERY_CACHE);
  203. return true;
  204. }
  205. } catch (Exception ignore) {
  206. }
  207. }
  208. return false;
  209. }
  210. private static boolean isGalleryCacheDirectory(String path) {
  211. if (TextUtils.isEmpty(path)) {
  212. return false;
  213. }
  214. return path.contains("com.android.gallery3d%2Fcache") ||
  215. path.contains("com.android.gallery3d/cache");
  216. }
  217. private static boolean isFilterImageDirectory(@RefreshScanType int scanType, @NonNull XFile file) {
  218. try {
  219. String path = file.getPath();
  220. if (TextUtils.isEmpty(path)) {
  221. return true;
  222. }
  223. if (FileType.IMAGE_GALLERY == scanType) {
  224. if (path.endsWith("Pictures/QQ") || path.endsWith("Pictures%2FQQ") ||
  225. path.endsWith("DCIM/WeixinWork") || path.endsWith("DCIM%2FWeixinWork") ||
  226. path.endsWith("Pictures/WeiXin") || path.endsWith("Pictures%2FWeiXin")
  227. ) {
  228. return true;
  229. }
  230. }
  231. } catch (Exception e) {
  232. }
  233. return false;
  234. }
  235. private static void releaseWakeLock(Context context) {
  236. PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
  237. PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
  238. "ImageDeepDetector::detect");
  239. if (wakeLock.isHeld()) {
  240. wakeLock.release();
  241. }
  242. }
  243. private static void acquireWakeLock(Context context) {
  244. PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
  245. PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
  246. "ImageDeepDetector::detect");
  247. wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/);
  248. }
  249. public static List<String[]> getGallery() {
  250. return galleryList;
  251. }
  252. public static @NonNull List<String[]> getFactoryGalleryDir() {
  253. List<String[]> brandGallery = new ArrayList<>();
  254. int brand = BrandUtil.getBrand();
  255. switch (brand) {
  256. case BrandUtil.HuaWei:
  257. brandGallery.add(new String[]{"Huawei"});
  258. brandGallery.add(new String[]{"Android", "data", "com.huawei.photos"});
  259. break;
  260. case BrandUtil.XiaoMi:
  261. brandGallery.add(new String[]{"Android", "data", "com.miui.gallery"});
  262. break;
  263. case BrandUtil.Vivo:
  264. brandGallery.add(new String[]{"Android", "data", "com.vivo.gallery"});
  265. break;
  266. case BrandUtil.Oppo:
  267. brandGallery.add(new String[]{"Android", "data", "com.oppo.gallery3d"});
  268. break;
  269. case BrandUtil.Meizu:
  270. brandGallery.add(new String[]{"Android", "data", "com.meizu.media.gallery"});
  271. break;
  272. case BrandUtil.Samsung:
  273. brandGallery.add(new String[]{"Android", "data", "com.sec.android.gallery3d"});
  274. break;
  275. case BrandUtil.Smartisan:
  276. brandGallery.add(new String[]{"Android", "data", "com.smartisanos.gallery"});
  277. break;
  278. case BrandUtil.Lenovo:
  279. brandGallery.add(new String[]{"Android", "data", "com.lenovo.scg"});
  280. break;
  281. case BrandUtil.ZTE:
  282. brandGallery.add(new String[]{"Android", "data", "com.zte.gallery"});
  283. break;
  284. case BrandUtil.OnePlus:
  285. brandGallery.add(new String[]{"Android", "data", "com.oneplus.gallery"});
  286. break;
  287. case BrandUtil.Google:
  288. brandGallery.add(new String[]{"Android", "data", "com.google.android.apps.photos"});
  289. break;
  290. case BrandUtil.Sony:
  291. brandGallery.add(new String[]{"Android", "data", "com.sonyericsson.album"});
  292. break;
  293. case BrandUtil.LG:
  294. brandGallery.add(new String[]{"Android", "data", "com.lge.gallery3d"});
  295. break;
  296. case BrandUtil.HTC:
  297. brandGallery.add(new String[]{"Android", "data", "com.htc.album"});
  298. break;
  299. case BrandUtil.ASUS:
  300. brandGallery.add(new String[]{"Android", "data", "com.asus.gallery"});
  301. break;
  302. case BrandUtil.GIONEE:
  303. brandGallery.add(new String[]{"Android", "data", "com.gionee.gallery"});
  304. break;
  305. case BrandUtil.MOTOROLA:
  306. brandGallery.add(new String[]{"Android", "data", "com.motorola.gallery"});
  307. break;
  308. case BrandUtil.NUBIA:
  309. brandGallery.add(new String[]{"Android", "data", "com.nubia.gallery"});
  310. break;
  311. case BrandUtil.SHARP:
  312. brandGallery.add(new String[]{"Android", "data", "com.sharp.gallery"});
  313. break;
  314. case BrandUtil.LEPHONE:
  315. brandGallery.add(new String[]{"Android", "data", "com.letv.android.gallery3d"});
  316. break;
  317. case BrandUtil.HONOR:
  318. brandGallery.add(new String[]{"Huawei"});
  319. brandGallery.add(new String[]{"Android", "data", "com.huawei.himovie"});
  320. break;
  321. }
  322. return brandGallery;
  323. }
  324. public static List<String[]> getWeiXin() {
  325. return wxDirList;
  326. }
  327. public static List<String[]> getQQ() {
  328. return qqList;
  329. }
  330. private static List<String[]> getImageScanDirectory(@RefreshScanType int scanType) {
  331. switch (scanType) {
  332. case FileType.IMAGE_GALLERY:
  333. return getGallery();
  334. case FileType.IMAGE_WEIXIN:
  335. return getWeiXin();
  336. case FileType.IMAGE_QQ:
  337. return getQQ();
  338. }
  339. return null;
  340. }
  341. public static List<String[]> getPreScanImageRelatedDirectory() {
  342. List<String[]> preList = new ArrayList<>();
  343. preList.add(new String[]{"DCIM"});
  344. preList.add(new String[]{"Pictures"});
  345. preList.add(new String[]{"Download"});
  346. preList.add(new String[]{"Android", "data", "com.android.gallery3d"});
  347. preList.add(new String[]{"Android", "data", "com.android.gallery"});
  348. preList.addAll(FileScanHelper.getFactoryGalleryDir());
  349. preList.add(new String[]{"Android", "data", "com.tencent.mm"});
  350. preList.add(new String[]{"Android", "data", "com.tencent.mobileqq"});
  351. return preList;
  352. }
  353. public static List<String[]> getPreScanFileRelatedDirectory() {
  354. List<String[]> preList = new ArrayList<>();
  355. //文件保存相关路径
  356. preList.add(new String[]{"Download"});
  357. preList.add(new String[]{"DCIM"});
  358. preList.add(new String[]{"Browser"});
  359. preList.add(new String[]{"Movies"});
  360. preList.add(new String[]{"Music"});
  361. preList.add(new String[]{"QQBrowser"});
  362. preList.add(new String[]{"UCDownloads"});
  363. preList.add(new String[]{"BaiduNetdisk"});
  364. preList.add(new String[]{"Quark"});
  365. preList.add(new String[]{"Android", "data", "com.tencent.mm"});
  366. preList.add(new String[]{"Android", "data", "com.tencent.mobileqq"});
  367. return preList;
  368. }
  369. public static void searchExternalDirectory(Context context) {
  370. }
  371. }