1 /* 2 * Copyright (C) 2013 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.text.cts; 18 19 import android.content.Context; 20 import android.cts.util.NullWebViewUtils; 21 import android.graphics.Bitmap; 22 import android.graphics.Canvas; 23 import android.graphics.Paint; 24 import android.graphics.Picture; 25 import android.test.ActivityInstrumentationTestCase2; 26 import android.view.KeyEvent; 27 import android.view.Menu; 28 import android.view.View; 29 import android.widget.TextView; 30 import android.widget.EditText; 31 import android.webkit.cts.WebViewOnUiThread; 32 33 public class EmojiTest extends ActivityInstrumentationTestCase2<EmojiCtsActivity> { 34 35 public EmojiTest() { 36 super("com.android.cts.text", EmojiCtsActivity.class); 37 } 38 39 protected void setUp() throws Exception { 40 super.setUp(); 41 } 42 43 protected void tearDown() throws Exception { 44 super.tearDown(); 45 } 46 47 /** 48 * Tests all Emoji are defined in Character class 49 */ 50 public void testEmojiCodePoints() { 51 for (int i = 0; i < EmojiConstants.emojiCodePoints.length; i++) { 52 assertTrue(Character.isDefined(EmojiConstants.emojiCodePoints[i])); 53 } 54 } 55 56 /** 57 * Tests Emoji has different glyph for different meaning characters. 58 * Test on Canvas, TextView, EditText and WebView 59 */ 60 public void testEmojiGlyph() { 61 CaptureCanvas ccanvas = new CaptureCanvas(getInstrumentation().getContext()); 62 63 Bitmap mBitmapA, mBitmapB; // Emoji displayed Bitmaps to compare 64 65 int comparedCodePoints[][] = { // Emojis should have different characters 66 {0x1F436, 0x1F435}, // Dog(U+1F436) and Monkey(U+1F435) 67 {0x26BD, 0x26BE}, // Soccer ball(U+26BD) and Baseball(U+26BE) 68 {0x1F47B, 0x1F381}, // Ghost(U+1F47B) and wrapped present(U+1F381) 69 {0x2764, 0x1F494}, // Heavy black heart(U+2764) and broken heart(U+1F494) 70 {0x1F603, 0x1F33B} // Smiling face with open mouth(U+1F603) and sunflower(U+1F33B) 71 }; 72 73 for (int i = 0; i < comparedCodePoints.length; i++) { 74 75 mBitmapA = ccanvas.capture(Character.toChars(comparedCodePoints[i][0])); 76 mBitmapB = ccanvas.capture(Character.toChars(comparedCodePoints[i][1])); 77 78 assertFalse(mBitmapA.sameAs(mBitmapB)); 79 80 // cannot reuse CaptureTextView as 2nd setText call throws NullPointerException 81 CaptureTextView cviewA = new CaptureTextView(getInstrumentation().getContext()); 82 mBitmapA = cviewA.capture(Character.toChars(comparedCodePoints[i][0])); 83 CaptureTextView cviewB = new CaptureTextView(getInstrumentation().getContext()); 84 mBitmapB = cviewB.capture(Character.toChars(comparedCodePoints[i][1])); 85 86 assertFalse(mBitmapA.sameAs(mBitmapB)); 87 88 CaptureEditText cedittextA = new CaptureEditText(getInstrumentation().getContext()); 89 mBitmapA = cedittextA.capture(Character.toChars(comparedCodePoints[i][0])); 90 CaptureEditText cedittextB = new CaptureEditText(getInstrumentation().getContext()); 91 mBitmapB = cedittextB.capture(Character.toChars(comparedCodePoints[i][1])); 92 93 assertFalse(mBitmapA.sameAs(mBitmapB)); 94 95 // Trigger activity bringup so we can determine if a WebView is available on this 96 // device. 97 EmojiCtsActivity activity = getActivity(); 98 if (NullWebViewUtils.isWebViewAvailable()) { 99 CaptureWebView cwebview = new CaptureWebView(getInstrumentation().getContext()); 100 mBitmapA = cwebview.capture(Character.toChars(comparedCodePoints[i][0])); 101 mBitmapB = cwebview.capture(Character.toChars(comparedCodePoints[i][1])); 102 assertFalse(mBitmapA.sameAs(mBitmapB)); 103 } 104 } 105 } 106 107 /** 108 * Tests EditText handles Emoji 109 */ 110 public void testEmojiEditable() throws Throwable { 111 int testedCodePoints[] = { 112 0xAE, // registered mark 113 0x2764, // heavy black heart 114 0x1F353 // strawberry - surrogate pair sample. Count as two characters. 115 }; 116 117 String origStr, newStr; 118 119 // delete Emoji by sending KEYCODE_DEL 120 for (int i = 0; i < testedCodePoints.length; i++) { 121 origStr = "Test character "; 122 // cannot reuse CaptureTextView as 2nd setText call throws NullPointerException 123 final EditText editText = new EditText(getInstrumentation().getContext()); 124 editText.setText(origStr + String.valueOf(Character.toChars(testedCodePoints[i]))); 125 126 // confirm the emoji is added. 127 newStr = editText.getText().toString(); 128 assertEquals(newStr.codePointCount(0, newStr.length()), origStr.length() + 1); 129 130 runTestOnUiThread(new Runnable() { 131 public void run() { 132 // Delete added character by sending KEYCODE_DEL event 133 editText.dispatchKeyEvent( 134 new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); 135 } 136 }); 137 getInstrumentation().waitForIdleSync(); 138 139 newStr = editText.getText().toString(); 140 assertEquals(newStr.codePointCount(0, newStr.length()), origStr.length() + 1); 141 } 142 } 143 144 private class CaptureCanvas extends View { 145 146 String mTestStr; 147 Paint paint = new Paint(); 148 149 CaptureCanvas(Context context) { 150 super(context); 151 } 152 153 public void onDraw(Canvas canvas) { 154 if (mTestStr != null) { 155 canvas.drawText(mTestStr, 50, 50, paint); 156 } 157 return; 158 } 159 160 Bitmap capture(char c[]) { 161 mTestStr = String.valueOf(c); 162 invalidate(); 163 164 setDrawingCacheEnabled(true); 165 measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 166 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 167 layout(0, 0, 200,200); 168 169 Bitmap bitmap = Bitmap.createBitmap(getDrawingCache()); 170 setDrawingCacheEnabled(false); 171 return bitmap; 172 } 173 174 } 175 176 private class CaptureTextView extends TextView { 177 178 CaptureTextView(Context context) { 179 super(context); 180 } 181 182 Bitmap capture(char c[]) { 183 setText(String.valueOf(c)); 184 185 invalidate(); 186 187 setDrawingCacheEnabled(true); 188 measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 189 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 190 layout(0, 0, 200,200); 191 192 Bitmap bitmap = Bitmap.createBitmap(getDrawingCache()); 193 setDrawingCacheEnabled(false); 194 return bitmap; 195 } 196 197 } 198 199 private class CaptureEditText extends EditText { 200 201 CaptureEditText(Context context) { 202 super(context); 203 } 204 205 Bitmap capture(char c[]) { 206 setText(String.valueOf(c)); 207 208 invalidate(); 209 210 setDrawingCacheEnabled(true); 211 measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 212 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 213 layout(0, 0, 200,200); 214 215 Bitmap bitmap = Bitmap.createBitmap(getDrawingCache()); 216 setDrawingCacheEnabled(false); 217 return bitmap; 218 } 219 220 } 221 222 223 private class CaptureWebView { 224 225 WebViewOnUiThread webViewOnUiThread; 226 Bitmap bitmap; 227 CaptureWebView(Context context) { 228 webViewOnUiThread = new WebViewOnUiThread(EmojiTest.this, getActivity().getWebView()); 229 } 230 231 Bitmap capture(char c[]) { 232 233 webViewOnUiThread.loadDataAndWaitForCompletion("<html><body>" + String.valueOf(c) + "</body></html>", 234 "text/html; charset=utf-8", "utf-8"); 235 // The Chromium-powered WebView renders asynchronously and there's nothing reliable 236 // we can easily wait for to be sure that capturePicture will return a fresh frame. 237 // So, just sleep for a sufficient time. 238 try { 239 Thread.sleep(250); 240 } catch (InterruptedException e) { 241 return null; 242 } 243 244 Picture picture = webViewOnUiThread.capturePicture(); 245 if (picture == null || picture.getHeight() <= 0 || picture.getWidth() <= 0) { 246 return null; 247 } else { 248 bitmap = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(), 249 Bitmap.Config.ARGB_8888); 250 Canvas canvas = new Canvas(bitmap); 251 picture.draw(canvas); 252 } 253 254 return bitmap; 255 } 256 257 } 258 259 } 260 261