EmojiEditTextHelper.java revision d6e62a9d119a07f4d2bd686f357d8a667085fe71
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package android.support.text.emoji.widget; 17 18import android.os.Build; 19import android.support.annotation.IntRange; 20import android.support.annotation.NonNull; 21import android.support.annotation.RequiresApi; 22import android.support.text.emoji.EmojiCompat; 23import android.support.v4.util.Preconditions; 24import android.text.method.KeyListener; 25import android.view.inputmethod.EditorInfo; 26import android.view.inputmethod.InputConnection; 27import android.widget.EditText; 28import android.widget.TextView; 29 30/** 31 * Utility class to enhance custom EditText widgets with {@link EmojiCompat}. 32 * <p/> 33 * <pre> 34 * public class MyEmojiEditText extends EditText { 35 * public MyEmojiEditText(Context context) { 36 * super(context); 37 * init(); 38 * } 39 * // ... 40 * private void init() { 41 * super.setKeyListener(getEmojiEditTextHelper().getKeyListener(getKeyListener())); 42 * } 43 * 44 * {@literal @}Override 45 * public void setKeyListener(android.text.method.KeyListener keyListener) { 46 * super.setKeyListener(getEmojiEditTextHelper().getKeyListener(keyListener)); 47 * } 48 * 49 * {@literal @}Override 50 * public InputConnection onCreateInputConnection(EditorInfo outAttrs) { 51 * InputConnection inputConnection = super.onCreateInputConnection(outAttrs); 52 * return getEmojiEditTextHelper().onCreateInputConnection(inputConnection, outAttrs); 53 * } 54 * 55 * private EmojiEditTextHelper getEmojiEditTextHelper() { 56 * if (mEmojiEditTextHelper == null) { 57 * mEmojiEditTextHelper = new EmojiEditTextHelper(this); 58 * } 59 * return mEmojiEditTextHelper; 60 * } 61 * } 62 * </pre> 63 * 64 */ 65public final class EmojiEditTextHelper { 66 67 private final HelperInternal mHelper; 68 private int mMaxEmojiCount; 69 70 /** 71 * Default constructor. 72 * 73 * @param editText EditText instance 74 */ 75 public EmojiEditTextHelper(@NonNull final EditText editText) { 76 Preconditions.checkNotNull(editText, "editText cannot be null"); 77 mHelper = Build.VERSION.SDK_INT >= 19 ? new HelperInternal19(editText) 78 : new HelperInternal(); 79 } 80 81 /** 82 * Set the maximum number of EmojiSpans to be added to a CharSequence. The number of spans in a 83 * CharSequence affects the performance of the EditText insert/delete operations. Insert/delete 84 * operations slow down as the number of spans increases. 85 * <p/> 86 * 87 * @param maxEmojiCount maximum number of EmojiSpans to be added to a single CharSequence, 88 * should be equal or greater than 0 89 * 90 * @see EmojiCompat#process(CharSequence, int, int, int) 91 */ 92 public void setMaxEmojiCount(@IntRange(from = 0) int maxEmojiCount) { 93 Preconditions.checkArgumentNonnegative(maxEmojiCount, 94 "maxEmojiCount should be greater than 0"); 95 mMaxEmojiCount = maxEmojiCount; 96 mHelper.setMaxEmojiCount(maxEmojiCount); 97 } 98 99 100 /** 101 * Returns the maximum number of EmojiSpans to be added to a CharSequence. 102 * 103 * @see #setMaxEmojiCount(int) 104 * @see EmojiCompat#process(CharSequence, int, int, int) 105 */ 106 public int getMaxEmojiCount() { 107 return mMaxEmojiCount; 108 } 109 110 /** 111 * Attaches EmojiCompat KeyListener to the widget. Should be called from {@link 112 * TextView#setKeyListener(KeyListener)}. Existing keyListener is wrapped into EmojiCompat 113 * KeyListener. When used on devices running API 18 or below, this method returns 114 * {@code keyListener} that is given as a parameter. 115 * 116 * @param keyListener KeyListener passed into {@link TextView#setKeyListener(KeyListener)} 117 * 118 * @return a new KeyListener instance that wraps {@code keyListener}. 119 */ 120 @NonNull 121 public KeyListener getKeyListener(@NonNull final KeyListener keyListener) { 122 Preconditions.checkNotNull(keyListener, "keyListener cannot be null"); 123 return mHelper.getKeyListener(keyListener); 124 } 125 126 /** 127 * Updates the InputConnection with emoji support. Should be called from {@link 128 * TextView#onCreateInputConnection(EditorInfo)}. When used on devices running API 18 or below, 129 * this method returns {@code inputConnection} that is given as a parameter. 130 * 131 * @param inputConnection InputConnection instance created by TextView 132 * @param outAttrs EditorInfo passed into 133 * {@link TextView#onCreateInputConnection(EditorInfo)} 134 * 135 * @return a new InputConnection instance that wraps {@code inputConnection} 136 */ 137 @NonNull 138 public InputConnection onCreateInputConnection(@NonNull final InputConnection inputConnection, 139 @NonNull final EditorInfo outAttrs) { 140 Preconditions.checkNotNull(inputConnection, "inputConnection cannot be null"); 141 return mHelper.onCreateInputConnection(inputConnection, outAttrs); 142 } 143 144 private static class HelperInternal { 145 146 KeyListener getKeyListener(@NonNull KeyListener keyListener) { 147 return keyListener; 148 } 149 150 InputConnection onCreateInputConnection(@NonNull InputConnection inputConnection, 151 @NonNull EditorInfo outAttrs) { 152 return inputConnection; 153 } 154 155 public void setMaxEmojiCount(int maxEmojiCount) { 156 // do nothing 157 } 158 } 159 160 @RequiresApi(19) 161 private static class HelperInternal19 extends HelperInternal { 162 private final EditText mEditText; 163 private final EmojiTextWatcher mTextWatcher; 164 165 HelperInternal19(@NonNull EditText editText) { 166 mEditText = editText; 167 mTextWatcher = new EmojiTextWatcher(mEditText); 168 mEditText.addTextChangedListener(mTextWatcher); 169 mEditText.setEditableFactory(EmojiEditableFactory.getInstance()); 170 } 171 172 @Override 173 public void setMaxEmojiCount(int maxEmojiCount) { 174 mTextWatcher.setMaxEmojiCount(maxEmojiCount); 175 } 176 177 @Override 178 KeyListener getKeyListener(@NonNull final KeyListener keyListener) { 179 if (keyListener instanceof EmojiKeyListener) { 180 return keyListener; 181 } 182 return new EmojiKeyListener(keyListener); 183 } 184 185 @Override 186 InputConnection onCreateInputConnection(@NonNull final InputConnection inputConnection, 187 @NonNull final EditorInfo outAttrs) { 188 if (inputConnection instanceof EmojiInputConnection) { 189 return inputConnection; 190 } 191 return new EmojiInputConnection(mEditText, inputConnection, outAttrs); 192 } 193 } 194} 195