From 466fcc63fed5ee2e9202675ab1cc75a59e42ee0c Mon Sep 17 00:00:00 2001 From: andrekir Date: Sun, 10 Mar 2024 08:18:03 -0300 Subject: [PATCH] refactor: limit message input by bytes instead of character length --- .../geeksville/mesh/ui/MessagesFragment.kt | 9 +-- .../mesh/util/Utf8ByteLengthFilter.java | 67 +++++++++++++++++++ app/src/main/res/layout/messages_fragment.xml | 1 - 3 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/geeksville/mesh/util/Utf8ByteLengthFilter.java diff --git a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt index ca689491d..4f1ab26ab 100644 --- a/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt +++ b/app/src/main/java/com/geeksville/mesh/ui/MessagesFragment.kt @@ -4,7 +4,6 @@ import android.graphics.Color import android.graphics.drawable.GradientDrawable import android.os.Bundle import android.view.* -import android.view.inputmethod.EditorInfo import android.widget.* import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.view.ActionMode @@ -26,7 +25,7 @@ import com.geeksville.mesh.database.entity.QuickChatAction import com.geeksville.mesh.databinding.AdapterMessageLayoutBinding import com.geeksville.mesh.databinding.MessagesFragmentBinding import com.geeksville.mesh.model.UIViewModel -import com.geeksville.mesh.util.onEditorAction +import com.geeksville.mesh.util.Utf8ByteLengthFilter import com.google.android.material.chip.Chip import com.google.android.material.dialog.MaterialAlertDialogBuilder import dagger.hilt.android.AndroidEntryPoint @@ -260,10 +259,8 @@ class MessagesFragment : Fragment(), Logging { sendMessageInputText() } - binding.messageInputText.onEditorAction(EditorInfo.IME_ACTION_SEND) { - debug("received IME_ACTION_SEND") - sendMessageInputText() - } + // max payload length should be 237 bytes but anything over 235 bytes crashes the radio + binding.messageInputText.filters += Utf8ByteLengthFilter(234) binding.messageListView.adapter = messagesAdapter val layoutManager = LinearLayoutManager(requireContext()) diff --git a/app/src/main/java/com/geeksville/mesh/util/Utf8ByteLengthFilter.java b/app/src/main/java/com/geeksville/mesh/util/Utf8ByteLengthFilter.java new file mode 100644 index 000000000..08185fd5a --- /dev/null +++ b/app/src/main/java/com/geeksville/mesh/util/Utf8ByteLengthFilter.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * 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.geeksville.mesh.util; + +import android.text.InputFilter; +import android.text.Spanned; + +/** + * This filter will constrain edits so that the text length is not + * greater than the specified number of bytes using UTF-8 encoding. + */ +public class Utf8ByteLengthFilter implements InputFilter { + private final int mMaxBytes; + public Utf8ByteLengthFilter(int maxBytes) { + mMaxBytes = maxBytes; + } + public CharSequence filter(CharSequence source, int start, int end, + Spanned dest, int dstart, int dend) { + int srcByteCount = 0; + // count UTF-8 bytes in source substring + for (int i = start; i < end; i++) { + char c = source.charAt(i); + srcByteCount += (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3); + } + int destLen = dest.length(); + int destByteCount = 0; + // count UTF-8 bytes in destination excluding replaced section + for (int i = 0; i < destLen; i++) { + if (i < dstart || i >= dend) { + char c = dest.charAt(i); + destByteCount += (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3); + } + } + int keepBytes = mMaxBytes - destByteCount; + if (keepBytes <= 0) { + return ""; + } else if (keepBytes >= srcByteCount) { + return null; // use original dest string + } else { + // find end position of largest sequence that fits in keepBytes + for (int i = start; i < end; i++) { + char c = source.charAt(i); + keepBytes -= (c < (char) 0x0080) ? 1 : (c < (char) 0x0800 ? 2 : 3); + if (keepBytes < 0) { + return source.subSequence(start, i); + } + } + // If the entire substring fits, we should have returned null + // above, so this line should not be reached. If for some + // reason it is, return null to use the original dest string. + return null; + } + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/messages_fragment.xml b/app/src/main/res/layout/messages_fragment.xml index 2785553cf..3a101a93f 100644 --- a/app/src/main/res/layout/messages_fragment.xml +++ b/app/src/main/res/layout/messages_fragment.xml @@ -80,7 +80,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textMultiLine|textCapSentences" - android:maxLength="228" android:text="" />