• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.holo.cts;
18 
19 import com.android.cts.holo.R;
20 
21 import android.app.Activity;
22 import android.content.Intent;
23 import android.graphics.Bitmap;
24 import android.graphics.Canvas;
25 import android.graphics.Color;
26 import android.holo.cts.LayoutAdapter.LayoutInfo;
27 import android.holo.cts.ThemeAdapter.ThemeInfo;
28 import android.os.AsyncTask;
29 import android.os.Bundle;
30 import android.util.Log;
31 import android.view.View;
32 import android.widget.Toast;
33 
34 import java.io.FileNotFoundException;
35 import java.io.IOException;
36 import java.nio.ByteBuffer;
37 
38 /**
39  * {@link Activity} that applies a theme, inflates a layout, and then either
40  * compares or generates a bitmap of the layout.
41  */
42 public class LayoutTestActivity extends Activity {
43 
44     private static final String TAG = LayoutTestActivity.class.getSimpleName();
45 
46     // Input extras
47     static final String EXTRA_THEME_INDEX = "themeIndex";
48     static final String EXTRA_LAYOUT_INDEX = "layoutIndex";
49     static final String EXTRA_TASK = "task";
50     static final String EXTRA_LAYOUT_ADAPTER_MODE = "layoutAdapterMode";
51 
52     // Output extras
53     static final String EXTRA_BITMAP_NAME = "bitmapName";
54     static final String EXTRA_MESSAGE = "message";
55     static final String EXTRA_SUCCESS = "success";
56 
57     private View mTestView;
58     private String mBitmapName;
59 
60     private ReferenceViewGroup mViewGroup;
61 
62     @Override
onCreate(Bundle savedInstanceState)63     protected void onCreate(Bundle savedInstanceState) {
64         super.onCreate(savedInstanceState);
65 
66         int themeIndex = getIntent().getIntExtra(EXTRA_THEME_INDEX, -1);
67         int layoutIndex = getIntent().getIntExtra(EXTRA_LAYOUT_INDEX, -1);
68         int layoutMode = getIntent().getIntExtra(EXTRA_LAYOUT_ADAPTER_MODE, -1);
69         int task = getIntent().getIntExtra(EXTRA_TASK, -1);
70 
71         ThemeAdapter themeAdapter = new ThemeAdapter(getLayoutInflater());
72         LayoutAdapter layoutAdapter = new LayoutAdapter(getLayoutInflater(), layoutMode);
73 
74         ThemeInfo themeInfo = themeAdapter.getItem(themeIndex);
75         LayoutInfo layoutInfo = layoutAdapter.getItem(layoutIndex);
76         mBitmapName = BitmapAssets.getBitmapName(themeInfo, layoutInfo);
77 
78         setTheme(themeInfo.getTheme());
79         setContentView(R.layout.holo_test);
80 
81         if (layoutInfo.hasModifier()) {
82             layoutInfo.getModifier().prepare();
83         }
84 
85         // Inflate the view in our special view group that is fixed at a certain size
86         // and layout the inflated view with the fixed measurements...
87         mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
88         mTestView = getLayoutInflater().inflate(layoutInfo.getLayout(), mViewGroup, false);
89         mViewGroup.addView(mTestView);
90         if (layoutInfo.hasModifier()) {
91             mTestView = layoutInfo.getModifier().modifyView(mTestView);
92         }
93         mViewGroup.measure(0,  0);
94         mViewGroup.layout(0, 0, mViewGroup.getMeasuredWidth(), mViewGroup.getMeasuredHeight());
95         mTestView.setFocusable(false);
96 
97         switch (task) {
98             case ThemeTestActivity.TASK_VIEW_LAYOUTS:
99                 break;
100 
101             case ThemeTestActivity.TASK_GENERATE_BITMAPS:
102                 mTestView.postDelayed(new GenerateBitmapRunnable(), layoutInfo.getTimeoutMs());
103                 break;
104 
105             case ThemeTestActivity.TASK_COMPARE_BITMAPS:
106                 mTestView.postDelayed(new CompareBitmapRunnable(), layoutInfo.getTimeoutMs());
107                 break;
108         }
109     }
110 
111     class GenerateBitmapRunnable implements Runnable {
112         @Override
run()113         public void run() {
114             new GenerateBitmapTask().execute();
115         }
116     }
117 
118     class CompareBitmapRunnable implements Runnable {
119         @Override
run()120         public void run() {
121             new CompareBitmapTask().execute();
122         }
123     }
124 
125     class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
126 
127         private Bitmap mBitmap;
128 
129         @Override
onPreExecute()130         protected void onPreExecute() {
131             super.onPreExecute();
132             mBitmap = getBitmap();
133         }
134 
135         @Override
doInBackground(Void... avoid)136         protected Boolean doInBackground(Void... avoid) {
137             try {
138                 return saveBitmap(mBitmap, BitmapAssets.TYPE_REFERENCE) != null;
139             } finally {
140                 mBitmap.recycle();
141                 mBitmap = null;
142             }
143         }
144 
145         @Override
onPostExecute(Boolean success)146         protected void onPostExecute(Boolean success) {
147             String path = BitmapAssets.getBitmapPath(mBitmapName,
148                     BitmapAssets.TYPE_REFERENCE).toString();
149             String message = path != null
150                     ? getString(R.string.generate_bitmap_success, path)
151                     : getString(R.string.generate_bitmap_failure, path);
152             Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
153             finishWithResult(success, message);
154         }
155     }
156 
157     class CompareBitmapTask extends AsyncTask<Void, Void, String[]> {
158         private Bitmap mBitmap;
159         private Bitmap mReferenceBitmap;
160         private boolean mSame;
161 
162         @Override
onPreExecute()163         protected void onPreExecute() {
164             mBitmap = getBitmap();
165             mReferenceBitmap = BitmapAssets.getBitmap(getApplicationContext(), mBitmapName);
166         }
167 
168         /* Compares 2 bitmaps' width, height and pixels.
169          * 2 Bitmaps are consider the same if color value difference is less than
170          * or equal to +/-threshold
171          */
compareTo(Bitmap bitmap, Bitmap reference, int threshold)172         private boolean compareTo(Bitmap bitmap, Bitmap reference, int threshold) {
173             if (bitmap.getConfig() != reference.getConfig() ||
174                     bitmap.getWidth() != reference.getWidth() ||
175                     bitmap.getHeight() != reference.getHeight()) {
176                 return false;
177             }
178 
179             int w = bitmap.getWidth();
180             int h = bitmap.getHeight();
181 
182             ByteBuffer buffer1 = ByteBuffer.allocate(bitmap.getByteCount());
183             ByteBuffer buffer2 = ByteBuffer.allocate(reference.getByteCount());
184 
185             bitmap.copyPixelsToBuffer(buffer1);
186             reference.copyPixelsToBuffer(buffer2);
187 
188             final int length = w*h;
189             for (int i = 0; i < length; i++) {
190                 int pel1 = buffer1.getInt(i);
191                 int pel2 = buffer2.getInt(i);
192                 int dr = (pel1 & 0x000000FF) - (pel2 & 0x000000FF);
193                 int dg = ((pel1 & 0x0000FF00) - (pel2 & 0x0000FF00)) >> 8;
194                 int db = ((pel1 & 0x00FF0000) - (pel2 & 0x00FF0000)) >> 16;
195 
196                 if (Math.abs(db) > threshold ||
197                         Math.abs(dg) > threshold ||
198                         Math.abs(dr) > threshold) {
199                     return false;
200                 }
201                 if (bitmap.hasAlpha()) {
202                     int da = ((pel1 & 0xFF000000) - (pel2 & 0xFF000000)) >> 24;
203                     if (Math.abs(da) > threshold) {
204                         return false;
205                     }
206                 }
207             }
208             return true;
209         }
210 
211         @Override
doInBackground(Void... devoid)212         protected String[] doInBackground(Void... devoid) {
213             try {
214                 final int threshold = 2;
215                 mSame = compareTo(mBitmap, mReferenceBitmap, threshold);
216                 if (!mSame) {
217                     String[] paths = new String[2];
218                     paths[0] = saveDiffBitmap(mBitmap, mReferenceBitmap);
219                     paths[1] = saveBitmap(mBitmap, BitmapAssets.TYPE_FAILED);
220                     return paths;
221                 } else {
222                     return null;
223                 }
224             } finally {
225                 mBitmap.recycle();
226                 mBitmap = null;
227             }
228         }
229 
saveDiffBitmap(Bitmap bitmap1, Bitmap bitmap2)230         private String saveDiffBitmap(Bitmap bitmap1, Bitmap bitmap2) {
231             int width = Math.max(bitmap1.getWidth(), bitmap2.getWidth());
232             int height = Math.max(bitmap1.getHeight(), bitmap2.getHeight());
233             Bitmap diff = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
234 
235             try {
236                 for (int i = 0; i < width; i++) {
237                     for (int j = 0; j < height; j++) {
238                         boolean inBounds1 = i < bitmap1.getWidth() && j < bitmap1.getHeight();
239                         boolean inBounds2 = i < bitmap2.getWidth() && j < bitmap2.getHeight();
240                         int color;
241 
242                         if (inBounds1 && inBounds2) {
243                             int color1 = bitmap1.getPixel(i, j);
244                             int color2 = bitmap2.getPixel(i, j);
245                             color = color1 == color2 ? color1 : Color.RED;
246                         } else if (inBounds1 && !inBounds2) {
247                             color = Color.BLUE;
248                         } else if (!inBounds1 && inBounds2) {
249                             color = Color.GREEN;
250                         } else {
251                             color = Color.MAGENTA;
252                         }
253                         diff.setPixel(i, j, color);
254                     }
255                 }
256 
257                 return saveBitmap(diff, BitmapAssets.TYPE_DIFF);
258             } finally {
259                 diff.recycle();
260             }
261         }
262 
263         @Override
264         protected void onPostExecute(String[] paths) {
265             String message = mSame
266                     ? getString(R.string.comparison_success)
267                     : getString(R.string.comparison_failure, paths[0], paths[1]);
268             finishWithResult(mSame, message);
269         }
270     }
271 
272     private Bitmap getBitmap() {
273         Log.i(TAG, "Getting bitmap for " + mBitmapName);
274         Bitmap bitmap = Bitmap.createBitmap(mTestView.getWidth(), mTestView.getHeight(),
275                 Bitmap.Config.ARGB_8888);
276         Canvas canvas = new Canvas(bitmap);
277         mTestView.draw(canvas);
278         return bitmap;
279     }
280 
281     private String saveBitmap(Bitmap bitmap, int type) {
282         try {
283             Log.i(TAG, "Saving bitmap for " + mBitmapName);
284             return BitmapAssets.saveBitmap(bitmap, mBitmapName, type);
285         } catch (FileNotFoundException e) {
286             Log.e(TAG, "FileNotFoundException while saving " + mBitmapName, e);
287             return null;
288         } catch (IOException e) {
289             Log.e(TAG, "IOException while saving " + mBitmapName, e);
290             return null;
291         }
292     }
293 
294     private void finishWithResult(boolean success, String message) {
295         Intent data = new Intent();
296         data.putExtra(EXTRA_SUCCESS, success);
297         data.putExtra(EXTRA_MESSAGE, message);
298         data.putExtra(EXTRA_BITMAP_NAME, mBitmapName);
299         setResult(RESULT_OK, data);
300         finish();
301     }
302 }
303