blob: 6232e9d8a690c6c8d863cd426f80d3295c68a552 [file] [log] [blame]
/*
* Copyright (C) 2008 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.widget.cts;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.database.DataSetObservable;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.test.ViewAsserts;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.GridLayoutAnimationController.AnimationParameters;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ListAdapter;
import androidx.test.InstrumentationRegistry;
import androidx.test.annotation.UiThreadTest;
import androidx.test.filters.LargeTest;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.compatibility.common.util.CtsKeyEventUtil;
import com.android.compatibility.common.util.PollingCheck;
import com.android.compatibility.common.util.WidgetTestUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* Test {@link GridView}.
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class GridViewTest {
private Instrumentation mInstrumentation;
private Activity mActivity;
private GridView mGridView;
@Rule
public ActivityTestRule<GridViewCtsActivity> mActivityRule =
new ActivityTestRule<>(GridViewCtsActivity.class);
@Before
public void setup() {
mInstrumentation = InstrumentationRegistry.getInstrumentation();
mActivity = mActivityRule.getActivity();
mGridView = (GridView) mActivity.findViewById(R.id.gridview);
PollingCheck.waitFor(mActivity::hasWindowFocus);
}
@Test
public void testConstructor() {
new GridView(mActivity);
new GridView(mActivity, null);
new GridView(mActivity, null, android.R.attr.gridViewStyle);
new GridView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_GridView);
new GridView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_Light_GridView);
new GridView(mActivity, null, 0, android.R.style.Widget_Material_GridView);
new GridView(mActivity, null, 0, android.R.style.Widget_Material_Light_GridView);
XmlPullParser parser = mActivity.getResources().getXml(R.layout.gridview_layout);
AttributeSet attrs = Xml.asAttributeSet(parser);
new GridView(mActivity, attrs);
new GridView(mActivity, attrs, 0);
}
@Test(expected=NullPointerException.class)
public void testConstructorNullContext1() {
new GridView(null);
}
@Test(expected=NullPointerException.class)
public void testConstructorNullContext2() {
new GridView(null, null);
}
@Test(expected=NullPointerException.class)
public void testConstructorNullContext3() {
new GridView(null, null, 0);
}
@UiThreadTest
@Test
public void testAccessAdapter() {
// set Adapter
ImageAdapter adapter = new ImageAdapter(mActivity);
mGridView.setAdapter(adapter);
assertSame(adapter, mGridView.getAdapter());
mGridView.setAdapter(null);
assertNull(mGridView.getAdapter());
}
@UiThreadTest
@Test
public void testSetSelection() {
mGridView.setSelection(0);
assertEquals(0, mGridView.getSelectedItemPosition());
mGridView.setSelection(-1);
assertEquals(-1, mGridView.getSelectedItemPosition());
mGridView.setSelection(mGridView.getCount());
assertEquals(mGridView.getCount(), mGridView.getSelectedItemPosition());
}
@Test
public void testPressKey() throws Throwable {
final int NUM_COLUMNS = 3;
GridView.OnItemClickListener mockItemClickListener =
mock(GridView.OnItemClickListener.class);
mGridView.setOnItemClickListener(mockItemClickListener);
// this test case can not be ran in UI thread.
mActivityRule.runOnUiThread(() -> {
mGridView.setAdapter(new ImageAdapter(mActivity));
mGridView.setNumColumns(NUM_COLUMNS);
mGridView.invalidate();
mGridView.requestLayout();
mGridView.requestFocus();
});
mInstrumentation.waitForIdleSync();
assertEquals(0, mGridView.getSelectedItemPosition());
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
mInstrumentation.sendKeySync(event);
assertEquals(1, mGridView.getSelectedItemPosition());
event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
mInstrumentation.sendKeySync(event);
assertEquals(0, mGridView.getSelectedItemPosition());
assertEquals(0, mGridView.getSelectedItemPosition());
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_RIGHT);
assertEquals(1, mGridView.getSelectedItemPosition());
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_LEFT);
assertEquals(0, mGridView.getSelectedItemPosition());
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_TAB);
assertEquals(1, mGridView.getSelectedItemPosition());
event = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_TAB, 0,
KeyEvent.META_SHIFT_LEFT_ON);
mInstrumentation.sendKeySync(event);
event = new KeyEvent(0, 0, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_TAB, 0,
KeyEvent.META_SHIFT_LEFT_ON);
mInstrumentation.sendKeySync(event);
assertEquals(0, mGridView.getSelectedItemPosition());
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_DOWN);
assertEquals(NUM_COLUMNS, mGridView.getSelectedItemPosition());
verify(mockItemClickListener, never()).onItemClick(any(AdapterView.class), any(View.class),
anyInt(), anyLong());
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_DPAD_CENTER);
verify(mockItemClickListener, times(1)).onItemClick(eq(mGridView), any(View.class),
eq(NUM_COLUMNS), eq((long) NUM_COLUMNS));
reset(mockItemClickListener);
mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_ENTER);
verify(mockItemClickListener, times(1)).onItemClick(eq(mGridView), any(View.class),
eq(NUM_COLUMNS), eq((long) NUM_COLUMNS));
}
@Test
public void testSetGravity() throws Throwable {
final int NUM_COLUMNS = 1;
// this test case can not be ran in UI thread.
mActivityRule.runOnUiThread(() -> {
mGridView.setAdapter(new ImageAdapter(mActivity));
mGridView.setNumColumns(NUM_COLUMNS);
mGridView.setHorizontalSpacing(0);
mGridView.setVerticalSpacing(0);
});
mInstrumentation.waitForIdleSync();
mActivityRule.runOnUiThread(() -> {
mGridView.setGravity(Gravity.CENTER_HORIZONTAL);
mGridView.invalidate();
mGridView.requestLayout();
});
mInstrumentation.waitForIdleSync();
assertEquals(Gravity.CENTER_HORIZONTAL, mGridView.getGravity());
ViewAsserts.assertHorizontalCenterAligned(mGridView, mGridView.getChildAt(0));
mActivityRule.runOnUiThread(() -> {
mGridView.setGravity(Gravity.LEFT);
mGridView.invalidate();
mGridView.requestLayout();
});
mInstrumentation.waitForIdleSync();
assertEquals(Gravity.LEFT, mGridView.getGravity());
ViewAsserts.assertLeftAligned(mGridView, mGridView.getChildAt(0),
mGridView.getListPaddingLeft());
mActivityRule.runOnUiThread(() -> {
mGridView.setGravity(Gravity.RIGHT);
mGridView.invalidate();
mGridView.requestLayout();
});
mInstrumentation.waitForIdleSync();
assertEquals(Gravity.RIGHT, mGridView.getGravity());
ViewAsserts.assertRightAligned(mGridView, mGridView.getChildAt(0),
mGridView.getListPaddingRight());
}
@Test
public void testAccessHorizontalSpacing() throws Throwable {
verifyAccessHorizontalSpacing(View.LAYOUT_DIRECTION_LTR);
}
@Test
public void testAccessHorizontalSpacingRTL() throws Throwable {
verifyAccessHorizontalSpacing(View.LAYOUT_DIRECTION_RTL);
}
private void verifyAccessHorizontalSpacing(final int layoutDir) throws Throwable {
mActivityRule.runOnUiThread(() -> mGridView.setLayoutDirection(layoutDir));
mGridView.setStretchMode(GridView.NO_STRETCH);
// Number of columns should be big enough, otherwise the
// horizontal spacing cannot be correctly verified.
mGridView.setNumColumns(28);
mActivityRule.runOnUiThread(() -> {
mGridView.setAdapter(new MockGridViewAdapter(3));
mGridView.setHorizontalSpacing(0);
});
mInstrumentation.waitForIdleSync();
assertEquals(0, mGridView.getRequestedHorizontalSpacing());
assertEquals(0, mGridView.getHorizontalSpacing());
View child0 = mGridView.getChildAt(0);
View child1 = mGridView.getChildAt(1);
if (layoutDir == View.LAYOUT_DIRECTION_LTR) {
assertEquals(0, child1.getLeft() - child0.getRight());
} else {
assertEquals(0, child0.getLeft() - child1.getRight());
}
mActivityRule.runOnUiThread(() -> mGridView.setHorizontalSpacing(5));
mInstrumentation.waitForIdleSync();
assertEquals(5, mGridView.getRequestedHorizontalSpacing());
assertEquals(5, mGridView.getHorizontalSpacing());
child0 = mGridView.getChildAt(0);
child1 = mGridView.getChildAt(1);
if (layoutDir == View.LAYOUT_DIRECTION_LTR) {
assertEquals(5, child1.getLeft() - child0.getRight());
} else {
assertEquals(5, child0.getLeft() - child1.getRight());
}
}
@Test
public void testAccessVerticalSpacing() throws Throwable {
mActivityRule.runOnUiThread(() -> {
mGridView.setAdapter(new MockGridViewAdapter(3));
mGridView.setVerticalSpacing(0);
});
mInstrumentation.waitForIdleSync();
assertEquals(0, mGridView.getVerticalSpacing());
View child0 = mGridView.getChildAt(0);
View child1 = mGridView.getChildAt(1);
assertEquals(0, child1.getTop() - child0.getBottom());
mActivityRule.runOnUiThread(() -> mGridView.setVerticalSpacing(5));
mInstrumentation.waitForIdleSync();
assertEquals(5, mGridView.getVerticalSpacing());
child0 = mGridView.getChildAt(0);
child1 = mGridView.getChildAt(1);
assertEquals(5, child1.getTop() - child0.getBottom());
}
@Test
public void testAccessStretchMode() throws Throwable {
View child;
final int NUM_COLUMNS = 8;
// this test case can not be ran in UI thread.
mActivityRule.runOnUiThread(() -> {
mGridView.setAdapter(new ImageAdapter(mActivity));
mGridView.setColumnWidth(10);
mGridView.setNumColumns(NUM_COLUMNS);
mGridView.setHorizontalSpacing(0);
mGridView.setVerticalSpacing(0);
mGridView.invalidate();
mGridView.requestLayout();
});
mInstrumentation.waitForIdleSync();
int[][] childRight = new int[3][3];
int STRETCH_SPACING = 0;
int STRETCH_COLUMN_WIDTH = 1;
int STRETCH_SPACING_UNIFORM = 2;
int INDEX_RIGHTMOST = 0;
int INDEX_0 = 1;
int INDEX_1 = 2;
mActivityRule.runOnUiThread(() -> {
mGridView.setColumnWidth(15);
mGridView.setStretchMode(GridView.STRETCH_SPACING);
mGridView.invalidate();
mGridView.requestLayout();
});
mInstrumentation.waitForIdleSync();
assertEquals(GridView.STRETCH_SPACING, mGridView.getStretchMode());
child = mGridView.getChildAt(NUM_COLUMNS - 1); // get the rightmost view at the first line.
childRight[STRETCH_SPACING][INDEX_RIGHTMOST] = child.getRight();
child = mGridView.getChildAt(0);
childRight[STRETCH_SPACING][INDEX_0] = child.getRight();
child = mGridView.getChildAt(1);
childRight[STRETCH_SPACING][INDEX_1] = child.getRight();
mActivityRule.runOnUiThread(() -> {
mGridView.setColumnWidth(15);
mGridView.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
mGridView.invalidate();
mGridView.requestLayout();
});
mInstrumentation.waitForIdleSync();
assertEquals(GridView.STRETCH_COLUMN_WIDTH, mGridView.getStretchMode());
child = mGridView.getChildAt(NUM_COLUMNS - 1); // get the rightmost view at the first line.
childRight[STRETCH_COLUMN_WIDTH][INDEX_RIGHTMOST] = child.getRight();
child = mGridView.getChildAt(0);
childRight[STRETCH_COLUMN_WIDTH][INDEX_0] = child.getRight();
child = mGridView.getChildAt(1);
childRight[STRETCH_COLUMN_WIDTH][INDEX_1] = child.getRight();
mActivityRule.runOnUiThread(() -> {
mGridView.setColumnWidth(15);
mGridView.setStretchMode(GridView.STRETCH_SPACING_UNIFORM);
mGridView.invalidate();
mGridView.requestLayout();
});
mInstrumentation.waitForIdleSync();
assertEquals(GridView.STRETCH_SPACING_UNIFORM, mGridView.getStretchMode());
child = mGridView.getChildAt(NUM_COLUMNS - 1); // get the rightmost view at the first line.
childRight[STRETCH_SPACING_UNIFORM][INDEX_RIGHTMOST] = child.getRight();
child = mGridView.getChildAt(0);
childRight[STRETCH_SPACING_UNIFORM][INDEX_0] = child.getRight();
child = mGridView.getChildAt(1);
childRight[STRETCH_SPACING_UNIFORM][INDEX_1] = child.getRight();
assertTrue(childRight[STRETCH_SPACING][INDEX_RIGHTMOST]
> childRight[STRETCH_COLUMN_WIDTH][INDEX_RIGHTMOST]);
assertTrue(childRight[STRETCH_SPACING][INDEX_RIGHTMOST]
> childRight[STRETCH_SPACING_UNIFORM][INDEX_RIGHTMOST]);
assertTrue(childRight[STRETCH_SPACING][INDEX_0]
== childRight[STRETCH_COLUMN_WIDTH][INDEX_0]);
assertTrue(childRight[STRETCH_SPACING][INDEX_0]
< childRight[STRETCH_SPACING_UNIFORM][INDEX_0]);
assertTrue(childRight[STRETCH_SPACING][INDEX_1]
> childRight[STRETCH_COLUMN_WIDTH][INDEX_1]);
assertTrue(childRight[STRETCH_SPACING][INDEX_1]
< childRight[STRETCH_SPACING_UNIFORM][INDEX_1]);
}
@Test
public void testSetNumColumns() throws Throwable {
// this test case can not be ran in UI thread.
mActivityRule.runOnUiThread(() -> {
mGridView.setAdapter(new MockGridViewAdapter(10));
mGridView.setHorizontalSpacing(0);
mGridView.setVerticalSpacing(0);
mGridView.setNumColumns(10);
});
mInstrumentation.waitForIdleSync();
View child0 = mGridView.getChildAt(0);
View child9 = mGridView.getChildAt(9);
assertEquals(child0.getBottom(), child9.getBottom());
mActivityRule.runOnUiThread(() -> mGridView.setNumColumns(9));
mInstrumentation.waitForIdleSync();
child0 = mGridView.getChildAt(0);
child9 = mGridView.getChildAt(9);
assertEquals(child0.getBottom(), child9.getTop());
assertEquals(child0.getLeft(), child9.getLeft());
mActivityRule.runOnUiThread(() -> mGridView.setNumColumns(1));
mInstrumentation.waitForIdleSync();
for (int i = 0; i < mGridView.getChildCount(); i++) {
View child = mGridView.getChildAt(i);
assertEquals(0, child.getLeft() - mGridView.getListPaddingLeft());
}
}
@Test
public void testDefaultNumColumns() {
final GridView gridView = new GridView(mActivity);
assertEquals(gridView.getNumColumns(), GridView.AUTO_FIT);
}
@Test
public void testGetNumColumns() throws Throwable {
mActivityRule.runOnUiThread(() -> {
mGridView.setAdapter(new MockGridViewAdapter(10));
mGridView.setNumColumns(10);
});
mInstrumentation.waitForIdleSync();
assertEquals(mGridView.getNumColumns(), 10);
mActivityRule.runOnUiThread(() -> mGridView.setNumColumns(1));
mInstrumentation.waitForIdleSync();
assertEquals(mGridView.getNumColumns(), 1);
mActivityRule.runOnUiThread(() -> mGridView.setNumColumns(0));
mInstrumentation.waitForIdleSync();
//although setNumColumns(0) was called, the number of columns should be 1
assertEquals(mGridView.getNumColumns(), 1);
}
@Test
public void testAttachLayoutAnimationParameters() {
MockGridView mockGridView = new MockGridView(mActivity);
ViewGroup.LayoutParams p = new ViewGroup.LayoutParams(320, 480);
mockGridView.attachLayoutAnimationParameters(null, p, 1, 2);
AnimationParameters animationParams = (AnimationParameters) p.layoutAnimationParameters;
assertEquals(1, animationParams.index);
assertEquals(2, animationParams.count);
}
@Test
public void testLayoutChildren() {
MockGridView mockGridView = new MockGridView(mActivity);
mockGridView.layoutChildren();
}
@UiThreadTest
@Test
public void testOnFocusChanged() {
final MockGridView mockGridView = new MockGridView(mActivity);
assertFalse(mockGridView.hasCalledOnFocusChanged());
mockGridView.setAdapter(new MockGridViewAdapter(10));
mockGridView.setFocusable(true);
mockGridView.requestFocus();
assertTrue(mockGridView.hasCalledOnFocusChanged());
mockGridView.reset();
assertFalse(mockGridView.hasCalledOnFocusChanged());
mockGridView.clearFocus();
assertTrue(mockGridView.hasCalledOnFocusChanged());
}
@Test
public void testAccessColumnWidth() throws Throwable {
mActivityRule.runOnUiThread(() -> {
mGridView.setAdapter(new MockGridViewAdapter(10));
mGridView.setNumColumns(GridView.AUTO_FIT);
mGridView.setHorizontalSpacing(0);
mGridView.setVerticalSpacing(0);
mGridView.setColumnWidth(0);
});
mInstrumentation.waitForIdleSync();
// Verify whether column number equals 2.
assertEquals(0, mGridView.getRequestedColumnWidth());
assertEquals(mGridView.getWidth() / 2, mGridView.getColumnWidth());
View child0 = mGridView.getChildAt(0);
View child1 = mGridView.getChildAt(1);
View child2 = mGridView.getChildAt(2);
assertEquals(child0.getBottom(), child1.getBottom());
assertEquals(child0.getLeft(), child2.getLeft());
mActivityRule.runOnUiThread(() -> {
mGridView.setNumColumns(GridView.AUTO_FIT);
mGridView.setColumnWidth(Integer.MAX_VALUE);
});
mInstrumentation.waitForIdleSync();
assertEquals(Integer.MAX_VALUE, mGridView.getRequestedColumnWidth());
assertEquals(mGridView.getWidth(), mGridView.getColumnWidth());
child0 = mGridView.getChildAt(0);
child1 = mGridView.getChildAt(1);
assertEquals(child0.getBottom(), child1.getTop());
assertEquals(child0.getLeft(), child1.getLeft());
}
@MediumTest
@Test
public void testFullyDetachUnusedViewOnScroll() throws Throwable {
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, () -> {
mGridView.setAdapter(new DummyAdapter(1000, theView));
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
assertEquals("test sanity", 0, theView.mOnDetachCount);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, () -> {
mGridView.scrollListBy(mGridView.getHeight() * 2);
});
assertNull("test sanity, unused view should be removed", theView.getParent());
assertEquals("unused view should be detached", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, () -> {
mGridView.scrollListBy(-mGridView.getHeight() * 2);
// listview limits scroll to 1 page which is why we call it twice here.
mGridView.scrollListBy(-mGridView.getHeight() * 2);
});
assertNotNull("test sanity, view should be re-added", theView.getParent());
assertEquals("view should receive another attach call", 2, theView.mOnAttachCount);
assertEquals("view should not receive a detach call", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
}
@MediumTest
@Test
public void testFullyDetachUnusedViewOnReLayout() throws Throwable {
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, () -> {
mGridView.setAdapter(new DummyAdapter(1000, theView));
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
assertEquals("test sanity", 0, theView.mOnDetachCount);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, () -> {
mGridView.setSelection(800);
});
assertNull("test sanity, unused view should be removed", theView.getParent());
assertEquals("unused view should be detached", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, () -> {
mGridView.setSelection(0);
});
assertNotNull("test sanity, view should be re-added", theView.getParent());
assertEquals("view should receive another attach call", 2, theView.mOnAttachCount);
assertEquals("view should not receive a detach call", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
}
@MediumTest
@Test
public void testFullyDetachUnusedViewOnScrollForFocus() throws Throwable {
final AttachDetachAwareView theView = new AttachDetachAwareView(mActivity);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, () -> {
mGridView.setAdapter(new DummyAdapter(1000, theView));
mGridView.requestFocus();
});
assertEquals("test sanity", 1, theView.mOnAttachCount);
assertEquals("test sanity", 0, theView.mOnDetachCount);
while(theView.getParent() != null) {
assertEquals("the view should NOT be detached", 0, theView.mOnDetachCount);
CtsKeyEventUtil.sendKeys(mInstrumentation, mGridView, KeyEvent.KEYCODE_DPAD_DOWN);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, null);
}
assertEquals("the view should be detached", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
while(theView.getParent() == null) {
CtsKeyEventUtil.sendKeys(mInstrumentation, mGridView, KeyEvent.KEYCODE_DPAD_UP);
WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mGridView, null);
}
assertEquals("the view should be re-attached", 2, theView.mOnAttachCount);
assertEquals("the view should not receive another detach", 1, theView.mOnDetachCount);
assertFalse(theView.isTemporarilyDetached());
}
@LargeTest
@Test
public void testSmoothScrollByOffset() throws Throwable {
final int itemCount = 300;
mActivityRule.runOnUiThread(() -> {
mGridView.setAdapter(new MockGridViewAdapter(itemCount));
mGridView.setNumColumns(GridView.AUTO_FIT);
mGridView.setHorizontalSpacing(0);
mGridView.setVerticalSpacing(0);
mGridView.setColumnWidth(Integer.MAX_VALUE);
});
mInstrumentation.waitForIdleSync();
assertEquals(0, mGridView.getFirstVisiblePosition());
// Register a scroll listener on our GridView. The listener will notify our latch
// when the "target" item comes into view. If that never happens, the latch will
// time out and fail the test.
final CountDownLatch latch = new CountDownLatch(1);
final int positionToScrollTo = itemCount - 10;
mGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
int totalItemCount) {
if ((positionToScrollTo >= firstVisibleItem) &&
(positionToScrollTo <= (firstVisibleItem + visibleItemCount))) {
latch.countDown();
}
}
});
int offset = positionToScrollTo - mGridView.getLastVisiblePosition();
mActivityRule.runOnUiThread(() -> mGridView.smoothScrollByOffset(offset));
boolean result = false;
try {
result = latch.await(20, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// ignore
}
assertTrue("Timed out while waiting for the target view to be scrolled into view", result);
}
private static class MockGridView extends GridView {
private boolean mCalledOnFocusChanged = false;
public boolean hasCalledOnFocusChanged() {
return mCalledOnFocusChanged;
}
public void reset() {
mCalledOnFocusChanged = false;
}
public MockGridView(Context context) {
super(context);
}
public MockGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MockGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void attachLayoutAnimationParameters(View child,
ViewGroup.LayoutParams params, int index, int count) {
super.attachLayoutAnimationParameters(child, params, index, count);
}
@Override
protected void layoutChildren() {
super.layoutChildren();
}
@Override
protected void onFocusChanged(boolean gainFocus, int direction,
Rect previouslyFocusedRect) {
mCalledOnFocusChanged = true;
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
}
}
private class MockGridViewAdapter implements ListAdapter, Filterable {
private final int mCount;
MockGridViewAdapter(int count) {
mCount = count;
}
public boolean areAllItemsEnabled() {
return true;
}
public boolean isEnabled(int position) {
return true;
}
public void registerDataSetObserver(DataSetObserver observer) {
}
public void unregisterDataSetObserver(DataSetObserver observer) {
}
public int getCount() {
return mCount;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public boolean hasStableIds() {
return false;
}
public View getView(int position, View convertView, ViewGroup parent) {
if ((convertView != null) && (convertView instanceof ImageView)) {
((ImageView) convertView).setImageResource(R.drawable.size_48x48);
return convertView;
}
ImageView newView = new ImageView(mActivity);
AbsListView.LayoutParams params = new AbsListView.LayoutParams(
AbsListView.LayoutParams.WRAP_CONTENT,
AbsListView.LayoutParams.WRAP_CONTENT);
newView.setLayoutParams(params);
newView.setImageResource(R.drawable.size_48x48);
return newView;
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return false;
}
public Filter getFilter() {
return new FilterTest();
}
}
private static class FilterTest extends Filter {
@Override
protected Filter.FilterResults performFiltering(CharSequence constraint) {
return null;
}
@Override
protected void publishResults(CharSequence constraint, Filter.FilterResults results) {
}
}
private class ImageAdapter implements ListAdapter {
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
int layoutSize = (int)(50 * mContext.getResources().getDisplayMetrics().density);
imageView.setLayoutParams(new GridView.LayoutParams(layoutSize, layoutSize));
imageView.setAdjustViewBounds(false);
imageView.setScaleType(ImageView.ScaleType.CENTER);
imageView.setPadding(0, 0, 0, 0);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
private Context mContext;
private Integer[] mThumbIds = {
R.drawable.failed, R.drawable.pass,
R.drawable.animated, R.drawable.black,
R.drawable.blue, R.drawable.red,
R.drawable.animated, R.drawable.black,
R.drawable.blue, R.drawable.failed,
R.drawable.pass, R.drawable.red,
};
private final DataSetObservable mDataSetObservable = new DataSetObservable();
public boolean hasStableIds() {
return false;
}
public void registerDataSetObserver(DataSetObserver observer) {
mDataSetObservable.registerObserver(observer);
}
public void unregisterDataSetObserver(DataSetObserver observer) {
mDataSetObservable.unregisterObserver(observer);
}
public boolean areAllItemsEnabled() {
return true;
}
public boolean isEnabled(int position) {
return true;
}
public int getItemViewType(int position) {
return 0;
}
public int getViewTypeCount() {
return 1;
}
public boolean isEmpty() {
return getCount() == 0;
}
}
}