1package com.xtremelabs.robolectric.shadows;
2
3import java.util.ArrayList;
4import java.util.List;
5
6import android.util.SparseBooleanArray;
7import android.view.View;
8import android.widget.ListAdapter;
9import android.widget.ListView;
10
11import com.xtremelabs.robolectric.internal.Implementation;
12import com.xtremelabs.robolectric.internal.Implements;
13import com.xtremelabs.robolectric.internal.RealObject;
14
15@SuppressWarnings({"UnusedDeclaration"})
16@Implements(ListView.class)
17public class ShadowListView extends ShadowAbsListView {
18    @RealObject private ListView realListView;
19
20    private boolean itemsCanFocus;
21    private List<View> headerViews = new ArrayList<View>();
22    private List<View> footerViews = new ArrayList<View>();
23
24    private int choiceMode;
25    private SparseBooleanArray checkedItemPositions = new SparseBooleanArray();
26
27    @Implementation
28    @Override
29    public View findViewById(int id) {
30        View child = super.findViewById(id);
31        if (child == null) {
32            child = findView(headerViews, id);
33
34            if (child == null) {
35                child = findView(footerViews, id);
36            }
37        }
38        return child;
39    }
40
41    private View findView(List<View> views, int viewId) {
42        View child = null;
43        for (View v : views) {
44            child = v.findViewById(viewId);
45            if (child != null) {
46                break;
47            }
48        }
49        return child;
50    }
51
52
53    @Implementation
54    public void setItemsCanFocus(boolean itemsCanFocus) {
55        this.itemsCanFocus = itemsCanFocus;
56    }
57
58    @Implementation
59    @Override
60    public boolean performItemClick(View view, int position, long id) {
61        boolean handled = false;
62        if (choiceMode != ListView.CHOICE_MODE_NONE) {
63            handled = true;
64
65            if (choiceMode == ListView.CHOICE_MODE_MULTIPLE) {
66                boolean newValue = !checkedItemPositions.get(position, false);
67                checkedItemPositions.put(position, newValue);
68            } else {
69                boolean newValue = !checkedItemPositions.get(position, false);
70                if (newValue) {
71                	checkedItemPositions.clear();
72                	checkedItemPositions.put(position, true);
73                }
74            }
75        }
76
77        handled |= super.performItemClick(view, position, id);
78        return handled;
79    }
80
81    @Implementation
82    public void setAdapter(ListAdapter adapter) {
83        super.setAdapter(adapter);
84    }
85
86    @Implementation
87    public void addHeaderView(View headerView) {
88        addHeaderView(headerView, null, true);
89    }
90
91    @Implementation
92    public void addHeaderView(View headerView, Object data, boolean isSelectable) {
93        ensureAdapterNotSet("header");
94        headerViews.add(headerView);
95        realListView.addView(headerView);
96    }
97
98    @Implementation
99    public int getHeaderViewsCount() {
100        return headerViews.size();
101    }
102
103    @Implementation
104    public int getFooterViewsCount() {
105        return footerViews.size();
106    }
107
108    @Implementation
109    public void addFooterView(View footerView, Object data, boolean isSelectable) {
110        ensureAdapterNotSet("footer");
111        footerViews.add(footerView);
112        realListView.addView(footerView);
113    }
114
115    @Implementation
116    public void addFooterView(View footerView) {
117        addFooterView(footerView, null, false);
118    }
119
120    @Implementation
121    public void removeAllViews() {
122        throw new UnsupportedOperationException();
123    }
124
125    @Implementation
126    public void removeView(View view) {
127        throw new UnsupportedOperationException();
128    }
129
130    @Implementation
131    public void removeViewAt(int index) {
132        throw new UnsupportedOperationException();
133    }
134
135    private void ensureAdapterNotSet(String view) {
136        if (getAdapter() != null) {
137            throw new IllegalStateException("Cannot add " + view + " view to list -- setAdapter has already been called");
138        }
139    }
140
141    public boolean isItemsCanFocus() {
142        return itemsCanFocus;
143    }
144
145    public List<View> getHeaderViews() {
146        return headerViews;
147    }
148
149    public void setHeaderViews(List<View> headerViews) {
150        this.headerViews = headerViews;
151    }
152
153    public List<View> getFooterViews() {
154        return footerViews;
155    }
156
157    public void setFooterViews(List<View> footerViews) {
158        this.footerViews = footerViews;
159    }
160
161    @Override
162    protected void addViews() {
163        for (View headerView : headerViews) {
164            addView(headerView);
165        }
166
167        super.addViews();
168
169        for (View footerView : footerViews) {
170            addView(footerView);
171        }
172    }
173
174    @Implementation
175    public int getChoiceMode() {
176        return choiceMode;
177    }
178
179    @Implementation
180    public void setChoiceMode(int choiceMode) {
181        this.choiceMode = choiceMode;
182    }
183
184    @Implementation
185    public void setItemChecked(int position, boolean value) {
186        if (choiceMode == ListView.CHOICE_MODE_SINGLE) {
187            checkedItemPositions.clear();
188            checkedItemPositions.put(position, value);
189        } else if (choiceMode == ListView.CHOICE_MODE_MULTIPLE) {
190            checkedItemPositions.put(position, value);
191        }
192    }
193
194    @Implementation
195    public int getCheckedItemPosition() {
196        if (choiceMode != ListView.CHOICE_MODE_SINGLE || checkedItemPositions.size() != 1)
197            return ListView.INVALID_POSITION;
198
199        return checkedItemPositions.keyAt(0);
200    }
201
202    @Implementation
203    public SparseBooleanArray getCheckedItemPositions() {
204        if (choiceMode == ListView.CHOICE_MODE_NONE)
205            return null;
206
207        return checkedItemPositions;
208    }
209}
210