Merge "Initial commit of leanback uibench test" into oc-mr1-dev

am: 70bc905201

Change-Id: Ib57348d8d5983383598aa5f0adcf1845038b76be
This commit is contained in:
Dake Gu
2017-07-21 21:01:07 +00:00
committed by android-build-merger
9 changed files with 604 additions and 3 deletions

View File

@@ -15,21 +15,24 @@ LOCAL_RESOURCE_DIR := \
frameworks/support/design/res \
frameworks/support/v7/appcompat/res \
frameworks/support/v7/cardview/res \
frameworks/support/v7/recyclerview/res
frameworks/support/v7/recyclerview/res \
frameworks/support/v17/leanback/res
LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--extra-packages android.support.design \
--extra-packages android.support.v7.appcompat \
--extra-packages android.support.v7.cardview \
--extra-packages android.support.v7.recyclerview
--extra-packages android.support.v7.recyclerview \
--extra-packages android.support.v17.leanback
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-design \
android-support-v4 \
android-support-v7-appcompat \
android-support-v7-cardview \
android-support-v7-recyclerview
android-support-v7-recyclerview \
android-support-v17-leanback
LOCAL_PACKAGE_NAME := UiBench

View File

@@ -257,5 +257,15 @@
<category android:name="com.android.test.uibench.TEST" />
</intent-filter>
</activity>
<activity
android:name=".leanback.BrowseActivity"
android:theme="@style/Theme.Leanback.Browse"
android:label="Leanback/Browse Fragment" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.uibench.TEST" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -36,4 +36,5 @@ dependencies {
compile 'com.android.support:cardview-v7:23.0.1'
compile 'com.android.support:recyclerview-v7:23.0.1'
compile 'com.android.support:design:23.0.1'
compile 'com.android.support:leanback-v17:23.0.1'
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2017 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 com.android.test.uibench.leanback;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.AsyncTask;
import android.support.v4.util.LruCache;
import android.util.DisplayMetrics;
import android.widget.ImageView;
/**
* This class simulates a typical Bitmap memory cache with up to 1.5 times of screen pixels.
* The sample bitmap is generated in worker threads in AsyncTask.THREAD_POOL_EXECUTOR.
* The class does not involve decoding, disk cache i/o, network i/o, as the test is mostly focusing
* on the graphics side.
* There will be two general use cases for cards in leanback test:
* 1. As a typical app, each card has its own id and load its own Bitmap, the test result will
* include impact of texture upload.
* 2. All cards share same id/Bitmap and there wont be texture upload.
*/
public class BitmapLoader {
/**
* Caches bitmaps with bytes adds up to 1.5 x screen
* DO NOT CHANGE this defines baseline of test result.
*/
static final float CACHE_SIZE_TO_SCREEN = 1.5f;
/**
* 4 bytes per pixel for RGBA_8888
*/
static final int BYTES_PER_PIXEL = 4;
static LruCache<Long, Bitmap> sLruCache;
static Paint sTextPaint = new Paint();
static {
sTextPaint.setColor(Color.BLACK);
}
/**
* get or initialize LruCache, the max is set to full screen pixels.
*/
static LruCache<Long, Bitmap> getLruCache(Context context) {
if (sLruCache == null) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
int maxBytes = (int) (width * height * BYTES_PER_PIXEL * CACHE_SIZE_TO_SCREEN);
sLruCache = new LruCache<Long, Bitmap>(maxBytes) {
@Override
protected int sizeOf(Long key, Bitmap value) {
return value.getByteCount();
}
};
}
return sLruCache;
}
static class BitmapAsyncTask extends AsyncTask<Void, Void, Bitmap> {
ImageView mImageView;
long mId;
int mWidth;
int mHeight;
BitmapAsyncTask(ImageView view, long id, int width, int height) {
mImageView = view;
mId = id;
mImageView.setTag(this);
mWidth = width;
mHeight = height;
}
@Override
protected Bitmap doInBackground(Void... voids) {
// generate a sample bitmap: white background and text showing id
Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawARGB(0xff, 0xff, 0xff, 0xff);
canvas.drawText(Long.toString(mId), 0f, mHeight / 2, sTextPaint);
canvas.setBitmap(null);
bitmap.prepareToDraw();
return bitmap;
}
@Override
protected void onCancelled() {
if (mImageView.getTag() == this) {
mImageView.setTag(null);
}
}
@Override
protected void onPostExecute(Bitmap bitmap) {
if (mImageView.getTag() == this) {
mImageView.setTag(null);
sLruCache.put(mId, bitmap);
mImageView.setImageBitmap(bitmap);
}
}
}
public static void loadBitmap(ImageView view, long id, int width, int height) {
Context context = view.getContext();
Bitmap bitmap = getLruCache(context).get(id);
if (bitmap != null) {
view.setImageBitmap(bitmap);
return;
}
new BitmapAsyncTask(view, id, width, height)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public static void cancel(ImageView view) {
BitmapAsyncTask task = (BitmapAsyncTask) view.getTag();
if (task != null && task.mImageView == view) {
task.mImageView.setTag(null);
task.cancel(false);
}
}
public static void clear() {
if (sLruCache != null) {
sLruCache.evictAll();
}
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (C) 2017 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 com.android.test.uibench.leanback;
import android.support.v4.app.FragmentActivity;
import android.app.Activity;
import android.os.Bundle;
public class BrowseActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, new BrowseFragment())
.commit();
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2017 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 com.android.test.uibench.leanback;
import android.os.Bundle;
public class BrowseFragment extends android.support.v17.leanback.app.BrowseSupportFragment {
public BrowseFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BitmapLoader.clear();
TestHelper.initBackground(getActivity());
boolean runEntranceTransition = TestHelper.runEntranceTransition(getActivity());
if (runEntranceTransition) {
prepareEntranceTransition();
}
setAdapter(TestHelper.initRowsAdapterBuilder(getActivity()).build());
if (runEntranceTransition) {
startEntranceTransition();
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2017 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 com.android.test.uibench.leanback;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v17.leanback.widget.ImageCardView;
import android.support.v17.leanback.widget.Presenter;
import android.support.v4.content.res.ResourcesCompat;
import android.view.ContextThemeWrapper;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
public class CardPresenter extends Presenter {
private int mImageWidth = 0;
private int mImageHeight = 0;
public CardPresenter(int width, int height) {
mImageWidth = width;
mImageHeight = height;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent) {
Context context = parent.getContext();
ImageCardView v = new ImageCardView(context);
v.setFocusable(true);
v.setFocusableInTouchMode(true);
v.setMainImageAdjustViewBounds(true);
v.setMainImageDimensions(mImageWidth, mImageHeight);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, Object item) {
PhotoItem photoItem = (PhotoItem) item;
ImageCardView cardView = (ImageCardView) viewHolder.view;
cardView.setTitleText(photoItem.getTitle());
BitmapLoader.loadBitmap(cardView.getMainImageView(), photoItem.getId(),
mImageWidth, mImageHeight);
}
@Override
public void onUnbindViewHolder(ViewHolder viewHolder) {
ImageCardView cardView = (ImageCardView) viewHolder.view;
BitmapLoader.cancel(cardView.getMainImageView());
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2017 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 com.android.test.uibench.leanback;
import android.os.Parcel;
import android.os.Parcelable;
public class PhotoItem implements Parcelable {
private String mTitle;
private long mId;
public PhotoItem(String title, long id) {
mTitle = title;
mId = id;
}
public long getId() {
return mId;
}
public String getTitle() {
return mTitle;
}
@Override
public String toString() {
return mTitle;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mTitle);
dest.writeLong(mId);
}
public static final Creator<PhotoItem> CREATOR
= new Creator<PhotoItem>() {
@Override
public PhotoItem createFromParcel(Parcel in) {
return new PhotoItem(in);
}
@Override
public PhotoItem[] newArray(int size) {
return new PhotoItem[size];
}
};
private PhotoItem(Parcel in) {
mTitle = in.readString();
mId = in.readLong();
}
}

View File

@@ -0,0 +1,238 @@
/*
* Copyright (C) 2017 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 com.android.test.uibench.leanback;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.support.v17.leanback.app.BackgroundManager;
import android.support.v17.leanback.widget.ArrayObjectAdapter;
import android.support.v17.leanback.widget.HeaderItem;
import android.support.v17.leanback.widget.ListRow;
import android.support.v17.leanback.widget.ListRowPresenter;
import android.support.v17.leanback.widget.ObjectAdapter;
import android.support.v17.leanback.widget.Presenter;
import android.util.DisplayMetrics;
import android.util.TypedValue;
public class TestHelper {
public static final String EXTRA_BACKGROUND = "extra_bg";
public static final String EXTRA_ROWS = "extra_rows";
public static final String EXTRA_CARDS_PER_ROW = "extra_cards_per_row";
public static final String EXTRA_CARD_HEIGHT_DP = "extra_card_height";
public static final String EXTRA_CARD_WIDTH_DP = "extra_card_width";
public static final String EXTRA_CARD_SHADOW = "extra_card_shadow";
public static final String EXTRA_CARD_ROUND_RECT = "extra_card_round_rect";
public static final String EXTRA_ENTRANCE_TRANSITION = "extra_entrance_transition";
public static final String EXTRA_BITMAP_UPLOAD = "extra_bitmap_upload";
/**
* Dont change the default values, they gave baseline for measuring the performance
*/
static final int DEFAULT_CARD_HEIGHT_DP = 180;
static final int DEFAULT_CARD_WIDTH_DP = 125;
static final int DEFAULT_CARDS_PER_ROW = 20;
static final int DEFAULT_ROWS = 10;
static final boolean DEFAULT_ENTRANCE_TRANSITION = false;
static final boolean DEFAULT_BACKGROUND = true;
static final boolean DEFAULT_CARD_SHADOW = true;
static final boolean DEFAULT_CARD_ROUND_RECT = true;
static final boolean DEFAULT_BITMAP_UPLOAD = true;
static long sCardIdSeed = 0;
static long sRowIdSeed = 0;
public static class ListRowPresenterBuilder {
boolean mShadow = DEFAULT_CARD_SHADOW;
boolean mRoundedCorner = DEFAULT_CARD_ROUND_RECT;
ListRowPresenterBuilder(Context context) {
}
public ListRowPresenterBuilder configShadow(boolean shadow) {
mShadow = shadow;
return this;
}
public ListRowPresenterBuilder configRoundedCorner(boolean roundedCorner) {
mRoundedCorner = roundedCorner;
return this;
}
public ListRowPresenter build() {
ListRowPresenter listRowPresenter = new ListRowPresenter();
listRowPresenter.setShadowEnabled(mShadow);
listRowPresenter.enableChildRoundedCorners(mRoundedCorner);
return listRowPresenter;
}
}
public static class CardPresenterBuilder {
Context mContext;
int mWidthDP = DEFAULT_CARD_WIDTH_DP;
int mHeightDP = DEFAULT_CARD_HEIGHT_DP;
CardPresenterBuilder(Context context) {
mContext = context;
}
public CardPresenterBuilder configWidthDP(int widthDP) {
mWidthDP = widthDP;
return this;
}
public CardPresenterBuilder configHeightDP(int hightDP) {
mHeightDP = hightDP;
return this;
}
public Presenter build() {
DisplayMetrics dm = mContext.getResources().getDisplayMetrics();
return new CardPresenter(
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mWidthDP, dm),
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, mHeightDP, dm));
}
}
public static class RowsAdapterBuilder {
Context mContext;
int mCardsPerRow = DEFAULT_CARDS_PER_ROW;
int mRows = DEFAULT_ROWS;
CardPresenterBuilder mCardPresenterBuilder;
ListRowPresenterBuilder mListRowPresenterBuilder;
Presenter mCardPresenter;
boolean mBitmapUpload = DEFAULT_BITMAP_UPLOAD;
static final String[] sSampleStrings = new String[] {
"Hello world", "This is a test", "Android TV", "UI Jank Test",
"Scroll Up", "Scroll Down", "Load Bitmaps"
};
/**
* Create a RowsAdapterBuilder with default settings
*/
public RowsAdapterBuilder(Context context) {
mContext = context;
mCardPresenterBuilder = new CardPresenterBuilder(context);
mListRowPresenterBuilder = new ListRowPresenterBuilder(context);
}
public ListRowPresenterBuilder getListRowPresenterBuilder() {
return mListRowPresenterBuilder;
}
public CardPresenterBuilder getCardPresenterBuilder() {
return mCardPresenterBuilder;
}
public RowsAdapterBuilder configRows(int rows) {
mRows = rows;
return this;
}
public RowsAdapterBuilder configCardsPerRow(int cardsPerRow) {
mCardsPerRow = cardsPerRow;
return this;
}
public RowsAdapterBuilder configBitmapUpLoad(boolean bitmapUpload) {
mBitmapUpload = bitmapUpload;
return this;
}
public ListRow buildListRow() {
ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(mCardPresenter);
ListRow listRow = new ListRow(new HeaderItem(sRowIdSeed++, "Row"), listRowAdapter);
int indexSample = 0;
for (int i = 0; i < mCardsPerRow; i++) {
// when doing bitmap upload, use different id so each card has different bitmap
// otherwise all cards share the same bitmap
listRowAdapter.add(new PhotoItem(sSampleStrings[indexSample],
(mBitmapUpload ? sCardIdSeed++ : 0)));
indexSample++;
if (indexSample >= sSampleStrings.length) {
indexSample = 0;
}
}
return listRow;
}
public ObjectAdapter build() {
try {
mCardPresenter = mCardPresenterBuilder.build();
ArrayObjectAdapter adapter = new ArrayObjectAdapter(
mListRowPresenterBuilder.build());
for (int i = 0; i < mRows; i++) {
adapter.add(buildListRow());
}
return adapter;
} finally {
mCardPresenter = null;
}
}
}
public static boolean runEntranceTransition(Activity activity) {
return activity.getIntent().getBooleanExtra(EXTRA_ENTRANCE_TRANSITION,
DEFAULT_ENTRANCE_TRANSITION);
}
public static RowsAdapterBuilder initRowsAdapterBuilder(Activity activity) {
RowsAdapterBuilder builder = new RowsAdapterBuilder(activity);
boolean shadow = activity.getIntent().getBooleanExtra(EXTRA_CARD_SHADOW,
DEFAULT_CARD_SHADOW);
boolean roundRect = activity.getIntent().getBooleanExtra(EXTRA_CARD_ROUND_RECT,
DEFAULT_CARD_ROUND_RECT);
int widthDp = activity.getIntent().getIntExtra(EXTRA_CARD_WIDTH_DP,
DEFAULT_CARD_WIDTH_DP);
int heightDp = activity.getIntent().getIntExtra(EXTRA_CARD_HEIGHT_DP,
DEFAULT_CARD_HEIGHT_DP);
int rows = activity.getIntent().getIntExtra(EXTRA_ROWS, DEFAULT_ROWS);
int cardsPerRow = activity.getIntent().getIntExtra(EXTRA_CARDS_PER_ROW,
DEFAULT_CARDS_PER_ROW);
boolean bitmapUpload = activity.getIntent().getBooleanExtra(EXTRA_BITMAP_UPLOAD,
DEFAULT_BITMAP_UPLOAD);
builder.configRows(rows)
.configCardsPerRow(cardsPerRow)
.configBitmapUpLoad(bitmapUpload);
builder.getListRowPresenterBuilder()
.configRoundedCorner(roundRect)
.configShadow(shadow);
builder.getCardPresenterBuilder()
.configWidthDP(widthDp)
.configHeightDP(heightDp);
return builder;
}
public static void initBackground(Activity activity) {
if (activity.getIntent().getBooleanExtra(EXTRA_BACKGROUND, DEFAULT_BACKGROUND)) {
BackgroundManager manager = BackgroundManager.getInstance(activity);
manager.attach(activity.getWindow());
DisplayMetrics metrics = activity.getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawARGB(255, 128, 128, 128);
canvas.setBitmap(null);
manager.setBitmap(bitmap);
}
}
}