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.graphics.Bitmap; 21 import android.graphics.Canvas; 22 import android.graphics.Paint; 23 import android.graphics.Picture; 24 import android.test.ActivityInstrumentationTestCase2; 25 import android.view.KeyEvent; 26 import android.view.Menu; 27 import android.view.View; 28 import android.webkit.cts.WebViewOnUiThread; 29 import android.widget.TextView; 30 import android.widget.EditText; 31 32 public class EmojiTest extends ActivityInstrumentationTestCase2<EmojiStubActivity> { 33 34 public EmojiTest() { 35 super("com.android.cts.stub", EmojiStubActivity.class); 36 } 37 38 protected void setUp() throws Exception { 39 super.setUp(); 40 } 41 42 protected void tearDown() throws Exception { 43 super.tearDown(); 44 } 45 46 /** 47 * Tests all Emoji are defined in Character class 48 */ 49 public void testEmojiCodePoints() { 50 for (int i = 0; i < EmojiConstants.emojiCodePoints.length; i++) { 51 assertTrue(Character.isDefined(EmojiConstants.emojiCodePoints[i])); 52 } 53 } 54 55 /** 56 * Tests Emoji has different glyph for different meaning characters. 57 * Test on Canvas, TextView, EditText and WebView 58 */ 59 public void testEmojiGlyph() { 60 CaptureCanvas ccanvas = new CaptureCanvas(getInstrumentation().getContext()); 61 CaptureWebView cwebview = new CaptureWebView(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 mBitmapA = cwebview.capture(Character.toChars(comparedCodePoints[i][0])); 96 mBitmapB = cwebview.capture(Character.toChars(comparedCodePoints[i][1])); 97 98 assertFalse(mBitmapA.sameAs(mBitmapB)); 99 100 } 101 } 102 103 /** 104 * Tests EditText handles Emoji 105 */ 106 public void testEmojiEditable() { 107 int testedCodePoints[] = { 108 0xAE, // registered mark 109 0x2764, // heavy black heart 110 0x1F353 // strawberry - surrogate pair sample. Count as two characters. 111 }; 112 113 String origStr, newStr; 114 115 // delete Emoji by sending KEYCODE_DEL 116 for (int i = 0; i < testedCodePoints.length; i++) { 117 origStr = "Test character "; 118 // cannot reuse CaptureTextView as 2nd setText call throws NullPointerException 119 EditText editText = new EditText(getInstrumentation().getContext()); 120 editText.setText(origStr + String.valueOf(Character.toChars(testedCodePoints[i]))); 121 122 // confirm the emoji is added. 123 newStr = editText.getText().toString(); 124 assertEquals(newStr.codePointCount(0, newStr.length()), origStr.length() + 1); 125 126 // Delete added character by sending KEYCODE_DEL event 127 editText.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL)); 128 129 newStr = editText.getText().toString(); 130 assertEquals(newStr.codePointCount(0, newStr.length()), origStr.length() + 1); 131 } 132 } 133 134 private class CaptureCanvas extends View { 135 136 String mTestStr; 137 Paint paint = new Paint(); 138 139 CaptureCanvas(Context context) { 140 super(context); 141 } 142 143 public void onDraw(Canvas canvas) { 144 if (mTestStr != null) { 145 canvas.drawText(mTestStr, 50, 50, paint); 146 } 147 return; 148 } 149 150 Bitmap capture(char c[]) { 151 mTestStr = String.valueOf(c); 152 invalidate(); 153 154 setDrawingCacheEnabled(true); 155 measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 156 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 157 layout(0, 0, 200,200); 158 159 Bitmap bitmap = Bitmap.createBitmap(getDrawingCache()); 160 setDrawingCacheEnabled(false); 161 return bitmap; 162 } 163 164 } 165 166 private class CaptureTextView extends TextView { 167 168 CaptureTextView(Context context) { 169 super(context); 170 } 171 172 Bitmap capture(char c[]) { 173 setText(String.valueOf(c)); 174 175 invalidate(); 176 177 setDrawingCacheEnabled(true); 178 measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 179 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 180 layout(0, 0, 200,200); 181 182 Bitmap bitmap = Bitmap.createBitmap(getDrawingCache()); 183 setDrawingCacheEnabled(false); 184 return bitmap; 185 } 186 187 } 188 189 private class CaptureEditText extends EditText { 190 191 CaptureEditText(Context context) { 192 super(context); 193 } 194 195 Bitmap capture(char c[]) { 196 setText(String.valueOf(c)); 197 198 invalidate(); 199 200 setDrawingCacheEnabled(true); 201 measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 202 MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 203 layout(0, 0, 200,200); 204 205 Bitmap bitmap = Bitmap.createBitmap(getDrawingCache()); 206 setDrawingCacheEnabled(false); 207 return bitmap; 208 } 209 210 } 211 212 213 private class CaptureWebView { 214 215 WebViewOnUiThread webViewOnUiThread; 216 Bitmap bitmap; 217 CaptureWebView(Context context) { 218 webViewOnUiThread = new WebViewOnUiThread(EmojiTest.this, getActivity().getWebView()); 219 } 220 221 Bitmap capture(char c[]) { 222 223 webViewOnUiThread.loadDataAndWaitForCompletion("<html><body>" + String.valueOf(c) + "</body></html>", 224 "text/html; charset=utf-8", "utf-8"); 225 // The Chromium-powered WebView renders asynchronously and there's nothing reliable 226 // we can easily wait for to be sure that capturePicture will return a fresh frame. 227 // So, just sleep for a sufficient time. 228 try { 229 Thread.sleep(250); 230 } catch (InterruptedException e) { 231 return null; 232 } 233 234 Picture picture = webViewOnUiThread.capturePicture(); 235 if (picture == null || picture.getHeight() <= 0 || picture.getWidth() <= 0) { 236 return null; 237 } else { 238 bitmap = Bitmap.createBitmap(picture.getWidth(), picture.getHeight(), 239 Bitmap.Config.ARGB_8888); 240 Canvas canvas = new Canvas(bitmap); 241 picture.draw(canvas); 242 } 243 244 return bitmap; 245 } 246 247 } 248 249 } 250 251