182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/* 282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Copyright (C) 2017 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 */ 1682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpackage android.support.text.emoji; 1782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 1882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 1982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 2082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.graphics.Paint; 2182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.annotation.NonNull; 2277b5c5b734f9f665577d1e3d178615db43ae1d4fSiyamed Sinirimport android.support.annotation.RequiresApi; 2382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.annotation.RestrictTo; 2482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.annotation.VisibleForTesting; 2582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.v4.util.Preconditions; 2682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.text.style.ReplacementSpan; 2782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 2882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/** 2982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Base span class for the emoji replacement. When an emoji is found and needs to be replaced in a 3082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * CharSequence, an instance of this class is added to the CharSequence. 3182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 3277b5c5b734f9f665577d1e3d178615db43ae1d4fSiyamed Sinir@RequiresApi(19) 3382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpublic abstract class EmojiSpan extends ReplacementSpan { 3482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 3582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 3682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Temporary object to calculate the size of the span. 3782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 3882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private final Paint.FontMetricsInt mTmpFontMetrics = new Paint.FontMetricsInt(); 3982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 4082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 4182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Information about emoji. This is not parcelled since we do not want multiple objects 4282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * representing same emoji to be in memory. When unparcelled, EmojiSpan tries to set it back 4382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * using the singleton EmojiCompat instance. 4482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 4582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private final EmojiMetadata mMetadata; 4682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 4782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 4882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Cached width of the span. Width is calculated according to the font metrics. 4982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 5082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private short mWidth = -1; 5182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 5282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 5382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Cached height of the span. Height is calculated according to the font metrics. 5482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 5582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private short mHeight = -1; 5682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 5782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 5882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Cached ratio of current font height to emoji image height. 5982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 6082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir private float mRatio = 1.0f; 6182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 6282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 6382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Default constructor. 6482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 6582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @param metadata information about the emoji, cannot be {@code null} 6682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 6782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 6882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 6982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 7082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir EmojiSpan(@NonNull final EmojiMetadata metadata) { 7182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir Preconditions.checkNotNull(metadata, "metadata cannot be null"); 7282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir mMetadata = metadata; 7382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 7482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 7582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @Override 7682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir public int getSize(@NonNull final Paint paint, final CharSequence text, final int start, 7782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final int end, final Paint.FontMetricsInt fm) { 7882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir paint.getFontMetricsInt(mTmpFontMetrics); 7982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final int fontHeight = Math.abs(mTmpFontMetrics.descent - mTmpFontMetrics.ascent); 8082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 8182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir mRatio = fontHeight * 1.0f / mMetadata.getHeight(); 8282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir mHeight = (short) (mMetadata.getHeight() * mRatio); 8382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir mWidth = (short) (mMetadata.getWidth() * mRatio); 8482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 8582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir if (fm != null) { 8682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir fm.ascent = mTmpFontMetrics.ascent; 8782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir fm.descent = mTmpFontMetrics.descent; 8882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir fm.top = mTmpFontMetrics.top; 8982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir fm.bottom = mTmpFontMetrics.bottom; 9082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 9182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 9282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mWidth; 9382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 9482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 9582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 9682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 9782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 9882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 9982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final EmojiMetadata getMetadata() { 10082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mMetadata; 10182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 10282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 10382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 10482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @return width of the span 10582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 10682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 10782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 10882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 10982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final int getWidth() { 11082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mWidth; 11182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 11282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 11382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 11482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @return height of the span 11582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 11682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 11782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 11882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 11982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final int getHeight() { 12082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mHeight; 12182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 12282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 12382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 12482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 12582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 12682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 12782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir final float getRatio() { 12882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return mRatio; 12982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 13082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir 13182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir /** 13282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @return unique id for the emoji that this EmojiSpan is used for 13382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * 13482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * @hide 13582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */ 13682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @RestrictTo(LIBRARY_GROUP) 13782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir @VisibleForTesting 13882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir public final int getId() { 13982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir return getMetadata().getId(); 14082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir } 14182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir} 142