Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 112d9c7f authored by Roozbeh Pournader's avatar Roozbeh Pournader
Browse files

Remove EmojiFactory and its mentions from frameworks.

Bug: 18134313
Bug: 20158206
Change-Id: If46cdbd9d558e6592280b2b95f00b87d04de70a2
parent 456c7a76
Loading
Loading
Loading
Loading
+0 −289
Original line number Diff line number Diff line
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.emoji;

import android.graphics.Bitmap;

import java.lang.ref.WeakReference;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * A class for the factories which produce Emoji (pictgram) images.
 * This is intended to be used by IME, Email app, etc.
 * There's no plan to make this public for now.
 * @hide
 */
public final class EmojiFactory {
    // private static final String LOG_TAG = "EmojiFactory";
    
    private int sCacheSize = 100;
    
    // HashMap for caching Bitmap object. In order not to make a cache object
    // blow up, we use LinkedHashMap with size limit.
    private class CustomLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
        public CustomLinkedHashMap() {
            // These magic numbers are gotten from the source code of
            // LinkedHashMap.java and HashMap.java.
            super(16, 0.75f, true);
        }
        
        /*
         * If size() becomes more than sCacheSize, least recently used cache
         * is erased. 
         * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
         */
        @Override
        protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
            return size() > sCacheSize;
        }
    }
    
    // A pointer to native EmojiFactory object.
    private long mNativeEmojiFactory;
    private String mName;
    // Cache.
    private Map<Integer, WeakReference<Bitmap>> mCache;
    
    /**
     * @noinspection UnusedDeclaration
     */
    /*
     * Private constructor that must received an already allocated native
     * EmojiFactory int (pointer).
     *
     * This can be called from JNI code.
     */
    private EmojiFactory(long nativeEmojiFactory, String name) {
        mNativeEmojiFactory = nativeEmojiFactory;
        mName = name;
        mCache = new CustomLinkedHashMap<Integer, WeakReference<Bitmap>>();
    }
    
    @Override
    protected void finalize() throws Throwable {
        try {
            nativeDestructor(mNativeEmojiFactory);
        } finally {
            super.finalize();
        }
    }
    
    public String name() {
        return mName;
    }
    
    /**
     * Returns Bitmap object corresponding to the AndroidPua.
     * 
     * Note that each Bitmap is cached by this class, which means that, if you modify a
     * Bitmap object (using setPos() method), all same emoji Bitmap will be modified.
     * If it is unacceptable, please copy the object before modifying it.
     *  
     * @param pua A unicode codepoint.
     * @return Bitmap object when this factory knows the Bitmap relevant to the codepoint.
     * Otherwise null is returned.  
     */
    public synchronized Bitmap getBitmapFromAndroidPua(int pua) {
        WeakReference<Bitmap> cache = mCache.get(pua);
        if (cache == null) {
            Bitmap ret = nativeGetBitmapFromAndroidPua(mNativeEmojiFactory, pua);
            // There is no need to cache returned null, since in most cases it means there
            // is no map from the AndroidPua to a specific image. In other words, it usually does
            // not include the cost of creating Bitmap object.
            if (ret != null) {
               mCache.put(pua, new WeakReference<Bitmap>(ret));
            }
            return ret;
        } else {
            Bitmap tmp = cache.get();
            if (tmp == null) {
                Bitmap ret = nativeGetBitmapFromAndroidPua(mNativeEmojiFactory, pua);
                mCache.put(pua, new WeakReference<Bitmap>(ret));
                return ret;
            } else {
                return tmp;
            }
        }
    }

    /**
     * Returns Bitmap object corresponding to the vendor specified sjis.
     * 
     * See comments in getBitmapFromAndroidPua().
     * 
     * @param sjis sjis code specific to each career(vendor)
     * @return Bitmap object when this factory knows the Bitmap relevant to the code. Otherwise
     * null is returned.
     */
    public synchronized Bitmap getBitmapFromVendorSpecificSjis(char sjis) {
        return getBitmapFromAndroidPua(getAndroidPuaFromVendorSpecificSjis(sjis));
    }

    /**
     * Returns Bitmap object corresponding to the vendor specific Unicode.
     * 
     * See comments in getBitmapFromAndroidPua().
     * 
     * @param vsp vendor specific PUA.
     * @return Bitmap object when this factory knows the Bitmap relevant to the code. Otherwise
     * null is returned.
     */
    public synchronized Bitmap getBitmapFromVendorSpecificPua(int vsp) {
        return getBitmapFromAndroidPua(getAndroidPuaFromVendorSpecificPua(vsp));
    }
    
    /**
     * Returns Unicode PUA for Android corresponding to the vendor specific sjis.
     * 
     * @param sjis vendor specific sjis
     * @return Unicode PUA for Android, or -1 if there's no map for the sjis.
     */
    public int getAndroidPuaFromVendorSpecificSjis(char sjis) {
        return nativeGetAndroidPuaFromVendorSpecificSjis(mNativeEmojiFactory, sjis);
    }
    
    /**
     * Returns vendor specific sjis corresponding to the Unicode AndroidPua.
     * 
     * @param pua Unicode PUA for Android,
     * @return vendor specific sjis, or -1 if there's no map for the AndroidPua.
     */
    public int getVendorSpecificSjisFromAndroidPua(int pua) {
        return nativeGetVendorSpecificSjisFromAndroidPua(mNativeEmojiFactory, pua);
    }
    
    /**
     * Returns Unicode PUA for Android corresponding to the vendor specific Unicode.
     * 
     * @param vsp vendor specific PUA.
     * @return Unicode PUA for Android, or -1 if there's no map for the
     * Unicode.
     */
    public int getAndroidPuaFromVendorSpecificPua(int vsp) {
        return nativeGetAndroidPuaFromVendorSpecificPua(mNativeEmojiFactory, vsp);
    }

    public String getAndroidPuaFromVendorSpecificPua(String vspString) {
        if (vspString == null) {
            return null;
        }
        int minVsp = nativeGetMinimumVendorSpecificPua(mNativeEmojiFactory);
        int maxVsp = nativeGetMaximumVendorSpecificPua(mNativeEmojiFactory);
        int len = vspString.length();
        int[] codePoints = new int[vspString.codePointCount(0, len)];

        int new_len = 0;
        for (int i = 0; i < len; i = vspString.offsetByCodePoints(i, 1), new_len++) {
            int codePoint = vspString.codePointAt(i);
            if (minVsp <= codePoint && codePoint <= maxVsp) {
                int newCodePoint = getAndroidPuaFromVendorSpecificPua(codePoint);
                if (newCodePoint > 0) {
                    codePoints[new_len] = newCodePoint;
                    continue;
                }
            }
            codePoints[new_len] = codePoint;
        }
        return new String(codePoints, 0, new_len);
    }
    
    /**
     * Returns vendor specific Unicode corresponding to the Unicode AndroidPua.
     * 
     * @param pua Unicode PUA for Android,
     * @return vendor specific sjis, or -1 if there's no map for the AndroidPua.
     */
    public int getVendorSpecificPuaFromAndroidPua(int pua) {
        return nativeGetVendorSpecificPuaFromAndroidPua(mNativeEmojiFactory, pua);
    }

    public String getVendorSpecificPuaFromAndroidPua(String puaString) {
        if (puaString == null) {
            return null;
        }
        int minVsp = nativeGetMinimumAndroidPua(mNativeEmojiFactory);
        int maxVsp = nativeGetMaximumAndroidPua(mNativeEmojiFactory);
        int len = puaString.length();
        int[] codePoints = new int[puaString.codePointCount(0, len)];

        int new_len = 0;
        for (int i = 0; i < len; i = puaString.offsetByCodePoints(i, 1), new_len++) {
            int codePoint = puaString.codePointAt(i);
            if (minVsp <= codePoint && codePoint <= maxVsp) {
                int newCodePoint = getVendorSpecificPuaFromAndroidPua(codePoint);
                if (newCodePoint > 0) {
                    codePoints[new_len] = newCodePoint;
                    continue;
                }
            }
            codePoints[new_len] = codePoint;
        }
        return new String(codePoints, 0, new_len);
    }

    /**
     * Constructs an instance of EmojiFactory corresponding to the name.
     *  
     * @param class_name Name of the factory. This must include complete package name.
     * @return A concrete EmojiFactory instance corresponding to factory_name.
     * If factory_name is invalid, null is returned. 
     */
    public static native EmojiFactory newInstance(String class_name);
    
    /**
     * Constructs an instance of available EmojiFactory.
     * 
     * @return A concrete EmojiFactory instance. If there are several available
     * EmojiFactory class, preferred one is chosen by the system. If there isn't, null
     * is returned. 
     */
    public static native EmojiFactory newAvailableInstance();

    /**
     * Returns the lowest code point corresponding to an Android
     * emoji character.
     */
    public int getMinimumAndroidPua() {
        return nativeGetMinimumAndroidPua(mNativeEmojiFactory);
    }

    /**
     * Returns the highest code point corresponding to an Android
     * emoji character.
     */
    public int getMaximumAndroidPua() {
        return nativeGetMaximumAndroidPua(mNativeEmojiFactory);
    }
    
    // native methods
    
    private native void nativeDestructor(long nativeEmojiFactory);
    private native Bitmap nativeGetBitmapFromAndroidPua(long nativeEmojiFactory, int AndroidPua);
    private native int nativeGetAndroidPuaFromVendorSpecificSjis(long nativeEmojiFactory,
            char sjis);
    private native int nativeGetVendorSpecificSjisFromAndroidPua(long nativeEmojiFactory,
            int pua);
    private native int nativeGetAndroidPuaFromVendorSpecificPua(long nativeEmojiFactory,
            int vsp);
    private native int nativeGetVendorSpecificPuaFromAndroidPua(long nativeEmojiFactory,
            int pua);
    private native int nativeGetMaximumVendorSpecificPua(long nativeEmojiFactory);
    private native int nativeGetMinimumVendorSpecificPua(long nativeEmojiFactory);
    private native int nativeGetMaximumAndroidPua(long nativeEmojiFactory);
    private native int nativeGetMinimumAndroidPua(long nativeEmojiFactory);
}
+13 −28
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package android.text;

