diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt
index 989b02df..71010b5c 100644
--- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationEntity.kt
@@ -22,7 +22,7 @@ import androidx.room.Entity
import androidx.room.TypeConverters
import com.keylesspalace.tusky.db.Converters
import com.keylesspalace.tusky.entity.*
-import com.keylesspalace.tusky.util.SmartLengthInputFilter
+import com.keylesspalace.tusky.util.shouldTrimStatus
import java.util.*
@Entity(primaryKeys = ["id","accountId"])
@@ -176,7 +176,7 @@ fun Status.toEntity() =
spoilerText, attachments, mentions,
false,
false,
- !SmartLengthInputFilter.hasBadRatio(content, SmartLengthInputFilter.LENGTH_DEFAULT),
+ !shouldTrimStatus(content),
true,
poll
)
diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java
index b7c5e1e1..52563375 100644
--- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java
+++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewThreadFragment.java
@@ -48,7 +48,7 @@ import com.keylesspalace.tusky.interfaces.StatusActionListener;
import com.keylesspalace.tusky.network.MastodonApi;
import com.keylesspalace.tusky.util.ListStatusAccessibilityDelegate;
import com.keylesspalace.tusky.util.PairedList;
-import com.keylesspalace.tusky.util.SmartLengthInputFilter;
+import com.keylesspalace.tusky.util.SmartLengthInputFilterKt;
import com.keylesspalace.tusky.util.ThemeUtils;
import com.keylesspalace.tusky.util.ViewDataUtils;
import com.keylesspalace.tusky.view.ConversationLineItemDecoration;
@@ -360,10 +360,7 @@ public final class ViewThreadFragment extends SFragment implements
}
StatusViewData.Concrete updatedStatus = new StatusViewData.Builder(status)
- .setCollapsible(!SmartLengthInputFilter.hasBadRatio(
- status.getContent(),
- SmartLengthInputFilter.LENGTH_DEFAULT
- ))
+ .setCollapsible(!SmartLengthInputFilterKt.shouldTrimStatus(status.getContent()))
.setCollapsed(isCollapsed)
.createStatusViewData();
statuses.setPairedItem(position, updatedStatus);
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.java b/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.java
deleted file mode 100644
index 41400b25..00000000
--- a/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.java
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright 2018 Diego Rossi (@_HellPie)
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.keylesspalace.tusky.util;
-
-import android.text.InputFilter;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-
-/**
- * A customized version of {@link android.text.InputFilter.LengthFilter} which allows smarter
- * constraints and adds better visuals such as:
- *
- * - Ellipsis at the end of the constrained text to show continuation.
- * - Trimming of invisible characters (new lines, spaces, etc.) from the constrained text.
- * - Constraints end at the end of the last "word", before a whitespace.
- * - Expansion of the limit by up to 10 characters to facilitate the previous constraint.
- * - Constraints are not applied if the percentage of hidden content is too small.
- *
- *
- * Some of these features are configurable through at instancing time.
- */
-public class SmartLengthInputFilter implements InputFilter {
-
- /**
- * Defines how many characters to extend beyond the limit to cut at the end of the word on the
- * boundary of it rather than cutting at the word preceding that one.
- */
- private static final int RUNWAY = 10;
-
- /**
- * Default for maximum status length on Mastodon and default collapsing length on Pleroma.
- */
- public static final int LENGTH_DEFAULT = 500;
-
- /**
- * Stores a reusable singleton instance of a {@link SmartLengthInputFilter} already configured
- * to the default maximum length of {@value #LENGTH_DEFAULT}.
- */
- public static final SmartLengthInputFilter INSTANCE = new SmartLengthInputFilter(LENGTH_DEFAULT);
-
- private final int max;
- private final boolean allowRunway;
- private final boolean skipIfBadRatio;
-
- /**
- * Creates a new {@link SmartLengthInputFilter} instance with a predefined maximum length and
- * all the smart constraint features this class supports.
- *
- * @param max The maximum length before trimming. May change based on other constraints.
- */
- public SmartLengthInputFilter(int max) {
- this(max, true, true);
- }
-
- /**
- * Fully configures a new {@link SmartLengthInputFilter} to fine tune the state of the supported
- * smart constraints this class supports.
- *
- * @param max The maximum length before trimming.
- * @param allowRunway Whether to extend {@param max} by an extra 10 characters
- * and trim precisely at the end of the closest word.
- * @param skipIfBadRatio Whether to skip trimming entirely if the trimmed content
- * will be less than 25% of the shown content.
- */
- public SmartLengthInputFilter(int max, boolean allowRunway, boolean skipIfBadRatio) {
- this.max = max;
- this.allowRunway = allowRunway;
- this.skipIfBadRatio = skipIfBadRatio;
- }
-
- /**
- * Calculates if it's worth trimming the message at a specific limit or if the content that will
- * be hidden will not be enough to justify the operation.
- *
- * @param message The message to trim.
- * @param limit The maximum length after trimming.
- * @return Whether the message should be trimmed or not.
- */
- public static boolean hasBadRatio(Spanned message, int limit) {
- return (double) limit / message.length() > 0.75;
- }
-
- /** {@inheritDoc} */
- @Override
- public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
- // Code originally imported from InputFilter.LengthFilter but heavily customized.
- // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/InputFilter.java#175
-
- int sourceLength = source.length();
- int keep = max - (dest.length() - (dend - dstart));
- if (keep <= 0) return "";
- if (keep >= end - start) return null; // keep original
-
- keep += start;
-
- // Enable skipping trimming if the ratio is not good enough
- if (skipIfBadRatio && (double)keep / sourceLength > 0.75)
- return null;
-
- // Enable trimming at the end of the closest word if possible
- if (allowRunway && Character.isLetterOrDigit(source.charAt(keep))) {
- int boundary;
-
- // Android N+ offer a clone of the ICU APIs in Java for better internationalization and
- // unicode support. Using the ICU version of BreakIterator grants better support for
- // those without having to add the ICU4J library at a minimum Api trade-off.
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
- android.icu.text.BreakIterator iterator = android.icu.text.BreakIterator.getWordInstance();
- iterator.setText(source.toString());
- boundary = iterator.following(keep);
- if (keep - boundary > RUNWAY) boundary = iterator.preceding(keep);
- } else {
- java.text.BreakIterator iterator = java.text.BreakIterator.getWordInstance();
- iterator.setText(source.toString());
- boundary = iterator.following(keep);
- if (keep - boundary > RUNWAY) boundary = iterator.preceding(keep);
- }
-
- keep = boundary;
- } else {
-
- // If no runway is allowed simply remove whitespaces if present
- while (Character.isWhitespace(source.charAt(keep - 1))) {
- --keep;
- if (keep == start) return "";
- }
- }
-
- if (Character.isHighSurrogate(source.charAt(keep - 1))) {
- --keep;
- if (keep == start) return "";
- }
-
- if (source instanceof Spanned) {
- return new SpannableStringBuilder(source, start, keep).append("…");
- } else {
- return source.subSequence(start, keep) + "…";
- }
- }
-}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt b/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt
new file mode 100644
index 00000000..82d13d87
--- /dev/null
+++ b/app/src/main/java/com/keylesspalace/tusky/util/SmartLengthInputFilter.kt
@@ -0,0 +1,111 @@
+/* Copyright 2019 Tusky contributors
+ *
+ * This file is a part of Tusky.
+ *
+ * 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.
+ *
+ * Tusky 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 Tusky; if not,
+ * see . */
+
+package com.keylesspalace.tusky.util
+
+import android.text.InputFilter
+import android.text.SpannableStringBuilder
+import android.text.Spanned
+
+/**
+ * Defines how many characters to extend beyond the limit to cut at the end of the word on the
+ * boundary of it rather than cutting at the word preceding that one.
+ */
+private const val RUNWAY = 10
+
+/**
+ * Default for maximum status length on Mastodon and default collapsing length on Pleroma.
+ */
+private const val LENGTH_DEFAULT = 500
+
+/**
+ * Calculates if it's worth trimming the message at a specific limit or if the content that will
+ * be hidden will not be enough to justify the operation.
+ *
+ * @param message The message to trim.
+ * @return Whether the message should be trimmed or not.
+ */
+fun shouldTrimStatus(message: Spanned): Boolean {
+ return LENGTH_DEFAULT / message.length > 0.75
+}
+
+/**
+ * A customized version of {@link android.text.InputFilter.LengthFilter} which allows smarter
+ * constraints and adds better visuals such as:
+ *
+ * - Ellipsis at the end of the constrained text to show continuation.
+ * - Trimming of invisible characters (new lines, spaces, etc.) from the constrained text.
+ * - Constraints end at the end of the last "word", before a whitespace.
+ * - Expansion of the limit by up to 10 characters to facilitate the previous constraint.
+ * - Constraints are not applied if the percentage of hidden content is too small.
+ *
+ */
+object SmartLengthInputFilter : InputFilter {
+ /** {@inheritDoc} */
+ override fun filter(source: CharSequence, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? {
+ // Code originally imported from InputFilter.LengthFilter but heavily customized and converted to Kotlin.
+ // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/InputFilter.java#175
+
+ val sourceLength = source.length
+ var keep = LENGTH_DEFAULT - dest.length - dend - dstart
+ if (keep <= 0) return ""
+ if (keep >= end - start) return null // Keep original
+
+ keep += start
+
+ // Skip trimming if the ratio doesn't warrant it
+ if (keep.toDouble() / sourceLength > 0.75) return null
+
+ // Enable trimming at the end of the closest word if possible
+ if (source[keep].isLetterOrDigit()) {
+ var boundary: Int
+
+ // Android N+ offer a clone of the ICU APIs in Java for better internationalization and
+ // unicode support. Using the ICU version of BreakIterator grants better support for
+ // those without having to add the ICU4J library at a minimum Api trade-off.
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ val iterator = android.icu.text.BreakIterator.getWordInstance()
+ iterator.setText(source.toString())
+ boundary = iterator.following(keep)
+ if (keep - boundary > RUNWAY) boundary = iterator.preceding(keep)
+ } else {
+ val iterator = java.text.BreakIterator.getWordInstance()
+ iterator.setText(source.toString())
+ boundary = iterator.following(keep)
+ if (keep - boundary > RUNWAY) boundary = iterator.preceding(keep)
+ }
+
+ keep = boundary
+ } else {
+
+ // If no runway is allowed simply remove whitespaces if present
+ while(source[keep - 1].isWhitespace()) {
+ --keep
+ if (keep == start) return ""
+ }
+ }
+
+ if (source[keep - 1].isHighSurrogate()) {
+ --keep
+ if (keep == start) return ""
+ }
+
+ return if (source is Spanned) {
+ SpannableStringBuilder(source, start, keep).append("…")
+ } else {
+ "${source.subSequence(start, keep)}…"
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusExtensions.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusExtensions.kt
index e0cc7b49..0c4b5e79 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/StatusExtensions.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusExtensions.kt
@@ -18,6 +18,6 @@ package com.keylesspalace.tusky.util
import com.keylesspalace.tusky.entity.Status
fun Status.isCollapsible(): Boolean {
- return !SmartLengthInputFilter.hasBadRatio(content, SmartLengthInputFilter.LENGTH_DEFAULT)
+ return !shouldTrimStatus(content)
}
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
index 148461e8..f7353b0a 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
+++ b/app/src/main/java/com/keylesspalace/tusky/util/StatusViewHelper.kt
@@ -310,7 +310,7 @@ class StatusViewHelper(private val itemView: View) {
}
companion object {
- val COLLAPSE_INPUT_FILTER = arrayOf(SmartLengthInputFilter.INSTANCE)
+ val COLLAPSE_INPUT_FILTER = arrayOf(SmartLengthInputFilter)
val NO_INPUT_FILTER = arrayOfNulls(0)
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java
index 0ca42c10..ebd56eab 100644
--- a/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java
+++ b/app/src/main/java/com/keylesspalace/tusky/util/ViewDataUtils.java
@@ -58,10 +58,7 @@ public final class ViewDataUtils {
.setApplication(visibleStatus.getApplication())
.setStatusEmojis(visibleStatus.getEmojis())
.setAccountEmojis(visibleStatus.getAccount().getEmojis())
- .setCollapsible(!SmartLengthInputFilter.hasBadRatio(
- visibleStatus.getContent(),
- SmartLengthInputFilter.LENGTH_DEFAULT
- ))
+ .setCollapsible(!SmartLengthInputFilterKt.shouldTrimStatus(visibleStatus.getContent()))
.setCollapsed(true)
.setPoll(visibleStatus.getPoll())
.setCard(visibleStatus.getCard())