Home | History | Annotate | Download | only in emoji
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.emoji;
     18 
     19 import android.graphics.Bitmap;
     20 
     21 import java.lang.ref.WeakReference;
     22 import java.util.LinkedHashMap;
     23 import java.util.Map;
     24 
     25 /**
     26  * A class for the factories which produce Emoji (pictgram) images.
     27  * This is intended to be used by IME, Email app, etc.
     28  * There's no plan to make this public for now.
     29  * @hide
     30  */
     31 public final class EmojiFactory {
     32     // private static final String LOG_TAG = "EmojiFactory";
     33 
     34     private int sCacheSize = 100;
     35 
     36     // HashMap for caching Bitmap object. In order not to make a cache object
     37     // blow up, we use LinkedHashMap with size limit.
     38     private class CustomLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
     39         public CustomLinkedHashMap() {
     40             // These magic numbers are gotten from the source code of
     41             // LinkedHashMap.java and HashMap.java.
     42             super(16, 0.75f, true);
     43         }
     44 
     45         /*
     46          * If size() becomes more than sCacheSize, least recently used cache
     47          * is erased.
     48          * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
     49          */
     50         @Override
     51         protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
     52             return size() > sCacheSize;
     53         }
     54     }
     55 
     56     // A pointer to native EmojiFactory object.
     57     private int mNativeEmojiFactory;
     58     private String mName;
     59     // Cache.
     60     private Map<Integer, WeakReference<Bitmap>> mCache;
     61 
     62     /**
     63      * @noinspection UnusedDeclaration
     64      */
     65     /*
     66      * Private constructor that must received an already allocated native
     67      * EmojiFactory int (pointer).
     68      *
     69      * This can be called from JNI code.
     70      */
     71     private EmojiFactory(int nativeEmojiFactory, String name) {
     72         mNativeEmojiFactory = nativeEmojiFactory;
     73         mName = name;
     74         mCache = new CustomLinkedHashMap<Integer, WeakReference<Bitmap>>();
     75     }
     76 
     77     @Override
     78     protected void finalize() throws Throwable {
     79         try {
     80             nativeDestructor(mNativeEmojiFactory);
     81         } finally {
     82             super.finalize();
     83         }
     84     }
     85 
     86     public String name() {
     87         return mName;
     88     }
     89 
     90     /**
     91      * Returns Bitmap object corresponding to the AndroidPua.
     92      *
     93      * Note that each Bitmap is cached by this class, which means that, if you modify a
     94      * Bitmap object (using setPos() method), all same emoji Bitmap will be modified.
     95      * If it is unacceptable, please copy the object before modifying it.
     96      *
     97      * @param pua A unicode codepoint.
     98      * @return Bitmap object when this factory knows the Bitmap relevant to the codepoint.
     99      * Otherwise null is returned.
    100      */
    101     public synchronized Bitmap getBitmapFromAndroidPua(int pua) {
    102         WeakReference<Bitmap> cache = mCache.get(pua);
    103         if (cache == null) {
    104             Bitmap ret = nativeGetBitmapFromAndroidPua(mNativeEmojiFactory, pua);
    105             // There is no need to cache returned null, since in most cases it means there
    106             // is no map from the AndroidPua to a specific image. In other words, it usually does
    107             // not include the cost of creating Bitmap object.
    108             if (ret != null) {
    109                mCache.put(pua, new WeakReference<Bitmap>(ret));
    110             }
    111             return ret;
    112         } else {
    113             Bitmap tmp = cache.get();
    114             if (tmp == null) {
    115                 Bitmap ret = nativeGetBitmapFromAndroidPua(mNativeEmojiFactory, pua);
    116                 mCache.put(pua, new WeakReference<Bitmap>(ret));
    117                 return ret;
    118             } else {
    119                 return tmp;
    120             }
    121         }
    122     }
    123 
    124     /**
    125      * Returns Bitmap object corresponding to the vendor specified sjis.
    126      *
    127      * See comments in getBitmapFromAndroidPua().
    128      *
    129      * @param sjis sjis code specific to each career(vendor)
    130      * @return Bitmap object when this factory knows the Bitmap relevant to the code. Otherwise
    131      * null is returned.
    132      */
    133     public synchronized Bitmap getBitmapFromVendorSpecificSjis(char sjis) {
    134         return getBitmapFromAndroidPua(getAndroidPuaFromVendorSpecificSjis(sjis));
    135     }
    136 
    137     /**
    138      * Returns Bitmap object corresponding to the vendor specific Unicode.
    139      *
    140      * See comments in getBitmapFromAndroidPua().
    141      *
    142      * @param vsp vendor specific PUA.
    143      * @return Bitmap object when this factory knows the Bitmap relevant to the code. Otherwise
    144      * null is returned.
    145      */
    146     public synchronized Bitmap getBitmapFromVendorSpecificPua(int vsp) {
    147         return getBitmapFromAndroidPua(getAndroidPuaFromVendorSpecificPua(vsp));
    148     }
    149 
    150     /**
    151      * Returns Unicode PUA for Android corresponding to the vendor specific sjis.
    152      *
    153      * @param sjis vendor specific sjis
    154      * @return Unicode PUA for Android, or -1 if there's no map for the sjis.
    155      */
    156     public int getAndroidPuaFromVendorSpecificSjis(char sjis) {
    157         return nativeGetAndroidPuaFromVendorSpecificSjis(mNativeEmojiFactory, sjis);
    158     }
    159 
    160     /**
    161      * Returns vendor specific sjis corresponding to the Unicode AndroidPua.
    162      *
    163      * @param pua Unicode PUA for Android,
    164      * @return vendor specific sjis, or -1 if there's no map for the AndroidPua.
    165      */
    166     public int getVendorSpecificSjisFromAndroidPua(int pua) {
    167         return nativeGetVendorSpecificSjisFromAndroidPua(mNativeEmojiFactory, pua);
    168     }
    169 
    170     /**
    171      * Returns Unicode PUA for Android corresponding to the vendor specific Unicode.
    172      *
    173      * @param vsp vendor specific PUA.
    174      * @return Unicode PUA for Android, or -1 if there's no map for the
    175      * Unicode.
    176      */
    177     public int getAndroidPuaFromVendorSpecificPua(int vsp) {
    178         return nativeGetAndroidPuaFromVendorSpecificPua(mNativeEmojiFactory, vsp);
    179     }
    180 
    181     public String getAndroidPuaFromVendorSpecificPua(String vspString) {
    182         if (vspString == null) {
    183             return null;
    184         }
    185         int minVsp = nativeGetMinimumVendorSpecificPua(mNativeEmojiFactory);
    186         int maxVsp = nativeGetMaximumVendorSpecificPua(mNativeEmojiFactory);
    187         int len = vspString.length();
    188         int[] codePoints = new int[vspString.codePointCount(0, len)];
    189 
    190         int new_len = 0;
    191         for (int i = 0; i < len; i = vspString.offsetByCodePoints(i, 1), new_len++) {
    192             int codePoint = vspString.codePointAt(i);
    193             if (minVsp <= codePoint && codePoint <= maxVsp) {
    194                 int newCodePoint = getAndroidPuaFromVendorSpecificPua(codePoint);
    195                 if (newCodePoint > 0) {
    196                     codePoints[new_len] = newCodePoint;
    197                     continue;
    198                 }
    199             }
    200             codePoints[new_len] = codePoint;
    201         }
    202         return new String(codePoints, 0, new_len);
    203     }
    204 
    205     /**
    206      * Returns vendor specific Unicode corresponding to the Unicode AndroidPua.
    207      *
    208      * @param pua Unicode PUA for Android,
    209      * @return vendor specific sjis, or -1 if there's no map for the AndroidPua.
    210      */
    211     public int getVendorSpecificPuaFromAndroidPua(int pua) {
    212         return nativeGetVendorSpecificPuaFromAndroidPua(mNativeEmojiFactory, pua);
    213     }
    214 
    215     public String getVendorSpecificPuaFromAndroidPua(String puaString) {
    216         if (puaString == null) {
    217             return null;
    218         }
    219         int minVsp = nativeGetMinimumAndroidPua(mNativeEmojiFactory);
    220         int maxVsp = nativeGetMaximumAndroidPua(mNativeEmojiFactory);
    221         int len = puaString.length();
    222         int[] codePoints = new int[puaString.codePointCount(0, len)];
    223 
    224         int new_len = 0;
    225         for (int i = 0; i < len; i = puaString.offsetByCodePoints(i, 1), new_len++) {
    226             int codePoint = puaString.codePointAt(i);
    227             if (minVsp <= codePoint && codePoint <= maxVsp) {
    228                 int newCodePoint = getVendorSpecificPuaFromAndroidPua(codePoint);
    229                 if (newCodePoint > 0) {
    230                     codePoints[new_len] = newCodePoint;
    231                     continue;
    232                 }
    233             }
    234             codePoints[new_len] = codePoint;
    235         }
    236         return new String(codePoints, 0, new_len);
    237     }
    238 
    239     /**
    240      * Constructs an instance of EmojiFactory corresponding to the name.
    241      *
    242      * @param class_name Name of the factory. This must include complete package name.
    243      * @return A concrete EmojiFactory instance corresponding to factory_name.
    244      * If factory_name is invalid, null is returned.
    245      */
    246     public static native EmojiFactory newInstance(String class_name);
    247 
    248     /**
    249      * Constructs an instance of available EmojiFactory.
    250      *
    251      * @return A concrete EmojiFactory instance. If there are several available
    252      * EmojiFactory class, preferred one is chosen by the system. If there isn't, null
    253      * is returned.
    254      */
    255     public static native EmojiFactory newAvailableInstance();
    256 
    257     /**
    258      * Returns the lowest code point corresponding to an Android
    259      * emoji character.
    260      */
    261     public int getMinimumAndroidPua() {
    262         return nativeGetMinimumAndroidPua(mNativeEmojiFactory);
    263     }
    264 
    265     /**
    266      * Returns the highest code point corresponding to an Android
    267      * emoji character.
    268      */
    269     public int getMaximumAndroidPua() {
    270         return nativeGetMaximumAndroidPua(mNativeEmojiFactory);
    271     }
    272 
    273     // native methods
    274 
    275     private native void nativeDestructor(int factory);
    276     private native Bitmap nativeGetBitmapFromAndroidPua(int nativeEmojiFactory, int AndroidPua);
    277     private native int nativeGetAndroidPuaFromVendorSpecificSjis(int nativeEmojiFactory,
    278             char sjis);
    279     private native int nativeGetVendorSpecificSjisFromAndroidPua(int nativeEmojiFactory,
    280             int pua);
    281     private native int nativeGetAndroidPuaFromVendorSpecificPua(int nativeEmojiFactory,
    282             int vsp);
    283     private native int nativeGetVendorSpecificPuaFromAndroidPua(int nativeEmojiFactory,
    284             int pua);
    285     private native int nativeGetMaximumVendorSpecificPua(int nativeEmojiFactory);
    286     private native int nativeGetMinimumVendorSpecificPua(int nativeEmojiFactory);
    287     private native int nativeGetMaximumAndroidPua(int nativeEmojiFactory);
    288     private native int nativeGetMinimumAndroidPua(int nativeEmojiFactory);
    289 }
    290