import android.annotation.IntDef;
import android.emoji.EmojiFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
@@ -103,19 +102,6 @@ public abstract class Layout {
    private static final ParagraphStyle[] NO_PARA_SPANS =
        ArrayUtils.emptyArray(ParagraphStyle.class);

    /* package */ static final EmojiFactory EMOJI_FACTORY = EmojiFactory.newAvailableInstance();
    /* package */ static final int MIN_EMOJI, MAX_EMOJI;

    static {
        if (EMOJI_FACTORY != null) {
            MIN_EMOJI = EMOJI_FACTORY.getMinimumAndroidPua();
            MAX_EMOJI = EMOJI_FACTORY.getMaximumAndroidPua();
        } else {
            MIN_EMOJI = -1;
            MAX_EMOJI = -1;
        }
    }

    /**
     * Return how wide a layout must be in order to display the
     * specified text with one line per paragraph.
@@ -360,9 +346,9 @@ public abstract class Layout {
                }
            }

            boolean hasTabOrEmoji = getLineContainsTab(lineNum);
            boolean hasTab = getLineContainsTab(lineNum);
            // Can't tell if we have tabs for sure, currently
            if (hasTabOrEmoji && !tabStopsIsInitialized) {
            if (hasTab && !tabStopsIsInitialized) {
                if (tabStops == null) {
                    tabStops = new TabStops(TAB_INCREMENT, spans);
                } else {
@@ -405,11 +391,11 @@ public abstract class Layout {

            paint.setHyphenEdit(getHyphen(lineNum));
            Directions directions = getLineDirections(lineNum);
            if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) {
            if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTab) {
                // XXX: assumes there's nothing additional to be done
                canvas.drawText(buf, start, end, x, lbaseline, paint);
            } else {
                tl.set(paint, buf, start, end, dir, directions, hasTabOrEmoji, tabStops);
                tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops);
                tl.draw(canvas, x, ltop, lbaseline, lbottom);
            }
            paint.setHyphenEdit(0);
@@ -710,8 +696,7 @@ public abstract class Layout {

    /**
     * Returns whether the specified line contains one or more
     * characters that need to be handled specially, like tabs
     * or emoji.
     * characters that need to be handled specially, like tabs.
     */
    public abstract boolean getLineContainsTab(int line);

@@ -911,11 +896,11 @@ public abstract class Layout {
        int start = getLineStart(line);
        int end = getLineEnd(line);
        int dir = getParagraphDirection(line);
        boolean hasTabOrEmoji = getLineContainsTab(line);
        boolean hasTab = getLineContainsTab(line);
        Directions directions = getLineDirections(line);

        TabStops tabStops = null;
        if (hasTabOrEmoji && mText instanceof Spanned) {
        if (hasTab && mText instanceof Spanned) {
            // Just checking this line should be good enough, tabs should be
            // consistent across all lines in a paragraph.
            TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class);
@@ -925,7 +910,7 @@ public abstract class Layout {
        }

        TextLine tl = TextLine.obtain();
        tl.set(mPaint, mText, start, end, dir, directions, hasTabOrEmoji, tabStops);
        tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops);
        float wid = tl.measure(offset - start, trailing, null);
        TextLine.recycle(tl);

@@ -1031,9 +1016,9 @@ public abstract class Layout {
        int start = getLineStart(line);
        int end = full ? getLineEnd(line) : getLineVisibleEnd(line);

        boolean hasTabsOrEmoji = getLineContainsTab(line);
        boolean hasTabs = getLineContainsTab(line);
        TabStops tabStops = null;
        if (hasTabsOrEmoji && mText instanceof Spanned) {
        if (hasTabs && mText instanceof Spanned) {
            // Just checking this line should be good enough, tabs should be
            // consistent across all lines in a paragraph.
            TabStopSpan[] tabs = getParagraphSpans((Spanned) mText, start, end, TabStopSpan.class);
@@ -1049,7 +1034,7 @@ public abstract class Layout {
        int dir = getParagraphDirection(line);

        TextLine tl = TextLine.obtain();
        tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops);
        tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops);
        float width = tl.metrics(null);
        TextLine.recycle(tl);
        return width;
@@ -1066,12 +1051,12 @@ public abstract class Layout {
    private float getLineExtent(int line, TabStops tabStops, boolean full) {
        int start = getLineStart(line);
        int end = full ? getLineEnd(line) : getLineVisibleEnd(line);
        boolean hasTabsOrEmoji = getLineContainsTab(line);
        boolean hasTabs = getLineContainsTab(line);
        Directions directions = getLineDirections(line);
        int dir = getParagraphDirection(line);

        TextLine tl = TextLine.obtain();
        tl.set(mPaint, mText, start, end, dir, directions, hasTabsOrEmoji, tabStops);
        tl.set(mPaint, mText, start, end, dir, directions, hasTabs, tabStops);
        float width = tl.metrics(null);
        TextLine.recycle(tl);
        return width;
+1 −1
Original line number Diff line number Diff line
@@ -1308,7 +1308,7 @@ public class StaticLayout extends Layout {
        private static final int INITIAL_SIZE = 16;
        public int[] breaks = new int[INITIAL_SIZE];
        public float[] widths = new float[INITIAL_SIZE];
        public int[] flags = new int[INITIAL_SIZE]; // hasTabOrEmoji
        public int[] flags = new int[INITIAL_SIZE]; // hasTab
        // breaks, widths, and flags should all have the same length
    }

+9 −65
Original line number Diff line number Diff line
@@ -128,7 +128,7 @@ class TextLine {
     * @param limit the limit of the line relative to the text
     * @param dir the paragraph direction of this line
     * @param directions the directions information of this line
     * @param hasTabs true if the line might contain tabs or emoji
     * @param hasTabs true if the line might contain tabs
     * @param tabStops the tabStops. Can be null.
     */
    void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
@@ -204,7 +204,6 @@ class TextLine {

        float h = 0;
        int[] runs = mDirections.mDirections;
        RectF emojiRect = null;

        int lastRunIndex = runs.length - 2;
        for (int i = 0; i < runs.length; i += 2) {
@@ -218,41 +217,23 @@ class TextLine {
            int segstart = runStart;
            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
                int codept = 0;
                Bitmap bm = null;

                if (mHasTabs && j < runLimit) {
                    codept = mChars[j];
                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
                    if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
                        codept = Character.codePointAt(mChars, j);
                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
                        } else if (codept > 0xffff) {
                        if (codept > 0xFFFF) {
                            ++j;
                            continue;
                        }
                    }
                }

                if (j == runLimit || codept == '\t' || bm != null) {
                if (j == runLimit || codept == '\t') {
                    h += drawRun(c, segstart, j, runIsRtl, x+h, top, y, bottom,
                            i != lastRunIndex || j != mLen);

                    if (codept == '\t') {
                        h = mDir * nextTab(h * mDir);
                    } else if (bm != null) {
                        float bmAscent = ascent(j);
                        float bitmapHeight = bm.getHeight();
                        float scale = -bmAscent / bitmapHeight;
                        float width = bm.getWidth() * scale;

                        if (emojiRect == null) {
                            emojiRect = new RectF();
                        }
                        emojiRect.set(x + h, y + bmAscent,
                                x + h + width, y);
                        c.drawBitmap(bm, null, emojiRect, mPaint);
                        h += width;
                        j++;
                    }
                    segstart = j + 1;
                }
@@ -313,22 +294,18 @@ class TextLine {
            int segstart = runStart;
            for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
                int codept = 0;
                Bitmap bm = null;

                if (mHasTabs && j < runLimit) {
                    codept = chars[j];
                    if (codept >= 0xd800 && codept < 0xdc00 && j + 1 < runLimit) {
                    if (codept >= 0xD800 && codept < 0xDC00 && j + 1 < runLimit) {
                        codept = Character.codePointAt(chars, j);
                        if (codept >= Layout.MIN_EMOJI && codept <= Layout.MAX_EMOJI) {
                            bm = Layout.EMOJI_FACTORY.getBitmapFromAndroidPua(codept);
                        } else if (codept > 0xffff) {
                        if (codept > 0xFFFF) {
                            ++j;
                            continue;
                        }
                    }
                }

                if (j == runLimit || codept == '\t' || bm != null) {
                if (j == runLimit || codept == '\t') {
                    boolean inSegment = target >= segstart && target < j;

                    boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
@@ -353,13 +330,6 @@ class TextLine {
                        }
                    }

                    if (bm != null) {
                        float bmAscent = ascent(j);
                        float wid = bm.getWidth() * -bmAscent / bm.getHeight();
                        h += mDir * wid;
                        j++;
                    }

                    segstart = j + 1;
                }
            }
@@ -705,7 +675,7 @@ class TextLine {

    /**
     * Utility function for measuring and rendering text.  The text must
     * not include a tab or emoji.
     * not include a tab.
     *
     * @param wp the working paint
     * @param start the start of the text
@@ -860,7 +830,7 @@ class TextLine {

    /**
     * Utility function for handling a unidirectional run.  The run must not
     * contain tabs or emoji but can contain styles.
     * contain tabs but can contain styles.
     *
     *
     * @param start the line-relative start of the run
@@ -993,32 +963,6 @@ class TextLine {
        }
    }

    /**
     * Returns the ascent of the text at start.  This is used for scaling
     * emoji.
     *
     * @param pos the line-relative position
     * @return the ascent of the text at start
     */
    float ascent(int pos) {
        if (mSpanned == null) {
            return mPaint.ascent();
        }

        pos += mStart;
        MetricAffectingSpan[] spans = mSpanned.getSpans(pos, pos + 1, MetricAffectingSpan.class);
        if (spans.length == 0) {
            return mPaint.ascent();
        }

        TextPaint wp = mWorkPaint;
        wp.set(mPaint);
        for (MetricAffectingSpan span : spans) {
            span.updateMeasureState(wp);
        }
        return wp.ascent();
    }

    /**
     * Returns the next tab position.
     *
+0 −2
Original line number Diff line number Diff line
@@ -49,7 +49,6 @@ LOCAL_SRC_FILES:= \
    android_database_SQLiteConnection.cpp \
    android_database_SQLiteGlobal.cpp \
    android_database_SQLiteDebug.cpp \
    android_emoji_EmojiFactory.cpp \
    android_view_DisplayEventReceiver.cpp \
    android_view_DisplayListCanvas.cpp \
    android_view_GraphicBuffer.cpp \
@@ -201,7 +200,6 @@ LOCAL_C_INCLUDES += \
    external/tremor/Tremor \
    external/jpeg \
    external/harfbuzz_ng/src \
    frameworks/opt/emoji \
    libcore/include \
    $(call include-path-for, audio-utils) \
    frameworks/minikin/include \
Loading