1package eu.siacs.conversations.ui;
2
3import android.content.Context;
4import android.os.Build;
5import android.os.Handler;
6import android.text.Editable;
7import android.text.InputFilter;
8import android.text.Spanned;
9import android.util.AttributeSet;
10import android.view.KeyEvent;
11import android.widget.EditText;
12
13import eu.siacs.conversations.Config;
14
15public class EditMessage extends EditText {
16
17 public EditMessage(Context context, AttributeSet attrs) {
18 super(context, attrs);
19 }
20
21 public EditMessage(Context context) {
22 super(context);
23 }
24
25 protected Handler mTypingHandler = new Handler();
26
27 protected Runnable mTypingTimeout = new Runnable() {
28 @Override
29 public void run() {
30 if (isUserTyping && keyboardListener != null) {
31 keyboardListener.onTypingStopped();
32 isUserTyping = false;
33 }
34 }
35 };
36
37 private boolean isUserTyping = false;
38
39 private boolean lastInputWasTab = false;
40
41 protected KeyboardListener keyboardListener;
42
43 @Override
44 public boolean onKeyDown(int keyCode, KeyEvent e) {
45 if (keyCode == KeyEvent.KEYCODE_ENTER && !e.isShiftPressed()) {
46 lastInputWasTab = false;
47 if (keyboardListener != null && keyboardListener.onEnterPressed()) {
48 return true;
49 }
50 } else if (keyCode == KeyEvent.KEYCODE_TAB && !e.isAltPressed() && !e.isCtrlPressed()) {
51 if (keyboardListener != null && keyboardListener.onTabPressed(this.lastInputWasTab)) {
52 lastInputWasTab = true;
53 return true;
54 }
55 } else {
56 lastInputWasTab = false;
57 }
58 return super.onKeyDown(keyCode, e);
59 }
60
61 @Override
62 public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
63 super.onTextChanged(text,start,lengthBefore,lengthAfter);
64 lastInputWasTab = false;
65 if (this.mTypingHandler != null && this.keyboardListener != null) {
66 this.mTypingHandler.removeCallbacks(mTypingTimeout);
67 this.mTypingHandler.postDelayed(mTypingTimeout, Config.TYPING_TIMEOUT * 1000);
68 final int length = text.length();
69 if (!isUserTyping && length > 0) {
70 this.isUserTyping = true;
71 this.keyboardListener.onTypingStarted();
72 } else if (length == 0) {
73 this.isUserTyping = false;
74 this.keyboardListener.onTextDeleted();
75 }
76 this.keyboardListener.onTextChanged();
77 }
78 }
79
80 public void setKeyboardListener(KeyboardListener listener) {
81 this.keyboardListener = listener;
82 if (listener != null) {
83 this.isUserTyping = false;
84 }
85 }
86
87 public interface KeyboardListener {
88 boolean onEnterPressed();
89 void onTypingStarted();
90 void onTypingStopped();
91 void onTextDeleted();
92 void onTextChanged();
93 boolean onTabPressed(boolean repeated);
94 }
95
96 private static final InputFilter SPAN_FILTER = new InputFilter() {
97
98 @Override
99 public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
100 return source instanceof Spanned ? source.toString() : source;
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}