EditMessage.java

  1package eu.siacs.conversations.ui.widget;
  2
  3import android.support.text.emoji.widget.EmojiEditText;
  4import android.support.v13.view.inputmethod.EditorInfoCompat;
  5import android.support.v13.view.inputmethod.InputConnectionCompat;
  6import android.support.v13.view.inputmethod.InputContentInfoCompat;
  7
  8import android.content.Context;
  9import android.os.Build;
 10import android.os.Bundle;
 11import android.os.Handler;
 12import android.text.Editable;
 13import android.text.InputFilter;
 14import android.text.Spanned;
 15import android.util.AttributeSet;
 16import android.view.KeyEvent;
 17import android.view.inputmethod.EditorInfo;
 18import android.view.inputmethod.InputConnection;
 19
 20import eu.siacs.conversations.Config;
 21
 22public class EditMessage extends EmojiEditText {
 23
 24	private static final InputFilter SPAN_FILTER = new InputFilter() {
 25
 26		@Override
 27		public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
 28			return source instanceof Spanned ? source.toString() : source;
 29		}
 30	};
 31	protected Handler mTypingHandler = new Handler();
 32	protected KeyboardListener keyboardListener;
 33	private OnCommitContentListener mCommitContentListener = null;
 34	private String[] mimeTypes = null;
 35	private boolean isUserTyping = false;
 36	protected Runnable mTypingTimeout = new Runnable() {
 37		@Override
 38		public void run() {
 39			if (isUserTyping && keyboardListener != null) {
 40				keyboardListener.onTypingStopped();
 41				isUserTyping = false;
 42			}
 43		}
 44	};
 45	private boolean lastInputWasTab = false;
 46
 47	public EditMessage(Context context, AttributeSet attrs) {
 48		super(context, attrs);
 49	}
 50
 51	public EditMessage(Context context) {
 52		super(context);
 53	}
 54
 55	@Override
 56	public boolean onKeyDown(int keyCode, KeyEvent e) {
 57		if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
 58			lastInputWasTab = false;
 59			if (keyboardListener != null && keyboardListener.onEnterPressed()) {
 60				return true;
 61			}
 62		} else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
 63			if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
 64				lastInputWasTab = true;
 65				return true;
 66			}
 67		} else {
 68			lastInputWasTab = false;
 69		}
 70		return super.onKeyDown(keyCode, e);
 71	}
 72
 73	@Override
 74	public int getAutofillType() {
 75		return AUTOFILL_TYPE_NONE;
 76	}
 77
 78	@Override
 79	public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
 80		super.onTextChanged(text, start, lengthBefore, lengthAfter);
 81		lastInputWasTab = false;
 82		if (this.mTypingHandler != null && this.keyboardListener != null) {
 83			this.mTypingHandler.removeCallbacks(mTypingTimeout);
 84			this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
 85			final int length = text.length();
 86			if (!isUserTyping && length > 0) {
 87				this.isUserTyping = true;
 88				this.keyboardListener.onTypingStarted();
 89			} else if (length == 0) {
 90				this.isUserTyping = false;
 91				this.keyboardListener.onTextDeleted();
 92			}
 93			this.keyboardListener.onTextChanged();
 94		}
 95	}
 96
 97	public void setKeyboardListener(KeyboardListener listener) {
 98		this.keyboardListener = listener;
 99		if (listener != null) {
100			this.isUserTyping = false;
101		}
102	}
103
104	@Override
105	public boolean onTextContextMenuItem(int id) {
106		if (id == android.R.id.paste) {
107			if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
108				return super.onTextContextMenuItem(android.R.id.pasteAsPlainText);
109			} else {
110				Editable editable = getEditableText();
111				InputFilter[] filters = editable.getFilters();
112				InputFilter[] tempFilters = new InputFilter[filters != null ? filters.length + 1 : 1];
113				if (filters != null) {
114					System.arraycopy(filters, 0, tempFilters, 1, filters.length);
115				}
116				tempFilters[0] = SPAN_FILTER;
117				editable.setFilters(tempFilters);
118				try {
119					return super.onTextContextMenuItem(id);
120				} finally {
121					editable.setFilters(filters);
122				}
123			}
124		} else {
125			return super.onTextContextMenuItem(id);
126		}
127	}
128
129	public void setRichContentListener(String[] mimeTypes, OnCommitContentListener listener) {
130		this.mimeTypes = mimeTypes;
131		this.mCommitContentListener = listener;
132	}
133
134	@Override
135	public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
136		final InputConnection ic = super.onCreateInputConnection(editorInfo);
137
138		if (mimeTypes != null && mCommitContentListener != null) {
139			EditorInfoCompat.setContentMimeTypes(editorInfo, mimeTypes);
140			return InputConnectionCompat.createWrapper(ic, editorInfo, new InputConnectionCompat.OnCommitContentListener() {
141				@Override
142				public boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts) {
143					return EditMessage.this.mCommitContentListener.onCommitContent(inputContentInfo, flags, opts, mimeTypes);
144				}
145			});
146		} else {
147			return ic;
148		}
149	}
150
151	public interface OnCommitContentListener {
152		boolean onCommitContent(InputContentInfoCompat inputContentInfo, int flags, Bundle opts, String[] mimeTypes);
153	}
154
155	public interface KeyboardListener {
156		boolean onEnterPressed();
157
158		void onTypingStarted();
159
160		void onTypingStopped();
161
162		void onTextDeleted();
163
164		void onTextChanged();
165
166		boolean onTabPressed(boolean repeated);
167	}
168}