|
|
@@ -41,6 +41,7 @@ public class ImageDeepDetector {
|
|
|
private static final int WECHAT_CACHE = 2;
|
|
|
private static final int GALLERY_CACHE = 3;
|
|
|
private static final int JPG_MAGIC = 4;
|
|
|
+ private static final int OPPO_GALLERY_CACHE = 5;
|
|
|
|
|
|
public static Flowable<ImageFile> detect(Context context) {
|
|
|
return Flowable.create((FlowableOnSubscribe<XFile>) emitter -> {
|
|
|
@@ -88,12 +89,20 @@ public class ImageDeepDetector {
|
|
|
return detectWechatCache(context, xFile);
|
|
|
case GALLERY_CACHE:
|
|
|
return detectGalleryCache(context, xFile);
|
|
|
+ case OPPO_GALLERY_CACHE:
|
|
|
+ return detectOppoGalleryCache(context, xFile);
|
|
|
default:
|
|
|
return Flowable.empty();
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ private static Publisher<ImageFile> detectOppoGalleryCache(Context context, XFile xFile) {
|
|
|
+ return new GenericImgCollectionDetector(context, xFile)
|
|
|
+ .subscribeOn(Schedulers.io())
|
|
|
+ .onErrorComplete();
|
|
|
+ }
|
|
|
+
|
|
|
private static Flowable<ImageFile> detectGalleryCache(Context context, XFile xFile) {
|
|
|
return new GalleryCacheDetector(context, xFile)
|
|
|
.subscribeOn(Schedulers.io())
|
|
|
@@ -139,6 +148,10 @@ public class ImageDeepDetector {
|
|
|
file.setTag(WECHAT_CACHE);
|
|
|
return true;
|
|
|
}
|
|
|
+ if (isOppoGalleryCacheFile(path)) {
|
|
|
+ file.setTag(OPPO_GALLERY_CACHE);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
} catch (Exception ignore) {
|
|
|
}
|
|
|
if (hasJpgMagic(file)) {
|
|
|
@@ -193,6 +206,18 @@ public class ImageDeepDetector {
|
|
|
name.contains("com.tencent.mm/cache/imgcache/cache.data");
|
|
|
}
|
|
|
|
|
|
+ private static boolean isOppoGalleryCacheFile(String path) {
|
|
|
+ if (TextUtils.isEmpty(path)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!path.contains("com.coloros.gallery3d%2Fcache") &&
|
|
|
+ !path.contains("com.coloros.gallery3d/cache")) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ return path.contains("imgcache") || path.contains("screennailcache")
|
|
|
+ || path.contains("tilecache");
|
|
|
+ }
|
|
|
+
|
|
|
private static boolean isImageSuffix(String name) {
|
|
|
if (TextUtils.isEmpty(name)) {
|
|
|
return false;
|
|
|
@@ -338,7 +363,7 @@ public class ImageDeepDetector {
|
|
|
|
|
|
try (InputStream inputStream = xFile.newInputStream()) {
|
|
|
ArrayList<Byte> imageBytes = new ArrayList<>();
|
|
|
- byte[] buffer = new byte[1024];
|
|
|
+ byte[] buffer = new byte[2048];
|
|
|
int read;
|
|
|
while ((read = inputStream.read(buffer)) != -1) {
|
|
|
for (int i = 0; i < read; i++) {
|
|
|
@@ -817,4 +842,97 @@ public class ImageDeepDetector {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ private static class GenericImgCollectionDetector extends Flowable<ImageFile> {
|
|
|
+
|
|
|
+ private String CACHE_DOMAIN = "generic_img_collection_detector";
|
|
|
+ private final Context context;
|
|
|
+ private final XFile xFile;
|
|
|
+
|
|
|
+ public GenericImgCollectionDetector(Context context, XFile xFile) {
|
|
|
+ this.context = context;
|
|
|
+ this.xFile = xFile;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected void subscribeActual(@NonNull Subscriber<? super ImageFile> subscriber) {
|
|
|
+ long lastModified;
|
|
|
+ try {
|
|
|
+ lastModified = xFile.lastModified();
|
|
|
+ CACHE_DOMAIN += xFile.getName();
|
|
|
+ } 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();
|
|
|
+ }
|
|
|
+
|
|
|
+ try (InputStream inputStream = xFile.newInputStream()) {
|
|
|
+ ArrayList<Byte> imageBytes = new ArrayList<>();
|
|
|
+ byte[] buffer = new byte[2048];
|
|
|
+ int read;
|
|
|
+ while ((read = inputStream.read(buffer)) != -1) {
|
|
|
+ for (int i = 0; i < read; i++) {
|
|
|
+ byte b = buffer[i];
|
|
|
+ imageBytes.add(b);
|
|
|
+ if (imageBytes.size() < 2) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (imageBytes.size() == 2) {
|
|
|
+ if (imageBytes.get(0) != (byte) 0xFF || imageBytes.get(1) != (byte) 0xD8) {
|
|
|
+ imageBytes.remove(0);
|
|
|
+ }
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (i == read - 1 && inputStream.available() == 0) {
|
|
|
+ if (imageBytes.get(imageBytes.size() - 2) == (byte) 0xFF && imageBytes.get(imageBytes.size() - 1) == (byte) 0xD9) {
|
|
|
+ File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
|
|
|
+ if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
|
|
|
+ subscriber.onNext(new ImageFile(new XPathFile(context, cache)));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ imageBytes.clear();
|
|
|
+ } else if (imageBytes.size() >= 4) {
|
|
|
+ if (imageBytes.get(imageBytes.size() - 1) == (byte) 0xD9
|
|
|
+ && imageBytes.get(imageBytes.size() - 2) == (byte) 0xFF
|
|
|
+ ) {
|
|
|
+ File cache = new File(detectedCacheDir, UUID.randomUUID() + ".jpg");
|
|
|
+ if (cache.createNewFile() && bytes2File(imageBytes, cache)) {
|
|
|
+ subscriber.onNext(new ImageFile(new XPathFile(context, cache)));
|
|
|
+ }
|
|
|
+ imageBytes.clear();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ subscriber.onComplete();
|
|
|
+ } catch (Exception e) {
|
|
|
+ subscriber.onError(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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) {
|
|
|
+ subscriber.onNext(new ImageFile(new XPathFile(this.context, file)));
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|