run keyboard listeners on background executor

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java | 342 +-
1 file changed, 175 insertions(+), 167 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/widget/EditMessage.java 🔗

@@ -19,176 +19,184 @@ import android.view.KeyEvent;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
 
 public class EditMessage extends EmojiWrapperEditText {
 
-	private static final InputFilter SPAN_FILTER = (source, start, end, dest, dstart, dend) -> source instanceof Spanned ? source.toString() : source;
-	protected Handler mTypingHandler = new Handler();
-	protected KeyboardListener keyboardListener;
-	private OnCommitContentListener mCommitContentListener = null;
-	private String[] mimeTypes = null;
-	private boolean isUserTyping = false;
-	protected Runnable mTypingTimeout = new Runnable() {
-		@Override
-		public void run() {
-			if (isUserTyping && keyboardListener != null) {
-				keyboardListener.onTypingStopped();
-				isUserTyping = false;
-			}
-		}
-	};
-	private boolean lastInputWasTab = false;
-
-	public EditMessage(Context context, AttributeSet attrs) {
-		super(context, attrs);
-	}
-
-	public EditMessage(Context context) {
-		super(context);
-	}
-
-	@Override
-	public boolean onKeyDown(int keyCode, KeyEvent e) {
-		if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
-			lastInputWasTab = false;
-			if (keyboardListener != null && keyboardListener.onEnterPressed()) {
-				return true;
-			}
-		} else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
-			if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
-				lastInputWasTab = true;
-				return true;
-			}
-		} else {
-			lastInputWasTab = false;
-		}
-		return super.onKeyDown(keyCode, e);
-	}
-
-	@Override
-	public int getAutofillType() {
-		return AUTOFILL_TYPE_NONE;
-	}
-
-	@Override
-	public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
-		super.onTextChanged(text, start, lengthBefore, lengthAfter);
-		lastInputWasTab = false;
-		if (this.mTypingHandler != null && this.keyboardListener != null) {
-			this.mTypingHandler.removeCallbacks(mTypingTimeout);
-			this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
-			final int length = text.length();
-			if (!isUserTyping && length > 0) {
-				this.isUserTyping = true;
-				this.keyboardListener.onTypingStarted();
-			} else if (length == 0) {
-				this.isUserTyping = false;
-				this.keyboardListener.onTextDeleted();
-			}
-			this.keyboardListener.onTextChanged();
-		}
-	}
-
-	public void setKeyboardListener(KeyboardListener listener) {
-		this.keyboardListener = listener;
-		if (listener != null) {
-			this.isUserTyping = false;
-		}
-	}
-
-	@Override
-	public boolean onTextContextMenuItem(int id) {
-		if (id == android.R.id.paste) {
-			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
-				return super.onTextContextMenuItem(android.R.id.pasteAsPlainText);
-			} else {
-				Editable editable = getEditableText();
-				InputFilter[] filters = editable.getFilters();
-				InputFilter[] tempFilters = new InputFilter[filters != null ? filters.length + 1 : 1];
-				if (filters != null) {
-					System.arraycopy(filters, 0, tempFilters, 1, filters.length);
-				}
-				tempFilters[0] = SPAN_FILTER;
-				editable.setFilters(tempFilters);
-				try {
-					return super.onTextContextMenuItem(id);
-				} finally {
-					editable.setFilters(filters);
-				}
-			}
-		} else {
-			return super.onTextContextMenuItem(id);
-		}
-	}
-
-	public void setRichContentListener(String[] mimeTypes, OnCommitContentListener listener) {
-		this.mimeTypes = mimeTypes;
-		this.mCommitContentListener = listener;
-	}
-
-	public void insertAsQuote(String text) {
-		text = text.replaceAll("(\n *){2,}", "\n").replaceAll("(^|\n)", "$1> ").replaceAll("\n$", "");
-		Editable editable = getEditableText();
-		int position = getSelectionEnd();
-		if (position == -1) position = editable.length();
-		if (position > 0 && editable.charAt(position - 1) != '\n') {
-			editable.insert(position++, "\n");
-		}
-		editable.insert(position, text);
-		position += text.length();
-		editable.insert(position++, "\n");
-		if (position < editable.length() && editable.charAt(position) != '\n') {
-			editable.insert(position, "\n");
-		}
-		setSelection(position);
-	}
-
-	@Override
-	public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
-		final InputConnection ic = super.onCreateInputConnection(editorInfo);
-
-		if (mimeTypes != null && mCommitContentListener != null && ic != null) {
-			EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
-			return InputConnectionCompat.createWrapper(ic, editorInfo, (inputContentInfo, flags, opts) -> EditMessage.this.mCommitContentListener.onCommitContent(inputContentInfo, flags, opts, mimeTypes));
-		} else {
-			return ic;
-		}
-	}
-
-	public void refreshIme() {
-		SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getContext());
-		final boolean usingEnterKey = p.getBoolean("display_enter_key", getResources().getBoolean(R.bool.display_enter_key));
-		final boolean enterIsSend = p.getBoolean("enter_is_send", getResources().getBoolean(R.bool.enter_is_send));
-
-		if (usingEnterKey && enterIsSend) {
-			setInputType(getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
-			setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
-		} else if (usingEnterKey) {
-			setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
-			setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
-		} else {
-			setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
-			setInputType(getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
-		}
-	}
-
-	public interface OnCommitContentListener {
-		boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts, String[] mimeTypes);
-	}
-
-	public interface KeyboardListener {
-		boolean onEnterPressed();
-
-		void onTypingStarted();
-
-		void onTypingStopped();
-
-		void onTextDeleted();
-
-		void onTextChanged();
-
-		boolean onTabPressed(boolean repeated);
-	}
+    private static final InputFilter SPAN_FILTER = (source, start, end, dest, dstart, dend) -> source instanceof Spanned ? source.toString() : source;
+    private final ExecutorService executor = Executors.newSingleThreadExecutor();
+    protected Handler mTypingHandler = new Handler();
+    protected KeyboardListener keyboardListener;
+    private OnCommitContentListener mCommitContentListener = null;
+    private String[] mimeTypes = null;
+    private boolean isUserTyping = false;
+    private final Runnable mTypingTimeout = new Runnable() {
+        @Override
+        public void run() {
+            if (isUserTyping && keyboardListener != null) {
+                keyboardListener.onTypingStopped();
+                isUserTyping = false;
+            }
+        }
+    };
+    private boolean lastInputWasTab = false;
+
+    public EditMessage(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public EditMessage(Context context) {
+        super(context);
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent e) {
+        if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
+            lastInputWasTab = false;
+            if (keyboardListener != null && keyboardListener.onEnterPressed()) {
+                return true;
+            }
+        } else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
+            if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
+                lastInputWasTab = true;
+                return true;
+            }
+        } else {
+            lastInputWasTab = false;
+        }
+        return super.onKeyDown(keyCode, e);
+    }
+
+    @Override
+    public int getAutofillType() {
+        return AUTOFILL_TYPE_NONE;
+    }
+
+
+    @Override
+    public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
+        super.onTextChanged(text, start, lengthBefore, lengthAfter);
+        lastInputWasTab = false;
+        if (this.mTypingHandler != null && this.keyboardListener != null) {
+            executor.execute(() -> triggerKeyboardEvents(text.length()));
+        }
+    }
+
+    private void triggerKeyboardEvents(final int length) {
+        this.mTypingHandler.removeCallbacks(mTypingTimeout);
+        this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
+        if (!isUserTyping && length > 0) {
+            this.isUserTyping = true;
+            this.keyboardListener.onTypingStarted();
+        } else if (length == 0) {
+            this.isUserTyping = false;
+            this.keyboardListener.onTextDeleted();
+        }
+        this.keyboardListener.onTextChanged();
+    }
+
+    public void setKeyboardListener(KeyboardListener listener) {
+        this.keyboardListener = listener;
+        if (listener != null) {
+            this.isUserTyping = false;
+        }
+    }
+
+    @Override
+    public boolean onTextContextMenuItem(int id) {
+        if (id == android.R.id.paste) {
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+                return super.onTextContextMenuItem(android.R.id.pasteAsPlainText);
+            } else {
+                Editable editable = getEditableText();
+                InputFilter[] filters = editable.getFilters();
+                InputFilter[] tempFilters = new InputFilter[filters != null ? filters.length + 1 : 1];
+                if (filters != null) {
+                    System.arraycopy(filters, 0, tempFilters, 1, filters.length);
+                }
+                tempFilters[0] = SPAN_FILTER;
+                editable.setFilters(tempFilters);
+                try {
+                    return super.onTextContextMenuItem(id);
+                } finally {
+                    editable.setFilters(filters);
+                }
+            }
+        } else {
+            return super.onTextContextMenuItem(id);
+        }
+    }
+
+    public void setRichContentListener(String[] mimeTypes, OnCommitContentListener listener) {
+        this.mimeTypes = mimeTypes;
+        this.mCommitContentListener = listener;
+    }
+
+    public void insertAsQuote(String text) {
+        text = text.replaceAll("(\n *){2,}", "\n").replaceAll("(^|\n)", "$1> ").replaceAll("\n$", "");
+        Editable editable = getEditableText();
+        int position = getSelectionEnd();
+        if (position == -1) position = editable.length();
+        if (position > 0 && editable.charAt(position - 1) != '\n') {
+            editable.insert(position++, "\n");
+        }
+        editable.insert(position, text);
+        position += text.length();
+        editable.insert(position++, "\n");
+        if (position < editable.length() && editable.charAt(position) != '\n') {
+            editable.insert(position, "\n");
+        }
+        setSelection(position);
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+        final InputConnection ic = super.onCreateInputConnection(editorInfo);
+
+        if (mimeTypes != null && mCommitContentListener != null && ic != null) {
+            EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
+            return InputConnectionCompat.createWrapper(ic, editorInfo, (inputContentInfo, flags, opts) -> EditMessage.this.mCommitContentListener.onCommitContent(inputContentInfo, flags, opts, mimeTypes));
+        } else {
+            return ic;
+        }
+    }
+
+    public void refreshIme() {
+        SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(getContext());
+        final boolean usingEnterKey = p.getBoolean("display_enter_key", getResources().getBoolean(R.bool.display_enter_key));
+        final boolean enterIsSend = p.getBoolean("enter_is_send", getResources().getBoolean(R.bool.enter_is_send));
+
+        if (usingEnterKey && enterIsSend) {
+            setInputType(getInputType() & (~InputType.TYPE_TEXT_FLAG_MULTI_LINE));
+            setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
+        } else if (usingEnterKey) {
+            setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+            setInputType(getInputType() & (~InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE));
+        } else {
+            setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_MULTI_LINE);
+            setInputType(getInputType() | InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE);
+        }
+    }
+
+    public interface OnCommitContentListener {
+        boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts, String[] mimeTypes);
+    }
+
+    public interface KeyboardListener {
+        boolean onEnterPressed();
+
+        void onTypingStarted();
+
+        void onTypingStopped();
+
+        void onTextDeleted();
+
+        void onTextChanged();
+
+        boolean onTabPressed(boolean repeated);
+    }
 }