From 0716dd39ab53e4147b7a8a36f2b3aba1f3727c0c Mon Sep 17 00:00:00 2001 From: Harshad Vedartham Date: Wed, 25 May 2022 08:03:32 -0700 Subject: [PATCH 1/4] Pulled changes --- .../openeditor/OpenEditorActivity.java | 4 +- .../net/gsantner/markor/model/Document.java | 43 +++++++++------- .../net/gsantner/opoc/util/FileUtils.java | 50 ++++++++++++------- .../net/gsantner/opoc/util/StringUtils.java | 26 ++++++---- 4 files changed, 73 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java b/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java index 4217fe49b..70a00d5c7 100644 --- a/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java @@ -42,9 +42,7 @@ protected void openActivityAndClose(final Intent openIntent, File file) { file.getParentFile().mkdirs(); } if (!file.exists() && !file.isDirectory()) { - final Map options = new HashMap<>(1); - options.put(FileUtils.WITH_BOM, new AppSettings(getApplicationContext()).getNewFileDialogLastUsedUtf8Bom()); - FileUtils.writeFile(file, "", options); + FileUtils.writeFile(file, "", new FileUtils.FileInfo().setBom(new AppSettings(getApplicationContext()).getNewFileDialogLastUsedUtf8Bom())); } openIntent.putExtra(Document.EXTRA_PATH, openIntent.hasExtra(Document.EXTRA_PATH) ? openIntent.getSerializableExtra(Document.EXTRA_PATH) : file); new ActivityUtils(this).animateToActivity(openIntent, true, 1).freeContextRef(); diff --git a/app/src/main/java/net/gsantner/markor/model/Document.java b/app/src/main/java/net/gsantner/markor/model/Document.java index 5a2669edc..08376718f 100644 --- a/app/src/main/java/net/gsantner/markor/model/Document.java +++ b/app/src/main/java/net/gsantner/markor/model/Document.java @@ -26,6 +26,7 @@ import net.gsantner.markor.util.AppSettings; import net.gsantner.markor.util.ShareUtil; import net.gsantner.opoc.util.FileUtils; +import net.gsantner.opoc.util.StringUtils; import java.io.File; import java.io.FileInputStream; @@ -36,9 +37,7 @@ import java.nio.file.Files; import java.nio.file.attribute.BasicFileAttributes; import java.security.SecureRandom; -import java.util.HashMap; import java.util.Locale; -import java.util.Map; import other.de.stanetz.jpencconverter.JavaPasswordbasedCryption; @@ -55,7 +54,7 @@ public class Document implements Serializable { private String _title = ""; private String _path = ""; private long _modTime = 0; - private boolean _withBom = false; + private FileUtils.FileInfo _fileInfo; @StringRes private int _format = TextFormat.FORMAT_UNKNOWN; @@ -156,11 +155,11 @@ public boolean isEncrypted() { private void setContentHash(final CharSequence s) { _lastLength = s.length(); - _lastHash = FileUtils.crc32(s.toString().getBytes()); + _lastHash = FileUtils.crc32(s); } public boolean isContentSame(final CharSequence s) { - return s != null && s.length() == _lastLength && _lastHash == (FileUtils.crc32(s.toString().getBytes())); + return s != null && s.length() == _lastLength && _lastHash == FileUtils.crc32(s); } public synchronized String loadContent(final Context context) { @@ -183,9 +182,9 @@ public synchronized String loadContent(final Context context) { content = ""; } } else { - final Pair> result = FileUtils.readTextFileFast(_file); + final Pair result = FileUtils.readTextFileFast(_file); content = result.first; - _withBom = (boolean) result.second.get(FileUtils.WITH_BOM); + _fileInfo = result.second; } if (MainActivity.IS_DEBUG_ENABLED) { @@ -234,18 +233,22 @@ public boolean saveContent(final Context context, final String content) { return saveContent(context, content, null, false); } - public synchronized boolean saveContent(final Context context, final String content, ShareUtil shareUtil, boolean isManualSave) { - if (!isManualSave && content.trim().length() < ShareUtil.MIN_OVERWRITE_LENGTH) { + // Doing as we don't want to convert to string or copy unless necessary + private static int trimmedLength(final CharSequence c) { + return StringUtils.getLastNonWhitespace(c, c.length()) - StringUtils.getNextNonWhitespace(c, 0); + } + + public synchronized boolean saveContent(final Context context, final CharSequence content, ShareUtil shareUtil, boolean isManualSave) { + if (!isManualSave && trimmedLength(content) < ShareUtil.MIN_OVERWRITE_LENGTH) { return false; } if (!testCreateParent()) { return false; } - shareUtil = shareUtil != null ? shareUtil : new ShareUtil(context); // Don't write same content if base file not changed - if (isContentSame(content) && _modTime >= lastModified()) { + if (_modTime >= lastModified() && isContentSame(content)) { return true; } @@ -254,15 +257,17 @@ public synchronized boolean saveContent(final Context context, final String cont final char[] pw; final byte[] contentAsBytes; if (isEncrypted() && (pw = getPasswordWithWarning(context)) != null) { - contentAsBytes = new JavaPasswordbasedCryption(Build.VERSION.SDK_INT, new SecureRandom()).encrypt(content, pw); + contentAsBytes = new JavaPasswordbasedCryption(Build.VERSION.SDK_INT, new SecureRandom()).encrypt(content.toString(), pw); } else { - contentAsBytes = content.getBytes(); + contentAsBytes = content.toString().getBytes(); } + shareUtil = shareUtil != null ? shareUtil : new ShareUtil(context); + if (shareUtil.isUnderStorageAccessFolder(_file)) { shareUtil.writeFile(_file, false, (fileOpened, fos) -> { try { - if (_withBom) { + if (_fileInfo != null && _fileInfo.hasBom) { fos.write(0xEF); fos.write(0xBB); fos.write(0xBF); @@ -274,9 +279,7 @@ public synchronized boolean saveContent(final Context context, final String cont }); success = true; } else { - final Map options = new HashMap<>(1); - options.put(FileUtils.WITH_BOM, _withBom); - success = FileUtils.writeFile(_file, contentAsBytes, options); + success = FileUtils.writeFile(_file, contentAsBytes, _fileInfo); } } catch (JavaPasswordbasedCryption.EncryptionFailedException e) { Log.e(Document.class.getName(), "writeContent: encrypt failed for File " + getPath() + ". " + e.getMessage(), e); @@ -286,7 +289,11 @@ public synchronized boolean saveContent(final Context context, final String cont if (success) { setContentHash(content); - _modTime = lastModified(); + final long curModTime = lastModified(); + if (_modTime >= curModTime) { + Log.w("MARKOR_DOCUMENT", "File modification time unchanged after write"); + } + _modTime = curModTime; } return success; diff --git a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java index 83bc65eff..94bc58cdd 100644 --- a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java @@ -10,7 +10,6 @@ #########################################################*/ package net.gsantner.opoc.util; - import android.text.TextUtils; import android.util.Pair; @@ -33,37 +32,43 @@ import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; import java.util.zip.CRC32; -@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "deprecation", "TryFinallyCanBeTryWithResources"}) +@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "TryFinallyCanBeTryWithResources"}) public class FileUtils { // Used on methods like copyFile(src, dst) private static final int BUFFER_SIZE = 4096; - public final static String WITH_BOM = "withBom"; + /** + * Info of various types about a file + */ + public static class FileInfo { + public boolean hasBom = false; + public FileInfo setBom(boolean hasBom) { + this.hasBom = hasBom; + return this; + } + } - public static Pair> readTextFileFast(final File file) { - Map info = new HashMap<>(1); + public static Pair readTextFileFast(final File file) { + final FileInfo info = new FileInfo(); try (final FileInputStream inputStream = new FileInputStream(file)) { final ByteArrayOutputStream result = new ByteArrayOutputStream(); final byte[] bomBuffer = new byte[3]; - int bomReadLength = inputStream.read(bomBuffer); - boolean withBom = bomReadLength == 3 && + final int bomReadLength = inputStream.read(bomBuffer); + info.hasBom = bomReadLength == 3 && bomBuffer[0] == (byte) 0xEF && bomBuffer[1] == (byte) 0xBB && bomBuffer[2] == (byte) 0xBF; - info.put(WITH_BOM, withBom); - if (!withBom && bomReadLength > 0) { + if (!info.hasBom && bomReadLength > 0) { result.write(bomBuffer, 0, bomReadLength); } if (bomReadLength < 3) { @@ -201,11 +206,9 @@ public static byte[] readCloseBinaryStream(final InputStream stream) { return baos.toByteArray(); } - public static boolean writeFile(final File file, final byte[] data, final Map options) { - boolean withBom = options != null && (Boolean) options.get(WITH_BOM); - + public static boolean writeFile(final File file, final byte[] data, final FileInfo options) { try (final FileOutputStream output = new FileOutputStream(file)) { - if (withBom) { + if (options != null && options.hasBom) { output.write(0xEF); output.write(0xBB); output.write(0xBF); @@ -219,7 +222,7 @@ public static boolean writeFile(final File file, final byte[] data, final Map options) { + public static boolean writeFile(final File file, final String data, final FileInfo options) { return writeFile(file, data.getBytes(), options); } @@ -308,7 +311,7 @@ public static boolean deleteRecursive(final File file) { boolean ok = true; if (file.exists()) { if (file.isDirectory()) { - for (File child : file.listFiles()) + for (final File child : file.listFiles()) ok &= deleteRecursive(child); } ok &= file.delete(); @@ -352,7 +355,6 @@ public static boolean renameFile(File srcFile, File destFile) { return true; } - @SuppressWarnings("ResultOfMethodCallIgnored") public static boolean renameFileInSameFolder(File srcFile, String destFilename) { return renameFile(srcFile, new File(srcFile.getParent(), destFilename)); } @@ -553,6 +555,18 @@ public static String sha512(final byte[] data) { return hash(data, "SHA-512"); } + public static long crc32(final CharSequence data) { + final CRC32 alg = new CRC32(); + final int length = data.length(); + for (int i = 0; i < length; i++) { + final char c = data.charAt(i); + // Upper and lower bytes + alg.update((byte) (c & 0xff)); + alg.update((byte) (c >> 8)); + } + return alg.getValue(); + } + public static long crc32(final byte[] data) { final CRC32 alg = new CRC32(); alg.update(data); diff --git a/app/src/main/java/net/gsantner/opoc/util/StringUtils.java b/app/src/main/java/net/gsantner/opoc/util/StringUtils.java index 9dbb919df..e65216b18 100644 --- a/app/src/main/java/net/gsantner/opoc/util/StringUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/StringUtils.java @@ -82,21 +82,25 @@ public static int getLineEnd(CharSequence s, int start, int maxRange) { return i; } - public static int getNextNonWhitespace(CharSequence s, int start) { - return getNextNonWhitespace(s, start, s.length()); + public static int getLastNonWhitespace(final CharSequence s, final int start) { + for (int i = Math.min(s.length() - 1, start); i >= 0; i--) { + char c = s.charAt(i); + if (c != ' ' && c != '\t') { + return i; + } + } + return -1; } - public static int getNextNonWhitespace(CharSequence s, int start, int maxRange) { - int i = start; - if (isValidIndex(s, start, maxRange - 1)) { - for (; i < maxRange; i++) { - char c = s.charAt(i); - if (c != ' ' && c != '\t') { - break; - } + public static int getNextNonWhitespace(final CharSequence s, final int start) { + final int length = s.length(); + for (int i = Math.max(0, start); i < length; i++) { + char c = s.charAt(i); + if (c != ' ' && c != '\t') { + return i; } } - return i; + return -1; } public static boolean isNullOrWhitespace(String str) { From e5205dff03925ed950fbb23e733f86056db01dce Mon Sep 17 00:00:00 2001 From: Harshad Vedartham Date: Wed, 25 May 2022 08:17:40 -0700 Subject: [PATCH 2/4] Add serializable --- app/src/main/java/net/gsantner/opoc/util/FileUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java index 94bc58cdd..a9989b39a 100644 --- a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Serializable; import java.net.URLConnection; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -47,7 +48,7 @@ public class FileUtils { /** * Info of various types about a file */ - public static class FileInfo { + public static class FileInfo implements Serializable { public boolean hasBom = false; public FileInfo setBom(boolean hasBom) { this.hasBom = hasBom; From 4d8e19d4f297d699869ad3ab0863081ccd70769f Mon Sep 17 00:00:00 2001 From: Harshad Vedartham Date: Thu, 26 May 2022 07:58:09 -0700 Subject: [PATCH 3/4] Don't convert to string so early --- .../java/net/gsantner/markor/activity/DocumentEditFragment.java | 2 +- app/src/main/java/net/gsantner/markor/model/Document.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentEditFragment.java b/app/src/main/java/net/gsantner/markor/activity/DocumentEditFragment.java index 5a54a3c5a..03d0d48a0 100644 --- a/app/src/main/java/net/gsantner/markor/activity/DocumentEditFragment.java +++ b/app/src/main/java/net/gsantner/markor/activity/DocumentEditFragment.java @@ -659,7 +659,7 @@ public String getFragmentTag() { public boolean saveDocument(final boolean forceSaveEmpty) { // Document is written iff content has changed if (_isTextChanged && _document != null && _hlEditor != null && isAdded()) { - if (_document.saveContent(getContext(), _hlEditor.getText().toString(), _shareUtil, forceSaveEmpty)) { + if (_document.saveContent(getContext(), _hlEditor.getText(), _shareUtil, forceSaveEmpty)) { checkTextChangeState(); return true; } else { diff --git a/app/src/main/java/net/gsantner/markor/model/Document.java b/app/src/main/java/net/gsantner/markor/model/Document.java index 08376718f..72c225fe6 100644 --- a/app/src/main/java/net/gsantner/markor/model/Document.java +++ b/app/src/main/java/net/gsantner/markor/model/Document.java @@ -229,7 +229,7 @@ public static boolean testCreateParent(final File file) { } } - public boolean saveContent(final Context context, final String content) { + public boolean saveContent(final Context context, final CharSequence content) { return saveContent(context, content, null, false); } From d2c9a9a44cebf4559402ed7d4b0826f51c707565 Mon Sep 17 00:00:00 2001 From: Gregor Santner Date: Sun, 5 Jun 2022 15:00:33 +0200 Subject: [PATCH 4/4] q --- .../activity/openeditor/OpenEditorActivity.java | 4 +--- .../main/java/net/gsantner/opoc/util/FileUtils.java | 12 +++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java b/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java index 70a00d5c7..783011145 100644 --- a/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/openeditor/OpenEditorActivity.java @@ -21,8 +21,6 @@ import net.gsantner.opoc.util.PermissionChecker; import java.io.File; -import java.util.HashMap; -import java.util.Map; public class OpenEditorActivity extends MarkorBaseActivity { protected void openEditorForFile(File file) { @@ -42,7 +40,7 @@ protected void openActivityAndClose(final Intent openIntent, File file) { file.getParentFile().mkdirs(); } if (!file.exists() && !file.isDirectory()) { - FileUtils.writeFile(file, "", new FileUtils.FileInfo().setBom(new AppSettings(getApplicationContext()).getNewFileDialogLastUsedUtf8Bom())); + FileUtils.writeFile(file, "", new FileUtils.FileInfo().withBom(new AppSettings(getApplicationContext()).getNewFileDialogLastUsedUtf8Bom())); } openIntent.putExtra(Document.EXTRA_PATH, openIntent.hasExtra(Document.EXTRA_PATH) ? openIntent.getSerializableExtra(Document.EXTRA_PATH) : file); new ActivityUtils(this).animateToActivity(openIntent, true, 1).freeContextRef(); diff --git a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java index a9989b39a..96e21d3e4 100644 --- a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java @@ -40,7 +40,7 @@ import java.util.regex.Pattern; import java.util.zip.CRC32; -@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "TryFinallyCanBeTryWithResources"}) +@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "TryFinallyCanBeTryWithResources"}) public class FileUtils { // Used on methods like copyFile(src, dst) private static final int BUFFER_SIZE = 4096; @@ -50,8 +50,9 @@ public class FileUtils { */ public static class FileInfo implements Serializable { public boolean hasBom = false; - public FileInfo setBom(boolean hasBom) { - this.hasBom = hasBom; + + public FileInfo withBom(boolean bom) { + hasBom = bom; return this; } } @@ -64,10 +65,11 @@ public static Pair readTextFileFast(final File file) { final byte[] bomBuffer = new byte[3]; final int bomReadLength = inputStream.read(bomBuffer); - info.hasBom = bomReadLength == 3 && + info.withBom(bomReadLength == 3 && bomBuffer[0] == (byte) 0xEF && bomBuffer[1] == (byte) 0xBB && - bomBuffer[2] == (byte) 0xBF; + bomBuffer[2] == (byte) 0xBF + ); if (!info.hasBom && bomReadLength > 0) { result.write(bomBuffer, 0, bomReadLength);