• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 com.android.cts.mockime;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.Color;
21 
22 import androidx.annotation.AnyThread;
23 import androidx.annotation.NonNull;
24 
25 /**
26  * A utility class to put a unique image on {@link MockIme} so that whether the software keyboard is
27  * visible to the user or not can be determined from the screenshot.
28  */
29 public final class Watermark {
30     /**
31      * Tolerance level between the expected color and the actual color in each color channel.
32      *
33      * <p>See Bug 174534092 about why we ended up having this.</p>
34      */
35     private static final int TOLERANCE = 8;
36 
37     /**
38      * A utility class that represents A8R8G8B bitmap as an integer array.
39      */
40     private static final class BitmapImage {
41         @NonNull
42         private final int[] mPixels;
43         private final int mWidth;
44         private final int mHeight;
45 
BitmapImage(@onNull int[] pixels, int width, int height)46         BitmapImage(@NonNull int[] pixels, int width, int height) {
47             mWidth = width;
48             mHeight = height;
49             mPixels = pixels;
50         }
51 
52         /**
53          * Create {@link BitmapImage} from {@link Bitmap}.
54          *
55          * @param bitmap {@link Bitmap} from which {@link BitmapImage} will be created.
56          * @return A new instance of {@link BitmapImage}.
57          */
58         @AnyThread
createFromBitmap(@onNull Bitmap bitmap)59         static BitmapImage createFromBitmap(@NonNull Bitmap bitmap) {
60             final int width = bitmap.getWidth();
61             final int height = bitmap.getHeight();
62             final int[] pixels = new int[width * height];
63             bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
64             return new BitmapImage(pixels, width, height);
65         }
66 
67         /**
68          * @return Width of this image.
69          */
70         @AnyThread
getWidth()71         int getWidth() {
72             return mWidth;
73         }
74 
75         /**
76          * @return Height of this image.
77          */
78         @AnyThread
getHeight()79         int getHeight() {
80             return mHeight;
81         }
82 
83         /**
84          * @return {@link Bitmap} that has the same pixel patterns.
85          */
86         @AnyThread
87         @NonNull
toBitmap()88         Bitmap toBitmap() {
89             return Bitmap.createBitmap(mPixels, mWidth, mHeight, Bitmap.Config.ARGB_8888);
90         }
91 
92         /**
93          * Checks if the same image can be found in the specified {@link BitmapImage} within
94          * within {@link #TOLERANCE}.
95          *
96          * @param targetImage {@link BitmapImage} to be checked.
97          * @param offsetX X offset in the {@code targetImage} used when comparing.
98          * @param offsetY Y offset in the {@code targetImage} used when comparing.
99          * @return {@true} if two given images are the same within {@link #TOLERANCE}.
100          */
101         @AnyThread
robustMatch(@onNull BitmapImage targetImage, int offsetX, int offsetY)102         boolean robustMatch(@NonNull BitmapImage targetImage, int offsetX, int offsetY) {
103             final int targetWidth = targetImage.getWidth();
104             final int targetHeight = targetImage.getHeight();
105             final int[] targetPixels = targetImage.mPixels;
106             final int[] sourcePixels = mPixels;
107 
108             if (offsetX < 0 || targetWidth <= mWidth - 1 + offsetX) return false;
109             if (offsetY < 0 || targetHeight <= mHeight - 1 + offsetY) return false;
110 
111             for (int y = 0; y < mHeight; ++y) {
112                 for (int x = 0; x < mWidth; ++x) {
113                     final int targetX = x + offsetX;
114                     final int targetY = y + offsetY;
115                     final int targetPx = targetPixels[targetY * targetWidth + targetX];
116                     final int sourcePx = sourcePixels[y * mWidth + x];
117                     // Compares two given pixels (targetPx & sourcePx) to determine whether those
118                     // two pixels are considered to be the same within {@link #TOLERANCE}.
119                     boolean match = targetPx == sourcePx
120                             || (Math.abs(Color.red(targetPx) - Color.red(sourcePx)) <= TOLERANCE
121                             && Math.abs(Color.green(targetPx) - Color.green(sourcePx)) <= TOLERANCE
122                             && Math.abs(Color.blue(targetPx) - Color.blue(sourcePx)) <= TOLERANCE);
123                     if (!match) {
124                         return false;
125                     }
126                 }
127             }
128             return true;
129         }
130     }
131 
132     /**
133      * Not intended to be instantiated.
134      */
Watermark()135     private Watermark() {
136     }
137 
138     private static final BitmapImage sImage;
139     static {
140         final long[] bitImage = new long[] {
141                 0b0000000000000000000000000000000000000000000000000000000000000000L,
142                 0b0000000000000000000000000000000000000000000000000000000000000000L,
143                 0b0000000000000000000000000000000000000000000000000000000000000000L,
144                 0b0001111111111111111111111111111111111111111111111111111111111000L,
145                 0b0001111111111111111111111111111111111111111111111111111111111000L,
146                 0b0001110010110100010000111100001000100001111000010001011010011000L,
147                 0b0001100011111011001110101101100000001101101011100110111110011000L,
148                 0b0001101101001011101111000011110111011110000111101110100101111000L,
149                 0b0001111100000100110001010010011111110010010100011001000001111000L,
150                 0b0001110010110100010000111100001000100001111000010001011010011000L,
151                 0b0001110011111011001110101101100000001101101011100110111110011000L,
152                 0b0001110101001011101111000011110111011110000111101110100101011000L,
153                 0b0001100100000100110001010010011111110010010100011001000001011000L,
154                 0b0001101110110100010000111100001000100001111000010001011011111000L,
155                 0b0001111111111011001110101101100000001101101011100110111111111000L,
156                 0b0001110011001011101111000011110111011110000111101110100110011000L,
157                 0b0001100001000100110001010010011111110010010100011001000100011000L,
158                 0b0001101101010100010000111100001000100001111000010001010101111000L,
159                 0b0001111110011011001110101101100000001101101011100110110011111000L,
160                 0b0001110010111011101111000011110111011110000111101110111010011000L,
161                 0b0001100001111100110001010010011111110010010100011001111100011000L,
162                 0b0001101101001000010000111100001000100001111000010000100101111000L,
163                 0b0001111110000101001110101101100000001101101011100101000011111000L,
164                 0b0001110010110100101111000011110111011110000111101001011010011000L,
165                 0b0001100001111011110001010010011111110010010100011110111100011000L,
166                 0b0001101101001011100000111100001000100001111000001110100101111000L,
167                 0b0001111110000100010110101101100000001101101011010001000011111000L,
168                 0b0001110010110100010011000011110111011110000110010001011010011000L,
169                 0b0001100001111011101111010010011111110010010111101110111100011000L,
170                 0b0001101101001011101111011100001000100001110111101110100101111000L,
171                 0b0001111110000100010001011101100000001101110100010001000011111000L,
172                 0b0001110010110100010000111101110111011101111000010001011010011000L,
173                 0b0001100001111011101110100101100010001101001011101110111100011000L,
174                 0b0001101101001011101111000011110111011110000111101110100101111000L,
175                 0b0001111110000100010001011010011101110010110100010001000011111000L,
176                 0b0001110010110100010000111100001000100001111000010001011010011000L,
177                 0b0001100001111011101110100101100010001101001011101110111100011000L,
178                 0b0001101101001011101111000011110111011110000111101110100101111000L,
179                 0b0001111110000100010001011010011101110010110100010001000011111000L,
180                 0b0001110010110100010000111100001000100001111000010001011010011000L,
181                 0b0001100001111011101110100101100010001101001011101110111100011000L,
182                 0b0001101101001011101111000011110111011110000111101110100101111000L,
183                 0b0001111110000100010001011010011101110010110100010001000011111000L,
184                 0b0001110010110100010000111100001000100001111000010001011010011000L,
185                 0b0001100001111011101110100101100010001101001011101110111100011000L,
186                 0b0001101101001011101111000011110111011110000111101110100101111000L,
187                 0b0001111110000100010001011010011101110010110100010001000011111000L,
188                 0b0001110010110100010000111100001000100001111000010001011010011000L,
189                 0b0001100001111011101110100101100010001101001011101110111100011000L,
190                 0b0001101101001011101111000011110111011110000111101110100101111000L,
191                 0b0001111110000100010001011010011101110010110100010001000011111000L,
192                 0b0001110010110100010000111100001000100001111000010001011010011000L,
193                 0b0001100001111011101110100101100010001101001011101110111100011000L,
194                 0b0001101101001011101111000011110111011110000111101110100101111000L,
195                 0b0001111110000100010001011010011101110010110100010001000011111000L,
196                 0b0001110010110100010000111100001000100001111000010001011010011000L,
197                 0b0001100001111011101110100101100010001101001011101110111100011000L,
198                 0b0001101101001011101111000011110111011110000111101110100101111000L,
199                 0b0001111110000100010001011010011101110010110100010001000011111000L,
200                 0b0001111111111111111111111111111111111111111111111111111111111000L,
201                 0b0001111111111111111111111111111111111111111111111111111111111000L,
202                 0b0000000000000000000000000000000000000000000000000000000000000000L,
203                 0b0000000000000000000000000000000000000000000000000000000000000000L,
204                 0b0000000000000000000000000000000000000000000000000000000000000000L,
205         };
206         final int size = 64;
207         final long mask = 0b1000000000000000000000000000000000000000000000000000000000000000L;
208         final int[] pixels = new int[size * size];
209         for (int y = 0; y < size; ++y) {
210             for (int x = 0; x < size; ++x) {
211                 pixels[y * size + x] =
212                         ((bitImage[y] << x) & mask) == mask ? 0xffffffff : 0xff000000;
213             }
214         }
215         sImage = new BitmapImage(pixels, size, size);
216     }
217 
218     /**
219      * @return {@link Bitmap} object of the unique image.
220      */
create()221     public static Bitmap create() {
222         return sImage.toBitmap();
223     }
224 
225     /**
226      * Check the unique image can be found in the specified {@link Bitmap}.
227      *
228      * @param bitmap {@link Bitmap} to be checked.
229      * @return {@code true} if the corresponding unique image is found in the {@code bitmap}.
230      */
detect(@onNull Bitmap bitmap)231     public static boolean detect(@NonNull Bitmap bitmap) {
232         final BitmapImage targetImage = BitmapImage.createFromBitmap(bitmap);
233 
234         // Search from the bottom line with an assumption that the IME is shown at the bottom.
235         final int targetImageHeight = targetImage.getHeight();
236         final int targetImageWidth = targetImage.getWidth();
237         for (int offsetY = targetImageHeight - 1; offsetY >= 0; --offsetY) {
238             for (int offsetX = 0; offsetX < targetImageWidth; ++offsetX) {
239                 if (sImage.robustMatch(targetImage, offsetX, offsetY)) {
240                     return true;
241                 }
242             }
243         }
244         return false;
245     }
246 }
247