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> { CustomLinkedHashMap()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 removeEldestEntry(Map.Entry<K, V> eldest)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 long 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 */ EmojiFactory(long nativeEmojiFactory, String name)71 private EmojiFactory(long nativeEmojiFactory, String name) { 72 mNativeEmojiFactory = nativeEmojiFactory; 73 mName = name; 74 mCache = new CustomLinkedHashMap<Integer, WeakReference<Bitmap>>(); 75 } 76 77 @Override finalize()78 protected void finalize() throws Throwable { 79 try { 80 nativeDestructor(mNativeEmojiFactory); 81 } finally { 82 super.finalize(); 83 } 84 } 85 name()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 */ getBitmapFromAndroidPua(int pua)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 */ getBitmapFromVendorSpecificSjis(char sjis)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 */ getBitmapFromVendorSpecificPua(int vsp)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 */ getAndroidPuaFromVendorSpecificSjis(char sjis)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 */ getVendorSpecificSjisFromAndroidPua(int pua)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 */ getAndroidPuaFromVendorSpecificPua(int vsp)177 public int getAndroidPuaFromVendorSpecificPua(int vsp) { 178 return nativeGetAndroidPuaFromVendorSpecificPua(mNativeEmojiFactory, vsp); 179 } 180 getAndroidPuaFromVendorSpecificPua(String vspString)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 */ getVendorSpecificPuaFromAndroidPua(int pua)211 public int getVendorSpecificPuaFromAndroidPua(int pua) { 212 return nativeGetVendorSpecificPuaFromAndroidPua(mNativeEmojiFactory, pua); 213 } 214 getVendorSpecificPuaFromAndroidPua(String puaString)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 */ newInstance(String class_name)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 */ newAvailableInstance()255 public static native EmojiFactory newAvailableInstance(); 256 257 /** 258 * Returns the lowest code point corresponding to an Android 259 * emoji character. 260 */ getMinimumAndroidPua()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 */ getMaximumAndroidPua()269 public int getMaximumAndroidPua() { 270 return nativeGetMaximumAndroidPua(mNativeEmojiFactory); 271 } 272 273 // native methods 274 nativeDestructor(long nativeEmojiFactory)275 private native void nativeDestructor(long nativeEmojiFactory); nativeGetBitmapFromAndroidPua(long nativeEmojiFactory, int AndroidPua)276 private native Bitmap nativeGetBitmapFromAndroidPua(long nativeEmojiFactory, int AndroidPua); nativeGetAndroidPuaFromVendorSpecificSjis(long nativeEmojiFactory, char sjis)277 private native int nativeGetAndroidPuaFromVendorSpecificSjis(long nativeEmojiFactory, 278 char sjis); nativeGetVendorSpecificSjisFromAndroidPua(long nativeEmojiFactory, int pua)279 private native int nativeGetVendorSpecificSjisFromAndroidPua(long nativeEmojiFactory, 280 int pua); nativeGetAndroidPuaFromVendorSpecificPua(long nativeEmojiFactory, int vsp)281 private native int nativeGetAndroidPuaFromVendorSpecificPua(long nativeEmojiFactory, 282 int vsp); nativeGetVendorSpecificPuaFromAndroidPua(long nativeEmojiFactory, int pua)283 private native int nativeGetVendorSpecificPuaFromAndroidPua(long nativeEmojiFactory, 284 int pua); nativeGetMaximumVendorSpecificPua(long nativeEmojiFactory)285 private native int nativeGetMaximumVendorSpecificPua(long nativeEmojiFactory); nativeGetMinimumVendorSpecificPua(long nativeEmojiFactory)286 private native int nativeGetMinimumVendorSpecificPua(long nativeEmojiFactory); nativeGetMaximumAndroidPua(long nativeEmojiFactory)287 private native int nativeGetMaximumAndroidPua(long nativeEmojiFactory); nativeGetMinimumAndroidPua(long nativeEmojiFactory)288 private native int nativeGetMinimumAndroidPua(long nativeEmojiFactory); 289 } 290