182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/* 2ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikas * Copyright 2018 The Android Open Source Project 382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Licensed under the Apache License, Version 2.0 (the "License"); 582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * you may not use this file except in compliance with the License. 682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * You may obtain a copy of the License at 782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * http://www.apache.org/licenses/LICENSE-2.0 982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 1082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Unless required by applicable law or agreed to in writing, software 1182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * distributed under the License is distributed on an "AS IS" BASIS, 1282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * See the License for the specific language governing permissions and 1482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * limitations under the License. 1582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 16ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.emoji.text; 1782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 18ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; 1982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 2082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.graphics.Paint; 2138746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikasimport android.text.style.ReplacementSpan; 2238746a682208c764867ffe4415d0b62fb22b5b9aAurimas Liutikas 23ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.NonNull; 24ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RequiresApi; 25ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.RestrictTo; 26ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.annotation.VisibleForTesting; 27ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikasimport androidx.core.util.Preconditions; 2882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 2982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/** 3082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Base span class for the emoji replacement. When an emoji is found and needs to be replaced in a 3182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * CharSequence, an instance of this class is added to the CharSequence. 3282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 3377b5c5b734f9f665577d1e3d178615db43ae1d4fSiyamed Sinir@RequiresApi(19) 3482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpublic abstract class EmojiSpan extends ReplacementSpan { 3582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 3682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 3782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Temporary object to calculate the size of the span. 3882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 3982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private final Paint.FontMetricsInt mTmpFontMetrics = new Paint.FontMetricsInt(); 4082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 4182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 4282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Information about emoji. This is not parcelled since we do not want multiple objects 4382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * representing same emoji to be in memory. When unparcelled, EmojiSpan tries to set it back 4482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * using the singleton EmojiCompat instance. 4582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 4682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private final EmojiMetadata mMetadata; 4782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 4882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 4982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Cached width of the span. Width is calculated according to the font metrics. 5082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 5182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private short mWidth = -1; 5282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 5382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 5482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Cached height of the span. Height is calculated according to the font metrics. 5582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 5682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private short mHeight = -1; 5782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 5882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 5982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Cached ratio of current font height to emoji image height. 6082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 6182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private float mRatio = 1.0f; 6282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 6382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 6482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Default constructor. 6582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 6682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @param metadata information about the emoji, cannot be {@code null} 6782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 6882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 6982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 7082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 7182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir EmojiSpan(@NonNull final EmojiMetadata metadata) { 7282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir Preconditions.checkNotNull(metadata, "metadata cannot be null"); 7382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir mMetadata = metadata; 7482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 7582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 7682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @Override 7782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir public int getSize(@NonNull final Paint paint, final CharSequence text, final int start, 7882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final int end, final Paint.FontMetricsInt fm) { 7982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir paint.getFontMetricsInt(mTmpFontMetrics); 8082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final int fontHeight = Math.abs(mTmpFontMetrics.descent - mTmpFontMetrics.ascent); 8182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 8282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir mRatio = fontHeight * 1.0f / mMetadata.getHeight(); 8382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir mHeight = (short) (mMetadata.getHeight() * mRatio); 8482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir mWidth = (short) (mMetadata.getWidth() * mRatio); 8582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 8682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir if (fm != null) { 8782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir fm.ascent = mTmpFontMetrics.ascent; 8882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir fm.descent = mTmpFontMetrics.descent; 8982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir fm.top = mTmpFontMetrics.top; 9082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir fm.bottom = mTmpFontMetrics.bottom; 9182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 9282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 9382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mWidth; 9482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 9582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 9682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 9782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 9882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 9982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 10082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final EmojiMetadata getMetadata() { 10182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mMetadata; 10282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 10382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 10482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 10582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @return width of the span 10682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 10782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 10882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 10982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 11082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final int getWidth() { 11182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mWidth; 11282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 11382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 11482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 11582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @return height of the span 11682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 11782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 11882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 11982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 12082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final int getHeight() { 12182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mHeight; 12282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 12382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 12482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 12582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 12682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 12782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 12882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final float getRatio() { 12982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mRatio; 13082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 13182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 13282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 13382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @return unique id for the emoji that this EmojiSpan is used for 13482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 13582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 13682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 13782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 13882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @VisibleForTesting 13982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir public final int getId() { 14082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return getMetadata().getId(); 14182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 14282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir} 143