Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff28490f authored by Gilles Debunne's avatar Gilles Debunne Committed by Android (Google) Code Review
Browse files

Merge "ExpandableListView tests moved to CTS."

parents 6c00fbe3 50f79c95
Loading
Loading
Loading
Loading
+0 −386
Original line number Diff line number Diff line
/*
 * 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 android.util;

import java.util.ArrayList;
import java.util.List;

import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ListView;
import android.widget.TextView;

/**
 * Utility base class for creating various Expandable List scenarios.
 * <p>
 * WARNING: A lot of the features are mixed between ListView's expected position
 * (flat list position) and an ExpandableListView's expected position.  You must add/change
 * features as you need them.
 * 
 * @see ListScenario
 */
public abstract class ExpandableListScenario extends ListScenario {
    protected ExpandableListAdapter mAdapter; 
    protected List<MyGroup> mGroups;
    
    @Override
    protected ListView createListView() {
        return new ExpandableListView(this);
    }

    @Override
    protected Params createParams() {
        return new ExpandableParams();
    }

    @Override
    protected void setAdapter(ListView listView) {
        ((ExpandableListView) listView).setAdapter(mAdapter = createAdapter());
    }
    
    protected ExpandableListAdapter createAdapter() {
        return new MyAdapter();
    }
    
    @Override
    protected void readAndValidateParams(Params params) {
        ExpandableParams expandableParams = (ExpandableParams) params;
        
        int[] numChildren = expandableParams.mNumChildren;
        
        mGroups = new ArrayList<MyGroup>(numChildren.length);
        for (int i = 0; i < numChildren.length; i++) {
            mGroups.add(new MyGroup(numChildren[i]));
        }
        
        expandableParams.superSetNumItems();
        
        super.readAndValidateParams(params);
    }

    /**
     * Get the ExpandableListView widget.
     * @return The main widget.
     */
    public ExpandableListView getExpandableListView() {
        return (ExpandableListView) super.getListView();
    }

    public static class ExpandableParams extends Params {
        private int[] mNumChildren;
        
        /**
         * Sets the number of children per group.
         *  
         * @param numChildrenPerGroup The number of children per group.
         */
        public ExpandableParams setNumChildren(int[] numChildren) {
            mNumChildren = numChildren;
            return this;
        }

        /**
         * Sets the number of items on the superclass based on the number of
         * groups and children per group.
         */
        private ExpandableParams superSetNumItems() {
            int numItems = 0;
            
            if (mNumChildren != null) {
                for (int i = mNumChildren.length - 1; i >= 0; i--) {
                    numItems += mNumChildren[i];
                }
            }
            
            super.setNumItems(numItems);
            
            return this;
        }
        
        @Override
        public Params setNumItems(int numItems) {
            throw new IllegalStateException("Use setNumGroups and setNumChildren instead.");
        }

        @Override
        public ExpandableParams setFadingEdgeScreenSizeFactor(double fadingEdgeScreenSizeFactor) {
            return (ExpandableParams) super.setFadingEdgeScreenSizeFactor(fadingEdgeScreenSizeFactor);
        }

        @Override
        public ExpandableParams setItemScreenSizeFactor(double itemScreenSizeFactor) {
            return (ExpandableParams) super.setItemScreenSizeFactor(itemScreenSizeFactor);
        }

        @Override
        public ExpandableParams setItemsFocusable(boolean itemsFocusable) {
            return (ExpandableParams) super.setItemsFocusable(itemsFocusable);
        }

        @Override
        public ExpandableParams setMustFillScreen(boolean fillScreen) {
            return (ExpandableParams) super.setMustFillScreen(fillScreen);
        }

        @Override
        public ExpandableParams setPositionScreenSizeFactorOverride(int position, double itemScreenSizeFactor) {
            return (ExpandableParams) super.setPositionScreenSizeFactorOverride(position, itemScreenSizeFactor);
        }

        @Override
        public ExpandableParams setPositionUnselectable(int position) {
            return (ExpandableParams) super.setPositionUnselectable(position);
        }

        @Override
        public ExpandableParams setStackFromBottom(boolean stackFromBottom) {
            return (ExpandableParams) super.setStackFromBottom(stackFromBottom);
        }

