1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import android.database.Cursor;
20import android.os.Bundle;
21import java.util.List;
22import android.view.ContextMenu;
23import android.view.View;
24import android.view.ContextMenu.ContextMenuInfo;
25import android.view.View.OnCreateContextMenuListener;
26import android.widget.ExpandableListAdapter;
27import android.widget.ExpandableListView;
28import android.widget.SimpleCursorTreeAdapter;
29import android.widget.SimpleExpandableListAdapter;
30import android.widget.AdapterView.AdapterContextMenuInfo;
31
32import java.util.Map;
33
34/**
35 * An activity that displays an expandable list of items by binding to a data
36 * source implementing the ExpandableListAdapter, and exposes event handlers
37 * when the user selects an item.
38 * <p>
39 * ExpandableListActivity hosts a
40 * {@link android.widget.ExpandableListView ExpandableListView} object that can
41 * be bound to different data sources that provide a two-levels of data (the
42 * top-level is group, and below each group are children). Binding, screen
43 * layout, and row layout are discussed in the following sections.
44 * <p>
45 * <strong>Screen Layout</strong>
46 * </p>
47 * <p>
48 * ExpandableListActivity has a default layout that consists of a single,
49 * full-screen, centered expandable list. However, if you desire, you can
50 * customize the screen layout by setting your own view layout with
51 * setContentView() in onCreate(). To do this, your own view MUST contain an
52 * ExpandableListView object with the id "@android:id/list" (or
53 * {@link android.R.id#list} if it's in code)
54 * <p>
55 * Optionally, your custom view can contain another view object of any type to
56 * display when the list view is empty. This "empty list" notifier must have an
57 * id "android:empty". Note that when an empty view is present, the expandable
58 * list view will be hidden when there is no data to display.
59 * <p>
60 * The following code demonstrates an (ugly) custom screen layout. It has a list
61 * with a green background, and an alternate red "no data" message.
62 * </p>
63 *
64 * <pre>
65 * &lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
66 * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
67 *         android:orientation=&quot;vertical&quot;
68 *         android:layout_width=&quot;match_parent&quot;
69 *         android:layout_height=&quot;match_parent&quot;
70 *         android:paddingLeft=&quot;8dp&quot;
71 *         android:paddingRight=&quot;8dp&quot;&gt;
72 *
73 *     &lt;ExpandableListView android:id=&quot;@id/android:list&quot;
74 *               android:layout_width=&quot;match_parent&quot;
75 *               android:layout_height=&quot;match_parent&quot;
76 *               android:background=&quot;#00FF00&quot;
77 *               android:layout_weight=&quot;1&quot;
78 *               android:drawSelectorOnTop=&quot;false&quot;/&gt;
79 *
80 *     &lt;TextView android:id=&quot;@id/android:empty&quot;
81 *               android:layout_width=&quot;match_parent&quot;
82 *               android:layout_height=&quot;match_parent&quot;
83 *               android:background=&quot;#FF0000&quot;
84 *               android:text=&quot;No data&quot;/&gt;
85 * &lt;/LinearLayout&gt;
86 * </pre>
87 *
88 * <p>
89 * <strong>Row Layout</strong>
90 * </p>
91 * The {@link ExpandableListAdapter} set in the {@link ExpandableListActivity}
92 * via {@link #setListAdapter(ExpandableListAdapter)} provides the {@link View}s
93 * for each row. This adapter has separate methods for providing the group
94 * {@link View}s and child {@link View}s. There are a couple provided
95 * {@link ExpandableListAdapter}s that simplify use of adapters:
96 * {@link SimpleCursorTreeAdapter} and {@link SimpleExpandableListAdapter}.
97 * <p>
98 * With these, you can specify the layout of individual rows for groups and
99 * children in the list. These constructor takes a few parameters that specify
100 * layout resources for groups and children. It also has additional parameters
101 * that let you specify which data field to associate with which object in the
102 * row layout resource. The {@link SimpleCursorTreeAdapter} fetches data from
103 * {@link Cursor}s and the {@link SimpleExpandableListAdapter} fetches data
104 * from {@link List}s of {@link Map}s.
105 * </p>
106 * <p>
107 * Android provides some standard row layout resources. These are in the
108 * {@link android.R.layout} class, and have names such as simple_list_item_1,
109 * simple_list_item_2, and two_line_list_item. The following layout XML is the
110 * source for the resource two_line_list_item, which displays two data
111 * fields,one above the other, for each list row.
112 * </p>
113 *
114 * <pre>
115 * &lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
116 * &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
117 *     android:layout_width=&quot;match_parent&quot;
118 *     android:layout_height=&quot;wrap_content&quot;
119 *     android:orientation=&quot;vertical&quot;&gt;
120 *
121 *     &lt;TextView android:id=&quot;@+id/text1&quot;
122 *         android:textSize=&quot;16sp&quot;
123 *         android:textStyle=&quot;bold&quot;
124 *         android:layout_width=&quot;match_parent&quot;
125 *         android:layout_height=&quot;wrap_content&quot;/&gt;
126 *
127 *     &lt;TextView android:id=&quot;@+id/text2&quot;
128 *         android:textSize=&quot;16sp&quot;
129 *         android:layout_width=&quot;match_parent&quot;
130 *         android:layout_height=&quot;wrap_content&quot;/&gt;
131 * &lt;/LinearLayout&gt;
132 * </pre>
133 *
134 * <p>
135 * You must identify the data bound to each TextView object in this layout. The
136 * syntax for this is discussed in the next section.
137 * </p>
138 * <p>
139 * <strong>Binding to Data</strong>
140 * </p>
141 * <p>
142 * You bind the ExpandableListActivity's ExpandableListView object to data using
143 * a class that implements the
144 * {@link android.widget.ExpandableListAdapter ExpandableListAdapter} interface.
145 * Android provides two standard list adapters:
146 * {@link android.widget.SimpleExpandableListAdapter SimpleExpandableListAdapter}
147 * for static data (Maps), and
148 * {@link android.widget.SimpleCursorTreeAdapter SimpleCursorTreeAdapter} for
149 * Cursor query results.
150 * </p>
151 *
152 * @see #setListAdapter
153 * @see android.widget.ExpandableListView
154 */
155public class ExpandableListActivity extends Activity implements
156        OnCreateContextMenuListener,
157        ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupCollapseListener,
158        ExpandableListView.OnGroupExpandListener {
159    ExpandableListAdapter mAdapter;
160    ExpandableListView mList;
161    boolean mFinishedStart = false;
162
163    /**
164     * Override this to populate the context menu when an item is long pressed. menuInfo
165     * will contain an {@link android.widget.ExpandableListView.ExpandableListContextMenuInfo}
166     * whose packedPosition is a packed position
167     * that should be used with {@link ExpandableListView#getPackedPositionType(long)} and
168     * the other similar methods.
169     * <p>
170     * {@inheritDoc}
171     */
172    @Override
173    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
174    }
175
176    /**
177     * Override this for receiving callbacks when a child has been clicked.
178     * <p>
179     * {@inheritDoc}
180     */
181    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
182            int childPosition, long id) {
183        return false;
184    }
185
186    /**
187     * Override this for receiving callbacks when a group has been collapsed.
188     */
189    public void onGroupCollapse(int groupPosition) {
190    }
191
192    /**
193     * Override this for receiving callbacks when a group has been expanded.
194     */
195    public void onGroupExpand(int groupPosition) {
196    }
197
198    /**
199     * Ensures the expandable list view has been created before Activity restores all
200     * of the view states.
201     *
202     *@see Activity#onRestoreInstanceState(Bundle)
203     */
204    @Override
205    protected void onRestoreInstanceState(Bundle state) {
206        ensureList();
207        super.onRestoreInstanceState(state);
208    }
209
210    /**
211     * Updates the screen state (current list and other views) when the
212     * content changes.
213     *
214     * @see Activity#onContentChanged()
215     */
216    @Override
217    public void onContentChanged() {
218        super.onContentChanged();
219        View emptyView = findViewById(com.android.internal.R.id.empty);
220        mList = (ExpandableListView)findViewById(com.android.internal.R.id.list);
221        if (mList == null) {
222            throw new RuntimeException(
223                    "Your content must have a ExpandableListView whose id attribute is " +
224                    "'android.R.id.list'");
225        }
226        if (emptyView != null) {
227            mList.setEmptyView(emptyView);
228        }
229        mList.setOnChildClickListener(this);
230        mList.setOnGroupExpandListener(this);
231        mList.setOnGroupCollapseListener(this);
232
233        if (mFinishedStart) {
234            setListAdapter(mAdapter);
235        }
236        mFinishedStart = true;
237    }
238
239    /**
240     * Provide the adapter for the expandable list.
241     */
242    public void setListAdapter(ExpandableListAdapter adapter) {
243        synchronized (this) {
244            ensureList();
245            mAdapter = adapter;
246            mList.setAdapter(adapter);
247        }
248    }
249
250    /**
251     * Get the activity's expandable list view widget.  This can be used to get the selection,
252     * set the selection, and many other useful functions.
253     *
254     * @see ExpandableListView
255     */
256    public ExpandableListView getExpandableListView() {
257        ensureList();
258        return mList;
259    }
260
261    /**
262     * Get the ExpandableListAdapter associated with this activity's
263     * ExpandableListView.
264     */
265    public ExpandableListAdapter getExpandableListAdapter() {
266        return mAdapter;
267    }
268
269    private void ensureList() {
270        if (mList != null) {
271            return;
272        }
273        setContentView(com.android.internal.R.layout.expandable_list_content);
274    }
275
276    /**
277     * Gets the ID of the currently selected group or child.
278     *
279     * @return The ID of the currently selected group or child.
280     */
281    public long getSelectedId() {
282        return mList.getSelectedId();
283    }
284
285    /**
286     * Gets the position (in packed position representation) of the currently
287     * selected group or child. Use
288     * {@link ExpandableListView#getPackedPositionType},
289     * {@link ExpandableListView#getPackedPositionGroup}, and
290     * {@link ExpandableListView#getPackedPositionChild} to unpack the returned
291     * packed position.
292     *
293     * @return A packed position representation containing the currently
294     *         selected group or child's position and type.
295     */
296    public long getSelectedPosition() {
297        return mList.getSelectedPosition();
298    }
299
300    /**
301     * Sets the selection to the specified child. If the child is in a collapsed
302     * group, the group will only be expanded and child subsequently selected if
303     * shouldExpandGroup is set to true, otherwise the method will return false.
304     *
305     * @param groupPosition The position of the group that contains the child.
306     * @param childPosition The position of the child within the group.
307     * @param shouldExpandGroup Whether the child's group should be expanded if
308     *            it is collapsed.
309     * @return Whether the selection was successfully set on the child.
310     */
311    public boolean setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup) {
312        return mList.setSelectedChild(groupPosition, childPosition, shouldExpandGroup);
313    }
314
315    /**
316     * Sets the selection to the specified group.
317     * @param groupPosition The position of the group that should be selected.
318     */
319    public void setSelectedGroup(int groupPosition) {
320        mList.setSelectedGroup(groupPosition);
321    }
322
323}
324
325