auto import from //depot/cupcake/@135843
diff --git a/tests/FrameworkTest/src/com/android/frameworktest/util/ListScenario.java b/tests/FrameworkTest/src/com/android/frameworktest/util/ListScenario.java
new file mode 100644
index 0000000..5889658
--- /dev/null
+++ b/tests/FrameworkTest/src/com/android/frameworktest/util/ListScenario.java
@@ -0,0 +1,662 @@
+/*
+ * Copyright (C) 2007 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.frameworktest.util;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.TextView;
+import com.google.android.collect.Maps;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility base class for creating various List scenarios. Configurable by the number
+ * of items, how tall each item should be (in relation to the screen height), and
+ * what item should start with selection.
+ */
+public abstract class ListScenario extends Activity {
+
+ private ListView mListView;
+ private TextView mHeaderTextView;
+
+ private int mNumItems;
+ protected boolean mItemsFocusable;
+
+ private int mStartingSelectionPosition;
+ private double mItemScreenSizeFactor;
+ private Map<Integer, Double> mOverrideItemScreenSizeFactors = Maps.newHashMap();
+
+ private int mScreenHeight;
+
+ // whether to include a text view above the list
+ private boolean mIncludeHeader;
+
+ // separators
+ private Set<Integer> mUnselectableItems = new HashSet<Integer>();
+
+ private boolean mStackFromBottom;
+
+ private int mClickedPosition = -1;
+
+ private int mLongClickedPosition = -1;
+
+ private int mConvertMisses = 0;
+
+ private int mHeaderViewCount;
+ private boolean mHeadersFocusable;
+
+ private int mFooterViewCount;
+ private LinearLayout mLinearLayout;
+
+ public ListView getListView() {
+ return mListView;
+ }
+
+ protected int getScreenHeight() {
+ return mScreenHeight;
+ }
+
+ /**
+ * Return whether the item at position is selectable (i.e is a separator).
+ * (external users can access this info using the adapter)
+ */
+ private boolean isItemAtPositionSelectable(int position) {
+ return !mUnselectableItems.contains(position);
+ }
+
+ /**
+ * Better way to pass in optional params than a honkin' paramater list :)
+ */
+ public static class Params {
+ private int mNumItems = 4;
+ private boolean mItemsFocusable = false;
+ private int mStartingSelectionPosition = 0;
+ private double mItemScreenSizeFactor = 1 / 5;
+ private Double mFadingEdgeScreenSizeFactor = null;
+
+ private Map<Integer, Double> mOverrideItemScreenSizeFactors = Maps.newHashMap();
+
+ // separators
+ private List<Integer> mUnselectableItems = new ArrayList<Integer>(8);
+ // whether to include a text view above the list
+ private boolean mIncludeHeader = false;
+ private boolean mStackFromBottom = false;
+ public boolean mMustFillScreen = true;
+ private int mHeaderViewCount;
+ private boolean mHeaderFocusable = false;
+ private int mFooterViewCount;
+
+ private boolean mConnectAdapter = true;
+
+ /**
+ * Set the number of items in the list.
+ */
+ public Params setNumItems(int numItems) {
+ mNumItems = numItems;
+ return this;
+ }
+
+ /**
+ * Set whether the items are focusable.
+ */
+ public Params setItemsFocusable(boolean itemsFocusable) {
+ mItemsFocusable = itemsFocusable;
+ return this;
+ }
+
+ /**
+ * Set the position that starts selected.
+ *
+ * @param startingSelectionPosition The selected position within the adapter's data set.
+ * Pass -1 if you do not want to force a selection.
+ * @return
+ */
+ public Params setStartingSelectionPosition(int startingSelectionPosition) {
+ mStartingSelectionPosition = startingSelectionPosition;
+ return this;
+ }
+
+ /**
+ * Set the factor that determines how tall each item is in relation to the
+ * screen height.
+ */
+ public Params setItemScreenSizeFactor(double itemScreenSizeFactor) {
+ mItemScreenSizeFactor = itemScreenSizeFactor;
+ return this;
+ }
+
+ /**
+ * Override the item screen size factor for a particular item. Useful for
+ * creating lists with non-uniform item height.
+ * @param position The position in the list.
+ * @param itemScreenSizeFactor The screen size factor to use for the height.
+ */
+ public Params setPositionScreenSizeFactorOverride(
+ int position, double itemScreenSizeFactor) {
+ mOverrideItemScreenSizeFactors.put(position, itemScreenSizeFactor);
+ return this;
+ }
+
+ /**
+ * Set a position as unselectable (a.k.a a separator)
+ * @param position
+ * @return
+ */
+ public Params setPositionUnselectable(int position) {
+ mUnselectableItems.add(position);
+ return this;
+ }
+
+ /**
+ * Set positions as unselectable (a.k.a a separator)
+ */
+ public Params setPositionsUnselectable(int ...positions) {
+ for (int pos : positions) {
+ setPositionUnselectable(pos);
+ }
+ return this;
+ }
+
+ /**
+ * Include a header text view above the list.
+ * @param includeHeader
+ * @return
+ */
+ public Params includeHeaderAboveList(boolean includeHeader) {
+ mIncludeHeader = includeHeader;
+ return this;
+ }
+
+ /**
+ * Sets the stacking direction
+ * @param stackFromBottom
+ * @return
+ */
+ public Params setStackFromBottom(boolean stackFromBottom) {
+ mStackFromBottom = stackFromBottom;
+ return this;
+ }
+
+ /**
+ * Sets whether the sum of the height of the list items must be at least the
+ * height of the list view.
+ */
+ public Params setMustFillScreen(boolean fillScreen) {
+ mMustFillScreen = fillScreen;
+ return this;
+ }
+
+ /**
+ * Set the factor for the fading edge length.
+ */
+ public Params setFadingEdgeScreenSizeFactor(double fadingEdgeScreenSizeFactor) {
+ mFadingEdgeScreenSizeFactor = fadingEdgeScreenSizeFactor;
+ return this;
+ }
+
+ /**
+ * Set the number of header views to appear within the list
+ */
+ public Params setHeaderViewCount(int headerViewCount) {
+ mHeaderViewCount = headerViewCount;
+ return this;
+ }
+
+ /**
+ * Set whether the headers should be focusable.
+ * @param headerFocusable Whether the headers should be focusable (i.e
+ * created as edit texts rather than text views).
+ */
+ public Params setHeaderFocusable(boolean headerFocusable) {
+ mHeaderFocusable = headerFocusable;
+ return this;
+ }
+
+ /**
+ * Set the number of footer views to appear within the list
+ */
+ public Params setFooterViewCount(int footerViewCount) {
+ mFooterViewCount = footerViewCount;
+ return this;
+ }
+
+ /**
+ * Sets whether the {@link ListScenario} will automatically set the
+ * adapter on the list view. If this is false, the client MUST set it
+ * manually (this is useful when adding headers to the list view, which
+ * must be done before the adapter is set).
+ */
+ public Params setConnectAdapter(boolean connectAdapter) {
+ mConnectAdapter = connectAdapter;
+ return this;
+ }
+ }
+
+ /**
+ * How each scenario customizes its behavior.
+ * @param params
+ */
+ protected abstract void init(Params params);
+
+ /**
+ * Override this if you want to know when something has been selected (perhaps
+ * more importantly, that {@link android.widget.AdapterView.OnItemSelectedListener} has
+ * been triggered).
+ */
+ protected void positionSelected(int positon) {
+ }
+
+ /**
+ * Override this if you want to know that nothing is selected.
+ */
+ protected void nothingSelected() {
+ }
+
+ /**
+ * Override this if you want to know when something has been clicked (perhaps
+ * more importantly, that {@link android.widget.AdapterView.OnItemClickListener} has
+ * been triggered).
+ */
+ protected void positionClicked(int position) {
+ setClickedPosition(position);
+ }
+
+ /**
+ * Override this if you want to know when something has been long clicked (perhaps
+ * more importantly, that {@link android.widget.AdapterView.OnItemLongClickListener} has
+ * been triggered).
+ */
+ protected void positionLongClicked(int position) {
+ setLongClickedPosition(position);
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // for test stability, turn off title bar
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+
+ mScreenHeight = getWindowManager().getDefaultDisplay().getHeight();
+
+ final Params params = createParams();
+ init(params);
+
+ readAndValidateParams(params);
+
+
+ mListView = createListView();
+ mListView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.FILL_PARENT));
+ mListView.setDrawSelectorOnTop(false);
+
+ for (int i=0; i<mHeaderViewCount; i++) {
+ TextView header = mHeadersFocusable ?
+ new EditText(this) :
+ new TextView(this);
+ header.setText("Header: " + i);
+ mListView.addHeaderView(header);
+ }
+
+ for (int i=0; i<mFooterViewCount; i++) {
+ TextView header = new TextView(this);
+ header.setText("Footer: " + i);
+ mListView.addFooterView(header);
+ }
+
+ if (params.mConnectAdapter) {
+ setAdapter(mListView);
+ }
+
+ mListView.setItemsCanFocus(mItemsFocusable);
+ if (mStartingSelectionPosition >= 0) {
+ mListView.setSelection(mStartingSelectionPosition);
+ }
+ mListView.setPadding(0, 0, 0, 0);
+ mListView.setStackFromBottom(mStackFromBottom);
+ mListView.setDivider(null);
+
+ mListView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ public void onItemSelected(AdapterView parent, View v, int position, long id) {
+ positionSelected(position);
+ }
+
+ public void onNothingSelected(AdapterView parent) {
+ nothingSelected();
+ }
+ });
+
+ mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ public void onItemClick(AdapterView parent, View v, int position, long id) {
+ positionClicked(position);
+ }
+ });
+
+ // set the fading edge length porportionally to the screen
+ // height for test stability
+ if (params.mFadingEdgeScreenSizeFactor != null) {
+ mListView.setFadingEdgeLength((int) (params.mFadingEdgeScreenSizeFactor * mScreenHeight));
+ } else {
+ mListView.setFadingEdgeLength((int) ((64.0 / 480) * mScreenHeight));
+ }
+
+ if (mIncludeHeader) {
+ mLinearLayout = new LinearLayout(this);
+
+ mHeaderTextView = new TextView(this);
+ mHeaderTextView.setText("hi");
+ mHeaderTextView.setLayoutParams(new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ mLinearLayout.addView(mHeaderTextView);
+
+ mLinearLayout.setOrientation(LinearLayout.VERTICAL);
+ mLinearLayout.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.FILL_PARENT));
+ mListView.setLayoutParams((new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ 0,
+ 1f)));
+
+ mLinearLayout.addView(mListView);
+ setContentView(mLinearLayout);
+ } else {
+ mLinearLayout = new LinearLayout(this);
+ mLinearLayout.setOrientation(LinearLayout.VERTICAL);
+ mLinearLayout.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.FILL_PARENT));
+ mListView.setLayoutParams((new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ 0,
+ 1f)));
+ mLinearLayout.addView(mListView);
+ setContentView(mLinearLayout);
+ }
+ }
+
+ /**
+ * Returns the LinearLayout containing the ListView in this scenario.
+ *
+ * @return The LinearLayout in which the ListView is held.
+ */
+ protected LinearLayout getListViewContainer() {
+ return mLinearLayout;
+ }
+
+ /**
+ * Attaches a long press listener. You can find out which views were clicked by calling
+ * {@link #getLongClickedPosition()}.
+ */
+ public void enableLongPress() {
+ mListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ public boolean onItemLongClick(AdapterView parent, View v, int position, long id) {
+ positionLongClicked(position);
+ return true;
+ }
+ });
+ }
+
+ /**
+ * @return The newly created ListView widget.
+ */
+ protected ListView createListView() {
+ return new ListView(this);
+ }
+
+ /**
+ * @return The newly created Params object.
+ */
+ protected Params createParams() {
+ return new Params();
+ }
+
+ /**
+ * Sets an adapter on a ListView.
+ *
+ * @param listView The ListView to set the adapter on.
+ */
+ protected void setAdapter(ListView listView) {
+ listView.setAdapter(new MyAdapter());
+ }
+
+ /**
+ * Read in and validate all of the params passed in by the scenario.
+ * @param params
+ */
+ protected void readAndValidateParams(Params params) {
+ if (params.mMustFillScreen ) {
+ double totalFactor = 0.0;
+ for (int i = 0; i < params.mNumItems; i++) {
+ if (params.mOverrideItemScreenSizeFactors.containsKey(i)) {
+ totalFactor += params.mOverrideItemScreenSizeFactors.get(i);
+ } else {
+ totalFactor += params.mItemScreenSizeFactor;
+ }
+ }
+ if (totalFactor < 1.0) {
+ throw new IllegalArgumentException("list items must combine to be at least " +
+ "the height of the screen. this is not the case with " + params.mNumItems
+ + " items and " + params.mItemScreenSizeFactor + " screen factor and " +
+ "screen height of " + mScreenHeight);
+ }
+ }
+
+ mNumItems = params.mNumItems;
+ mItemsFocusable = params.mItemsFocusable;
+ mStartingSelectionPosition = params.mStartingSelectionPosition;
+ mItemScreenSizeFactor = params.mItemScreenSizeFactor;
+
+ mOverrideItemScreenSizeFactors.putAll(params.mOverrideItemScreenSizeFactors);
+
+ mUnselectableItems.addAll(params.mUnselectableItems);
+ mIncludeHeader = params.mIncludeHeader;
+ mStackFromBottom = params.mStackFromBottom;
+ mHeaderViewCount = params.mHeaderViewCount;
+ mHeadersFocusable = params.mHeaderFocusable;
+ mFooterViewCount = params.mFooterViewCount;
+ }
+
+ public final String getValueAtPosition(int position) {
+ return isItemAtPositionSelectable(position)
+ ?
+ "position " + position:
+ "------- " + position;
+ }
+
+ /**
+ * @return The height that will be set for a particular position.
+ */
+ public int getHeightForPosition(int position) {
+ int desiredHeight = (int) (mScreenHeight * mItemScreenSizeFactor);
+ if (mOverrideItemScreenSizeFactors.containsKey(position)) {
+ desiredHeight = (int) (mScreenHeight * mOverrideItemScreenSizeFactors.get(position));
+ }
+ return desiredHeight;
+ }
+
+
+ /**
+ * @return The contents of the header above the list.
+ * @throws IllegalArgumentException if there is no header.
+ */
+ public final String getHeaderValue() {
+ if (!mIncludeHeader) {
+ throw new IllegalArgumentException("no header above list");
+ }
+ return mHeaderTextView.getText().toString();
+ }
+
+ /**
+ * @param value What to put in the header text view
+ * @throws IllegalArgumentException if there is no header.
+ */
+ protected final void setHeaderValue(String value) {
+ if (!mIncludeHeader) {
+ throw new IllegalArgumentException("no header above list");
+ }
+ mHeaderTextView.setText(value);
+ }
+
+ /**
+ * Create a view for a list item. Override this to create a custom view beyond
+ * the simple focusable / unfocusable text view.
+ * @param position The position.
+ * @param parent The parent
+ * @param desiredHeight The height the view should be to respect the desired item
+ * to screen height ratio.
+ * @return a view for the list.
+ */
+ protected View createView(int position, ViewGroup parent, int desiredHeight) {
+ return ListItemFactory.text(position, parent.getContext(), getValueAtPosition(position),
+ desiredHeight);
+ }
+
+ /**
+ * Convert a non-null view.
+ */
+ public View convertView(int position, View convertView, ViewGroup parent) {
+ return ListItemFactory.convertText(convertView, getValueAtPosition(position), position);
+ }
+
+ public void setClickedPosition(int clickedPosition) {
+ mClickedPosition = clickedPosition;
+ }
+
+ public int getClickedPosition() {
+ return mClickedPosition;
+ }
+
+ public void setLongClickedPosition(int longClickedPosition) {
+ mLongClickedPosition = longClickedPosition;
+ }
+
+ public int getLongClickedPosition() {
+ return mLongClickedPosition;
+ }
+
+ /**
+ * Have a child of the list view call {@link View#requestRectangleOnScreen(android.graphics.Rect)}.
+ * @param childIndex The index into the viewgroup children (i.e the children that are
+ * currently visible).
+ * @param rect The rectangle, in the child's coordinates.
+ */
+ public void requestRectangleOnScreen(int childIndex, final Rect rect) {
+ final View child = getListView().getChildAt(childIndex);
+
+ child.post(new Runnable() {
+ public void run() {
+ child.requestRectangleOnScreen(rect);
+ }
+ });
+ }
+
+ /**
+ * Return an item type for the specified position in the adapter. Override if your
+ * adapter creates more than one type.
+ */
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ /**
+ * Return an the number of types created by the adapter. Override if your
+ * adapter creates more than one type.
+ */
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ /**
+ * @return The number of times convertView failed
+ */
+ public int getConvertMisses() {
+ return mConvertMisses;
+ }
+
+ private class MyAdapter extends BaseAdapter {
+
+ public int getCount() {
+ return mNumItems;
+ }
+
+ public Object getItem(int position) {
+ return getValueAtPosition(position);
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return mUnselectableItems.isEmpty();
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return isItemAtPositionSelectable(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View result = null;
+ if (position >= mNumItems || position < 0) {
+ throw new IllegalStateException("position out of range for adapter!");
+ }
+
+ if (convertView != null) {
+ result = convertView(position, convertView, parent);
+ if (result == null) {
+ mConvertMisses++;
+ }
+ }
+
+ if (result == null) {
+ int desiredHeight = getHeightForPosition(position);
+ result = createView(position, parent, desiredHeight);
+ }
+ return result;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return ListScenario.this.getItemViewType(position);
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return ListScenario.this.getViewTypeCount();
+ }
+
+ }
+}