blob: 2b6c37152c55f5ef684b8cf9a9d4e51cc687e7c7 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.holo.cts;
import com.android.cts.holo.R;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.holo.cts.LayoutAdapter.LayoutInfo;
import android.holo.cts.ThemeAdapter.ThemeInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* {@link Activity} that applies a theme, inflates a layout, and then either
* compares or generates a bitmap of the layout.
*/
public class LayoutTestActivity extends Activity {
private static final String TAG = LayoutTestActivity.class.getSimpleName();
// Input extras
static final String EXTRA_THEME_INDEX = "themeIndex";
static final String EXTRA_LAYOUT_INDEX = "layoutIndex";
static final String EXTRA_TASK = "task";
static final String EXTRA_LAYOUT_ADAPTER_MODE = "layoutAdapterMode";
// Output extras
static final String EXTRA_BITMAP_NAME = "bitmapName";
static final String EXTRA_MESSAGE = "message";
static final String EXTRA_SUCCESS = "success";
private View mTestView;
private String mBitmapName;
private ReferenceViewGroup mViewGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
int themeIndex = getIntent().getIntExtra(EXTRA_THEME_INDEX, -1);
int layoutIndex = getIntent().getIntExtra(EXTRA_LAYOUT_INDEX, -1);
int layoutMode = getIntent().getIntExtra(EXTRA_LAYOUT_ADAPTER_MODE, -1);
int task = getIntent().getIntExtra(EXTRA_TASK, -1);
ThemeAdapter themeAdapter = new ThemeAdapter(getLayoutInflater());
LayoutAdapter layoutAdapter = new LayoutAdapter(getLayoutInflater(), layoutMode);
ThemeInfo themeInfo = themeAdapter.getItem(themeIndex);
LayoutInfo layoutInfo = layoutAdapter.getItem(layoutIndex);
mBitmapName = BitmapAssets.getBitmapName(themeInfo, layoutInfo);
setTheme(themeInfo.getTheme());
setContentView(R.layout.holo_test);
if (layoutInfo.hasModifier()) {
layoutInfo.getModifier().prepare();
}
// Inflate the view in our special view group that is fixed at a certain size
// and layout the inflated view with the fixed measurements...
mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
mTestView = getLayoutInflater().inflate(layoutInfo.getLayout(), mViewGroup, false);
mViewGroup.addView(mTestView);
if (layoutInfo.hasModifier()) {
mTestView = layoutInfo.getModifier().modifyView(mTestView);
}
mViewGroup.measure(0, 0);
mViewGroup.layout(0, 0, mViewGroup.getMeasuredWidth(), mViewGroup.getMeasuredHeight());
mTestView.setFocusable(false);
switch (task) {
case ThemeTestActivity.TASK_VIEW_LAYOUTS:
break;
case ThemeTestActivity.TASK_GENERATE_BITMAPS:
mTestView.postDelayed(new GenerateBitmapRunnable(), layoutInfo.getTimeoutMs());
break;
case ThemeTestActivity.TASK_COMPARE_BITMAPS:
mTestView.postDelayed(new CompareBitmapRunnable(), layoutInfo.getTimeoutMs());
break;
}
}
class GenerateBitmapRunnable implements Runnable {
@Override
public void run() {
new GenerateBitmapTask().execute();
}
}
class CompareBitmapRunnable implements Runnable {
@Override
public void run() {
new CompareBitmapTask().execute();
}
}
class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
private Bitmap mBitmap;
@Override
protected void onPreExecute() {
super.onPreExecute();
mBitmap = getBitmap();
}
@Override
protected Boolean doInBackground(Void... avoid) {
try {
return saveBitmap(mBitmap, BitmapAssets.TYPE_REFERENCE) != null;
} finally {
mBitmap.recycle();
mBitmap = null;
}
}
@Override
protected void onPostExecute(Boolean success) {
String path = BitmapAssets.getBitmapPath(mBitmapName,
BitmapAssets.TYPE_REFERENCE).toString();
String message = path != null
? getString(R.string.generate_bitmap_success, path)
: getString(R.string.generate_bitmap_failure, path);
Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
finishWithResult(success, message);
}
}
class CompareBitmapTask extends AsyncTask<Void, Void, String[]> {
private Bitmap mBitmap;
private Bitmap mReferenceBitmap;
private boolean mSame;
@Override
protected void onPreExecute() {
mBitmap = getBitmap();
mReferenceBitmap = BitmapAssets.getBitmap(getApplicationContext(), mBitmapName);
}
/* Compares 2 bitmaps' width, height and pixels.
* 2 Bitmaps are consider the same if color value difference is less than
* or equal to +/-threshold
*/
private boolean compareTo(Bitmap bitmap, Bitmap reference, int threshold) {
if (bitmap.getConfig() != reference.getConfig() ||
bitmap.getWidth() != reference.getWidth() ||
bitmap.getHeight() != reference.getHeight()) {
return false;
}
int w = bitmap.getWidth();
int h = bitmap.getHeight();
ByteBuffer buffer1 = ByteBuffer.allocate(bitmap.getByteCount());
ByteBuffer buffer2 = ByteBuffer.allocate(reference.getByteCount());
bitmap.copyPixelsToBuffer(buffer1);
reference.copyPixelsToBuffer(buffer2);
final int length = w*h;
for (int i = 0; i < length; i++) {
int pel1 = buffer1.getInt(i);
int pel2 = buffer2.getInt(i);
int dr = (pel1 & 0x000000FF) - (pel2 & 0x000000FF);
int dg = ((pel1 & 0x0000FF00) - (pel2 & 0x0000FF00)) >> 8;
int db = ((pel1 & 0x00FF0000) - (pel2 & 0x00FF0000)) >> 16;
if (Math.abs(db) > threshold ||
Math.abs(dg) > threshold ||
Math.abs(dr) > threshold) {
return false;
}
if (bitmap.hasAlpha()) {
int da = ((pel1 & 0xFF000000) - (pel2 & 0xFF000000)) >> 24;
if (Math.abs(da) > threshold) {
return false;
}
}
}
return true;
}
@Override
protected String[] doInBackground(Void... devoid) {
try {
final int threshold = 2;
mSame = compareTo(mBitmap, mReferenceBitmap, threshold);
if (!mSame) {
String[] paths = new String[2];
paths[0] = saveDiffBitmap(mBitmap, mReferenceBitmap);
paths[1] = saveBitmap(mBitmap, BitmapAssets.TYPE_FAILED);
return paths;
} else {
return null;
}
} finally {
mBitmap.recycle();
mBitmap = null;
}
}
private String saveDiffBitmap(Bitmap bitmap1, Bitmap bitmap2) {
int width = Math.max(bitmap1.getWidth(), bitmap2.getWidth());
int height = Math.max(bitmap1.getHeight(), bitmap2.getHeight());
Bitmap diff = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
try {
for (int i = 0; i < width; i++) {
for (int j = 0; j < height; j++) {
boolean inBounds1 = i < bitmap1.getWidth() && j < bitmap1.getHeight();
boolean inBounds2 = i < bitmap2.getWidth() && j < bitmap2.getHeight();
int color;
if (inBounds1 && inBounds2) {
int color1 = bitmap1.getPixel(i, j);
int color2 = bitmap2.getPixel(i, j);
color = color1 == color2 ? color1 : Color.RED;
} else if (inBounds1 && !inBounds2) {
color = Color.BLUE;
} else if (!inBounds1 && inBounds2) {
color = Color.GREEN;
} else {
color = Color.MAGENTA;
}
diff.setPixel(i, j, color);
}
}
return saveBitmap(diff, BitmapAssets.TYPE_DIFF);
} finally {
diff.recycle();
}
}
@Override
protected void onPostExecute(String[] paths) {
String message = mSame
? getString(R.string.comparison_success)
: getString(R.string.comparison_failure, paths[0], paths[1]);
finishWithResult(mSame, message);
}
}
private Bitmap getBitmap() {
Log.i(TAG, "Getting bitmap for " + mBitmapName);
Bitmap bitmap = Bitmap.createBitmap(mTestView.getWidth(), mTestView.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
mTestView.draw(canvas);
return bitmap;
}
private String saveBitmap(Bitmap bitmap, int type) {
try {
Log.i(TAG, "Saving bitmap for " + mBitmapName);
return BitmapAssets.saveBitmap(bitmap, mBitmapName, type);
} catch (FileNotFoundException e) {
Log.e(TAG, "FileNotFoundException while saving " + mBitmapName, e);
return null;
} catch (IOException e) {
Log.e(TAG, "IOException while saving " + mBitmapName, e);
return null;
}
}
private void finishWithResult(boolean success, String message) {
Intent data = new Intent();
data.putExtra(EXTRA_SUCCESS, success);
data.putExtra(EXTRA_MESSAGE, message);
data.putExtra(EXTRA_BITMAP_NAME, mBitmapName);
setResult(RESULT_OK, data);
finish();
}
}