1f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir/* 2f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * Copyright (C) 2017 The Android Open Source Project 3f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 4f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * Licensed under the Apache License, Version 2.0 (the "License"); 5f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * you may not use this file except in compliance with the License. 6f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * You may obtain a copy of the License at 7f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 8f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * http://www.apache.org/licenses/LICENSE-2.0 9f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 10f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * Unless required by applicable law or agreed to in writing, software 11f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * distributed under the License is distributed on an "AS IS" BASIS, 12f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * See the License for the specific language governing permissions and 14f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * limitations under the License. 15f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */ 16f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirpackage android.support.text.emoji.widget; 17f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 18c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinirimport android.os.Build; 19f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.support.annotation.NonNull; 20c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinirimport android.support.annotation.Nullable; 21c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinirimport android.support.annotation.RequiresApi; 22c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinirimport android.support.text.emoji.EmojiCompat; 23f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.support.v4.util.Preconditions; 24f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.InputFilter; 25f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.method.PasswordTransformationMethod; 26f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.text.method.TransformationMethod; 27f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirimport android.widget.TextView; 28f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 29f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir/** 30c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * Utility class to enhance custom TextView widgets with {@link EmojiCompat}. 31c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * <pre> 32c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * public class MyEmojiTextView extends TextView { 33c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * public MyEmojiTextView(Context context) { 34c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * super(context); 35c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * init(); 36c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * } 37c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * // .. 38c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * private void init() { 39c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * getEmojiTextViewHelper().updateTransformationMethod(); 40c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * } 41c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * 42c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * {@literal @}Override 43c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * public void setFilters(InputFilter[] filters) { 44c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * super.setFilters(getEmojiTextViewHelper().getFilters(filters)); 45c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * } 46c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * 47c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * {@literal @}Override 48c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * public void setAllCaps(boolean allCaps) { 49c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * super.setAllCaps(allCaps); 50c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * getEmojiTextViewHelper().setAllCaps(allCaps); 51c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * } 52c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * 53c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * private EmojiTextViewHelper getEmojiTextViewHelper() { 54c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * if (mEmojiTextViewHelper == null) { 55c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * mEmojiTextViewHelper = new EmojiTextViewHelper(this); 56c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * } 57c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * return mEmojiTextViewHelper; 58c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * } 59c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * } 60c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * </pre> 61f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */ 62f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinirpublic final class EmojiTextViewHelper { 63c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 64c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir private final HelperInternal mHelper; 65f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 66f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir /** 67f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * Default constructor. 68f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 69f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * @param textView TextView instance 70f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */ 71f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir public EmojiTextViewHelper(@NonNull TextView textView) { 72f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir Preconditions.checkNotNull(textView, "textView cannot be null"); 73c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir mHelper = Build.VERSION.SDK_INT >= 19 ? new HelperInternal19(textView) 74c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir : new HelperInternal(); 75f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 76f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 77f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir /** 78f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * Updates widget's TransformationMethod so that the transformed text can be processed. 79c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * Should be called in the widget constructor. When used on devices running API 18 or below, 80c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * this method does nothing. 81f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 82c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * @see #wrapTransformationMethod(TransformationMethod) 83f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */ 84f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir public void updateTransformationMethod() { 85c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir mHelper.updateTransformationMethod(); 86f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 87f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 88f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir /** 89f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * Appends EmojiCompat InputFilters to the widget InputFilters. Should be called by {@link 90c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * TextView#setFilters(InputFilter[])} to update the InputFilters. When used on devices running 91c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * API 18 or below, this method returns {@code filters} that is given as a parameter. 92f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 93f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * @param filters InputFilter array passed to {@link TextView#setFilters(InputFilter[])} 94f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 95f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * @return same copy if the array already contains EmojiCompat InputFilter. A new array copy if 96f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * not. 97f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */ 98c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir @NonNull 99f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir public InputFilter[] getFilters(@NonNull final InputFilter[] filters) { 100c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir return mHelper.getFilters(filters); 101f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 102f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 103f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir /** 104c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * Returns transformation method that can update the transformed text to display emojis. When 105c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * used on devices running API 18 or below, this method returns {@code transformationMethod} 106c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * that is given as a parameter. 107f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 108f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * @param transformationMethod instance to be wrapped 109f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */ 110c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir @Nullable 111c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir public TransformationMethod wrapTransformationMethod( 112c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir @Nullable TransformationMethod transformationMethod) { 113c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir return mHelper.wrapTransformationMethod(transformationMethod); 114f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 115f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir 116f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir /** 117c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * Call when allCaps is set on TextView. When used on devices running API 18 or below, this 118c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir * method does nothing. 119f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * 120f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir * @param allCaps allCaps parameter passed to {@link TextView#setAllCaps(boolean)} 121f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir */ 122f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir public void setAllCaps(boolean allCaps) { 123c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir mHelper.setAllCaps(allCaps); 124c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 125c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 126c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir private static class HelperInternal { 127c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 128c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir void updateTransformationMethod() { 129c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir // do nothing 130c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 131c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 132c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir InputFilter[] getFilters(@NonNull final InputFilter[] filters) { 133c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir return filters; 134c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 135c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 136c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir TransformationMethod wrapTransformationMethod(TransformationMethod transformationMethod) { 137c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir return transformationMethod; 138c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 139c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 140c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir void setAllCaps(boolean allCaps) { 141c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir // do nothing 142f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 143f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir } 144c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 145c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir @RequiresApi(19) 146c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir private static class HelperInternal19 extends HelperInternal { 147c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir private final TextView mTextView; 148c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir private final EmojiInputFilter mEmojiInputFilter; 149c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 150c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir HelperInternal19(TextView textView) { 151c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir mTextView = textView; 152c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir mEmojiInputFilter = new EmojiInputFilter(textView); 153c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 154c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 155c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir @Override 156c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir void updateTransformationMethod() { 157c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir final TransformationMethod tm = mTextView.getTransformationMethod(); 158c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir if (tm != null && !(tm instanceof PasswordTransformationMethod)) { 159c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir mTextView.setTransformationMethod(wrapTransformationMethod(tm)); 160c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 161c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 162c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 163c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir @Override 164c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir InputFilter[] getFilters(@NonNull final InputFilter[] filters) { 165c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir final int count = filters.length; 166c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir for (int i = 0; i < count; i++) { 167c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir if (filters[i] instanceof EmojiInputFilter) { 168c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir return filters; 169c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 170c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 171c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir final InputFilter[] newFilters = new InputFilter[filters.length + 1]; 172c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir System.arraycopy(filters, 0, newFilters, 0, count); 173c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir newFilters[count] = mEmojiInputFilter; 174c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir return newFilters; 175c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 176c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 177c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir @Override 178c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir TransformationMethod wrapTransformationMethod(TransformationMethod transformationMethod) { 179c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir if (transformationMethod instanceof EmojiTransformationMethod) { 180c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir return transformationMethod; 181c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 182c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir return new EmojiTransformationMethod(transformationMethod); 183c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 184c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 185c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir @Override 186c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir void setAllCaps(boolean allCaps) { 187c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir // When allCaps is set to false TextView sets the transformation method to be null. We 188c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir // are only interested when allCaps is set to true in order to wrap the original method. 189c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir if (allCaps) { 190c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir updateTransformationMethod(); 191c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 192c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 193c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir 194c79a89a2b8631c9ee81ae015ea74a6e1ac10de14Siyamed Sinir } 195f8ec169d022fbed42fd82091d24c45f3767cdfe7Siyamed Sinir} 196