        @Override
        public ExpandableParams setStartingSelectionPosition(int startingSelectionPosition) {
            return (ExpandableParams) super.setStartingSelectionPosition(startingSelectionPosition);
        }

        @Override
        public ExpandableParams setConnectAdapter(boolean connectAdapter) {
            return (ExpandableParams) super.setConnectAdapter(connectAdapter);
        }
    }

    /**
     * Gets a string for the value of some item.
     * @param packedPosition The position of the item.
     * @return The string.
     */
    public final String getValueAtPosition(long packedPosition) {
        final int type = ExpandableListView.getPackedPositionType(packedPosition);
        
        if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
            return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
                    .children.get(ExpandableListView.getPackedPositionChild(packedPosition))
                    .name;
        } else if (type == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
            return mGroups.get(ExpandableListView.getPackedPositionGroup(packedPosition))
                    .name;
        } else {
            throw new IllegalStateException("packedPosition is not a valid position.");
        }
    }

    /**
     * Whether a particular position is out of bounds.
     * 
     * @param packedPosition The packed position.
     * @return Whether it's out of bounds.
     */
    private boolean isOutOfBounds(long packedPosition) {
        final int type = ExpandableListView.getPackedPositionType(packedPosition);
        
        if (type == ExpandableListView.PACKED_POSITION_TYPE_NULL) {
            throw new IllegalStateException("packedPosition is not a valid position.");
        }

        final int group = ExpandableListView.getPackedPositionGroup(packedPosition); 
        if (group >= mGroups.size() || group < 0) {
            return true;
        }
        
        if (type == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {
            final int child = ExpandableListView.getPackedPositionChild(packedPosition); 
            if (child >= mGroups.get(group).children.size() || child < 0) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Gets a view for the packed position, possibly reusing the convertView.
     * 
     * @param packedPosition The position to get a view for.
     * @param convertView Optional view to convert.
     * @param parent The future parent.
     * @return A view.
     */
    private View getView(long packedPosition, View convertView, ViewGroup parent) {
        if (isOutOfBounds(packedPosition)) {
            throw new IllegalStateException("position out of range for adapter!");
        }
        
        final ExpandableListView elv = getExpandableListView();
        final int flPos = elv.getFlatListPosition(packedPosition); 
        
        if (convertView != null) {
            ((TextView) convertView).setText(getValueAtPosition(packedPosition));
            convertView.setId(flPos);
            return convertView;
        }

        int desiredHeight = getHeightForPosition(flPos);
        return createView(packedPosition, flPos, parent, desiredHeight);
    }
    
    /**
     * Create a view for a group or child position.
     * 
     * @param packedPosition The packed position (has type, group pos, and optionally child pos).
     * @param flPos The flat list position (the position that the ListView goes by).
     * @param parent The parent view.
     * @param desiredHeight The desired height.
     * @return A view.
     */
    protected View createView(long packedPosition, int flPos, ViewGroup parent, int desiredHeight) {
        TextView result = new TextView(parent.getContext());
        result.setHeight(desiredHeight);
        result.setText(getValueAtPosition(packedPosition));
        final ViewGroup.LayoutParams lp = new AbsListView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        result.setLayoutParams(lp);
        result.setGravity(Gravity.CENTER_VERTICAL);
        result.setPadding(36, 0, 0, 0);
        result.setId(flPos);
        return result;
    }
    
    /**
     * Returns a group index containing either the number of children or at
     * least one child.
     * 
     * @param numChildren The group must have this amount, or -1 if using
     *            atLeastOneChild.
     * @param atLeastOneChild The group must have at least one child, or false
     *            if using numChildren.
     * @return A group index with the requirements.
     */
    public int findGroupWithNumChildren(int numChildren, boolean atLeastOneChild) {
        final ExpandableListAdapter adapter = mAdapter;
        
        for (int i = adapter.getGroupCount() - 1; i >= 0; i--) {
            final int curNumChildren = adapter.getChildrenCount(i);
            
            if (numChildren == curNumChildren || atLeastOneChild && curNumChildren > 0) {
                return i;
            }
        }
        
        return -1;
    }
    
    public List<MyGroup> getGroups() {
        return mGroups;
    }
    
    public ExpandableListAdapter getAdapter() {
        return mAdapter;
    }

    /**
     * Simple expandable list adapter.
     */
    protected class MyAdapter extends BaseExpandableListAdapter {
        public Object getChild(int groupPosition, int childPosition) {
            return getValueAtPosition(ExpandableListView.getPackedPositionForChild(groupPosition,
                    childPosition));
        }

        public long getChildId(int groupPosition, int childPosition) {
            return mGroups.get(groupPosition).children.get(childPosition).id;
        }

        public int getChildrenCount(int groupPosition) {
            return mGroups.get(groupPosition).children.size();
        }

        public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
                View convertView, ViewGroup parent) {
            return getView(ExpandableListView.getPackedPositionForChild(groupPosition,
                    childPosition), convertView, parent);
        }

        public Object getGroup(int groupPosition) {
            return getValueAtPosition(ExpandableListView.getPackedPositionForGroup(groupPosition));
        }

        public int getGroupCount() {
            return mGroups.size();
        }

        public long getGroupId(int groupPosition) {
            return mGroups.get(groupPosition).id;
        }

        public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
                ViewGroup parent) {
            return getView(ExpandableListView.getPackedPositionForGroup(groupPosition),
                    convertView, parent);
        }

        public boolean isChildSelectable(int groupPosition, int childPosition) {
            return true;
        }

        public boolean hasStableIds() {
            return true;
        }
        
    }

    public static class MyGroup {
        private static long mNextId = 1000;
        
        String name;
        long id = mNextId++;
        List<MyChild> children;
        
        public MyGroup(int numChildren) {
            name = "Group " + id;
            children = new ArrayList<MyChild>(numChildren);
            for (int i = 0; i < numChildren; i++) {
                children.add(new MyChild());
            }
        }
    }
    
    public static class MyChild {
        private static long mNextId = 2000;
        
        String name;
        long id = mNextId++;
        
        public MyChild() {
            name = "Child " + id;
        }
    }
    
    @Override
    protected final void init(Params params) {
        init((ExpandableParams) params);
    }

    /**
     * @see ListScenario#init
     */
    protected abstract void init(ExpandableParams params);
}
+0 −144
Original line number Diff line number Diff line
/*
 * 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 android.widget.expandablelistview;

import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.ExpandableListScenario;
import android.util.ListUtil;
import android.util.ExpandableListScenario.MyGroup;
import android.view.KeyEvent;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListAdapter;
import android.widget.ExpandableListView;

import java.util.List;

public class ExpandableListBasicTest extends ActivityInstrumentationTestCase2<ExpandableListSimple> {
    private ExpandableListScenario mActivity;
    private ExpandableListView mExpandableListView;
    private ExpandableListAdapter mAdapter;
    private ListUtil mListUtil;
    
    public ExpandableListBasicTest() {
        super(ExpandableListSimple.class);
    }

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        
        mActivity = getActivity();
        mExpandableListView = mActivity.getExpandableListView();
        mAdapter = mExpandableListView.getExpandableListAdapter();
        mListUtil = new ListUtil(mExpandableListView, getInstrumentation());
    }
    
    @MediumTest
    public void testPreconditions() {
        assertNotNull(mActivity);
        assertNotNull(mExpandableListView);
    }
    
    private int expandGroup(int numChildren, boolean atLeastOneChild) {
        final int groupPos = mActivity.findGroupWithNumChildren(numChildren, atLeastOneChild);
        assertTrue("Could not find group to expand", groupPos >= 0);

        assertFalse("Group is already expanded", mExpandableListView.isGroupExpanded(groupPos));
        mListUtil.arrowScrollToSelectedPosition(groupPos);
        getInstrumentation().waitForIdleSync();
        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
        getInstrumentation().waitForIdleSync();
        assertTrue("Group did not expand", mExpandableListView.isGroupExpanded(groupPos));

        return groupPos;
    }

    @MediumTest
    public void testExpandGroup() {
        expandGroup(-1, true);
    }
    
    @MediumTest
    public void testCollapseGroup() {
        final int groupPos = expandGroup(-1, true);
        
        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
        getInstrumentation().waitForIdleSync();
        assertFalse("Group did not collapse", mExpandableListView.isGroupExpanded(groupPos));
    }
    
    @MediumTest
    public void testExpandedGroupMovement() {
        // Expand the first group
        mListUtil.arrowScrollToSelectedPosition(0);
        sendKeys(KeyEvent.KEYCODE_DPAD_CENTER);
        getInstrumentation().waitForIdleSync();

        // Ensure it expanded
        assertTrue("Group did not expand", mExpandableListView.isGroupExpanded(0));
        
        // Wait until that's all good
        getInstrumentation().waitForIdleSync();
        
        // Make sure it expanded
        assertTrue("Group did not expand", mExpandableListView.isGroupExpanded(0));
        
        // Insert a collapsed group in front of the one just expanded
        List<MyGroup> groups = mActivity.getGroups();
        MyGroup insertedGroup = new MyGroup(1);
        groups.add(0, insertedGroup);
        
        // Notify data change
        assertTrue("Adapter is not an instance of the base adapter",
                mAdapter instanceof BaseExpandableListAdapter);
        final BaseExpandableListAdapter adapter = (BaseExpandableListAdapter) mAdapter;
     
        mActivity.runOnUiThread(new Runnable() {
            public void run() {
                adapter.notifyDataSetChanged();
            }
        });
        getInstrumentation().waitForIdleSync();
        
        // Make sure the right group is expanded
        assertTrue("The expanded state didn't stay with the proper group",
                mExpandableListView.isGroupExpanded(1));
        assertFalse("The expanded state was given to the inserted group",
                mExpandableListView.isGroupExpanded(0));
    }

    @MediumTest
    public void testContextMenus() {
        ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
        tester.testContextMenus();
    }

    @MediumTest
    public void testConvertionBetweenFlatAndPacked() {
        ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
        tester.testConvertionBetweenFlatAndPackedOnGroups();
        tester.testConvertionBetweenFlatAndPackedOnChildren();
    }

    @MediumTest
    public void testSelectedPosition() {
        ExpandableListTester tester = new ExpandableListTester(mExpandableListView, this);
        tester.testSelectedPositionOnGroups();
        tester.testSelectedPositionOnChildren();
    }
}
+0 −49
Original line number Diff line number Diff line
/*
 * 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 android.widget.expandablelistview;

import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.widget.BaseExpandableListAdapter;

import android.util.ExpandableListScenario;

public class ExpandableListSimple extends ExpandableListScenario {
    private static final int[] NUM_CHILDREN = {4, 3, 2, 1, 0};

    @Override
    protected void init(ExpandableParams params) {
        params.setNumChildren(NUM_CHILDREN)
                .setItemScreenSizeFactor(0.14);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        menu.add("Add item").setOnMenuItemClickListener(new OnMenuItemClickListener() {
            public boolean onMenuItemClick(MenuItem item) {
                mGroups.add(0, new MyGroup(2));
                ((BaseExpandableListAdapter) mAdapter).notifyDataSetChanged();
                return true;
            }
        });
        
        return true;
    }
    
}
+0 −241

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −67
Original line number Diff line number Diff line
/*
 * 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 android.widget.expandablelistview;

import android.os.Bundle;
import android.util.ExpandableListScenario;
import android.widget.Button;
import android.widget.ExpandableListView;

public class ExpandableListWithHeaders extends ExpandableListScenario {
    private static final int[] sNumChildren = {1, 4, 3, 2, 6};
    private static final int sNumOfHeadersAndFooters = 12;
    
    @Override
    protected void init(ExpandableParams params) {
        params.setStackFromBottom(false)
                .setStartingSelectionPosition(-1)
                .setNumChildren(sNumChildren)
                .setItemScreenSizeFactor(0.14)
                .setConnectAdapter(false);
    }

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        final ExpandableListView expandableListView = getExpandableListView();
        expandableListView.setItemsCanFocus(true);

        for (int i = 0; i < sNumOfHeadersAndFooters; i++) {
            Button header = new Button(this);
            header.setText("Header View " + i);
            expandableListView.addHeaderView(header);
        }

        for (int i = 0; i < sNumOfHeadersAndFooters; i++) {
            Button footer = new Button(this);
            footer.setText("Footer View " + i);
            expandableListView.addFooterView(footer);
        }
        
        // Set adapter here AFTER we set header and footer views
        setAdapter(expandableListView);
    }
    
    /**
     * @return The number of headers (and the same number of footers)
     */
    public int getNumOfHeadersAndFooters() {
        return sNumOfHeadersAndFooters;
    }

}
Loading