EmojiTextViewHelper.java revision 71505ed0dcbc4ca21d126eee3ccfb616ea35666e
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.NonNull; 20import android.support.annotation.Nullable; 21import android.support.text.emoji.EmojiCompat; 22import android.support.v4.util.Preconditions; 23import android.text.InputFilter; 24import android.text.method.PasswordTransformationMethod; 25import android.text.method.TransformationMethod; 26import android.widget.TextView; 27 28/** 29 * Utility class to enhance custom TextView widgets with {@link EmojiCompat}. 30 * <pre> 31 * public class MyEmojiTextView extends TextView { 32 * public MyEmojiTextView(Context context) { 33 * super(context); 34 * init(); 35 * } 36 * // .. 37 * private void init() { 38 * getEmojiTextViewHelper().updateTransformationMethod(); 39 * } 40 * 41 * {@literal @}Override 42 * public void setFilters(InputFilter[] filters) { 43 * super.setFilters(getEmojiTextViewHelper().getFilters(filters)); 44 * } 45 * 46 * {@literal @}Override 47 * public void setAllCaps(boolean allCaps) { 48 * super.setAllCaps(allCaps); 49 * getEmojiTextViewHelper().setAllCaps(allCaps); 50 * } 51 * 52 * private EmojiTextViewHelper getEmojiTextViewHelper() { 53 * if (mEmojiTextViewHelper == null) { 54 * mEmojiTextViewHelper = new EmojiTextViewHelper(this); 55 * } 56 * return mEmojiTextViewHelper; 57 * } 58 * } 59 * </pre> 60 */ 61public final class EmojiTextViewHelper { 62 63 private final HelperInternal mHelper; 64 65 /** 66 * Default constructor. 67 * 68 * @param textView TextView instance 69 */ 70 public EmojiTextViewHelper(@NonNull TextView textView) { 71 Preconditions.checkNotNull(textView, "textView cannot be null"); 72 mHelper = Build.VERSION.SDK_INT >= 19 ? new HelperInternal19(textView) 73 : new HelperInternal(); 74 } 75 76 /** 77 * Updates widget's TransformationMethod so that the transformed text can be processed. 78 * Should be called in the widget constructor. When used on devices running API 18 or below, 79 * this method does nothing. 80 * 81 * @see #wrapTransformationMethod(TransformationMethod) 82 */ 83 public void updateTransformationMethod() { 84 mHelper.updateTransformationMethod(); 85 } 86 87 /** 88 * Appends EmojiCompat InputFilters to the widget InputFilters. Should be called by {@link 89 * TextView#setFilters(InputFilter[])} to update the InputFilters. When used on devices running 90 * API 18 or below, this method returns {@code filters} that is given as a parameter. 91 * 92 * @param filters InputFilter array passed to {@link TextView#setFilters(InputFilter[])} 93 * 94 * @return same copy if the array already contains EmojiCompat InputFilter. A new array copy if 95 * not. 96 */ 97 @NonNull 98 public InputFilter[] getFilters(@NonNull final InputFilter[] filters) { 99 return mHelper.getFilters(filters); 100 } 101 102 /** 103 * Returns transformation method that can update the transformed text to display emojis. When 104 * used on devices running API 18 or below, this method returns {@code transformationMethod} 105 * that is given as a parameter. 106 * 107 * @param transformationMethod instance to be wrapped 108 */ 109 @Nullable 110 public TransformationMethod wrapTransformationMethod( 111 @Nullable TransformationMethod transformationMethod) { 112 return mHelper.wrapTransformationMethod(transformationMethod); 113 } 114 115 /** 116 * Call when allCaps is set on TextView. When used on devices running API 18 or below, this 117 * method does nothing. 118 * 119 * @param allCaps allCaps parameter passed to {@link TextView#setAllCaps(boolean)} 120 */ 121 public void setAllCaps(boolean allCaps) { 122 mHelper.setAllCaps(allCaps); 123 } 124 125 private static class HelperInternal { 126 127 void updateTransformationMethod() { 128 // do nothing 129 } 130 131 InputFilter[] getFilters(@NonNull final InputFilter[] filters) { 132 return filters; 133 } 134 135 TransformationMethod wrapTransformationMethod(TransformationMethod transformationMethod) { 136 return transformationMethod; 137 } 138 139 void setAllCaps(boolean allCaps) { 140 // do nothing 141 } 142 } 143 144 private static class HelperInternal19 extends HelperInternal { 145 private final TextView mTextView; 146 private final EmojiInputFilter mEmojiInputFilter; 147 148 HelperInternal19(TextView textView) { 149 mTextView = textView; 150 mEmojiInputFilter = new EmojiInputFilter(textView); 151 } 152 153 @Override 154 void updateTransformationMethod() { 155 final TransformationMethod tm = mTextView.getTransformationMethod(); 156 if (tm != null && !(tm instanceof PasswordTransformationMethod)) { 157 mTextView.setTransformationMethod(wrapTransformationMethod(tm)); 158 } 159 } 160 161 @Override 162 InputFilter[] getFilters(@NonNull final InputFilter[] filters) { 163 final int count = filters.length; 164 for (int i = 0; i < count; i++) { 165 if (filters[i] instanceof EmojiInputFilter) { 166 return filters; 167 } 168 } 169 final InputFilter[] newFilters = new InputFilter[filters.length + 1]; 170 System.arraycopy(filters, 0, newFilters, 0, count); 171 newFilters[count] = mEmojiInputFilter; 172 return newFilters; 173 } 174 175 @Override 176 TransformationMethod wrapTransformationMethod(TransformationMethod transformationMethod) { 177 if (transformationMethod instanceof EmojiTransformationMethod) { 178 return transformationMethod; 179 } 180 return new EmojiTransformationMethod(transformationMethod); 181 } 182 183 @Override 184 void setAllCaps(boolean allCaps) { 185 // When allCaps is set to false TextView sets the transformation method to be null. We 186 // are only interested when allCaps is set to true in order to wrap the original method. 187 if (allCaps) { 188 updateTransformationMethod(); 189 } 190 } 191 192 } 193} 194