diff --git a/app/src/main/java/de/k3b/android/io/AndroidFileFacade.java b/app/src/main/java/de/k3b/android/io/AndroidFileFacade.java index 4129a1f8..dddc90a2 100644 --- a/app/src/main/java/de/k3b/android/io/AndroidFileFacade.java +++ b/app/src/main/java/de/k3b/android/io/AndroidFileFacade.java @@ -107,8 +107,8 @@ public void set(IFile src) { super.set(src); } - public static DocumentFile getDocumentFileOrDirOrNull(@NonNull File file) { - return documentFileTranslator.getDocumentFileOrDirOrNull(file, null); + private DocumentFile getDocumentFileOrDirOrNull(@NonNull File file) { + return documentFileTranslator.getDocumentFileOrDirOrNull(file, null, this.strategyID); } @Override @@ -255,7 +255,8 @@ public long lastModified() { @Override public boolean mkdirs() { - this.androidFile = documentFileTranslator.getOrCreateDirectory(getFile()); + this.androidFile = documentFileTranslator.getOrCreateDirectory(getFile(), strategyID); + invalidateParentDirCache(); return null != this.androidFile; } @@ -303,7 +304,7 @@ public OutputStream openOutputStream() throws FileNotFoundException { DocumentFile androidFile = getAndroidFile(false); String context = "openOutputStream overwrite existing "; if (androidFile == null) { - final DocumentFile documentFileParent = documentFileTranslator.getOrCreateDirectory(getFile().getParentFile()); + final DocumentFile documentFileParent = documentFileTranslator.getOrCreateDirectory(getFile().getParentFile(), strategyID); androidFile = this.androidFile = documentFileParent.createFile(null, getFile().getName()); context = "openOutputStream create new "; } @@ -335,6 +336,14 @@ public InputStream openInputStream() throws FileNotFoundException { return resultInputStream; } + //------- file cache support for android + @Override + public void invalidateParentDirCache() { + if (documentFileTranslator != null) + documentFileTranslator.documentFileCache.invalidateParentDirCache(strategyID); + } + + private IFile[] get(DocumentFile[] docs) { AndroidFileFacade[] f = new AndroidFileFacade[docs.length]; final File parent = getFile(); diff --git a/app/src/main/java/de/k3b/android/io/DocumentFileCache.java b/app/src/main/java/de/k3b/android/io/DocumentFileCache.java new file mode 100644 index 00000000..835fe0f1 --- /dev/null +++ b/app/src/main/java/de/k3b/android/io/DocumentFileCache.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2021 by k3b. + * + * This file is part of AndroFotoFinder / #APhotoManager. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see + */ +package de.k3b.android.io; + +import android.support.v4.provider.DocumentFile; +import android.util.Log; + +import java.io.File; +import java.util.HashMap; + +import de.k3b.io.filefacade.IFile; +import de.k3b.media.PhotoPropertiesUtil; + +public class DocumentFileCache { + private static final String TAG = DocumentFileTranslator.TAG; + + private CurrentFileCache[] currentFileCaches = null; + + /** + * strategyID : IFile.STRATEGY_XXX. + */ + public CurrentFileCache getCacheStrategy(int strategyID) { + if (currentFileCaches == null) { + currentFileCaches = new CurrentFileCache[IFile.STRATEGY_MAX + 1]; + currentFileCaches[IFile.STRATEGY_INPUT] = new CurrentFileCache(); + currentFileCaches[IFile.STRATEGY_OUTPUT] = new CurrentFileCache(); + currentFileCaches[IFile.STRATEGY_NONE] = null; + } + if (strategyID >= 0 && strategyID < currentFileCaches.length) { + return currentFileCaches[strategyID]; + } + Log.w(TAG, "DocumentFileCache.setCacheStrategy(id=" + strategyID + + ") unknow Strategy"); + return currentFileCaches[IFile.STRATEGY_NONE]; + } + + public DocumentFile findFile(DocumentFile parentDoc, File parentFile, String displayName, int strategyID) { + CurrentFileCache currentFileCache = getCacheStrategy(strategyID); + + if (currentFileCache != null) { + if (!parentFile.equals(currentFileCache.lastParentFile)) { + currentFileCache.lastParentFile = parentFile; + currentFileCache.lastChildDocFiles.clear(); + for (DocumentFile childDoc : parentDoc.listFiles()) { + if (childDoc.isFile()) { + String childDocName = childDoc.getName().toLowerCase(); + if (PhotoPropertiesUtil.isImage(childDocName, PhotoPropertiesUtil.IMG_TYPE_ALL | PhotoPropertiesUtil.IMG_TYPE_XMP)) { + currentFileCache.lastChildDocFiles.put(childDocName, childDoc); + } + } + } + + } + + if (PhotoPropertiesUtil.isImage(displayName, PhotoPropertiesUtil.IMG_TYPE_ALL | PhotoPropertiesUtil.IMG_TYPE_XMP)) { + return currentFileCache.lastChildDocFiles.get(displayName.toLowerCase()); + } + } + return parentDoc.findFile(displayName); + } + + public void invalidateParentDirCache(int strategyID) { + CurrentFileCache currentFileCache = getCacheStrategy(strategyID); + if (currentFileCache != null) currentFileCache.lastParentFile = null; + } + + /** + * Mapping from local file name inside lastParentFile to photo-related-DocumentFiles + */ + private static class CurrentFileCache { + private final HashMap lastChildDocFiles = new HashMap<>(); + private File lastParentFile = null; + } + +} diff --git a/app/src/main/java/de/k3b/android/io/DocumentFileTranslator.java b/app/src/main/java/de/k3b/android/io/DocumentFileTranslator.java index 0e190a61..f9f48662 100644 --- a/app/src/main/java/de/k3b/android/io/DocumentFileTranslator.java +++ b/app/src/main/java/de/k3b/android/io/DocumentFileTranslator.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 by k3b. + * Copyright (c) 2020-2021 by k3b. * * This file is part of AndroFotoFinder / #APhotoManager. * @@ -36,7 +36,6 @@ import java.util.Map; import de.k3b.io.filefacade.FileFacade; -import de.k3b.media.PhotoPropertiesUtil; /** * Handles Translation from File to android specific DocumentFileUtils @@ -67,12 +66,8 @@ public class DocumentFileTranslator { * Mapping from known File to DocumentFile-Directory translation */ private final Map dirCache = new HashMap<>(); + protected final DocumentFileCache documentFileCache = new DocumentFileCache(); - /** - * Mapping from local file name inside lastParentFile to photo-related-DocumentFiles - */ - private final File lastParentFile = null; - private final HashMap lastChildDocFiles = new HashMap<>(); private static final File internalRootCandidate = new File("/storage/emulated/0"); // for debugging @@ -181,21 +176,21 @@ public DocumentFileTranslator add(File directory, DocumentFile documentFileDir) return this; } - private DocumentFile getDocumentFileOrDirImpl(File fileOrDir, boolean isDir) { + private DocumentFile getDocumentFileOrDirImpl(File fileOrDir, boolean isDir, int strategyID) { DocumentFile result = null; if (fileOrDir != null) { result = getFromCache(fileOrDir); if (result == null) { - DocumentFile parent = getDocumentFileOrDirImpl(fileOrDir.getParentFile(), true); + DocumentFile parent = getDocumentFileOrDirImpl(fileOrDir.getParentFile(), true, strategyID); if (parent != null) { - result = findFile(parent, fileOrDir, isDir); + result = findFile(parent, fileOrDir, isDir, strategyID); } } } return result; } - private DocumentFile findFile(DocumentFile parentDoc, File fileOrDir, boolean isDir) { + private DocumentFile findFile(DocumentFile parentDoc, File fileOrDir, boolean isDir, int strategyID) { String displayName = fileOrDir.getName(); File parentFile = fileOrDir.getParentFile(); if (isDir) { @@ -214,23 +209,7 @@ private DocumentFile findFile(DocumentFile parentDoc, File fileOrDir, boolean is } return foundDoc; } else { - if (!parentFile.equals(lastParentFile)) { - lastChildDocFiles.clear(); - for (DocumentFile childDoc : parentDoc.listFiles()) { - if (childDoc.isFile()) { - String childDocName = childDoc.getName().toLowerCase(); - if (PhotoPropertiesUtil.isImage(childDocName, PhotoPropertiesUtil.IMG_TYPE_ALL | PhotoPropertiesUtil.IMG_TYPE_XMP)) { - lastChildDocFiles.put(childDocName, childDoc); - } - } - } - - } - - if (PhotoPropertiesUtil.isImage(displayName, PhotoPropertiesUtil.IMG_TYPE_ALL | PhotoPropertiesUtil.IMG_TYPE_XMP)) { - return lastChildDocFiles.get(displayName.toLowerCase()); - } - return parentDoc.findFile(fileOrDir.getName()); + return documentFileCache.findFile(parentDoc, parentFile, displayName, strategyID); } } @@ -239,14 +218,14 @@ private DocumentFile findFile(DocumentFile parentDoc, File fileOrDir, boolean is * * @return the found or created directory */ - public DocumentFile getOrCreateDirectory(File directory) { + public DocumentFile getOrCreateDirectory(File directory, int strategyID) { DocumentFile result = null; if (directory != null) { result = getFromCache(directory); if (result == null) { - DocumentFile parent = getOrCreateDirectory(directory.getParentFile()); + DocumentFile parent = getOrCreateDirectory(directory.getParentFile(), strategyID); if ((parent != null) && parent.isDirectory()) { - result = findFile(parent, directory, true); + result = findFile(parent, directory, true, strategyID); if (result == null) { result = parent.createDirectory(directory.getName()); @@ -262,17 +241,18 @@ public DocumentFile getOrCreateDirectory(File directory) { * gets existing DocumentFile that correspondws to fileOrDir * or null if not exists or no write permissions * - * @param fileOrDir where DocumentFile is searched for - * @param isDir if null: return null if isDir is matchning + * @param fileOrDir where DocumentFile is searched for + * @param isDir if null: return null if isDir is matchning + * @param strategyID * @return DocumentFile or null */ - public DocumentFile getDocumentFileOrDirOrNull(File fileOrDir, Boolean isDir) { + public DocumentFile getDocumentFileOrDirOrNull(File fileOrDir, Boolean isDir, int strategyID) { DocumentFile result = null; String path = fileOrDir != null ? fileOrDir.getAbsolutePath() : ""; final String context = FileFacade.debugLogFacade ? (mDebugPrefix + "getDocumentFile('" + path + "') ") : null; try { - result = getDocumentFileOrDirImpl(fileOrDir, isDir == Boolean.TRUE); + result = getDocumentFileOrDirImpl(fileOrDir, isDir == Boolean.TRUE, strategyID); if ((context != null) && (result == null)) { Log.i(TAG, context + "not found"); } diff --git a/fotolib2/src/main/java/de/k3b/media/ExifInterface.java b/fotolib2/src/main/java/de/k3b/media/ExifInterface.java index 5c2ca4a3..a6e69583 100644 --- a/fotolib2/src/main/java/de/k3b/media/ExifInterface.java +++ b/fotolib2/src/main/java/de/k3b/media/ExifInterface.java @@ -1416,6 +1416,7 @@ public void saveAttributes(IFile inFile, IFile outFile, if (overwriteOriginal) { final String name = inFile.getName(); final String tempName = name + TMP_FILE_SUFFIX; + // inFile.setCacheStrategy(3); //!!! inFile = renameSouraceFileBeforeReplaceOrThrow(inFile, tempName); currentOutFile = outFile.getParentFile().create(name); diff --git a/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/FileFacade.java b/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/FileFacade.java index 0700c5ec..5c93ffce 100644 --- a/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/FileFacade.java +++ b/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/FileFacade.java @@ -57,6 +57,8 @@ public IFile convert(String dbgContext, File file) { private File file; + protected int strategyID = 0; + private static final List allowedFileSuffixesLowercase = new ArrayList<>(); static { @@ -338,4 +340,19 @@ public File getFile() { protected void setFile(File file) { this.file = file; } + + + /** + * true: forInput; false: forOutput; null: disable cache + */ + @Override + public int setCacheStrategy(int strategyID) { + int old = this.strategyID; + this.strategyID = strategyID; + return old; + } + + @Override + public void invalidateParentDirCache() { + } } diff --git a/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/FileWrapper.java b/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/FileWrapper.java index b1c6231b..5dfa8836 100644 --- a/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/FileWrapper.java +++ b/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/FileWrapper.java @@ -182,6 +182,16 @@ public long length() { return child.length(); } + @Override + public int setCacheStrategy(int strategyID) { + return child.setCacheStrategy(strategyID); + } + + @Override + public void invalidateParentDirCache() { + child.invalidateParentDirCache(); + } + @Override public boolean equals(Object o) { return child.equals(o); diff --git a/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/IFile.java b/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/IFile.java index 1610e77c..c5052df9 100644 --- a/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/IFile.java +++ b/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/IFile.java @@ -29,10 +29,15 @@ * Goal: to become an android independant replacement for java.io.File * that can be implemented by android independant de.k3b.io.File * and android specific de.k3b.android.io.... - * + *

* This interface has the similar method names/sinatures as de.k3b.io.File */ public interface IFile { + int STRATEGY_INPUT = 0; + int STRATEGY_OUTPUT = 1; + int STRATEGY_NONE = 2; + int STRATEGY_MAX = STRATEGY_NONE; + void set(IFile src); @Deprecated @@ -95,4 +100,13 @@ public interface IFile { File getFile(); long length(); + + //------- file cache support + + /** + * 0: forInput; 1: forOutput; 3: disable cache. + */ + int setCacheStrategy(int strategyID); + + void invalidateParentDirCache(); } diff --git a/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/StringFileFacade.java b/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/StringFileFacade.java index fb551560..98dc597f 100644 --- a/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/StringFileFacade.java +++ b/libK3bFilefacade/src/main/java/de/k3b/io/filefacade/StringFileFacade.java @@ -232,4 +232,14 @@ public File getFile() { public long length() { return 0; } + + @Override + public int setCacheStrategy(int strategyID) { + return 0; + } + + @Override + public void invalidateParentDirCache() { + + } }