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