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

Commit 708cde15 authored by Roozbeh Pournader's avatar Roozbeh Pournader Committed by Android (Google) Code Review
Browse files

Merge "Remove EmojiFactory and its mentions from frameworks."

parents 3acd6932 112d9c7f
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