diff --git a/app/build.gradle b/app/build.gradle index acd7bf3355..bb4e528e1c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -25,8 +25,8 @@ android { defaultConfig { resValue "string", "manifest_package_id", "net.gsantner.markor" applicationId "net.gsantner.markor" - versionName "2.13.0" - versionCode 155 + versionName "2.13.1" + versionCode 156 multiDexEnabled true minSdkVersion rootProject.ext.version_minSdk diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentActivity.java b/app/src/main/java/net/gsantner/markor/activity/DocumentActivity.java index fd563564b9..4dfc8bc2ed 100644 --- a/app/src/main/java/net/gsantner/markor/activity/DocumentActivity.java +++ b/app/src/main/java/net/gsantner/markor/activity/DocumentActivity.java @@ -16,6 +16,7 @@ import android.os.Build; import android.os.Bundle; import android.text.Html; +import android.util.Log; import android.view.KeyEvent; import android.view.MotionEvent; @@ -95,8 +96,8 @@ private static void launch( intent = new Intent(activity, DocumentActivity.class); if (!(activity instanceof DocumentActivity) && - Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && - as.isMultiWindowEnabled() + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && + as.isMultiWindowEnabled() ) { intent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); } @@ -254,7 +255,8 @@ public boolean dispatchTouchEvent(MotionEvent event) { } try { return super.dispatchTouchEvent(event); - } catch (IndexOutOfBoundsException ignored) { + } catch (Exception e) { + Log.e(getClass().getName(), "Error in super.dispatchTouchEvent: " + e); return false; } } diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java b/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java index 39f02c6fbd..5cec9c9409 100644 --- a/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java +++ b/app/src/main/java/net/gsantner/markor/activity/DocumentEditAndViewFragment.java @@ -286,7 +286,7 @@ public void onPause() { _appSettings.setDocumentPreviewState(_document.path, _isPreviewVisible); _appSettings.setLastEditPosition(_document.path, TextViewUtils.getSelection(_hlEditor)[0]); - if(_document.path.equals(_appSettings.getTodoFile().getAbsolutePath())){ + if (_document.path.equals(_appSettings.getTodoFile().getAbsolutePath())) { TodoWidgetProvider.updateTodoWidgets(); } super.onPause(); @@ -862,7 +862,12 @@ private boolean isDisplayedAtMainActivity() { } public void updateViewModeText() { - _format.getConverter().convertMarkupShowInWebView(_document, getTextString(), getActivity(), _webView, _nextConvertToPrintMode, _hlEditor.isLineNumbersEnabled()); + // Don't let text to view mode crash app + try { + _format.getConverter().convertMarkupShowInWebView(_document, getTextString(), getActivity(), _webView, _nextConvertToPrintMode, _hlEditor.isLineNumbersEnabled()); + } catch (OutOfMemoryError e) { + _format.getConverter().convertMarkupShowInWebView(_document, "updateViewModeText getTextString(): OutOfMemory " + e, getActivity(), _webView, _nextConvertToPrintMode, _hlEditor.isLineNumbersEnabled()); + } } public void setViewModeVisibility(final boolean show) { diff --git a/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java b/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java index 4a688fbb7e..1e8f9469a1 100644 --- a/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java +++ b/app/src/main/java/net/gsantner/markor/activity/DocumentShareIntoFragment.java @@ -17,6 +17,7 @@ import android.os.Build; import android.os.Bundle; import android.text.TextUtils; +import android.util.Log; import android.util.Pair; import android.util.Patterns; import android.util.TypedValue; @@ -375,7 +376,7 @@ private String formatShare(final String shared) { } // Put the shared text in the right place - parts.add(1, shared); + parts.add(parts.isEmpty() ? 0 : 1, shared); return TextUtils.join("", parts); } @@ -433,7 +434,9 @@ public void onFsViewerConfig(GsFileBrowserOptions.Options dopt) { @Override public void onFsViewerSelected(final String request, final File sel, final Integer lineNumber) { - if (sel.isDirectory()) { + if (sel == null) { + Log.e(getClass().getName(), "onFsViewerSelected: selected file is null"); + } else if (sel.isDirectory()) { NewFileDialog.newInstance(sel, false, f -> { if (f.isFile()) { appendToExistingDocumentAndClose(f, true); diff --git a/app/src/main/java/net/gsantner/markor/format/ActionButtonBase.java b/app/src/main/java/net/gsantner/markor/format/ActionButtonBase.java index f58de8fd99..48c22775ba 100644 --- a/app/src/main/java/net/gsantner/markor/format/ActionButtonBase.java +++ b/app/src/main/java/net/gsantner/markor/format/ActionButtonBase.java @@ -297,7 +297,7 @@ public void recreateActionButtons(final ViewGroup barLayout, final ActionItem.Di @SuppressLint("ClickableViewAccessibility") private void setupRepeat(final View btn) { // Velocity and acceleration parameters - final int INITIAL_DELAY = 400, DELTA_DELAY = 50, MIN_DELAY = 100; + final int INITIAL_DELAY = 300, DELTA_DELAY = 100, MIN_DELAY = 100; final Handler handler = new Handler(); final Runnable repeater = new Runnable() { diff --git a/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java b/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java index 42dec9e891..1202fc5e1a 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java +++ b/app/src/main/java/net/gsantner/markor/frontend/textview/HighlightingEditor.java @@ -19,12 +19,14 @@ import android.text.TextWatcher; import android.util.AttributeSet; import android.view.ActionMode; +import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.RequiresApi; @@ -41,6 +43,7 @@ import java.util.Objects; import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -123,12 +126,22 @@ public void afterTextChanged(final Editable s) { @Override public boolean onPreDraw() { _lineNumbersDrawer.setTextSize(getTextSize()); - return super.onPreDraw(); + try { + return super.onPreDraw(); + } catch (OutOfMemoryError ignored) { + return false; // return false to cancel current drawing pass/round + } } @Override protected void onDraw(Canvas canvas) { - super.onDraw(canvas); + try { + super.onDraw(canvas); + } catch (Exception e) { + // Hinder drawing from crashing the app + Log.e(getClass().getName(), "HighlightingEdtior onDraw->super.onDraw crash" + e); + Toast.makeText(getContext(), e.toString(), Toast.LENGTH_SHORT).show(); + } if (_numEnabled) { _lineNumbersDrawer.draw(canvas); @@ -188,7 +201,10 @@ public void recomputeHighlighting() { */ private void recomputeHighlightingAsync() { if (runHighlight(true)) { - executor.execute(this::_recomputeHighlightingWorker); + try { + executor.execute(this::_recomputeHighlightingWorker); + } catch (RejectedExecutionException ignored) { + } } } @@ -296,14 +312,12 @@ public boolean bringPointIntoView(int i) { private int rowStart(final int y) { final Layout layout = getLayout(); - final int line = layout.getLineForVertical(y); - return layout.getLineStart(line); + return layout == null ? 0 : layout.getLineStart(layout.getLineForVertical(y)); } private int rowEnd(final int y) { final Layout layout = getLayout(); - final int line = layout.getLineForVertical(y); - return layout.getLineEnd(line); + return layout == null ? 0 : layout.getLineEnd(layout.getLineForVertical(y)); } // Text-Casing @@ -405,7 +419,12 @@ public boolean onTextContextMenuItem(int id) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && id == android.R.id.paste) { id = android.R.id.pasteAsPlainText; } - return super.onTextContextMenuItem(id); + try { + // i.e. DeadSystemRuntimeException can happen here + return super.onTextContextMenuItem(id); + } catch (Exception ignored) { + return true; + } } // Accessibility code is blocked during rapid update events diff --git a/app/src/main/java/net/gsantner/markor/frontend/textview/SyntaxHighlighterBase.java b/app/src/main/java/net/gsantner/markor/frontend/textview/SyntaxHighlighterBase.java index fdd5a36ba5..00756b5b21 100644 --- a/app/src/main/java/net/gsantner/markor/frontend/textview/SyntaxHighlighterBase.java +++ b/app/src/main/java/net/gsantner/markor/frontend/textview/SyntaxHighlighterBase.java @@ -62,9 +62,9 @@ * Spans are further divided into two categories: dynamic and static. * - Dynamic spans are updated as one scrolls, as described above * - Static spans are applied once and never updated. These are typically used for - * spans which affect the text layout. - * - For example, a span which makes text bigger. - * - Updating these dynamically would make the text jump around as one scrolls + * spans which affect the text layout. + * - For example, a span which makes text bigger. + * - Updating these dynamically would make the text jump around as one scrolls *
* Fixup: * - As the user types we shift all spans to accomodate the changed text. @@ -75,19 +75,19 @@ * - Derived classes should override generateSpans() to generate all spans * - New spans are added by calling addSpanGroup() * - The HighlightingEditor will trigger the generation of spans when the text changes. - * - This is debounced so that changes are batched - * - Span generation is done on a background thread + * - This is debounced so that changes are batched + * - Span generation is done on a background thread *
* Other performance tips:
* - Performance is heavily dependent on the number of spans applied to the text.
* - Combine related spans into a single span if possible
- * - HighlightSpan is a helper class which can be used to create a span with multiple attributes
- * - For example, a span which makes text bold and italic
+ * - HighlightSpan is a helper class which can be used to create a span with multiple attributes
+ * - For example, a span which makes text bold and italic
* - Absolutely minimize the number of spans implementing `UpdateLayout`
- * - These spans trigger a text layout update when changed in any way
- * - Instead consider using a span implementing `StaticSpan`
- * - If StaticSpans are present, the text is reflowed after applying them
- * - This happens once, and not for each span, which is much more efficient
+ * - These spans trigger a text layout update when changed in any way
+ * - Instead consider using a span implementing `StaticSpan`
+ * - If StaticSpans are present, the text is reflowed after applying them
+ * - This happens once, and not for each span, which is much more efficient
*/
public abstract class SyntaxHighlighterBase {
@@ -230,7 +230,7 @@ public SyntaxHighlighterBase clearStatic() {
boolean hasStatic = false;
for (int i = _groups.size() - 1; i >= 0; i--) {
final SpanGroup group = _groups.get(i);
- if (group.isStatic) {
+ if (group != null && group.isStatic) {
hasStatic = true;
_spannable.removeSpan(group.span);
}
@@ -348,7 +348,7 @@ public SyntaxHighlighterBase applyDynamic(final int[] range) {
for (int i = 0; i < _groups.size(); i++) {
final SpanGroup group = _groups.get(i);
- if (group.isStatic) {
+ if (group == null || group.isStatic) {
continue;
}
@@ -368,7 +368,6 @@ public SyntaxHighlighterBase applyDynamic(final int[] range) {
}
-
public SyntaxHighlighterBase applyStatic() {
if (_spannable != null && !_staticApplied) {
applyFixup();
diff --git a/app/src/main/java/net/gsantner/markor/model/AppSettings.java b/app/src/main/java/net/gsantner/markor/model/AppSettings.java
index 7c83c5095f..f354681012 100644
--- a/app/src/main/java/net/gsantner/markor/model/AppSettings.java
+++ b/app/src/main/java/net/gsantner/markor/model/AppSettings.java
@@ -855,7 +855,7 @@ public boolean isExperimentalFeaturesEnabled() {
}
public boolean isHighlightBiggerHeadings() {
- return getBool(R.string.pref_key__editor_markdown_bigger_headings_2, false);
+ return getBool(R.string.pref_key__editor_markdown_bigger_headings_3, false);
}
public String getViewModeLinkColor() {
diff --git a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserListAdapter.java b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserListAdapter.java
index e665d0547c..1dee2e5ae8 100644
--- a/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserListAdapter.java
+++ b/app/src/main/java/net/gsantner/opoc/frontend/filebrowser/GsFileBrowserListAdapter.java
@@ -56,6 +56,7 @@
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ExecutorService;
+import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -152,7 +153,7 @@ public Map